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