Added support for multiple platforms in the <platform> tag.

You can also use any of the whitespace characters as extension delimiters now (", \n\r\t").
This commit is contained in:
Aloshi 2014-06-03 19:38:10 -05:00
parent ae129c5325
commit 28e7f357f7
7 changed files with 85 additions and 45 deletions

View file

@ -147,14 +147,17 @@ All systems must be contained within the <systemList> tag.-->
All subdirectories (and non-recursive links) will be included. -->
<path>~/roms/snes</path>
<!-- A list of extensions to search for, delimited by a space. You MUST include the period! It's also case sensitive. -->
<!-- A list of extensions to search for, delimited by any of the whitespace characters (", \r\n\t").
You MUST include the period at the start of the extension! It's also case sensitive. -->
<extension>.smc .sfc .SMC .SFC</extension>
<!-- The shell command executed when a game is selected. A few special tags are replaced if found in a command, like %ROM%. -->
<command>snesemulator %ROM%</command>
<!-- This example would run the bash command "snesemulator /home/user/roms/snes/Super\ Mario\ World.sfc". -->
<!-- The platform to use when scraping. You can see the full list of accepted platforms in src/PlatformIds.cpp. -->
<!-- The platform(s) to use when scraping. You can see the full list of accepted platforms in src/PlatformIds.cpp.
It's case sensitive, but everything is lowercase. This tag is optional.
You can use multiple platforms too, delimited with any of the whitespace characters (", \r\n\t"), eg: "<platform>genesis, megadrive</platform>" -->
<platform>snes</platform>
</system>
</systemList>

View file

@ -56,7 +56,7 @@ FileData::~FileData()
std::string FileData::getCleanName() const
{
std::string stem = mPath.stem().generic_string();
if(mSystem && mSystem->getPlatformId() == PlatformIds::ARCADE || mSystem->getPlatformId() == PlatformIds::NEOGEO)
if(mSystem && mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO))
stem = PlatformIds::getCleanMameName(stem.c_str());
return removeParenthesis(stem);

View file

@ -18,7 +18,7 @@ std::vector<SystemData*> SystemData::sSystemVector;
namespace fs = boost::filesystem;
SystemData::SystemData(const std::string& name, const std::string& fullName, const std::string& startPath, const std::vector<std::string>& extensions,
const std::string& command, PlatformIds::PlatformId platformId)
const std::string& command, const std::vector<PlatformIds::PlatformId>& platformIds)
{
mName = name;
mFullName = fullName;
@ -33,7 +33,7 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, con
mSearchExtensions = extensions;
mLaunchCommand = command;
mPlatformId = platformId;
mPlatformIds = platformIds;
mRootFolder = new FileData(FOLDER, mStartPath, this);
mRootFolder->metadata.set("name", mFullName);
@ -200,6 +200,23 @@ void SystemData::populateFolder(FileData* folder)
}
}
std::vector<std::string> readList(const std::string& str, const char* delims = " \t\r\n,")
{
std::vector<std::string> ret;
size_t prevOff = str.find_first_not_of(delims, 0);
size_t off = str.find_first_of(delims, prevOff);
while(off != std::string::npos || prevOff != std::string::npos)
{
ret.push_back(str.substr(prevOff, off - prevOff));
prevOff = str.find_first_not_of(delims, off);
off = str.find_first_of(delims, prevOff);
}
return ret;
}
//creates systems from information located in a config file
bool SystemData::loadConfig()
{
@ -244,26 +261,34 @@ bool SystemData::loadConfig()
fullname = system.child("fullname").text().get();
path = system.child("path").text().get();
//convert extensions list from a string into a vector of strings
const pugi::char_t* extStr = system.child("extension").text().get();
std::vector<std::string> extensions;
std::vector<char> buff(strlen(extStr) + 1);
strcpy(buff.data(), extStr);
char* ext = strtok(buff.data(), " ");
while(ext != NULL)
{
extensions.push_back(ext);
ext = strtok(NULL, " ");
}
// convert extensions list from a string into a vector of strings
std::vector<std::string> extensions = readList(system.child("extension").text().get());
cmd = system.child("command").text().get();
const char* platformIdString = system.child("platform").text().as_string();
platformId = PlatformIds::getPlatformId(platformIdString);
// platform id list
const char* platformList = system.child("platform").text().get();
std::vector<std::string> platformStrs = readList(platformList);
std::vector<PlatformIds::PlatformId> platformIds;
for(auto it = platformStrs.begin(); it != platformStrs.end(); it++)
{
const char* str = it->c_str();
PlatformIds::PlatformId platformId = PlatformIds::getPlatformId(str);
if(platformId == PlatformIds::PLATFORM_IGNORE)
{
// when platform is ignore, do not allow other platforms
platformIds.clear();
platformIds.push_back(platformId);
break;
}
// if there appears to be an actual platform ID supplied but it didn't match the list, warn
if(platformIdString != NULL && platformIdString[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN)
LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \"" << platformIdString << "\")";
if(str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN)
LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \"" << str << "\" from list \"" << platformList << "\")";
else if(platformId != PlatformIds::PLATFORM_UNKNOWN)
platformIds.push_back(platformId);
}
//validate
if(name.empty() || path.empty() || extensions.empty() || cmd.empty())
@ -276,7 +301,7 @@ bool SystemData::loadConfig()
boost::filesystem::path genericPath(path);
path = genericPath.generic_string();
SystemData* newSys = new SystemData(name, fullname, path, extensions, cmd, platformId);
SystemData* newSys = new SystemData(name, fullname, path, extensions, cmd, platformIds);
if(newSys->getRootFolder()->getChildren().size() == 0)
{
LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it.";
@ -309,7 +334,8 @@ void SystemData::writeExampleConfig(const std::string& path)
" <!-- The path to start searching for ROMs in. '~' will be expanded to $HOME on Linux or %HOMEPATH% on Windows. -->\n"
" <path>~/roms/nes</path>\n"
"\n"
" <!-- A list of extensions to search for, delimited by a space. You MUST include the period! It's also case sensitive. -->\n"
" <!-- A list of extensions to search for, delimited by any of the whitespace characters (\", \\r\\n\\t\").\n"
" You MUST include the period at the start of the extension! It's also case sensitive. -->\n"
" <extension>.nes .NES</extension>\n"
"\n"
" <!-- The shell command executed when a game is selected. A few special tags are replaced if found in a command:\n"
@ -318,7 +344,9 @@ void SystemData::writeExampleConfig(const std::string& path)
" %ROM_RAW% is the raw, unescaped path to the ROM. -->\n"
" <command>retroarch -L ~/cores/libretro-fceumm.so %ROM%</command>\n"
"\n"
" <!-- The platform to use when scraping. You can see the full list of accepted platforms in src/PlatformIds.cpp. This tag is optional. -->\n"
" <!-- The platform to use when scraping. You can see the full list of accepted platforms in src/PlatformIds.cpp.\n"
" It's case sensitive, but everything is lowercase. This tag is optional.\n"
" You can use multiple platforms too, delimited with any of the whitespace characters (\", \\r\\n\\t\"), eg: \"<platform>genesis, megadrive</platform>\" -->\n"
" <platform>nes</platform>\n"
"\n"
" </system>\n"

View file

@ -1,5 +1,4 @@
#ifndef _SYSTEMDATA_H_
#define _SYSTEMDATA_H_
#pragma once
#include <vector>
#include <string>
@ -13,7 +12,7 @@ class SystemData
{
public:
SystemData(const std::string& name, const std::string& fullName, const std::string& startPath, const std::vector<std::string>& extensions,
const std::string& command, PlatformIds::PlatformId platformId = PlatformIds::PLATFORM_UNKNOWN);
const std::string& command, const std::vector<PlatformIds::PlatformId>& platformIds);
~SystemData();
inline FileData* getRootFolder() const { return mRootFolder; };
@ -21,7 +20,10 @@ public:
inline const std::string& getFullName() const { return mFullName; }
inline const std::string& getStartPath() const { return mStartPath; }
inline const std::vector<std::string>& getExtensions() const { return mSearchExtensions; }
inline PlatformIds::PlatformId getPlatformId() const { return mPlatformId; }
inline const std::vector<PlatformIds::PlatformId>& getPlatformIds() const { return mPlatformIds; }
inline bool hasPlatformId(PlatformIds::PlatformId id) { return std::find(mPlatformIds.begin(), mPlatformIds.end(), id) != mPlatformIds.end(); }
inline const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; }
std::string getGamelistPath(bool forWrite) const;
@ -67,12 +69,10 @@ private:
std::string mStartPath;
std::vector<std::string> mSearchExtensions;
std::string mLaunchCommand;
PlatformIds::PlatformId mPlatformId;
std::vector<PlatformIds::PlatformId> mPlatformIds;
std::shared_ptr<ThemeData> mTheme;
void populateFolder(FileData* folder);
FileData* mRootFolder;
};
#endif

View file

@ -129,7 +129,7 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window, MetaDataList* md, const std::vector
std::vector< std::shared_ptr<ButtonComponent> > buttons;
if(scraperParams.system->getPlatformId() != PlatformIds::PLATFORM_IGNORE)
if(!scraperParams.system->hasPlatformId(PlatformIds::PLATFORM_IGNORE))
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SCRAPE", "scrape", std::bind(&GuiMetaDataEd::fetch, this)));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "SAVE", "save", [&] { save(); delete this; }));

View file

@ -24,8 +24,8 @@ GuiScraperStart::GuiScraperStart(Window* window) : GuiComponent(window),
mSystems = std::make_shared< OptionListComponent<SystemData*> >(mWindow, "SCRAPE THESE SYSTEMS", true);
for(auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); it++)
{
if((*it)->getPlatformId() != PlatformIds::PLATFORM_IGNORE)
mSystems->add((*it)->getFullName(), *it, (*it)->getPlatformId() != PlatformIds::PLATFORM_UNKNOWN);
if(!(*it)->hasPlatformId(PlatformIds::PLATFORM_IGNORE))
mSystems->add((*it)->getFullName(), *it, !(*it)->getPlatformIds().empty());
}
mMenu.addWithLabel("Systems", mSystems);
@ -44,7 +44,7 @@ void GuiScraperStart::pressedStart()
std::vector<SystemData*> sys = mSystems->getSelectedObjects();
for(auto it = sys.begin(); it != sys.end(); it++)
{
if((*it)->getPlatformId() == PlatformIds::PLATFORM_UNKNOWN)
if((*it)->getPlatformIds().empty())
{
mWindow->pushGui(new GuiMsgBox(mWindow,
strToUpper("Warning: some of your selected systems do not have a platform set. Results may be even more inaccurate than usual!\nContinue anyway?"),

View file

@ -70,20 +70,29 @@ void thegamesdb_generate_scraper_requests(const ScraperSearchParams& params, std
path += "name=" + HttpReq::urlEncode(cleanName);
if(params.system->getPlatformId() != PLATFORM_UNKNOWN)
if(params.system->getPlatformIds().empty())
{
auto platformIt = gamesdb_platformid_map.find(params.system->getPlatformId());
if(platformIt != gamesdb_platformid_map.end())
// no platform specified, we're done
requests.push(std::unique_ptr<ScraperRequest>(new ScraperHttpRequest(results, path, &thegamesdb_process_httpreq)));
}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(platformIt->second);
path += HttpReq::urlEncode(mapIt->second);
}else{
LOG(LogWarning) << "TheGamesDB scraper warning - no support for platform " << getPlatformName(*platformIt);
}
else{
LOG(LogWarning) << "TheGamesDB scraper warning - no support for platform " << getPlatformName(params.system->getPlatformId());
}
}
requests.push(std::unique_ptr<ScraperRequest>(new ScraperHttpRequest(results, path, &thegamesdb_process_httpreq)));
}
}
}
void thegamesdb_process_httpreq(const std::unique_ptr<HttpReq>& req, std::vector<ScraperSearchResult>& results)