MythTV master
gamehandler.cpp
Go to the documentation of this file.
1// C++
2#include <utility>
3
4// Qt
5#include <QDir>
6#include <QList>
7#include <QRegularExpression>
8
9// MythTV
11#include <libmythbase/mythdb.h>
19
20// MythGame
21#include "gamehandler.h"
22#include "rominfo.h"
23#include "rom_metadata.h"
24
25#define LOC_ERR QString("MythGame:GAMEHANDLER Error: ")
26#define LOC QString("MythGame:GAMEHANDLER: ")
27
28static QList<GameHandler*> *handlers = nullptr;
29
30static void checkHandlers(void)
31{
32 // If a handlers list doesn't currently exist create one. Otherwise
33 // clear the existing list so that we can regenerate a new one.
34 if (!handlers)
35 handlers = new QList<GameHandler*>;
36 else
37 {
38 while (!handlers->isEmpty())
39 delete handlers->takeFirst();
40 handlers->clear();
41 }
42
44 if (!query.exec("SELECT DISTINCT playername FROM gameplayers "
45 "WHERE playername <> '';"))
46 MythDB::DBError("checkHandlers - selecting playername", query);
47
48 while (query.next())
49 {
50 QString name = query.value(0).toString();
52 }
53}
54
56{
57 return handlers->at(i);
58}
59
61{
63
64 query.prepare("SELECT rompath, workingpath, commandline, screenshots, "
65 "gameplayerid, gametype, extensions, spandisks "
66 "FROM gameplayers WHERE playername = :SYSTEM ");
67
68 query.bindValue(":SYSTEM", handler->SystemName());
69
70 if (query.exec() && query.next())
71 {
72 handler->m_rompath = query.value(0).toString();
73 handler->m_workingpath = query.value(1).toString();
74 handler->m_commandline = query.value(2).toString();
75 handler->m_screenshots = query.value(3).toString();
76 handler->m_gameplayerid = query.value(4).toInt();
77 handler->m_gametype = query.value(5).toString();
78 handler->m_validextensions = query.value(6).toString().trimmed()
79 .remove(" ").split(",", Qt::SkipEmptyParts);
80 handler->m_spandisks = query.value(7).toBool();
81 }
82}
83
85
87{
89 s_newInstance->m_systemname = std::move(name);
90
92
93 return s_newInstance;
94}
95
96// Creates/rebuilds the handler list and then returns the count.
98{
100 return handlers->count();
101}
102
104{
105 QString key;
106
108 query.prepare("SELECT crc, category, year, country, name, "
109 "description, publisher, platform, version, "
110 "binfile FROM romdb WHERE platform = :GAMETYPE;");
111
112 query.bindValue(":GAMETYPE",GameType);
113
114 if (query.exec())
115 {
116 while (query.next())
117 {
118 key = QString("%1:%2")
119 .arg(query.value(0).toString(),
120 query.value(9).toString());
121 m_romDB[key] = RomData(
122 query.value(1).toString(),
123 query.value(2).toString(),
124 query.value(3).toString(),
125 query.value(4).toString(),
126 query.value(5).toString(),
127 query.value(6).toString(),
128 query.value(7).toString(),
129 query.value(8).toString());
130 }
131 }
132
133 if (m_romDB.count() == 0)
134 {
135 LOG(VB_GENERAL, LOG_ERR, LOC + QString("No romDB data read from "
136 "database for gametype %1 . Not imported?").arg(GameType));
137 }
138 else
139 {
140 LOG(VB_GENERAL, LOG_INFO, LOC +
141 QString("Loaded %1 items from romDB Database") .arg(m_romDB.count()));
142 }
143}
144
145void GameHandler::GetMetadata(GameHandler *handler, const QString& rom, QString* Genre, QString* Year,
146 QString* Country, QString* CRC32, QString* GameName,
147 QString *Plot, QString *Publisher, QString *Version,
148 QString* Fanart, QString* Boxart)
149{
150 QString key;
151
152 *CRC32 = crcinfo(rom, handler->GameType(), &key, &m_romDB);
153
154#if 0
155 LOG(VB_GENERAL, LOG_DEBUG, "Key = " + key);
156#endif
157
158 // Set our default values
159 *Year = tr("19xx", "Default game year");
160 *Country = tr("Unknown", "Unknown country");
161 *GameName = tr("Unknown", "Unknown game name");
162 *Genre = tr("Unknown", "Unknown genre");
163 *Plot = tr("Unknown", "Unknown plot");
164 *Publisher = tr("Unknown", "Unknown publisher");
165 *Version = tr("0", "Default game version");
166 (*Fanart).clear();
167 (*Boxart).clear();
168
169 if (!(*CRC32).isEmpty())
170 {
171 if (m_romDB.contains(key))
172 {
173 LOG(VB_GENERAL, LOG_INFO, LOC + QString("ROMDB FOUND for %1 - %2")
174 .arg(m_romDB[key].GameName(), key));
175 *Year = m_romDB[key].Year();
176 *Country = m_romDB[key].Country();
177 *Genre = m_romDB[key].Genre();
178 *Publisher = m_romDB[key].Publisher();
179 *GameName = m_romDB[key].GameName();
180 *Version = m_romDB[key].Version();
181 }
182 else
183 {
184 LOG(VB_GENERAL, LOG_ERR, LOC + QString("NO ROMDB FOUND for %1 (%2)")
185 .arg(rom, *CRC32));
186 }
187
188 };
189
190 if ((*Genre == tr("Unknown", "Unknown genre")) || (*Genre).isEmpty())
191 *Genre = tr("Unknown %1", "Unknown genre")
192 .arg( handler->GameType() );
193
194}
195
196static void purgeGameDB(const QString& filename, const QString& RomPath)
197{
198 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Purging %1 - %2")
199 .arg(RomPath, filename));
200
202
203 // This should have the added benefit of removing the rom from
204 // other games of the same gametype so we wont be asked to remove it
205 // more than once.
206 query.prepare("DELETE FROM gamemetadata WHERE "
207 "romname = :ROMNAME AND "
208 "rompath = :ROMPATH ");
209
210 query.bindValue(":ROMNAME",filename);
211 query.bindValue(":ROMPATH",RomPath);
212
213 if (!query.exec())
214 MythDB::DBError("purgeGameDB", query);
215
216}
217
219{
220 QString filename = scan.Rom();
221 QString RomPath = scan.RomFullPath();
222
223 if (m_removeAll)
225
226 if (m_keepAll || m_removeAll)
227 return;
228
229 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
230 auto *removalPopup = new MythDialogBox(
231 //: %1 is the file name
232 tr("%1 appears to be missing.\n"
233 "Remove it from the database?")
234 .arg(filename), popupStack, "chooseSystemPopup");
235
236 if (removalPopup->Create())
237 {
238 removalPopup->SetReturnEvent(this, "removalPopup");
239
240 removalPopup->AddButton(tr("No"));
241 removalPopup->AddButton(tr("No to all"));
242 removalPopup->AddButtonV(tr("Yes"), QVariant::fromValue(scan));
243 removalPopup->AddButtonV(tr("Yes to all"), QVariant::fromValue(scan));
244 popupStack->AddScreen(removalPopup);
245}
246 else
247 {
248 delete removalPopup;
249 }
250}
251
252static void updateDisplayRom(const QString& romname, int display, const QString& Systemname)
253{
255 query.prepare("UPDATE gamemetadata SET display = :DISPLAY "
256 "WHERE romname = :ROMNAME AND `system` = :SYSTEM");
257
258 query.bindValue(":DISPLAY", display);
259 query.bindValue(":ROMNAME", romname);
260 query.bindValue(":SYSTEM", Systemname);
261
262 if (!query.exec())
263 MythDB::DBError("updateDisplayRom", query);
264
265}
266
267static void updateDiskCount(const QString& romname, int diskcount, const QString& GameType)
268{
270 query.prepare("UPDATE gamemetadata SET diskcount = :DISKCOUNT "
271 "WHERE romname = :ROMNAME AND gametype = :GAMETYPE ");
272
273 query.bindValue(":DISKCOUNT",diskcount);
274 query.bindValue(":ROMNAME", romname);
275 query.bindValue(":GAMETYPE",GameType);
276
277 if (!query.exec())
278 MythDB::DBError("updateDiskCount", query);
279
280}
281
282static void updateGameName(const QString& romname, const QString& GameName, const QString& Systemname)
283{
285 query.prepare("UPDATE gamemetadata SET GameName = :GAMENAME "
286 "WHERE romname = :ROMNAME AND `system` = :SYSTEM ");
287
288 query.bindValue(":GAMENAME", GameName);
289 query.bindValue(":ROMNAME", romname);
290 query.bindValue(":SYSTEM", Systemname);
291
292 if (!query.exec())
293 MythDB::DBError("updateGameName", query);
294
295}
296
297
298static void UpdateGameCounts(const QStringList& updatelist)
299{
301
302 static const QRegularExpression multiDiskRGXP { "[0-4]$" };
303
304 QString lastrom;
305 QString firstname;
306 QString basename;
307
308 for (const auto & GameType : std::as_const(updatelist))
309 {
310 LOG(VB_GENERAL, LOG_NOTICE,
311 LOC + QString("Update gametype %1").arg(GameType));
312
313 query.prepare("SELECT romname,`system`,spandisks,gamename FROM "
314 "gamemetadata,gameplayers WHERE "
315 "gamemetadata.gametype = :GAMETYPE AND "
316 "playername = `system` ORDER BY romname");
317
318 query.bindValue(":GAMETYPE",GameType);
319
320 if (query.exec())
321 {
322 while (query.next())
323 {
324 QString RomName = query.value(0).toString();
325 QString System = query.value(1).toString();
326 int spandisks = query.value(2).toInt();
327 QString GameName = query.value(3).toString();
328
329 basename = RomName;
330
331 if (spandisks)
332 {
333 int diskcount = 0;
334 int extlength = 0;
335 int pos = RomName.lastIndexOf(".");
336 if (pos > 1)
337 {
338 extlength = RomName.length() - pos;
339 pos--;
340
341 basename = RomName.mid(pos,1);
342 }
343
344 if (basename.contains(multiDiskRGXP))
345 {
346 pos = (RomName.length() - extlength) - 1;
347 basename = RomName.left(pos);
348
349 if (basename.right(1) == ".")
350 basename = RomName.left(pos - 1);
351 }
352 else
353 {
354 basename = GameName;
355 }
356
357 if (basename == lastrom)
358 {
359 updateDisplayRom(RomName,0,System);
360 diskcount++;
361 if (diskcount > 1)
362 updateDiskCount(firstname,diskcount,GameType);
363 }
364 else
365 {
366 firstname = RomName;
367 lastrom = basename;
368 }
369
370 if (basename != GameName)
371 updateGameName(RomName,basename,System);
372 }
373 else
374 {
375 if (basename == lastrom)
376 updateDisplayRom(RomName,0,System);
377 else
378 lastrom = basename;
379 }
380 }
381 }
382 }
383}
384
386{
387 int counter = 0;
389
390 //: %1 is the system name, %2 is the game type
391 QString message = tr("Updating %1 (%2) ROM database")
392 .arg(handler->SystemName(), handler->GameType());
393
394 CreateProgress(message);
395
396 if (m_progressDlg)
398
399 QString GameName;
400 QString Genre;
401 QString Country;
402 QString CRC32;
403 QString Year;
404 QString Plot;
405 QString Publisher;
406 QString Version;
407 QString Fanart;
408 QString Boxart;
409 QString ScreenShot;
410
411 int removalprompt = gCoreContext->GetSetting("GameRemovalPrompt").toInt();
412 int indepth = gCoreContext->GetSetting("GameDeepScan").toInt();
413 QString screenShotPath = gCoreContext->GetSetting("mythgame.screenshotdir");
414
415 for (const auto & game : std::as_const(m_gameMap))
416 {
417
418 if (game.FoundLoc() == inFileSystem)
419 {
420 if (indepth)
421 {
422 GetMetadata(handler, game.RomFullPath(), &Genre, &Year, &Country, &CRC32, &GameName,
423 &Plot, &Publisher, &Version, &Fanart, &Boxart);
424 }
425 else
426 {
427 /*: %1 is the game type, when we don't know the genre we use the
428 * game type */
429 Genre = tr("Unknown %1", "Unknown genre").arg(handler->GameType());
430 Country = tr("Unknown", "Unknown country");
431 CRC32.clear();
432 Year = tr("19xx", "Default game year");
433 GameName = tr("Unknown", "Unknown game name");
434 Plot = tr("Unknown", "Unknown plot");
435 Publisher = tr("Unknown", "Unknown publisher");
436 Version = tr("0", "Default game version");
437 Fanart.clear();
438 Boxart.clear();
439 }
440
441 if (GameName == tr("Unknown", "Unknown game name"))
442 GameName = game.GameName();
443
444 int suffixPos = game.Rom().lastIndexOf(QChar('.'));
445 QString baseName = game.Rom();
446
447 if (suffixPos > 0)
448 baseName = game.Rom().left(suffixPos);
449
450 baseName = screenShotPath + "/" + baseName;
451
452 if (QFile(baseName + ".png").exists())
453 ScreenShot = baseName + ".png";
454 else if (QFile(baseName + ".jpg").exists())
455 ScreenShot = baseName + ".jpg";
456 else if (QFile(baseName + ".gif").exists())
457 ScreenShot = baseName + ".gif";
458 else
459 ScreenShot.clear();
460
461#if 0
462 LOG(VB_GENERAL, LOG_INFO, QString("file %1 - genre %2 ")
463 .arg(iter.data().Rom()).arg(Genre));
464 LOG(VB_GENERAL, LOG_INFO, QString("screenshot %1").arg(ScreenShot));
465#endif
466
467 query.prepare("INSERT INTO gamemetadata "
468 "(`system`, romname, gamename, genre, year, gametype, "
469 "rompath, country, crc_value, diskcount, display, plot, "
470 "publisher, version, fanart, boxart, screenshot) "
471 "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
472 ":GAMETYPE, :ROMPATH, :COUNTRY, :CRC32, '1', '1', :PLOT, :PUBLISHER, :VERSION, "
473 ":FANART, :BOXART, :SCREENSHOT)");
474
475 query.bindValueNoNull(":SYSTEM",handler->SystemName());
476 query.bindValueNoNull(":ROMNAME",game.Rom());
477 query.bindValueNoNull(":GAMENAME",GameName);
478 query.bindValueNoNull(":GENRE",Genre);
479 query.bindValueNoNull(":YEAR",Year);
480 query.bindValueNoNull(":GAMETYPE",handler->GameType());
481 query.bindValueNoNull(":ROMPATH",game.RomPath());
482 query.bindValueNoNull(":COUNTRY",Country);
483 query.bindValueNoNull(":CRC32", CRC32);
484 query.bindValueNoNull(":PLOT", Plot);
485 query.bindValueNoNull(":PUBLISHER", Publisher);
486 query.bindValueNoNull(":VERSION", Version);
487 query.bindValueNoNull(":FANART", Fanart);
488 query.bindValueNoNull(":BOXART", Boxart);
489 query.bindValueNoNull(":SCREENSHOT", ScreenShot);
490
491 if (!query.exec())
492 MythDB::DBError("GameHandler::UpdateGameDB - "
493 "insert gamemetadata", query);
494 }
495 else if ((game.FoundLoc() == inDatabase) && (removalprompt))
496 {
497
498 promptForRemoval( game );
499 }
500
501 if (m_progressDlg)
502 m_progressDlg->SetProgress(++counter);
503 }
504
505 if (m_progressDlg)
506 {
508 m_progressDlg = nullptr;
509}
510}
511
513{
514 int counter = 0;
515
517 query.prepare("SELECT romname,rompath,gamename FROM gamemetadata "
518 "WHERE `system` = :SYSTEM");
519
520 query.bindValue(":SYSTEM",handler->SystemName());
521
522 if (!query.exec())
523 MythDB::DBError("GameHandler::VerifyGameDB - "
524 "select", query);
525
526 //: %1 is the system name
527 QString message = tr("Verifying %1 files...").arg(handler->SystemName());
528
529 CreateProgress(message);
530
531 if (m_progressDlg)
532 m_progressDlg->SetTotal(query.size());
533
534 // For every file we know about, check to see if it still exists.
535 while (query.next())
536 {
537 QString RomName = query.value(0).toString();
538 QString RomPath = query.value(1).toString();
539 QString GameName = query.value(2).toString();
540 if (!RomName.isEmpty())
541 {
542#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
543 auto iter = m_gameMap.find(RomName);
544#else
545 auto iter = m_gameMap.constFind(RomName);
546#endif
547 if (iter != m_gameMap.end())
548 {
549 // If it's both on disk and in the database we're done with it.
550 m_gameMap.erase(iter);
551 }
552 else
553 {
554 // If it's only in the database add it to our list and mark it for
555 // removal.
556 m_gameMap[RomName] = GameScan(RomName,RomPath + "/" + RomName,inDatabase,
557 GameName,RomPath);
558 }
559 }
560 if (m_progressDlg)
561 m_progressDlg->SetProgress(++counter);
562 }
563
564 if (m_progressDlg)
565 {
567 m_progressDlg = nullptr;
568 }
569}
570
571// Recurse through the directory and gather a count on how many files there are to process.
572// This is used for the progressbar info.
573int GameHandler::buildFileCount(const QString& directory, GameHandler *handler)
574{
575 int filecount = 0;
576 QDir RomDir(directory);
577
578 // If we can't read it's contents move on
579 if (!RomDir.isReadable())
580 return 0;
581
582 RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
583 QFileInfoList List = RomDir.entryInfoList();
584 for (const auto & Info : std::as_const(List))
585 {
586 if (Info.isDir())
587 {
588 filecount += buildFileCount(Info.filePath(), handler);
589 continue;
590 }
591
592 if (handler->m_validextensions.count() > 0)
593 {
594 QRegularExpression r {
595 "^" + Info.suffix() + "$",
596 QRegularExpression::CaseInsensitiveOption };
597 QStringList result;
598 QStringList& exts = handler->m_validextensions;
599 std::copy_if(exts.cbegin(), exts.cend(), std::back_inserter(result),
600 [&r](const QString& extension){ return extension.contains(r); } );
601 if (result.isEmpty())
602 continue;
603 }
604
605 filecount++;
606 }
607
608 return filecount;
609}
610
612{
613 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
614 auto *clearPopup = new MythDialogBox(
615 tr("This will clear all game metadata from the database. Are you sure "
616 "you want to do this?"), popupStack, "clearAllPopup");
617
618 if (clearPopup->Create())
619 {
620 clearPopup->SetReturnEvent(this, "clearAllPopup");
621 clearPopup->AddButton(tr("No"));
622 clearPopup->AddButton(tr("Yes"));
623 popupStack->AddScreen(clearPopup);
624 }
625 else
626 {
627 delete clearPopup;
628 }
629}
630
631void GameHandler::buildFileList(const QString& directory, GameHandler *handler,
632 int* filecount)
633{
634 QDir RomDir(directory);
635
636 // If we can't read its contents move on
637 if (!RomDir.isReadable())
638 return;
639
640 RomDir.setSorting( QDir:: DirsFirst | QDir::Name );
641 RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
642 QFileInfoList List = RomDir.entryInfoList();
643 for (const auto & Info : std::as_const(List))
644 {
645 QString RomName = Info.fileName();
646 QString GameName = Info.completeBaseName();
647
648 if (Info.isDir())
649 {
650 buildFileList(Info.filePath(), handler, filecount);
651 continue;
652 }
653
654 if (handler->m_validextensions.count() > 0)
655 {
656 QRegularExpression r {
657 "^" + Info.suffix() + "$",
658 QRegularExpression::CaseInsensitiveOption };
659 QStringList result;
660 QStringList& exts = handler->m_validextensions;
661 std::copy_if(exts.cbegin(), exts.cend(), std::back_inserter(result),
662 [&r](const QString& extension){ return extension.contains(r); } );
663 if (result.isEmpty())
664 continue;
665 }
666
667 m_gameMap[RomName] = GameScan(RomName,Info.filePath(),inFileSystem,
668 GameName, Info.absoluteDir().path());
669
670 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found ROM : (%1) - %2")
671 .arg(handler->SystemName(), RomName));
672
673 *filecount = *filecount + 1;
674 if (m_progressDlg)
675 m_progressDlg->SetProgress(*filecount);
676 }
677}
678
680{
681 int maxcount = 0;
683
684 if ((!handler->SystemRomPath().isEmpty()) && (handler->GameType() != "PC"))
685 {
686 QDir d(handler->SystemRomPath());
687 if (d.exists())
688 maxcount = buildFileCount(handler->SystemRomPath(),handler);
689 else
690 {
691 LOG(VB_GENERAL, LOG_ERR, LOC +
692 QString("ROM Path does not exist: %1")
693 .arg(handler->SystemRomPath()));
694 return;
695 }
696 }
697 else
698 {
699 maxcount = 100;
700 }
701
702 if (handler->GameType() == "PC")
703 {
704 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
705
706 //: %1 is the system name
707 QString message = tr("Scanning for %1 games...")
708 .arg(handler->SystemName());
709 auto *busyDialog = new MythUIBusyDialog(message, popupStack,
710 "gamescanbusy");
711
712 if (busyDialog->Create())
713 popupStack->AddScreen(busyDialog, false);
714 else
715 {
716 delete busyDialog;
717 busyDialog = nullptr;
718 }
719
720 m_gameMap[handler->SystemCmdLine()] =
721 GameScan(handler->SystemCmdLine(),
722 handler->SystemCmdLine(),
724 handler->SystemName(),
725 handler->SystemCmdLine().left(handler->SystemCmdLine().lastIndexOf("/")));
726
727 if (busyDialog)
728 busyDialog->Close();
729
730 LOG(VB_GENERAL, LOG_INFO, LOC +
731 QString("PC Game %1").arg(handler->SystemName()));
732 }
733 else
734 {
735 QString message = tr("Scanning for %1 games...")
736 .arg(handler->SystemName());
737 CreateProgress(message);
738
739 if (m_progressDlg)
740 m_progressDlg->SetTotal(maxcount);
741
742 int filecount = 0;
743 buildFileList(handler->SystemRomPath(), handler, &filecount);
744
745 if (m_progressDlg)
746 {
748 m_progressDlg = nullptr;
749 }
750 }
751
752 VerifyGameDB(handler);
753
754 // If we still have some games in the list then update the database
755 if (!m_gameMap.empty())
756 {
757 InitMetaDataMap(handler->GameType());
758
759 UpdateGameDB(handler);
760
761 m_romDB.clear();
762 handler->setRebuild(true);
763 }
764 else
765 {
766 handler->setRebuild(false);
767 }
768}
769
771{
773 QStringList updatelist;
774
775 for (auto *handler : std::as_const(*handlers))
776 {
777 if (handler)
778 {
779 updateSettings(handler);
780 handler->processGames(handler);
781
782 if (handler->needRebuild())
783 updatelist.append(handler->GameType());
784 }
785 }
786
787 if (!updatelist.isEmpty())
788 UpdateGameCounts(updatelist);
789}
790
792{
793 if (!rominfo)
794 return nullptr;
795
796 for (auto *handler : std::as_const(*handlers))
797 {
798 if (handler)
799 {
800 if (rominfo->System() == handler->SystemName())
801 return handler;
802 }
803 }
804
805 return nullptr;
806}
807
809{
810 if (systemname.isEmpty())
811 return nullptr;
812
813 for (auto *handler : std::as_const(*handlers))
814 {
815 if (handler)
816 {
817 if (handler->SystemName() == systemname)
818 return handler;
819 }
820 }
821
822 return nullptr;
823}
824
825void GameHandler::Launchgame(RomInfo *romdata, const QString& systemname)
826{
827 GameHandler *handler = nullptr;
828
829 if (!systemname.isEmpty())
830 {
831 handler = GetHandlerByName(systemname);
832 }
833 else
834 {
835 handler = GetHandler(romdata);
836 if (handler == nullptr)
837 {
838 // Couldn't get handler so abort.
839 return;
840 }
841 }
842 QString exec = handler->SystemCmdLine();
843
844 if (exec.isEmpty())
845 return;
846
847 if (handler->GameType() != "PC")
848 {
849 QString arg = "\"" + romdata->Rompath() +
850 "/" + romdata->Romname() + "\"";
851
852 // If they specified a %s in the commandline place the romname
853 // in that location, otherwise tack it on to the end of
854 // the command.
855 if (exec.contains("%s") || handler->SpanDisks())
856 {
857 exec = exec.replace("%s",arg);
858
859 if (handler->SpanDisks())
860 {
861 static const QRegularExpression rxp { "%d[0-4]" };
862
863 if (exec.contains(rxp))
864 {
865 if (romdata->DiskCount() > 1)
866 {
867 // Chop off the extension, . and last character of the name which we are assuming is the disk #
868 QString basename = romdata->Romname().left(romdata->Romname().length() - (romdata->getExtension().length() + 2));
869 QString extension = romdata->getExtension();
870 QString rom;
871 std::array<QString,7> diskid { "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6" };
872
873 for (int disk = 1; disk <= romdata->DiskCount(); disk++)
874 {
875 rom = QString("\"%1/%2%3.%4\"")
876 .arg(romdata->Rompath(), basename,
877 QString::number(disk), extension);
878 exec = exec.replace(diskid[disk],rom);
879 }
880 } else
881 { // If there is only one disk make sure we replace %d1 just like %s
882 exec = exec.replace("%d1",arg);
883 }
884 }
885 }
886 }
887 else
888 {
889 exec = exec + " \"" +
890 romdata->Rompath() + "/" +
891 romdata->Romname() + "\"";
892 }
893 }
894
895 QString savedir = QDir::current().path();
896 QDir d;
897 if (!handler->SystemWorkingPath().isEmpty())
898 {
899 if (!d.cd(handler->SystemWorkingPath()))
900 {
901 LOG(VB_GENERAL, LOG_ERR, LOC +
902 QString("Failed to change to specified Working Directory: %1")
903 .arg(handler->SystemWorkingPath()));
904 }
905 }
906 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Launching Game : %1 : %2")
907 .arg(handler->SystemName(), exec));
908
909 GetMythUI()->AddCurrentLocation(QString("MythGame %1 ( %2 )")
910 .arg(handler->SystemName(), exec));
911
912 QStringList cmdlist = exec.split(";");
913 if (cmdlist.count() > 0)
914 {
915 for (const auto & cmd : std::as_const(cmdlist))
916 {
917 LOG(VB_GENERAL, LOG_INFO, LOC +
918 QString("Executing : %1").arg(cmd));
920 }
921 }
922 else
923 {
924 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Executing : %1").arg(exec));
926 }
927
929
930 (void)d.cd(savedir);
931}
932
934{
935 if (!parent || !GetHandler(parent))
936 return nullptr;
937
938 return new RomInfo(*parent);
939}
940
942{
943 handlers->append(handler);
944}
945
946void GameHandler::customEvent(QEvent *event)
947{
948 if (auto *dce = dynamic_cast<DialogCompletionEvent*>(event))
949 {
950 QString resultid = dce->GetId();
951// QString resulttext = dce->GetResultText();
952
953 if (resultid == "removalPopup")
954 {
955 int buttonNum = dce->GetResult();
956 auto scan = dce->GetData().value<GameScan>();
957 switch (buttonNum)
958 {
959 case 1:
960 m_keepAll = true;
961 break;
962 case 2:
963 purgeGameDB(scan.Rom() , scan.RomFullPath());
964 break;
965 case 3:
966 m_removeAll = true;
967 purgeGameDB(scan.Rom() , scan.RomFullPath());
968 break;
969 default:
970 break;
971 };
972 }
973 else if (resultid == "clearAllPopup")
974 {
975 int buttonNum = dce->GetResult();
976 switch (buttonNum)
977 {
978 case 1:
980 break;
981 default:
982 break;
983 }
984 }
985 }
986}
987
989{
991 if (!query.exec("DELETE FROM gamemetadata;"))
992 MythDB::DBError("GameHandler::clearAllGameData - "
993 "delete gamemetadata", query);
994}
995
996void GameHandler::CreateProgress(const QString& message)
997{
998 if (m_progressDlg)
999 return;
1000
1001 MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1002
1003 m_progressDlg = new MythUIProgressDialog(message, popupStack,
1004 "gameprogress");
1005
1006 if (m_progressDlg->Create())
1007 {
1008 popupStack->AddScreen(m_progressDlg, false);
1009 }
1010 else
1011 {
1012 delete m_progressDlg;
1013 m_progressDlg = nullptr;
1014 }
1015}
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
QString GameType() const
Definition: gamehandler.h:111
static GameHandler * GetHandler(RomInfo *rominfo)
static GameHandler * newHandler(QString name)
Definition: gamehandler.cpp:86
static uint count(void)
Definition: gamehandler.cpp:97
static void processAllGames(void)
bool m_spandisks
Definition: gamehandler.h:123
void VerifyGameDB(GameHandler *handler)
QString m_gametype
Definition: gamehandler.h:130
RomDBMap m_romDB
Definition: gamehandler.h:133
MythUIProgressDialog * m_progressDlg
Definition: gamehandler.h:143
static GameHandler * getHandler(uint i)
Definition: gamehandler.cpp:55
QString SystemCmdLine() const
Definition: gamehandler.h:106
static RomInfo * CreateRomInfo(RomInfo *parent)
QString SystemName() const
Definition: gamehandler.h:105
static GameHandler * s_newInstance
Definition: gamehandler.h:141
static void Launchgame(RomInfo *romdata, const QString &systemname)
static int buildFileCount(const QString &directory, GameHandler *handler)
QString m_systemname
Definition: gamehandler.h:124
GameScanMap m_gameMap
Definition: gamehandler.h:134
static void registerHandler(GameHandler *handler)
void setRebuild(bool setrebuild)
Definition: gamehandler.h:100
static void updateSettings(GameHandler *handler)
Definition: gamehandler.cpp:60
void customEvent(QEvent *event) override
QString SystemRomPath() const
Definition: gamehandler.h:107
void promptForRemoval(const GameScan &scan)
QString SystemWorkingPath() const
Definition: gamehandler.h:108
static void clearAllMetadata(void)
void processGames(GameHandler *handler)
QString m_commandline
Definition: gamehandler.h:126
QString m_rompath
Definition: gamehandler.h:125
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)
uint m_gameplayerid
Definition: gamehandler.h:129
void clearAllGameData(void)
QString m_screenshots
Definition: gamehandler.h:128
void UpdateGameDB(GameHandler *handler)
static GameHandler * GetHandlerByName(const QString &systemname)
GameHandler()=default
void InitMetaDataMap(const QString &GameType)
bool m_removeAll
Definition: gamehandler.h:136
bool SpanDisks() const
Definition: gamehandler.h:104
bool m_keepAll
Definition: gamehandler.h:137
QString m_workingpath
Definition: gamehandler.h:127
void CreateProgress(const QString &message)
QStringList m_validextensions
Definition: gamehandler.h:131
void buildFileList(const QString &directory, GameHandler *handler, int *filecount)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:902
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
QString GetSetting(const QString &key, const QString &defaultval="")
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
Basic menu dialog, message and a list of options.
MythScreenStack * GetStack(const QString &Stackname)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
virtual void Close()
QString RemoveCurrentLocation()
void AddCurrentLocation(const QString &Location)
bool Create(void) override
void SetProgress(uint count)
QString getExtension() const
Definition: rominfo.cpp:234
QString System() const
Definition: rominfo.h:75
int DiskCount() const
Definition: rominfo.h:96
QString Romname() const
Definition: rominfo.h:72
QString Rompath() const
Definition: rominfo.h:60
unsigned int uint
Definition: freesurround.h:24
#define LOC
Definition: gamehandler.cpp:26
static void UpdateGameCounts(const QStringList &updatelist)
static void purgeGameDB(const QString &filename, const QString &RomPath)
static void checkHandlers(void)
Definition: gamehandler.cpp:30
static QList< GameHandler * > * handlers
Definition: gamehandler.cpp:28
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)
@ inFileSystem
Definition: gamehandler.h:27
@ inDatabase
Definition: gamehandler.h:28
static const iso6937table * d
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
@ kMSProcessEvents
process events while waiting
Definition: mythsystem.h:39
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.
Definition: mythdate.cpp:15
def scan(profile, smoonURL, gate)
Definition: scan.py:54
bool exists(str path)
Definition: xbmcvfs.py:51
QString crcinfo(const QString &romname, const QString &GameType, QString *key, RomDBMap *romDB)