8#include <QRegularExpression>
26#define LOC_ERR QString("MythGame:GAMEHANDLER Error: ")
27#define LOC QString("MythGame:GAMEHANDLER: ")
47 if (!query.
exec(
"SELECT DISTINCT playername FROM gameplayers "
48 "WHERE playername <> '';"))
53 QString name = query.
value(0).toString();
67 query.
prepare(
"SELECT rompath, workingpath, commandline, screenshots, "
68 "gameplayerid, gametype, extensions, spandisks "
69 "FROM gameplayers WHERE playername = :SYSTEM ");
82 .remove(
" ").split(
",", Qt::SkipEmptyParts);
111 query.
prepare(
"SELECT crc, category, year, country, name, "
112 "description, publisher, platform, version, "
113 "binfile FROM romdb WHERE platform = :GAMETYPE;");
121 key = QString(
"%1:%2")
122 .arg(query.
value(0).toString(),
123 query.
value(9).toString());
125 query.
value(1).toString(),
126 query.
value(2).toString(),
127 query.
value(3).toString(),
128 query.
value(4).toString(),
129 query.
value(5).toString(),
130 query.
value(6).toString(),
131 query.
value(7).toString(),
132 query.
value(8).toString());
138 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"No romDB data read from "
139 "database for gametype %1 . Not imported?").arg(
GameType));
143 LOG(VB_GENERAL, LOG_INFO,
LOC +
144 QString(
"Loaded %1 items from romDB Database") .arg(
m_romDB.count()));
149 QString* Country, QString* CRC32, QString* GameName,
150 QString *Plot, QString *Publisher, QString *Version,
151 QString* Fanart, QString* Boxart)
158 LOG(VB_GENERAL, LOG_DEBUG,
"Key = " + key);
162 *Year = tr(
"19xx",
"Default game year");
163 *Country = tr(
"Unknown",
"Unknown country");
164 *GameName = tr(
"Unknown",
"Unknown game name");
165 *Genre = tr(
"Unknown",
"Unknown genre");
166 *Plot = tr(
"Unknown",
"Unknown plot");
167 *Publisher = tr(
"Unknown",
"Unknown publisher");
168 *Version = tr(
"0",
"Default game version");
172 if (!(*CRC32).isEmpty())
176 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"ROMDB FOUND for %1 - %2")
177 .arg(
m_romDB[key].GameName(), key));
179 *Country =
m_romDB[key].Country();
181 *Publisher =
m_romDB[key].Publisher();
182 *GameName =
m_romDB[key].GameName();
183 *Version =
m_romDB[key].Version();
187 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"NO ROMDB FOUND for %1 (%2)")
193 if ((*Genre == tr(
"Unknown",
"Unknown genre")) || (*Genre).isEmpty())
194 *Genre = tr(
"Unknown %1",
"Unknown genre")
201 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Purging %1 - %2")
209 query.
prepare(
"DELETE FROM gamemetadata WHERE "
210 "romname = :ROMNAME AND "
211 "rompath = :ROMPATH ");
235 tr(
"%1 appears to be missing.\n"
236 "Remove it from the database?")
237 .arg(
filename), popupStack,
"chooseSystemPopup");
239 if (removalPopup->Create())
241 removalPopup->SetReturnEvent(
this,
"removalPopup");
243 removalPopup->AddButton(tr(
"No"));
244 removalPopup->AddButton(tr(
"No to all"));
245 removalPopup->AddButtonV(tr(
"Yes"), QVariant::fromValue(
scan));
246 removalPopup->AddButtonV(tr(
"Yes to all"), QVariant::fromValue(
scan));
255static void updateDisplayRom(
const QString& romname,
int display,
const QString& Systemname)
258 query.
prepare(
"UPDATE gamemetadata SET display = :DISPLAY "
259 "WHERE romname = :ROMNAME AND `system` = :SYSTEM");
273 query.
prepare(
"UPDATE gamemetadata SET diskcount = :DISKCOUNT "
274 "WHERE romname = :ROMNAME AND gametype = :GAMETYPE ");
285static void updateGameName(
const QString& romname,
const QString& GameName,
const QString& Systemname)
288 query.
prepare(
"UPDATE gamemetadata SET GameName = :GAMENAME "
289 "WHERE romname = :ROMNAME AND `system` = :SYSTEM ");
305 static const QRegularExpression multiDiskRGXP {
"[0-4]$" };
311 for (
const auto &
GameType : std::as_const(updatelist))
313 LOG(VB_GENERAL, LOG_NOTICE,
314 LOC + QString(
"Update gametype %1").arg(
GameType));
316 query.
prepare(
"SELECT romname,`system`,spandisks,gamename FROM "
317 "gamemetadata,gameplayers WHERE "
318 "gamemetadata.gametype = :GAMETYPE AND "
319 "playername = `system` ORDER BY romname");
327 QString RomName = query.
value(0).toString();
328 QString System = query.
value(1).toString();
329 int spandisks = query.
value(2).toInt();
330 QString GameName = query.
value(3).toString();
338 int pos = RomName.lastIndexOf(
".");
341 extlength = RomName.length() - pos;
344 basename = RomName.mid(pos,1);
347 if (basename.contains(multiDiskRGXP))
349 pos = (RomName.length() - extlength) - 1;
350 basename = RomName.left(pos);
352 if (basename.right(1) ==
".")
353 basename = RomName.left(pos - 1);
360 if (basename == lastrom)
373 if (basename != GameName)
378 if (basename == lastrom)
394 QString message = tr(
"Updating %1 (%2) ROM database")
418 for (
const auto & game : std::as_const(
m_gameMap))
425 GetMetadata(handler, game.RomFullPath(), &Genre, &Year, &Country, &CRC32, &GameName,
426 &Plot, &Publisher, &Version, &Fanart, &Boxart);
432 Genre = tr(
"Unknown %1",
"Unknown genre").arg(handler->
GameType());
433 Country = tr(
"Unknown",
"Unknown country");
435 Year = tr(
"19xx",
"Default game year");
436 GameName = tr(
"Unknown",
"Unknown game name");
437 Plot = tr(
"Unknown",
"Unknown plot");
438 Publisher = tr(
"Unknown",
"Unknown publisher");
439 Version = tr(
"0",
"Default game version");
444 if (GameName == tr(
"Unknown",
"Unknown game name"))
445 GameName = game.GameName();
447 int suffixPos = game.Rom().lastIndexOf(QChar(
'.'));
448 QString baseName = game.Rom();
451 baseName = game.Rom().left(suffixPos);
453 baseName = screenShotPath +
"/" + baseName;
455 if (QFile(baseName +
".png").
exists())
456 ScreenShot = baseName +
".png";
457 else if (QFile(baseName +
".jpg").
exists())
458 ScreenShot = baseName +
".jpg";
459 else if (QFile(baseName +
".gif").
exists())
460 ScreenShot = baseName +
".gif";
465 LOG(VB_GENERAL, LOG_INFO, QString(
"file %1 - genre %2 ")
466 .arg(iter.data().Rom()).arg(Genre));
467 LOG(VB_GENERAL, LOG_INFO, QString(
"screenshot %1").arg(ScreenShot));
470 query.
prepare(
"INSERT INTO gamemetadata "
471 "(`system`, romname, gamename, genre, year, gametype, "
472 "rompath, country, crc_value, diskcount, display, plot, "
473 "publisher, version, fanart, boxart, screenshot) "
474 "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
475 ":GAMETYPE, :ROMPATH, :COUNTRY, :CRC32, '1', '1', :PLOT, :PUBLISHER, :VERSION, "
476 ":FANART, :BOXART, :SCREENSHOT)");
496 "insert gamemetadata", query);
498 else if ((game.FoundLoc() ==
inDatabase) && removalprompt)
520 query.
prepare(
"SELECT romname,rompath,gamename FROM gamemetadata "
521 "WHERE `system` = :SYSTEM");
530 QString message = tr(
"Verifying %1 files...").arg(handler->
SystemName());
540 QString RomName = query.
value(0).toString();
542 QString GameName = query.
value(2).toString();
543 if (!RomName.isEmpty())
575 QDir RomDir(directory);
578 if (!RomDir.isReadable())
581 RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
582 QFileInfoList List = RomDir.entryInfoList();
583 for (
const auto & Info : std::as_const(List))
593 QRegularExpression r {
594 "^" + Info.suffix() +
"$",
595 QRegularExpression::CaseInsensitiveOption };
598 std::ranges::copy_if(std::as_const(exts), std::back_inserter(result),
599 [&r](
const QString& extension){
return extension.contains(r); } );
600 if (result.isEmpty())
614 tr(
"This will clear all game metadata from the database. Are you sure "
615 "you want to do this?"), popupStack,
"clearAllPopup");
617 if (clearPopup->Create())
619 clearPopup->SetReturnEvent(
this,
"clearAllPopup");
620 clearPopup->AddButton(tr(
"No"));
621 clearPopup->AddButton(tr(
"Yes"));
633 QDir RomDir(directory);
636 if (!RomDir.isReadable())
639 RomDir.setSorting( QDir:: DirsFirst | QDir::Name );
640 RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
641 QFileInfoList List = RomDir.entryInfoList();
642 for (
const auto & Info : std::as_const(List))
644 QString RomName = Info.fileName();
645 QString GameName = Info.completeBaseName();
655 QRegularExpression r {
656 "^" + Info.suffix() +
"$",
657 QRegularExpression::CaseInsensitiveOption };
660 std::ranges::copy_if(std::as_const(exts), std::back_inserter(result),
661 [&r](
const QString& extension){
return extension.contains(r); } );
662 if (result.isEmpty())
667 GameName, Info.absoluteDir().path());
669 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found ROM : (%1) - %2")
672 *filecount = *filecount + 1;
692 LOG(VB_GENERAL, LOG_ERR,
LOC +
693 QString(
"ROM Path does not exist: %1")
708 QString message = tr(
"Scanning for %1 games...")
713 if (busyDialog->Create())
715 popupStack->
AddScreen(busyDialog,
false);
720 busyDialog =
nullptr;
733 LOG(VB_GENERAL, LOG_INFO,
LOC +
734 QString(
"PC Game %1").arg(handler->
SystemName()));
738 QString message = tr(
"Scanning for %1 games...")
776 QStringList updatelist;
778 for (
auto *handler : std::as_const(*
handlers))
783 handler->processGames(handler);
785 if (handler->needRebuild())
786 updatelist.append(handler->GameType());
790 if (!updatelist.isEmpty())
799 for (
auto *handler : std::as_const(*
handlers))
803 if (rominfo->
System() == handler->SystemName())
813 if (systemname.isEmpty())
816 for (
auto *handler : std::as_const(*
handlers))
820 if (handler->SystemName() == systemname)
832 if (!systemname.isEmpty())
839 if (handler ==
nullptr)
852 QString arg =
"\"" + romdata->
Rompath() +
853 "/" + romdata->
Romname() +
"\"";
858 if (exec.contains(
"%s") || handler->
SpanDisks())
860 exec = exec.replace(
"%s",arg);
864 static const QRegularExpression rxp {
"%d[0-4]" };
866 if (exec.contains(rxp))
874 std::array<QString,7> diskid {
"%d0",
"%d1",
"%d2",
"%d3",
"%d4",
"%d5",
"%d6" };
876 for (
int disk = 1; disk <= romdata->
DiskCount(); disk++)
878 rom = QString(
"\"%1/%2%3.%4\"")
879 .arg(romdata->
Rompath(), basename,
880 QString::number(disk), extension);
881 exec = exec.replace(diskid[disk],rom);
885 exec = exec.replace(
"%d1",arg);
892 exec = exec +
" \"" +
904 LOG(VB_GENERAL, LOG_ERR,
LOC +
905 QString(
"Failed to change to specified Working Directory: %1")
909 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Launching Game : %1 : %2")
915 QStringList cmdlist = exec.split(
";");
916 if (cmdlist.count() > 0)
918 for (
const auto & cmd : std::as_const(cmdlist))
920 LOG(VB_GENERAL, LOG_INFO,
LOC +
921 QString(
"Executing : %1").arg(cmd));
927 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Executing : %1").arg(exec));
953 QString resultid = dce->GetId();
956 if (resultid ==
"removalPopup")
958 int buttonNum = dce->GetResult();
976 else if (resultid ==
"clearAllPopup")
978 int buttonNum = dce->GetResult();
994 if (!query.
exec(
"DELETE FROM gamemetadata;"))
996 "delete gamemetadata", query);
1020#include "moc_gamehandler.cpp"
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
static GameHandler * GetHandler(RomInfo *rominfo)
static GameHandler * newHandler(QString name)
static void processAllGames(void)
void VerifyGameDB(GameHandler *handler)
MythUIProgressDialog * m_progressDlg
static GameHandler * getHandler(uint i)
QString SystemCmdLine() const
static RomInfo * CreateRomInfo(RomInfo *parent)
QString SystemName() const
static GameHandler * s_newInstance
static void Launchgame(RomInfo *romdata, const QString &systemname)
static int buildFileCount(const QString &directory, GameHandler *handler)
static void registerHandler(GameHandler *handler)
void setRebuild(bool setrebuild)
static void updateSettings(GameHandler *handler)
void customEvent(QEvent *event) override
QString SystemRomPath() const
void promptForRemoval(const GameScan &scan)
QString SystemWorkingPath() const
static void clearAllMetadata(void)
void processGames(GameHandler *handler)
void GetMetadata(GameHandler *handler, const QString &rom, QString *Genre, QString *Year, QString *Country, QString *CRC32, QString *GameName, QString *Plot, QString *Publisher, QString *Version, QString *Fanart, QString *Boxart)
void clearAllGameData(void)
void UpdateGameDB(GameHandler *handler)
static GameHandler * GetHandlerByName(const QString &systemname)
void InitMetaDataMap(const QString &GameType)
void CreateProgress(const QString &message)
QStringList m_validextensions
void buildFileList(const QString &directory, GameHandler *handler, int *filecount)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Basic menu dialog, message and a list of options.
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
QString RemoveCurrentLocation()
void AddCurrentLocation(const QString &Location)
bool Create(void) override
void SetProgress(uint count)
void SetTotal(uint total)
QString getExtension() const
static void UpdateGameCounts(const QStringList &updatelist)
static void purgeGameDB(const QString &filename, const QString &RomPath)
static void checkHandlers(void)
static QList< GameHandler * > * handlers
static void updateGameName(const QString &romname, const QString &GameName, const QString &Systemname)
static void updateDisplayRom(const QString &romname, int display, const QString &Systemname)
static void updateDiskCount(const QString &romname, int diskcount, const QString &GameType)
static const iso6937table * d
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
MythMainWindow * GetMythMainWindow(void)
@ kMSProcessEvents
process events while waiting
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
MythUIHelper * GetMythUI()
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
def scan(profile, smoonURL, gate)