2013-03-15 22:46:16 +02:00
/* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law . You can redistribute it
* and / or modify it under the terms of the Do What The Fuck You Want
* To Public License , Version 2 , as published by Sam Hocevar . See
2013-03-24 23:56:57 +02:00
* http : //www.wtfpl.net/ for more details. */
2013-03-15 22:46:16 +02:00
# include "downloader.h"
# include "util.h"
2013-03-23 20:12:49 +02:00
# include "globalconstants.h"
2013-03-15 22:46:16 +02:00
# include <cstdio>
# include <cstdlib>
# include <ctime>
# include <iostream>
# include <sstream>
# include <unistd.h>
# include <fstream>
# include <iomanip>
# include <boost/filesystem.hpp>
# include <boost/regex.hpp>
# include <boost/date_time/posix_time/posix_time_types.hpp>
# include <tinyxml.h>
# include <jsoncpp/json/json.h>
# include <htmlcxx/html/ParserDom.h>
namespace bptime = boost : : posix_time ;
Downloader : : Downloader ( Config & conf )
{
this - > config = conf ;
}
Downloader : : ~ Downloader ( )
{
2014-02-13 11:16:31 +02:00
if ( config . bReport )
if ( this - > report_ofs )
this - > report_ofs . close ( ) ;
2013-03-15 22:46:16 +02:00
delete progressbar ;
delete gogAPI ;
curl_easy_cleanup ( curlhandle ) ;
curl_global_cleanup ( ) ;
}
/* Initialize the downloader
returns 0 if successful
returns 1 if failed
*/
int Downloader : : init ( )
{
this - > resume_position = 0 ;
2014-02-26 14:59:23 +02:00
this - > retries = 0 ;
2013-03-15 22:46:16 +02:00
2013-11-19 12:47:10 +02:00
// Initialize curl and set curl options
2013-03-15 22:46:16 +02:00
curl_global_init ( CURL_GLOBAL_ALL ) ;
curlhandle = curl_easy_init ( ) ;
curl_easy_setopt ( curlhandle , CURLOPT_FOLLOWLOCATION , 1 ) ;
curl_easy_setopt ( curlhandle , CURLOPT_USERAGENT , config . sVersionString . c_str ( ) ) ;
curl_easy_setopt ( curlhandle , CURLOPT_NOPROGRESS , 0 ) ;
2013-06-10 15:15:08 +03:00
curl_easy_setopt ( curlhandle , CURLOPT_CONNECTTIMEOUT , config . iTimeout ) ;
2013-03-15 22:46:16 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_PROGRESSDATA , this ) ;
curl_easy_setopt ( curlhandle , CURLOPT_FAILONERROR , true ) ;
curl_easy_setopt ( curlhandle , CURLOPT_COOKIEFILE , config . sCookiePath . c_str ( ) ) ;
curl_easy_setopt ( curlhandle , CURLOPT_COOKIEJAR , config . sCookiePath . c_str ( ) ) ;
2013-04-25 12:05:17 +03:00
curl_easy_setopt ( curlhandle , CURLOPT_SSL_VERIFYPEER , config . bVerifyPeer ) ;
2013-04-28 18:46:37 +03:00
curl_easy_setopt ( curlhandle , CURLOPT_VERBOSE , config . bVerbose ) ;
2013-06-08 00:20:55 +03:00
curl_easy_setopt ( curlhandle , CURLOPT_WRITEFUNCTION , Downloader : : writeData ) ;
curl_easy_setopt ( curlhandle , CURLOPT_READFUNCTION , Downloader : : readData ) ;
curl_easy_setopt ( curlhandle , CURLOPT_PROGRESSFUNCTION , Downloader : : progressCallback ) ;
2013-06-28 15:49:00 +03:00
curl_easy_setopt ( curlhandle , CURLOPT_MAX_RECV_SPEED_LARGE , config . iDownloadRate ) ;
2013-03-15 22:46:16 +02:00
2013-11-19 12:47:10 +02:00
// Create new API handle and set curl options for the API
2013-06-28 16:06:08 +03:00
gogAPI = new API ( config . sToken , config . sSecret ) ;
gogAPI - > curlSetOpt ( CURLOPT_VERBOSE , config . bVerbose ) ;
gogAPI - > curlSetOpt ( CURLOPT_SSL_VERIFYPEER , config . bVerifyPeer ) ;
gogAPI - > curlSetOpt ( CURLOPT_CONNECTTIMEOUT , config . iTimeout ) ;
2013-11-14 15:40:59 +02:00
progressbar = new ProgressBar ( config . bUnicode , config . bColor ) ;
2013-03-15 22:46:16 +02:00
2013-11-19 12:47:10 +02:00
bool bInitOK = gogAPI - > init ( ) ; // Initialize the API
2013-10-20 02:17:55 +03:00
if ( config . bLogin | | ! bInitOK )
2013-03-16 21:04:55 +02:00
return this - > login ( ) ;
2013-03-15 22:46:16 +02:00
2013-11-19 12:47:10 +02:00
if ( config . bCover & & config . bDownload & & ! config . bUpdateCheck )
2013-11-14 15:52:14 +02:00
coverXML = this - > getResponse ( " https://sites.google.com/site/gogdownloader/covers.xml " ) ;
2013-03-15 22:46:16 +02:00
2013-03-16 03:17:38 +02:00
if ( ! config . bUpdateCheck ) // updateCheck() calls getGameList() if needed
this - > getGameList ( ) ;
2013-03-15 22:46:16 +02:00
2014-02-13 11:16:31 +02:00
if ( config . bReport & & ( config . bDownload | | config . bRepair ) )
{
this - > report_ofs . open ( " lgogdownloader-report.log " ) ;
if ( ! this - > report_ofs )
{
std : : cout < < " Failed to create lgogdownloader-report.log " < < std : : endl ;
return 1 ;
}
}
2013-03-15 22:46:16 +02:00
return 0 ;
}
/* Login
returns 1 if login fails
returns 0 if successful
*/
int Downloader : : login ( )
{
char * pwd ;
std : : string email ;
std : : cout < < " 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 ;
return 1 ;
}
else
{
// Login to website
if ( ! HTTP_Login ( email , password ) )
{
std : : cout < < " HTTP: Login failed " < < std : : endl ;
return 1 ;
}
2013-03-16 21:16:58 +02:00
else
{
std : : cout < < " HTTP: Login successful " < < std : : endl ;
}
2013-03-15 22:46:16 +02:00
// Login to API
if ( ! gogAPI - > login ( email , password ) )
{
std : : cout < < " API: Login failed " < < std : : endl ;
return 1 ;
}
else
{
2013-03-16 21:16:58 +02:00
std : : cout < < " API: Login successful " < < std : : endl ;
2013-03-15 22:46:16 +02:00
// Save token and secret to config file
std : : ofstream ofs ( config . sConfigFilePath . c_str ( ) ) ;
if ( ofs )
{
2013-05-04 19:46:42 +03:00
ofs < < " token = " < < gogAPI - > getToken ( ) < < std : : endl < < " secret = " < < gogAPI - > getSecret ( ) < < std : : endl ;
2013-03-15 22:46:16 +02:00
ofs . close ( ) ;
return 0 ;
}
else
{
std : : cout < < " Failed to create config: " < < config . sConfigFilePath < < std : : endl ;
return 1 ;
}
}
}
}
void Downloader : : updateCheck ( )
{
2013-11-26 12:31:20 +02:00
std : : cout < < " New forum replies: " < < gogAPI - > user . notifications_forum < < std : : endl ;
std : : cout < < " New private messages: " < < gogAPI - > user . notifications_messages < < std : : endl ;
std : : cout < < " Updated games: " < < gogAPI - > user . notifications_games < < std : : endl ;
2013-03-16 03:17:38 +02:00
if ( gogAPI - > user . notifications_games )
{
config . sGameRegex = " .* " ; // Always check all games
if ( config . bList | | config . bListDetails | | config . bDownload )
{
2013-03-16 04:56:32 +02:00
if ( config . bList )
config . bListDetails = true ; // Always list details
2013-03-16 03:17:38 +02:00
this - > getGameList ( ) ;
if ( config . bDownload )
this - > download ( ) ;
else
this - > listGames ( ) ;
}
}
2013-03-15 22:46:16 +02:00
}
void Downloader : : getGameList ( )
{
2013-10-13 11:54:35 +03:00
gameNamesIds = this - > getGames ( ) ;
2013-03-15 22:46:16 +02:00
// Filter the game list
if ( ! config . sGameRegex . empty ( ) )
{
// GameRegex filter aliases
if ( config . sGameRegex = = " all " )
config . sGameRegex = " .* " ;
if ( config . sGameRegex = = " free " )
{
2013-10-13 11:54:35 +03:00
gameNamesIds = this - > getFreeGames ( ) ;
2013-03-15 22:46:16 +02:00
}
else
2013-11-19 12:47:10 +02:00
{ // Filter the names
2013-10-13 11:54:35 +03:00
std : : vector < std : : pair < std : : string , std : : string > > gameNamesIdsFiltered ;
2013-03-15 22:46:16 +02:00
boost : : regex expression ( config . sGameRegex ) ;
boost : : match_results < std : : string : : const_iterator > what ;
2013-10-13 11:54:35 +03:00
for ( unsigned int i = 0 ; i < gameNamesIds . size ( ) ; + + i )
2013-03-15 22:46:16 +02:00
{
2013-11-19 12:47:10 +02:00
if ( boost : : regex_search ( gameNamesIds [ i ] . first , what , expression ) ) // Check if name matches the specified regex
2013-10-13 11:54:35 +03:00
gameNamesIdsFiltered . push_back ( gameNamesIds [ i ] ) ;
2013-03-15 22:46:16 +02:00
}
2013-10-13 11:54:35 +03:00
gameNamesIds = gameNamesIdsFiltered ;
2013-03-15 22:46:16 +02:00
}
}
}
/* Get detailed info about the games
returns 0 if successful
returns 1 if fails
*/
int Downloader : : getGameDetails ( )
{
gameDetails game ;
2013-03-16 04:56:32 +02:00
int updated = 0 ;
2013-10-13 11:54:35 +03:00
for ( unsigned int i = 0 ; i < gameNamesIds . size ( ) ; + + i )
2013-03-15 22:46:16 +02:00
{
2013-10-13 11:54:35 +03:00
std : : cout < < " Getting game info " < < i + 1 < < " / " < < gameNamesIds . size ( ) < < " \r " < < std : : flush ;
2013-11-18 13:19:02 +02:00
game = gogAPI - > getGameDetails ( gameNamesIds [ i ] . first , config . iInstallerType , config . iInstallerLanguage , config . bDuplicateHandler ) ;
2013-03-15 22:46:16 +02:00
if ( ! gogAPI - > getError ( ) )
2013-03-16 03:17:38 +02:00
{
2013-11-19 12:47:10 +02:00
if ( game . extras . empty ( ) & & config . bExtras ) // Try to get extras from account page if API didn't return any extras
2013-10-13 11:54:35 +03:00
{
game . extras = this - > getExtras ( gameNamesIds [ i ] . first , gameNamesIds [ i ] . second ) ;
}
2013-03-16 03:17:38 +02:00
if ( ! config . bUpdateCheck )
games . push_back ( game ) ;
else
{ // Update check, only add games that have updated files
for ( unsigned int j = 0 ; j < game . installers . size ( ) ; + + j )
{
if ( game . installers [ j ] . updated )
2013-03-16 04:08:30 +02:00
{
2013-03-16 03:17:38 +02:00
games . push_back ( game ) ;
2013-03-16 04:56:32 +02:00
updated + + ;
2013-03-16 04:08:30 +02:00
break ; // add the game only once
}
2013-03-16 03:17:38 +02:00
}
2013-03-16 04:56:32 +02:00
if ( updated > = gogAPI - > user . notifications_games )
2013-03-16 03:17:38 +02:00
{ // 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 ;
break ;
}
}
}
2013-03-15 22:46:16 +02:00
else
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
return 1 ;
}
}
std : : cout < < std : : endl ;
return 0 ;
}
void Downloader : : listGames ( )
{
if ( config . bListDetails ) // Detailed list
{
2013-11-26 12:59:42 +02:00
if ( this - > games . empty ( ) )
this - > getGameDetails ( ) ;
2013-03-15 22:46:16 +02:00
for ( unsigned int i = 0 ; i < games . size ( ) ; + + i )
{
std : : cout < < " gamename: " < < games [ i ] . gamename < < std : : endl
< < " title: " < < games [ i ] . title < < std : : endl
< < " icon: " < < " http://static.gog.com " < < games [ i ] . icon < < std : : endl ;
// List installers
2013-11-19 12:47:10 +02:00
if ( config . bInstallers )
2013-03-15 22:46:16 +02:00
{
std : : cout < < " installers: " < < std : : endl ;
for ( unsigned int j = 0 ; j < games [ i ] . installers . size ( ) ; + + j )
{
2013-03-16 03:17:38 +02:00
if ( ! config . bUpdateCheck | | games [ i ] . installers [ j ] . updated ) // Always list updated files
{
2013-11-18 13:19:02 +02:00
std : : string languages ;
2013-11-19 12:47:10 +02:00
for ( unsigned int k = 0 ; k < GlobalConstants : : LANGUAGES . size ( ) ; k + + ) // Check which languages the installer supports
2013-11-18 13:19:02 +02:00
{
if ( games [ i ] . installers [ j ] . language & GlobalConstants : : LANGUAGES [ k ] . languageId )
languages + = ( languages . empty ( ) ? " " : " , " ) + GlobalConstants : : LANGUAGES [ k ] . languageString ;
}
2013-03-16 03:17:38 +02:00
std : : cout < < " \t id: " < < games [ i ] . installers [ j ] . id < < std : : endl
2013-03-16 23:21:01 +02:00
< < " \t name: " < < games [ i ] . installers [ j ] . name < < std : : endl
2013-03-16 03:17:38 +02:00
< < " \t path: " < < games [ i ] . installers [ j ] . path < < std : : endl
< < " \t size: " < < games [ i ] . installers [ j ] . size < < std : : endl
2013-03-16 03:41:55 +02:00
< < " \t updated: " < < ( games [ i ] . installers [ j ] . updated ? " True " : " False " ) < < std : : endl
2013-11-18 13:19:02 +02:00
< < " \t language: " < < languages < < std : : endl
2013-03-16 03:17:38 +02:00
< < std : : endl ;
}
2013-03-15 22:46:16 +02:00
}
}
// List extras
2013-11-19 12:47:10 +02:00
if ( config . bExtras & & ! config . bUpdateCheck & & ! games [ i ] . extras . empty ( ) )
2013-03-15 22:46:16 +02:00
{
std : : cout < < " extras: " < < std : : endl ;
for ( unsigned int j = 0 ; j < games [ i ] . extras . size ( ) ; + + j )
{
2013-03-15 23:33:53 +02:00
std : : cout < < " \t id: " < < games [ i ] . extras [ j ] . id < < std : : endl
< < " \t name: " < < games [ i ] . extras [ j ] . name < < std : : endl
< < " \t path: " < < games [ i ] . extras [ j ] . path < < std : : endl
< < " \t size: " < < games [ i ] . extras [ j ] . size < < std : : endl
2013-03-15 22:46:16 +02:00
< < std : : endl ;
}
}
2013-05-15 01:28:37 +03:00
// List patches
2013-11-19 12:47:10 +02:00
if ( config . bPatches & & ! config . bUpdateCheck & & ! games [ i ] . patches . empty ( ) )
2013-05-15 01:28:37 +03:00
{
std : : cout < < " patches: " < < std : : endl ;
for ( unsigned int j = 0 ; j < games [ i ] . patches . size ( ) ; + + j )
{
std : : cout < < " \t id: " < < games [ i ] . patches [ j ] . id < < std : : endl
< < " \t name: " < < games [ i ] . patches [ j ] . name < < std : : endl
< < " \t path: " < < games [ i ] . patches [ j ] . path < < std : : endl
< < " \t size: " < < games [ i ] . patches [ j ] . size < < std : : endl
< < std : : endl ;
}
}
2013-08-09 23:14:10 +03:00
// List language packs
2013-11-19 12:47:10 +02:00
if ( config . bLanguagePacks & & ! config . bUpdateCheck & & ! games [ i ] . languagepacks . empty ( ) )
2013-08-09 23:14:10 +03:00
{
std : : cout < < " language packs: " < < std : : endl ;
for ( unsigned int j = 0 ; j < games [ i ] . languagepacks . size ( ) ; + + j )
{
std : : cout < < " \t id: " < < games [ i ] . languagepacks [ j ] . id < < std : : endl
< < " \t name: " < < games [ i ] . languagepacks [ j ] . name < < std : : endl
< < " \t path: " < < games [ i ] . languagepacks [ j ] . path < < std : : endl
< < " \t size: " < < games [ i ] . languagepacks [ j ] . size < < std : : endl
< < std : : endl ;
}
}
2013-03-15 22:46:16 +02:00
}
}
else
2013-11-19 12:47:10 +02:00
{ // List game names
2013-10-13 11:54:35 +03:00
for ( unsigned int i = 0 ; i < gameNamesIds . size ( ) ; + + i )
std : : cout < < gameNamesIds [ i ] . first < < std : : endl ;
2013-03-15 22:46:16 +02:00
}
}
void Downloader : : repair ( )
{
2013-11-26 12:59:42 +02:00
if ( this - > games . empty ( ) )
this - > getGameDetails ( ) ;
2013-03-15 22:46:16 +02:00
for ( unsigned int i = 0 ; i < games . size ( ) ; + + i )
{
// Installers (use remote or local file)
2013-11-19 12:47:10 +02:00
if ( config . bInstallers )
2013-03-15 22:46:16 +02:00
{
for ( unsigned int j = 0 ; j < games [ i ] . installers . size ( ) ; + + j )
{
2013-03-15 23:33:53 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . installers [ j ] . path , games [ i ] . gamename ) ;
2013-03-15 22:46:16 +02:00
// Get XML data
std : : string XML = " " ;
2013-11-19 12:47:10 +02:00
if ( config . bRemoteXML )
2013-03-15 22:46:16 +02:00
{
2013-03-15 23:33:53 +02:00
XML = gogAPI - > getXML ( games [ i ] . gamename , games [ i ] . installers [ j ] . id ) ;
2013-03-15 22:46:16 +02:00
if ( gogAPI - > getError ( ) )
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
}
}
// Repair
2013-11-19 12:47:10 +02:00
bool bUseLocalXML = ! config . bRemoteXML ;
if ( ! XML . empty ( ) | | bUseLocalXML )
2013-03-15 22:46:16 +02:00
{
2013-03-15 23:33:53 +02:00
std : : string url = gogAPI - > getInstallerLink ( games [ i ] . gamename , games [ i ] . installers [ j ] . id ) ;
2013-03-15 22:46:16 +02:00
if ( gogAPI - > getError ( ) )
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
}
std : : cout < < " Repairing file " < < filepath < < std : : endl ;
2013-04-08 13:43:11 +03:00
this - > repairFile ( url , filepath , XML ) ;
2013-03-15 22:46:16 +02:00
std : : cout < < std : : endl ;
}
}
}
// Extras (GOG doesn't provide XML data for extras, use local file)
2013-11-19 12:47:10 +02:00
if ( config . bExtras )
2013-03-15 22:46:16 +02:00
{
for ( unsigned int j = 0 ; j < games [ i ] . extras . size ( ) ; + + j )
{
2014-02-23 03:16:10 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . extras [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " extras " : " " ) ;
2013-03-15 22:46:16 +02:00
2013-03-15 23:33:53 +02:00
std : : string url = gogAPI - > getExtraLink ( games [ i ] . gamename , games [ i ] . extras [ j ] . id ) ;
2013-03-15 22:46:16 +02:00
if ( gogAPI - > getError ( ) )
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
}
std : : cout < < " Repairing file " < < filepath < < std : : endl ;
2013-04-08 13:43:11 +03:00
this - > repairFile ( url , filepath ) ;
2013-03-15 22:46:16 +02:00
std : : cout < < std : : endl ;
}
}
2013-05-15 01:28:37 +03:00
// Patches (use remote or local file)
2013-11-19 12:47:10 +02:00
if ( config . bPatches )
2013-05-15 01:28:37 +03:00
{
for ( unsigned int j = 0 ; j < games [ i ] . patches . size ( ) ; + + j )
{
2014-02-23 03:16:10 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . patches [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " patches " : " " ) ;
2013-05-15 01:28:37 +03:00
2013-08-09 23:14:10 +03:00
std : : string url = gogAPI - > getPatchLink ( games [ i ] . gamename , games [ i ] . patches [ j ] . id ) ;
if ( gogAPI - > getError ( ) )
2013-05-15 01:28:37 +03:00
{
2013-08-09 23:14:10 +03:00
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
2013-05-15 01:28:37 +03:00
}
2013-08-09 23:14:10 +03:00
std : : cout < < " Repairing file " < < filepath < < std : : endl ;
this - > repairFile ( url , filepath ) ;
std : : cout < < std : : endl ;
}
}
2013-05-15 01:28:37 +03:00
2013-08-09 23:14:10 +03:00
// Language packs (GOG doesn't provide XML data for language packs, use local file)
2013-11-19 12:47:10 +02:00
if ( config . bLanguagePacks )
2013-08-09 23:14:10 +03:00
{
for ( unsigned int j = 0 ; j < games [ i ] . languagepacks . size ( ) ; + + j )
{
2014-02-23 03:16:10 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . languagepacks [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " languagepacks " : " " ) ;
2013-08-09 23:14:10 +03:00
std : : string url = gogAPI - > getLanguagePackLink ( games [ i ] . gamename , games [ i ] . languagepacks [ j ] . id ) ;
if ( gogAPI - > getError ( ) )
2013-05-15 01:28:37 +03:00
{
2013-08-09 23:14:10 +03:00
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
2013-05-15 01:28:37 +03:00
}
2013-08-09 23:14:10 +03:00
std : : cout < < " Repairing file " < < filepath < < std : : endl ;
this - > repairFile ( url , filepath ) ;
std : : cout < < std : : endl ;
2013-05-15 01:28:37 +03:00
}
}
2013-08-09 23:14:10 +03:00
2013-03-15 22:46:16 +02:00
}
}
void Downloader : : download ( )
{
2013-11-26 12:59:42 +02:00
if ( this - > games . empty ( ) )
this - > getGameDetails ( ) ;
2013-03-15 22:46:16 +02:00
for ( unsigned int i = 0 ; i < games . size ( ) ; + + i )
{
// Download covers
2013-11-19 12:47:10 +02:00
if ( config . bCover & & ! config . bUpdateCheck )
2013-03-15 22:46:16 +02:00
{
2014-02-03 19:10:02 +02:00
if ( ! games [ i ] . installers . empty ( ) )
{
// Take path from installer path because for some games the base directory for installer/extra path is not "gamename"
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . installers [ 0 ] . path , games [ i ] . gamename ) ;
2013-03-15 22:46:16 +02:00
2014-02-03 19:10:02 +02:00
// Get base directory from filepath
boost : : match_results < std : : string : : const_iterator > what ;
boost : : regex expression ( " (.*)/.* " ) ;
boost : : regex_match ( filepath , what , expression ) ;
std : : string directory = what [ 1 ] ;
2013-03-15 22:46:16 +02:00
2014-02-03 19:10:02 +02:00
this - > downloadCovers ( games [ i ] . gamename , directory , coverXML ) ;
}
2013-03-15 22:46:16 +02:00
}
// Download installers
2013-11-19 12:47:10 +02:00
if ( config . bInstallers )
2013-03-15 22:46:16 +02:00
{
for ( unsigned int j = 0 ; j < games [ i ] . installers . size ( ) ; + + j )
{
2013-03-16 03:17:38 +02:00
// Not updated, skip to next installer
if ( config . bUpdateCheck & & ! games [ i ] . installers [ j ] . updated )
continue ;
2013-11-18 13:19:02 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . installers [ j ] . path , games [ i ] . gamename ) ;
2013-03-15 22:46:16 +02:00
// Get link
2013-03-15 23:33:53 +02:00
std : : string url = gogAPI - > getInstallerLink ( games [ i ] . gamename , games [ i ] . installers [ j ] . id ) ;
2013-03-15 22:46:16 +02:00
if ( gogAPI - > getError ( ) )
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
}
// Download
if ( ! url . empty ( ) )
{
2013-04-08 13:43:11 +03:00
std : : string XML ;
2013-11-19 12:47:10 +02:00
if ( config . bRemoteXML )
2013-04-08 13:43:11 +03:00
XML = gogAPI - > getXML ( games [ i ] . gamename , games [ i ] . installers [ j ] . id ) ;
2013-03-16 23:21:01 +02:00
if ( ! games [ i ] . installers [ j ] . name . empty ( ) )
std : : cout < < " Dowloading: " < < games [ i ] . installers [ j ] . name < < std : : endl ;
2013-03-15 22:46:16 +02:00
std : : cout < < filepath < < std : : endl ;
2013-04-08 13:43:11 +03:00
this - > downloadFile ( url , filepath , XML ) ;
2013-03-15 22:46:16 +02:00
std : : cout < < std : : endl ;
}
}
}
// Download extras
2013-11-19 12:47:10 +02:00
if ( config . bExtras & & ! config . bUpdateCheck )
2013-03-16 03:17:38 +02:00
{ // Save some time and don't process extras when running update check. Extras don't have updated flag, all of them would be skipped anyway.
2013-03-15 22:46:16 +02:00
for ( unsigned int j = 0 ; j < games [ i ] . extras . size ( ) ; + + j )
{
// Get link
2013-03-15 23:33:53 +02:00
std : : string url = gogAPI - > getExtraLink ( games [ i ] . gamename , games [ i ] . extras [ j ] . id ) ;
2013-03-15 22:46:16 +02:00
if ( gogAPI - > getError ( ) )
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
}
2014-02-23 03:16:10 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . extras [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " extras " : " " ) ;
2013-03-15 22:46:16 +02:00
// Download
if ( ! url . empty ( ) )
{
2013-03-15 23:33:53 +02:00
if ( ! games [ i ] . extras [ j ] . name . empty ( ) )
std : : cout < < " Dowloading: " < < games [ i ] . extras [ j ] . name < < std : : endl ;
2013-03-15 22:46:16 +02:00
std : : cout < < filepath < < std : : endl ;
CURLcode result = downloadFile ( url , filepath ) ;
std : : cout < < std : : endl ;
if ( result = = CURLE_OK & & config . sXMLFile = = " automatic " )
{
std : : cout < < " Starting automatic XML creation " < < std : : endl ;
Util : : createXML ( filepath , config . iChunkSize , config . sXMLDirectory ) ;
2013-11-14 15:40:59 +02:00
std : : cout < < std : : endl ;
2013-03-15 22:46:16 +02:00
}
}
}
}
2013-05-15 01:28:37 +03:00
// Download patches
2013-11-19 12:47:10 +02:00
if ( config . bPatches )
2013-05-15 01:28:37 +03:00
{
for ( unsigned int j = 0 ; j < games [ i ] . patches . size ( ) ; + + j )
{
// Get link
std : : string url = gogAPI - > getPatchLink ( games [ i ] . gamename , games [ i ] . patches [ j ] . id ) ;
if ( gogAPI - > getError ( ) )
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
}
2014-02-23 03:16:10 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . patches [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " patches " : " " ) ;
2013-05-15 01:28:37 +03:00
// Download
if ( ! url . empty ( ) )
{
if ( ! games [ i ] . patches [ j ] . name . empty ( ) )
std : : cout < < " Dowloading: " < < games [ i ] . gamename < < " " < < games [ i ] . patches [ j ] . name < < std : : endl ;
std : : cout < < filepath < < std : : endl ;
2013-08-09 23:14:10 +03:00
CURLcode result = downloadFile ( url , filepath ) ;
std : : cout < < std : : endl ;
if ( result = = CURLE_OK & & config . sXMLFile = = " automatic " )
{
std : : cout < < " Starting automatic XML creation " < < std : : endl ;
Util : : createXML ( filepath , config . iChunkSize , config . sXMLDirectory ) ;
2013-11-14 15:40:59 +02:00
std : : cout < < std : : endl ;
2013-08-09 23:14:10 +03:00
}
}
}
}
// Download language packs
2013-11-19 12:47:10 +02:00
if ( config . bLanguagePacks )
2013-08-09 23:14:10 +03:00
{
for ( unsigned int j = 0 ; j < games [ i ] . languagepacks . size ( ) ; + + j )
{
// Get link
std : : string url = gogAPI - > getLanguagePackLink ( games [ i ] . gamename , games [ i ] . languagepacks [ j ] . id ) ;
if ( gogAPI - > getError ( ) )
{
std : : cout < < gogAPI - > getErrorMessage ( ) < < std : : endl ;
gogAPI - > clearError ( ) ;
continue ;
}
2014-02-23 03:16:10 +02:00
std : : string filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . languagepacks [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " languagepacks " : " " ) ;
2013-08-09 23:14:10 +03:00
// Download
if ( ! url . empty ( ) )
{
if ( ! games [ i ] . languagepacks [ j ] . name . empty ( ) )
std : : cout < < " Dowloading: " < < games [ i ] . gamename < < " " < < games [ i ] . languagepacks [ j ] . name < < std : : endl ;
std : : cout < < filepath < < std : : endl ;
CURLcode result = downloadFile ( url , filepath ) ;
2013-05-15 01:28:37 +03:00
std : : cout < < std : : endl ;
2013-08-09 23:14:10 +03:00
if ( result = = CURLE_OK & & config . sXMLFile = = " automatic " )
{
std : : cout < < " Starting automatic XML creation " < < std : : endl ;
Util : : createXML ( filepath , config . iChunkSize , config . sXMLDirectory ) ;
2013-11-14 15:40:59 +02:00
std : : cout < < std : : endl ;
2013-08-09 23:14:10 +03:00
}
2013-05-15 01:28:37 +03:00
}
}
}
2013-03-15 22:46:16 +02:00
}
}
// Download a file, resume if possible
2013-04-08 13:43:11 +03:00
CURLcode Downloader : : downloadFile ( const std : : string & url , const std : : string & filepath , const std : : string & xml_data )
2013-03-15 22:46:16 +02:00
{
CURLcode res = CURLE_RECV_ERROR ; // assume network error
bool bResume = false ;
2013-03-16 23:35:57 +02:00
FILE * outfile ;
size_t offset = 0 ;
2013-03-15 22:46:16 +02:00
// Get directory from filepath
2013-03-16 23:35:57 +02:00
boost : : filesystem : : path pathname = filepath ;
std : : string directory = pathname . parent_path ( ) . string ( ) ;
2013-04-08 13:43:11 +03:00
std : : string filenameXML = pathname . filename ( ) . string ( ) + " .xml " ;
// Using local XML data for version check before resuming
boost : : filesystem : : path local_xml_file ;
2013-11-14 15:40:59 +02:00
local_xml_file = config . sXMLDirectory + " / " + filenameXML ;
2013-04-08 13:43:11 +03:00
bool bSameVersion = true ; // assume same version
2013-11-14 15:40:59 +02:00
bool bLocalXMLExists = boost : : filesystem : : exists ( local_xml_file ) ; // This is additional check to see if remote xml should be saved to speed up future version checks
std : : string localHash = this - > getLocalFileHash ( filepath ) ;
2013-04-08 13:43:11 +03:00
if ( ! xml_data . empty ( ) )
{
2013-11-14 15:40:59 +02:00
// Do version check if local hash exists
if ( ! localHash . empty ( ) )
2013-04-08 13:43:11 +03:00
{
2013-11-14 15:40:59 +02:00
TiXmlDocument remote_xml ;
2013-04-08 13:43:11 +03:00
remote_xml . Parse ( xml_data . c_str ( ) ) ;
TiXmlNode * fileNodeRemote = remote_xml . FirstChild ( " file " ) ;
2013-11-14 15:40:59 +02:00
if ( fileNodeRemote )
2013-04-08 13:43:11 +03:00
{
TiXmlElement * fileElemRemote = fileNodeRemote - > ToElement ( ) ;
std : : string remoteHash = fileElemRemote - > Attribute ( " md5 " ) ;
if ( remoteHash ! = localHash )
bSameVersion = false ;
}
}
}
2013-03-16 23:35:57 +02:00
// 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 res ;
}
}
else
{
if ( ! boost : : filesystem : : create_directories ( path ) )
{
std : : cout < < " Failed to create directory: " < < path < < std : : endl ;
return res ;
}
}
// Check if file exists
if ( ( outfile = fopen ( filepath . c_str ( ) , " r " ) ) ! = NULL )
{
2013-04-08 13:43:11 +03:00
if ( bSameVersion )
2013-03-16 23:35:57 +02:00
{
2013-04-08 13:43:11 +03:00
// File exists, resume
if ( ( outfile = freopen ( filepath . c_str ( ) , " r+ " , outfile ) ) ! = NULL )
{
bResume = true ;
fseek ( outfile , 0 , SEEK_END ) ;
offset = ftell ( outfile ) ;
curl_easy_setopt ( curlhandle , CURLOPT_RESUME_FROM , offset ) ;
this - > resume_position = offset ;
}
else
{
std : : cout < < " Failed to reopen " < < filepath < < std : : endl ;
return res ;
}
2013-03-16 23:35:57 +02:00
}
else
2013-11-19 12:47:10 +02:00
{ // File exists but is not the same version
2013-04-08 13:43:11 +03:00
fclose ( outfile ) ;
std : : cout < < " Remote file is different, renaming local file " < < std : : endl ;
2013-11-19 12:47:10 +02:00
boost : : filesystem : : path new_name = filepath + " .old " ; // Rename old file by appending ".old" to filename
2013-04-08 13:43:11 +03:00
if ( boost : : filesystem : : exists ( new_name ) )
2013-11-19 12:47:10 +02:00
{ // One even older file exists, delete the older file before renaming
2013-04-08 13:43:11 +03:00
std : : cout < < " Old renamed file found, deleting old file " < < std : : endl ;
if ( ! boost : : filesystem : : remove ( new_name ) )
{
std : : cout < < " Failed to delete " < < new_name . string ( ) < < std : : endl ;
std : : cout < < " Skipping file " < < std : : endl ;
return res ;
}
}
boost : : system : : error_code ec ;
2013-11-19 12:47:10 +02:00
boost : : filesystem : : rename ( pathname , new_name , ec ) ; // Rename the file
2013-04-08 13:43:11 +03:00
if ( ec )
{
std : : cout < < " Failed to rename " < < filepath < < " to " < < new_name . string ( ) < < std : : endl ;
std : : cout < < " Skipping file " < < std : : endl ;
return res ;
}
else
{
// Create new file
if ( ( outfile = fopen ( filepath . c_str ( ) , " w " ) ) ! = NULL )
{
curl_easy_setopt ( curlhandle , CURLOPT_RESUME_FROM , 0 ) ; // start downloading from the beginning of file
this - > resume_position = 0 ;
}
else
{
std : : cout < < " Failed to create " < < filepath < < std : : endl ;
return res ;
}
}
2013-03-16 23:35:57 +02:00
}
}
else
{
2013-03-15 22:46:16 +02:00
// File doesn't exist, create new file
if ( ( outfile = fopen ( filepath . c_str ( ) , " w " ) ) ! = NULL )
{
2013-04-08 13:43:11 +03:00
curl_easy_setopt ( curlhandle , CURLOPT_RESUME_FROM , 0 ) ; // start downloading from the beginning of file
2013-03-16 23:35:57 +02:00
this - > resume_position = 0 ;
2013-03-15 22:46:16 +02:00
}
else
{
std : : cout < < " Failed to create " < < filepath < < std : : endl ;
return res ;
}
2013-03-16 23:35:57 +02:00
}
2013-03-15 22:46:16 +02:00
2013-04-08 13:43:11 +03:00
// Save remote XML
if ( ! xml_data . empty ( ) )
{
if ( ( bLocalXMLExists & & ( ! bSameVersion | | config . bRepair ) ) | | ! bLocalXMLExists )
{
std : : ofstream ofs ( local_xml_file . string ( ) . c_str ( ) ) ;
if ( ofs )
{
ofs < < xml_data ;
ofs . close ( ) ;
}
else
{
std : : cout < < " Can't create " < < local_xml_file . string ( ) < < std : : endl ;
}
}
}
2013-03-15 22:46:16 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_URL , url . c_str ( ) ) ;
curl_easy_setopt ( curlhandle , CURLOPT_WRITEDATA , outfile ) ;
res = this - > beginDownload ( ) ;
2013-03-16 23:35:57 +02:00
fclose ( outfile ) ;
2013-03-15 22:46:16 +02:00
// Download failed and was not a resume attempt so delete the file
2014-02-26 14:59:23 +02:00
if ( ( res ! = CURLE_OK | | res ! = CURLE_PARTIAL_FILE ) & & ! bResume )
2013-03-15 22:46:16 +02:00
{
boost : : filesystem : : path path = filepath ;
if ( boost : : filesystem : : exists ( path ) )
if ( ! boost : : filesystem : : remove ( path ) )
std : : cout < < " Failed to delete " < < path < < std : : endl ;
}
2014-02-13 10:05:16 +02:00
if ( config . bReport )
{
std : : string status = static_cast < std : : string > ( curl_easy_strerror ( res ) ) ;
if ( bResume & & res = = CURLE_RANGE_ERROR ) // CURLE_RANGE_ERROR on resume attempts is not an error that user needs to know about
status = " No error " ;
std : : string report_line = " Downloaded [ " + status + " ] " + filepath ;
2014-02-13 11:16:31 +02:00
this - > report_ofs < < report_line < < std : : endl ;
2014-02-13 10:05:16 +02:00
}
2014-02-26 14:59:23 +02:00
// Retry partially downloaded file
if ( res = = CURLE_PARTIAL_FILE & & ( this - > retries < config . iRetries ) )
{
this - > retries + + ;
res = this - > downloadFile ( url , filepath , xml_data ) ;
}
else
{
this - > retries = 0 ; // Reset retries counter
}
2013-03-16 23:35:57 +02:00
return res ;
2013-03-15 22:46:16 +02:00
}
// Repair file
2013-04-08 13:43:11 +03:00
int Downloader : : repairFile ( const std : : string & url , const std : : string & filepath , const std : : string & xml_data )
2013-03-15 22:46:16 +02:00
{
int res = 0 ;
2013-03-16 23:35:57 +02:00
FILE * outfile ;
2013-04-29 11:06:15 +03:00
size_t offset = 0 , from_offset , to_offset , filesize ;
std : : string filehash ;
int chunks ;
std : : vector < size_t > chunk_from , chunk_to ;
std : : vector < std : : string > chunk_hash ;
2013-09-04 20:49:57 +03:00
bool bParsingFailed = false ;
2013-03-15 22:46:16 +02:00
// Get filename
boost : : filesystem : : path pathname = filepath ;
std : : string filename = pathname . filename ( ) . string ( ) ;
2013-12-12 14:21:43 +02:00
std : : string xml_file = config . sXMLDirectory + " / " + filename + " .xml " ;
bool bFileExists = boost : : filesystem : : exists ( pathname ) ;
bool bLocalXMLExists = boost : : filesystem : : exists ( xml_file ) ;
2013-03-15 22:46:16 +02:00
2013-03-16 23:35:57 +02:00
TiXmlDocument xml ;
2013-11-19 12:47:10 +02:00
if ( ! xml_data . empty ( ) ) // Parse remote XML data
{
2013-03-16 23:35:57 +02:00
std : : cout < < " XML: Using remote file " < < std : : endl ;
xml . Parse ( xml_data . c_str ( ) ) ;
}
2013-03-15 22:46:16 +02:00
else
2013-11-19 12:47:10 +02:00
{ // Parse local XML data
2013-03-15 22:46:16 +02:00
std : : cout < < " XML: Using local file " < < std : : endl ;
2013-12-12 14:21:43 +02:00
if ( ! bLocalXMLExists )
std : : cout < < " XML: File doesn't exist ( " < < xml_file < < " ) " < < std : : endl ;
2013-03-15 22:46:16 +02:00
xml . LoadFile ( xml_file ) ;
}
2013-11-19 12:47:10 +02:00
// Check if file node exists in XML data
2013-03-16 23:35:57 +02:00
TiXmlNode * fileNode = xml . FirstChild ( " file " ) ;
if ( ! fileNode )
2013-11-19 12:47:10 +02:00
{ // File node doesn't exist
2013-03-16 23:35:57 +02:00
std : : cout < < " XML: Parsing failed / not valid XML " < < std : : endl ;
2013-09-04 20:49:57 +03:00
if ( config . bDownload )
bParsingFailed = true ;
else
return res ;
2013-03-16 23:35:57 +02:00
}
else
2013-11-19 12:47:10 +02:00
{ // File node exists --> valid XML
2013-03-16 23:35:57 +02:00
std : : cout < < " XML: Valid XML " < < std : : endl ;
TiXmlElement * fileElem = fileNode - > ToElement ( ) ;
2013-04-29 11:06:15 +03:00
filename = fileElem - > Attribute ( " name " ) ;
filehash = fileElem - > Attribute ( " md5 " ) ;
std : : stringstream ( fileElem - > Attribute ( " chunks " ) ) > > chunks ;
std : : stringstream ( fileElem - > Attribute ( " total_size " ) ) > > filesize ;
2013-03-16 23:35:57 +02:00
//Iterate through all chunk nodes
TiXmlNode * chunkNode = fileNode - > FirstChild ( ) ;
while ( chunkNode )
{
TiXmlElement * chunkElem = chunkNode - > ToElement ( ) ;
std : : stringstream ( chunkElem - > Attribute ( " from " ) ) > > from_offset ;
std : : stringstream ( chunkElem - > Attribute ( " to " ) ) > > to_offset ;
2013-04-29 11:06:15 +03:00
chunk_from . push_back ( from_offset ) ;
chunk_to . push_back ( to_offset ) ;
chunk_hash . push_back ( chunkElem - > GetText ( ) ) ;
2013-03-16 23:35:57 +02:00
chunkNode = fileNode - > IterateChildren ( chunkNode ) ;
}
2013-03-15 22:46:16 +02:00
2013-12-12 11:01:25 +02:00
std : : cout < < " XML: Parsing finished " < < std : : endl < < std : : endl
< < filename < < std : : endl
< < " \t MD5: \t " < < filehash < < std : : endl
< < " \t Chunks: \t " < < chunks < < std : : endl
< < " \t Size: \t " < < filesize < < " bytes " < < std : : endl < < std : : endl ;
}
2013-03-15 22:46:16 +02:00
2013-12-12 14:21:43 +02:00
// No local XML file and parsing failed.
if ( bParsingFailed & & ! bLocalXMLExists )
{
2013-12-14 14:24:52 +02:00
if ( this - > config . bDownload )
2013-12-12 14:21:43 +02:00
{
std : : cout < < " Downloading: " < < filepath < < std : : endl ;
CURLcode result = this - > downloadFile ( url , filepath , xml_data ) ;
2013-12-14 14:24:52 +02:00
std : : cout < < std : : endl ;
if (
( ! bFileExists & & result = = CURLE_OK ) | | /* File doesn't exist so only accept if everything was OK */
( bFileExists & & ( result = = CURLE_OK | | result = = CURLE_RANGE_ERROR ) ) /* File exists so accept also CURLE_RANGE_ERROR because curl will return CURLE_RANGE_ERROR */
) /* if the file is already fully downloaded and we want to resume it */
2013-12-12 14:21:43 +02:00
{
bLocalXMLExists = boost : : filesystem : : exists ( xml_file ) ; // Check to see if downloadFile saved XML data
if ( config . sXMLFile = = " automatic " & & ! bLocalXMLExists )
{
std : : cout < < " Starting automatic XML creation " < < std : : endl ;
Util : : createXML ( filepath , config . iChunkSize , config . sXMLDirectory ) ;
}
res = 1 ;
}
}
else
{
std : : cout < < " Can't repair file. " < < std : : endl ;
}
return res ;
}
2013-03-16 23:35:57 +02:00
// Check if file exists
2013-12-12 14:21:43 +02:00
if ( bFileExists )
2013-03-16 23:35:57 +02:00
{
// File exists
2013-12-12 14:21:43 +02:00
if ( ( outfile = fopen ( filepath . c_str ( ) , " r+ " ) ) ! = NULL )
2013-03-16 23:35:57 +02:00
{
fseek ( outfile , 0 , SEEK_END ) ;
offset = ftell ( outfile ) ;
}
2013-03-15 22:46:16 +02:00
else
{
2013-12-12 14:21:43 +02:00
std : : cout < < " Failed to open " < < filepath < < std : : endl ;
2013-03-16 23:35:57 +02:00
return res ;
}
}
else
{
2013-12-12 14:21:43 +02:00
std : : cout < < " File doesn't exist " < < filepath < < std : : endl ;
2013-07-01 16:57:55 +03:00
if ( this - > config . bDownload )
{
std : : cout < < " Downloading: " < < filepath < < std : : endl ;
CURLcode result = this - > downloadFile ( url , filepath , xml_data ) ;
2013-12-14 14:24:52 +02:00
std : : cout < < std : : endl ;
2013-07-01 16:57:55 +03:00
if ( result = = CURLE_OK )
2013-09-04 21:03:43 +03:00
{
if ( config . sXMLFile = = " automatic " & & bParsingFailed )
{
std : : cout < < " Starting automatic XML creation " < < std : : endl ;
Util : : createXML ( filepath , config . iChunkSize , config . sXMLDirectory ) ;
}
2013-07-01 16:57:55 +03:00
res = 1 ;
2013-09-04 21:03:43 +03:00
}
2013-07-01 16:57:55 +03:00
}
2013-03-15 22:46:16 +02:00
return res ;
2013-03-16 23:35:57 +02:00
}
2013-03-15 22:46:16 +02:00
2013-04-29 11:06:15 +03:00
if ( offset ! = filesize )
2013-03-16 23:35:57 +02:00
{
std : : cout < < " Filesizes don't match " < < std : : endl
2013-03-15 22:46:16 +02:00
< < " Incomplete download or different version " < < std : : endl ;
2013-04-06 19:35:20 +03:00
fclose ( outfile ) ;
2013-04-05 21:19:03 +03:00
if ( this - > config . bDownload )
{
std : : cout < < " Redownloading file " < < std : : endl ;
boost : : filesystem : : path path = filepath ;
if ( ! boost : : filesystem : : remove ( path ) )
{
std : : cout < < " Failed to delete " < < path < < std : : endl ;
res = 0 ;
}
else
{
2013-04-08 13:43:11 +03:00
CURLcode result = this - > downloadFile ( url , filepath , xml_data ) ;
2013-04-05 21:19:03 +03:00
std : : cout < < std : : endl ;
if ( result = = CURLE_OK )
res = 1 ;
else
res = 0 ;
}
}
2013-03-16 23:35:57 +02:00
return res ;
}
2013-03-15 22:46:16 +02:00
2013-11-19 12:47:10 +02:00
// Check all chunks
2014-02-13 10:05:16 +02:00
int iChunksRepaired = 0 ;
2013-04-29 11:06:15 +03:00
for ( int i = 0 ; i < chunks ; i + + )
2013-03-15 22:46:16 +02:00
{
2013-04-29 11:06:15 +03:00
size_t chunk_begin = chunk_from . at ( i ) ;
size_t chunk_end = chunk_to . at ( i ) ;
size_t size = 0 , chunk_size = chunk_end - chunk_begin + 1 ;
2013-11-19 12:47:10 +02:00
std : : string range = std : : to_string ( chunk_begin ) + " - " + std : : to_string ( chunk_end ) ; // Download range string for curl
2013-03-15 22:46:16 +02:00
2013-04-06 05:50:44 +03:00
std : : cout < < " \033 [0K \r Chunk " < < i < < " ( " < < chunk_size < < " bytes): " ;
2013-04-29 11:06:15 +03:00
fseek ( outfile , chunk_begin , SEEK_SET ) ;
2013-03-15 22:46:16 +02:00
unsigned char * chunk = ( unsigned char * ) malloc ( chunk_size * sizeof ( unsigned char * ) ) ;
if ( chunk = = NULL )
{
std : : cout < < " Memory error " < < std : : endl ;
2013-12-11 10:38:32 +02:00
fclose ( outfile ) ;
2013-03-15 22:46:16 +02:00
return res ;
}
size = fread ( chunk , 1 , chunk_size , outfile ) ;
if ( size ! = chunk_size )
{
std : : cout < < " Read error " < < std : : endl ;
free ( chunk ) ;
2013-12-11 10:38:32 +02:00
fclose ( outfile ) ;
2013-03-15 22:46:16 +02:00
return res ;
}
std : : string hash = Util : : getChunkHash ( chunk , chunk_size , RHASH_MD5 ) ;
2013-04-29 11:06:15 +03:00
if ( hash ! = chunk_hash . at ( i ) )
2013-03-15 22:46:16 +02:00
{
std : : cout < < " Failed - downloading chunk " < < std : : endl ;
2013-04-29 11:06:15 +03:00
fseek ( outfile , chunk_begin , SEEK_SET ) ;
2013-03-15 22:46:16 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_URL , url . c_str ( ) ) ;
curl_easy_setopt ( curlhandle , CURLOPT_WRITEDATA , outfile ) ;
curl_easy_setopt ( curlhandle , CURLOPT_RANGE , range . c_str ( ) ) ; //download range
this - > beginDownload ( ) ; //begin chunk download
std : : cout < < std : : endl ;
2014-02-13 10:05:16 +02:00
if ( config . bReport )
iChunksRepaired + + ;
2013-03-15 22:46:16 +02:00
i - - ; //verify downloaded chunk
}
else
{
2013-04-06 05:50:44 +03:00
std : : cout < < " OK \r " < < std : : flush ;
2013-03-15 22:46:16 +02:00
}
free ( chunk ) ;
res = 1 ;
}
2013-04-06 05:50:44 +03:00
std : : cout < < std : : endl ;
2013-03-16 23:35:57 +02:00
fclose ( outfile ) ;
2013-03-15 22:46:16 +02:00
2014-02-13 10:05:16 +02:00
if ( config . bReport )
{
std : : string report_line = " Repaired [ " + std : : to_string ( iChunksRepaired ) + " / " + std : : to_string ( chunks ) + " ] " + filepath ;
2014-02-13 11:16:31 +02:00
this - > report_ofs < < report_line < < std : : endl ;
2014-02-13 10:05:16 +02:00
}
2013-03-15 22:46:16 +02:00
return res ;
}
// Download cover images
2013-04-08 13:43:11 +03:00
int Downloader : : downloadCovers ( const std : : string & gamename , const std : : string & directory , const std : : string & cover_xml_data )
2013-03-15 22:46:16 +02:00
{
int res = 0 ;
2013-03-16 23:35:57 +02:00
TiXmlDocument xml ;
// 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 res ;
}
}
else
{
if ( ! boost : : filesystem : : create_directories ( path ) )
{
std : : cout < < " Failed to create directory: " < < path < < std : : endl ;
return res ;
}
}
xml . Parse ( cover_xml_data . c_str ( ) ) ;
TiXmlElement * rootNode = xml . RootElement ( ) ;
if ( ! rootNode )
{
std : : cout < < " Not valid XML " < < std : : endl ;
return res ;
}
else
{
TiXmlNode * gameNode = rootNode - > FirstChild ( ) ;
while ( gameNode )
{
TiXmlElement * gameElem = gameNode - > ToElement ( ) ;
std : : string game_name = gameElem - > Attribute ( " name " ) ;
if ( game_name = = gamename )
{
boost : : match_results < std : : string : : const_iterator > what ;
TiXmlNode * coverNode = gameNode - > FirstChild ( ) ;
while ( coverNode )
{
TiXmlElement * coverElem = coverNode - > ToElement ( ) ;
std : : string cover_url = coverElem - > GetText ( ) ;
// Get file extension for the image
boost : : regex e1 ( " .*( \\ . \\ w+)$ " , boost : : regex : : perl | boost : : regex : : icase ) ;
boost : : regex_search ( cover_url , what , e1 ) ;
std : : string file_extension = what [ 1 ] ;
std : : string cover_name = std : : string ( " cover_ " ) + coverElem - > Attribute ( " id " ) + file_extension ;
std : : string filepath = directory + " / " + cover_name ;
2013-03-15 22:46:16 +02:00
std : : cout < < " Downloading cover " < < filepath < < std : : endl ;
CURLcode result = this - > downloadFile ( cover_url , filepath ) ;
std : : cout < < std : : endl ;
if ( result = = CURLE_OK )
res = 1 ;
else
res = 0 ;
if ( result = = CURLE_HTTP_RETURNED_ERROR )
{
long int response_code = 0 ;
result = curl_easy_getinfo ( curlhandle , CURLINFO_RESPONSE_CODE , & response_code ) ;
std : : cout < < " HTTP ERROR: " ;
if ( result = = CURLE_OK )
std : : cout < < response_code < < std : : endl ;
else
std : : cout < < " failed to get error code: " < < curl_easy_strerror ( result ) < < std : : endl ;
}
2013-03-16 23:35:57 +02:00
coverNode = gameNode - > IterateChildren ( coverNode ) ;
}
2013-03-15 22:46:16 +02:00
break ; // Found cover for game, no need to go through rest of the game nodes
2013-03-16 23:35:57 +02:00
}
gameNode = rootNode - > IterateChildren ( gameNode ) ;
}
}
2013-03-15 22:46:16 +02:00
2013-03-16 23:35:57 +02:00
return res ;
2013-03-15 22:46:16 +02:00
}
CURLcode Downloader : : beginDownload ( )
{
this - > timer . reset ( ) ;
CURLcode result = curl_easy_perform ( curlhandle ) ;
this - > resume_position = 0 ;
return result ;
}
std : : string Downloader : : getResponse ( const std : : string & url )
{
std : : ostringstream memory ;
curl_easy_setopt ( curlhandle , CURLOPT_URL , url . c_str ( ) ) ;
curl_easy_setopt ( curlhandle , CURLOPT_NOPROGRESS , 1 ) ;
curl_easy_setopt ( curlhandle , CURLOPT_WRITEFUNCTION , Downloader : : writeMemoryCallback ) ;
curl_easy_setopt ( curlhandle , CURLOPT_WRITEDATA , & memory ) ;
2013-06-01 17:22:37 +03:00
CURLcode result = curl_easy_perform ( curlhandle ) ;
2013-06-08 00:20:55 +03:00
curl_easy_setopt ( curlhandle , CURLOPT_WRITEFUNCTION , Downloader : : writeData ) ;
2013-03-15 22:46:16 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_NOPROGRESS , 0 ) ;
std : : string response = memory . str ( ) ;
memory . str ( std : : string ( ) ) ;
2013-06-01 17:22:37 +03:00
if ( result ! = CURLE_OK )
std : : cout < < curl_easy_strerror ( result ) < < std : : endl ;
2013-03-15 22:46:16 +02:00
return response ;
}
int Downloader : : progressCallback ( void * clientp , double dltotal , double dlnow , double ultotal , double ulnow )
{
// on entry: dltotal - how much remains to download till the end of the file (bytes)
// dlnow - how much was downloaded from the start of the program (bytes)
unsigned int bar_length = 26 ;
Downloader * downloader = static_cast < Downloader * > ( clientp ) ;
double rate ; // average download speed in B/s
// trying to get rate and setting to NaN if it fails
if ( CURLE_OK ! = curl_easy_getinfo ( downloader - > curlhandle , CURLINFO_SPEED_DOWNLOAD , & rate ) )
rate = std : : numeric_limits < double > : : quiet_NaN ( ) ;
// (Shmerl): this flag is needed to catch the case before anything was downloaded on resume,
// and there is no way to calculate the fraction, so we set to 0 (otherwise it'd be 1).
// This is to prevent the progress bar from jumping to 100% and then to lower value.
// It's visually better to jump from 0% to higher one.
bool starting = ( ( 0.0 = = dlnow ) & & ( 0.0 = = dltotal ) ) ;
// (Shmerl): DEBUG: strange thing - when resuming a file which is already downloaded, dlnow is correctly 0.0
// but dltotal is 389.0! This messes things up in the progress bar not showing the very last bar as full.
// enable this debug line to test the problem:
//
// printf("\r\033[0K dlnow: %0.2f, dltotal: %0.2f\r", dlnow, dltotal); fflush(stdout); return 0;
//
// For now making a quirky workaround and setting dltotal to 0.0 in that case.
// It's probably better to find a real fix.
if ( ( 0.0 = = dlnow ) & & ( 389.0 = = dltotal ) ) dltotal = 0.0 ;
// setting full dlwnow and dltotal
double offset = static_cast < double > ( downloader - > getResumePosition ( ) ) ;
if ( offset > 0 )
{
dlnow + = offset ;
dltotal + = offset ;
}
// Update progress bar every 100ms
if ( downloader - > timer . getTimeBetweenUpdates ( ) > = 100 | | dlnow = = dltotal )
{
downloader - > timer . reset ( ) ;
bptime : : time_duration eta ( bptime : : seconds ( ( long ) ( ( dltotal - dlnow ) / rate ) ) ) ;
std : : stringstream eta_ss ;
if ( eta . hours ( ) > 23 )
{
eta_ss < < eta . hours ( ) / 24 < < " d " < <
std : : setfill ( ' 0 ' ) < < std : : setw ( 2 ) < < eta . hours ( ) % 24 < < " h " < <
std : : setfill ( ' 0 ' ) < < std : : setw ( 2 ) < < eta . minutes ( ) < < " m " < <
std : : setfill ( ' 0 ' ) < < std : : setw ( 2 ) < < eta . seconds ( ) < < " s " ;
}
else if ( eta . hours ( ) > 0 )
{
eta_ss < < eta . hours ( ) < < " h " < <
std : : setfill ( ' 0 ' ) < < std : : setw ( 2 ) < < eta . minutes ( ) < < " m " < <
std : : setfill ( ' 0 ' ) < < std : : setw ( 2 ) < < eta . seconds ( ) < < " s " ;
}
else if ( eta . minutes ( ) > 0 )
{
eta_ss < < eta . minutes ( ) < < " m " < <
std : : setfill ( ' 0 ' ) < < std : : setw ( 2 ) < < eta . seconds ( ) < < " s " ;
}
else
{
eta_ss < < eta . seconds ( ) < < " s " ;
}
// Create progressbar
double fraction = starting ? 0.0 : dlnow / dltotal ;
// assuming that config is provided.
printf ( " \033 [0K \r %3.0f%% " , fraction * 100 ) ;
downloader - > progressbar - > draw ( bar_length , fraction ) ;
2013-11-19 12:47:10 +02:00
// Download rate unit conversion
2013-08-27 10:01:04 +03:00
std : : string rate_unit ;
if ( rate > 1048576 ) // 1 MB
{
rate / = 1048576 ;
rate_unit = " MB/s " ;
}
else
{
rate / = 1024 ;
rate_unit = " kB/s " ;
}
printf ( " %0.2f/%0.2fMB @ %0.2f%s ETA: %s \r " , dlnow / 1024 / 1024 , dltotal / 1024 / 1024 , rate , rate_unit . c_str ( ) , eta_ss . str ( ) . c_str ( ) ) ;
2013-03-15 22:46:16 +02:00
fflush ( stdout ) ;
}
return 0 ;
}
size_t Downloader : : writeMemoryCallback ( char * ptr , size_t size , size_t nmemb , void * userp ) {
std : : ostringstream * stream = ( std : : ostringstream * ) userp ;
size_t count = size * nmemb ;
stream - > write ( ptr , count ) ;
return count ;
}
size_t Downloader : : writeData ( void * ptr , size_t size , size_t nmemb , FILE * stream )
{
return fwrite ( ptr , size , nmemb , stream ) ;
}
size_t Downloader : : readData ( void * ptr , size_t size , size_t nmemb , FILE * stream )
{
return fread ( ptr , size , nmemb , stream ) ;
}
size_t Downloader : : getResumePosition ( )
{
return this - > resume_position ;
}
// Login to GOG website
int Downloader : : HTTP_Login ( const std : : string & email , const std : : string & password )
{
int res = 0 ;
2013-03-16 23:35:57 +02:00
std : : string postdata ;
std : : ostringstream memory ;
std : : string buk ;
// Get "buk" for login form
std : : string json = this - > getResponse ( " http://www.gog.com/user/ajax/?a=get " ) ;
Json : : Value root ;
Json : : Reader * jsonparser = new Json : : Reader ;
bool parsingSuccessful = jsonparser - > parse ( json , root ) ;
if ( ! parsingSuccessful )
{
2013-03-15 22:46:16 +02:00
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::HTTP_Login) " < < std : : endl < < json < < std : : endl ;
# endif
2013-03-16 23:35:57 +02:00
std : : cout < < jsonparser - > getFormatedErrorMessages ( ) ;
return res = 0 ;
}
2013-03-15 22:46:16 +02:00
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::HTTP_Login) " < < std : : endl < < root < < std : : endl ;
# endif
2013-03-16 23:35:57 +02:00
buk = root [ " buk " ] . asString ( ) ;
2013-03-15 22:46:16 +02:00
2013-03-16 23:35:57 +02:00
//Create postdata - escape characters in email/password to support special characters
postdata = " log_email= " + ( std : : string ) curl_easy_escape ( curlhandle , email . c_str ( ) , email . size ( ) )
2013-03-15 22:46:16 +02:00
+ " &log_password= " + ( std : : string ) curl_easy_escape ( curlhandle , password . c_str ( ) , password . size ( ) )
+ " &buk= " + ( std : : string ) curl_easy_escape ( curlhandle , buk . c_str ( ) , buk . size ( ) ) ;
curl_easy_setopt ( curlhandle , CURLOPT_URL , " https://secure.gog.com/login " ) ;
curl_easy_setopt ( curlhandle , CURLOPT_POST , 1 ) ;
curl_easy_setopt ( curlhandle , CURLOPT_POSTFIELDS , postdata . c_str ( ) ) ;
2013-11-02 21:10:01 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_WRITEFUNCTION , Downloader : : writeMemoryCallback ) ;
2013-03-15 22:46:16 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_WRITEDATA , & memory ) ;
curl_easy_setopt ( curlhandle , CURLOPT_NOPROGRESS , 1 ) ;
2013-03-16 21:48:52 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_MAXREDIRS , 1 ) ;
2013-03-16 20:48:35 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_POSTREDIR , CURL_REDIR_POST_ALL ) ;
2013-06-01 17:22:37 +03:00
CURLcode result = curl_easy_perform ( curlhandle ) ;
2013-03-15 22:46:16 +02:00
memory . str ( std : : string ( ) ) ;
2013-06-01 17:22:37 +03:00
if ( result ! = CURLE_OK )
{
// Expected to hit maximum amount of redirects so don't print error on it
if ( result ! = CURLE_TOO_MANY_REDIRECTS )
std : : cout < < curl_easy_strerror ( result ) < < std : : endl ;
}
2013-03-15 22:46:16 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_HTTPGET , 1 ) ;
2013-03-16 21:48:52 +02:00
curl_easy_setopt ( curlhandle , CURLOPT_MAXREDIRS , - 1 ) ;
2013-03-15 22:46:16 +02:00
json = this - > getResponse ( " http://www.gog.com/user/ajax/?a=get " ) ;
2013-03-16 23:35:57 +02:00
parsingSuccessful = jsonparser - > parse ( json , root ) ;
if ( ! parsingSuccessful )
{
2013-03-15 22:46:16 +02:00
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::HTTP_Login) " < < std : : endl < < json < < std : : endl ;
# endif
2013-03-16 23:35:57 +02:00
std : : cout < < jsonparser - > getFormatedErrorMessages ( ) ;
return res = 0 ;
}
2013-03-15 22:46:16 +02:00
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::HTTP_Login) " < < std : : endl < < root < < std : : endl ;
# endif
2013-06-01 17:22:37 +03:00
if ( root [ " user " ] [ " email " ] . asString ( ) = = email & & ( result = = CURLE_OK | | result = = CURLE_TOO_MANY_REDIRECTS ) )
2013-03-16 23:35:57 +02:00
res = 1 ; // Login successful
else
res = 0 ; // Login failed
2013-03-15 22:46:16 +02:00
2013-03-16 23:35:57 +02:00
delete jsonparser ;
2013-03-15 22:46:16 +02:00
2013-03-16 23:35:57 +02:00
return res ;
2013-03-15 22:46:16 +02:00
}
// Get list of games from account page
2013-10-13 11:54:35 +03:00
std : : vector < std : : pair < std : : string , std : : string > > Downloader : : getGames ( )
2013-03-15 22:46:16 +02:00
{
2013-10-13 11:54:35 +03:00
std : : vector < std : : pair < std : : string , std : : string > > games ;
2013-03-15 22:46:16 +02:00
Json : : Value root ;
Json : : Reader * jsonparser = new Json : : Reader ;
int i = 1 ;
std : : string html = " " ;
std : : string page_html = " " ;
do
{
std : : string response = this - > getResponse ( " https://secure.gog.com/en/account/ajax?a=gamesShelfMore&s=title&q=&t=0&p= " + std : : to_string ( i ) ) ;
// Parse JSON
if ( ! jsonparser - > parse ( response , root ) )
{
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::getGames) " < < std : : endl < < response < < std : : endl ;
# endif
std : : cout < < jsonparser - > getFormatedErrorMessages ( ) ;
delete jsonparser ;
exit ( 1 ) ;
}
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::getGames) " < < std : : endl < < root < < std : : endl ;
# endif
page_html = root [ " html " ] . asString ( ) ;
html + = page_html ;
i + + ;
} while ( ! page_html . empty ( ) ) ;
delete jsonparser ;
// Parse HTML to get game names
2013-03-16 23:35:57 +02:00
htmlcxx : : HTML : : ParserDom parser ;
tree < htmlcxx : : HTML : : Node > dom = parser . parseTree ( html ) ;
tree < htmlcxx : : HTML : : Node > : : iterator it = dom . begin ( ) ;
tree < htmlcxx : : HTML : : Node > : : iterator end = dom . end ( ) ;
for ( ; it ! = end ; + + it )
{
if ( it - > tagName ( ) = = " div " )
{
it - > parseAttributes ( ) ;
std : : string classname = it - > attribute ( " class " ) . second ;
if ( classname = = " shelf_game " )
{
// Game name is contained in data-gameindex attribute
std : : string game = it - > attribute ( " data-gameindex " ) . second ;
2013-10-13 11:54:35 +03:00
std : : string gameid = it - > attribute ( " data-gameid " ) . second ;
if ( ! game . empty ( ) & & ! gameid . empty ( ) )
games . push_back ( std : : make_pair ( game , gameid ) ) ;
2013-03-16 23:35:57 +02:00
}
}
}
2013-03-15 22:46:16 +02:00
return games ;
}
// Get list of free games
2013-10-13 11:54:35 +03:00
std : : vector < std : : pair < std : : string , std : : string > > Downloader : : getFreeGames ( )
2013-03-15 22:46:16 +02:00
{
2013-09-04 19:58:03 +03:00
Json : : Value root ;
Json : : Reader * jsonparser = new Json : : Reader ;
2013-10-13 11:54:35 +03:00
std : : vector < std : : pair < std : : string , std : : string > > games ;
2013-11-17 11:01:35 +02:00
std : : string json = this - > getResponse ( " https://secure.gog.com/games/ajax?a=search&f={ \" price \" :[ \" free \" ], \" sort \" : \" title \" }&p=1&t=all " ) ;
2013-09-04 19:58:03 +03:00
// Parse JSON
if ( ! jsonparser - > parse ( json , root ) )
{
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::getFreeGames) " < < std : : endl < < json < < std : : endl ;
# endif
std : : cout < < jsonparser - > getFormatedErrorMessages ( ) ;
delete jsonparser ;
exit ( 1 ) ;
}
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::getFreeGames) " < < std : : endl < < root < < std : : endl ;
# endif
std : : string html = root [ " result " ] [ " html " ] . asString ( ) ;
delete jsonparser ;
2013-03-15 22:46:16 +02:00
// Parse HTML to get game names
2013-03-16 23:35:57 +02:00
htmlcxx : : HTML : : ParserDom parser ;
tree < htmlcxx : : HTML : : Node > dom = parser . parseTree ( html ) ;
tree < htmlcxx : : HTML : : Node > : : iterator it = dom . begin ( ) ;
tree < htmlcxx : : HTML : : Node > : : iterator end = dom . end ( ) ;
for ( ; it ! = end ; + + it )
{
2013-09-04 19:58:03 +03:00
if ( it - > tagName ( ) = = " span " )
2013-03-16 23:35:57 +02:00
{
it - > parseAttributes ( ) ;
std : : string classname = it - > attribute ( " class " ) . second ;
2013-09-04 19:58:03 +03:00
if ( classname = = " gog-price game-owned " )
2013-03-16 23:35:57 +02:00
{
2013-09-04 19:58:03 +03:00
// Game name is contained in data-gameindex attribute
std : : string game = it - > attribute ( " data-gameindex " ) . second ;
2013-10-13 11:54:35 +03:00
std : : string id = it - > attribute ( " data-gameid " ) . second ;
if ( ! game . empty ( ) & & ! id . empty ( ) )
games . push_back ( std : : make_pair ( game , id ) ) ;
2013-03-16 23:35:57 +02:00
}
}
}
2013-03-15 22:46:16 +02:00
return games ;
}
2013-10-13 11:54:35 +03:00
std : : vector < gameFile > Downloader : : getExtras ( const std : : string & gamename , const std : : string & gameid )
{
Json : : Value root ;
Json : : Reader * jsonparser = new Json : : Reader ;
std : : vector < gameFile > extras ;
std : : string gameDataUrl = " https://secure.gog.com/en/account/ajax?a=gamesListDetails&g= " + gameid ;
std : : string json = this - > getResponse ( gameDataUrl ) ;
// Parse JSON
if ( ! jsonparser - > parse ( json , root ) )
{
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::getExtras) " < < std : : endl < < json < < std : : endl ;
# endif
std : : cout < < jsonparser - > getFormatedErrorMessages ( ) ;
delete jsonparser ;
exit ( 1 ) ;
}
# ifdef DEBUG
std : : cerr < < " DEBUG INFO (Downloader::getExtras) " < < std : : endl < < root < < std : : endl ;
# endif
std : : string html = root [ " details " ] [ " html " ] . asString ( ) ;
delete jsonparser ;
htmlcxx : : HTML : : ParserDom parser ;
tree < htmlcxx : : HTML : : Node > dom = parser . parseTree ( html ) ;
tree < htmlcxx : : HTML : : Node > : : iterator it = dom . begin ( ) ;
tree < htmlcxx : : HTML : : Node > : : iterator end = dom . end ( ) ;
for ( ; it ! = end ; + + it )
{
if ( it - > tagName ( ) = = " a " )
{
it - > parseAttributes ( ) ;
std : : string href = it - > attribute ( " href " ) . second ;
// Extra links https://secure.gog.com/downlink/file/gamename/id_number
if ( href . find ( " /downlink/file/ " + gamename + " / " ) ! = std : : string : : npos )
{
std : : string id , name , path ;
id . assign ( href . begin ( ) + href . find_last_of ( " / " ) + 1 , href . end ( ) ) ;
// Get path from download link
std : : string url = gogAPI - > getExtraLink ( gamename , id ) ;
2014-02-17 16:49:29 +02:00
if ( url . find ( " /extras/ " ) ! = std : : string : : npos )
{
path . assign ( url . begin ( ) + url . find ( " /extras/ " ) , url . begin ( ) + url . find_first_of ( " ? " ) ) ;
path = " / " + gamename + path ;
}
else
{
path . assign ( url . begin ( ) + url . find_last_of ( " / " ) + 1 , url . begin ( ) + url . find_first_of ( " ? " ) ) ;
path = " / " + gamename + " /extras/ " + path ;
}
2013-10-13 11:54:35 +03:00
// Get name from path
name . assign ( path . begin ( ) + path . find_last_of ( " / " ) + 1 , path . end ( ) ) ;
extras . push_back (
gameFile ( false ,
id ,
name ,
path ,
std : : string ( )
)
) ;
}
}
}
return extras ;
}
2013-10-14 22:31:12 +03:00
void Downloader : : checkOrphans ( )
{
2013-11-26 12:59:42 +02:00
// Always check everything when checking for orphaned files
config . bInstallers = true ;
config . bExtras = true ;
config . bPatches = true ;
config . bLanguagePacks = true ;
if ( this - > games . empty ( ) )
this - > getGameDetails ( ) ;
2013-10-14 22:31:12 +03:00
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 ;
boost : : filesystem : : path path ( config . sDirectory + games [ i ] . gamename ) ;
std : : vector < boost : : filesystem : : path > filepath_vector ;
try
{
if ( boost : : filesystem : : exists ( path ) )
{
if ( boost : : filesystem : : is_directory ( path ) )
{
2013-11-19 12:47:10 +02:00
// Recursively iterate over files in directory
2013-10-14 22:31:12 +03:00
boost : : filesystem : : recursive_directory_iterator end_iter ;
boost : : filesystem : : recursive_directory_iterator dir_iter ( path ) ;
while ( dir_iter ! = end_iter )
{
if ( boost : : filesystem : : is_regular_file ( dir_iter - > status ( ) ) )
{
std : : string filename = dir_iter - > path ( ) . filename ( ) . string ( ) ;
2013-12-22 13:02:48 +02:00
boost : : regex expression ( config . sOrphanRegex ) ; // Limit to files matching the regex
2013-10-14 22:31:12 +03:00
boost : : match_results < std : : string : : const_iterator > what ;
if ( boost : : regex_search ( filename , what , expression ) )
filepath_vector . push_back ( dir_iter - > path ( ) ) ;
}
dir_iter + + ;
}
}
}
else
std : : cout < < path < < " does not exist " < < std : : endl ;
}
catch ( const boost : : filesystem : : filesystem_error & ex )
{
std : : cout < < ex . what ( ) < < std : : endl ;
}
if ( ! filepath_vector . empty ( ) )
{
for ( unsigned int j = 0 ; j < filepath_vector . size ( ) ; + + j )
{
2013-11-19 12:47:10 +02:00
bool bFoundFile = false ; // Assume that the file is orphaned
// Check installers
2013-10-14 22:31:12 +03:00
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 )
2013-11-19 12:47:10 +02:00
{ // Check extras
2013-10-14 22:31:12 +03:00
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 )
2013-11-19 12:47:10 +02:00
{ // Check patches
2013-10-14 22:31:12 +03:00
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 )
2013-11-19 12:47:10 +02:00
{ // Check language packs
2013-10-14 22:31:12 +03:00
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 )
orphans . push_back ( filepath_vector [ j ] . string ( ) ) ;
}
}
}
std : : cout < < std : : endl ;
if ( ! orphans . empty ( ) )
{
for ( unsigned int i = 0 ; i < orphans . size ( ) ; + + i )
{
std : : cout < < orphans [ i ] < < std : : endl ;
}
}
else
{
std : : cout < < " No orphaned files " < < std : : endl ;
}
return ;
}
2013-11-14 15:40:59 +02:00
// Check status of files
void Downloader : : checkStatus ( )
{
2013-11-26 12:59:42 +02:00
if ( this - > games . empty ( ) )
this - > getGameDetails ( ) ;
2013-11-14 15:40:59 +02:00
for ( unsigned int i = 0 ; i < games . size ( ) ; + + i )
{
2013-11-19 12:47:10 +02:00
if ( config . bInstallers )
2013-11-14 15:40:59 +02:00
{
for ( unsigned int j = 0 ; j < games [ i ] . installers . size ( ) ; + + j )
{
boost : : filesystem : : path filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . installers [ j ] . path , games [ i ] . gamename ) ;
std : : string remoteHash ;
std : : string localHash ;
bool bHashOK = true ; // assume hash OK
size_t filesize ;
localHash = this - > getLocalFileHash ( filepath . string ( ) ) ;
remoteHash = this - > getRemoteFileHash ( games [ i ] . gamename , games [ i ] . installers [ j ] . id ) ;
if ( boost : : filesystem : : exists ( filepath ) )
{
filesize = boost : : filesystem : : file_size ( filepath ) ;
if ( remoteHash ! = localHash )
bHashOK = false ;
std : : cout < < ( bHashOK ? " OK " : " MD5 " ) < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < " " < < filesize < < " " < < localHash < < std : : endl ;
}
else
{
std : : cout < < " ND " < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < std : : endl ;
}
}
}
2013-11-19 12:47:10 +02:00
if ( config . bExtras )
2013-11-14 15:40:59 +02:00
{
for ( unsigned int j = 0 ; j < games [ i ] . extras . size ( ) ; + + j )
{
2014-02-23 03:16:10 +02:00
boost : : filesystem : : path filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . extras [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " extras " : " " ) ;
2013-11-14 15:40:59 +02:00
std : : string localHash = this - > getLocalFileHash ( filepath . string ( ) ) ;
size_t filesize ;
if ( boost : : filesystem : : exists ( filepath ) )
{
filesize = boost : : filesystem : : file_size ( filepath ) ;
std : : cout < < " OK " < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < " " < < filesize < < " " < < localHash < < std : : endl ;
}
else
{
std : : cout < < " ND " < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < std : : endl ;
}
}
}
2013-11-19 12:47:10 +02:00
if ( config . bPatches )
2013-11-14 15:40:59 +02:00
{
for ( unsigned int j = 0 ; j < games [ i ] . patches . size ( ) ; + + j )
{
2014-02-23 03:16:10 +02:00
boost : : filesystem : : path filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . patches [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " patches " : " " ) ;
2013-11-14 15:40:59 +02:00
std : : string localHash = this - > getLocalFileHash ( filepath . string ( ) ) ;
size_t filesize ;
if ( boost : : filesystem : : exists ( filepath ) )
{
filesize = boost : : filesystem : : file_size ( filepath ) ;
std : : cout < < " OK " < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < " " < < filesize < < " " < < localHash < < std : : endl ;
}
else
{
std : : cout < < " ND " < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < std : : endl ;
}
}
}
2013-11-19 12:47:10 +02:00
if ( config . bLanguagePacks )
2013-11-14 15:40:59 +02:00
{
for ( unsigned int j = 0 ; j < games [ i ] . languagepacks . size ( ) ; + + j )
{
2014-02-23 03:16:10 +02:00
boost : : filesystem : : path filepath = Util : : makeFilepath ( config . sDirectory , games [ i ] . languagepacks [ j ] . path , games [ i ] . gamename , config . bSubDirectories ? " languagepacks " : " " ) ;
2013-11-14 15:40:59 +02:00
std : : string localHash = this - > getLocalFileHash ( filepath . string ( ) ) ;
size_t filesize ;
if ( boost : : filesystem : : exists ( filepath ) )
{
filesize = boost : : filesystem : : file_size ( filepath ) ;
std : : cout < < " OK " < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < " " < < filesize < < " " < < localHash < < std : : endl ;
}
else
{
std : : cout < < " ND " < < games [ i ] . gamename < < " " < < filepath . filename ( ) . string ( ) < < std : : endl ;
}
}
}
}
return ;
}
std : : string Downloader : : getLocalFileHash ( const std : : string & filepath )
{
std : : string localHash ;
boost : : filesystem : : path path = filepath ;
boost : : filesystem : : path local_xml_file = config . sXMLDirectory + " / " + path . filename ( ) . string ( ) + " .xml " ;
if ( boost : : filesystem : : exists ( local_xml_file ) )
{
TiXmlDocument local_xml ;
local_xml . LoadFile ( local_xml_file . string ( ) ) ;
TiXmlNode * fileNodeLocal = local_xml . FirstChild ( " file " ) ;
if ( fileNodeLocal )
{
TiXmlElement * fileElemLocal = fileNodeLocal - > ToElement ( ) ;
localHash = fileElemLocal - > Attribute ( " md5 " ) ;
}
}
else
{
if ( boost : : filesystem : : exists ( path ) )
{
localHash = Util : : getFileHash ( path . string ( ) , RHASH_MD5 ) ;
}
}
return localHash ;
}
std : : string Downloader : : getRemoteFileHash ( const std : : string & gamename , const std : : string & id )
{
std : : string remoteHash ;
std : : string xml_data = gogAPI - > getXML ( gamename , id ) ;
if ( ! xml_data . empty ( ) )
{
TiXmlDocument remote_xml ;
remote_xml . Parse ( xml_data . c_str ( ) ) ;
TiXmlNode * fileNodeRemote = remote_xml . FirstChild ( " file " ) ;
if ( fileNodeRemote )
{
TiXmlElement * fileElemRemote = fileNodeRemote - > ToElement ( ) ;
remoteHash = fileElemRemote - > Attribute ( " md5 " ) ;
}
}
return remoteHash ;
}