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