2014-06-25 16:29:58 +00:00
|
|
|
#include "scrapers/GamesDBScraper.h"
|
|
|
|
#include "Log.h"
|
2017-04-03 18:45:28 +00:00
|
|
|
#include "pugixml/src/pugixml.hpp"
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "MetaData.h"
|
|
|
|
#include "Settings.h"
|
|
|
|
#include "Util.h"
|
|
|
|
#include <boost/assign.hpp>
|
|
|
|
|
|
|
|
using namespace PlatformIds;
|
|
|
|
const std::map<PlatformId, const char*> gamesdb_platformid_map = boost::assign::map_list_of
|
|
|
|
(THREEDO, "3DO")
|
|
|
|
(AMIGA, "Amiga")
|
|
|
|
(AMSTRAD_CPC, "Amstrad CPC")
|
|
|
|
// missing apple2
|
|
|
|
(ARCADE, "Arcade")
|
|
|
|
// missing atari 800
|
|
|
|
(ATARI_2600, "Atari 2600")
|
|
|
|
(ATARI_5200, "Atari 5200")
|
|
|
|
(ATARI_7800, "Atari 7800")
|
|
|
|
(ATARI_JAGUAR, "Atari Jaguar")
|
|
|
|
(ATARI_JAGUAR_CD, "Atari Jaguar CD")
|
|
|
|
(ATARI_LYNX, "Atari Lynx")
|
|
|
|
// missing atari ST/STE/Falcon
|
|
|
|
(ATARI_XE, "Atari XE")
|
|
|
|
(COLECOVISION, "Colecovision")
|
|
|
|
(COMMODORE_64, "Commodore 64")
|
|
|
|
(INTELLIVISION, "Intellivision")
|
|
|
|
(MAC_OS, "Mac OS")
|
|
|
|
(XBOX, "Microsoft Xbox")
|
|
|
|
(XBOX_360, "Microsoft Xbox 360")
|
2017-05-23 20:22:34 +00:00
|
|
|
(MSX, "MSX")
|
2017-03-03 21:34:57 +00:00
|
|
|
(NEOGEO, "Neo Geo")
|
2014-06-25 16:29:58 +00:00
|
|
|
(NEOGEO_POCKET, "Neo Geo Pocket")
|
|
|
|
(NEOGEO_POCKET_COLOR, "Neo Geo Pocket Color")
|
|
|
|
(NINTENDO_3DS, "Nintendo 3DS")
|
|
|
|
(NINTENDO_64, "Nintendo 64")
|
|
|
|
(NINTENDO_DS, "Nintendo DS")
|
2017-05-22 18:28:04 +00:00
|
|
|
(FAMICOM_DISK_SYSTEM, "Famicom Disk System")
|
2014-06-25 16:29:58 +00:00
|
|
|
(NINTENDO_ENTERTAINMENT_SYSTEM, "Nintendo Entertainment System (NES)")
|
|
|
|
(GAME_BOY, "Nintendo Game Boy")
|
|
|
|
(GAME_BOY_ADVANCE, "Nintendo Game Boy Advance")
|
|
|
|
(GAME_BOY_COLOR, "Nintendo Game Boy Color")
|
|
|
|
(NINTENDO_GAMECUBE, "Nintendo GameCube")
|
|
|
|
(NINTENDO_WII, "Nintendo Wii")
|
|
|
|
(NINTENDO_WII_U, "Nintendo Wii U")
|
2017-05-23 20:22:34 +00:00
|
|
|
(NINTENDO_VIRTUAL_BOY, "Nintendo Virtual Boy")
|
|
|
|
(NINTENDO_GAME_AND_WATCH, "Game & Watch")
|
2014-06-25 16:29:58 +00:00
|
|
|
(PC, "PC")
|
|
|
|
(SEGA_32X, "Sega 32X")
|
|
|
|
(SEGA_CD, "Sega CD")
|
|
|
|
(SEGA_DREAMCAST, "Sega Dreamcast")
|
|
|
|
(SEGA_GAME_GEAR, "Sega Game Gear")
|
|
|
|
(SEGA_GENESIS, "Sega Genesis")
|
|
|
|
(SEGA_MASTER_SYSTEM, "Sega Master System")
|
|
|
|
(SEGA_MEGA_DRIVE, "Sega Mega Drive")
|
|
|
|
(SEGA_SATURN, "Sega Saturn")
|
2017-05-23 20:22:34 +00:00
|
|
|
(SEGA_SG1000, "SEGA SG-1000")
|
2014-06-25 16:29:58 +00:00
|
|
|
(PLAYSTATION, "Sony Playstation")
|
|
|
|
(PLAYSTATION_2, "Sony Playstation 2")
|
|
|
|
(PLAYSTATION_3, "Sony Playstation 3")
|
|
|
|
(PLAYSTATION_4, "Sony Playstation 4")
|
|
|
|
(PLAYSTATION_VITA, "Sony Playstation Vita")
|
2017-05-09 23:31:30 +00:00
|
|
|
(PLAYSTATION_PORTABLE, "Sony Playstation Portable")
|
2014-06-25 16:29:58 +00:00
|
|
|
(SUPER_NINTENDO, "Super Nintendo (SNES)")
|
|
|
|
(TURBOGRAFX_16, "TurboGrafx 16")
|
|
|
|
(WONDERSWAN, "WonderSwan")
|
|
|
|
(WONDERSWAN_COLOR, "WonderSwan Color")
|
2017-05-23 20:22:34 +00:00
|
|
|
(ZX_SPECTRUM, "Sinclair ZX Spectrum")
|
|
|
|
(VIDEOPAC_ODYSSEY2, "Magnavox Odyssey 2")
|
|
|
|
(VECTREX, "Vectrex")
|
|
|
|
(TRS80_COLOR_COMPUTER, "TRS-80 Color Computer")
|
|
|
|
(TANDY, "TRS-80 Color Computer");
|
2014-06-25 16:29:58 +00:00
|
|
|
|
|
|
|
void thegamesdb_generate_scraper_requests(const ScraperSearchParams& params, std::queue< std::unique_ptr<ScraperRequest> >& requests,
|
|
|
|
std::vector<ScraperSearchResult>& results)
|
|
|
|
{
|
2017-09-03 19:11:38 +00:00
|
|
|
std::string path;
|
2017-05-22 18:58:30 +00:00
|
|
|
bool usingGameID = false;
|
2014-06-25 16:29:58 +00:00
|
|
|
|
|
|
|
std::string cleanName = params.nameOverride;
|
2017-09-03 19:11:38 +00:00
|
|
|
if (!cleanName.empty() && cleanName.substr(0,3) == "id:")
|
2017-05-04 15:33:45 +00:00
|
|
|
{
|
2017-09-03 19:11:38 +00:00
|
|
|
std::string gameID = cleanName.substr(3);
|
|
|
|
path = "thegamesdb.net/api/GetGame.php?id=" + HttpReq::urlEncode(gameID);
|
|
|
|
usingGameID = true;
|
|
|
|
}else{
|
|
|
|
if (cleanName.empty())
|
|
|
|
cleanName = params.game->getCleanName();
|
|
|
|
path += "thegamesdb.net/api/GetGamesList.php?name=" + HttpReq::urlEncode(cleanName);
|
2017-05-04 15:33:45 +00:00
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2017-09-03 19:11:38 +00:00
|
|
|
if(usingGameID)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2017-09-03 19:11:38 +00:00
|
|
|
// if we have the ID already, we don't need the GetGameList request
|
2014-06-25 16:29:58 +00:00
|
|
|
requests.push(std::unique_ptr<ScraperRequest>(new TheGamesDBRequest(results, path)));
|
2017-09-03 19:11:38 +00:00
|
|
|
}else if(params.system->getPlatformIds().empty()){
|
|
|
|
// no platform specified, we're done
|
|
|
|
requests.push(std::unique_ptr<ScraperRequest>(new TheGamesDBRequest(requests, results, path)));
|
2014-06-25 16:29:58 +00:00
|
|
|
}else{
|
|
|
|
// go through the list, we need to split this into multiple requests
|
|
|
|
// because TheGamesDB API either sucks or I don't know how to use it properly...
|
|
|
|
std::string urlBase = path;
|
|
|
|
auto& platforms = params.system->getPlatformIds();
|
|
|
|
for(auto platformIt = platforms.begin(); platformIt != platforms.end(); platformIt++)
|
|
|
|
{
|
|
|
|
path = urlBase;
|
|
|
|
auto mapIt = gamesdb_platformid_map.find(*platformIt);
|
|
|
|
if(mapIt != gamesdb_platformid_map.end())
|
|
|
|
{
|
|
|
|
path += "&platform=";
|
|
|
|
path += HttpReq::urlEncode(mapIt->second);
|
|
|
|
}else{
|
|
|
|
LOG(LogWarning) << "TheGamesDB scraper warning - no support for platform " << getPlatformName(*platformIt);
|
|
|
|
}
|
|
|
|
|
2017-09-03 19:11:38 +00:00
|
|
|
requests.push(std::unique_ptr<ScraperRequest>(new TheGamesDBRequest(requests, results, path)));
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TheGamesDBRequest::process(const std::unique_ptr<HttpReq>& req, std::vector<ScraperSearchResult>& results)
|
|
|
|
{
|
|
|
|
assert(req->status() == HttpReq::REQ_SUCCESS);
|
|
|
|
|
|
|
|
pugi::xml_document doc;
|
|
|
|
pugi::xml_parse_result parseResult = doc.load(req->getContent().c_str());
|
|
|
|
if(!parseResult)
|
|
|
|
{
|
|
|
|
std::stringstream ss;
|
2017-09-03 19:11:38 +00:00
|
|
|
ss << "TheGamesDBRequest - Error parsing XML. \n\t" << parseResult.description() << "";
|
2014-06-25 16:29:58 +00:00
|
|
|
std::string err = ss.str();
|
|
|
|
setError(err);
|
|
|
|
LOG(LogError) << err;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-03 19:11:38 +00:00
|
|
|
if (isGameRequest())
|
|
|
|
processGame(doc, results);
|
|
|
|
else
|
|
|
|
processList(doc, results);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TheGamesDBRequest::processGame(const pugi::xml_document& xmldoc, std::vector<ScraperSearchResult>& results)
|
|
|
|
{
|
|
|
|
pugi::xml_node data = xmldoc.child("Data");
|
2014-06-25 16:29:58 +00:00
|
|
|
|
|
|
|
std::string baseImageUrl = data.child("baseImgUrl").text().get();
|
|
|
|
|
|
|
|
pugi::xml_node game = data.child("Game");
|
2017-09-03 19:11:38 +00:00
|
|
|
if(game)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
|
|
|
ScraperSearchResult result;
|
|
|
|
|
|
|
|
result.mdl.set("name", game.child("GameTitle").text().get());
|
|
|
|
result.mdl.set("desc", game.child("Overview").text().get());
|
|
|
|
|
|
|
|
boost::posix_time::ptime rd = string_to_ptime(game.child("ReleaseDate").text().get(), "%m/%d/%Y");
|
|
|
|
result.mdl.setTime("releasedate", rd);
|
|
|
|
|
|
|
|
result.mdl.set("developer", game.child("Developer").text().get());
|
|
|
|
result.mdl.set("publisher", game.child("Publisher").text().get());
|
|
|
|
result.mdl.set("genre", game.child("Genres").first_child().text().get());
|
|
|
|
result.mdl.set("players", game.child("Players").text().get());
|
|
|
|
|
|
|
|
if(Settings::getInstance()->getBool("ScrapeRatings") && game.child("Rating"))
|
|
|
|
{
|
|
|
|
float ratingVal = (game.child("Rating").text().as_int() / 10.0f);
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << ratingVal;
|
|
|
|
result.mdl.set("rating", ss.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
pugi::xml_node images = game.child("Images");
|
|
|
|
|
|
|
|
if(images)
|
|
|
|
{
|
|
|
|
pugi::xml_node art = images.find_child_by_attribute("boxart", "side", "front");
|
|
|
|
|
|
|
|
if(art)
|
|
|
|
{
|
|
|
|
result.thumbnailUrl = baseImageUrl + art.attribute("thumb").as_string();
|
|
|
|
result.imageUrl = baseImageUrl + art.text().get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
results.push_back(result);
|
2017-09-03 19:11:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TheGamesDBRequest::processList(const pugi::xml_document& xmldoc, std::vector<ScraperSearchResult>& results)
|
|
|
|
{
|
|
|
|
assert(mRequestQueue != nullptr);
|
|
|
|
|
|
|
|
pugi::xml_node data = xmldoc.child("Data");
|
|
|
|
pugi::xml_node game = data.child("Game");
|
|
|
|
|
|
|
|
// limit the number of results per platform, not in total.
|
|
|
|
// otherwise if the first platform returns >= 7 games
|
|
|
|
// but the second platform contains the relevant game,
|
|
|
|
// the relevant result would not be shown.
|
|
|
|
for(int i = 0; game && i < MAX_SCRAPER_RESULTS; i++)
|
|
|
|
{
|
|
|
|
std::string id = game.child("id").text().get();
|
|
|
|
std::string path = "thegamesdb.net/api/GetGame.php?id=" + id;
|
|
|
|
|
|
|
|
mRequestQueue->push(std::unique_ptr<ScraperRequest>(new TheGamesDBRequest(results, path)));
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
game = game.next_sibling("Game");
|
|
|
|
}
|
|
|
|
}
|