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