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