This is an automated email from the git hooks/post-receive script. skitt pushed a commit to branch master in repository lgogdownloader.
commit 6375924597eaee90bc2d10a616f7e40ae251968f Author: Stephen Kitt <[email protected]> Date: Fri Mar 18 12:47:45 2016 +0100 Imported Upstream version 2.27 --- .gitignore | 6 + CMakeLists.txt | 115 +++++++ Makefile | 180 ----------- README.md | 7 +- cmake/FindHtmlcxx.cmake | 54 ++++ cmake/FindJsoncpp.cmake | 34 ++ cmake/FindOAuth.cmake | 28 ++ cmake/FindRhash.cmake | 22 ++ cmake/FindTinyxml.cmake | 33 ++ include/api.h | 2 +- include/config.h | 4 + include/downloader.h | 3 + include/gamedetails.h | 10 +- include/globalconstants.h | 2 +- include/util.h | 16 + lgogdownloader.cbp | 76 ----- main.cpp | 86 +++--- man/CMakeLists.txt | 22 ++ man/lgogdownloader.supplemental.groff | 26 +- src/downloader.cpp | 567 +++++++++++++++++++++++----------- src/gamedetails.cpp | 15 +- src/util.cpp | 93 +++++- version.sh | 9 - 23 files changed, 894 insertions(+), 516 deletions(-) diff --git a/.gitignore b/.gitignore index aa3b985..461ec87 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,9 @@ bin/* obj/* *.1 *.gz +Makefile +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +build/ +*.cbp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..743f955 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,115 @@ +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) +project (lgogdownloader LANGUAGES CXX VERSION 2.27) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG=1") + +find_package(Boost + REQUIRED + system + filesystem + regex + program_options + date_time + ) +find_package(CURL REQUIRED) +find_package(OAuth REQUIRED) +find_package(Jsoncpp REQUIRED) +find_package(Htmlcxx REQUIRED) +find_package(Tinyxml REQUIRED) +find_package(Rhash REQUIRED) + +file(GLOB SRC_FILES + main.cpp + src/api.cpp + src/downloader.cpp + src/progressbar.cpp + src/util.cpp + src/blacklist.cpp + src/gamefile.cpp + src/gamedetails.cpp + ) + +set(GIT_CHECKOUT FALSE) +if(EXISTS ${PROJECT_SOURCE_DIR}/.git) + if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) + find_package(Git) + if(GIT_FOUND) + set(GIT_CHECKOUT TRUE) + else(GIT_FOUND) + message(WARNING "Git executable not found") + endif(GIT_FOUND) + else(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) + message(STATUS "Shallow Git clone detected, not attempting to retrieve version info") + endif(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow) +endif(EXISTS ${PROJECT_SOURCE_DIR}/.git) + +if(GIT_CHECKOUT) + execute_process(COMMAND ${GIT_EXECUTABLE} diff --shortstat + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_SHORTSTAT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_REV_PARSE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(GIT_SHORTSTAT) + set(GIT_DIRTY ON) + endif(GIT_SHORTSTAT) + + if(GIT_DIRTY) + set(PROJECT_VERSION_MINOR ${PROJECT_VERSION_MINOR}M) + endif(GIT_DIRTY) + + set(PROJECT_VERSION_PATCH ${GIT_REV_PARSE}) + set(PROJECT_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) +endif(GIT_CHECKOUT) + +set(VERSION_NUMBER ${PROJECT_VERSION}) +set(VERSION_STRING "LGOGDownloader ${VERSION_NUMBER}") + +add_definitions(-D_FILE_OFFSET_BITS=64 -DVERSION_NUMBER="${VERSION_NUMBER}" -DVERSION_STRING="${VERSION_STRING}") + +add_executable (${PROJECT_NAME} ${SRC_FILES}) + + +target_include_directories(${PROJECT_NAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + PRIVATE ${Boost_INCLUDE_DIRS} + PRIVATE ${CURL_INCLUDE_DIRS} + PRIVATE ${OAuth_INCLUDE_DIRS} + PRIVATE ${Jsoncpp_INCLUDE_DIRS} + PRIVATE ${Htmlcxx_INCLUDE_DIRS} + PRIVATE ${Tinyxml_INCLUDE_DIRS} + PRIVATE ${Rhash_INCLUDE_DIRS} + ) + +target_link_libraries(${PROJECT_NAME} + PRIVATE ${Boost_LIBRARIES} + PRIVATE ${CURL_LIBRARIES} + PRIVATE ${OAuth_LIBRARIES} + PRIVATE ${Jsoncpp_LIBRARIES} + PRIVATE ${Htmlcxx_LIBRARIES} + PRIVATE ${Tinyxml_LIBRARIES} + PRIVATE ${Rhash_LIBRARIES} + ) + +if(MSVC) + # Force to always compile with W4 + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Update if necessary + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-long-long -fexceptions") +endif() + +set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +set(INSTALL_SHARE_DIR share CACHE PATH "Installation directory for resource files") + +install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX} DESTINATION ${INSTALL_BIN_DIR}) +add_subdirectory(man) diff --git a/Makefile b/Makefile deleted file mode 100644 index 9e7d4d7..0000000 --- a/Makefile +++ /dev/null @@ -1,180 +0,0 @@ -#------------------------------------------------------------------------------# -# This makefile was generated by 'cbp2make' tool rev.127 # -#------------------------------------------------------------------------------# - - -WORKDIR = `pwd` - -DESTDIR = -PREFIX = /usr -MANPREFIX = $(PREFIX)/share - -CC = gcc -CXX = g++ -AR = ar -LD = g++ -WINDRES = windres - -INC = -Iinclude -I/usr/include/rhash -I/usr/include/jsoncpp -CFLAGS = -std=c++11 -Wall -fexceptions -D_FILE_OFFSET_BITS=64 -RESINC = -LIBDIR = -LIB = -lcurl -loauth -ljsoncpp -lhtmlcxx -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lboost_date_time -ltinyxml -lrhash -LDFLAGS = - -VERSION = -DVERSION_STRING="\"$(shell sh version.sh)\"" -HELP2MAN = $(shell which help2man 2> /dev/null) -MAN_DIR = man -MAN_PAGE = lgogdownloader.1 - -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 $(OBJDIR_DEBUG)/src/blacklist.o $(OBJDIR_DEBUG)/src/gamedetails.o $(OBJDIR_DEBUG)/src/gamefile.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 $(OBJDIR_RELEASE)/src/blacklist.o $(OBJDIR_RELEASE)/src/gamedetails.o $(OBJDIR_RELEASE)/src/gamefile.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: out_debug -ifdef HELP2MAN - if ! test -f $(MAN_DIR)/$(MAN_PAGE).gz; then \ - help2man -N -i $(MAN_DIR)/lgogdownloader.supplemental.groff -o $(MAN_DIR)/$(MAN_PAGE) $(OUT_DEBUG); \ - gzip -f -9 $(MAN_DIR)/$(MAN_PAGE); \ - fi -endif - -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) - -$(OBJ_DEBUG): | before_debug - -$(OBJDIR_DEBUG)/main.o: main.cpp - $(CXX) $(CFLAGS_DEBUG) $(VERSION) $(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 - -$(OBJDIR_DEBUG)/src/blacklist.o: src/blacklist.cpp - $(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/blacklist.cpp -o $(OBJDIR_DEBUG)/src/blacklist.o - -$(OBJDIR_DEBUG)/src/gamefile.o: src/gamefile.cpp - $(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/gamefile.cpp -o $(OBJDIR_DEBUG)/src/gamefile.o - -$(OBJDIR_DEBUG)/src/gamedetails.o: src/gamedetails.cpp - $(CXX) $(CFLAGS_DEBUG) $(INC_DEBUG) -c src/gamedetails.cpp -o $(OBJDIR_DEBUG)/src/gamedetails.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: out_release -ifdef HELP2MAN - if ! test -f $(MAN_DIR)/$(MAN_PAGE).gz; then \ - help2man -N -i $(MAN_DIR)/lgogdownloader.supplemental.groff -o $(MAN_DIR)/$(MAN_PAGE) $(OUT_RELEASE); \ - gzip -f -9 $(MAN_DIR)/$(MAN_PAGE); \ - fi -endif - -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) - -$(OBJ_RELEASE): | before_release - -$(OBJDIR_RELEASE)/main.o: main.cpp - $(CXX) $(CFLAGS_RELEASE) $(VERSION) $(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 - -$(OBJDIR_RELEASE)/src/blacklist.o: src/blacklist.cpp - $(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/blacklist.cpp -o $(OBJDIR_RELEASE)/src/blacklist.o - -$(OBJDIR_RELEASE)/src/gamefile.o: src/gamefile.cpp - $(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/gamefile.cpp -o $(OBJDIR_RELEASE)/src/gamefile.o - -$(OBJDIR_RELEASE)/src/gamedetails.o: src/gamedetails.cpp - $(CXX) $(CFLAGS_RELEASE) $(INC_RELEASE) -c src/gamedetails.cpp -o $(OBJDIR_RELEASE)/src/gamedetails.o - -clean_release: - rm -f $(OBJ_RELEASE) $(OUT_RELEASE) - rm -rf bin/Release - rm -rf $(OBJDIR_RELEASE) - rm -rf $(OBJDIR_RELEASE)/src - rm -f $(MAN_DIR)/$(MAN_PAGE) $(MAN_DIR)/$(MAN_PAGE).gz - -install: release - install -d $(DESTDIR)/$(PREFIX)/bin/ - if test -f $(OUT_DEBUG); then \ - install -m 755 $(OUT_DEBUG) $(DESTDIR)/$(PREFIX)/bin/lgogdownloader; \ - else \ - install -m 755 $(OUT_RELEASE) $(DESTDIR)/$(PREFIX)/bin/lgogdownloader; \ - fi - if test -f $(MAN_DIR)/$(MAN_PAGE).gz; then \ - install -d $(DESTDIR)/$(MANPREFIX)/man/man1/; \ - install -m 644 $(MAN_DIR)/$(MAN_PAGE).gz $(DESTDIR)/$(MANPREFIX)/man/man1/$(MAN_PAGE).gz; \ - fi - -uninstall: - rm $(DESTDIR)/$(PREFIX)/bin/lgogdownloader - if test -f $(DESTDIR)/$(MANPREFIX)/man/man1/$(MAN_PAGE).gz; then \ - rm $(DESTDIR)/$(MANPREFIX)/man/man1/$(MAN_PAGE).gz; \ - fi - -.PHONY: before_debug after_debug clean_debug before_release after_release clean_release - diff --git a/README.md b/README.md index 007d181..0d28587 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,14 @@ This repository contains the code of unofficial [GOG](http://www.gog.com/) downl # apt install build-essential libcurl4-openssl-dev libboost-regex-dev \ libjsoncpp-dev liboauth-dev librhash-dev libtinyxml-dev libhtmlcxx-dev\ libboost-system-dev libboost-filesystem-dev libboost-program-options-dev\ - libboost-date-time-dev help2man + libboost-date-time-dev help2man cmake ## Build and install - $ make release + $ mkdir build + $ cd build + $ cmake .. + $ make # sudo make install ## Use diff --git a/cmake/FindHtmlcxx.cmake b/cmake/FindHtmlcxx.cmake new file mode 100644 index 0000000..6dbf4b1 --- /dev/null +++ b/cmake/FindHtmlcxx.cmake @@ -0,0 +1,54 @@ +# - Try to find htmlcxx +# +# Once done this will define +# Htmlcxx_FOUND - System has htmlcxx +# Htmlcxx_INCLUDE_DIRS - The htmlcxx include directories +# Htmlcxx_LIBRARIES - The libraries needed to use htmlcxx + +find_package(PkgConfig) +pkg_check_modules(PC_HTMLCXX REQUIRED htmlcxx) + +find_path(HTMLCXX_INCLUDE_DIR + NAMES + css/parser.h + html/tree.h + HINTS + ${PC_HTMLCXX_INCLUDEDIR} + ${PC_HTMLCXX_INCLUDE_DIRS} + PATH_SUFFIXES + htmlcxx + PATHS + ${PC_HTMLCXX_INCLUDE_DIRS} + ) + +find_library(HTMLCXX_LIBRARY_HTMLCXX htmlcxx + HINTS + ${PC_HTMLCXX_LIBDIR} + ${PC_HTMLCXX_LIBRARY_DIRS} + PATHS + ${PC_HTMLCXX_LIBRARY_DIRS} + ) + +find_library(HTMLCXX_LIBRARY_CSS_PARSER css_parser + HINTS + ${PC_HTMLCXX_LIBDIR} + ${PC_HTMLCXX_LIBRARY_DIRS} + PATHS + ${PC_HTMLCXX_LIBRARY_DIRS} + ) + +find_library(HTMLCXX_LIBRARY_CSS_PARSER_PP css_parser_pp + HINTS + ${PC_HTMLCXX_LIBDIR} + ${PC_HTMLCXX_LIBRARY_DIRS} + PATHS + ${PC_HTMLCXX_LIBRARY_DIRS} + ) + +mark_as_advanced(HTMLCXX_INCLUDE_DIR HTMLCXX_LIBRARY_HTMLCXX HTMLCXX_LIBRARY_CSS_PARSER HTMLCXX_LIBRARY_CSS_PARSER_PP) + +if(PC_HTMLCXX_FOUND) + set(Htmlcxx_FOUND ON) + set(Htmlcxx_INCLUDE_DIRS ${HTMLCXX_INCLUDE_DIR}) + set(Htmlcxx_LIBRARIES ${HTMLCXX_LIBRARY_HTMLCXX} ${HTMLCXX_LIBRARY_CSS_PARSER} ${HTMLCXX_LIBRARY_CSS_PARSER_PP}) +endif(PC_HTMLCXX_FOUND) diff --git a/cmake/FindJsoncpp.cmake b/cmake/FindJsoncpp.cmake new file mode 100644 index 0000000..5731980 --- /dev/null +++ b/cmake/FindJsoncpp.cmake @@ -0,0 +1,34 @@ +# - Try to find Jsoncpp +# +# Once done, this will define +# Jsoncpp_FOUND - system has Jsoncpp +# Jsoncpp_INCLUDE_DIRS - the Jsoncpp include directories +# Jsoncpp_LIBRARIES - link these to use Jsoncpp + +find_package(PkgConfig) +pkg_check_modules(PC_JSONCPP REQUIRED jsoncpp) + +find_path(JSONCPP_INCLUDE_DIR + NAMES + json/features.h + HINTS + ${PC_JSONCPP_INCLUDEDIR} + ${PC_JSONCPP_INCLUDEDIRS} + PATH_SUFFIXES + jsoncpp + PATHS + ${PC_JSONCPP_INCLUDE_DIRS} + ) + +find_library(JSONCPP_LIBRARY jsoncpp + PATHS + ${PC_JSONCPP_LIBRARY_DIRS} + ) + +mark_as_advanced(JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY) + +if(PC_JSONCPP_FOUND) + set(Jsoncpp_FOUND ON) + set(Jsoncpp_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR}) + set(Jsoncpp_LIBRARIES ${JSONCPP_LIBRARY}) +endif(PC_JSONCPP_FOUND) diff --git a/cmake/FindOAuth.cmake b/cmake/FindOAuth.cmake new file mode 100644 index 0000000..55dbd63 --- /dev/null +++ b/cmake/FindOAuth.cmake @@ -0,0 +1,28 @@ +# - Try to find oauth +# +# Once done this will define +# OAuth_FOUND - System has oauth +# OAuth_INCLUDE_DIRS - The oauth include directories +# OAuth_LIBRARIES - The libraries needed to use oauth + +find_package(PkgConfig) +pkg_check_modules(PC_OAUTH REQUIRED oauth) + +find_path(OAUTH_INCLUDE_DIR oauth.h + HINTS ${PC_OAUTH_INCLUDEDIR} + ${PC_OAUTH_INCLUDE_DIRS} + PATH_SUFFIXES oauth + ) + +find_library(OAUTH_LIBRARY NAMES oauth + HINTS ${PC_OAUTH_LIBDIR} + ${PC_OAUTH_LIBRARY_DIRS} + ) + +mark_as_advanced(OAUTH_INCLUDE_DIR OAUTH_LIBRARY) + +if(PC_OAUTH_FOUND) + set(OAuth_FOUND ON) + set(OAuth_INCLUDE_DIRS ${OAUTH_INCLUDE_DIR}) + set(OAuth_LIBRARIES ${OAUTH_LIBRARY}) +endif(PC_OAUTH_FOUND) diff --git a/cmake/FindRhash.cmake b/cmake/FindRhash.cmake new file mode 100644 index 0000000..cdfecd3 --- /dev/null +++ b/cmake/FindRhash.cmake @@ -0,0 +1,22 @@ +# - Try to find rhash +# +# Once done this will define +# Rhash_FOUND - System has rhash +# Rhash_INCLUDE_DIRS - The rhash include directories +# Rhash_LIBRARIES - The libraries needed to use rhash + +find_path(RHASH_INCLUDE_DIR rhash.h) +find_library(RHASH_LIBRARY rhash) + +mark_as_advanced(RHASH_INCLUDE_DIR RHASH_LIBRARY) + +if(RHASH_LIBRARY AND RHASH_INCLUDE_DIR) + set(Rhash_FOUND ON) + set(Rhash_LIBRARIES ${RHASH_LIBRARY}) + set(Rhash_INCLUDE_DIRS ${RHASH_INCLUDE_DIR}) +else() + set(Rhash_FOUND OFF) + if(Rhash_FIND_REQUIRED) + message(FATAL_ERROR "Could not find rhash") + endif(Rhash_FIND_REQUIRED) +endif(RHASH_LIBRARY AND RHASH_INCLUDE_DIR) diff --git a/cmake/FindTinyxml.cmake b/cmake/FindTinyxml.cmake new file mode 100644 index 0000000..f399381 --- /dev/null +++ b/cmake/FindTinyxml.cmake @@ -0,0 +1,33 @@ +# - Try to find tinyxml +# +# Once done this will define +# Tinyxml_FOUND - System has tinyxml +# Tinyxml_INCLUDE_DIRS - The tinyxml include directories +# Tinyxml_LIBRARIES - The libraries needed to use tinyxml + +find_package(PkgConfig) +pkg_check_modules(PC_TINYXML tinyxml) + +find_path(TINYXML_INCLUDE_DIR tinyxml.h + HINTS + ${PC_TINYXML_INCLUDEDIR} + ${PC_TINYXML_INCLUDE_DIRS} + PATHS + ${PC_TINYXML_INCLUDE_DIRS} + ) + +find_library(TINYXML_LIBRARY tinyxml + HINTS + ${PC_TINYXML_LIBDIR} + ${PC_TINYXML_LIBRARY_DIRS} + PATHS + ${PC_TINYXML_LIBRARY_DIRS} + ) + +mark_as_advanced(TINYXML_INCLUDE_DIR TINYXML_LIBRARY) + +if(TINYXML_INCLUDE_DIR) + set(Tinyxml_FOUND ON) + set(Tinyxml_INCLUDE_DIRS ${TINYXML_INCLUDE_DIR}) + set(Tinyxml_LIBRARIES ${TINYXML_LIBRARY}) +endif(TINYXML_INCLUDE_DIR) diff --git a/include/api.h b/include/api.h index 3104230..97abe45 100644 --- a/include/api.h +++ b/include/api.h @@ -72,7 +72,7 @@ class API std::string getErrorMessage() { return this->error_message; }; std::string getToken() { return this->config.oauth_token; }; std::string getSecret() { return this->config.oauth_secret; }; - template <typename T> CURLcode curlSetOpt(CURLoption option, T value) { return curl_easy_setopt(this->curlhandle, option, value); }; + template <typename T> CURLcode curlSetOpt(CURLoption option, T value) { return curl_easy_setopt(this->curlhandle, option, value); } virtual ~API(); protected: private: diff --git a/include/config.h b/include/config.h index ea6fe6c..75cc478 100644 --- a/include/config.h +++ b/include/config.h @@ -46,6 +46,8 @@ class Config bool bSaveSerials; bool bPlatformDetection; bool bShowWishlist; + bool bAutomaticXMLCreation; + bool bSaveChangelogs; std::string sGameRegex; std::string sDirectory; std::string sCacheDirectory; @@ -59,6 +61,7 @@ class Config std::string sCookiePath; std::string sConfigFilePath; std::string sBlacklistFilePath; + std::string sIgnorelistFilePath; std::string sOrphanRegex; std::string sCoverList; std::string sReportFilePath; @@ -86,6 +89,7 @@ class Config curl_off_t iDownloadRate; long int iTimeout; Blacklist blacklist; + Blacklist ignorelist; }; #endif // CONFIG_H__ diff --git a/include/downloader.h b/include/downloader.h index 666eecd..b52153c 100644 --- a/include/downloader.h +++ b/include/downloader.h @@ -52,6 +52,7 @@ class gameItem { std::string name; std::string id; std::vector<std::string> dlcnames; + Json::Value gamedetailsjson; }; class Downloader @@ -97,6 +98,8 @@ class Downloader Json::Value getGameDetailsJSON(const std::string& gameid); std::string getSerialsFromJSON(const Json::Value& json); void saveSerials(const std::string& serials, const std::string& filepath); + std::string getChangelogFromJSON(const Json::Value& json); + void saveChangelog(const std::string& changelog, const std::string& filepath); 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); diff --git a/include/gamedetails.h b/include/gamedetails.h index 4ef052a..1737fff 100644 --- a/include/gamedetails.h +++ b/include/gamedetails.h @@ -4,6 +4,7 @@ #include "globalconstants.h" #include "gamefile.h" #include "config.h" +#include "util.h" #include <iostream> #include <vector> @@ -22,15 +23,18 @@ class gameDetails std::string title; std::string icon; std::string serials; - void filterWithPriorities(const Config& config); - void makeFilepaths(const Config& config); + std::string changelog; + void filterWithPriorities(const gameSpecificConfig& config); + void makeFilepaths(const gameSpecificDirectoryConfig& config); std::string getSerialsFilepath(); + std::string getChangelogFilepath(); Json::Value getDetailsAsJson(); virtual ~gameDetails(); protected: - void filterListWithPriorities(std::vector<gameFile>& list, const Config& config); + void filterListWithPriorities(std::vector<gameFile>& list, const gameSpecificConfig& config); private: std::string serialsFilepath; + std::string changelogFilepath; }; #endif // GAMEDETAILS_H diff --git a/include/globalconstants.h b/include/globalconstants.h index 47ada3a..2ae0f96 100644 --- a/include/globalconstants.h +++ b/include/globalconstants.h @@ -74,6 +74,6 @@ namespace GlobalConstants { PLATFORM_MAC, "mac", "Mac" , "m|mac|osx" }, { PLATFORM_LINUX, "linux", "Linux" , "l|lin|linux" } }; -}; +} #endif // GLOBALCONSTANTS_H_INCLUDED diff --git a/include/util.h b/include/util.h index d8d2813..8f77162 100644 --- a/include/util.h +++ b/include/util.h @@ -20,12 +20,27 @@ #include <boost/regex.hpp> #include <json/json.h> +struct gameSpecificDirectoryConfig +{ + bool bSubDirectories; + std::string sDirectory; + std::string sGameSubdir; + std::string sInstallersSubdir; + std::string sExtrasSubdir; + std::string sPatchesSubdir; + std::string sLanguagePackSubdir; + std::string sDLCSubdir; +}; + struct gameSpecificConfig { unsigned int iInstallerPlatform; unsigned int iInstallerLanguage; bool bDLC; bool bIgnoreDLCCount; + gameSpecificDirectoryConfig dirConf; + std::vector<unsigned int> vLanguagePriority; + std::vector<unsigned int> vPlatformPriority; }; namespace Util @@ -48,6 +63,7 @@ namespace Util std::vector<std::string> tokenize(const std::string& str, const std::string& separator = ","); unsigned int getOptionValue(const std::string& str, const std::vector<GlobalConstants::optionsStruct>& options); std::string getOptionNameString(const unsigned int& value, const std::vector<GlobalConstants::optionsStruct>& options); + void parseOptionString(const std::string &option_string, std::vector<unsigned int> &priority, unsigned int &type, const std::vector<GlobalConstants::optionsStruct>& options); } #endif // UTIL_H diff --git a/lgogdownloader.cbp b/lgogdownloader.cbp deleted file mode 100644 index dfb1012..0000000 --- a/lgogdownloader.cbp +++ /dev/null @@ -1,76 +0,0 @@ -<?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" /> - <Add directory="include" /> - </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" /> - <Add directory="include" /> - </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/blacklist.h" /> - <Unit filename="include/config.h" /> - <Unit filename="include/downloader.h" /> - <Unit filename="include/gamedetails.h" /> - <Unit filename="include/gamefile.h" /> - <Unit filename="include/globalconstants.h" /> - <Unit filename="include/progressbar.h" /> - <Unit filename="include/util.h" /> - <Unit filename="main.cpp" /> - <Unit filename="src/api.cpp" /> - <Unit filename="src/blacklist.cpp" /> - <Unit filename="src/downloader.cpp" /> - <Unit filename="src/gamedetails.cpp" /> - <Unit filename="src/gamefile.cpp" /> - <Unit filename="src/progressbar.cpp" /> - <Unit filename="src/util.cpp" /> - <Extensions> - <code_completion /> - <debugger /> - <envvars /> - </Extensions> - </Project> -</CodeBlocks_project_file> diff --git a/main.cpp b/main.cpp index 020f3a7..3307dcc 100644 --- a/main.cpp +++ b/main.cpp @@ -13,12 +13,6 @@ #include <boost/filesystem.hpp> #include <boost/program_options.hpp> -#define VERSION_NUMBER "2.26" - -#ifndef VERSION_STRING -# define VERSION_STRING "LGOGDownloader " VERSION_NUMBER -#endif - namespace bpo = boost::program_options; template<typename T> void set_vm_value(std::map<std::string, bpo::variable_value>& vm, const std::string& option, const T& value) @@ -26,24 +20,6 @@ template<typename T> void set_vm_value(std::map<std::string, bpo::variable_value vm[option].value() = boost::any(value); } -// Parse the options string -void parseOptionString(const std::string &option_string, std::vector<unsigned int> &priority, unsigned int &type, const std::vector<GlobalConstants::optionsStruct>& options) -{ - type = 0; - std::vector<std::string> tokens_priority = Util::tokenize(option_string, ","); - for (std::vector<std::string>::iterator it_priority = tokens_priority.begin(); it_priority != tokens_priority.end(); it_priority++) - { - unsigned int value = 0; - std::vector<std::string> tokens_value = Util::tokenize(*it_priority, "+"); - for (std::vector<std::string>::iterator it_value = tokens_value.begin(); it_value != tokens_value.end(); it_value++) - { - value |= Util::getOptionValue(*it_value, options); - } - priority.push_back(value); - type |= value; - } -} - int main(int argc, char *argv[]) { // Constants for option selection with include/exclude @@ -78,6 +54,7 @@ int main(int argc, char *argv[]) config.sCookiePath = config.sConfigDirectory + "/cookies.txt"; config.sConfigFilePath = config.sConfigDirectory + "/config.cfg"; config.sBlacklistFilePath = config.sConfigDirectory + "/blacklist.txt"; + config.sIgnorelistFilePath = config.sConfigDirectory + "/ignorelist.txt"; std::string priority_help_text = "Set priority by separating values with \",\"\nCombine values by separating with \"+\""; // Create help text for --platform option @@ -198,6 +175,8 @@ int main(int argc, char *argv[]) ("ignore-dlc-count", bpo::value<std::string>(&config.sIgnoreDLCCountRegex)->implicit_value(".*"), "Set regular expression filter for games to ignore DLC count information\nIgnoring DLC count information helps in situations where the account page doesn't provide accurate information about DLCs") ("include", bpo::value<std::string>(&sIncludeOptions)->default_value("all"), ("Select what to download/list/repair\n" + include_options_text).c_str()) ("exclude", bpo::value<std::string>(&sExcludeOptions)->default_value("covers"), ("Select what not to download/list/repair\n" + include_options_text).c_str()) + ("automatic-xml-creation", bpo::value<bool>(&config.bAutomaticXMLCreation)->zero_tokens()->default_value(false), "Automatically create XML data after download has completed") + ("save-changelogs", bpo::value<bool>(&config.bSaveChangelogs)->zero_tokens()->default_value(false), "Save changelogs when downloading") ; // Options read from config file options_cfg_only.add_options() @@ -232,7 +211,7 @@ int main(int argc, char *argv[]) { if (!boost::filesystem::create_directories(path)) { - std::cout << "Failed to create directory: " << path << std::endl; + std::cerr << "Failed to create directory: " << path << std::endl; return 1; } } @@ -242,7 +221,7 @@ int main(int argc, char *argv[]) { if (!boost::filesystem::create_directories(path)) { - std::cout << "Failed to create directory: " << path << std::endl; + std::cerr << "Failed to create directory: " << path << std::endl; return 1; } } @@ -252,7 +231,7 @@ int main(int argc, char *argv[]) { if (!boost::filesystem::create_directories(path)) { - std::cout << "Failed to create directory: " << path << std::endl; + std::cerr << "Failed to create directory: " << path << std::endl; return 1; } } @@ -262,7 +241,7 @@ int main(int argc, char *argv[]) std::ifstream ifs(config.sConfigFilePath.c_str()); if (!ifs) { - std::cout << "Could not open config file: " << config.sConfigFilePath << std::endl; + std::cerr << "Could not open config file: " << config.sConfigFilePath << std::endl; return 1; } else @@ -279,7 +258,7 @@ int main(int argc, char *argv[]) std::ifstream ifs(config.sBlacklistFilePath.c_str()); if (!ifs) { - std::cout << "Could not open blacklist file: " << config.sBlacklistFilePath << std::endl; + std::cerr << "Could not open blacklist file: " << config.sBlacklistFilePath << std::endl; return 1; } else @@ -295,6 +274,27 @@ int main(int argc, char *argv[]) } } + if (boost::filesystem::exists(config.sIgnorelistFilePath)) + { + std::ifstream ifs(config.sIgnorelistFilePath.c_str()); + if (!ifs) + { + std::cerr << "Could not open ignorelist file: " << config.sIgnorelistFilePath << std::endl; + return 1; + } + else + { + std::string line; + std::vector<std::string> lines; + while (!ifs.eof()) + { + std::getline(ifs, line); + lines.push_back(std::move(line)); + } + config.ignorelist.initialize(lines); + } + } + if (vm.count("chunk-size")) config.iChunkSize <<= 20; // Convert chunk size from bytes to megabytes @@ -344,8 +344,11 @@ int main(int argc, char *argv[]) config.bLoginHTTP = true; } - parseOptionString(sInstallerLanguage, config.vLanguagePriority, config.iInstallerLanguage, GlobalConstants::LANGUAGES); - parseOptionString(sInstallerPlatform, config.vPlatformPriority, config.iInstallerPlatform, GlobalConstants::PLATFORMS); + if (config.sXMLFile == "automatic") + config.bAutomaticXMLCreation = true; + + Util::parseOptionString(sInstallerLanguage, config.vLanguagePriority, config.iInstallerLanguage, GlobalConstants::LANGUAGES); + Util::parseOptionString(sInstallerPlatform, config.vPlatformPriority, config.iInstallerPlatform, GlobalConstants::PLATFORMS); unsigned int include_value = 0; unsigned int exclude_value = 0; @@ -383,13 +386,13 @@ int main(int argc, char *argv[]) if (config.iInstallerPlatform < GlobalConstants::PLATFORMS[0].id || config.iInstallerPlatform > platform_all) { - std::cout << "Invalid value for --platform" << std::endl; + std::cerr << "Invalid value for --platform" << std::endl; return 1; } if (config.iInstallerLanguage < GlobalConstants::LANGUAGES[0].id || config.iInstallerLanguage > language_all) { - std::cout << "Invalid value for --language" << std::endl; + std::cerr << "Invalid value for --language" << std::endl; return 1; } @@ -451,12 +454,11 @@ int main(int argc, char *argv[]) { set_vm_value(vm, "token", downloader.config.sToken); set_vm_value(vm, "secret", downloader.config.sSecret); - bpo::notify(vm); } std::ofstream ofs(config.sConfigFilePath.c_str()); if (ofs) { - std::cout << "Saving config: " << config.sConfigFilePath << std::endl; + std::cerr << "Saving config: " << config.sConfigFilePath << std::endl; for (bpo::variables_map::iterator it = vm.begin(); it != vm.end(); ++it) { std::string option = it->first; @@ -507,7 +509,7 @@ int main(int argc, char *argv[]) } else { - std::cout << "Failed to create config: " << config.sConfigFilePath << std::endl; + std::cerr << "Failed to create config: " << config.sConfigFilePath << std::endl; return 1; } } @@ -527,7 +529,7 @@ int main(int argc, char *argv[]) } else { - std::cout << "Failed to create config: " << config.sConfigFilePath << std::endl; + std::cerr << "Failed to create config: " << config.sConfigFilePath << std::endl; return 1; } } @@ -555,9 +557,13 @@ int main(int argc, char *argv[]) else if (config.bCheckStatus) downloader.checkStatus(); else - { // Show help message - std::cout << config.sVersionString << std::endl - << options_cli_all << std::endl; + { + if (!(config.bLoginAPI || config.bLoginHTTP)) + { + // Show help message + std::cerr << config.sVersionString << std::endl + << options_cli_all << std::endl; + } } // Orphan check was called at the same time as download. Perform it after download has finished diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt new file mode 100644 index 0000000..7f123b0 --- /dev/null +++ b/man/CMakeLists.txt @@ -0,0 +1,22 @@ +find_program(HELP2MAN help2man DOC "Location of the help2man program") +find_program(GZIP gzip DOC "Location of the gzip program") +mark_as_advanced(HELP2MAN) +mark_as_advanced(GZIP) + +if(HELP2MAN AND GZIP) + set(H2M_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}.supplemental.groff) + set(MAN_PAGE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.1) + set(MAN_FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.1.gz) + add_custom_command( + OUTPUT ${MAN_FILE} + COMMAND ${HELP2MAN} -N -i ${H2M_FILE} -o ${MAN_PAGE} ${PROJECT_BINARY_DIR}/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX} + COMMAND ${GZIP} -f -9 ${MAN_PAGE} + MAIN_DEPENDENCY ${H2M_FILE} + COMMENT "Building man page" + VERBATIM + ) + add_custom_target(manpage ALL DEPENDS ${MAN_FILE} ${PROJECT_NAME}) + install(FILES ${MAN_FILE} DESTINATION ${INSTALL_SHARE_DIR}/man/man1) +else(HELP2MAN AND GZIP) + message("WARNING: One of the following is missing: help2man, gzip; man page will not be generated") +endif(HELP2MAN AND GZIP) diff --git a/man/lgogdownloader.supplemental.groff b/man/lgogdownloader.supplemental.groff index 22423a0..a376412 100644 --- a/man/lgogdownloader.supplemental.groff +++ b/man/lgogdownloader.supplemental.groff @@ -62,7 +62,15 @@ If \fB$XDG_CACHE_HOME\fP is not set, it will use \fI$HOME/.cache/lgogdownloader/ .TP \fI$XDG_CONFIG_HOME/lgogdownloader/blacklist.txt\fP -Allows user to specify individual files that should not be downloaded or mentioned as orphans. +Allows user to specify individual files that should not be downloaded. +.br +It doesn't have to exist, but if it does exist, it must be readable to lgogdownloader. + +.TP +\fI$XDG_CONFIG_HOME/lgogdownloader/ignorelist.txt\fP +Allows user to specify individual files that should not be mentioned +as orphans. The file has the same format and interpretation as a +blacklist. .br It doesn't have to exist, but if it does exist, it must be readable to lgogdownloader. @@ -70,17 +78,25 @@ It doesn't have to exist, but if it does exist, it must be readable to lgogdownl \fI$XDG_CONFIG_HOME/lgogdownloader/gamespecific/gamename.conf\fP JSON formatted file. Sets game specific settings for \fBgamename\fP. .br -Allowed settings are \fBlanguage\fP, \fBplatform\fP, \fBdlc\fP and \fBignore-dlc-count\fP. +Allowed settings are \fBlanguage\fP, \fBplatform\fP, \fBdlc\fP, \fBignore-dlc-count\fP \fBsubdirectories\fP, \fBdirectory\fP, \fBsubdir-game\fP, \fBsubdir-installers\fP, \fBsubdir-extras\fP, \fBsubdir-patches\fP, \fBsubdir-language-packs\fP and \fBsubdir-dlc\fP. .br The \fBdlc\fP option is limited to disabling DLC for specific game. It can't enable DLC listing/downloading if \fB--no-dlc\fP option is used. .br Must be in the following format: .br { - "language" : <int>, - "platform" : <int>, + "language" : <string>, + "platform" : <string>, "dlc" : <bool>, - "ignore-dlc-count" : <bool> + "ignore-dlc-count" : <bool>, + "subdirectories" : <bool>, + "directory" : <string>, + "subdir-game" : <string>, + "subdir-installers" : <string>, + "subdir-extras" : <string>, + "subdir-patches" : <string>, + "subdir-language-packs" : <string>, + "subdir-dlc" : <string> .br } diff --git a/src/downloader.cpp b/src/downloader.cpp index 9a5ed31..650da80 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -32,7 +32,7 @@ Downloader::Downloader(Config &conf) this->config = conf; if (config.bLoginHTTP && boost::filesystem::exists(config.sCookiePath)) if (!boost::filesystem::remove(config.sCookiePath)) - std::cout << "Failed to delete " << config.sCookiePath << std::endl; + std::cerr << "Failed to delete " << config.sCookiePath << std::endl; } Downloader::~Downloader() @@ -105,7 +105,7 @@ int Downloader::init() this->report_ofs.open(config.sReportFilePath); if (!this->report_ofs) { - std::cout << "Failed to create " << config.sReportFilePath << std::endl; + std::cerr << "Failed to create " << config.sReportFilePath << std::endl; return 1; } } @@ -122,16 +122,16 @@ int Downloader::login() char *pwd; std::string email; if (!isatty(STDIN_FILENO)) { - std::cout << "Unable to read email and password" << std::endl; + std::cerr << "Unable to read email and password" << std::endl; return 0; } - std::cout << "Email: "; + std::cerr << "Email: "; std::getline(std::cin,email); pwd = getpass("Password: "); std::string password = (std::string)pwd; if (email.empty() || password.empty()) { - std::cout << "Email and/or password empty" << std::endl; + std::cerr << "Email and/or password empty" << std::endl; return 0; } else @@ -141,12 +141,12 @@ int Downloader::login() { if (!HTTP_Login(email, password)) { - std::cout << "HTTP: Login failed" << std::endl; + std::cerr << "HTTP: Login failed" << std::endl; return 0; } else { - std::cout << "HTTP: Login successful" << std::endl; + std::cerr << "HTTP: Login successful" << std::endl; if (!config.bLoginAPI) return 1; } @@ -156,12 +156,12 @@ int Downloader::login() { if (!gogAPI->login(email, password)) { - std::cout << "API: Login failed" << std::endl; + std::cerr << "API: Login failed" << std::endl; return 0; } else { - std::cout << "API: Login successful" << std::endl; + std::cerr << "API: Login successful" << std::endl; config.sToken = gogAPI->getToken(); config.sSecret = gogAPI->getSecret(); return 1; @@ -211,32 +211,48 @@ void Downloader::getGameList() */ int Downloader::getGameDetails() { + // Set default game specific directory options to values from config + gameSpecificDirectoryConfig dirConfDefault; + dirConfDefault.sDirectory = config.sDirectory; + dirConfDefault.bSubDirectories = config.bSubDirectories; + dirConfDefault.sGameSubdir = config.sGameSubdir; + dirConfDefault.sInstallersSubdir = config.sInstallersSubdir; + dirConfDefault.sExtrasSubdir = config.sExtrasSubdir; + dirConfDefault.sLanguagePackSubdir = config.sLanguagePackSubdir; + dirConfDefault.sDLCSubdir = config.sDLCSubdir; + dirConfDefault.sPatchesSubdir = config.sPatchesSubdir; + if (config.bUseCache && !config.bUpdateCache) { // GameRegex filter alias for all games if (config.sGameRegex == "all") config.sGameRegex = ".*"; else if (config.sGameRegex == "free") - std::cout << "Warning: regex alias \"free\" doesn't work with cached details" << std::endl; + std::cerr << "Warning: regex alias \"free\" doesn't work with cached details" << std::endl; int result = this->loadGameDetailsCache(); if (result == 0) { for (unsigned int i = 0; i < this->games.size(); ++i) - this->games[i].makeFilepaths(config); + { + gameSpecificConfig conf; + conf.dirConf = dirConfDefault; + Util::getGameSpecificConfig(games[i].gamename, &conf); + this->games[i].makeFilepaths(conf.dirConf); + } return 0; } else { if (result == 1) { - std::cout << "Cache doesn't exist." << std::endl; - std::cout << "Create cache with --update-cache" << std::endl; + std::cerr << "Cache doesn't exist." << std::endl; + std::cerr << "Create cache with --update-cache" << std::endl; } else if (result == 3) { - std::cout << "Cache is too old." << std::endl; - std::cout << "Update cache with --update-cache or use bigger --cache-valid" << std::endl; + std::cerr << "Cache is too old." << std::endl; + std::cerr << "Update cache with --update-cache or use bigger --cache-valid" << std::endl; } return 1; } @@ -246,7 +262,7 @@ int Downloader::getGameDetails() int updated = 0; for (unsigned int i = 0; i < gameItems.size(); ++i) { - std::cout << "Getting game info " << i+1 << " / " << gameItems.size() << "\r" << std::flush; + std::cerr << "Getting game info " << i+1 << " / " << gameItems.size() << "\r" << std::flush; bool bHasDLC = !gameItems[i].dlcnames.empty(); gameSpecificConfig conf; @@ -254,21 +270,58 @@ int Downloader::getGameDetails() conf.bIgnoreDLCCount = false; conf.iInstallerLanguage = config.iInstallerLanguage; conf.iInstallerPlatform = config.iInstallerPlatform; + conf.dirConf = dirConfDefault; + conf.vLanguagePriority = config.vLanguagePriority; + conf.vPlatformPriority = config.vPlatformPriority; if (!config.bUpdateCache) // Disable game specific config files for cache update { - if (Util::getGameSpecificConfig(gameItems[i].name, &conf) > 0) - std::cout << std::endl << gameItems[i].name << " - Language: " << conf.iInstallerLanguage << ", Platform: " << conf.iInstallerPlatform << ", DLC: " << (conf.bDLC ? "true" : "false") << ", Ignore DLC count: " << (conf.bIgnoreDLCCount ? "true" : "false") << std::endl; + int iOptionsOverridden = Util::getGameSpecificConfig(gameItems[i].name, &conf); + if (iOptionsOverridden > 0) + { + std::cerr << std::endl << gameItems[i].name << " - " << iOptionsOverridden << " options overridden with game specific options" << std::endl; + if (config.bVerbose) + { + if (conf.bIgnoreDLCCount) + std::cerr << "\tIgnore DLC count" << std::endl; + if (conf.bDLC != config.bDLC) + std::cerr << "\tDLC: " << (conf.bDLC ? "true" : "false") << std::endl; + if (conf.iInstallerLanguage != config.iInstallerLanguage) + std::cerr << "\tLanguage: " << Util::getOptionNameString(conf.iInstallerLanguage, GlobalConstants::LANGUAGES) << std::endl; + if (conf.vLanguagePriority != config.vLanguagePriority) + { + std::cerr << "\tLanguage priority:" << std::endl; + for (unsigned int j = 0; j < conf.vLanguagePriority.size(); ++j) + { + std::cerr << "\t " << j << ": " << Util::getOptionNameString(conf.vLanguagePriority[j], GlobalConstants::LANGUAGES) << std::endl; + } + } + if (conf.iInstallerPlatform != config.iInstallerPlatform) + std::cerr << "\tPlatform: " << Util::getOptionNameString(conf.iInstallerPlatform, GlobalConstants::PLATFORMS) << std::endl; + if (conf.vPlatformPriority != config.vPlatformPriority) + { + std::cerr << "\tPlatform priority:" << std::endl; + for (unsigned int j = 0; j < conf.vPlatformPriority.size(); ++j) + { + std::cerr << "\t " << j << ": " << Util::getOptionNameString(conf.vPlatformPriority[j], GlobalConstants::PLATFORMS) << std::endl; + } + } + } + } } game = gogAPI->getGameDetails(gameItems[i].name, conf.iInstallerPlatform, conf.iInstallerLanguage, config.bDuplicateHandler); if (!gogAPI->getError()) { - game.filterWithPriorities(config); + game.filterWithPriorities(conf); Json::Value gameDetailsJSON; + if (!gameItems[i].gamedetailsjson.empty()) + gameDetailsJSON = gameItems[i].gamedetailsjson; + if (game.extras.empty() && config.bExtras) // Try to get extras from account page if API didn't return any extras { - gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id); + if (gameDetailsJSON.empty()) + gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id); game.extras = this->getExtrasFromJSON(gameDetailsJSON, gameItems[i].name); } if (config.bSaveSerials) @@ -277,6 +330,12 @@ int Downloader::getGameDetails() gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id); game.serials = this->getSerialsFromJSON(gameDetailsJSON); } + if (config.bSaveChangelogs) + { + if (gameDetailsJSON.empty()) + gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id); + game.changelog = this->getChangelogFromJSON(gameDetailsJSON); + } // Ignore DLC count and try to get DLCs from JSON if (game.dlcs.empty() && !bHasDLC && conf.bDLC && conf.bIgnoreDLCCount) @@ -294,7 +353,7 @@ int Downloader::getGameDetails() { gameDetails dlc; dlc = gogAPI->getGameDetails(gameItems[i].dlcnames[j], conf.iInstallerPlatform, conf.iInstallerLanguage, config.bDuplicateHandler); - dlc.filterWithPriorities(config); + dlc.filterWithPriorities(conf); if (dlc.extras.empty() && config.bExtras) // Try to get extras from account page if API didn't return any extras { if (gameDetailsJSON.empty()) @@ -342,11 +401,36 @@ int Downloader::getGameDetails() } } + if (config.bSaveChangelogs) + { + if (gameDetailsJSON.empty()) + gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id); + + // Make sure we save changelog for the right DLC + for (unsigned int k = 0; k < gameDetailsJSON["dlcs"].size(); ++k) + { + std::vector<std::string> urls; + if (gameDetailsJSON["dlcs"][k].isMember("changelog") && gameDetailsJSON["dlcs"][k].isMember("downloads")) + { + // Assuming that only DLC with installers can have changelog + Util::getDownloaderUrlsFromJSON(gameDetailsJSON["dlcs"][k]["downloads"], urls); + } + + if (!urls.empty()) + { + if (urls[0].find("/" + gameItems[i].dlcnames[j] + "/") != std::string::npos) + { + dlc.changelog = this->getChangelogFromJSON(gameDetailsJSON["dlcs"][k]); + } + } + } + } + game.dlcs.push_back(dlc); } } - game.makeFilepaths(config); + game.makeFilepaths(conf.dirConf); if (!config.bUpdateCheck) games.push_back(game); @@ -363,19 +447,19 @@ int Downloader::getGameDetails() } if (updated >= gogAPI->user.notifications_games) { // Gone through all updated games. No need to go through the rest. - std::cout << std::endl << "Got info for all updated games. Moving on..." << std::endl; + std::cerr << std::endl << "Got info for all updated games. Moving on..." << std::endl; break; } } } else { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } } - std::cout << std::endl; + std::cerr << std::endl; return 0; } @@ -406,7 +490,7 @@ void Downloader::listGames() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -432,7 +516,7 @@ void Downloader::listGames() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -453,7 +537,7 @@ void Downloader::listGames() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -478,7 +562,7 @@ void Downloader::listGames() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -506,7 +590,7 @@ void Downloader::listGames() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -523,7 +607,7 @@ void Downloader::listGames() std::string filepath = games[i].dlcs[j].patches[k].getFilepath(); if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -539,7 +623,7 @@ void Downloader::listGames() std::string filepath = games[i].dlcs[j].extras[k].getFilepath(); if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -582,7 +666,7 @@ void Downloader::repair() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -593,7 +677,7 @@ void Downloader::repair() XML = gogAPI->getXML(games[i].gamename, games[i].installers[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -606,7 +690,7 @@ void Downloader::repair() std::string url = gogAPI->getInstallerLink(games[i].gamename, games[i].installers[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -626,14 +710,14 @@ void Downloader::repair() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } std::string url = gogAPI->getExtraLink(games[i].gamename, games[i].extras[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -652,7 +736,7 @@ void Downloader::repair() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -663,7 +747,7 @@ void Downloader::repair() XML = gogAPI->getXML(games[i].gamename, games[i].patches[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); } } @@ -671,7 +755,7 @@ void Downloader::repair() std::string url = gogAPI->getPatchLink(games[i].gamename, games[i].patches[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -690,14 +774,14 @@ void Downloader::repair() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } std::string url = gogAPI->getLanguagePackLink(games[i].gamename, games[i].languagepacks[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -718,7 +802,7 @@ void Downloader::repair() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -729,7 +813,7 @@ void Downloader::repair() XML = gogAPI->getXML(games[i].dlcs[j].gamename, games[i].dlcs[j].installers[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -742,7 +826,7 @@ void Downloader::repair() std::string url = gogAPI->getInstallerLink(games[i].dlcs[j].gamename, games[i].dlcs[j].installers[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -759,7 +843,7 @@ void Downloader::repair() std::string filepath = games[i].dlcs[j].patches[k].getFilepath(); if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -770,7 +854,7 @@ void Downloader::repair() XML = gogAPI->getXML(games[i].dlcs[j].gamename, games[i].dlcs[j].patches[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); } } @@ -778,7 +862,7 @@ void Downloader::repair() std::string url = gogAPI->getPatchLink(games[i].dlcs[j].gamename, games[i].dlcs[j].patches[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -794,14 +878,14 @@ void Downloader::repair() std::string filepath = games[i].dlcs[j].extras[k].getFilepath(); if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } std::string url = gogAPI->getExtraLink(games[i].dlcs[j].gamename, games[i].dlcs[j].extras[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -828,6 +912,12 @@ void Downloader::download() this->saveSerials(games[i].serials, filepath); } + if (config.bSaveChangelogs && !games[i].changelog.empty()) + { + std::string filepath = games[i].getChangelogFilepath(); + this->saveChangelog(games[i].changelog, filepath); + } + // Download covers if (config.bCover && !config.bUpdateCheck) { @@ -858,7 +948,7 @@ void Downloader::download() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -866,7 +956,7 @@ void Downloader::download() std::string url = gogAPI->getInstallerLink(games[i].gamename, games[i].installers[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -880,7 +970,7 @@ void Downloader::download() XML = gogAPI->getXML(games[i].gamename, games[i].installers[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); } } @@ -901,7 +991,7 @@ void Downloader::download() std::string url = gogAPI->getExtraLink(games[i].gamename, games[i].extras[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -910,7 +1000,7 @@ void Downloader::download() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -922,7 +1012,7 @@ void Downloader::download() std::cout << filepath << std::endl; CURLcode result = this->downloadFile(url, filepath); std::cout << std::endl; - if (result==CURLE_OK && config.sXMLFile == "automatic") + if (result==CURLE_OK && config.bAutomaticXMLCreation) { std::cout << "Starting automatic XML creation" << std::endl; std::string xml_dir = config.sXMLDirectory + "/" + games[i].gamename; @@ -945,7 +1035,7 @@ void Downloader::download() std::string url = gogAPI->getPatchLink(games[i].gamename, games[i].patches[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -954,7 +1044,7 @@ void Downloader::download() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -967,7 +1057,7 @@ void Downloader::download() XML = gogAPI->getXML(games[i].gamename, games[i].patches[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); } } @@ -988,7 +1078,7 @@ void Downloader::download() std::string url = gogAPI->getLanguagePackLink(games[i].gamename, games[i].languagepacks[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -997,7 +1087,7 @@ void Downloader::download() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -1010,7 +1100,7 @@ void Downloader::download() XML = gogAPI->getXML(games[i].gamename, games[i].languagepacks[j].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); } } @@ -1031,6 +1121,11 @@ void Downloader::download() std::string filepath = games[i].dlcs[j].getSerialsFilepath(); this->saveSerials(games[i].dlcs[j].serials, filepath); } + if (config.bSaveChangelogs && !games[i].dlcs[j].changelog.empty()) + { + std::string filepath = games[i].dlcs[j].getChangelogFilepath(); + this->saveChangelog(games[i].dlcs[j].changelog, filepath); + } if (config.bInstallers) { @@ -1040,7 +1135,7 @@ void Downloader::download() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -1048,7 +1143,7 @@ void Downloader::download() std::string url = gogAPI->getInstallerLink(games[i].dlcs[j].gamename, games[i].dlcs[j].installers[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -1062,7 +1157,7 @@ void Downloader::download() XML = gogAPI->getXML(games[i].dlcs[j].gamename, games[i].dlcs[j].installers[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); } } @@ -1082,7 +1177,7 @@ void Downloader::download() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -1090,7 +1185,7 @@ void Downloader::download() std::string url = gogAPI->getPatchLink(games[i].dlcs[j].gamename, games[i].dlcs[j].patches[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -1104,7 +1199,7 @@ void Downloader::download() XML = gogAPI->getXML(games[i].dlcs[j].gamename, games[i].dlcs[j].patches[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); } } @@ -1124,7 +1219,7 @@ void Downloader::download() if (config.blacklist.isBlacklisted(filepath)) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped blacklisted file " << filepath << std::endl; continue; } @@ -1132,7 +1227,7 @@ void Downloader::download() std::string url = gogAPI->getExtraLink(games[i].dlcs[j].gamename, games[i].dlcs[j].extras[k].id); if (gogAPI->getError()) { - std::cout << gogAPI->getErrorMessage() << std::endl; + std::cerr << gogAPI->getErrorMessage() << std::endl; gogAPI->clearError(); continue; } @@ -1144,7 +1239,7 @@ void Downloader::download() std::cout << "Dowloading: " << games[i].dlcs[j].extras[k].name << std::endl; CURLcode result = this->downloadFile(url, filepath); std::cout << std::endl; - if (result==CURLE_OK && config.sXMLFile == "automatic") + if (result==CURLE_OK && config.bAutomaticXMLCreation) { std::cout << "Starting automatic XML creation" << std::endl; std::string xml_dir = config.sXMLDirectory + "/" + games[i].dlcs[j].gamename; @@ -1210,7 +1305,7 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil { if (!boost::filesystem::is_directory(path)) { - std::cout << path << " is not directory" << std::endl; + std::cerr << path << " is not directory" << std::endl; return res; } } @@ -1218,7 +1313,7 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil { if (!boost::filesystem::create_directories(path)) { - std::cout << "Failed to create directory: " << path << std::endl; + std::cerr << "Failed to create directory: " << path << std::endl; return res; } } @@ -1240,22 +1335,22 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil } else { - std::cout << "Failed to reopen " << filepath << std::endl; + std::cerr << "Failed to reopen " << filepath << std::endl; return res; } } else { // File exists but is not the same version fclose(outfile); - std::cout << "Remote file is different, renaming local file" << std::endl; + std::cerr << "Remote file is different, renaming local file" << std::endl; std::string date_old = "." + bptime::to_iso_string(bptime::second_clock::local_time()) + ".old"; boost::filesystem::path new_name = filepath + date_old; // Rename old file by appending date and ".old" to filename boost::system::error_code ec; boost::filesystem::rename(pathname, new_name, ec); // Rename the file if (ec) { - std::cout << "Failed to rename " << filepath << " to " << new_name.string() << std::endl; - std::cout << "Skipping file" << std::endl; + std::cerr << "Failed to rename " << filepath << " to " << new_name.string() << std::endl; + std::cerr << "Skipping file" << std::endl; return res; } else @@ -1268,7 +1363,7 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil } else { - std::cout << "Failed to create " << filepath << std::endl; + std::cerr << "Failed to create " << filepath << std::endl; return res; } } @@ -1284,7 +1379,7 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil } else { - std::cout << "Failed to create " << filepath << std::endl; + std::cerr << "Failed to create " << filepath << std::endl; return res; } } @@ -1300,14 +1395,14 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil { if (!boost::filesystem::is_directory(path)) { - std::cout << path << " is not directory" << std::endl; + std::cerr << path << " is not directory" << std::endl; } } else { if (!boost::filesystem::create_directories(path)) { - std::cout << "Failed to create directory: " << path << std::endl; + std::cerr << "Failed to create directory: " << path << std::endl; } } std::ofstream ofs(local_xml_file.string().c_str()); @@ -1318,7 +1413,7 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil } else { - std::cout << "Can't create " << local_xml_file.string() << std::endl; + std::cerr << "Can't create " << local_xml_file.string() << std::endl; } } } @@ -1335,7 +1430,7 @@ CURLcode Downloader::downloadFile(const std::string& url, const std::string& fil boost::filesystem::path path = filepath; if (boost::filesystem::exists(path)) if (!boost::filesystem::remove(path)) - std::cout << "Failed to delete " << path << std::endl; + std::cerr << "Failed to delete " << path << std::endl; } if (config.bReport) @@ -1462,7 +1557,7 @@ int Downloader::repairFile(const std::string& url, const std::string& filepath, { bLocalXMLExists = boost::filesystem::exists(xml_file); // Check to see if downloadFile saved XML data - if (config.sXMLFile == "automatic" && !bLocalXMLExists) + if (config.bAutomaticXMLCreation && !bLocalXMLExists) { std::cout << "Starting automatic XML creation" << std::endl; Util::createXML(filepath, config.iChunkSize, xml_directory); @@ -1503,7 +1598,7 @@ int Downloader::repairFile(const std::string& url, const std::string& filepath, std::cout << std::endl; if (result == CURLE_OK) { - if (config.sXMLFile == "automatic" && bParsingFailed) + if (config.bAutomaticXMLCreation && bParsingFailed) { std::cout << "Starting automatic XML creation" << std::endl; Util::createXML(filepath, config.iChunkSize, xml_directory); @@ -2040,6 +2135,84 @@ int Downloader::HTTP_Login(const std::string& email, const std::string& password char *redirect_url; curl_easy_getinfo(curlhandle, CURLINFO_REDIRECT_URL, &redirect_url); + // Handle two step authorization + if (std::string(redirect_url).find("two_step") != std::string::npos) + { + std::string security_code, tagname_two_step_send, tagname_two_step_auth_letter_1, tagname_two_step_auth_letter_2, tagname_two_step_auth_letter_3, tagname_two_step_auth_letter_4, tagname_two_step_token, token_two_step; + std::string two_step_html = this->getResponse(redirect_url); + redirect_url = NULL; + + tree<htmlcxx::HTML::Node> two_step_dom = parser.parseTree(two_step_html); + tree<htmlcxx::HTML::Node>::iterator two_step_it = two_step_dom.begin(); + tree<htmlcxx::HTML::Node>::iterator two_step_it_end = two_step_dom.end(); + for (; two_step_it != two_step_it_end; ++two_step_it) + { + if (two_step_it->tagName()=="input") + { + two_step_it->parseAttributes(); + std::string id_two_step = two_step_it->attribute("id").second; + if (id_two_step == "second_step_authentication_token_letter_1") + { + tagname_two_step_auth_letter_1 = two_step_it->attribute("name").second; + } + else if (id_two_step == "second_step_authentication_token_letter_2") + { + tagname_two_step_auth_letter_2 = two_step_it->attribute("name").second; + } + else if (id_two_step == "second_step_authentication_token_letter_3") + { + tagname_two_step_auth_letter_3 = two_step_it->attribute("name").second; + } + else if (id_two_step == "second_step_authentication_token_letter_4") + { + tagname_two_step_auth_letter_4 = two_step_it->attribute("name").second; + } + else if (id_two_step == "second_step_authentication__token") + { + token_two_step = two_step_it->attribute("value").second; // two step token + tagname_two_step_token = two_step_it->attribute("name").second; + } + } + else if (two_step_it->tagName()=="button") + { + two_step_it->parseAttributes(); + std::string id_two_step = two_step_it->attribute("id").second; + if (id_two_step == "second_step_authentication_send") + { + tagname_two_step_send = two_step_it->attribute("name").second; + } + } + } + std::cerr << "Security code: "; + std::getline(std::cin,security_code); + if (security_code.size() != 4) + { + std::cerr << "Security code must be 4 characters long" << std::endl; + exit(1); + } + postdata = (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_1.c_str(), tagname_two_step_auth_letter_1.size()) + "=" + security_code[0] + + "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_2.c_str(), tagname_two_step_auth_letter_2.size()) + "=" + security_code[1] + + "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_3.c_str(), tagname_two_step_auth_letter_3.size()) + "=" + security_code[2] + + "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_auth_letter_4.c_str(), tagname_two_step_auth_letter_4.size()) + "=" + security_code[3] + + "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_send.c_str(), tagname_two_step_send.size()) + "=" + + "&" + (std::string)curl_easy_escape(curlhandle, tagname_two_step_token.c_str(), tagname_two_step_token.size()) + "=" + (std::string)curl_easy_escape(curlhandle, token_two_step.c_str(), token_two_step.size()); + + curl_easy_setopt(curlhandle, CURLOPT_URL, "https://login.gog.com/login/two_step"); + curl_easy_setopt(curlhandle, CURLOPT_POST, 1); + curl_easy_setopt(curlhandle, CURLOPT_POSTFIELDS, postdata.c_str()); + curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, Downloader::writeMemoryCallback); + curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, &memory); + curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(curlhandle, CURLOPT_MAXREDIRS, 0); + curl_easy_setopt(curlhandle, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); + + // Don't follow to redirect location because it doesn't work properly. Must clean up the redirect url first. + curl_easy_setopt(curlhandle, CURLOPT_FOLLOWLOCATION, 0); + result = curl_easy_perform(curlhandle); + memory.str(std::string()); + curl_easy_getinfo(curlhandle, CURLINFO_REDIRECT_URL, &redirect_url); + } + curl_easy_setopt(curlhandle, CURLOPT_URL, redirect_url); curl_easy_setopt(curlhandle, CURLOPT_HTTPGET, 1); curl_easy_setopt(curlhandle, CURLOPT_MAXREDIRS, -1); @@ -2213,24 +2386,9 @@ std::vector<gameItem> Downloader::getGames() if (bDownloadDLCInfo) { - std::string gameinfo = this->getResponse("https://www.gog.com/account/gameDetails/" + game.id + ".json"); - Json::Value info; - if (!jsonparser->parse(gameinfo, info)) - { - #ifdef DEBUG - std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << gameinfo << std::endl; - #endif - std::cout << jsonparser->getFormattedErrorMessages(); - delete jsonparser; - exit(1); - } - else - { - #ifdef DEBUG - std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << info << std::endl; - #endif - game.dlcnames = Util::getDLCNamesFromJSON(info["dlcs"]); - } + game.gamedetailsjson = this->getGameDetailsJSON(game.id); + if (!game.gamedetailsjson.empty()) + game.dlcnames = Util::getDLCNamesFromJSON(game.gamedetailsjson["dlcs"]); } } games.push_back(game); @@ -2419,6 +2577,38 @@ std::string Downloader::getSerialsFromJSON(const Json::Value& json) return serials.str(); } +std::string Downloader::getChangelogFromJSON(const Json::Value& json) +{ + std::string changelog; + std::string title = "Changelog"; + + if (!json.isMember("changelog")) + return std::string(); + + changelog = json["changelog"].asString(); + + if (changelog.empty()) + return std::string(); + + if (json.isMember("title")) + title = title + ": " + json["title"].asString(); + + changelog = "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"UTF-8\">\n<title>" + title + "</title>\n</head>\n<body>" + changelog + "</body>\n</html>"; + + return changelog; +} + +// Linear search. Good thing computers are fast and lists are small. +static int isPresent(std::vector<gameFile>& list, const boost::filesystem::path& path, Blacklist& blacklist) +{ + if(blacklist.isBlacklisted(path.native())) + return false; + for (unsigned int k = 0; k < list.size(); ++k) + if (list[k].getFilepath() == path.native()) + return true; + return false; +} + void Downloader::checkOrphans() { // Always check everything when checking for orphaned files @@ -2433,7 +2623,7 @@ void Downloader::checkOrphans() std::vector<std::string> orphans; for (unsigned int i = 0; i < games.size(); ++i) { - std::cout << "Checking for orphaned files " << i+1 << " / " << games.size() << "\r" << std::flush; + std::cerr << "Checking for orphaned files " << i+1 << " / " << games.size() << "\r" << std::flush; std::vector<boost::filesystem::path> filepath_vector; try @@ -2481,9 +2671,9 @@ void Downloader::checkOrphans() if (boost::filesystem::is_regular_file(dir_iter->status())) { std::string filepath = dir_iter->path().string(); - if (config.blacklist.isBlacklisted(filepath.substr(pathlen))) { + if (config.ignorelist.isBlacklisted(filepath.substr(pathlen))) { if (config.bVerbose) - std::cout << "skipped blacklisted file " << filepath << std::endl; + std::cerr << "skipped ignorelisted file " << filepath << std::endl; } else { boost::regex expression(config.sOrphanRegex); // Limit to files matching the regex boost::match_results<std::string::const_iterator> what; @@ -2496,7 +2686,7 @@ void Downloader::checkOrphans() } } else - std::cout << paths[j] << " does not exist" << std::endl; + std::cerr << paths[j] << " does not exist" << std::endl; } } catch (const boost::filesystem::filesystem_error& ex) @@ -2508,80 +2698,20 @@ void Downloader::checkOrphans() { for (unsigned int j = 0; j < filepath_vector.size(); ++j) { - bool bFoundFile = false; // Assume that the file is orphaned + bool bFoundFile = isPresent(games[i].installers, filepath_vector[j], config.blacklist) + || isPresent(games[i].extras, filepath_vector[j], config.blacklist) + || isPresent(games[i].patches, filepath_vector[j], config.blacklist) + || isPresent(games[i].languagepacks, filepath_vector[j], config.blacklist); - // Check installers - for (unsigned int k = 0; k < games[i].installers.size(); ++k) - { - if (games[i].installers[k].path.find(filepath_vector[j].filename().string()) != std::string::npos) - { - bFoundFile = true; - break; - } - } - if (!bFoundFile) - { // Check extras - for (unsigned int k = 0; k < games[i].extras.size(); ++k) - { - if (games[i].extras[k].path.find(filepath_vector[j].filename().string()) != std::string::npos) - { - bFoundFile = true; - break; - } - } - } - if (!bFoundFile) - { // Check patches - for (unsigned int k = 0; k < games[i].patches.size(); ++k) - { - if (games[i].patches[k].path.find(filepath_vector[j].filename().string()) != std::string::npos) - { - bFoundFile = true; - break; - } - } - } - if (!bFoundFile) - { // Check language packs - for (unsigned int k = 0; k < games[i].languagepacks.size(); ++k) - { - if (games[i].languagepacks[k].path.find(filepath_vector[j].filename().string()) != std::string::npos) - { - bFoundFile = true; - break; - } - } - } if (!bFoundFile) { // Check dlcs for (unsigned int k = 0; k < games[i].dlcs.size(); ++k) { - for (unsigned int index = 0; index < games[i].dlcs[k].installers.size(); ++index) - { - if (games[i].dlcs[k].installers[index].path.find(filepath_vector[j].filename().string()) != std::string::npos) - { - bFoundFile = true; - break; - } - } - if (bFoundFile) break; - for (unsigned int index = 0; index < games[i].dlcs[k].patches.size(); ++index) - { - if (games[i].dlcs[k].patches[index].path.find(filepath_vector[j].filename().string()) != std::string::npos) - { - bFoundFile = true; - break; - } - } - for (unsigned int index = 0; index < games[i].dlcs[k].extras.size(); ++index) - { - if (games[i].dlcs[k].extras[index].path.find(filepath_vector[j].filename().string()) != std::string::npos) - { - bFoundFile = true; - break; - } - } - if (bFoundFile) break; + bFoundFile = isPresent(games[i].dlcs[k].installers, filepath_vector[j], config.blacklist) + || isPresent(games[i].dlcs[k].extras, filepath_vector[j], config.blacklist) + || isPresent(games[i].dlcs[k].patches, filepath_vector[j], config.blacklist); + if(bFoundFile) + break; } } if (!bFoundFile) @@ -2620,6 +2750,8 @@ void Downloader::checkStatus() { boost::filesystem::path filepath = games[i].installers[j].getFilepath(); + if (config.blacklist.isBlacklisted(filepath.native())) + continue; std::string remoteHash; std::string localHash; bool bHashOK = true; // assume hash OK @@ -2682,6 +2814,8 @@ void Downloader::checkStatus() { boost::filesystem::path filepath = games[i].extras[j].getFilepath(); + if (config.blacklist.isBlacklisted(filepath.native())) + continue; std::string localHash = this->getLocalFileHash(filepath.string(), games[i].gamename); uintmax_t filesize; @@ -2703,6 +2837,8 @@ void Downloader::checkStatus() { boost::filesystem::path filepath = games[i].patches[j].getFilepath(); + if (config.blacklist.isBlacklisted(filepath.native())) + continue; std::string localHash = this->getLocalFileHash(filepath.string(), games[i].gamename); uintmax_t filesize; @@ -2724,6 +2860,8 @@ void Downloader::checkStatus() { boost::filesystem::path filepath = games[i].languagepacks[j].getFilepath(); + if (config.blacklist.isBlacklisted(filepath.native())) + continue; std::string localHash = this->getLocalFileHash(filepath.string(), games[i].gamename); uintmax_t filesize; @@ -2749,6 +2887,8 @@ void Downloader::checkStatus() { boost::filesystem::path filepath = games[i].dlcs[j].installers[k].getFilepath(); + if (config.blacklist.isBlacklisted(filepath.native())) + continue; std::string remoteHash; std::string localHash; bool bHashOK = true; // assume hash OK @@ -2811,6 +2951,8 @@ void Downloader::checkStatus() { boost::filesystem::path filepath = games[i].dlcs[j].patches[k].getFilepath(); + if (config.blacklist.isBlacklisted(filepath.native())) + continue; std::string localHash = this->getLocalFileHash(filepath.string(), games[i].dlcs[j].gamename); uintmax_t filesize; @@ -2832,6 +2974,8 @@ void Downloader::checkStatus() { boost::filesystem::path filepath = games[i].dlcs[j].extras[k].getFilepath(); + if (config.blacklist.isBlacklisted(filepath.native())) + continue; std::string localHash = this->getLocalFileHash(filepath.string(), games[i].dlcs[j].gamename); uintmax_t filesize; @@ -2863,23 +3007,35 @@ std::string Downloader::getLocalFileHash(const std::string& filepath, const std: else local_xml_file = config.sXMLDirectory + "/" + path.filename().string() + ".xml"; + if (config.bAutomaticXMLCreation && !boost::filesystem::exists(local_xml_file) && boost::filesystem::exists(path)) + { + std::string xml_directory = config.sXMLDirectory + "/" + gamename; + Util::createXML(filepath, config.iChunkSize, xml_directory); + } + if (boost::filesystem::exists(local_xml_file)) { TiXmlDocument local_xml; local_xml.LoadFile(local_xml_file.string()); TiXmlNode *fileNodeLocal = local_xml.FirstChild("file"); + if (!fileNodeLocal && config.bAutomaticXMLCreation) + { + std::string xml_directory = config.sXMLDirectory + "/" + gamename; + Util::createXML(filepath, config.iChunkSize, xml_directory); + local_xml.LoadFile(local_xml_file.string()); + fileNodeLocal = local_xml.FirstChild("file"); + } if (fileNodeLocal) { TiXmlElement *fileElemLocal = fileNodeLocal->ToElement(); localHash = fileElemLocal->Attribute("md5"); + return localHash; } } - else + + if (boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)) { - if (boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)) - { - localHash = Util::getFileHash(path.string(), RHASH_MD5); - } + localHash = Util::getFileHash(path.string(), RHASH_MD5); } return localHash; } @@ -3028,6 +3184,7 @@ std::vector<gameDetails> Downloader::getGameDetailsFromJsonNode(Json::Value root game.title = gameDetailsNode["title"].asString(); game.icon = gameDetailsNode["icon"].asString(); game.serials = gameDetailsNode["serials"].asString(); + game.changelog = gameDetailsNode["changelog"].asString(); // Make a vector of valid node names to make things easier std::vector<std::string> nodes; @@ -3041,8 +3198,10 @@ std::vector<gameDetails> Downloader::getGameDetailsFromJsonNode(Json::Value root conf.bDLC = config.bDLC; conf.iInstallerLanguage = config.iInstallerLanguage; conf.iInstallerPlatform = config.iInstallerPlatform; + conf.vLanguagePriority = config.vLanguagePriority; + conf.vPlatformPriority = config.vPlatformPriority; if (Util::getGameSpecificConfig(game.gamename, &conf) > 0) - std::cout << game.gamename << " - Language: " << conf.iInstallerLanguage << ", Platform: " << conf.iInstallerPlatform << ", DLC: " << (conf.bDLC ? "true" : "false") << std::endl; + std::cerr << game.gamename << " - Language: " << conf.iInstallerLanguage << ", Platform: " << conf.iInstallerPlatform << ", DLC: " << (conf.bDLC ? "true" : "false") << std::endl; for (unsigned int j = 0; j < nodes.size(); ++j) { @@ -3090,7 +3249,7 @@ std::vector<gameDetails> Downloader::getGameDetailsFromJsonNode(Json::Value root } if (!game.extras.empty() || !game.installers.empty() || !game.patches.empty() || !game.languagepacks.empty() || !game.dlcs.empty()) { - game.filterWithPriorities(config); + game.filterWithPriorities(conf); details.push_back(game); } } @@ -3110,6 +3269,7 @@ void Downloader::updateCache() config.iInstallerPlatform = Util::getOptionValue("all", GlobalConstants::PLATFORMS); config.vLanguagePriority.clear(); config.vPlatformPriority.clear(); + config.sIgnoreDLCCountRegex = ".*"; // Ignore DLC count for all games because GOG doesn't report DLC count correctly this->getGameList(); this->getGameDetails(); @@ -3165,6 +3325,47 @@ void Downloader::saveSerials(const std::string& serials, const std::string& file return; } +// Save changelog to file +void Downloader::saveChangelog(const std::string& changelog, const std::string& filepath) +{ + // Get directory from filepath + boost::filesystem::path pathname = filepath; + std::string directory = pathname.parent_path().string(); + + // Check that directory exists and create subdirectories + boost::filesystem::path path = directory; + if (boost::filesystem::exists(path)) + { + if (!boost::filesystem::is_directory(path)) + { + std::cout << path << " is not directory" << std::endl; + return; + } + } + else + { + if (!boost::filesystem::create_directories(path)) + { + std::cout << "Failed to create directory: " << path << std::endl; + return; + } + } + + std::ofstream ofs(filepath); + if (ofs) + { + std::cout << "Saving changelog: " << filepath << std::endl; + ofs << changelog; + ofs.close(); + } + else + { + std::cout << "Failed to create file: " << filepath << std::endl; + } + + return; +} + void Downloader::downloadFileWithId(const std::string& fileid_string, const std::string& output_filepath) { size_t pos = fileid_string.find("/"); diff --git a/src/gamedetails.cpp b/src/gamedetails.cpp index 5e57777..2ac9067 100644 --- a/src/gamedetails.cpp +++ b/src/gamedetails.cpp @@ -1,5 +1,4 @@ #include "gamedetails.h" -#include "util.h" gameDetails::gameDetails() { @@ -11,7 +10,7 @@ gameDetails::~gameDetails() //dtor } -void gameDetails::filterWithPriorities(const Config& config) +void gameDetails::filterWithPriorities(const gameSpecificConfig& config) { if (config.vPlatformPriority.empty() && config.vLanguagePriority.empty()) return; @@ -21,7 +20,7 @@ void gameDetails::filterWithPriorities(const Config& config) filterListWithPriorities(languagepacks, config); } -void gameDetails::filterListWithPriorities(std::vector<gameFile>& list, const Config& config) +void gameDetails::filterListWithPriorities(std::vector<gameFile>& list, const gameSpecificConfig& config) { /* Compute the score of each item - we use a scoring mechanism and we keep all ties @@ -66,12 +65,13 @@ void gameDetails::filterListWithPriorities(std::vector<gameFile>& list, const Co } } -void gameDetails::makeFilepaths(const Config& config) +void gameDetails::makeFilepaths(const gameSpecificDirectoryConfig& config) { std::string filepath; std::string directory = config.sDirectory + "/" + config.sGameSubdir + "/"; std::string subdir; this->serialsFilepath = Util::makeFilepath(directory, "serials.txt", this->gamename, subdir, 0); + this->changelogFilepath = Util::makeFilepath(directory, "changelog_" + gamename + ".html", this->gamename, subdir, 0); for (unsigned int i = 0; i < this->installers.size(); ++i) { @@ -105,6 +105,7 @@ void gameDetails::makeFilepaths(const Config& config) { subdir = config.bSubDirectories ? config.sDLCSubdir + "/" + config.sInstallersSubdir : ""; this->dlcs[i].serialsFilepath = Util::makeFilepath(directory, "serials.txt", this->gamename, subdir, 0); + this->dlcs[i].changelogFilepath = Util::makeFilepath(directory, "changelog_" + this->dlcs[i].gamename + ".html", this->gamename, subdir, 0); for (unsigned int j = 0; j < this->dlcs[i].installers.size(); ++j) { subdir = config.bSubDirectories ? config.sDLCSubdir + "/" + config.sInstallersSubdir : ""; @@ -136,6 +137,7 @@ Json::Value gameDetails::getDetailsAsJson() json["title"] = this->title; json["icon"] = this->icon; json["serials"] = this->serials; + json["changelog"] = this->changelog; for (unsigned int i = 0; i < this->extras.size(); ++i) json["extras"].append(this->extras[i].getAsJson()); @@ -161,3 +163,8 @@ std::string gameDetails::getSerialsFilepath() { return this->serialsFilepath; } + +std::string gameDetails::getChangelogFilepath() +{ + return this->changelogFilepath; +} diff --git a/src/util.cpp b/src/util.cpp index fcfb93e..4b98c45 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -64,7 +64,7 @@ std::string Util::getFileHash(const std::string& filename, unsigned 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; + std::cerr << "LibRHash error: " << strerror(errno) << std::endl; else rhash_print_bytes(result, digest, rhash_get_digest_size(hash_id), RHPR_HEX); @@ -79,7 +79,7 @@ std::string Util::getChunkHash(unsigned char *chunk, uintmax_t chunk_size, unsig rhash_library_init(); int i = rhash_msg(hash_id, chunk, chunk_size, digest); if (i < 0) - std::cout << "LibRHash error: " << strerror(errno) << std::endl; + std::cerr << "LibRHash error: " << strerror(errno) << std::endl; else rhash_print_bytes(result, digest, rhash_get_digest_size(hash_id), RHPR_HEX); @@ -104,7 +104,7 @@ int Util::createXML(std::string filepath, uintmax_t chunk_size, std::string xml_ 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; + std::cerr << "Failed to create directory: " << path << std::endl; return res; } } @@ -115,7 +115,7 @@ int Util::createXML(std::string filepath, uintmax_t chunk_size, std::string xml_ filesize = ftell(infile); rewind(infile); } else { - std::cout << filepath << " doesn't exist" << std::endl; + std::cerr << filepath << " doesn't exist" << std::endl; return res; } @@ -159,13 +159,13 @@ int Util::createXML(std::string filepath, uintmax_t chunk_size, std::string xml_ unsigned char *chunk = (unsigned char *) malloc(chunk_size * sizeof(unsigned char *)); if (chunk == NULL) { - std::cout << "Memory error" << std::endl; + std::cerr << "Memory error" << std::endl; return res; } size = fread(chunk, 1, chunk_size, infile); if (size != chunk_size) { - std::cout << "Read error" << std::endl; + std::cerr << "Read error" << std::endl; free(chunk); return res; } @@ -204,7 +204,7 @@ int Util::createXML(std::string filepath, uintmax_t chunk_size, std::string xml_ fclose(xmlfile); res = 1; } else { - std::cout << "Can't create " << filenameXML << std::endl; + std::cerr << "Can't create " << filenameXML << std::endl; return res; } @@ -240,12 +240,22 @@ int Util::getGameSpecificConfig(std::string gamename, gameSpecificConfig* conf, { if (root.isMember("language")) { - conf->iInstallerLanguage = root["language"].asUInt(); + if (root["language"].isInt()) + conf->iInstallerLanguage = root["language"].asUInt(); + else + { + Util::parseOptionString(root["language"].asString(), conf->vLanguagePriority, conf->iInstallerLanguage, GlobalConstants::LANGUAGES); + } res++; } if (root.isMember("platform")) { - conf->iInstallerPlatform = root["platform"].asUInt(); + if (root["platform"].isInt()) + conf->iInstallerPlatform = root["platform"].asUInt(); + else + { + Util::parseOptionString(root["platform"].asString(), conf->vPlatformPriority, conf->iInstallerPlatform, GlobalConstants::PLATFORMS); + } res++; } if (root.isMember("dlc")) @@ -258,11 +268,51 @@ int Util::getGameSpecificConfig(std::string gamename, gameSpecificConfig* conf, conf->bIgnoreDLCCount = root["ignore-dlc-count"].asBool(); res++; } + if (root.isMember("subdirectories")) + { + conf->dirConf.bSubDirectories = root["subdirectories"].asBool(); + res++; + } + if (root.isMember("directory")) + { + conf->dirConf.sDirectory = root["directory"].asString(); + res++; + } + if (root.isMember("subdir-game")) + { + conf->dirConf.sGameSubdir = root["subdir-game"].asString(); + res++; + } + if (root.isMember("subdir-installers")) + { + conf->dirConf.sInstallersSubdir = root["subdir-installers"].asString(); + res++; + } + if (root.isMember("subdir-extras")) + { + conf->dirConf.sExtrasSubdir = root["subdir-extras"].asString(); + res++; + } + if (root.isMember("subdir-patches")) + { + conf->dirConf.sPatchesSubdir = root["subdir-patches"].asString(); + res++; + } + if (root.isMember("subdir-language-packs")) + { + conf->dirConf.sLanguagePackSubdir = root["subdir-language-packs"].asString(); + res++; + } + if (root.isMember("subdir-dlc")) + { + conf->dirConf.sDLCSubdir = root["subdir-dlc"].asString(); + res++; + } } else { - std::cout << "Failed to parse game specific config" << std::endl; - std::cout << jsonparser->getFormattedErrorMessages() << std::endl; + std::cerr << "Failed to parse game specific config " << filepath << std::endl; + std::cerr << jsonparser->getFormattedErrorMessages() << std::endl; } delete jsonparser; if (json) @@ -320,7 +370,7 @@ void Util::setFilePermissions(const boost::filesystem::path& path, const boost:: boost::filesystem::permissions(path, permissions, ec); if (ec) { - std::cout << "Failed to set file permissions for " << path.string() << std::endl; + std::cerr << "Failed to set file permissions for " << path.string() << std::endl; } } } @@ -471,3 +521,22 @@ std::string Util::getOptionNameString(const unsigned int& value, const std::vect } return str; } + +// Parse the options string +void Util::parseOptionString(const std::string &option_string, std::vector<unsigned int> &priority, unsigned int &type, const std::vector<GlobalConstants::optionsStruct>& options) +{ + type = 0; + priority.clear(); + std::vector<std::string> tokens_priority = Util::tokenize(option_string, ","); + for (std::vector<std::string>::iterator it_priority = tokens_priority.begin(); it_priority != tokens_priority.end(); it_priority++) + { + unsigned int value = 0; + std::vector<std::string> tokens_value = Util::tokenize(*it_priority, "+"); + for (std::vector<std::string>::iterator it_value = tokens_value.begin(); it_value != tokens_value.end(); it_value++) + { + value |= Util::getOptionValue(*it_value, options); + } + priority.push_back(value); + type |= value; + } +} diff --git a/version.sh b/version.sh deleted file mode 100644 index 113ebf6..0000000 --- a/version.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -version="LGOGDownloader `grep VERSION_NUMBER < main.cpp | head -n 1 | sed -e 's/.*\([0-9]\+\.[0-9]\+\).*/\1/'`" -if [ -e .git/HEAD ]; then - if git status | grep -q 'modified:'; then - version="${version}M" - fi - version="$version git `git rev-parse --short HEAD`" -fi -echo "$version" -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/lgogdownloader.git _______________________________________________ Pkg-games-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-games-commits

