MythTV  master
gamehandler.cpp
Go to the documentation of this file.
1 
2 #include "gamehandler.h"
3 #include "rominfo.h"
4 #include "rom_metadata.h"
5 
6 #include <QDir>
7 #include <QList>
8 #include <QRegularExpression>
9 #include <utility>
10 
11 #include <mythdb.h>
12 #include <mythdbcon.h>
13 #include <mythsystemlegacy.h>
14 #include <mythcontext.h>
15 #include <mythuihelper.h>
16 #include <mythdialogbox.h>
17 #include <mythmainwindow.h>
18 #include <mythprogressdialog.h>
19 
20 #define LOC_ERR QString("MythGame:GAMEHANDLER Error: ")
21 #define LOC QString("MythGame:GAMEHANDLER: ")
22 
23 static QList<GameHandler*> *handlers = nullptr;
24 
25 static void checkHandlers(void)
26 {
27  // If a handlers list doesn't currently exist create one. Otherwise
28  // clear the existing list so that we can regenerate a new one.
29  if (!handlers)
30  handlers = new QList<GameHandler*>;
31  else
32  {
33  while (!handlers->isEmpty())
34  delete handlers->takeFirst();
35  handlers->clear();
36  }
37 
39  if (!query.exec("SELECT DISTINCT playername FROM gameplayers "
40  "WHERE playername <> '';"))
41  MythDB::DBError("checkHandlers - selecting playername", query);
42 
43  while (query.next())
44  {
45  QString name = query.value(0).toString();
47  }
48 }
49 
51 {
52  return handlers->at(i);
53 }
54 
56 {
58 
59  query.prepare("SELECT rompath, workingpath, commandline, screenshots, "
60  "gameplayerid, gametype, extensions, spandisks "
61  "FROM gameplayers WHERE playername = :SYSTEM ");
62 
63  query.bindValue(":SYSTEM", handler->SystemName());
64 
65  if (query.exec() && query.next())
66  {
67  handler->m_rompath = query.value(0).toString();
68  handler->m_workingpath = query.value(1).toString();
69  handler->m_commandline = query.value(2).toString();
70  handler->m_screenshots = query.value(3).toString();
71  handler->m_gameplayerid = query.value(4).toInt();
72  handler->m_gametype = query.value(5).toString();
73 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
74  handler->m_validextensions = query.value(6).toString().trimmed()
75  .remove(" ").split(",", QString::SkipEmptyParts);
76 #else
77  handler->m_validextensions = query.value(6).toString().trimmed()
78  .remove(" ").split(",", Qt::SkipEmptyParts);
79 #endif
80  handler->m_spandisks = query.value(7).toBool();
81  }
82 }
83 
85 
87 {
88  s_newInstance = new GameHandler();
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 {
99  checkHandlers();
100  return handlers->count();
101 }
102 
104 {
105  QString key;
106 
107  MSqlQuery query(MSqlQuery::InitCon());
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 
145 void 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 
196 static 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 
201  MSqlQuery query(MSqlQuery::InitCon());
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  delete removalPopup;
248 }
249 
250 static void updateDisplayRom(const QString& romname, int display, const QString& Systemname)
251 {
252  MSqlQuery query(MSqlQuery::InitCon());
253  query.prepare("UPDATE gamemetadata SET display = :DISPLAY "
254  "WHERE romname = :ROMNAME AND `system` = :SYSTEM");
255 
256  query.bindValue(":DISPLAY", display);
257  query.bindValue(":ROMNAME", romname);
258  query.bindValue(":SYSTEM", Systemname);
259 
260  if (!query.exec())
261  MythDB::DBError("updateDisplayRom", query);
262 
263 }
264 
265 static void updateDiskCount(const QString& romname, int diskcount, const QString& GameType)
266 {
267  MSqlQuery query(MSqlQuery::InitCon());
268  query.prepare("UPDATE gamemetadata SET diskcount = :DISKCOUNT "
269  "WHERE romname = :ROMNAME AND gametype = :GAMETYPE ");
270 
271  query.bindValue(":DISKCOUNT",diskcount);
272  query.bindValue(":ROMNAME", romname);
273  query.bindValue(":GAMETYPE",GameType);
274 
275  if (!query.exec())
276  MythDB::DBError("updateDiskCount", query);
277 
278 }
279 
280 static void updateGameName(const QString& romname, const QString& GameName, const QString& Systemname)
281 {
282  MSqlQuery query(MSqlQuery::InitCon());
283  query.prepare("UPDATE gamemetadata SET GameName = :GAMENAME "
284  "WHERE romname = :ROMNAME AND `system` = :SYSTEM ");
285 
286  query.bindValue(":GAMENAME", GameName);
287  query.bindValue(":ROMNAME", romname);
288  query.bindValue(":SYSTEM", Systemname);
289 
290  if (!query.exec())
291  MythDB::DBError("updateGameName", query);
292 
293 }
294 
295 
296 static void UpdateGameCounts(const QStringList& updatelist)
297 {
298  MSqlQuery query(MSqlQuery::InitCon());
299 
300  static const QRegularExpression multiDiskRGXP { "[0-4]$" };
301  int pos = 0;
302 
303  QString lastrom;
304  QString firstname;
305  QString basename;
306 
307  for (const auto & GameType : qAsConst(updatelist))
308  {
309  LOG(VB_GENERAL, LOG_NOTICE,
310  LOC + QString("Update gametype %1").arg(GameType));
311 
312  query.prepare("SELECT romname,`system`,spandisks,gamename FROM "
313  "gamemetadata,gameplayers WHERE "
314  "gamemetadata.gametype = :GAMETYPE AND "
315  "playername = `system` ORDER BY romname");
316 
317  query.bindValue(":GAMETYPE",GameType);
318 
319  if (query.exec())
320  {
321  while (query.next())
322  {
323  QString RomName = query.value(0).toString();
324  QString System = query.value(1).toString();
325  int spandisks = query.value(2).toInt();
326  QString GameName = query.value(3).toString();
327 
328  basename = RomName;
329 
330  if (spandisks)
331  {
332  int diskcount = 0;
333  int extlength = 0;
334  pos = RomName.lastIndexOf(".");
335  if (pos > 1)
336  {
337  extlength = RomName.length() - pos;
338  pos--;
339 
340  basename = RomName.mid(pos,1);
341  }
342 
343  if (basename.contains(multiDiskRGXP))
344  {
345  pos = (RomName.length() - extlength) - 1;
346  basename = RomName.left(pos);
347 
348  if (basename.right(1) == ".")
349  basename = RomName.left(pos - 1);
350  }
351  else
352  basename = GameName;
353 
354  if (basename == lastrom)
355  {
356  updateDisplayRom(RomName,0,System);
357  diskcount++;
358  if (diskcount > 1)
359  updateDiskCount(firstname,diskcount,GameType);
360  }
361  else
362  {
363  firstname = RomName;
364  lastrom = basename;
365  }
366 
367  if (basename != GameName)
368  updateGameName(RomName,basename,System);
369  }
370  else
371  {
372  if (basename == lastrom)
373  updateDisplayRom(RomName,0,System);
374  else
375  lastrom = basename;
376  }
377  }
378  }
379  }
380 }
381 
383 {
384  int counter = 0;
385  MSqlQuery query(MSqlQuery::InitCon());
386 
387  //: %1 is the system name, %2 is the game type
388  QString message = tr("Updating %1 (%2) ROM database")
389  .arg(handler->SystemName(), handler->GameType());
390 
391  CreateProgress(message);
392 
393  if (m_progressDlg)
395 
396  QString GameName;
397  QString Genre;
398  QString Country;
399  QString CRC32;
400  QString Year;
401  QString Plot;
402  QString Publisher;
403  QString Version;
404  QString Fanart;
405  QString Boxart;
406  QString ScreenShot;
407 
408  int removalprompt = gCoreContext->GetSetting("GameRemovalPrompt").toInt();
409  int indepth = gCoreContext->GetSetting("GameDeepScan").toInt();
410  QString screenShotPath = gCoreContext->GetSetting("mythgame.screenshotdir");
411 
412  for (const auto & game : qAsConst(m_gameMap))
413  {
414 
415  if (game.FoundLoc() == inFileSystem)
416  {
417  if (indepth)
418  {
419  GetMetadata(handler, game.RomFullPath(), &Genre, &Year, &Country, &CRC32, &GameName,
420  &Plot, &Publisher, &Version, &Fanart, &Boxart);
421  }
422  else
423  {
424  /*: %1 is the game type, when we don't know the genre we use the
425  * game type */
426  Genre = tr("Unknown %1", "Unknown genre").arg(handler->GameType());
427  Country = tr("Unknown", "Unknown country");
428  CRC32.clear();
429  Year = tr("19xx", "Default game year");
430  GameName = tr("Unknown", "Unknown game name");
431  Plot = tr("Unknown", "Unknown plot");
432  Publisher = tr("Unknown", "Unknown publisher");
433  Version = tr("0", "Default game version");
434  Fanart.clear();
435  Boxart.clear();
436  }
437 
438  if (GameName == tr("Unknown", "Unknown game name"))
439  GameName = game.GameName();
440 
441  int suffixPos = game.Rom().lastIndexOf(QChar('.'));
442  QString baseName = game.Rom();
443 
444  if (suffixPos > 0)
445  baseName = game.Rom().left(suffixPos);
446 
447  baseName = screenShotPath + "/" + baseName;
448 
449  if (QFile(baseName + ".png").exists())
450  ScreenShot = baseName + ".png";
451  else if (QFile(baseName + ".jpg").exists())
452  ScreenShot = baseName + ".jpg";
453  else if (QFile(baseName + ".gif").exists())
454  ScreenShot = baseName + ".gif";
455  else
456  ScreenShot.clear();
457 
458 #if 0
459  LOG(VB_GENERAL, LOG_INFO, QString("file %1 - genre %2 ")
460  .arg(iter.data().Rom()).arg(Genre));
461  LOG(VB_GENERAL, LOG_INFO, QString("screenshot %1").arg(ScreenShot));
462 #endif
463 
464  query.prepare("INSERT INTO gamemetadata "
465  "(`system`, romname, gamename, genre, year, gametype, "
466  "rompath, country, crc_value, diskcount, display, plot, "
467  "publisher, version, fanart, boxart, screenshot) "
468  "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
469  ":GAMETYPE, :ROMPATH, :COUNTRY, :CRC32, '1', '1', :PLOT, :PUBLISHER, :VERSION, "
470  ":FANART, :BOXART, :SCREENSHOT)");
471 
472  query.bindValueNoNull(":SYSTEM",handler->SystemName());
473  query.bindValueNoNull(":ROMNAME",game.Rom());
474  query.bindValueNoNull(":GAMENAME",GameName);
475  query.bindValueNoNull(":GENRE",Genre);
476  query.bindValueNoNull(":YEAR",Year);
477  query.bindValueNoNull(":GAMETYPE",handler->GameType());
478  query.bindValueNoNull(":ROMPATH",game.RomPath());
479  query.bindValueNoNull(":COUNTRY",Country);
480  query.bindValueNoNull(":CRC32", CRC32);
481  query.bindValueNoNull(":PLOT", Plot);
482  query.bindValueNoNull(":PUBLISHER", Publisher);
483  query.bindValueNoNull(":VERSION", Version);
484  query.bindValueNoNull(":FANART", Fanart);
485  query.bindValueNoNull(":BOXART", Boxart);
486  query.bindValueNoNull(":SCREENSHOT", ScreenShot);
487 
488  if (!query.exec())
489  MythDB::DBError("GameHandler::UpdateGameDB - "
490  "insert gamemetadata", query);
491  }
492  else if ((game.FoundLoc() == inDatabase) && (removalprompt))
493  {
494 
495  promptForRemoval( game );
496  }
497 
498  if (m_progressDlg)
499  m_progressDlg->SetProgress(++counter);
500  }
501 
502  if (m_progressDlg)
503  {
504  m_progressDlg->Close();
505  m_progressDlg = nullptr;
506 }
507 }
508 
510 {
511  int counter = 0;
512 
513  MSqlQuery query(MSqlQuery::InitCon());
514  query.prepare("SELECT romname,rompath,gamename FROM gamemetadata "
515  "WHERE `system` = :SYSTEM");
516 
517  query.bindValue(":SYSTEM",handler->SystemName());
518 
519  if (!query.exec())
520  MythDB::DBError("GameHandler::VerifyGameDB - "
521  "select", query);
522 
523  //: %1 is the system name
524  QString message = tr("Verifying %1 files...").arg(handler->SystemName());
525 
526  CreateProgress(message);
527 
528  if (m_progressDlg)
529  m_progressDlg->SetTotal(query.size());
530 
531  // For every file we know about, check to see if it still exists.
532  while (query.next())
533  {
534  QString RomName = query.value(0).toString();
535  QString RomPath = query.value(1).toString();
536  QString GameName = query.value(2).toString();
537  if (!RomName.isEmpty())
538  {
539 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
540  auto iter = m_gameMap.find(RomName);
541 #else
542  auto iter = m_gameMap.constFind(RomName);
543 #endif
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  {
563  m_progressDlg->Close();
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.
570 int 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 : qAsConst(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::copy_if(exts.cbegin(), exts.cend(), 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  delete clearPopup;
624 }
625 
626 void GameHandler::buildFileList(const QString& directory, GameHandler *handler,
627  int* filecount)
628 {
629  QDir RomDir(directory);
630 
631  // If we can't read its contents move on
632  if (!RomDir.isReadable())
633  return;
634 
635  RomDir.setSorting( QDir:: DirsFirst | QDir::Name );
636  RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
637  QFileInfoList List = RomDir.entryInfoList();
638  for (const auto & Info : qAsConst(List))
639  {
640  QString RomName = Info.fileName();
641  QString GameName = Info.completeBaseName();
642 
643  if (Info.isDir())
644  {
645  buildFileList(Info.filePath(), handler, filecount);
646  continue;
647  }
648 
649  if (handler->m_validextensions.count() > 0)
650  {
651  QRegularExpression r {
652  "^" + Info.suffix() + "$",
653  QRegularExpression::CaseInsensitiveOption };
654  QStringList result;
655  QStringList& exts = handler->m_validextensions;
656  std::copy_if(exts.cbegin(), exts.cend(), std::back_inserter(result),
657  [&r](const QString& extension){ return extension.contains(r); } );
658  if (result.isEmpty())
659  continue;
660  }
661 
662  m_gameMap[RomName] = GameScan(RomName,Info.filePath(),inFileSystem,
663  GameName, Info.absoluteDir().path());
664 
665  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found ROM : (%1) - %2")
666  .arg(handler->SystemName(), RomName));
667 
668  *filecount = *filecount + 1;
669  if (m_progressDlg)
670  m_progressDlg->SetProgress(*filecount);
671  }
672 }
673 
675 {
676  int maxcount = 0;
677  MSqlQuery query(MSqlQuery::InitCon());
678 
679  if ((!handler->SystemRomPath().isEmpty()) && (handler->GameType() != "PC"))
680  {
681  QDir d(handler->SystemRomPath());
682  if (d.exists())
683  maxcount = buildFileCount(handler->SystemRomPath(),handler);
684  else
685  {
686  LOG(VB_GENERAL, LOG_ERR, LOC +
687  QString("ROM Path does not exist: %1")
688  .arg(handler->SystemRomPath()));
689  return;
690  }
691  }
692  else
693  maxcount = 100;
694 
695  if (handler->GameType() == "PC")
696  {
697  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
698 
699  //: %1 is the system name
700  QString message = tr("Scanning for %1 games...")
701  .arg(handler->SystemName());
702  auto *busyDialog = new MythUIBusyDialog(message, popupStack,
703  "gamescanbusy");
704 
705  if (busyDialog->Create())
706  popupStack->AddScreen(busyDialog, false);
707  else
708  {
709  delete busyDialog;
710  busyDialog = nullptr;
711  }
712 
713  m_gameMap[handler->SystemCmdLine()] =
714  GameScan(handler->SystemCmdLine(),
715  handler->SystemCmdLine(),
716  inFileSystem,
717  handler->SystemName(),
718  handler->SystemCmdLine().left(handler->SystemCmdLine().lastIndexOf("/")));
719 
720  if (busyDialog)
721  busyDialog->Close();
722 
723  LOG(VB_GENERAL, LOG_INFO, LOC +
724  QString("PC Game %1").arg(handler->SystemName()));
725  }
726  else
727  {
728  QString message = tr("Scanning for %1 games...")
729  .arg(handler->SystemName());
730  CreateProgress(message);
731 
732  if (m_progressDlg)
733  m_progressDlg->SetTotal(maxcount);
734 
735  int filecount = 0;
736  buildFileList(handler->SystemRomPath(), handler, &filecount);
737 
738  if (m_progressDlg)
739  {
740  m_progressDlg->Close();
741  m_progressDlg = nullptr;
742  }
743  }
744 
745  VerifyGameDB(handler);
746 
747  // If we still have some games in the list then update the database
748  if (!m_gameMap.empty())
749  {
750  InitMetaDataMap(handler->GameType());
751 
752  UpdateGameDB(handler);
753 
754  m_romDB.clear();
755  handler->setRebuild(true);
756  }
757  else
758  handler->setRebuild(false);
759 }
760 
762 {
763  checkHandlers();
764  QStringList updatelist;
765 
766  for (auto *handler : qAsConst(*handlers))
767  {
768  if (handler)
769  {
770  updateSettings(handler);
771  handler->processGames(handler);
772 
773  if (handler->needRebuild())
774  updatelist.append(handler->GameType());
775  }
776  }
777 
778  if (!updatelist.isEmpty())
779  UpdateGameCounts(updatelist);
780 }
781 
783 {
784  if (!rominfo)
785  return nullptr;
786 
787  for (auto *handler : qAsConst(*handlers))
788  {
789  if (handler)
790  {
791  if (rominfo->System() == handler->SystemName())
792  return handler;
793  }
794  }
795 
796  return nullptr;
797 }
798 
799 GameHandler* GameHandler::GetHandlerByName(const QString& systemname)
800 {
801  if (systemname.isEmpty() || systemname.isNull())
802  return nullptr;
803 
804  for (auto *handler : qAsConst(*handlers))
805  {
806  if (handler)
807  {
808  if (handler->SystemName() == systemname)
809  return handler;
810  }
811  }
812 
813  return nullptr;
814 }
815 
816 void GameHandler::Launchgame(RomInfo *romdata, const QString& systemname)
817 {
818  GameHandler *handler = nullptr;
819 
820  if (!systemname.isEmpty() && !systemname.isNull())
821  {
822  handler = GetHandlerByName(systemname);
823  }
824  else if (!(handler = GetHandler(romdata)))
825  {
826  // Couldn't get handler so abort.
827  return;
828  }
829  QString exec = handler->SystemCmdLine();
830 
831  if (exec.isEmpty())
832  return;
833 
834  if (handler->GameType() != "PC")
835  {
836  QString arg = "\"" + romdata->Rompath() +
837  "/" + romdata->Romname() + "\"";
838 
839  // If they specified a %s in the commandline place the romname
840  // in that location, otherwise tack it on to the end of
841  // the command.
842  if (exec.contains("%s") || handler->SpanDisks())
843  {
844  exec = exec.replace("%s",arg);
845 
846  if (handler->SpanDisks())
847  {
848  QRegularExpression rxp { "%d[0-4]" };
849 
850  if (exec.contains(rxp))
851  {
852  if (romdata->DiskCount() > 1)
853  {
854  // Chop off the extension, . and last character of the name which we are assuming is the disk #
855  QString basename = romdata->Romname().left(romdata->Romname().length() - (romdata->getExtension().length() + 2));
856  QString extension = romdata->getExtension();
857  QString rom;
858  std::array<QString,7> diskid { "%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6" };
859 
860  for (int disk = 1; disk <= romdata->DiskCount(); disk++)
861  {
862  rom = QString("\"%1/%2%3.%4\"")
863  .arg(romdata->Rompath(), basename,
864  QString::number(disk), extension);
865  exec = exec.replace(diskid[disk],rom);
866  }
867  } else
868  { // If there is only one disk make sure we replace %d1 just like %s
869  exec = exec.replace("%d1",arg);
870  }
871  }
872  }
873  }
874  else
875  {
876  exec = exec + " \"" +
877  romdata->Rompath() + "/" +
878  romdata->Romname() + "\"";
879  }
880  }
881 
882  QString savedir = QDir::current().path();
883  QDir d;
884  if (!handler->SystemWorkingPath().isEmpty())
885  {
886  if (!d.cd(handler->SystemWorkingPath()))
887  {
888  LOG(VB_GENERAL, LOG_ERR, LOC +
889  QString("Failed to change to specified Working Directory: %1")
890  .arg(handler->SystemWorkingPath()));
891  }
892  }
893  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Launching Game : %1 : %2")
894  .arg(handler->SystemName(), exec));
895 
896  GetMythUI()->AddCurrentLocation(QString("MythGame %1 ( %2 )")
897  .arg(handler->SystemName(), exec));
898 
899  QStringList cmdlist = exec.split(";");
900  if (cmdlist.count() > 0)
901  {
902  for (const auto & cmd : qAsConst(cmdlist))
903  {
904  LOG(VB_GENERAL, LOG_INFO, LOC +
905  QString("Executing : %1").arg(cmd));
907  }
908  }
909  else
910  {
911  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Executing : %1").arg(exec));
913  }
914 
916 
917  (void)d.cd(savedir);
918 }
919 
921 {
922  if (!parent || !GetHandler(parent))
923  return nullptr;
924 
925  return new RomInfo(*parent);
926 }
927 
929 {
930  handlers->append(handler);
931 }
932 
933 void GameHandler::customEvent(QEvent *event)
934 {
935  if (auto *dce = dynamic_cast<DialogCompletionEvent*>(event))
936  {
937  QString resultid = dce->GetId();
938 // QString resulttext = dce->GetResultText();
939 
940  if (resultid == "removalPopup")
941  {
942  int buttonNum = dce->GetResult();
943  auto scan = dce->GetData().value<GameScan>();
944  switch (buttonNum)
945  {
946  case 1:
947  m_keepAll = true;
948  break;
949  case 2:
950  purgeGameDB(scan.Rom() , scan.RomFullPath());
951  break;
952  case 3:
953  m_removeAll = true;
954  purgeGameDB(scan.Rom() , scan.RomFullPath());
955  break;
956  default:
957  break;
958  };
959  }
960  else if (resultid == "clearAllPopup")
961  {
962  int buttonNum = dce->GetResult();
963  switch (buttonNum)
964  {
965  case 1:
967  break;
968  default:
969  break;
970  }
971  }
972  }
973 }
974 
976 {
977  MSqlQuery query(MSqlQuery::InitCon());
978  if (!query.exec("DELETE FROM gamemetadata;"))
979  MythDB::DBError("GameHandler::clearAllGameData - "
980  "delete gamemetadata", query);
981 }
982 
983 void GameHandler::CreateProgress(const QString& message)
984 {
985  if (m_progressDlg)
986  return;
987 
988  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
989 
990  m_progressDlg = new MythUIProgressDialog(message, popupStack,
991  "gameprogress");
992 
993  if (m_progressDlg->Create())
994  {
995  popupStack->AddScreen(m_progressDlg, false);
996  }
997  else
998  {
999  delete m_progressDlg;
1000  m_progressDlg = nullptr;
1001  }
1002 }
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:802
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:124
MythUIProgressDialog::Create
bool Create(void) override
Definition: mythprogressdialog.cpp:130
updateDisplayRom
static void updateDisplayRom(const QString &romname, int display, const QString &Systemname)
Definition: gamehandler.cpp:250
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:211
MSqlQuery::bindValueNoNull
void bindValueNoNull(const QString &placeholder, const QVariant &val)
Add a single binding, taking care not to set a NULL value.
Definition: mythdbcon.cpp:883
MythUILocation::RemoveCurrentLocation
QString RemoveCurrentLocation()
Definition: mythuilocation.cpp:12
GameHandler::CreateProgress
void CreateProgress(const QString &message)
Definition: gamehandler.cpp:983
mythdb.h
GameHandler::setRebuild
void setRebuild(bool setrebuild)
Definition: gamehandler.h:96
MythScreenType::Close
virtual void Close()
Definition: mythscreentype.cpp:385
RomPath
Definition: gamesettings.cpp:260
GameHandler::GetMetadata
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)
Definition: gamehandler.cpp:145
GameHandler::clearAllGameData
void clearAllGameData(void)
Definition: gamehandler.cpp:608
GameHandler
Definition: gamehandler.h:60
mythdialogbox.h
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:201
MythScreenStack
Definition: mythscreenstack.h:16
GameHandler::SpanDisks
bool SpanDisks() const
Definition: gamehandler.h:100
mythdbcon.h
GameHandler::registerHandler
static void registerHandler(GameHandler *handler)
Definition: gamehandler.cpp:928
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
GameHandler::CreateRomInfo
static RomInfo * CreateRomInfo(RomInfo *parent)
Definition: gamehandler.cpp:920
GameHandler::customEvent
void customEvent(QEvent *event) override
Definition: gamehandler.cpp:933
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
GameHandler::UpdateGameDB
void UpdateGameDB(GameHandler *handler)
Definition: gamehandler.cpp:382
RomInfo::System
QString System() const
Definition: rominfo.h:73
myth_system
uint myth_system(const QString &command, uint flags, std::chrono::seconds timeout)
Definition: mythsystemlegacy.cpp:502
hardwareprofile.scan.scan
def scan(profile, smoonURL, gate)
Definition: scan.py:57
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
mythprogressdialog.h
GameHandler::clearAllMetadata
static void clearAllMetadata(void)
Definition: gamehandler.cpp:975
mythsystemlegacy.h
GameHandler::GetHandlerByName
static GameHandler * GetHandlerByName(const QString &systemname)
Definition: gamehandler.cpp:799
GameHandler::m_progressDlg
MythUIProgressDialog * m_progressDlg
Definition: gamehandler.h:139
UpdateGameCounts
static void UpdateGameCounts(const QStringList &updatelist)
Definition: gamehandler.cpp:296
LOC
#define LOC
Definition: gamehandler.cpp:21
MythUIProgressDialog
Definition: mythprogressdialog.h:59
GameScan
Definition: gamehandler.h:28
RomInfo::Rompath
QString Rompath() const
Definition: rominfo.h:58
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:166
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:200
MythUIProgressDialog::SetProgress
void SetProgress(uint count)
Definition: mythprogressdialog.cpp:199
GameHandler::getHandler
static GameHandler * getHandler(uint i)
Definition: gamehandler.cpp:50
updateDiskCount
static void updateDiskCount(const QString &romname, int diskcount, const QString &GameType)
Definition: gamehandler.cpp:265
GameHandler::promptForRemoval
void promptForRemoval(const GameScan &scan)
Definition: gamehandler.cpp:218
GameHandler::processAllGames
static void processAllGames(void)
Definition: gamehandler.cpp:761
GameHandler::GameType
QString GameType() const
Definition: gamehandler.h:107
GameHandler::processGames
void processGames(GameHandler *handler)
Definition: gamehandler.cpp:674
rominfo.h
handlers
static QList< GameHandler * > * handlers
Definition: gamehandler.cpp:23
MythUIBusyDialog
Definition: mythprogressdialog.h:36
GameHandler::m_systemname
QString m_systemname
Definition: gamehandler.h:120
checkHandlers
static void checkHandlers(void)
Definition: gamehandler.cpp:25
uint
unsigned int uint
Definition: compat.h:140
inFileSystem
@ inFileSystem
Definition: gamehandler.h:23
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
inDatabase
@ inDatabase
Definition: gamehandler.h:24
GameHandler::m_screenshots
QString m_screenshots
Definition: gamehandler.h:124
GameHandler::newHandler
static GameHandler * newHandler(QString name)
Definition: gamehandler.cpp:86
GameHandler::m_keepAll
bool m_keepAll
Definition: gamehandler.h:133
GameHandler::GetHandler
static GameHandler * GetHandler(RomInfo *rominfo)
Definition: gamehandler.cpp:782
RomInfo
Definition: rominfo.h:12
mythuihelper.h
RomInfo::DiskCount
int DiskCount() const
Definition: rominfo.h:94
GameHandler::SystemCmdLine
QString SystemCmdLine() const
Definition: gamehandler.h:102
crcinfo
QString crcinfo(const QString &romname, const QString &GameType, QString *key, RomDBMap *romDB)
Definition: rom_metadata.cpp:54
GameHandler::m_gameplayerid
uint m_gameplayerid
Definition: gamehandler.h:125
GameHandler::SystemName
QString SystemName() const
Definition: gamehandler.h:101
GameHandler::m_commandline
QString m_commandline
Definition: gamehandler.h:122
GameHandler::updateSettings
static void updateSettings(GameHandler *handler)
Definition: gamehandler.cpp:55
kMSProcessEvents
@ kMSProcessEvents
process events while waiting
Definition: mythsystem.h:39
GameHandler::VerifyGameDB
void VerifyGameDB(GameHandler *handler)
Definition: gamehandler.cpp:509
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:878
MythUIProgressDialog::SetTotal
void SetTotal(uint total)
Definition: mythprogressdialog.cpp:193
RomInfo::getExtension
QString getExtension() const
Definition: rominfo.cpp:231
DialogCompletionEvent
Event dispatched from MythUI modal dialogs to a listening class containing a result of some form.
Definition: mythdialogbox.h:41
mythcontext.h
GameHandler::buildFileList
void buildFileList(const QString &directory, GameHandler *handler, int *filecount)
Definition: gamehandler.cpp:626
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
GameHandler::Launchgame
static void Launchgame(RomInfo *romdata, const QString &systemname)
Definition: gamehandler.cpp:816
purgeGameDB
static void purgeGameDB(const QString &filename, const QString &RomPath)
Definition: gamehandler.cpp:196
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:322
GameHandler::m_gameMap
GameScanMap m_gameMap
Definition: gamehandler.h:130
GameHandler::s_newInstance
static GameHandler * s_newInstance
Definition: gamehandler.h:137
GameType
Definition: gamesettings.cpp:240
d
static const iso6937table * d
Definition: iso6937tables.cpp:1025
MythUILocation::AddCurrentLocation
void AddCurrentLocation(const QString &Location)
Definition: mythuilocation.cpp:5
GetMythUI
MythUIHelper * GetMythUI()
Definition: mythuihelper.cpp:66
RomData
Definition: rom_metadata.h:9
rom_metadata.h
build_compdb.filename
filename
Definition: build_compdb.py:21
mythmainwindow.h
GameHandler::m_rompath
QString m_rompath
Definition: gamehandler.h:121
GameHandler::m_romDB
RomDBMap m_romDB
Definition: gamehandler.h:129
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:50
GameHandler::m_validextensions
QStringList m_validextensions
Definition: gamehandler.h:127
GameHandler::GameHandler
GameHandler()=default
GameHandler::m_spandisks
bool m_spandisks
Definition: gamehandler.h:119
updateGameName
static void updateGameName(const QString &romname, const QString &GameName, const QString &Systemname)
Definition: gamehandler.cpp:280
gamehandler.h
GameHandler::SystemRomPath
QString SystemRomPath() const
Definition: gamehandler.h:103
GameHandler::m_gametype
QString m_gametype
Definition: gamehandler.h:126
GameHandler::buildFileCount
static int buildFileCount(const QString &directory, GameHandler *handler)
Definition: gamehandler.cpp:570
GameHandler::m_removeAll
bool m_removeAll
Definition: gamehandler.h:132
GameHandler::m_workingpath
QString m_workingpath
Definition: gamehandler.h:123
RomInfo::Romname
QString Romname() const
Definition: rominfo.h:70
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:922
GameHandler::count
static uint count(void)
Definition: gamehandler.cpp:97
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:827
GameHandler::InitMetaDataMap
void InitMetaDataMap(const QString &GameType)
Definition: gamehandler.cpp:103
GameHandler::SystemWorkingPath
QString SystemWorkingPath() const
Definition: gamehandler.h:104