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 <QRegExp>
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  handler->m_validextensions = query.value(6).toString().trimmed()
74  .remove(" ").split(",", QString::SkipEmptyParts);
75  handler->m_spandisks = query.value(7).toBool();
76  }
77 }
78 
80 
82 {
83  s_newInstance = new GameHandler();
84  s_newInstance->m_systemname = std::move(name);
85 
87 
88  return s_newInstance;
89 }
90 
91 // Creates/rebuilds the handler list and then returns the count.
93 {
94  checkHandlers();
95  return handlers->count();
96 }
97 
99 {
100  QString key;
101 
103  query.prepare("SELECT crc, category, year, country, name, "
104  "description, publisher, platform, version, "
105  "binfile FROM romdb WHERE platform = :GAMETYPE;");
106 
107  query.bindValue(":GAMETYPE",GameType);
108 
109  if (query.exec())
110  {
111  while (query.next())
112  {
113  key = QString("%1:%2")
114  .arg(query.value(0).toString())
115  .arg(query.value(9).toString());
116  m_romDB[key] = RomData(
117  query.value(1).toString(),
118  query.value(2).toString(),
119  query.value(3).toString(),
120  query.value(4).toString(),
121  query.value(5).toString(),
122  query.value(6).toString(),
123  query.value(7).toString(),
124  query.value(8).toString());
125  }
126  }
127 
128  if (m_romDB.count() == 0)
129  {
130  LOG(VB_GENERAL, LOG_ERR, LOC + QString("No romDB data read from "
131  "database for gametype %1 . Not imported?").arg(GameType));
132  }
133  else
134  {
135  LOG(VB_GENERAL, LOG_INFO, LOC +
136  QString("Loaded %1 items from romDB Database") .arg(m_romDB.count()));
137  }
138 }
139 
140 void GameHandler::GetMetadata(GameHandler *handler, const QString& rom, QString* Genre, QString* Year,
141  QString* Country, QString* CRC32, QString* GameName,
142  QString *Plot, QString *Publisher, QString *Version,
143  QString* Fanart, QString* Boxart)
144 {
145  QString key;
146  QString tmpcrc;
147 
148  *CRC32 = crcinfo(rom, handler->GameType(), &key, &m_romDB);
149 
150 #if 0
151  LOG(VB_GENERAL, LOG_DEBUG, "Key = " + key);
152 #endif
153 
154  // Set our default values
155  *Year = tr("19xx", "Default game year");
156  *Country = tr("Unknown", "Unknown country");
157  *GameName = tr("Unknown", "Unknown game name");
158  *Genre = tr("Unknown", "Unknown genre");
159  *Plot = tr("Unknown", "Unknown plot");
160  *Publisher = tr("Unknown", "Unknown publisher");
161  *Version = tr("0", "Default game version");
162  (*Fanart).clear();
163  (*Boxart).clear();
164 
165  if (!(*CRC32).isEmpty())
166  {
167  if (m_romDB.contains(key))
168  {
169  LOG(VB_GENERAL, LOG_INFO, LOC + QString("ROMDB FOUND for %1 - %2")
170  .arg(m_romDB[key].GameName()).arg(key));
171  *Year = m_romDB[key].Year();
172  *Country = m_romDB[key].Country();
173  *Genre = m_romDB[key].Genre();
174  *Publisher = m_romDB[key].Publisher();
175  *GameName = m_romDB[key].GameName();
176  *Version = m_romDB[key].Version();
177  }
178  else
179  {
180  LOG(VB_GENERAL, LOG_ERR, LOC + QString("NO ROMDB FOUND for %1 (%2)")
181  .arg(rom).arg(*CRC32));
182  }
183 
184  };
185 
186  if ((*Genre == tr("Unknown", "Unknown genre")) || (*Genre).isEmpty())
187  *Genre = tr("Unknown %1", "Unknown genre")
188  .arg( handler->GameType() );
189 
190 }
191 
192 static void purgeGameDB(const QString& filename, const QString& RomPath)
193 {
194  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Purging %1 - %2").arg(RomPath)
195  .arg(filename));
196 
198 
199  // This should have the added benefit of removing the rom from
200  // other games of the same gametype so we wont be asked to remove it
201  // more than once.
202  query.prepare("DELETE FROM gamemetadata WHERE "
203  "romname = :ROMNAME AND "
204  "rompath = :ROMPATH ");
205 
206  query.bindValue(":ROMNAME",filename);
207  query.bindValue(":ROMPATH",RomPath);
208 
209  if (!query.exec())
210  MythDB::DBError("purgeGameDB", query);
211 
212 }
213 
215 {
216  QString filename = scan.Rom();
217  QString RomPath = scan.RomFullPath();
218 
219  if (m_RemoveAll)
221 
222  if (m_KeepAll || m_RemoveAll)
223  return;
224 
225  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
226  auto *removalPopup = new MythDialogBox(
227  //: %1 is the file name
228  tr("%1 appears to be missing.\n"
229  "Remove it from the database?")
230  .arg(filename), popupStack, "chooseSystemPopup");
231 
232  if (removalPopup->Create())
233  {
234  removalPopup->SetReturnEvent(this, "removalPopup");
235 
236  removalPopup->AddButton(tr("No"));
237  removalPopup->AddButton(tr("No to all"));
238  removalPopup->AddButton(tr("Yes"), QVariant::fromValue(scan));
239  removalPopup->AddButton(tr("Yes to all"), QVariant::fromValue(scan));
240  popupStack->AddScreen(removalPopup);
241 }
242  else
243  delete removalPopup;
244 }
245 
246 static void updateDisplayRom(const QString& romname, int display, const QString& Systemname)
247 {
249  query.prepare("UPDATE gamemetadata SET display = :DISPLAY "
250  "WHERE romname = :ROMNAME AND `system` = :SYSTEM");
251 
252  query.bindValue(":DISPLAY", display);
253  query.bindValue(":ROMNAME", romname);
254  query.bindValue(":SYSTEM", Systemname);
255 
256  if (!query.exec())
257  MythDB::DBError("updateDisplayRom", query);
258 
259 }
260 
261 static void updateDiskCount(const QString& romname, int diskcount, const QString& GameType)
262 {
264  query.prepare("UPDATE gamemetadata SET diskcount = :DISKCOUNT "
265  "WHERE romname = :ROMNAME AND gametype = :GAMETYPE ");
266 
267  query.bindValue(":DISKCOUNT",diskcount);
268  query.bindValue(":ROMNAME", romname);
269  query.bindValue(":GAMETYPE",GameType);
270 
271  if (!query.exec())
272  MythDB::DBError("updateDiskCount", query);
273 
274 }
275 
276 static void updateGameName(const QString& romname, const QString& GameName, const QString& Systemname)
277 {
279  query.prepare("UPDATE gamemetadata SET GameName = :GAMENAME "
280  "WHERE romname = :ROMNAME AND `system` = :SYSTEM ");
281 
282  query.bindValue(":GAMENAME", GameName);
283  query.bindValue(":ROMNAME", romname);
284  query.bindValue(":SYSTEM", Systemname);
285 
286  if (!query.exec())
287  MythDB::DBError("updateGameName", query);
288 
289 }
290 
291 
292 static void UpdateGameCounts(const QStringList& updatelist)
293 {
295 
296  QRegExp multiDiskRGXP = QRegExp( "[0-4]$", Qt::CaseSensitive, QRegExp::RegExp);
297  int pos = 0;
298 
299  QString lastrom;
300  QString firstname;
301  QString basename;
302 
303  for (const auto & GameType : qAsConst(updatelist))
304  {
305  LOG(VB_GENERAL, LOG_NOTICE,
306  LOC + QString("Update gametype %1").arg(GameType));
307 
308  query.prepare("SELECT romname,`system`,spandisks,gamename FROM "
309  "gamemetadata,gameplayers WHERE "
310  "gamemetadata.gametype = :GAMETYPE AND "
311  "playername = `system` ORDER BY romname");
312 
313  query.bindValue(":GAMETYPE",GameType);
314 
315  if (query.exec())
316  {
317  while (query.next())
318  {
319  QString RomName = query.value(0).toString();
320  QString System = query.value(1).toString();
321  int spandisks = query.value(2).toInt();
322  QString GameName = query.value(3).toString();
323 
324  basename = RomName;
325 
326  if (spandisks)
327  {
328  int diskcount = 0;
329  int extlength = 0;
330  pos = RomName.lastIndexOf(".");
331  if (pos > 1)
332  {
333  extlength = RomName.length() - pos;
334  pos--;
335 
336  basename = RomName.mid(pos,1);
337  }
338 
339  if (basename.contains(multiDiskRGXP))
340  {
341  pos = (RomName.length() - extlength) - 1;
342  basename = RomName.left(pos);
343 
344  if (basename.right(1) == ".")
345  basename = RomName.left(pos - 1);
346  }
347  else
348  basename = GameName;
349 
350  if (basename == lastrom)
351  {
352  updateDisplayRom(RomName,0,System);
353  diskcount++;
354  if (diskcount > 1)
355  updateDiskCount(firstname,diskcount,GameType);
356  }
357  else
358  {
359  firstname = RomName;
360  lastrom = basename;
361  }
362 
363  if (basename != GameName)
364  updateGameName(RomName,basename,System);
365  }
366  else
367  {
368  if (basename == lastrom)
369  updateDisplayRom(RomName,0,System);
370  else
371  lastrom = basename;
372  }
373  }
374  }
375  }
376 }
377 
379 {
380  int counter = 0;
382 
383  //: %1 is the system name, %2 is the game type
384  QString message = tr("Updating %1 (%2) ROM database")
385  .arg(handler->SystemName())
386  .arg(handler->GameType());
387 
388  CreateProgress(message);
389 
390  if (m_progressDlg)
392 
393  GameScanMap::Iterator iter;
394 
395  QString GameName;
396  QString Genre;
397  QString Country;
398  QString CRC32;
399  QString Year;
400  QString Plot;
401  QString Publisher;
402  QString Version;
403  QString Fanart;
404  QString Boxart;
405  QString ScreenShot;
406  QString thequery;
407  QString queryvalues;
408 
409  int removalprompt = gCoreContext->GetSetting("GameRemovalPrompt").toInt();
410  int indepth = gCoreContext->GetSetting("GameDeepScan").toInt();
411  QString screenShotPath = gCoreContext->GetSetting("mythgame.screenshotdir");
412 
413  for (iter = m_GameMap.begin(); iter != m_GameMap.end(); ++iter)
414  {
415 
416  if (iter.value().FoundLoc() == inFileSystem)
417  {
418  if (indepth)
419  {
420  GetMetadata(handler, iter.value().RomFullPath(), &Genre, &Year, &Country, &CRC32, &GameName,
421  &Plot, &Publisher, &Version, &Fanart, &Boxart);
422  }
423  else
424  {
425  /*: %1 is the game type, when we don't know the genre we use the
426  * game type */
427  Genre = tr("Unknown %1", "Unknown genre").arg(handler->GameType());
428  Country = tr("Unknown", "Unknown country");
429  CRC32.clear();
430  Year = tr("19xx", "Default game year");
431  GameName = tr("Unknown", "Unknown game name");
432  Plot = tr("Unknown", "Unknown plot");
433  Publisher = tr("Unknown", "Unknown publisher");
434  Version = tr("0", "Default game version");
435  Fanart.clear();
436  Boxart.clear();
437  }
438 
439  if (GameName == tr("Unknown", "Unknown game name"))
440  GameName = iter.value().GameName();
441 
442  int suffixPos = iter.value().Rom().lastIndexOf(QChar('.'));
443  QString baseName = iter.value().Rom();
444 
445  if (suffixPos > 0)
446  baseName = iter.value().Rom().left(suffixPos);
447 
448  baseName = screenShotPath + "/" + baseName;
449 
450  if (QFile(baseName + ".png").exists())
451  ScreenShot = baseName + ".png";
452  else if (QFile(baseName + ".jpg").exists())
453  ScreenShot = baseName + ".jpg";
454  else if (QFile(baseName + ".gif").exists())
455  ScreenShot = baseName + ".gif";
456  else
457  ScreenShot.clear();
458 
459 #if 0
460  LOG(VB_GENERAL, LOG_INFO, QString("file %1 - genre %2 ")
461  .arg(iter.data().Rom()).arg(Genre));
462  LOG(VB_GENERAL, LOG_INFO, QString("screenshot %1").arg(ScreenShot));
463 #endif
464 
465  query.prepare("INSERT INTO gamemetadata "
466  "(`system`, romname, gamename, genre, year, gametype, "
467  "rompath, country, crc_value, diskcount, display, plot, "
468  "publisher, version, fanart, boxart, screenshot) "
469  "VALUES (:SYSTEM, :ROMNAME, :GAMENAME, :GENRE, :YEAR, "
470  ":GAMETYPE, :ROMPATH, :COUNTRY, :CRC32, '1', '1', :PLOT, :PUBLISHER, :VERSION, "
471  ":FANART, :BOXART, :SCREENSHOT)");
472 
473  query.bindValue(":SYSTEM",handler->SystemName());
474  query.bindValue(":ROMNAME",iter.value().Rom());
475  query.bindValue(":GAMENAME",GameName);
476  query.bindValue(":GENRE",Genre);
477  query.bindValue(":YEAR",Year);
478  query.bindValue(":GAMETYPE",handler->GameType());
479  query.bindValue(":ROMPATH",iter.value().RomPath());
480  query.bindValue(":COUNTRY",Country);
481  query.bindValue(":CRC32", CRC32);
482  query.bindValue(":PLOT", Plot);
483  query.bindValue(":PUBLISHER", Publisher);
484  query.bindValue(":VERSION", Version);
485  query.bindValue(":FANART", Fanart);
486  query.bindValue(":BOXART", Boxart);
487  query.bindValue(":SCREENSHOT", ScreenShot);
488 
489  if (!query.exec())
490  MythDB::DBError("GameHandler::UpdateGameDB - "
491  "insert gamemetadata", query);
492  }
493  else if ((iter.value().FoundLoc() == inDatabase) && (removalprompt))
494  {
495 
496  promptForRemoval( iter.value() );
497  }
498 
499  if (m_progressDlg)
500  m_progressDlg->SetProgress(++counter);
501  }
502 
503  if (m_progressDlg)
504  {
505  m_progressDlg->Close();
506  m_progressDlg = nullptr;
507 }
508 }
509 
511 {
512  int counter = 0;
513  GameScanMap::Iterator iter;
514 
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)
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 ((iter = m_GameMap.find(RomName)) != m_GameMap.end())
542  {
543  // If it's both on disk and in the database we're done with it.
544  m_GameMap.erase(iter);
545  }
546  else
547  {
548  // If it's only in the database add it to our list and mark it for
549  // removal.
550  m_GameMap[RomName] = GameScan(RomName,RomPath + "/" + RomName,inDatabase,
551  GameName,RomPath);
552  }
553  }
554  if (m_progressDlg)
555  m_progressDlg->SetProgress(++counter);
556  }
557 
558  if (m_progressDlg)
559  {
560  m_progressDlg->Close();
561  m_progressDlg = nullptr;
562  }
563 }
564 
565 // Recurse through the directory and gather a count on how many files there are to process.
566 // This is used for the progressbar info.
567 int GameHandler::buildFileCount(const QString& directory, GameHandler *handler)
568 {
569  int filecount = 0;
570  QDir RomDir(directory);
571 
572  // If we can't read it's contents move on
573  if (!RomDir.isReadable())
574  return 0;
575 
576  RomDir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
577  QFileInfoList List = RomDir.entryInfoList();
578  for (QFileInfoList::const_iterator it = List.begin();
579  it != List.end(); ++it)
580  {
581  QFileInfo Info = *it;
582  QString RomName = Info.fileName();
583 
584  if (Info.isDir())
585  {
586  filecount += buildFileCount(Info.filePath(), handler);
587  continue;
588  }
589 
590  if (handler->m_validextensions.count() > 0)
591  {
592  QRegExp r;
593 
594  r.setPattern("^" + Info.suffix() + "$");
595  r.setCaseSensitivity(Qt::CaseInsensitive);
596  QStringList result;
597  for (int x = 0; x < handler->m_validextensions.size(); x++)
598  {
599  QString extension = handler->m_validextensions.at(x);
600  if (extension.contains(r))
601  result.append(extension);
602  }
603  if (result.isEmpty())
604  continue;
605  }
606 
607  filecount++;
608  }
609 
610  return filecount;
611 }
612 
614 {
615  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
616  auto *clearPopup = new MythDialogBox(
617  tr("This will clear all game metadata from the database. Are you sure "
618  "you want to do this?"), popupStack, "clearAllPopup");
619 
620  if (clearPopup->Create())
621  {
622  clearPopup->SetReturnEvent(this, "clearAllPopup");
623  clearPopup->AddButton(tr("No"));
624  clearPopup->AddButton(tr("Yes"));
625  popupStack->AddScreen(clearPopup);
626  }
627  else
628  delete clearPopup;
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 (QFileInfoList::const_iterator it = List.begin();
644  it != List.end(); ++it)
645  {
646  QFileInfo Info = *it;
647  QString RomName = Info.fileName();
648  QString GameName = Info.completeBaseName();
649 
650  if (Info.isDir())
651  {
652  buildFileList(Info.filePath(), handler, filecount);
653  continue;
654  }
655 
656  if (handler->m_validextensions.count() > 0)
657  {
658  QRegExp r;
659 
660  r.setPattern("^" + Info.suffix() + "$");
661  r.setCaseSensitivity(Qt::CaseInsensitive);
662  QStringList result;
663  for (int x = 0; x < handler->m_validextensions.size(); x++)
664  {
665  QString extension = handler->m_validextensions.at(x);
666  if (extension.contains(r))
667  result.append(extension);
668  }
669 
670  if (result.isEmpty())
671  continue;
672  }
673 
674  m_GameMap[RomName] = GameScan(RomName,Info.filePath(),inFileSystem,
675  GameName, Info.absoluteDir().path());
676 
677  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found ROM : (%1) - %2")
678  .arg(handler->SystemName()).arg(RomName));
679 
680  *filecount = *filecount + 1;
681  if (m_progressDlg)
682  m_progressDlg->SetProgress(*filecount);
683  }
684 }
685 
687 {
688  QString thequery;
689  int maxcount = 0;
691 
692  if ((!handler->SystemRomPath().isEmpty()) && (handler->GameType() != "PC"))
693  {
694  QDir d(handler->SystemRomPath());
695  if (d.exists())
696  maxcount = buildFileCount(handler->SystemRomPath(),handler);
697  else
698  {
699  LOG(VB_GENERAL, LOG_ERR, LOC +
700  QString("ROM Path does not exist: %1")
701  .arg(handler->SystemRomPath()));
702  return;
703  }
704  }
705  else
706  maxcount = 100;
707 
708  if (handler->GameType() == "PC")
709  {
710  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
711 
712  //: %1 is the system name
713  QString message = tr("Scanning for %1 games...")
714  .arg(handler->SystemName());
715  auto *busyDialog = new MythUIBusyDialog(message, popupStack,
716  "gamescanbusy");
717 
718  if (busyDialog->Create())
719  popupStack->AddScreen(busyDialog, false);
720  else
721  {
722  delete busyDialog;
723  busyDialog = nullptr;
724  }
725 
726  m_GameMap[handler->SystemCmdLine()] =
727  GameScan(handler->SystemCmdLine(),
728  handler->SystemCmdLine(),
729  inFileSystem,
730  handler->SystemName(),
731  handler->SystemCmdLine().left(handler->SystemCmdLine().lastIndexOf(QRegExp("/"))));
732 
733  if (busyDialog)
734  busyDialog->Close();
735 
736  LOG(VB_GENERAL, LOG_INFO, LOC +
737  QString("PC Game %1").arg(handler->SystemName()));
738  }
739  else
740  {
741  QString message = tr("Scanning for %1 games...")
742  .arg(handler->SystemName());
743  CreateProgress(message);
744 
745  if (m_progressDlg)
746  m_progressDlg->SetTotal(maxcount);
747 
748  int filecount = 0;
749  buildFileList(handler->SystemRomPath(), handler, &filecount);
750 
751  if (m_progressDlg)
752  {
753  m_progressDlg->Close();
754  m_progressDlg = nullptr;
755  }
756  }
757 
758  VerifyGameDB(handler);
759 
760  // If we still have some games in the list then update the database
761  if (!m_GameMap.empty())
762  {
763  InitMetaDataMap(handler->GameType());
764 
765  UpdateGameDB(handler);
766 
767  m_romDB.clear();
768  handler->setRebuild(true);
769  }
770  else
771  handler->setRebuild(false);
772 }
773 
775 {
776  checkHandlers();
777  QStringList updatelist;
778 
779  for (auto *handler : qAsConst(*handlers))
780  {
781  if (handler)
782  {
783  updateSettings(handler);
784  handler->processGames(handler);
785 
786  if (handler->needRebuild())
787  updatelist.append(handler->GameType());
788  }
789  }
790 
791  if (!updatelist.isEmpty())
792  UpdateGameCounts(updatelist);
793 }
794 
796 {
797  if (!rominfo)
798  return nullptr;
799 
800  for (auto *handler : qAsConst(*handlers))
801  {
802  if (handler)
803  {
804  if (rominfo->System() == handler->SystemName())
805  return handler;
806  }
807  }
808 
809  return nullptr;
810 }
811 
812 GameHandler* GameHandler::GetHandlerByName(const QString& systemname)
813 {
814  if (systemname.isEmpty() || systemname.isNull())
815  return nullptr;
816 
817  for (auto *handler : qAsConst(*handlers))
818  {
819  if (handler)
820  {
821  if (handler->SystemName() == systemname)
822  return handler;
823  }
824  }
825 
826  return nullptr;
827 }
828 
829 void GameHandler::Launchgame(RomInfo *romdata, const QString& systemname)
830 {
831  GameHandler *handler = nullptr;
832 
833  if (!systemname.isEmpty() && !systemname.isNull())
834  {
835  handler = GetHandlerByName(systemname);
836  }
837  else if (!(handler = GetHandler(romdata)))
838  {
839  // Couldn't get handler so abort.
840  return;
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(QRegExp("%s"),arg);
858 
859  if (handler->SpanDisks())
860  {
861  QRegExp rxp = QRegExp( "%d[0-4]", Qt::CaseSensitive, QRegExp::RegExp);
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  QString 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())
877  .arg(basename)
878  .arg(disk)
879  .arg(extension);
880  exec = exec.replace(QRegExp(diskid[disk]),rom);
881  }
882  } else
883  { // If there is only one disk make sure we replace %d1 just like %s
884  exec = exec.replace(QRegExp("%d1"),arg);
885  }
886  }
887  }
888  }
889  else
890  {
891  exec = exec + " \"" +
892  romdata->Rompath() + "/" +
893  romdata->Romname() + "\"";
894  }
895  }
896 
897  QString savedir = QDir::current().path();
898  QDir d;
899  if (!handler->SystemWorkingPath().isEmpty())
900  {
901  if (!d.cd(handler->SystemWorkingPath()))
902  {
903  LOG(VB_GENERAL, LOG_ERR, LOC +
904  QString("Failed to change to specified Working Directory: %1")
905  .arg(handler->SystemWorkingPath()));
906  }
907  }
908  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Launching Game : %1 : %2")
909  .arg(handler->SystemName())
910  .arg(exec));
911 
912  GetMythUI()->AddCurrentLocation(QString("MythGame %1 ( %2 )").arg(handler->SystemName()).arg(exec));
913 
914  QStringList cmdlist = exec.split(";");
915  if (cmdlist.count() > 0)
916  {
917  for (const auto & cmd : qAsConst(cmdlist))
918  {
919  LOG(VB_GENERAL, LOG_INFO, LOC +
920  QString("Executing : %1").arg(cmd));
922  }
923  }
924  else
925  {
926  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Executing : %1").arg(exec));
928  }
929 
931 
932  (void)d.cd(savedir);
933 }
934 
936 {
937  if (!parent || !GetHandler(parent))
938  return nullptr;
939 
940  return new RomInfo(*parent);
941 }
942 
944 {
945  handlers->append(handler);
946 }
947 
948 void GameHandler::customEvent(QEvent *event)
949 {
950  if (auto *dce = dynamic_cast<DialogCompletionEvent*>(event))
951  {
952  QString resultid = dce->GetId();
953  QString resulttext = dce->GetResultText();
954 
955  if (resultid == "removalPopup")
956  {
957  int buttonNum = dce->GetResult();
958  auto scan = dce->GetData().value<GameScan>();
959  switch (buttonNum)
960  {
961  case 1:
962  m_KeepAll = true;
963  break;
964  case 2:
965  purgeGameDB(scan.Rom() , scan.RomFullPath());
966  break;
967  case 3:
968  m_RemoveAll = true;
969  purgeGameDB(scan.Rom() , scan.RomFullPath());
970  break;
971  default:
972  break;
973  };
974  }
975  else if (resultid == "clearAllPopup")
976  {
977  int buttonNum = dce->GetResult();
978  switch (buttonNum)
979  {
980  case 1:
982  break;
983  default:
984  break;
985  }
986  }
987  }
988 }
989 
991 {
993  if (!query.exec("DELETE FROM gamemetadata;"))
994  MythDB::DBError("GameHandler::clearAllGameData - "
995  "delete gamemetadata", query);
996 }
997 
998 void GameHandler::CreateProgress(const QString& message)
999 {
1000  if (m_progressDlg)
1001  return;
1002 
1003  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
1004 
1005  m_progressDlg = new MythUIProgressDialog(message, popupStack,
1006  "gameprogress");
1007 
1008  if (m_progressDlg->Create())
1009  {
1010  popupStack->AddScreen(m_progressDlg, false);
1011  }
1012  else
1013  {
1014  delete m_progressDlg;
1015  m_progressDlg = nullptr;
1016  }
1017 }
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
static int buildFileCount(const QString &directory, GameHandler *handler)
void clearAllGameData(void)
bool m_KeepAll
Definition: gamehandler.h:133
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
def scan(profile, smoonURL, gate)
Definition: scan.py:57
QString m_gametype
Definition: gamehandler.h:126
void SetProgress(uint count)
bool Create(void) override
bool SpanDisks() const
Definition: gamehandler.h:100
void setRebuild(bool setrebuild)
Definition: gamehandler.h:96
#define LOC
Definition: gamehandler.cpp:21
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
Basic menu dialog, message and a list of options.
void customEvent(QEvent *event) override
void UpdateGameDB(GameHandler *handler)
int size(void) const
Definition: mythdbcon.h:203
MythScreenStack * GetStack(const QString &stackname)
void AddCurrentLocation(const QString &location)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static void registerHandler(GameHandler *handler)
QString Romname() const
Definition: rominfo.h:93
static void clearAllMetadata(void)
static RomInfo * CreateRomInfo(RomInfo *parent)
static void updateGameName(const QString &romname, const QString &GameName, const QString &Systemname)
static void checkHandlers(void)
Definition: gamehandler.cpp:25
QVariant value(int i) const
Definition: mythdbcon.h:198
virtual void Close()
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static void updateDisplayRom(const QString &romname, int display, const QString &Systemname)
QString m_systemname
Definition: gamehandler.h:120
bool m_RemoveAll
Definition: gamehandler.h:132
MSqlQuery query(MSqlQuery::InitCon())
static void processAllGames(void)
static void purgeGameDB(const QString &filename, const QString &RomPath)
void processGames(GameHandler *handler)
QString crcinfo(const QString &romname, const QString &GameType, QString *key, RomDBMap *romDB)
static const uint16_t * d
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
static GameHandler * GetHandlerByName(const QString &systemname)
GameScanMap m_GameMap
Definition: gamehandler.h:130
QString SystemCmdLine() const
Definition: gamehandler.h:102
static GameHandler * getHandler(uint i)
Definition: gamehandler.cpp:50
MythUIProgressDialog * m_progressDlg
Definition: gamehandler.h:139
void promptForRemoval(const GameScan &scan)
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
static QList< GameHandler * > * handlers
Definition: gamehandler.cpp:23
QString m_commandline
Definition: gamehandler.h:122
static void updateDiskCount(const QString &romname, int diskcount, const QString &GameType)
static GameHandler * newHandler(QString name)
Definition: gamehandler.cpp:81
uint myth_system(const QString &command, uint flags, uint timeout)
#define CRC32(c, b)
CRC32 routine.
Definition: unzip.cpp:177
MythUIHelper * GetMythUI()
MythMainWindow * GetMythMainWindow(void)
static void Launchgame(RomInfo *romdata, const QString &systemname)
QString GameType() const
Definition: gamehandler.h:107
static void updateSettings(GameHandler *handler)
Definition: gamehandler.cpp:55
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
process events while waiting
Definition: mythsystem.h:37
QString Rompath() const
Definition: rominfo.h:81
int DiskCount() const
Definition: rominfo.h:117
QString RemoveCurrentLocation(void)
QString m_screenshots
Definition: gamehandler.h:124
QString m_rompath
Definition: gamehandler.h:121
QString SystemName() const
Definition: gamehandler.h:101
bool m_spandisks
Definition: gamehandler.h:119
static GameHandler * GetHandler(RomInfo *rominfo)
void VerifyGameDB(GameHandler *handler)
void buildFileList(const QString &directory, GameHandler *handler, int *filecount)
void InitMetaDataMap(const QString &GameType)
Definition: gamehandler.cpp:98
QString getExtension() const
Definition: rominfo.cpp:234
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
uint m_gameplayerid
Definition: gamehandler.h:125
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
static GameHandler * s_newInstance
Definition: gamehandler.h:137
void CreateProgress(const QString &message)
static void UpdateGameCounts(const QStringList &updatelist)
QString m_workingpath
Definition: gamehandler.h:123
static uint count(void)
Definition: gamehandler.cpp:92
QString SystemWorkingPath() const
Definition: gamehandler.h:104
QString System() const
Definition: rominfo.h:96
RomDBMap m_romDB
Definition: gamehandler.h:129
GameHandler()=default
QStringList m_validextensions
Definition: gamehandler.h:127
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)
QString SystemRomPath() const
Definition: gamehandler.h:103
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23