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