[lgogdownloader] 01/04: Imported Upstream version 2.27
Stephen Kitt
skitt at moszumanska.debian.org
Fri Mar 18 12:03:08 UTC 2016
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 <steve at sk2.org>
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
More information about the Pkg-games-commits
mailing list