8#include <QRegularExpression>
26#define LOC_ERR QString("MythGame:GAMEHANDLER Error: ")
27#define LOC QString("MythGame:GAMEHANDLER: ")
45 if (!query.
exec(
"SELECT DISTINCT playername FROM gameplayers "
46 "WHERE playername <> '';"))
51 QString name = query.
value(0).toString();
65 query.
prepare(
"SELECT rompath, workingpath, commandline, screenshots, "
66 "gameplayerid, gametype, extensions, spandisks "
67 "FROM gameplayers WHERE playername = :SYSTEM ");
80 .remove(
" ").split(
",", Qt::SkipEmptyParts);
109 query.
prepare(
"SELECT crc, category, year, country, name, "
110 "description, publisher, platform, version, "
111 "binfile FROM romdb WHERE platform = :GAMETYPE;");
119 key = QString(
"%1:%2")
120 .arg(query.
value(0).toString(),
121 query.
value(9).toString());
123 query.
value(1).toString(),
124 query.
value(2).toString(),
125 query.
value(3).toString(),
126 query.
value(4).toString(),
127 query.
value(5).toString(),
128 query.
value(6).toString(),
129 query.
value(7).toString(),
130 query.
value(8).toString());
136 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"No romDB data read from "
137 "database for gametype %1 . Not imported?").arg(
GameType));
141 LOG(VB_GENERAL, LOG_INFO,
LOC +
142 QString(
"Loaded %1 items from romDB Database") .arg(
m_romDB.count()));
147 QString* Country, QString* CRC32, QString* GameName,
148 QString *Plot, QString *Publisher, QString *Version,
149 QString* Fanart, QString* Boxart)
156 LOG(VB_GENERAL, LOG_DEBUG,
"Key = " + key);
160 *Year = tr(
"19xx",
"Default game year");
161 *Country = tr(
"Unknown",
"Unknown country");
162 *GameName = tr(
"Unknown",
"Unknown game name");
163 *Genre = tr(
"Unknown",
"Unknown genre");
164 *Plot = tr(
"Unknown",
"Unknown plot");
165 *Publisher = tr(
"Unknown",
"Unknown publisher");
166 *Version = tr(
"0",
"Default game version");
170 if (!(*CRC32).isEmpty())
174 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"ROMDB FOUND for %1 - %2")
175 .arg(
m_romDB[key].GameName(), key));
177 *Country =
m_romDB[key].Country();
179 *Publisher =
m_romDB[key].Publisher();
180 *GameName =
m_romDB[key].GameName();
181 *Version =
m_romDB[key].Version();
185 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"NO ROMDB FOUND for %1 (%2)")
191 if ((*Genre == tr(
"Unknown",
"Unknown genre")) || (*Genre).isEmpty())
192 *Genre = tr(
"Unknown %1",
"Unknown genre")
199 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Purging %1 - %2")
207 query.
prepare(
"DELETE FROM gamemetadata WHERE "
208 "romname = :ROMNAME AND "
209 "rompath = :ROMPATH ");
233 tr(
"%1 appears to be missing.\n"
234 "Remove it from the database?")
235 .arg(
filename), popupStack,
"chooseSystemPopup");
237 if (removalPopup->Create())
239 removalPopup->SetReturnEvent(
this,
"removalPopup");
241 removalPopup->AddButton(tr(
"No"));
242 removalPopup->AddButton(tr(
"No to all"));
243 removalPopup->AddButtonV(tr(
"Yes"), QVariant::fromValue(
scan));
244 removalPopup->AddButtonV(tr(
"Yes to all"), QVariant::fromValue(
scan));
253static void updateDisplayRom(
const QString& romname,
int display,
const QString& Systemname)
256 query.
prepare(
"UPDATE gamemetadata SET display = :DISPLAY "
257 "WHERE romname = :ROMNAME AND `system` = :SYSTEM");
271 query.
prepare(
"UPDATE gamemetadata SET diskcount = :DISKCOUNT "
272 "WHERE romname = :ROMNAME AND gametype = :GAMETYPE ");
283static void updateGameName(
const QString& romname,
const QString& GameName,
const QString& Systemname)
286 query.
prepare(
"UPDATE gamemetadata SET GameName = :GAMENAME "
287 "WHERE romname = :ROMNAME AND `system` = :SYSTEM ");
303 static const QRegularExpression multiDiskRGXP {
"[0-4]$" };
309 for (
const auto &
GameType : std::as_const(updatelist))
311 LOG(VB_GENERAL, LOG_NOTICE,
312 LOC + QString(
"Update gametype %1").arg(
GameType));
314 query.
prepare(
"SELECT romname,`system`,spandisks,gamename FROM "
315 "gamemetadata,gameplayers WHERE "
316 "gamemetadata.gametype = :GAMETYPE AND "
317 "playername = `system` ORDER BY romname");
325 QString RomName = query.
value(0).toString();
326 QString System = query.
value(1).toString();
327 int spandisks = query.
value(2).toInt();
328 QString GameName = query.
value(3).toString();
336 int pos = RomName.lastIndexOf(
".");
339 extlength = RomName.length() - pos;
342 basename = RomName.mid(pos,1);
345 if (basename.contains(multiDiskRGXP))
347 pos = (RomName.length() - extlength) - 1;
348 basename = RomName.left(pos);
350 if (basename.right(1) ==
".")
351 basename = RomName.left(pos - 1);
358 if (basename == lastrom)
371 if (basename != GameName)
376 if (basename == lastrom)
392 QString message = tr(
"Updating %1 (%2) ROM database")
416 for (
const auto & game : std::as_const(
m_gameMap))
423 GetMetadata(handler, game.RomFullPath(), &Genre, &Year, &Country, &CRC32, &GameName,
424 &Plot, &Publisher, &Version, &Fanart, &Boxart);
430 Genre = tr(
"Unknown %1",
"Unknown genre").arg(handler->
GameType());
431 Country = tr(
"Unknown",
"Unknown country");
433 Year = tr(
"19xx",
"Default game year");
434 GameName = tr(
"Unknown",
"Unknown game name");
435 Plot = tr(
"Unknown",
"Unknown plot");
436 Publisher = tr(
"Unknown",
"Unknown publisher");
437 Version = tr(
"0",
"Default game version");
442 if (GameName == tr(
"Unknown",
"Unknown game name"))
443 GameName = game.GameName();
445 int suffixPos = game.Rom().lastIndexOf(QChar(
'.'));
446 QString baseName = game.Rom();
449 baseName = game.Rom().left(suffixPos);
451 baseName = screenShotPath +
"/" + baseName;
453 if (QFile(baseName +
".png").
exists())
454 ScreenShot = baseName +
".png";
455 else if (QFile(baseName +
".jpg").
exists())
456 ScreenShot = baseName +
".jpg";
457 else if (QFile(baseName +
".gif").
exists())
458 ScreenShot = baseName +
".gif";
463 LOG(VB_GENERAL, LOG_INFO, QString(
"file %1 - genre %2 ")
464 .arg(iter.data().Rom()).arg(Genre));
465 LOG(VB_GENERAL, LOG_INFO, QString(
"screenshot %1").arg(ScreenShot));
468 query.
prepare(
"INSERT INTO gamemetadata "
469 "(`system`, romname, gamename, genre, year, gametype, "
470 "rompath, country, crc_value, diskcount, display, plot, "
471 "publisher, version, fanart, boxart, screenshot) "
472 "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
473 ":GAMETYPE, :ROMPATH, :COUNTRY, :CRC32, '1', '1', :PLOT, :PUBLISHER, :VERSION, "
474 ":FANART, :BOXART, :SCREENSHOT)");
494 "insert gamemetadata", query);
496 else if ((game.FoundLoc() ==
inDatabase) && (removalprompt))
518 query.
prepare(
"SELECT romname,rompath,gamename FROM gamemetadata "
519 "WHERE `system` = :SYSTEM");
528 QString message = tr(
"Verifying %1 files...").arg(handler->
SystemName());
538 QString RomName = query.
value(0).toString();
540 QString GameName = query.
value(2).toString();
541 if (!RomName.isEmpty())
573 QDir RomDir(directory);
576 if (!RomDir.isReadable())
579 RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
580 QFileInfoList List = RomDir.entryInfoList();
581 for (
const auto & Info : std::as_const(List))
591 QRegularExpression r {
592 "^" + Info.suffix() +
"$",
593 QRegularExpression::CaseInsensitiveOption };
596 std::ranges::copy_if(std::as_const(exts), std::back_inserter(result),
597 [&r](
const QString& extension){
return extension.contains(r); } );
598 if (result.isEmpty())
612 tr(
"This will clear all game metadata from the database. Are you sure "
613 "you want to do this?"), popupStack,
"clearAllPopup");
615 if (clearPopup->Create())
617 clearPopup->SetReturnEvent(
this,
"clearAllPopup");
618 clearPopup->AddButton(tr(
"No"));
619 clearPopup->AddButton(tr(
"Yes"));
631 QDir RomDir(directory);
634 if (!RomDir.isReadable())
637 RomDir.setSorting( QDir:: DirsFirst | QDir::Name );
638 RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
639 QFileInfoList List = RomDir.entryInfoList();
640 for (
const auto & Info : std::as_const(List))
642 QString RomName = Info.fileName();
643 QString GameName = Info.completeBaseName();
653 QRegularExpression r {
654 "^" + Info.suffix() +
"$",
655 QRegularExpression::CaseInsensitiveOption };
658 std::ranges::copy_if(std::as_const(exts), std::back_inserter(result),
659 [&r](
const QString& extension){
return extension.contains(r); } );
660 if (result.isEmpty())
665 GameName, Info.absoluteDir().path());
667 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found ROM : (%1) - %2")
670 *filecount = *filecount + 1;
688 LOG(VB_GENERAL, LOG_ERR,
LOC +
689 QString(
"ROM Path does not exist: %1")
704 QString message = tr(
"Scanning for %1 games...")
709 if (busyDialog->Create())
710 popupStack->
AddScreen(busyDialog,
false);
714 busyDialog =
nullptr;
727 LOG(VB_GENERAL, LOG_INFO,
LOC +
728 QString(
"PC Game %1").arg(handler->
SystemName()));
732 QString message = tr(
"Scanning for %1 games...")
770 QStringList updatelist;
772 for (
auto *handler : std::as_const(*
handlers))
777 handler->processGames(handler);
779 if (handler->needRebuild())
780 updatelist.append(handler->GameType());
784 if (!updatelist.isEmpty())
793 for (
auto *handler : std::as_const(*
handlers))
797 if (rominfo->
System() == handler->SystemName())
807 if (systemname.isEmpty())
810 for (
auto *handler : std::as_const(*
handlers))
814 if (handler->SystemName() == systemname)
826 if (!systemname.isEmpty())
833 if (handler ==
nullptr)
846 QString arg =
"\"" + romdata->
Rompath() +
847 "/" + romdata->
Romname() +
"\"";
852 if (exec.contains(
"%s") || handler->
SpanDisks())
854 exec = exec.replace(
"%s",arg);
858 static const QRegularExpression rxp {
"%d[0-4]" };
860 if (exec.contains(rxp))
868 std::array<QString,7> diskid {
"%d0",
"%d1",
"%d2",
"%d3",
"%d4",
"%d5",
"%d6" };
870 for (
int disk = 1; disk <= romdata->
DiskCount(); disk++)
872 rom = QString(
"\"%1/%2%3.%4\"")
873 .arg(romdata->
Rompath(), basename,
874 QString::number(disk), extension);
875 exec = exec.replace(diskid[disk],rom);
879 exec = exec.replace(
"%d1",arg);
886 exec = exec +
" \"" +
898 LOG(VB_GENERAL, LOG_ERR,
LOC +
899 QString(
"Failed to change to specified Working Directory: %1")
903 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Launching Game : %1 : %2")
909 QStringList cmdlist = exec.split(
";");
910 if (cmdlist.count() > 0)
912 for (
const auto & cmd : std::as_const(cmdlist))
914 LOG(VB_GENERAL, LOG_INFO,
LOC +
915 QString(
"Executing : %1").arg(cmd));
921 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Executing : %1").arg(exec));
947 QString resultid = dce->GetId();
950 if (resultid ==
"removalPopup")
952 int buttonNum = dce->GetResult();
970 else if (resultid ==
"clearAllPopup")
972 int buttonNum = dce->GetResult();
988 if (!query.
exec(
"DELETE FROM gamemetadata;"))
990 "delete gamemetadata", query);
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)