1 #include <Qt>
2 #if QT_VERSION >= 0x050000
3 #include <QtWebKitWidgets/QWebView>
4 #else
5 #include <QWebView>
6 #endif
7 #include <QTextStream>
8 #include <QHeaderView>
9 #include <QTreeWidgetItem>
10 #include <QStringList>
11 #include <QFile>
12 #include <QFileInfoList>
13 #include <QFontMetrics>
14 #include <QFont>
15 #include <QTimer>
16 #include <QMap>
17 #include <QSet>
18 #include <QChar>
19 #include <QDir>
20 #include <QDirIterator>
21 #include <QBitArray>
22 #include <QByteArray>
23 #include <QCryptographicHash>
24 #include <QApplication>
25 #include <QSplashScreen>
26 
27 #include "machinelist.h"
28 #include "imagewidget.h"
29 #include "emuopt.h"
30 #include "qmc2main.h"
31 #include "options.h"
32 #include "preview.h"
33 #include "flyer.h"
34 #include "cabinet.h"
35 #include "controller.h"
36 #include "marquee.h"
37 #include "title.h"
38 #include "pcb.h"
39 #include "romstatusexport.h"
40 #include "miniwebbrowser.h"
41 #include "romalyzer.h"
42 #include "macros.h"
43 #include "unzip.h"
44 #include "sevenzipfile.h"
45 #include "demomode.h"
46 #include "deviceconfigurator.h"
47 #include "softwarelist.h"
48 #if defined(QMC2_YOUTUBE_ENABLED)
49 #include "youtubevideoplayer.h"
50 #endif
51 #include "htmleditor/htmleditor.h"
52 #include "aspectratiolabel.h"
53 #include "processmanager.h"
54 #if defined(QMC2_LIBARCHIVE_ENABLED)
55 #include "archivefile.h"
56 #endif
57 
58 // external global variables
59 extern MainWindow *qmc2MainWindow;
60 extern Options *qmc2Options;
61 extern Settings *qmc2Config;
62 extern EmulatorOptions *qmc2EmulatorOptions;
63 extern ROMStatusExporter *qmc2ROMStatusExporter;
64 extern ROMAlyzer *qmc2SystemROMAlyzer;
65 extern ROMAlyzer *qmc2SoftwareROMAlyzer;
66 extern bool qmc2ReloadActive;
67 extern bool qmc2EarlyReloadActive;
68 extern bool qmc2LoadingInterrupted;
69 extern bool qmc2StartingUp;
70 extern bool qmc2VerifyActive;
71 extern bool qmc2VerifyTaggedActive;
72 extern bool qmc2FilterActive;
73 extern bool qmc2UseIconFile;
74 extern bool qmc2IconsPreloaded;
75 extern bool qmc2WidgetsEnabled;
76 extern bool qmc2StatesTogglesEnabled;
77 extern bool qmc2ForceCacheRefresh;
78 extern bool qmc2SortingActive;
79 extern bool qmc2GuiReady;
80 extern int qmc2MachineListResponsiveness;
81 extern Preview *qmc2Preview;
82 extern Flyer *qmc2Flyer;
83 extern Cabinet *qmc2Cabinet;
84 extern Controller *qmc2Controller;
85 extern Marquee *qmc2Marquee;
86 extern Title *qmc2Title;
87 extern PCB *qmc2PCB;
88 extern QTreeWidgetItem *qmc2CurrentItem;
89 extern QTreeWidgetItem *qmc2LastMachineInfoItem;
90 extern QTreeWidgetItem *qmc2LastEmuInfoItem;
91 extern QTreeWidgetItem *qmc2LastSoftwareListItem;
92 extern QTreeWidgetItem *qmc2LastDeviceConfigItem;
93 extern DeviceConfigurator *qmc2DeviceConfigurator;
94 extern SoftwareList *qmc2SoftwareList;
95 extern QHash<QString, QStringList> systemSoftwareListHash;
96 extern QHash<QString, QStringList> systemSoftwareFilterHash;
97 extern QHash<QString, QTreeWidgetItem *> qmc2MachineListItemHash;
98 extern QHash<QString, QTreeWidgetItem *> qmc2HierarchyItemHash;
99 extern QHash<QString, QString> qmc2ParentHash;
100 extern int qmc2SortCriteria;
101 extern Qt::SortOrder qmc2SortOrder;
102 extern QBitArray qmc2Filter;
103 extern QMap<QString, unzFile> qmc2IconFileMap;
104 extern QMap<QString, SevenZipFile *> qmc2IconFileMap7z;
105 #if defined(QMC2_LIBARCHIVE_ENABLED)
106 extern QMap<QString, ArchiveFile *> qmc2IconArchiveMap;
107 #endif
108 extern QHash<QString, QIcon> qmc2IconHash;
109 extern QTreeWidgetItem *qmc2LastProjectMESSItem;
110 extern MiniWebBrowser *qmc2ProjectMESSLookup;
111 extern QHash<QString, QTreeWidgetItem *> qmc2CategoryItemHash;
112 extern QHash<QString, QTreeWidgetItem *> qmc2VersionItemHash;
113 extern DemoModeDialog *qmc2DemoModeDialog;
114 #if defined(QMC2_YOUTUBE_ENABLED)
115 extern YouTubeVideoPlayer *qmc2YouTubeWidget;
116 extern QTreeWidgetItem *qmc2LastYouTubeItem;
117 #endif
118 extern HtmlEditor *qmc2SystemNotesEditor;
119 extern HtmlEditor *qmc2SoftwareNotesEditor;
120 extern QList<QTreeWidgetItem *> qmc2ExpandedMachineListItems;
121 extern MachineList *qmc2MachineList;
122 extern bool qmc2TemplateCheck;
123 extern bool qmc2ParentImageFallback;
124 extern QTime qmc2StartupTimer;
125 extern QSplashScreen *qmc2SplashScreen;
126 
127 QStringList MachineList::phraseTranslatorList;
128 QStringList MachineList::romTypeNames;
129 QHash<QString, QString> MachineList::reverseTranslations;
130 QHash<QString, QString> MachineList::machineStateTranslations;
131 bool MachineList::creatingCatView = false;
132 bool MachineList::creatingVerView = false;
133 QString MachineList::trQuestionMark;
134 QString MachineList::trWaitingForData;
135 Qt::ItemFlags MachineListItem::defaultItemFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
136 
137 #define mainProgressBar		qmc2MainWindow->progressBarMachineList
138 
MachineList(QObject * parent)139 MachineList::MachineList(QObject *parent) :
140 	QObject(parent),
141 	m_showC(true),
142 	m_showM(true),
143 	m_showI(true),
144 	m_showN(true),
145 	m_showU(true),
146 	m_showDeviceSets(true),
147 	m_showBiosSets(true),
148 	m_doFilter(true)
149 {
150 	numMachines = numTotalMachines = numCorrectMachines = numMostlyCorrectMachines = numIncorrectMachines = numUnknownMachines = numNotFoundMachines = -1;
151 	uncommittedXmlDbRows = numTaggedSets = numMatchedMachines = numVerifyRoms = 0;
152 	loadProc = verifyProc = 0;
153 	checkedItem = 0;
154 	emulatorVersion = tr("unknown");
155 	mergeCategories = autoRomCheck = verifyCurrentOnly = dtdBufferReady = false;
156 	initialLoad = true;
157 
158 	m_trL = tr("L:");
159 	m_trC = tr("C:");
160 	m_trM = tr("M:");
161 	m_trI = tr("I:");
162 	m_trN = tr("N:");
163        	m_trU = tr("U:");
164        	m_trS = tr("S:");
165 	m_trT = tr("T:");
166 
167 	qmc2UnknownImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_blue.png"));
168 	qmc2UnknownBIOSImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_blue_bios.png"));
169 	qmc2UnknownDeviceImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_blue_device.png"));
170 	qmc2CorrectImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_green.png"));
171 	qmc2CorrectBIOSImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_green_bios.png"));
172 	qmc2CorrectDeviceImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_green_device.png"));
173 	qmc2MostlyCorrectImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_yellowgreen.png"));
174 	qmc2MostlyCorrectBIOSImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_yellowgreen_bios.png"));
175 	qmc2MostlyCorrectDeviceImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_yellowgreen_device.png"));
176 	qmc2IncorrectImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_red.png"));
177 	qmc2IncorrectBIOSImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_red_bios.png"));
178 	qmc2IncorrectDeviceImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_red_device.png"));
179 	qmc2NotFoundImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_grey.png"));
180 	qmc2NotFoundBIOSImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_grey_bios.png"));
181 	qmc2NotFoundDeviceImageIcon = QIcon(QString::fromUtf8(":/data/img/sphere_grey_device.png"));
182 
183 	// translation look-up hashes & maps
184 	phraseTranslatorList << tr("good") << tr("bad") << tr("preliminary") << tr("supported") << tr("unsupported")
185 		<< tr("imperfect") << tr("yes") << tr("no") << tr("baddump") << tr("nodump")
186 		<< tr("vertical") << tr("horizontal") << tr("raster") << tr("unknown") << tr("Unknown")
187 		<< tr("On") << tr("Off") << tr("audio") << tr("unused") << tr("Unused") << tr("cpu")
188 		<< tr("vector") << tr("lcd") << tr("joy4way") << tr("joy8way") << tr("trackball")
189 		<< tr("joy2way") << tr("doublejoy8way") << tr("dial") << tr("paddle") << tr("pedal")
190 		<< tr("stick") << tr("vjoy2way") << tr("lightgun") << tr("doublejoy4way") << tr("vdoublejoy2way")
191 		<< tr("doublejoy2way") << tr("printer") << tr("cdrom") << tr("cartridge") << tr("cassette")
192 		<< tr("quickload") << tr("floppydisk") << tr("serial") << tr("snapshot") << tr("original")
193 		<< tr("compatible") << tr("N/A");
194 	reverseTranslations.insert(tr("good"), "good");
195 	reverseTranslations.insert(tr("bad"), "bad");
196 	reverseTranslations.insert(tr("preliminary"), "preliminary");
197 	reverseTranslations.insert(tr("supported"), "supported");
198 	reverseTranslations.insert(tr("unsupported"), "unsupported");
199 	reverseTranslations.insert(tr("imperfect"), "imperfect");
200 	reverseTranslations.insert(QObject::tr("yes"), "yes");
201 	reverseTranslations.insert(QObject::tr("no"), "no");
202 	reverseTranslations.insert(QObject::tr("partially"), "partially");
203 	machineStateTranslations.insert("good", tr("good"));
204 	machineStateTranslations.insert("preliminary", tr("preliminary"));
205 	machineStateTranslations.insert("imperfect", tr("imperfect"));
206 	machineStateTranslations.insert("N/A", tr("N/A"));
207 	romTypeNames << "--" << tr("ROM") << tr("CHD") << tr("ROM, CHD");
208 	trQuestionMark = tr("?");
209 	trWaitingForData = tr("Waiting for data...");
210 
211 	// identifier strings (see "mame -help") that we support - others will produce a warning
212 	emulatorIdentifiers << "MAME" << "M.A.M.E." << "HBMAME" << "HB.M.A.M.E." << "MESS" << "M.E.S.S.";
213 
214 	// status string template
215 	m_statusTemplate = "<b><font color=\"black\">%1</font>&nbsp;"
216 			   "<font color=\"#00cc00\">%2</font>&nbsp;"
217 			   "<font color=\"#799632\">%3</font>&nbsp;"
218 			   "<font color=\"#f90000\">%4</font>&nbsp;"
219 			   "<font color=\"#7f7f7f\">%5</font>&nbsp;"
220 			   "<font color=\"#0000f9\">%6</font>&nbsp;"
221 			   "<font color=\"chocolate\">%7</font>&nbsp;"
222 			   "<font color=\"sandybrown\">%8</font></b>";
223 
224 	switch ( qmc2Options->iconFileType() ) {
225 		case QMC2_ICON_FILETYPE_ZIP:
226 			foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/IconFile").toString().split(";", QString::SkipEmptyParts)) {
227 				unzFile iconFile = unzOpen(filePath.toUtf8().constData());
228 				if ( iconFile == 0 )
229 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open icon file, please check access permissions for %1").arg(filePath));
230 				else
231 					qmc2IconFileMap.insert(filePath, iconFile);
232 			}
233 			break;
234 		case QMC2_ICON_FILETYPE_7Z:
235 			foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/IconFile").toString().split(";", QString::SkipEmptyParts)) {
236 				SevenZipFile *iconFile = new SevenZipFile(filePath);
237 				if ( !iconFile->open() ) {
238 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open icon file %1").arg(filePath) + " - " + tr("7z error") + ": " + iconFile->lastError());
239 					delete iconFile;
240 				} else
241 					qmc2IconFileMap7z.insert(filePath, iconFile);
242 			}
243 			break;
244 #if defined(QMC2_LIBARCHIVE_ENABLED)
245 		case QMC2_ICON_FILETYPE_ARCHIVE:
246 			foreach (QString filePath, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/IconFile").toString().split(";", QString::SkipEmptyParts)) {
247 				ArchiveFile *archiveFile = new ArchiveFile(filePath, true);
248 				if ( !archiveFile->open() ) {
249 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open icon file %1").arg(filePath) + " - " + tr("libarchive error") + ": " + archiveFile->errorString());
250 					delete archiveFile;
251 				} else
252 					qmc2IconArchiveMap.insert(filePath, archiveFile);
253 			}
254 			break;
255 #endif
256 	}
257 
258 	m_xmlDb = new XmlDatabaseManager(this);
259 	xmlDb()->setSyncMode(QMC2_DB_SYNC_MODE_OFF);
260 	xmlDb()->setJournalMode(QMC2_DB_JOURNAL_MODE_MEMORY);
261 	m_userDataDb = new UserDataDatabaseManager(this);
262 	userDataDb()->setSyncMode(QMC2_DB_SYNC_MODE_OFF);
263 	userDataDb()->setJournalMode(QMC2_DB_JOURNAL_MODE_MEMORY);
264 	m_datInfoDb = new DatInfoDatabaseManager(this);
265 	datInfoDb()->setSyncMode(QMC2_DB_SYNC_MODE_OFF);
266 	datInfoDb()->setJournalMode(QMC2_DB_JOURNAL_MODE_MEMORY);
267 	m_machineListDb = new MachineListDatabaseManager(this);
268 	machineListDb()->setSyncMode(QMC2_DB_SYNC_MODE_OFF);
269 	machineListDb()->setJournalMode(QMC2_DB_JOURNAL_MODE_MEMORY);
270 	m_iconCacheDb = new IconCacheDatabaseManager(this);
271 	iconCacheDb()->setSyncMode(QMC2_DB_SYNC_MODE_OFF);
272 	iconCacheDb()->setJournalMode(QMC2_DB_JOURNAL_MODE_MEMORY);
273 
274 	connect(this, SIGNAL(widgetsEnabled(bool)), qmc2Options, SLOT(enableWidgets(bool)));
275 }
276 
~MachineList()277 MachineList::~MachineList()
278 {
279 	if ( loadProc )
280 		loadProc->kill();
281 	if ( verifyProc )
282 		verifyProc->kill();
283 	clearCategoryNames();
284 	categoryHash.clear();
285 	clearVersionNames();
286 	versionHash.clear();
287 	foreach (unzFile iconFile, qmc2IconFileMap)
288 		unzClose(iconFile);
289 	foreach (SevenZipFile *iconFile, qmc2IconFileMap7z) {
290 		iconFile->close();
291 		delete iconFile;
292 	}
293 #if defined(QMC2_LIBARCHIVE_ENABLED)
294 	foreach (ArchiveFile *iconFile, qmc2IconArchiveMap) {
295 		iconFile->close();
296 		delete iconFile;
297 	}
298 #endif
299 	QString connectionName(xmlDb()->connectionName());
300 	delete xmlDb();
301 	QSqlDatabase::removeDatabase(connectionName);
302 	connectionName = userDataDb()->connectionName();
303 	delete userDataDb();
304 	QSqlDatabase::removeDatabase(connectionName);
305 	connectionName = datInfoDb()->connectionName();
306 	delete datInfoDb();
307 	QSqlDatabase::removeDatabase(connectionName);
308 	connectionName = machineListDb()->connectionName();
309 	delete machineListDb();
310 	QSqlDatabase::removeDatabase(connectionName);
311 	connectionName = iconCacheDb()->connectionName();
312 	delete iconCacheDb();
313 	QSqlDatabase::removeDatabase(connectionName);
314 }
315 
enableWidgets(bool enable)316 void MachineList::enableWidgets(bool enable)
317 {
318 	static bool lastEnable = true;
319 	qmc2WidgetsEnabled = enable;
320 	if ( enable ) {
321 		if ( qmc2MainWindow->labelLoadingMachineList->isVisible() || qmc2MainWindow->labelLoadingHierarchy->isVisible() || qmc2MainWindow->labelLoadingAttachedViews->isVisible() ) {
322 			// show machine list / hide loading animation
323 			qmc2MainWindow->loadAnimMovie->setPaused(true);
324 			qmc2MainWindow->labelLoadingMachineList->setVisible(false);
325 			qmc2MainWindow->treeWidgetMachineList->setVisible(true);
326 			qmc2MainWindow->labelLoadingHierarchy->setVisible(false);
327 			qmc2MainWindow->treeWidgetHierarchy->setVisible(true);
328 			qmc2MainWindow->labelLoadingAttachedViews->setVisible(false);
329 			qmc2MainWindow->attachedViewsWidget->setVisible(true);
330 		}
331 	}
332 	// avoid redundant operations
333 	if ( lastEnable == enable )
334 		return;
335 	lastEnable = enable;
336 #if QMC2_USE_PHONON_API || QMC2_MULTIMEDIA_ENABLED
337 	qmc2MainWindow->toolButtonAudioAddTracks->setEnabled(enable);
338 	qmc2MainWindow->toolButtonAudioAddURL->setEnabled(enable);
339 #endif
340 	qmc2MainWindow->actionRelaunchSetupWizard->setEnabled(enable);
341 	if ( qmc2ROMStatusExporter )
342 		qmc2ROMStatusExporter->pushButtonExport->setEnabled(enable);
343 	if ( qmc2SystemROMAlyzer ) {
344 		qmc2SystemROMAlyzer->pushButtonAnalyze->setEnabled(enable);
345 		qmc2SystemROMAlyzer->toolButtonToolsMenu->setEnabled(enable);
346 		qmc2SystemROMAlyzer->toolButtonBrowseBackupFolder->setEnabled(qmc2SystemROMAlyzer->checkBoxCreateBackups->isChecked() && enable);
347 		if ( qmc2SystemROMAlyzer->groupBoxCHDManager->isChecked() ) {
348 			qmc2SystemROMAlyzer->toolButtonBrowseCHDManagerExecutableFile->setEnabled(enable);
349 			qmc2SystemROMAlyzer->toolButtonBrowseTemporaryWorkingDirectory->setEnabled(enable);
350 		}
351 		if ( qmc2SystemROMAlyzer->groupBoxSetRewriter->isChecked() ) {
352 			qmc2SystemROMAlyzer->toolButtonBrowseSetRewriterOutputPath->setEnabled(enable);
353 			qmc2SystemROMAlyzer->toolButtonBrowseSetRewriterAdditionalRomPath->setEnabled(qmc2SystemROMAlyzer->checkBoxSetRewriterUseAdditionalRomPath->isChecked() && enable);
354 		}
355 		if ( qmc2SystemROMAlyzer->groupBoxCheckSumDatabase->isChecked() ) {
356 			qmc2SystemROMAlyzer->toolButtonBrowseCheckSumDbDatabasePath->setEnabled(enable);
357 			qmc2SystemROMAlyzer->toolButtonCheckSumDbAddPath->setEnabled(enable);
358 		}
359 	} else {
360 		qmc2MainWindow->actionSystemROMAlyzer->setEnabled(enable);
361 		qmc2MainWindow->actionAnalyseCurrentROM->setEnabled(enable);
362 		qmc2MainWindow->actionAnalyseROMTagged->setEnabled(enable);
363 		foreach (QAction *action, qmc2MainWindow->criticalActions)
364 			action->setEnabled(enable);
365 	}
366 	if ( qmc2SoftwareROMAlyzer ) {
367 		qmc2SoftwareROMAlyzer->pushButtonAnalyze->setEnabled(enable);
368 		qmc2SoftwareROMAlyzer->toolButtonToolsMenu->setEnabled(enable);
369 		qmc2SoftwareROMAlyzer->toolButtonBrowseBackupFolder->setEnabled(qmc2SoftwareROMAlyzer->checkBoxCreateBackups->isChecked() && enable);
370 		if ( qmc2SoftwareROMAlyzer->groupBoxCHDManager->isChecked() ) {
371 			qmc2SoftwareROMAlyzer->toolButtonBrowseCHDManagerExecutableFile->setEnabled(enable);
372 			qmc2SoftwareROMAlyzer->toolButtonBrowseTemporaryWorkingDirectory->setEnabled(enable);
373 		}
374 		if ( qmc2SoftwareROMAlyzer->groupBoxSetRewriter->isChecked() ) {
375 			qmc2SoftwareROMAlyzer->toolButtonBrowseSetRewriterOutputPath->setEnabled(enable);
376 			qmc2SoftwareROMAlyzer->toolButtonBrowseSetRewriterAdditionalRomPath->setEnabled(qmc2SoftwareROMAlyzer->checkBoxSetRewriterUseAdditionalRomPath->isChecked() && enable);
377 		}
378 		if ( qmc2SoftwareROMAlyzer->groupBoxCheckSumDatabase->isChecked() ) {
379 			qmc2SoftwareROMAlyzer->toolButtonBrowseCheckSumDbDatabasePath->setEnabled(enable);
380 			qmc2SoftwareROMAlyzer->toolButtonCheckSumDbAddPath->setEnabled(enable);
381 		}
382 	} else
383 		qmc2MainWindow->actionSoftwareROMAlyzer->setEnabled(enable);
384 	qmc2MainWindow->toolButtonSelectRomFilter->setEnabled(enable);
385 	qmc2MainWindow->actionLaunchArcade->setEnabled(enable);
386 	qmc2MainWindow->actionArcadeSetup->setEnabled(enable);
387 	emit widgetsEnabled(enable);
388 }
389 
load()390 void MachineList::load()
391 {
392 	qmc2ReloadActive = qmc2EarlyReloadActive = true;
393 	qmc2LoadingInterrupted = false;
394 	if ( qmc2DemoModeDialog )
395 		qmc2DemoModeDialog->saveCategoryFilter();
396 	QTimer::singleShot(0, this, SLOT(disableWidgets()));
397 	machineStatusHash.clear();
398 	qmc2MachineListItemHash.clear();
399 	qmc2HierarchyItemHash.clear();
400 	qmc2CategoryItemHash.clear();
401 	qmc2VersionItemHash.clear();
402 	qmc2MainWindow->clearSortedItemMap();
403 	qmc2ExpandedMachineListItems.clear();
404 	biosSets.clear();
405 	deviceSets.clear();
406 	qDeleteAll(qmc2MainWindow->rankItemWidgets());
407 	qmc2MainWindow->rankItemWidgets().clear();
408 	userDataDb()->clearRankCache();
409 	userDataDb()->clearCommentCache();
410 	qmc2MainWindow->stackedWidgetSpecial_setCurrentIndex(QMC2_SPECIAL_DEFAULT_PAGE);
411 	numMachines = numTotalMachines = numCorrectMachines = numMostlyCorrectMachines = numIncorrectMachines = numUnknownMachines = numNotFoundMachines = -1;
412 	numTaggedSets = numMatchedMachines = 0;
413 	qmc2MainWindow->treeWidgetMachineList->clear();
414 	qmc2MainWindow->treeWidgetHierarchy->clear();
415 	qmc2MainWindow->treeWidgetCategoryView->clear();
416 	qmc2MainWindow->treeWidgetVersionView->clear();
417 	qmc2MainWindow->listWidgetSearch->clear();
418 	qmc2MainWindow->listWidgetFavorites->clear();
419 	qmc2MainWindow->listWidgetPlayed->clear();
420 	qmc2MainWindow->textBrowserMachineInfo->clear();
421 	qmc2MainWindow->textBrowserEmuInfo->clear();
422 	qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorBlue);
423 	qmc2CurrentItem = 0;
424 	QTreeWidgetItem *dummyItem;
425 	dummyItem = new QTreeWidgetItem(qmc2MainWindow->treeWidgetMachineList);
426 	dummyItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, trWaitingForData);
427 	dummyItem = new QTreeWidgetItem(qmc2MainWindow->treeWidgetHierarchy);
428 	dummyItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, trWaitingForData);
429 	dummyItem = new QTreeWidgetItem(qmc2MainWindow->treeWidgetCategoryView);
430 	dummyItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, trWaitingForData);
431 	dummyItem = new QTreeWidgetItem(qmc2MainWindow->treeWidgetVersionView);
432 	dummyItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, trWaitingForData);
433 	qmc2MainWindow->labelMachineListStatus->setText(status());
434 	ImageWidget::updateArtwork();
435 	if ( qmc2DeviceConfigurator ) {
436 		qmc2DeviceConfigurator->save();
437 		qmc2DeviceConfigurator->saveSetup();
438 		qmc2DeviceConfigurator->setVisible(false);
439 		QLayout *vbl = qmc2MainWindow->tabDevices->layout();
440 		if ( vbl )
441 			delete vbl;
442 		delete qmc2DeviceConfigurator;
443 		qmc2DeviceConfigurator = 0;
444 	}
445 	qmc2LastDeviceConfigItem = 0;
446 	if ( qmc2SystemNotesEditor ) {
447 		qmc2SystemNotesEditor->save();
448 		qmc2SystemNotesEditor->closeXmlBuffer();
449 		qmc2SystemNotesEditor->clearContent();
450 	}
451 	if ( qmc2SoftwareNotesEditor ) {
452 		qmc2SoftwareNotesEditor->save();
453 		qmc2SoftwareNotesEditor->closeXmlBuffer();
454 		qmc2SoftwareNotesEditor->clearContent();
455 	}
456 	if ( qmc2SoftwareList ) {
457 		if ( qmc2SoftwareList->isLoading ) {
458 			qmc2SoftwareList->interruptLoad = true;
459 			qmc2LastSoftwareListItem = 0;
460 			QTimer::singleShot(0, this, SLOT(load()));
461 			return;
462 		}
463 		qmc2SoftwareList->save();
464 		qmc2SoftwareList->setVisible(false);
465 		QLayout *vbl = qmc2MainWindow->tabSoftwareList->layout();
466 		if ( vbl )
467 			delete vbl;
468 		delete qmc2SoftwareList;
469 		qmc2SoftwareList = 0;
470 	}
471 	qmc2LastSoftwareListItem = 0;
472 	SoftwareList::swlSupported = true;
473 	systemSoftwareListHash.clear();
474 	systemSoftwareFilterHash.clear();
475 	qmc2LastMachineInfoItem = 0;
476 	qmc2LastEmuInfoItem = 0;
477 	if ( qmc2ProjectMESSLookup ) {
478 		qmc2ProjectMESSLookup->setVisible(false);
479 		QLayout *vbl = qmc2MainWindow->tabProjectMESS->layout();
480 		if ( vbl )
481 			delete vbl;
482 		delete qmc2ProjectMESSLookup;
483 		qmc2ProjectMESSLookup = 0;
484 	}
485 	qmc2LastProjectMESSItem = 0;
486 #if defined(QMC2_YOUTUBE_ENABLED)
487 	qmc2LastYouTubeItem = 0;
488 	if ( qmc2YouTubeWidget ) {
489 		qmc2YouTubeWidget->setVisible(false);
490 		QLayout *vbl = qmc2MainWindow->tabYouTube->layout();
491 		if ( vbl )
492 			delete vbl;
493 		delete qmc2YouTubeWidget;
494 		qmc2YouTubeWidget = 0;
495 	}
496 #endif
497 	if ( qmc2EmulatorOptions ) {
498 		qmc2EmulatorOptions->save();
499 		QLayout *vbl = qmc2MainWindow->tabConfiguration->layout();
500 		if ( vbl )
501 			delete vbl;
502 		delete qmc2MainWindow->labelEmuSelector;
503 		if ( qmc2CurrentItem ) {
504 			QString selectedEmulator(qmc2MainWindow->comboBoxEmuSelector->currentText());
505 			if ( selectedEmulator == tr("Default") || selectedEmulator.isEmpty() )
506 				qmc2Config->remove(QString(QMC2_EMULATOR_PREFIX + "Configuration/%1/SelectedEmulator").arg(qmc2CurrentItem->text(QMC2_MACHINELIST_COLUMN_NAME)));
507 			else
508 				qmc2Config->setValue(QString(QMC2_EMULATOR_PREFIX + "Configuration/%1/SelectedEmulator").arg(qmc2CurrentItem->text(QMC2_MACHINELIST_COLUMN_NAME)), selectedEmulator);
509 		}
510 		delete qmc2MainWindow->comboBoxEmuSelector;
511 		qmc2MainWindow->comboBoxEmuSelector = 0;
512 		delete qmc2EmulatorOptions;
513 		delete qmc2MainWindow->pushButtonCurrentEmulatorOptionsExportToFile;
514 		delete qmc2MainWindow->pushButtonCurrentEmulatorOptionsImportFromFile;
515 		qmc2EmulatorOptions = 0;
516 	}
517 	if ( qmc2MainWindow->tabWidgetMachineList->indexOf(qmc2MainWindow->tabMachineList) == qmc2MainWindow->tabWidgetMachineList->currentIndex() ) {
518 		switch ( qmc2MainWindow->stackedWidgetView->currentIndex() ) {
519 			case QMC2_VIEWCATEGORY_INDEX:
520 				QTimer::singleShot(0, qmc2MainWindow, SLOT(viewByCategory()));
521 				break;
522 			case QMC2_VIEWVERSION_INDEX:
523 				QTimer::singleShot(0, qmc2MainWindow, SLOT(viewByVersion()));
524 				break;
525 			default:
526 				break;
527 		}
528 	}
529 	QString execFile(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ExecutableFile").toString());
530 	QFileInfo fi(execFile);
531 	uint cacheTime = qmc2Config->value(QMC2_EMULATOR_PREFIX + "Cache/Time", 0).toUInt();
532 	if ( !qmc2Config->value(QMC2_EMULATOR_PREFIX + "SkipEmuIdent", true).toBool() || fi.lastModified().toTime_t() != cacheTime || cacheTime == 0 ) {
533 		QTime elapsedTime(0, 0, 0, 0);
534 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("determining emulator version and supported sets"));
535 		parseTimer.start();
536 		qmc2Config->setValue(QMC2_EMULATOR_PREFIX + "Cache/Time", fi.lastModified().toTime_t());
537 		QProcess commandProc;
538 		bool started = false, commandProcStarted = false;
539 		int retries = 0;
540 		// emulator version
541 		if ( fi.exists() && fi.isReadable() ) {
542 			commandProc.start(execFile, QStringList() << "-help");
543 			started = commandProc.waitForStarted(QMC2_PROCESS_POLL_TIME);
544 			while ( !started && retries++ < QMC2_PROCESS_POLL_RETRIES ) {
545 				qApp->processEvents();
546 				started = commandProc.waitForStarted(QMC2_PROCESS_POLL_TIME_LONG);
547 			}
548 			if ( started ) {
549 				commandProcStarted = true;
550 				bool commandProcRunning = (commandProc.state() == QProcess::Running);
551 				while ( commandProcRunning && !commandProc.waitForFinished(QMC2_PROCESS_POLL_TIME) ) {
552 					qApp->processEvents();
553 					commandProcRunning = (commandProc.state() == QProcess::Running);
554 				}
555 			} else {
556 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't start MAME executable within a reasonable time frame, giving up") + " (" + tr("error text = %1").arg(ProcessManager::errorText(commandProc.error())) + ")");
557 				qmc2ReloadActive = qmc2EarlyReloadActive = false;
558 				qmc2LoadingInterrupted = true;
559 				QTimer::singleShot(0, this, SLOT(enableWidgets()));
560 				return;
561 			}
562 		} else {
563 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't start %1 executable, file '%2' does not exist").arg(QMC2_EMU_NAME).arg(execFile));
564 			qmc2ReloadActive = qmc2EarlyReloadActive = false;
565 			qmc2LoadingInterrupted = true;
566 			QTimer::singleShot(0, this, SLOT(enableWidgets()));
567 			return;
568 		}
569 		if ( commandProcStarted ) {
570 			QString s(commandProc.readAllStandardOutput());
571 #if defined(QMC2_OS_WIN)
572 			s.replace("\r\n", "\n"); // convert WinDOS's "0x0D 0x0A" to just "0x0A"
573 #endif
574 			QStringList versionLines(s.split('\n'));
575 			QStringList versionWords(versionLines.first().split(' '));
576 			if ( versionWords.count() > 1 ) {
577 				emulatorVersion = versionWords[1].remove('v');
578 				if ( emulatorIdentifiers.contains(versionWords.first()) )
579 					emulatorType = "MAME";
580 				else {
581 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the selected emulator executable cannot be identified as MAME"));
582 					emulatorType = versionWords.first();
583 				}
584 			} else {
585 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the selected emulator executable cannot be identified as MAME"));
586 				emulatorVersion = tr("unknown");
587 				emulatorType = tr("unknown");
588 			}
589 		} else {
590 			emulatorVersion = tr("unknown");
591 			emulatorType = tr("unknown");
592 		}
593 		qmc2Config->setValue(QMC2_EMULATOR_PREFIX + "Cache/Version", emulatorVersion);
594 		qmc2Config->setValue(QMC2_EMULATOR_PREFIX + "Cache/Type", emulatorType);
595 		// supported sets
596 		commandProcStarted = false;
597 		retries = 0;
598 		commandProc.start(execFile, QStringList() << "-listfull");
599 		started = commandProc.waitForStarted(QMC2_PROCESS_POLL_TIME);
600 		while ( !started && retries++ < QMC2_PROCESS_POLL_RETRIES ) {
601 			qApp->processEvents();
602 			started = commandProc.waitForStarted(QMC2_PROCESS_POLL_TIME_LONG);
603 		}
604 		if ( started ) {
605 			commandProcStarted = true;
606 			bool commandProcRunning = (commandProc.state() == QProcess::Running);
607 			while ( commandProcRunning && !commandProc.waitForFinished(QMC2_PROCESS_POLL_TIME) ) {
608 				qApp->processEvents();
609 				commandProcRunning = (commandProc.state() == QProcess::Running);
610 			}
611 		} else {
612 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't start MAME executable within a reasonable time frame, giving up") + " (" + tr("error text = %1").arg(ProcessManager::errorText(commandProc.error())) + ")");
613 			qmc2ReloadActive = qmc2EarlyReloadActive = false;
614 			qmc2LoadingInterrupted = true;
615 			return;
616 		}
617 		QString listfullSha1;
618 		if ( commandProcStarted ) {
619 			QCryptographicHash sha1(QCryptographicHash::Sha1);
620 			QString lfOutput(commandProc.readAllStandardOutput());
621 			numTotalMachines = lfOutput.count('\n') - 1;
622 			sha1.addData(lfOutput.toUtf8().constData());
623 			listfullSha1 = sha1.result().toHex();
624 			elapsedTime = elapsedTime.addMSecs(parseTimer.elapsed());
625 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (determining emulator version and supported sets, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
626 		}
627 		qmc2MainWindow->labelMachineListStatus->setText(status());
628 		if ( emulatorVersion != tr("unknown") )
629 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("emulator info: type = %1, version = %2").arg(emulatorType).arg(emulatorVersion));
630 		else {
631 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: couldn't determine emulator type and version"));
632 			qmc2ReloadActive = false;
633 			QTimer::singleShot(0, this, SLOT(enableWidgets()));
634 			return;
635 		}
636 		if ( numTotalMachines > 0 ) {
637 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("%n supported set(s)", "", numTotalMachines));
638 			qmc2Config->setValue(QMC2_EMULATOR_PREFIX + "Cache/TotalMachines", numTotalMachines);
639 		} else {
640 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: couldn't determine the number of supported sets"));
641 			qmc2Config->remove(QMC2_EMULATOR_PREFIX + "Cache/TotalMachines");
642 			qmc2ReloadActive = false;
643 			QTimer::singleShot(0, this, SLOT(enableWidgets()));
644 			return;
645 		}
646 		if ( qmc2Config->contains(QMC2_EMULATOR_PREFIX + "ListfullSha1") && qmc2Config->value(QMC2_EMULATOR_PREFIX + "ListfullSha1", QString()).toString() != listfullSha1 ) {
647 			if ( !QMC2_CLI_OPT_CLEAR_ALL_CACHES && qmc2Config->value(QMC2_EMULATOR_PREFIX + "AutoClearEmuCaches", true).toBool() ) {
648 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the output from -listfull changed, forcing a refresh of all emulator caches"));
649 				qmc2ForceCacheRefresh = true;
650 				qmc2MainWindow->on_actionClearAllEmulatorCaches_triggered();
651 				qmc2ForceCacheRefresh = false;
652 			}
653 		}
654 		qmc2Config->setValue(QMC2_EMULATOR_PREFIX + "ListfullSha1", listfullSha1);
655 	} else {
656 		emulatorVersion = qmc2Config->value(QMC2_EMULATOR_PREFIX + "Cache/Version", QString()).toString();
657 		emulatorType = qmc2Config->value(QMC2_EMULATOR_PREFIX + "Cache/Type", QString()).toString();
658 		numTotalMachines = qmc2Config->value(QMC2_EMULATOR_PREFIX + "Cache/TotalMachines", 0).toInt();
659 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("emulator info: type = %1, version = %2").arg(emulatorType).arg(emulatorVersion));
660 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("%n supported set(s)", "", numTotalMachines));
661 	}
662 	categoryHash.clear();
663 	versionHash.clear();
664 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCatverIni", false).toBool() ) {
665 		loadCatverIni();
666 		mergeCategories = true;
667 	}
668 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCategoryIni", false).toBool() )
669 		loadCategoryIni();
670 	mergeCategories = false;
671 	if ( qmc2DemoModeDialog )
672 		QTimer::singleShot(0, qmc2DemoModeDialog, SLOT(updateCategoryFilter()));
673 	qmc2EarlyReloadActive = false;
674 	if ( qmc2LoadingInterrupted ) {
675 		mainProgressBar->reset();
676 		qmc2ReloadActive = false;
677 		QTimer::singleShot(0, this, SLOT(enableWidgets()));
678 		return;
679 	}
680 	if ( !initialLoad ) {
681 		// hide machine list / show loading animation
682 		qmc2MainWindow->treeWidgetMachineList->setVisible(false);
683 		((AspectRatioLabel *)qmc2MainWindow->labelLoadingMachineList)->setLabelText(tr("Loading, please wait..."));
684 		qmc2MainWindow->labelLoadingMachineList->setVisible(true);
685 
686 		qmc2MainWindow->treeWidgetHierarchy->setVisible(false);
687 		((AspectRatioLabel *)qmc2MainWindow->labelLoadingHierarchy)->setLabelText(tr("Loading, please wait..."));
688 		qmc2MainWindow->labelLoadingHierarchy->setVisible(true);
689 
690 		qmc2MainWindow->treeWidgetCategoryView->setVisible(false);
691 		((AspectRatioLabel *)qmc2MainWindow->labelCreatingCategoryView)->setLabelText(tr("Loading, please wait..."));
692 		qmc2MainWindow->labelCreatingCategoryView->setVisible(true);
693 
694 		qmc2MainWindow->treeWidgetVersionView->setVisible(false);
695 		((AspectRatioLabel *)qmc2MainWindow->labelCreatingVersionView)->setLabelText(tr("Loading, please wait..."));
696 		qmc2MainWindow->labelCreatingVersionView->setVisible(true);
697 
698 		qmc2MainWindow->attachedViewsWidget->setVisible(false);
699 		((AspectRatioLabel *)qmc2MainWindow->labelLoadingAttachedViews)->setLabelText(tr("Loading, please wait..."));
700 		qmc2MainWindow->labelLoadingAttachedViews->setVisible(true);
701 
702 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
703 			qmc2MainWindow->loadAnimMovie->start();
704 
705 		qApp->processEvents();
706 	}
707 	if ( emulatorVersion == xmlDb()->emulatorVersion() && xmlDb()->xmlRowCount() > 0 ) {
708 		parse();
709 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading favorites and play history"));
710 		loadFavorites();
711 		loadPlayHistory();
712 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading favorites and play history)"));
713 		if ( initialLoad ) {
714 			QTime startupTime(0, 0, 0, 0);
715 			startupTime = startupTime.addMSecs(qmc2StartupTimer.elapsed());
716 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("total start-up time: %1").arg(startupTime.toString("mm:ss.zzz")));
717 			if ( qmc2SplashScreen )
718 				QTimer::singleShot(0, qmc2SplashScreen, SLOT(hide()));
719 			initialLoad = false;
720 		}
721 		// show machine list / hide loading animation
722 		qmc2MainWindow->loadAnimMovie->setPaused(true);
723 		qmc2MainWindow->labelLoadingMachineList->setVisible(false);
724 		qmc2MainWindow->treeWidgetMachineList->setVisible(true);
725 		qmc2MainWindow->labelLoadingHierarchy->setVisible(false);
726 		qmc2MainWindow->treeWidgetHierarchy->setVisible(true);
727 		qmc2MainWindow->labelLoadingAttachedViews->setVisible(false);
728 		qmc2MainWindow->attachedViewsWidget->setVisible(true);
729 		if ( qmc2MainWindow->tabWidgetMachineList->indexOf(qmc2MainWindow->tabMachineList) == qmc2MainWindow->tabWidgetMachineList->currentIndex() ) {
730 			if ( qApp->focusWidget() != qmc2MainWindow->comboBoxToolbarSearch ) {
731 				switch ( qmc2MainWindow->stackedWidgetView->currentIndex() ) {
732 					case QMC2_VIEWHIERARCHY_INDEX:
733 						qmc2MainWindow->treeWidgetHierarchy->setFocus();
734 						break;
735 					case QMC2_VIEWCATEGORY_INDEX:
736 						qmc2MainWindow->treeWidgetCategoryView->setFocus();
737 						break;
738 					case QMC2_VIEWVERSION_INDEX:
739 						qmc2MainWindow->treeWidgetVersionView->setFocus();
740 						break;
741 					case QMC2_VIEWCUSTOM_INDEX:
742 						if ( qmc2MainWindow->attachedViewer() )
743 							qmc2MainWindow->attachedViewer()->treeView->setFocus();
744 						break;
745 					case QMC2_VIEWMACHINELIST_INDEX:
746 					default:
747 						qmc2MainWindow->treeWidgetMachineList->setFocus();
748 						break;
749 				}
750 			}
751 			switch ( qmc2MainWindow->stackedWidgetView->currentIndex() ) {
752 				case QMC2_VIEWHIERARCHY_INDEX:
753 					qmc2MainWindow->treeWidgetHierarchy_verticalScrollChanged();
754 					break;
755 				case QMC2_VIEWCATEGORY_INDEX:
756 					qmc2MainWindow->treeWidgetCategoryView_verticalScrollChanged();
757 					break;
758 				case QMC2_VIEWVERSION_INDEX:
759 					qmc2MainWindow->treeWidgetVersionView_verticalScrollChanged();
760 					break;
761 				case QMC2_VIEWCUSTOM_INDEX:
762 					break;
763 				case QMC2_VIEWMACHINELIST_INDEX:
764 				default:
765 					qmc2MainWindow->treeWidgetMachineList_verticalScrollChanged();
766 					break;
767 			}
768 		}
769 	} else {
770 		loadTimer.start();
771 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading XML data and recreating cache"));
772 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
773 			mainProgressBar->setFormat(tr("XML data - %p%"));
774 		else
775 			mainProgressBar->setFormat("%p%");
776 		uncommittedXmlDbRows = 0;
777 		dtdBufferReady = false;
778 		xmlLineBuffer.clear();
779 		xmlDb()->setLogActive(false);
780 		xmlDb()->recreateDatabase();
781 		xmlDb()->setLogActive(true);
782 		xmlDb()->setEmulatorVersion(emulatorVersion);
783 		xmlDb()->setQmc2Version(XSTR(QMC2_VERSION));
784 		xmlDb()->setXmlCacheVersion(QMC2_XMLCACHE_VERSION);
785 		loadProc = new QProcess(this);
786 		connect(loadProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(loadFinished(int, QProcess::ExitStatus)));
787 		connect(loadProc, SIGNAL(readyReadStandardOutput()), this, SLOT(loadReadyReadStandardOutput()));
788 		connect(loadProc, SIGNAL(started()), this, SLOT(loadStarted()));
789 		loadProc->setProcessChannelMode(QProcess::MergedChannels);
790 		loadProc->start(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ExecutableFile").toString(), QStringList() << "-listxml");
791 	}
792 	userDataDb()->setEmulatorVersion(emulatorVersion);
793 	userDataDb()->setQmc2Version(XSTR(QMC2_VERSION));
794 	userDataDb()->setUserDataVersion(QMC2_USERDATA_VERSION);
795 }
796 
verify(bool currentOnly)797 void MachineList::verify(bool currentOnly)
798 {
799 	if ( currentOnly )
800 		if ( !qmc2CurrentItem )
801 			return;
802 	verifyCurrentOnly = currentOnly;
803 	qmc2VerifyActive = true;
804 	qmc2LoadingInterrupted = false;
805 	QTimer::singleShot(0, this, SLOT(disableWidgets()));
806 	verifiedList.clear();
807 	verifyLastLine.clear();
808 	verifyTimer.start();
809 	numVerifyRoms = 0;
810 	if ( verifyCurrentOnly ) {
811 		checkedItem = qmc2CurrentItem;
812 		romStateCache.setFileName(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ROMStateCacheFile").toString());
813 		romStateCache.open(QIODevice::WriteOnly | QIODevice::Text);
814 		if ( !romStateCache.isOpen() ) {
815 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ERROR: can't open ROM state cache for writing, path = %1").arg(romStateCache.fileName()));
816 			qmc2VerifyActive = false;
817 			QTimer::singleShot(0, this, SLOT(enableWidgets()));
818 			return;
819 		} else {
820 			tsRomCache.setDevice(&romStateCache);
821 			tsRomCache.reset();
822 			tsRomCache << "# THIS FILE IS AUTO-GENERATED - PLEASE DO NOT EDIT!\n";
823 		}
824 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("verifying ROM status for '%1'").arg(checkedItem->text(QMC2_MACHINELIST_COLUMN_MACHINE)));
825 		oldRomState = machineStatusHash.value(checkedItem->text(QMC2_MACHINELIST_COLUMN_NAME));
826 		// decrease counter for current game's/machine's state
827 		switch ( oldRomState ) {
828 			case 'C':
829 				numCorrectMachines--;
830 				numUnknownMachines++;
831 				break;
832 			case 'M':
833 				numMostlyCorrectMachines--;
834 				numUnknownMachines++;
835 				break;
836 			case 'I':
837 				numIncorrectMachines--;
838 				numUnknownMachines++;
839 				break;
840 			case 'N':
841 				numNotFoundMachines--;
842 				numUnknownMachines++;
843 				break;
844 			case 'U':
845 			default:
846 				break;
847 		}
848 	} else {
849 		checkedItem = 0;
850 		romStateCache.setFileName(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ROMStateCacheFile").toString());
851 		romStateCache.open(QIODevice::WriteOnly | QIODevice::Text);
852 		if ( !romStateCache.isOpen() ) {
853 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ERROR: can't open ROM state cache for writing, path = %1").arg(romStateCache.fileName()));
854 			qmc2VerifyActive = false;
855 			QTimer::singleShot(0, this, SLOT(enableWidgets()));
856 			return;
857 		} else {
858 			tsRomCache.setDevice(&romStateCache);
859 			tsRomCache.reset();
860 			tsRomCache << "# THIS FILE IS AUTO-GENERATED - PLEASE DO NOT EDIT!\n";
861 		}
862 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("verifying ROM status for all sets"));
863 		numCorrectMachines = numMostlyCorrectMachines = numIncorrectMachines = numNotFoundMachines = numUnknownMachines = 0;
864 		qmc2MainWindow->labelMachineListStatus->setText(status());
865 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
866 			mainProgressBar->setFormat(tr("ROM check - %p%"));
867 		else
868 			mainProgressBar->setFormat("%p%");
869 		mainProgressBar->setRange(0, numTotalMachines);
870 		mainProgressBar->reset();
871 	}
872 	QStringList args;
873 	QString command(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ExecutableFile").toString());
874 	if ( qmc2Config->contains(QMC2_EMULATOR_PREFIX + "Configuration/Global/rompath") )
875 		args << "-rompath" << QString("%1").arg(qmc2Config->value(QMC2_EMULATOR_PREFIX + "Configuration/Global/rompath").toString().replace("~", "$HOME"));
876 	args << "-verifyroms";
877 	if ( verifyCurrentOnly )
878 		args << checkedItem->text(QMC2_MACHINELIST_COLUMN_NAME);
879 	m_showC = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowCorrect", true).toBool();
880 	m_showM = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowMostlyCorrect", true).toBool();
881 	m_showI = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowIncorrect", true).toBool();
882 	m_showN = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowNotFound", true).toBool();
883 	m_showU = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowUnknown", true).toBool();
884 	m_showDeviceSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowDeviceSets", true).toBool();
885 	m_showBiosSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowBiosSets", true).toBool();
886 	m_doFilter = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/Enabled", true).toBool() && qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/DynamicStateFilter", false).toBool();
887 	verifyProc = new QProcess(this);
888 	connect(verifyProc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(verifyFinished(int, QProcess::ExitStatus)));
889 	connect(verifyProc, SIGNAL(readyReadStandardOutput()), this, SLOT(verifyReadyReadStandardOutput()));
890 	connect(verifyProc, SIGNAL(started()), this, SLOT(verifyStarted()));
891 	if ( !qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/WorkingDirectory", QString()).toString().isEmpty() )
892 		verifyProc->setWorkingDirectory(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/WorkingDirectory").toString());
893 	verifyProc->setProcessChannelMode(QProcess::MergedChannels);
894 	verifyProc->start(command, args, QIODevice::ReadOnly | QIODevice::Text);
895 }
896 
value(QString element,QString attribute,bool translate)897 QString MachineList::value(QString element, QString attribute, bool translate)
898 {
899 	QString attributePattern(" " + attribute + "=\"");
900 	if ( element.contains(attributePattern) ) {
901 		QString valueString(element.remove(0, element.indexOf(attributePattern) + attributePattern.length()));
902 		valueString = valueString.remove(valueString.indexOf("\""), valueString.lastIndexOf(">")).replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace("&apos;", "'");
903 		if ( valueString == ">" )
904 			return QString();
905 		if ( translate )
906 			return tr(valueString.toUtf8().constData());
907 		else
908 			return valueString;
909 	} else
910 		return QString();
911 }
912 
insertAttributeItems(QTreeWidgetItem * parent,QString element,QStringList attributes,QStringList descriptions,bool translate)913 void MachineList::insertAttributeItems(QTreeWidgetItem *parent, QString element, QStringList attributes, QStringList descriptions, bool translate)
914 {
915 	QList<QTreeWidgetItem *> itemList;
916 	for (int i = 0; i < attributes.count(); i++) {
917 		QString valueString(value(element, attributes.at(i), translate));
918 		if ( !valueString.isEmpty() ) {
919 			QTreeWidgetItem *attributeItem = new QTreeWidgetItem();
920 			attributeItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, descriptions.at(i));
921 			attributeItem->setText(QMC2_MACHINELIST_COLUMN_ICON, tr(valueString.toUtf8().constData()));
922 			itemList.append(attributeItem);
923 		}
924 	}
925 	parent->addChildren(itemList);
926 }
927 
insertAttributeItems(QList<QTreeWidgetItem * > * itemList,QString element,QStringList attributes,QStringList descriptions,bool translate)928 void MachineList::insertAttributeItems(QList<QTreeWidgetItem *> *itemList, QString element, QStringList attributes, QStringList descriptions, bool translate)
929 {
930 	for (int i = 0; i < attributes.count(); i++) {
931 		QString valueString(value(element, attributes.at(i), translate));
932 		if ( !valueString.isEmpty() ) {
933 			QTreeWidgetItem *attributeItem = new QTreeWidgetItem();
934 			attributeItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, descriptions.at(i));
935 			attributeItem->setText(QMC2_MACHINELIST_COLUMN_ICON, tr(valueString.toUtf8().constData()));
936 			itemList->append(attributeItem);
937 		}
938 	}
939 }
940 
parseMachineDetail(QTreeWidgetItem * item)941 void MachineList::parseMachineDetail(QTreeWidgetItem *item)
942 {
943 	QString machineName(item->text(QMC2_MACHINELIST_COLUMN_NAME));
944 	QStringList xmlLines(xmlDb()->xml(machineName).split("\n", QString::SkipEmptyParts));
945 	if ( xmlLines.count() < 2 ) {
946 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: couldn't find machine information for '%1'").arg(machineName));
947 		return;
948 	}
949 	int gamePos = 1;
950 	item->child(0)->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Updating"));
951 	qmc2MainWindow->treeWidgetMachineList->viewport()->repaint();
952 	qApp->processEvents();
953 	QString element, content;
954 	QStringList attributes, descriptions;
955 	QTreeWidgetItem *childItem = 0;
956 	QList<QTreeWidgetItem *> itemList;
957 
958 	attributes << "name" << "sourcefile" << "isbios" << "isdevice" << "runnable" << "cloneof" << "romof" << "sampleof";
959 	descriptions << tr("Name") << tr("Source file") << tr("Is BIOS?") << tr("Is device?") << tr("Runnable") << tr("Clone of") << tr("ROM of") << tr("Sample of");
960 	element = xmlLines.at(gamePos - 1).simplified();
961 	insertAttributeItems(&itemList, element, attributes, descriptions, true);
962 	QString endMark("</machine>");
963 
964 	while ( !xmlLines.at(gamePos).contains(endMark) ) {
965 		childItem = 0;
966 		element = xmlLines.at(gamePos).simplified();
967 		if ( element.contains("<year>") ) {
968 			content = element.remove("<year>").remove("</year>");
969 			childItem = new QTreeWidgetItem();
970 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Year"));
971 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, content);
972 		}
973 		if ( element.contains("<manufacturer>") ) {
974 			content = element.remove("<manufacturer>").remove("</manufacturer>");
975 			content.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace("&apos;", "'");
976 			childItem = new QTreeWidgetItem();
977 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Manufacturer"));
978 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, content);
979 		}
980 		if ( element.contains("<rom ") ) {
981 			childItem = new QTreeWidgetItem();
982 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("ROM"));
983 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
984 			attributes.clear();
985 			attributes << "bios" << "size" << "crc" << "sha1" << "merge" << "region" << "offset" << "status" << "optional";
986 			descriptions.clear();
987 			descriptions << tr("BIOS") << tr("Size") << tr("CRC") << tr("SHA-1") << tr("Merge") << tr("Region") << tr("Offset") << tr("Status") << tr("Optional");
988 			insertAttributeItems(childItem, element, attributes, descriptions, true);
989 		}
990 		if ( element.contains("<device_ref ") ) {
991 			childItem = new QTreeWidgetItem();
992 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Device reference"));
993 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
994 		}
995 		if ( element.contains("<chip ") ) {
996 			childItem = new QTreeWidgetItem();
997 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Chip"));
998 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
999 			attributes.clear();
1000 			attributes << "tag" << "type" << "clock";
1001 			descriptions.clear();
1002 			descriptions << tr("Tag") << tr("Type") << tr("Clock");
1003 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1004 		}
1005 		if ( element.contains("<display ") ) {
1006 			childItem = new QTreeWidgetItem();
1007 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Display"));
1008 			attributes.clear();
1009 			attributes << "type" << "rotate" << "flipx" << "width" << "height" << "refresh" << "pixclock" << "htotal" << "hbend" << "hbstart" << "vtotal" << "vbend" << "vbstart";
1010 			descriptions.clear();
1011 			descriptions << tr("Type") << tr("Rotate") << tr("Flip-X") << tr("Width") << tr("Height") << tr("Refresh") << tr("Pixel clock") << tr("H-Total") << tr("H-Bend") << tr("HB-Start") << tr("V-Total") << tr("V-Bend") << tr("VB-Start");
1012 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1013 		}
1014 		if ( element.contains("<sound ") ) {
1015 			childItem = new QTreeWidgetItem();
1016 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Sound"));
1017 			attributes.clear();
1018 			attributes << "channels";
1019 			descriptions.clear();
1020 			descriptions << tr("Channels");
1021 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1022 		}
1023 		if ( element.contains("<input ") ) {
1024 			childItem = new QTreeWidgetItem();
1025 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Input"));
1026 			attributes.clear();
1027 			attributes << "service" << "tilt" << "players" << "buttons" << "coins";
1028 			descriptions.clear();
1029 			descriptions << tr("Service") << tr("Tilt") << tr("Players") << tr("Buttons") << tr("Coins");
1030 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1031 			gamePos++;
1032 			while ( xmlLines.at(gamePos).contains("<control ") ) {
1033 				QString subElement(xmlLines.at(gamePos).simplified());
1034 				QTreeWidgetItem *nextChildItem = new QTreeWidgetItem(childItem);
1035 				nextChildItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Control"));
1036 				nextChildItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(subElement, "type", true));
1037 				attributes.clear();
1038 				attributes << "minimum" << "maximum" << "sensitivity" << "keydelta" << "reverse" << "player" << "buttons" << "ways";
1039 				descriptions.clear();
1040 				descriptions << tr("Minimum") << tr("Maximum") << tr("Sensitivity") << tr("Key Delta") << tr("Reverse") << tr("Player") << tr("Buttons") << tr("Ways");
1041 				insertAttributeItems(nextChildItem, subElement, attributes, descriptions, true);
1042 				gamePos++;
1043 			}
1044 		}
1045 		if ( element.contains("<dipswitch ") ) {
1046 			childItem = new QTreeWidgetItem();
1047 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("DIP switch"));
1048 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name", true));
1049 			gamePos++;
1050 			while ( xmlLines.at(gamePos).contains("<dipvalue ") ) {
1051 				QString subElement(xmlLines.at(gamePos).simplified());
1052 				QTreeWidgetItem *secondChildItem = new QTreeWidgetItem(childItem);
1053 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("DIP value"));
1054 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(subElement, "name", true));
1055 				attributes.clear();
1056 				attributes << "default";
1057 				descriptions.clear();
1058 				descriptions << tr("Default");
1059 				insertAttributeItems(secondChildItem, subElement, attributes, descriptions, true);
1060 				gamePos++;
1061 			}
1062 		}
1063 		if ( element.contains("<configuration ") ) {
1064 			childItem = new QTreeWidgetItem();
1065 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Configuration"));
1066 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name", true));
1067 			attributes.clear();
1068 			attributes << "tag" << "mask";
1069 			descriptions.clear();
1070 			descriptions << tr("Tag") << tr("Mask");
1071 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1072 			gamePos++;
1073 			while ( xmlLines.at(gamePos).contains("<confsetting ") ) {
1074 				QString subElement(xmlLines.at(gamePos).simplified());
1075 				QTreeWidgetItem *secondChildItem = new QTreeWidgetItem(childItem);
1076 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Setting"));
1077 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(subElement, "name", true));
1078 				attributes.clear();
1079 				attributes << "value" << "default";
1080 				descriptions.clear();
1081 				descriptions << tr("Value") << tr("Default");
1082 				insertAttributeItems(secondChildItem, subElement, attributes, descriptions, true);
1083 				gamePos++;
1084 			}
1085 		}
1086 		if ( element.contains("<driver ") ) {
1087 			childItem = new QTreeWidgetItem();
1088 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Driver"));
1089 			attributes.clear();
1090 			attributes << "status" << "emulation" << "color" << "sound" << "graphic" << "cocktail" << "protection" << "savestate" << "palettesize";
1091 			descriptions.clear();
1092 			descriptions << tr("Status") << tr("Emulation") << tr("Color") << tr("Sound") << tr("Graphic") << tr("Cocktail") << tr("Protection") << tr("Save state") << tr("Palette size");
1093 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1094 		}
1095 		if ( element.contains("<biosset ") ) {
1096 			childItem = new QTreeWidgetItem();
1097 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("BIOS set"));
1098 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
1099 			attributes.clear();
1100 			attributes << "description" << "default";
1101 			descriptions.clear();
1102 			descriptions << tr("Description") << tr("Default");
1103 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1104 		}
1105 		if ( element.contains("<sample ") ) {
1106 			childItem = new QTreeWidgetItem();
1107 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Sample"));
1108 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
1109 		}
1110 		if ( element.contains("<disk ") ) {
1111 			childItem = new QTreeWidgetItem();
1112 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Disk"));
1113 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
1114 			attributes.clear();
1115 			attributes << "md5" << "sha1" << "merge" << "region" << "index" << "status" << "optional";
1116 			descriptions.clear();
1117 			descriptions << tr("MD5") << tr("SHA-1") << tr("Merge") << tr("Region") << tr("Index") << tr("Status") << tr("Optional");
1118 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1119 		}
1120 		if ( element.contains("<adjuster ") ) {
1121 			childItem = new QTreeWidgetItem();
1122 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Adjuster"));
1123 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
1124 			attributes.clear();
1125 			attributes << "default";
1126 			descriptions.clear();
1127 			descriptions << tr("Default");
1128 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1129 		}
1130 		if ( element.contains("<softwarelist ") ) {
1131 			childItem = new QTreeWidgetItem();
1132 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Software list"));
1133 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name"));
1134 			attributes.clear();
1135 			attributes << "status";
1136 			descriptions.clear();
1137 			descriptions << tr("Status");
1138 			insertAttributeItems(childItem, element, attributes, descriptions, true);
1139 		}
1140 		if ( element.contains("<category ") ) {
1141 			childItem = new QTreeWidgetItem();
1142 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Category"));
1143 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "name", true));
1144 			gamePos++;
1145 			while ( xmlLines.at(gamePos).contains("<item ") ) {
1146 				QString subElement(xmlLines.at(gamePos).simplified());
1147 				QTreeWidgetItem *secondChildItem = new QTreeWidgetItem(childItem);
1148 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Item"));
1149 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(subElement, "name", true));
1150 				attributes.clear();
1151 				attributes << "default";
1152 				descriptions.clear();
1153 				descriptions << tr("Default");
1154 				insertAttributeItems(secondChildItem, subElement, attributes, descriptions, true);
1155 				gamePos++;
1156 			}
1157 		}
1158 		if ( element.contains("<device ") ) {
1159 			childItem = new QTreeWidgetItem();
1160 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Device"));
1161 			childItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(element, "type", true));
1162 			attributes.clear();
1163 			attributes << "tag" << "mandatory" << "interface";
1164 			descriptions.clear();
1165 			descriptions << tr("Tag") << tr("Mandatory") << tr("Interface");
1166 			insertAttributeItems(childItem, element, attributes, descriptions, false);
1167 			gamePos++;
1168 			while ( xmlLines.at(gamePos).contains("<instance ") ) {
1169 				QString subElement(xmlLines.at(gamePos).simplified());
1170 				QTreeWidgetItem *secondChildItem = new QTreeWidgetItem(childItem);
1171 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Instance"));
1172 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(subElement, "name", false));
1173 				attributes.clear();
1174 				attributes << "briefname";
1175 				descriptions.clear();
1176 				descriptions << tr("Brief name");
1177 				insertAttributeItems(secondChildItem, element, attributes, descriptions, false);
1178 				gamePos++;
1179 			}
1180 			while ( xmlLines.at(gamePos).contains("<extension ") ) {
1181 				QString subElement(xmlLines.at(gamePos).simplified());
1182 				QTreeWidgetItem *secondChildItem = new QTreeWidgetItem(childItem);
1183 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Extension"));
1184 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_ICON, value(subElement, "name", false));
1185 				gamePos++;
1186 			}
1187 		}
1188 		if ( element.contains("<ramoption") ) {
1189 			childItem = new QTreeWidgetItem();
1190 			childItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("RAM options"));
1191 			while ( xmlLines.at(gamePos).contains("<ramoption") ) {
1192 				QString subElement(xmlLines.at(gamePos).simplified());
1193 				QTreeWidgetItem *secondChildItem = new QTreeWidgetItem(childItem);
1194 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, tr("Option"));
1195 				int fromIndex = subElement.indexOf('>') + 1;
1196 				int toIndex = subElement.indexOf('<', fromIndex);
1197 				QString ramOptionValue(subElement.mid(fromIndex, toIndex - fromIndex));
1198 				secondChildItem->setText(QMC2_MACHINELIST_COLUMN_ICON, ramOptionValue);
1199 				attributes.clear();
1200 				attributes << "default";
1201 				descriptions.clear();
1202 				descriptions << tr("Default");
1203 				insertAttributeItems(secondChildItem, subElement, attributes, descriptions, false);
1204 				gamePos++;
1205 			}
1206 			if ( xmlLines.at(gamePos).contains(endMark) )
1207 				gamePos--;
1208 		}
1209 		gamePos++;
1210 		if ( childItem )
1211 			itemList.append(childItem);
1212 	}
1213 	qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(false);
1214 	delete item->takeChild(0);
1215 	item->addChildren(itemList);
1216 	qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(true);
1217 }
1218 
parse()1219 void MachineList::parse()
1220 {
1221 	if ( qmc2LoadingInterrupted ) {
1222 		qmc2ReloadActive = false;
1223 		QTimer::singleShot(0, this, SLOT(enableWidgets()));
1224 		return;
1225 	}
1226 	QWidget *fW = qApp->focusWidget();
1227 	foreach (MachineListViewer *v, MainWindow::machineListViewers)
1228 		v->setEnabled(false);
1229 	bool showROMStatusIcons = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowROMStatusIcons", true).toBool();
1230 	bool showDeviceSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowDeviceSets", true).toBool();
1231 	bool showBiosSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowBiosSets", true).toBool();
1232 	mainProgressBar->setRange(0, numTotalMachines);
1233 	romStateCache.setFileName(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/ROMStateCacheFile").toString());
1234 	if ( romStateCache.open(QIODevice::ReadOnly | QIODevice::Text) ) {
1235 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading ROM state from cache"));
1236 		parseTimer.start();
1237 		tsRomCache.setDevice(&romStateCache);
1238 		tsRomCache.reset();
1239 		tsRomCache.readLine(); // ignore the first line
1240 		QChar splitChar(' ');
1241 		while ( !tsRomCache.atEnd() ) {
1242 			QStringList words(tsRomCache.readLine().split(splitChar, QString::SkipEmptyParts));
1243 			machineStatusHash.insert(words.at(QMC2_RSC_INDEX_NAME), words.at(QMC2_RSC_INDEX_STATE).at(0).toLatin1());
1244 		}
1245 		numCorrectMachines = numMostlyCorrectMachines = numIncorrectMachines = numNotFoundMachines = 0;
1246 		QTime elapsedTime(0, 0, 0, 0);
1247 		elapsedTime = elapsedTime.addMSecs(parseTimer.elapsed());
1248 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading ROM state from cache, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
1249 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("%n cached ROM state(s) loaded", "", machineStatusHash.count()));
1250 		romStateCache.close();
1251 	} else if ( !autoRomCheck )
1252 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: can't open ROM state cache, please check ROMs"));
1253 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("processing machine list"));
1254 	parseTimer.start();
1255 	qmc2MainWindow->treeWidgetMachineList->clear();
1256 	qmc2ParentHash.clear();
1257 	hierarchyHash.clear();
1258 	mainProgressBar->reset();
1259 	QList<QTreeWidgetItem *> itemList;
1260 	QHash<QTreeWidgetItem *, bool> hiddenItemHash;
1261 	bool reparseMachineList = true, romStateCacheUpdate = false, loadedFromCache = false;
1262 	QString trSystemBios(tr("System / BIOS")), trSystemDevice(tr("System / Device"));
1263 	QChar columnSplitChar('\t'), lineSplitChar('\n');
1264 	machineListCache.setFileName(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/MachineListCacheFile").toString());
1265 	if ( machineListCache.open(QIODevice::ReadOnly | QIODevice::Text) ) {
1266 		tsMachineListCache.setDevice(&machineListCache);
1267 		tsMachineListCache.setCodec(QTextCodec::codecForName("UTF-8"));
1268 		tsMachineListCache.seek(0);
1269 		if ( !tsMachineListCache.atEnd() ) {
1270 			QString line(tsMachineListCache.readLine());
1271 			while ( line.startsWith('#') && !tsMachineListCache.atEnd() )
1272 				line = tsMachineListCache.readLine();
1273 			QStringList words(line.split(columnSplitChar));
1274 			if ( words.count() > 1 ) {
1275 				if ( words.at(0).compare("MAME_VERSION") == 0 )
1276 					romStateCacheUpdate = reparseMachineList = (words.at(1).compare(emulatorVersion) != 0);
1277 				else
1278 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: couldn't determine emulator version of machine list cache"));
1279 				if ( words.count() < 4 ) {
1280 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("INFORMATION: the machine list cache will now be updated due to a new format"));
1281 					reparseMachineList = true;
1282 				} else {
1283 					if ( words.at(3).toInt() < QMC2_MLC_VERSION ) {
1284 						qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("INFORMATION: the machine list cache will now be updated due to a new format"));
1285 						reparseMachineList = true;
1286 					}
1287 				}
1288 			}
1289 		} else {
1290 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the machine list cache is invalid, forcing a refresh"));
1291 			reparseMachineList = true;
1292 		}
1293 		if ( machineListDb()->isEmpty() ) {
1294 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: the machine list database is invalid, forcing a refresh"));
1295 			reparseMachineList = true;
1296 		}
1297 		bool useCatverIni = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCatverIni", false).toBool();
1298 		bool useCategories = useCatverIni | qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCategoryIni", false).toBool();
1299 		if ( !reparseMachineList && !qmc2LoadingInterrupted ) {
1300 			loadIcon(QString(), 0); // initiates icon pre-caching
1301 			mainProgressBar->setValue(0);
1302 			mainProgressBar->setRange(0, numTotalMachines * 2);
1303 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading machine data from machine list cache"));
1304 			if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
1305 				mainProgressBar->setFormat(tr("Machine data - %p%"));
1306 			else
1307 				mainProgressBar->setFormat("%p%");
1308 			miscTimer.start();
1309 			numMachines = numUnknownMachines = 0;
1310 			QString readBuffer;
1311 			QChar one('1');
1312 			while ( (!tsMachineListCache.atEnd() || !readBuffer.isEmpty()) && !qmc2LoadingInterrupted ) {
1313 				readBuffer.append(tsMachineListCache.read(QMC2_FILE_BUFFER_SIZE));
1314 				bool endsWithNewLine = readBuffer.endsWith(lineSplitChar);
1315 				QStringList lines(readBuffer.split(lineSplitChar, QString::SkipEmptyParts));
1316 				int lc = endsWithNewLine ? lines.count() : lines.count() - 1;
1317 				for (int l = 0; l < lc; l++) {
1318 					QStringList machineData(lines.at(l).split(columnSplitChar));
1319 					QString machineName(machineData.at(QMC2_MLC_INDEX_NAME));
1320 					QString machineCloneOf(machineData.at(QMC2_MLC_INDEX_CLONEOF));
1321 					QString machinePlayers(machineData.at(QMC2_MLC_INDEX_PLAYERS));
1322 					QString machineDrvStat(machineData.at(QMC2_MLC_INDEX_DRVSTAT));
1323 					int machineType = int(machineData.at(QMC2_MLC_INDEX_IS_BIOS).compare(one) == 0) + int(machineData.at(QMC2_MLC_INDEX_IS_DEVICE).compare(one) == 0) * 2; // 0: normal, 1: BIOS, 2: device
1324 					if ( machineCloneOf.isEmpty() ) {
1325 						if ( !hierarchyHash.contains(machineName) )
1326 							hierarchyHash.insert(machineName, QStringList());
1327 					} else
1328 						hierarchyHash[machineCloneOf].append(machineName);
1329 					MachineListItem *machineItem = new MachineListItem();
1330 					qmc2MachineListItemHash.insert(machineName, machineItem);
1331 					machineItem->setFlags(MachineListItem::defaultItemFlags);
1332 					machineItem->setCheckState(QMC2_MACHINELIST_COLUMN_TAG, Qt::Unchecked);
1333 					machineItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, machineData.at(QMC2_MLC_INDEX_MACHINE));
1334 					machineItem->setText(QMC2_MACHINELIST_COLUMN_YEAR, machineData.at(QMC2_MLC_INDEX_YEAR));
1335 					machineItem->setText(QMC2_MACHINELIST_COLUMN_MANU, machineData.at(QMC2_MLC_INDEX_MANU));
1336 					machineItem->setText(QMC2_MACHINELIST_COLUMN_NAME, machineName);
1337 					machineItem->setText(QMC2_MACHINELIST_COLUMN_SRCFILE, machineData.at(QMC2_MLC_INDEX_SRCFILE));
1338 					machineItem->setText(QMC2_MACHINELIST_COLUMN_RTYPES, romTypeNames.at(int(machineData.at(QMC2_MLC_INDEX_HAS_ROM).compare(one) == 0) + int(machineData.at(QMC2_MLC_INDEX_HAS_CHD).compare(one) == 0) * 2));
1339 					if ( useCatverIni ) {
1340 						QString *versionString = versionHash.value(machineName);
1341 						machineItem->setText(QMC2_MACHINELIST_COLUMN_VERSION, versionString ? *versionString : trQuestionMark);
1342 					}
1343 					switch ( machineStatusHash.value(machineName) ) {
1344 						case 'C':
1345 							numCorrectMachines++;
1346 							switch ( machineType ) {
1347 								case QMC2_MACHINETYPE_NORMAL:
1348 									if ( useCategories ) {
1349 										QString *categoryString = categoryHash.value(machineName);
1350 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, categoryString ? *categoryString : trQuestionMark);
1351 									}
1352 									if ( showROMStatusIcons )
1353 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
1354 									break;
1355 								case QMC2_MACHINETYPE_BIOS:
1356 									if ( showROMStatusIcons )
1357 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
1358 									if ( !showBiosSets )
1359 										hiddenItemHash.insert(machineItem, true);
1360 									if ( useCategories )
1361 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemBios);
1362 									biosSets.insert(machineName, true);
1363 									break;
1364 								case QMC2_MACHINETYPE_DEVICE:
1365 									if ( showROMStatusIcons )
1366 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
1367 									if ( !showDeviceSets )
1368 										hiddenItemHash.insert(machineItem, true);
1369 									if ( useCategories )
1370 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemDevice);
1371 									deviceSets.insert(machineName, true);
1372 									machinePlayers = machineDrvStat = "N/A";
1373 									break;
1374 							}
1375 							break;
1376 						case 'M':
1377 							numMostlyCorrectMachines++;
1378 							switch ( machineType ) {
1379 								case QMC2_MACHINETYPE_NORMAL:
1380 									if ( useCategories ) {
1381 										QString *categoryString = categoryHash.value(machineName);
1382 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, categoryString ? *categoryString : trQuestionMark);
1383 									}
1384 									if ( showROMStatusIcons )
1385 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
1386 									break;
1387 								case QMC2_MACHINETYPE_BIOS:
1388 									if ( showROMStatusIcons )
1389 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
1390 									if ( !showBiosSets )
1391 										hiddenItemHash.insert(machineItem, true);
1392 									if ( useCategories )
1393 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemBios);
1394 									biosSets.insert(machineName, true);
1395 									break;
1396 								case QMC2_MACHINETYPE_DEVICE:
1397 									if ( showROMStatusIcons )
1398 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
1399 									if ( !showDeviceSets )
1400 										hiddenItemHash.insert(machineItem, true);
1401 									if ( useCategories )
1402 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemDevice);
1403 									deviceSets.insert(machineName, true);
1404 									machinePlayers = machineDrvStat = "N/A";
1405 									break;
1406 							}
1407 							break;
1408 						case 'I':
1409 							numIncorrectMachines++;
1410 							switch ( machineType ) {
1411 								case QMC2_MACHINETYPE_NORMAL:
1412 									if ( useCategories ) {
1413 										QString *categoryString = categoryHash.value(machineName);
1414 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, categoryString ? *categoryString : trQuestionMark);
1415 									}
1416 									if ( showROMStatusIcons )
1417 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
1418 									break;
1419 								case QMC2_MACHINETYPE_BIOS:
1420 									if ( showROMStatusIcons )
1421 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
1422 									if ( !showBiosSets )
1423 										hiddenItemHash.insert(machineItem, true);
1424 									if ( useCategories )
1425 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemBios);
1426 									biosSets.insert(machineName, true);
1427 									break;
1428 								case QMC2_MACHINETYPE_DEVICE:
1429 									if ( showROMStatusIcons )
1430 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
1431 									if ( !showDeviceSets )
1432 										hiddenItemHash.insert(machineItem, true);
1433 									if ( useCategories )
1434 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemDevice);
1435 									deviceSets.insert(machineName, true);
1436 									machinePlayers = machineDrvStat = "N/A";
1437 									break;
1438 							}
1439 							break;
1440 						case 'N':
1441 							numNotFoundMachines++;
1442 							switch ( machineType ) {
1443 								case QMC2_MACHINETYPE_NORMAL:
1444 									if ( useCategories ) {
1445 										QString *categoryString = categoryHash.value(machineName);
1446 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, categoryString ? *categoryString : trQuestionMark);
1447 									}
1448 									if ( showROMStatusIcons )
1449 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
1450 									break;
1451 								case QMC2_MACHINETYPE_BIOS:
1452 									if ( showROMStatusIcons )
1453 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
1454 									if ( !showBiosSets )
1455 										hiddenItemHash.insert(machineItem, true);
1456 									if ( useCategories )
1457 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemBios);
1458 									biosSets.insert(machineName, true);
1459 									break;
1460 								case QMC2_MACHINETYPE_DEVICE:
1461 									if ( showROMStatusIcons )
1462 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
1463 									if ( !showDeviceSets )
1464 										hiddenItemHash.insert(machineItem, true);
1465 									if ( useCategories )
1466 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemDevice);
1467 									deviceSets.insert(machineName, true);
1468 									machinePlayers = machineDrvStat = "N/A";
1469 									break;
1470 							}
1471 							break;
1472 						default:
1473 							numUnknownMachines++;
1474 							machineStatusHash.insert(machineName, 'U');
1475 							switch ( machineType ) {
1476 								case QMC2_MACHINETYPE_NORMAL:
1477 									if ( useCategories ) {
1478 										QString *categoryString = categoryHash.value(machineName);
1479 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, categoryString ? *categoryString : trQuestionMark);
1480 									}
1481 									if ( showROMStatusIcons )
1482 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
1483 									break;
1484 								case QMC2_MACHINETYPE_BIOS:
1485 									if ( showROMStatusIcons )
1486 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
1487 									if ( !showBiosSets )
1488 										hiddenItemHash.insert(machineItem, true);
1489 									if ( useCategories )
1490 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemBios);
1491 									biosSets.insert(machineName, true);
1492 									break;
1493 								case QMC2_MACHINETYPE_DEVICE:
1494 									if ( showROMStatusIcons )
1495 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
1496 									if ( !showDeviceSets )
1497 										hiddenItemHash.insert(machineItem, true);
1498 									if ( useCategories )
1499 										machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemDevice);
1500 									deviceSets.insert(machineName, true);
1501 									machinePlayers = machineDrvStat = "N/A";
1502 									break;
1503 							}
1504 							break;
1505 					}
1506 					machineItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, machinePlayers);
1507 					machineItem->setText(QMC2_MACHINELIST_COLUMN_DRVSTAT, machineStateTranslations.value(machineDrvStat));
1508 					QTreeWidgetItem *nameItem = new QTreeWidgetItem(machineItem);
1509 					nameItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, trWaitingForData);
1510 					loadIcon(machineName, machineItem);
1511 					itemList.append(machineItem);
1512 					if ( numMachines++ % qmc2MachineListResponsiveness == 0 ) {
1513 						mainProgressBar->setValue(numMachines);
1514 						qmc2MainWindow->labelMachineListStatus->setText(status());
1515 					}
1516 				}
1517 				if ( endsWithNewLine )
1518 					readBuffer.clear();
1519 				else
1520 					readBuffer = lines.last();
1521 			}
1522 			mainProgressBar->setValue(numMachines);
1523 			loadedFromCache = true;
1524 		}
1525 		machineListCache.close();
1526 	}
1527 	if ( reparseMachineList && !qmc2LoadingInterrupted ) {
1528 		loadIcon(QString(), 0); // initiates icon pre-caching
1529 		mainProgressBar->setRange(0, numTotalMachines * 2);
1530 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("parsing machine data and recreating machine list cache"));
1531 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
1532 			mainProgressBar->setFormat(tr("Machine data - %p%"));
1533 		else
1534 			mainProgressBar->setFormat("%p%");
1535 		if ( machineListCache.open(QIODevice::WriteOnly | QIODevice::Text) ) {
1536 			machineListDb()->recreateDatabase();
1537 			machineListDb()->setEmulatorVersion(emulatorVersion);
1538 			machineListDb()->setQmc2Version(XSTR(QMC2_VERSION));
1539 			machineListDb()->setMachineListVersion(QMC2_MACHINELIST_DB_VERSION);
1540 			tsMachineListCache.setDevice(&machineListCache);
1541 			tsMachineListCache.setCodec(QTextCodec::codecForName("UTF-8"));
1542 			tsMachineListCache.reset();
1543 			tsMachineListCache << "# THIS FILE IS AUTO-GENERATED - PLEASE DO NOT EDIT!\n";
1544 			tsMachineListCache << "MAME_VERSION\t" + emulatorVersion + "\tMLC_VERSION\t" + QString::number(QMC2_MLC_VERSION) + "\n";
1545 			bool useCatverIni = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCatverIni", false).toBool();
1546 			bool useCategories = useCatverIni | qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCategoryIni", false).toBool();;
1547 			// parse XML data
1548 			numMachines = numUnknownMachines = 0;
1549 			qint64 xmlRowCount = xmlDb()->xmlRowCount();
1550 			machineListDb()->beginTransaction();
1551 			int pendingDbUpdates = 0;
1552 			for (qint64 rowCounter = 1; rowCounter <= xmlRowCount && !qmc2LoadingInterrupted; rowCounter++) {
1553 				QStringList xmlLines(xmlDb()->xml(rowCounter).split(lineSplitChar, QString::SkipEmptyParts));
1554 				for (int lineCounter = 0; lineCounter < xmlLines.count() && !qmc2LoadingInterrupted; lineCounter++) {
1555 					while ( lineCounter < xmlLines.count() && !xmlLines.at(lineCounter).contains("<description>") )
1556 						lineCounter++;
1557 					if ( !qmc2LoadingInterrupted && lineCounter < xmlLines.count() ) {
1558 						QString machineElement(xmlLines.at(lineCounter - 1).simplified());
1559 						if ( !machineElement.contains(" name=\"") )
1560 							continue;
1561 						bool isBIOS = value(machineElement, "isbios").compare("yes") == 0;
1562 						bool isDev = value(machineElement, "isdevice").compare("yes") == 0;
1563 						QString machineName(value(machineElement, "name"));
1564 						if ( machineName.isEmpty() ) {
1565 							qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: name attribute empty on XML line %1 (set will be ignored!) -- please inform MAME developers and include the offending output from -listxml").arg(lineCounter + 2));
1566 							qApp->processEvents();
1567 							continue;
1568 						}
1569 						QString machineSource(value(machineElement, "sourcefile"));
1570 						QString machineCloneOf(value(machineElement, "cloneof"));
1571 						QString descriptionElement(xmlLines.at(lineCounter).simplified());
1572 						QString machineDescription(descriptionElement.remove("<description>").remove("</description>").replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace("&apos;", "'"));
1573 						MachineListItem *machineItem = new MachineListItem();
1574 						qmc2MachineListItemHash.insert(machineName, machineItem);
1575 						machineItem->setFlags(MachineListItem::defaultItemFlags);
1576 						machineItem->setCheckState(QMC2_MACHINELIST_COLUMN_TAG, Qt::Unchecked);
1577 						if ( (isBIOS && !showBiosSets) || (isDev && !showDeviceSets) )
1578 							hiddenItemHash.insert(machineItem, true);
1579 						// find year & manufacturer and determine ROM/CHD requirements
1580 						bool endMachine = false;
1581 						int i = lineCounter;
1582 						QString machineYear(trQuestionMark), machineManufacturer(trQuestionMark), machinePlayers(trQuestionMark), machineDrvStat(trQuestionMark);
1583 						bool yearFound = false, manufacturerFound = false, hasROMs = false, hasCHDs = false, playersFound = false, statusFound = false;
1584 						QString endMark("</machine>");
1585 						while ( !endMachine ) {
1586 							QString xmlLine(xmlLines.at(i));
1587 							if ( xmlLine.contains("<year>") ) {
1588 								machineYear = xmlLine.simplified().remove("<year>").remove("</year>");
1589 								yearFound = true;
1590 							} else if ( xmlLine.contains("<manufacturer>") ) {
1591 								machineManufacturer = xmlLine.simplified().remove("<manufacturer>").remove("</manufacturer>").replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace("&apos;", "'");
1592 								manufacturerFound = true;
1593 							} else if ( xmlLine.contains("<rom name") ) {
1594 								hasROMs = true;
1595 							} else if ( xmlLine.contains("<disk name") ) {
1596 								hasCHDs = true;
1597 							} else if ( xmlLine.contains("<input players") ) {
1598 								int playersPos = xmlLine.indexOf("input players=\"") + 15;
1599 								if ( playersPos >= 0 ) {
1600 									machinePlayers = xmlLine.mid(playersPos, xmlLine.indexOf("\"", playersPos) - playersPos);
1601 									playersFound = true;
1602 								}
1603 							} else if ( xmlLine.contains("<driver status") ) {
1604 								int statusPos = xmlLine.indexOf("driver status=\"") + 15;
1605 								if ( statusPos >= 0 ) {
1606 									machineDrvStat = xmlLine.mid(statusPos, xmlLine.indexOf("\"", statusPos) - statusPos);
1607 									statusFound = true;
1608 								}
1609 							}
1610 							endMachine = xmlLine.contains(endMark) || (yearFound && manufacturerFound && hasROMs && hasCHDs && playersFound && statusFound);
1611 							i++;
1612 						}
1613 						if ( machineCloneOf.isEmpty() ) {
1614 							if ( !hierarchyHash.contains(machineName) )
1615 								hierarchyHash.insert(machineName, QStringList());
1616 						} else
1617 							hierarchyHash[machineCloneOf].append(machineName);
1618 						machineItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, machineDescription);
1619 						machineItem->setText(QMC2_MACHINELIST_COLUMN_YEAR, machineYear);
1620 						machineItem->setText(QMC2_MACHINELIST_COLUMN_MANU, machineManufacturer);
1621 						machineItem->setText(QMC2_MACHINELIST_COLUMN_NAME, machineName);
1622 						machineItem->setText(QMC2_MACHINELIST_COLUMN_SRCFILE, machineSource);
1623 						machineItem->setText(QMC2_MACHINELIST_COLUMN_RTYPES, romTypeNames.at(int(hasROMs) + int(hasCHDs) * 2));
1624 						if ( isDev ) {
1625 							if ( machinePlayers.compare(trQuestionMark) != 0 )
1626 								machineItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, machinePlayers);
1627 							else
1628 								machineItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, tr("N/A"));
1629 							machineItem->setText(QMC2_MACHINELIST_COLUMN_DRVSTAT, tr("N/A"));
1630 						} else {
1631 							machineItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, machinePlayers);
1632 							machineItem->setText(QMC2_MACHINELIST_COLUMN_DRVSTAT, machineStateTranslations.value(machineDrvStat));
1633 						}
1634 						if ( useCategories ) {
1635 							if ( isBIOS )
1636 								machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemBios);
1637 							else if ( isDev )
1638 								machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, trSystemDevice);
1639 							else {
1640 								QString *categoryString = categoryHash.value(machineName);
1641 								machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, categoryString ? *categoryString : trQuestionMark);
1642 							}
1643 						}
1644 						if ( useCatverIni ) {
1645 							QString *versionString = versionHash.value(machineName);
1646 							machineItem->setText(QMC2_MACHINELIST_COLUMN_VERSION, versionString ? *versionString : trQuestionMark);
1647 						}
1648 						switch ( machineStatusHash.value(machineName) ) {
1649 							case 'C':
1650 								numCorrectMachines++;
1651 								if ( isBIOS ) {
1652 									if ( showROMStatusIcons )
1653 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
1654 									biosSets.insert(machineName, true);
1655 								} else if ( isDev ) {
1656 									if ( showROMStatusIcons )
1657 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
1658 									deviceSets.insert(machineName, true);
1659 								} else if ( showROMStatusIcons )
1660 									machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
1661 								break;
1662 
1663 							case 'M':
1664 								numMostlyCorrectMachines++;
1665 								if ( isBIOS ) {
1666 									if ( showROMStatusIcons )
1667 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
1668 									biosSets.insert(machineName, true);
1669 								} else if ( isDev ) {
1670 									if ( showROMStatusIcons )
1671 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
1672 									deviceSets.insert(machineName, true);
1673 								} else if ( showROMStatusIcons )
1674 									machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
1675 								break;
1676 
1677 							case 'I':
1678 								numIncorrectMachines++;
1679 								if ( isBIOS ) {
1680 									if ( showROMStatusIcons )
1681 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
1682 									biosSets.insert(machineName, true);
1683 								} else if ( isDev ) {
1684 									if ( showROMStatusIcons )
1685 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
1686 									deviceSets.insert(machineName, true);
1687 								} else if ( showROMStatusIcons )
1688 									machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
1689 								break;
1690 
1691 							case 'N':
1692 								numNotFoundMachines++;
1693 								if ( isBIOS ) {
1694 									if ( showROMStatusIcons )
1695 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
1696 									biosSets.insert(machineName, true);
1697 								} else if ( isDev ) {
1698 									if ( showROMStatusIcons )
1699 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
1700 									deviceSets.insert(machineName, true);
1701 								} else if ( showROMStatusIcons )
1702 									machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
1703 								break;
1704 
1705 							default:
1706 								numUnknownMachines++;
1707 								machineStatusHash.insert(machineName, 'U');
1708 								if ( isBIOS ) {
1709 									if ( showROMStatusIcons )
1710 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
1711 									biosSets.insert(machineName, true);
1712 								} else if ( isDev ) {
1713 									if ( showROMStatusIcons )
1714 										machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
1715 									deviceSets.insert(machineName, true);
1716 								} else if ( showROMStatusIcons )
1717 									machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
1718 								break;
1719 						}
1720 						QTreeWidgetItem *nameItem = new QTreeWidgetItem(machineItem);
1721 						nameItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, trWaitingForData);
1722 						loadIcon(machineName, machineItem);
1723 						tsMachineListCache << machineName << columnSplitChar << machineDescription << columnSplitChar << machineManufacturer << columnSplitChar
1724 							<< machineYear << columnSplitChar << machineCloneOf << columnSplitChar << (isBIOS ? '1': '0') << columnSplitChar
1725 							<< (hasROMs ? '1' : '0') << columnSplitChar << (hasCHDs ? '1': '0') << columnSplitChar
1726 							<< machinePlayers << columnSplitChar << machineDrvStat << columnSplitChar << (isDev ? '1': '0') << columnSplitChar
1727 							<< machineSource << lineSplitChar;
1728 						machineListDb()->setData(machineName, machineDescription, machineManufacturer, machineYear, machineCloneOf, isBIOS, isDev, hasROMs, hasCHDs, machinePlayers.toInt(), machineDrvStat, machineSource);
1729 						pendingDbUpdates++;
1730 						if ( pendingDbUpdates >= QMC2_MACHINELIST_COMMIT ) {
1731 							machineListDb()->commitTransaction();
1732 							pendingDbUpdates = 0;
1733 							machineListDb()->beginTransaction();
1734 						}
1735 						numMachines++;
1736 						itemList.append(machineItem);
1737 					}
1738 					if ( numMachines % qmc2MachineListResponsiveness == 0 ) {
1739 						mainProgressBar->setValue(numMachines);
1740 						qmc2MainWindow->labelMachineListStatus->setText(status());
1741 						qApp->processEvents();
1742 					}
1743 				}
1744 			}
1745 			mainProgressBar->setValue(numMachines);
1746 			machineListCache.close();
1747 			machineListDb()->commitTransaction();
1748 		} else
1749 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ERROR: can't open machine list cache for writing, path = %1").arg(machineListCache.fileName()));
1750 	}
1751 	// create hierarchical view
1752 	bool useCatverIni = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCatverIni", false).toBool();
1753 	bool useCategories = useCatverIni | qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/UseCategoryIni", false).toBool();
1754 	bool iconFallback = qmc2ParentImageFallback && qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/IconFallback", 0).toInt() == 0;
1755 	qmc2MainWindow->labelMachineListStatus->setText(status());
1756 	qmc2MainWindow->treeWidgetHierarchy->clear();
1757 	QHashIterator<QString, QStringList> itHierarchyHash(hierarchyHash);
1758 	QList<QTreeWidgetItem *> hierarchyItemList;
1759 	QHash<QTreeWidgetItem *, bool> hierarchyHiddenItemHash;
1760 	int counter = numMachines;
1761 	qmc2HierarchyItemHash.reserve(numMachines);
1762 	while ( itHierarchyHash.hasNext() && !qmc2LoadingInterrupted ) {
1763 		itHierarchyHash.next();
1764 		if ( counter++ % qmc2MachineListResponsiveness == 0 ) {
1765 			mainProgressBar->setValue(counter);
1766 			qApp->processEvents();
1767 		}
1768 		const QString &parentName = itHierarchyHash.key();
1769 		QTreeWidgetItem *baseItem = qmc2MachineListItemHash.value(parentName);
1770 		MachineListItem *hierarchyItem = new MachineListItem();
1771 		qmc2HierarchyItemHash.insert(parentName, hierarchyItem);
1772 		if ( hiddenItemHash.contains(baseItem) )
1773 			hierarchyHiddenItemHash.insert(hierarchyItem, true);
1774 		hierarchyItemList.append(hierarchyItem);
1775 		hierarchyItem->setFlags(MachineListItem::defaultItemFlags);
1776 		hierarchyItem->setCheckState(QMC2_MACHINELIST_COLUMN_TAG, Qt::Unchecked);
1777 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, baseItem->text(QMC2_MACHINELIST_COLUMN_MACHINE));
1778 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_YEAR, baseItem->text(QMC2_MACHINELIST_COLUMN_YEAR));
1779 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_MANU, baseItem->text(QMC2_MACHINELIST_COLUMN_MANU));
1780 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_NAME, baseItem->text(QMC2_MACHINELIST_COLUMN_NAME));
1781 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_SRCFILE, baseItem->text(QMC2_MACHINELIST_COLUMN_SRCFILE));
1782 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_RTYPES, baseItem->text(QMC2_MACHINELIST_COLUMN_RTYPES));
1783 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, baseItem->text(QMC2_MACHINELIST_COLUMN_PLAYERS));
1784 		hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_DRVSTAT, baseItem->text(QMC2_MACHINELIST_COLUMN_DRVSTAT));
1785 		if ( useCategories )
1786 			hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, baseItem->text(QMC2_MACHINELIST_COLUMN_CATEGORY));
1787 		if ( useCatverIni )
1788 			hierarchyItem->setText(QMC2_MACHINELIST_COLUMN_VERSION, baseItem->text(QMC2_MACHINELIST_COLUMN_VERSION));
1789 		if ( showROMStatusIcons )
1790 			hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, baseItem->icon(QMC2_MACHINELIST_COLUMN_MACHINE));
1791 		hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_ICON, baseItem->icon(QMC2_MACHINELIST_COLUMN_ICON));
1792 		// sub-items
1793 		const QStringList &children = itHierarchyHash.value();
1794 		for (int j = 0; j < children.count(); j++) {
1795 			if ( counter++ % qmc2MachineListResponsiveness == 0 ) {
1796 				mainProgressBar->setValue(counter);
1797 				qApp->processEvents();
1798 			}
1799 			const QString &cloneName = children.at(j);
1800 			baseItem = qmc2MachineListItemHash.value(cloneName);
1801 			MachineListItem *hierarchySubItem = new MachineListItem(hierarchyItem);
1802 			qmc2HierarchyItemHash.insert(cloneName, hierarchySubItem);
1803 			if ( hiddenItemHash.contains(baseItem) )
1804 				hierarchyHiddenItemHash.insert(hierarchyItem, true);
1805 			hierarchySubItem->setFlags(MachineListItem::defaultItemFlags);
1806 			hierarchySubItem->setCheckState(QMC2_MACHINELIST_COLUMN_TAG, Qt::Unchecked);
1807 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, baseItem->text(QMC2_MACHINELIST_COLUMN_MACHINE));
1808 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_YEAR, baseItem->text(QMC2_MACHINELIST_COLUMN_YEAR));
1809 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_MANU, baseItem->text(QMC2_MACHINELIST_COLUMN_MANU));
1810 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_NAME, cloneName);
1811 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_SRCFILE, baseItem->text(QMC2_MACHINELIST_COLUMN_SRCFILE));
1812 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_RTYPES, baseItem->text(QMC2_MACHINELIST_COLUMN_RTYPES));
1813 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, baseItem->text(QMC2_MACHINELIST_COLUMN_PLAYERS));
1814 			hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_DRVSTAT, baseItem->text(QMC2_MACHINELIST_COLUMN_DRVSTAT));
1815 			if ( useCategories ) {
1816 				QString category(baseItem->text(QMC2_MACHINELIST_COLUMN_CATEGORY));
1817 				if ( category.compare(trQuestionMark) == 0 ) {
1818 					category = hierarchyItem->text(QMC2_MACHINELIST_COLUMN_CATEGORY);
1819 					baseItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, category);
1820 				}
1821 				hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, category);
1822 			}
1823 			if ( useCatverIni )
1824 				hierarchySubItem->setText(QMC2_MACHINELIST_COLUMN_VERSION, baseItem->text(QMC2_MACHINELIST_COLUMN_VERSION));
1825 			if ( showROMStatusIcons )
1826 				hierarchySubItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, baseItem->icon(QMC2_MACHINELIST_COLUMN_MACHINE));
1827 			QIcon icon(baseItem->icon(QMC2_MACHINELIST_COLUMN_ICON));
1828 			if ( icon.isNull() ) {
1829 				if ( iconFallback ) {
1830 					QIcon icon(hierarchyItem->icon(QMC2_MACHINELIST_COLUMN_ICON));
1831 					if ( !icon.isNull() ) {
1832 						baseItem->setIcon(QMC2_MACHINELIST_COLUMN_ICON, icon);
1833 						hierarchySubItem->setIcon(QMC2_MACHINELIST_COLUMN_ICON, icon);
1834 					}
1835 				}
1836 			} else
1837 				hierarchySubItem->setIcon(QMC2_MACHINELIST_COLUMN_ICON, icon);
1838 			qmc2ParentHash.insert(cloneName, parentName);
1839 		}
1840 	}
1841 	qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(false);
1842 	qmc2MainWindow->treeWidgetMachineList->addTopLevelItems(itemList);
1843 	QHashIterator<QTreeWidgetItem *, bool> itHiddenItemHash(hiddenItemHash);
1844 	while ( itHiddenItemHash.hasNext() ) {
1845 		itHiddenItemHash.next();
1846 		itHiddenItemHash.key()->setHidden(true);
1847 	}
1848 	qmc2MainWindow->treeWidgetHierarchy->setUpdatesEnabled(false);
1849 	qmc2MainWindow->treeWidgetHierarchy->addTopLevelItems(hierarchyItemList);
1850 	QHashIterator<QTreeWidgetItem *, bool> itHierarchyHiddenItemHash(hierarchyHiddenItemHash);
1851 	while ( itHierarchyHiddenItemHash.hasNext() ) {
1852 		itHierarchyHiddenItemHash.next();
1853 		itHierarchyHiddenItemHash.key()->setHidden(true);
1854 	}
1855 	if ( loadedFromCache ) {
1856 		QTime elapsedTime(0, 0, 0, 0);
1857 		elapsedTime = elapsedTime.addMSecs(miscTimer.elapsed());
1858 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading machine data from machine list cache, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
1859 	}
1860 	mainProgressBar->setValue(mainProgressBar->maximum());
1861 	if ( !qmc2MachineList->userDataDb()->rankCacheComplete() )
1862 		qmc2MachineList->userDataDb()->fillUpRankCache();
1863 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("sorting machine list by %1 in %2 order").arg(sortCriteriaName(qmc2SortCriteria)).arg(qmc2SortOrder == Qt::AscendingOrder ? tr("ascending") : tr("descending")));
1864 	qmc2MainWindow->treeWidgetMachineList->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
1865 	qmc2MainWindow->treeWidgetHierarchy->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
1866 	QTreeWidgetItem *ci = qmc2MainWindow->treeWidgetMachineList->currentItem();
1867 	if ( ci ) {
1868 		if ( ci->isSelected() )
1869 			QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
1870 		else if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/RestoreMachineSelection").toBool() ) {
1871 			QString selectedMachine(qmc2Config->value(QMC2_EMULATOR_PREFIX + "SelectedMachine", QString()).toString());
1872 			if ( !selectedMachine.isEmpty() ) {
1873 				QTreeWidgetItem *machineItem = qmc2MachineListItemHash.value(selectedMachine);
1874 				if ( machineItem ) {
1875 					qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("restoring machine selection"));
1876 					qmc2MainWindow->blockSignals(true);
1877 					qmc2CurrentItem = machineItem;
1878 					qmc2MainWindow->treeWidgetMachineList->setCurrentItem(machineItem);
1879 					qmc2MainWindow->blockSignals(false);
1880 					QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
1881 				} else
1882 					QTimer::singleShot(0, qmc2MainWindow, SLOT(updateUserData()));
1883 			} else
1884 				QTimer::singleShot(0, qmc2MainWindow, SLOT(updateUserData()));
1885 		}
1886 	} else if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/RestoreMachineSelection").toBool() ) {
1887 		QString selectedMachine(qmc2Config->value(QMC2_EMULATOR_PREFIX + "SelectedMachine", QString()).toString());
1888 		if ( !selectedMachine.isEmpty() ) {
1889 			QTreeWidgetItem *machineItem = qmc2MachineListItemHash.value(selectedMachine);
1890 			if ( machineItem ) {
1891 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("restoring machine selection"));
1892 				qmc2MainWindow->blockSignals(true);
1893 				qmc2CurrentItem = machineItem;
1894 				qmc2MainWindow->treeWidgetMachineList->setCurrentItem(machineItem);
1895 				qmc2MainWindow->blockSignals(false);
1896 				QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
1897 			} else
1898 				QTimer::singleShot(0, qmc2MainWindow, SLOT(updateUserData()));
1899 		} else
1900 			QTimer::singleShot(0, qmc2MainWindow, SLOT(updateUserData()));
1901 	} else
1902 		QTimer::singleShot(0, qmc2MainWindow, SLOT(updateUserData()));
1903 	qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(true);
1904 	qmc2MainWindow->treeWidgetHierarchy->setUpdatesEnabled(true);
1905 	QTime processMachineListElapsedTimer(0, 0, 0, 0);
1906 	processMachineListElapsedTimer = processMachineListElapsedTimer.addMSecs(parseTimer.elapsed());
1907 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (processing machine list, elapsed time = %1)").arg(processMachineListElapsedTimer.toString("mm:ss.zzz")));
1908 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("%n machine(s)", "", numTotalMachines - deviceSets.count() - biosSets.count()) + tr(", %n BIOS set(s)", "", biosSets.count()) + tr(" and %n device(s) loaded", "", deviceSets.count()));
1909 	if ( numMachines != numTotalMachines ) {
1910 		if ( reparseMachineList && qmc2LoadingInterrupted ) {
1911 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: machine list not fully parsed, invalidating machine list cache"));
1912 			QFile f(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/MachineListCacheFile").toString());
1913 			f.remove();
1914 			machineListDb()->recreateDatabase();
1915 		} else if ( !qmc2LoadingInterrupted ) {
1916 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: machine list cache is out of date, invalidating machine list cache"));
1917 			QFile f(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/MachineListCacheFile").toString());
1918 			f.remove();
1919 			machineListDb()->recreateDatabase();
1920 		}
1921 	}
1922 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ROM state info: L:%1 C:%2 M:%3 I:%4 N:%5 U:%6").
1923 					       arg(numTotalMachines >= 0 ? QString::number(numTotalMachines) : trQuestionMark).
1924 					       arg(numCorrectMachines >= 0 ? QString::number(numCorrectMachines) : trQuestionMark).
1925 					       arg(numMostlyCorrectMachines >= 0 ? QString::number(numMostlyCorrectMachines) : trQuestionMark).
1926 					       arg(numIncorrectMachines >= 0 ? QString::number(numIncorrectMachines) : trQuestionMark).
1927 					       arg(numNotFoundMachines >= 0 ? QString::number(numNotFoundMachines) : trQuestionMark).
1928 					       arg(numUnknownMachines >= 0 ? QString::number(numUnknownMachines) : trQuestionMark));
1929 	mainProgressBar->reset();
1930 	qmc2ReloadActive = qmc2StartingUp = false;
1931 	if ( qmc2LoadingInterrupted ) {
1932 		if ( loadProc )
1933 			loadProc->kill();
1934 		autoRomCheck = false;
1935 	} else {
1936 		if ( romStateCacheUpdate || machineStatusHash.count() != numTotalMachines ) {
1937 			if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/AutoTriggerROMCheck").toBool() ) {
1938 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: ROM state cache is incomplete or not up to date, triggering an automatic ROM check"));
1939 				autoRomCheck = true;
1940 			} else
1941 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: ROM state cache is incomplete or not up to date, please re-check ROMs"));
1942 		}
1943 	}
1944 	verifyCurrentOnly = false;
1945 	if ( autoRomCheck )
1946 		QTimer::singleShot(QMC2_AUTOROMCHECK_DELAY, qmc2MainWindow->actionCheckROMs, SLOT(trigger()));
1947 	else if ( !qmc2LoadingInterrupted && qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/Enabled", true).toBool() )
1948 		filter(true);
1949 	QTimer::singleShot(0, this, SLOT(enableWidgets()));
1950 	foreach (MachineListViewer *v, MainWindow::machineListViewers) {
1951 		v->setEnabled(true);
1952 		QTimer::singleShot(0, v->toolButtonUpdateView, SLOT(animateClick()));
1953 	}
1954 	if ( fW )
1955 		fW->setFocus();
1956 }
1957 
sortCriteriaName(int sc)1958 QString MachineList::sortCriteriaName(int sc)
1959 {
1960 	switch ( sc ) {
1961 		case QMC2_SORT_BY_DESCRIPTION:
1962 			return tr("machine description");
1963 		case QMC2_SORT_BY_ROM_STATE:
1964 			return tr("ROM state");
1965 		case QMC2_SORT_BY_TAG:
1966 			return tr("tag");
1967 		case QMC2_SORT_BY_YEAR:
1968 			return tr("year");
1969 		case QMC2_SORT_BY_MANUFACTURER:
1970 			return tr("manufacturer");
1971 		case QMC2_SORT_BY_NAME:
1972 			return tr("machine name");
1973 		case QMC2_SORT_BY_ROMTYPES:
1974 			return tr("ROM types");
1975 		case QMC2_SORT_BY_PLAYERS:
1976 			return tr("players");
1977 		case QMC2_SORT_BY_DRVSTAT:
1978 			return tr("driver status");
1979 		case QMC2_SORT_BY_SRCFILE:
1980 			return tr("source file");
1981 		case QMC2_SORT_BY_RANK:
1982 			return tr("rank");
1983 		case QMC2_SORT_BY_CATEGORY:
1984 			return tr("category");
1985 		case QMC2_SORT_BY_VERSION:
1986 			return tr("version");
1987 		default:
1988 			return trQuestionMark;
1989 	}
1990 }
1991 
filterOne(QTreeWidgetItem * item,char romState)1992 void MachineList::filterOne(QTreeWidgetItem *item, char romState)
1993 {
1994 	bool itemWasHidden = item->isHidden();
1995 	if ( !m_showBiosSets && isBios(item->text(QMC2_MACHINELIST_COLUMN_NAME)) )
1996 		item->setHidden(true);
1997 	else if ( !m_showDeviceSets && isDevice(item->text(QMC2_MACHINELIST_COLUMN_NAME)) )
1998 		item->setHidden(true);
1999 	else switch ( romState ) {
2000 		case 'C':
2001 			item->setHidden(!m_showC);
2002 			break;
2003 		case 'M':
2004 			item->setHidden(!m_showM);
2005 			break;
2006 		case 'I':
2007 			item->setHidden(!m_showI);
2008 			break;
2009 		case 'N':
2010 			item->setHidden(!m_showN);
2011 			break;
2012 		case 'U':
2013 		default:
2014 			item->setHidden(!m_showU);
2015 			break;
2016 	}
2017 	if ( itemWasHidden != item->isHidden() )
2018 		qmc2MainWindow->treeWidgetMachineList_verticalScrollChanged();
2019 }
2020 
filter(bool initial)2021 void MachineList::filter(bool initial)
2022 {
2023 	if ( !initial ) {
2024 		if ( qmc2FilterActive ) {
2025 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ROM state filter already active"));
2026 			return;
2027 		}
2028 		if ( qmc2VerifyActive || qmc2VerifyTaggedActive ) {
2029 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("please wait for ROM verification to finish and try again"));
2030 			return;
2031 		}
2032 		if ( qmc2ReloadActive ) {
2033 			qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("please wait for reload to finish and try again"));
2034 			return;
2035 		}
2036 	}
2037 	bool showC = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowCorrect", true).toBool();
2038 	bool showM = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowMostlyCorrect", true).toBool();
2039 	bool showI = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowIncorrect", true).toBool();
2040 	bool showN = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowNotFound", true).toBool();
2041 	bool showU = qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/ShowUnknown", true).toBool();
2042 	if ( initial && showC && showM && showI && showN && showU ) {
2043 		qmc2StatesTogglesEnabled = true;
2044 		return;
2045 	}
2046 	bool showDeviceSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowDeviceSets", true).toBool();
2047 	bool showBiosSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowBiosSets", true).toBool();
2048 	QTime elapsedTime(0, 0, 0, 0);
2049 	qmc2LoadingInterrupted = false;
2050 	qmc2FilterActive = true;
2051 	QTimer::singleShot(0, this, SLOT(disableWidgets()));
2052 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("applying ROM state filter"));
2053 	parseTimer.start();
2054 	mainProgressBar->reset();
2055 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
2056 		mainProgressBar->setFormat(tr("State filter - %p%"));
2057 	else
2058 		mainProgressBar->setFormat("%p%");
2059 	int itemCount = qmc2MainWindow->treeWidgetMachineList->topLevelItemCount();
2060 	mainProgressBar->setRange(0, itemCount - 1);
2061 	if ( verifyCurrentOnly && checkedItem ) {
2062 		QString machineName(checkedItem->text(QMC2_MACHINELIST_COLUMN_NAME));
2063 		if ( !showBiosSets && isBios(machineName) )
2064 			checkedItem->setHidden(true);
2065 		else if ( !showDeviceSets && isDevice(machineName) )
2066 			checkedItem->setHidden(true);
2067 		else switch ( machineStatusHash.value(machineName) ) {
2068 			case 'C':
2069 				checkedItem->setHidden(!showC);
2070 				break;
2071 			case 'M':
2072 				checkedItem->setHidden(!showM);
2073 				break;
2074 			case 'I':
2075 				checkedItem->setHidden(!showI);
2076 				break;
2077 			case 'N':
2078 				checkedItem->setHidden(!showN);
2079 				break;
2080 			case 'U':
2081 			default:
2082 				checkedItem->setHidden(!showU);
2083 				break;
2084 		}
2085 	} else {
2086 		QTreeWidgetItem *curItem = qmc2MainWindow->treeWidgetMachineList->currentItem();
2087 		QWidget *currentFocusWidget = qApp->focusWidget();
2088 		qmc2MainWindow->treeWidgetMachineList->setVisible(false);
2089 		// note: reset()'ing the tree-widget is essential to avoid an apparent Qt bug that slows down filtering under certain circumstances
2090 		qmc2MainWindow->treeWidgetMachineList->reset();
2091 		((AspectRatioLabel *)qmc2MainWindow->labelLoadingMachineList)->setLabelText(tr("Filtering, please wait..."));
2092 		qmc2MainWindow->labelLoadingMachineList->setVisible(true);
2093 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
2094 			qmc2MainWindow->loadAnimMovie->start();
2095 		int filterResponse = itemCount / QMC2_STATEFILTER_UPDATES;
2096 		for (int i = 0; i < itemCount && !qmc2LoadingInterrupted; i++) {
2097 			QTreeWidgetItem *item = qmc2MainWindow->treeWidgetMachineList->topLevelItem(i);
2098 			QString machineName(item->text(QMC2_MACHINELIST_COLUMN_NAME));
2099 			if ( !showBiosSets && isBios(machineName) )
2100 				item->setHidden(true);
2101 			else if ( !showDeviceSets && isDevice(machineName) )
2102 				item->setHidden(true);
2103 			else switch ( machineStatusHash.value(machineName) ) {
2104 				case 'C':
2105 					item->setHidden(!showC);
2106 					break;
2107 				case 'M':
2108 					item->setHidden(!showM);
2109 					break;
2110 				case 'I':
2111 					item->setHidden(!showI);
2112 					break;
2113 				case 'N':
2114 					item->setHidden(!showN);
2115 					break;
2116 				case 'U':
2117 				default:
2118 					item->setHidden(!showU);
2119 					break;
2120 			}
2121 			if ( i % filterResponse == 0 )
2122 				mainProgressBar->setValue(i);
2123 		}
2124 		qmc2MainWindow->loadAnimMovie->setPaused(true);
2125 		qmc2MainWindow->treeWidgetMachineList->setVisible(true);
2126 		qmc2MainWindow->labelLoadingMachineList->setVisible(false);
2127 		if ( curItem )
2128 			qmc2MainWindow->treeWidgetMachineList->setCurrentItem(curItem);
2129 		if ( currentFocusWidget )
2130 			currentFocusWidget->setFocus();
2131 	}
2132 	mainProgressBar->setValue(itemCount - 1);
2133 	qmc2FilterActive = false;
2134 	elapsedTime = elapsedTime.addMSecs(parseTimer.elapsed());
2135 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (applying ROM state filter, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
2136 	mainProgressBar->reset();
2137 	QTimer::singleShot(0, this, SLOT(enableWidgets()));
2138 	QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
2139 	qmc2StatesTogglesEnabled = true;
2140 }
2141 
loadFavorites()2142 void MachineList::loadFavorites()
2143 {
2144 	qmc2MainWindow->listWidgetFavorites->setUpdatesEnabled(false);
2145 	qmc2MainWindow->listWidgetFavorites->clear();
2146 	QFile f(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/FavoritesFile").toString());
2147 	if ( f.open(QIODevice::ReadOnly | QIODevice::Text) ) {
2148 		QTextStream ts(&f);
2149 		QList<QListWidgetItem *> itemList;
2150 		while ( !ts.atEnd() ) {
2151 			QString machineName(ts.readLine());
2152 			if ( !machineName.isEmpty() ) {
2153 				QTreeWidgetItem *machineItem = qmc2MachineListItemHash.value(machineName);
2154 				if ( machineItem ) {
2155 					QListWidgetItem *item = new QListWidgetItem();
2156 					itemList << item;
2157 					item->setText(machineItem->text(QMC2_MACHINELIST_COLUMN_MACHINE));
2158 					item->setWhatsThis(machineItem->text(QMC2_MACHINELIST_COLUMN_NAME));
2159 					if ( machineItem->isSelected() )
2160 						item->setSelected(true);
2161 				}
2162 			}
2163 		}
2164 		f.close();
2165 		foreach (QListWidgetItem *item, itemList)
2166 			qmc2MainWindow->listWidgetFavorites->addItem(item);
2167 	}
2168 	qmc2MainWindow->listWidgetFavorites->sortItems();
2169 	qmc2MainWindow->listWidgetFavorites->setUpdatesEnabled(true);
2170 	if ( qmc2MainWindow->tabWidgetMachineList->indexOf(qmc2MainWindow->tabFavorites) == qmc2MainWindow->tabWidgetMachineList->currentIndex() )
2171 		QTimer::singleShot(50, qmc2MainWindow, SLOT(checkCurrentFavoritesSelection()));
2172 	else
2173 		qmc2MainWindow->listWidgetFavorites->setCurrentIndex(QModelIndex());
2174 }
2175 
saveFavorites()2176 void MachineList::saveFavorites()
2177 {
2178 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("saving favorites"));
2179 	QFile f(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/FavoritesFile").toString());
2180 	if ( f.open(QIODevice::WriteOnly | QIODevice::Text) ) {
2181 		QTextStream ts(&f);
2182 		for (int i = 0; i < qmc2MainWindow->listWidgetFavorites->count(); i++)
2183 			ts << qmc2MainWindow->listWidgetFavorites->item(i)->whatsThis() << "\n";
2184 		f.close();
2185 	} else
2186 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open favorites file for writing, path = %1").arg(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/FavoritesFile").toString()));
2187 }
2188 
loadPlayHistory()2189 void MachineList::loadPlayHistory()
2190 {
2191 	qmc2MainWindow->listWidgetPlayed->setUpdatesEnabled(false);
2192 	qmc2MainWindow->listWidgetPlayed->clear();
2193 	QFile f(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/HistoryFile").toString());
2194 	if ( f.open(QIODevice::ReadOnly | QIODevice::Text) ) {
2195 		QTextStream ts(&f);
2196 		QList<QListWidgetItem *> itemList;
2197 		while ( !ts.atEnd() ) {
2198 			QString machineName(ts.readLine());
2199 			if ( !machineName.isEmpty() ) {
2200 				QTreeWidgetItem *machineItem = qmc2MachineListItemHash.value(machineName);
2201 				if ( machineItem ) {
2202 					QListWidgetItem *item = new QListWidgetItem();
2203 					itemList << item;
2204 					item->setText(machineItem->text(QMC2_MACHINELIST_COLUMN_MACHINE));
2205 					item->setWhatsThis(machineItem->text(QMC2_MACHINELIST_COLUMN_NAME));
2206 					if ( machineItem->isSelected() )
2207 						item->setSelected(true);
2208 				}
2209 			}
2210 		}
2211 		f.close();
2212 		foreach (QListWidgetItem *item, itemList)
2213 			qmc2MainWindow->listWidgetPlayed->addItem(item);
2214 	}
2215 	qmc2MainWindow->listWidgetPlayed->setUpdatesEnabled(true);
2216 	if ( qmc2MainWindow->tabWidgetMachineList->indexOf(qmc2MainWindow->tabPlayed) == qmc2MainWindow->tabWidgetMachineList->currentIndex() )
2217 		QTimer::singleShot(50, qmc2MainWindow, SLOT(checkCurrentPlayedSelection()));
2218 	else
2219 		qmc2MainWindow->listWidgetPlayed->setCurrentIndex(QModelIndex());
2220 }
2221 
savePlayHistory()2222 void MachineList::savePlayHistory()
2223 {
2224 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("saving play history"));
2225 	QFile f(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/HistoryFile").toString());
2226 	if ( f.open(QIODevice::WriteOnly | QIODevice::Text) ) {
2227 		QTextStream ts(&f);
2228 		for (int i = 0; i < qmc2MainWindow->listWidgetPlayed->count(); i++)
2229 			ts << qmc2MainWindow->listWidgetPlayed->item(i)->whatsThis() << "\n";
2230 		f.close();
2231 	} else
2232 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("FATAL: can't open play history file for writing, path = %1").arg(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/HistoryFile").toString()));
2233 }
2234 
status()2235 QString &MachineList::status()
2236 {
2237 	static QLocale locale;
2238 	m_statusString = m_statusTemplate.arg(m_trL + QString(numMachines > -1 ? locale.toString(numMachines) : trQuestionMark))
2239 					 .arg(m_trC + QString(numCorrectMachines > -1 ? locale.toString(numCorrectMachines) : trQuestionMark))
2240 					 .arg(m_trM + QString(numMostlyCorrectMachines > -1 ? locale.toString(numMostlyCorrectMachines) : trQuestionMark))
2241 					 .arg(m_trI + QString(numIncorrectMachines > -1 ? locale.toString(numIncorrectMachines) : trQuestionMark))
2242 					 .arg(m_trN + QString(numNotFoundMachines > -1 ? locale.toString(numNotFoundMachines) : trQuestionMark))
2243 					 .arg(m_trU + QString(numUnknownMachines > -1 ? locale.toString(numUnknownMachines) : trQuestionMark))
2244 					 .arg(m_trS + QString(numMatchedMachines > -1 ? locale.toString(numMatchedMachines) : trQuestionMark))
2245 					 .arg(m_trT + QString(numTaggedSets > -1 ? locale.toString(numTaggedSets) : trQuestionMark));
2246 	return m_statusString;
2247 }
2248 
loadStarted()2249 void MachineList::loadStarted()
2250 {
2251 	mainProgressBar->setRange(0, numTotalMachines);
2252 	mainProgressBar->reset();
2253 }
2254 
loadFinished(int exitCode,QProcess::ExitStatus exitStatus)2255 void MachineList::loadFinished(int exitCode, QProcess::ExitStatus exitStatus)
2256 {
2257 	bool invalidateListXmlCache = false;
2258 	if ( exitStatus != QProcess::NormalExit && !qmc2LoadingInterrupted ) {
2259 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: emulator audit call didn't exit cleanly -- exitCode = %1, exitStatus = %2").arg(exitCode).arg(QString(exitStatus == QProcess::NormalExit ? tr("normal") : tr("crashed"))));
2260 		qmc2LoadingInterrupted = invalidateListXmlCache = true;
2261 	} else if ( qmc2LoadingInterrupted && exitStatus == QProcess::CrashExit )
2262 		qmc2LoadingInterrupted = invalidateListXmlCache = true;
2263 	QTime elapsedTime(0, 0, 0, 0);
2264 	elapsedTime = elapsedTime.addMSecs(loadTimer.elapsed());
2265 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading XML data and recreating cache, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
2266 	mainProgressBar->reset();
2267 	qmc2EarlyReloadActive = false;
2268 	if ( loadProc )
2269 		delete loadProc;
2270 	loadProc = 0;
2271 	if ( romStateCache.isOpen() )
2272 		romStateCache.close();
2273 	xmlDb()->commitTransaction();
2274 	uncommittedXmlDbRows = 0;
2275 	if ( invalidateListXmlCache ) {
2276 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: XML data cache is incomplete, invalidating XML data cache"));
2277 		xmlDb()->recreateDatabase();
2278 	}
2279 	parse();
2280   	QTimer::singleShot(0, qmc2MainWindow, SLOT(updateUserData()));
2281 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading favorites and play history"));
2282 	loadFavorites();
2283 	loadPlayHistory();
2284 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading favorites and play history)"));
2285 	if ( initialLoad ) {
2286 		QTime startupTime(0, 0, 0, 0);
2287 		startupTime = startupTime.addMSecs(qmc2StartupTimer.elapsed());
2288 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("total start-up time: %1").arg(startupTime.toString("mm:ss.zzz")));
2289 		initialLoad = false;
2290 	}
2291 	// show machine list / hide loading animation
2292 	qmc2MainWindow->loadAnimMovie->setPaused(true);
2293 	qmc2MainWindow->labelLoadingMachineList->setVisible(false);
2294 	qmc2MainWindow->treeWidgetMachineList->setVisible(true);
2295 	qmc2MainWindow->labelLoadingHierarchy->setVisible(false);
2296 	qmc2MainWindow->treeWidgetHierarchy->setVisible(true);
2297 	qmc2MainWindow->labelLoadingAttachedViews->setVisible(false);
2298 	qmc2MainWindow->attachedViewsWidget->setVisible(true);
2299 	if ( qmc2MainWindow->tabWidgetMachineList->indexOf(qmc2MainWindow->tabMachineList) == qmc2MainWindow->tabWidgetMachineList->currentIndex() ) {
2300 		if ( qApp->focusWidget() != qmc2MainWindow->comboBoxToolbarSearch ) {
2301 			switch ( qmc2MainWindow->stackedWidgetView->currentIndex() ) {
2302 				case QMC2_VIEWHIERARCHY_INDEX:
2303 					qmc2MainWindow->treeWidgetHierarchy->setFocus();
2304 					break;
2305 				case QMC2_VIEWCATEGORY_INDEX:
2306 					qmc2MainWindow->treeWidgetCategoryView->setFocus();
2307 					break;
2308 				case QMC2_VIEWVERSION_INDEX:
2309 					qmc2MainWindow->treeWidgetVersionView->setFocus();
2310 					break;
2311 				case QMC2_VIEWMACHINELIST_INDEX:
2312 				default:
2313 					qmc2MainWindow->treeWidgetMachineList->setFocus();
2314 					break;
2315 			}
2316 		}
2317 	}
2318 }
2319 
loadReadyReadStandardOutput()2320 void MachineList::loadReadyReadStandardOutput()
2321 {
2322 	static bool lastCharacterWasSpace = false;
2323 	static QString dtdBuffer;
2324 	static QString setXmlBuffer;
2325 	static QString currentSetName;
2326 	static QRegExp rxDescYearManu("\\<description\\>$|\\<year\\>$|\\<manufacturer\\>$");
2327 
2328 	// this makes the GUI much more responsive, but is HAS to be called before loadProc->readAllStandardOutput()!
2329 	if ( QCoreApplication::hasPendingEvents() )
2330 		qApp->processEvents();
2331 #if defined(QMC2_OS_WIN)
2332 	QString readBuffer(QString::fromUtf8(loadProc->readAllStandardOutput()));
2333 #else
2334 	QString readBuffer(loadProc->readAllStandardOutput());
2335 #endif
2336 	bool startsWithSpace = readBuffer.startsWith(' ') && !lastCharacterWasSpace;
2337 	bool endsWithSpace = readBuffer.endsWith(' ');
2338 	lastCharacterWasSpace = false;
2339 	if ( uncommittedXmlDbRows == 0 )
2340 		xmlDb()->beginTransaction();
2341 	if ( qmc2LoadingInterrupted )
2342 		loadProc->kill();
2343 	readBuffer = readBuffer.simplified();
2344 	if ( startsWithSpace )
2345 		readBuffer.prepend(' ');
2346 	// ensure XML elements are on individual lines
2347 	for (int i = 0; i < readBuffer.length(); i++) {
2348 		if ( readBuffer.at(i) == '>' ) {
2349 			if ( i + 1 < readBuffer.length() ) {
2350 				if ( readBuffer[i + 1] == '<' )
2351 					readBuffer.insert(i + 1, '\n');
2352 				else if ( readBuffer[i + 1] == ' ' ) {
2353 					if ( i + 2 < readBuffer.length() ) {
2354 						if ( readBuffer.at(i + 2) == '<' )
2355 							readBuffer.replace(i + 1, 1, '\n');
2356 					}
2357 				}
2358 			}
2359 		}
2360 	}
2361 	QStringList sl(readBuffer.split('\n'));
2362 	for (int l = 0; l < sl.count(); l++) {
2363 		QString singleXMLLine(sl.at(l));
2364 		bool newLine = singleXMLLine.endsWith('>');
2365 		if ( newLine ) {
2366 			if ( singleXMLLine.indexOf(rxDescYearManu) >= 0 )
2367 				newLine = false;
2368 			if ( newLine ) {
2369 				bool found = false;
2370 				int i;
2371 				for (i = singleXMLLine.length() - 2; i > 0 && !found; i--)
2372 					found = (singleXMLLine.at(i) == '<');
2373 				if ( found && i == 0 )
2374 					newLine = false;
2375 			}
2376 		}
2377 		bool needsSpace = singleXMLLine.endsWith('\"');
2378 		if ( needsSpace ) {
2379 			bool found = false;
2380 			bool stop = false;
2381 			for (int i = singleXMLLine.length() - 2; i > 1 && !found && !stop; i--) {
2382 				if ( singleXMLLine[i] == '\"' ) {
2383 					if ( singleXMLLine.at(i - 1) == '=' )
2384 						found = true;
2385 					else
2386 						stop = true;
2387 				}
2388 			}
2389 			if ( !found )
2390 				needsSpace = false;
2391 		}
2392 		int i = singleXMLLine.length() - 1;
2393 		while ( i >= 0 && singleXMLLine.at(i).isSpace() )
2394 			singleXMLLine.remove(i--, 1);
2395 		needsSpace |= endsWithSpace;
2396 		if ( newLine )
2397 			singleXMLLine += '\n';
2398 		else if ( needsSpace ) {
2399 			singleXMLLine += ' ';
2400 			lastCharacterWasSpace = true;
2401 		}
2402 		xmlLineBuffer += singleXMLLine;
2403 		if ( xmlLineBuffer.endsWith('\n') ) {
2404 			if ( !dtdBufferReady ) {
2405 				dtdBufferReady = xmlLineBuffer.startsWith("<mame build=");
2406 				if ( !dtdBufferReady ) {
2407 					if ( !xmlLineBuffer.startsWith("<?xml version=") )
2408 						dtdBuffer += xmlLineBuffer;
2409 				} else {
2410 					if ( dtdBuffer.endsWith('\n') )
2411 						dtdBuffer.remove(dtdBuffer.length() - 1, 1);
2412 					xmlDb()->setDtd(dtdBuffer);
2413 					dtdBuffer.clear();
2414 				}
2415 			} else {
2416 				if ( currentSetName.isEmpty() ) {
2417 					int startIndex = xmlLineBuffer.indexOf("<machine name=\"");
2418 					if ( startIndex >= 0 ) {
2419 						startIndex += 15;
2420 						int endIndex = xmlLineBuffer.indexOf('\"', startIndex);
2421 						if ( endIndex >= 0 ) {
2422 							currentSetName = xmlLineBuffer.mid(startIndex, endIndex - startIndex);
2423 							setXmlBuffer += xmlLineBuffer;
2424 						}
2425 					}
2426 				} else {
2427 					setXmlBuffer += xmlLineBuffer;
2428 					int index = xmlLineBuffer.indexOf("</machine>");
2429 					if ( index >= 0 ) {
2430 						if ( setXmlBuffer.endsWith('\n') )
2431 							setXmlBuffer.remove(setXmlBuffer.length() - 1, 1);
2432 						if ( xmlDb()->exists(currentSetName) )
2433 							qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: XML bug: the name '%1' is used for multiple sets -- please inform MAME developers").arg(currentSetName));
2434 						xmlDb()->setXml(currentSetName, setXmlBuffer);
2435 						uncommittedXmlDbRows++;
2436 						currentSetName.clear();
2437 						setXmlBuffer.clear();
2438 					}
2439 				}
2440 			}
2441 			xmlLineBuffer.clear();
2442 		}
2443 	}
2444 	if ( uncommittedXmlDbRows >= QMC2_XMLCACHE_COMMIT ) {
2445 		xmlDb()->commitTransaction();
2446 		uncommittedXmlDbRows = 0;
2447 	}
2448 	mainProgressBar->setValue(mainProgressBar->value() + readBuffer.count("<machine name="));
2449 }
2450 
verifyStarted()2451 void MachineList::verifyStarted()
2452 {
2453 	if ( !verifyCurrentOnly )
2454 		mainProgressBar->setValue(0);
2455 }
2456 
verifyFinished(int exitCode,QProcess::ExitStatus exitStatus)2457 void MachineList::verifyFinished(int exitCode, QProcess::ExitStatus exitStatus)
2458 {
2459 	if ( !verifyProc->atEnd() )
2460 		verifyReadyReadStandardOutput();
2461 	bool cleanExit = true;
2462 	if ( exitStatus != QProcess::NormalExit && !qmc2LoadingInterrupted ) {
2463 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("WARNING: emulator audit call didn't exit cleanly -- exitCode = %1, exitStatus = %2").arg(exitCode).arg(QString(exitStatus == QProcess::NormalExit ? tr("normal") : tr("crashed"))));
2464 		cleanExit = false;
2465 	}
2466 	bool showROMStatusIcons = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowROMStatusIcons", true).toBool();
2467 	if ( !verifyCurrentOnly ) {
2468 		// the progress text may have changed in the meantime...
2469 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
2470 			mainProgressBar->setFormat(tr("ROM check - %p%"));
2471 		QSet<QString> gameSet(QSet<QString>::fromList(qmc2MachineListItemHash.uniqueKeys()));
2472 		QList<QString> remainingMachines(gameSet.subtract(QSet<QString>::fromList(verifiedList)).values());
2473 		int counter = mainProgressBar->value();
2474 		if ( qmc2LoadingInterrupted || !cleanExit ) {
2475 			for (int i = 0; i < remainingMachines.count(); i++) {
2476 				counter++;
2477 				if ( i % QMC2_REMAINING_SETS_CHECK_RSP == 0 || i == remainingMachines.count() - 1 ) {
2478 					mainProgressBar->setValue(counter);
2479 					qmc2MainWindow->labelMachineListStatus->setText(status());
2480 					qApp->processEvents();
2481 				}
2482 				QString machineName(remainingMachines.at(i));
2483 				QTreeWidgetItem *romItem = qmc2MachineListItemHash.value(machineName);
2484 				QTreeWidgetItem *hierarchyItem = qmc2HierarchyItemHash.value(machineName);
2485 				if ( romItem && hierarchyItem ) {
2486 					QTreeWidgetItem *categoryItem = qmc2CategoryItemHash.value(machineName);
2487 					QTreeWidgetItem *versionItem = qmc2VersionItemHash.value(machineName);
2488 					machineStatusHash.insert(machineName, 'U');
2489 					foreach (MachineListViewer *v, MainWindow::machineListViewers)
2490 						v->romStatusChanged(machineName, 'U');
2491 					bool isBIOS = isBios(machineName);
2492 					bool isDev = isDevice(machineName);
2493 					if ( romStateCache.isOpen() )
2494 						tsRomCache << machineName << " U\n";
2495 					numUnknownMachines++;
2496 					if ( isBIOS ) {
2497 						if ( showROMStatusIcons ) {
2498 							romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2499 							hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2500 							if ( categoryItem )
2501 								categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2502 							if ( versionItem )
2503 								versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2504 						}
2505 					} else if ( isDev ) {
2506 						if ( showROMStatusIcons ) {
2507 							romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2508 							hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2509 							if ( categoryItem )
2510 								categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2511 							if ( versionItem )
2512 								versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2513 						}
2514 					} else {
2515 						if ( showROMStatusIcons ) {
2516 							romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2517 							hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2518 							if ( categoryItem )
2519 								categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2520 							if ( versionItem )
2521 								versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2522 						}
2523 					}
2524 				}
2525 				if ( romItem == qmc2CurrentItem )
2526 					qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorBlue);
2527 			}
2528 		} else {
2529 			if ( !remainingMachines.isEmpty() && !qmc2LoadingInterrupted )
2530 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("checking real status of %n set(s) not mentioned during full audit", "", remainingMachines.count()));
2531 			for (int i = 0; i < remainingMachines.count() && !qmc2LoadingInterrupted; i++) {
2532 				counter++;
2533 				if ( i % QMC2_REMAINING_SETS_CHECK_RSP == 0 || i == remainingMachines.count() - 1 ) {
2534 					mainProgressBar->setValue(counter);
2535 					qmc2MainWindow->labelMachineListStatus->setText(status());
2536 					qApp->processEvents();
2537 				}
2538 				QString machineName(remainingMachines.at(i));
2539 				bool isBIOS = isBios(machineName);
2540 				bool isDev = isDevice(machineName);
2541 				QTreeWidgetItem *romItem = qmc2MachineListItemHash.value(machineName);
2542 				QTreeWidgetItem *hierarchyItem = qmc2HierarchyItemHash.value(machineName);
2543 				QTreeWidgetItem *categoryItem = qmc2CategoryItemHash.value(machineName);
2544 				QTreeWidgetItem *versionItem = qmc2VersionItemHash.value(machineName);
2545 				// there are quite a number of sets in MAME that don't require any ROMs... many/most device-sets in particular
2546 				bool romRequired = true;
2547 				int xmlCounter = 0;
2548 				QStringList xmlLines(xmlDb()->xml(machineName).split("\n", QString::SkipEmptyParts));
2549 				if ( xmlLines.count() > 0 ) {
2550 					int romCounter = 0;
2551 					int chdCounter = 0;
2552 					bool endFound = false;
2553 					QString endMark = "</machine>";
2554 					while ( !endFound && xmlCounter < xmlLines.count() ) {
2555 						if ( xmlLines.at(xmlCounter).contains("<rom name=\"") ) {
2556 							romCounter++;
2557 							endFound = true;
2558 						} else if ( xmlLines.at(xmlCounter).contains("<disk name=\"") ) {
2559 							chdCounter++;
2560 							endFound = true;
2561 						} else if ( xmlLines.at(xmlCounter).contains(endMark) )
2562 							endFound = true;
2563 						xmlCounter++;
2564 					}
2565 					if ( romCounter == 0 && chdCounter > 0 )
2566 						romRequired = true;
2567 					else
2568 						romRequired = (romCounter > 0);
2569 				}
2570 				if ( romItem && hierarchyItem ) {
2571 					if ( romStateCache.isOpen() ) {
2572 						if ( romRequired ) {
2573 							tsRomCache << machineName << " N\n";
2574 							numNotFoundMachines++;
2575 						} else {
2576 							tsRomCache << machineName << " C\n";
2577 							numCorrectMachines++;
2578 						}
2579 					}
2580 					if ( isBIOS ) {
2581 						if ( showROMStatusIcons ) {
2582 							if ( romRequired ) {
2583 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2584 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2585 								if ( categoryItem )
2586 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2587 								if ( versionItem )
2588 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2589 							} else {
2590 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2591 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2592 								if ( categoryItem )
2593 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2594 								if ( versionItem )
2595 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2596 							}
2597 						}
2598 					} else if ( isDev ) {
2599 						if ( romRequired ) {
2600 							romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2601 							hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2602 							if ( categoryItem )
2603 								categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2604 							if ( versionItem )
2605 								versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2606 						} else {
2607 							romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2608 							hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2609 							if ( categoryItem )
2610 								categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2611 							if ( versionItem )
2612 								versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2613 						}
2614 					} else {
2615 						if ( showROMStatusIcons ) {
2616 							if ( romRequired ) {
2617 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2618 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2619 								if ( categoryItem )
2620 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2621 								if ( versionItem )
2622 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2623 							} else {
2624 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2625 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2626 								if ( categoryItem )
2627 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2628 								if ( versionItem )
2629 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2630 							}
2631 						}
2632 					}
2633 					if ( romRequired ) {
2634 						machineStatusHash.insert(machineName, 'N');
2635 						foreach (MachineListViewer *v, MainWindow::machineListViewers)
2636 							v->romStatusChanged(machineName, 'N');
2637 						if ( romItem == qmc2CurrentItem )
2638 							qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorGrey);
2639 					} else {
2640 						machineStatusHash.insert(machineName, 'C');
2641 						foreach (MachineListViewer *v, MainWindow::machineListViewers)
2642 							v->romStatusChanged(machineName, 'C');
2643 						if ( romItem == qmc2CurrentItem )
2644 							qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorGreen);
2645 					}
2646 				}
2647 			}
2648 			if ( !remainingMachines.isEmpty() && !qmc2LoadingInterrupted )
2649 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (checking real status of %n set(s) not mentioned during full audit)", "", remainingMachines.count()));
2650 		}
2651 		qmc2MainWindow->labelMachineListStatus->setText(status());
2652 	}
2653 	bool doFilter = true;
2654 	if ( verifyCurrentOnly ) {
2655 		QString machineName;
2656 		if ( verifiedList.isEmpty() && checkedItem && exitCode == QMC2_MAME_ERROR_NO_SUCH_MACHINE ) {
2657 			// many device-sets that have no ROMs are declared as being "invalid" during the audit, but that isn't true :)
2658 			machineName = checkedItem->text(QMC2_MACHINELIST_COLUMN_NAME);
2659 			QTreeWidgetItem *hierarchyItem = qmc2HierarchyItemHash.value(machineName);
2660 			if ( hierarchyItem ) {
2661 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ROM status for '%1' is '%2'").arg(checkedItem->text(QMC2_MACHINELIST_COLUMN_MACHINE)).arg(QObject::tr("correct")));
2662 				machineStatusHash.insert(machineName, 'C');
2663 				foreach (MachineListViewer *v, MainWindow::machineListViewers)
2664 					v->romStatusChanged(machineName, 'C');
2665 				numUnknownMachines--;
2666 				numCorrectMachines++;
2667 				if ( checkedItem == qmc2CurrentItem )
2668 					qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorGreen);
2669 				QTreeWidgetItem *categoryItem = qmc2CategoryItemHash.value(machineName);
2670 				QTreeWidgetItem *versionItem = qmc2VersionItemHash.value(machineName);
2671 				if ( isBios(machineName) ) {
2672 					if ( showROMStatusIcons ) {
2673 						checkedItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2674 						hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2675 						if ( categoryItem )
2676 							categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2677 						if ( versionItem )
2678 							versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2679 					}
2680 				} else if ( isDevice(machineName) ) {
2681 					if ( showROMStatusIcons ) {
2682 						checkedItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2683 						hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2684 						if ( categoryItem )
2685 							categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2686 						if ( versionItem )
2687 							versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2688 					}
2689 				} else {
2690 					if ( showROMStatusIcons ) {
2691 						checkedItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2692 						hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2693 						if ( categoryItem )
2694 							categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2695 						if ( versionItem )
2696 							versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2697 					}
2698 				}
2699 			}
2700 			qmc2MainWindow->labelMachineListStatus->setText(status());
2701 		} else if ( checkedItem )
2702 			machineName = checkedItem->text(QMC2_MACHINELIST_COLUMN_NAME);
2703 		if ( romStateCache.isOpen() ) {
2704 			QHashIterator<QString, char> it(machineStatusHash);
2705 			while ( it.hasNext() ) {
2706 				it.next();
2707 				QString machineName(it.key());
2708 				if ( !machineName.isEmpty() ) {
2709 					tsRomCache << machineName << " ";
2710 					switch ( it.value() ) {
2711 						case 'C':
2712 							tsRomCache << "C\n";
2713 							break;
2714 						case 'M':
2715 							tsRomCache << "M\n";
2716 							break;
2717 						case 'I':
2718 							tsRomCache << "I\n";
2719 							break;
2720 						case 'N':
2721 							tsRomCache << "N\n";
2722 							break;
2723 						case 'U':
2724 						default:
2725 							tsRomCache << "U\n";
2726 							break;
2727 					}
2728 				}
2729 			}
2730 		}
2731 		doFilter = (oldRomState != machineStatusHash.value(machineName));
2732 	}
2733 	QTime elapsedTime(0, 0, 0, 0);
2734 	elapsedTime = elapsedTime.addMSecs(verifyTimer.elapsed());
2735 	if ( verifyCurrentOnly )
2736 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (verifying ROM status for '%1', elapsed time = %2)").arg(checkedItem->text(QMC2_MACHINELIST_COLUMN_MACHINE)).arg(elapsedTime.toString("mm:ss.zzz")));
2737 	else
2738 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (verifying ROM status for all sets, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
2739 	if ( romStateCache.isOpen() )
2740 		romStateCache.close();
2741 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ROM state info: L:%1 C:%2 M:%3 I:%4 N:%5 U:%6").
2742 					       arg(numTotalMachines >= 0 ? QString::number(numTotalMachines) : trQuestionMark).
2743 					       arg(numCorrectMachines >= 0 ? QString::number(numCorrectMachines) : trQuestionMark).
2744 					       arg(numMostlyCorrectMachines >= 0 ? QString::number(numMostlyCorrectMachines) : trQuestionMark).
2745 					       arg(numIncorrectMachines >= 0 ? QString::number(numIncorrectMachines) : trQuestionMark).
2746 					       arg(numNotFoundMachines >= 0 ? QString::number(numNotFoundMachines) : trQuestionMark).
2747 					       arg(numUnknownMachines >= 0 ? QString::number(numUnknownMachines) : trQuestionMark));
2748 	mainProgressBar->reset();
2749 	if ( verifyProc ) {
2750 		delete verifyProc;
2751 		verifyProc = 0;
2752 	}
2753 	qmc2VerifyActive = false;
2754 	if ( qmc2SortCriteria == QMC2_SORT_BY_ROM_STATE ) {
2755 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("sorting machine list by %1 in %2 order").arg(tr("ROM state")).arg(qmc2SortOrder == Qt::AscendingOrder ? tr("ascending") : tr("descending")));
2756 		qmc2SortingActive = true;
2757 		qApp->processEvents();
2758 		foreach (QTreeWidgetItem *ti, qmc2ExpandedMachineListItems) {
2759 			qmc2MainWindow->treeWidgetMachineList->collapseItem(ti);
2760 			QList<QTreeWidgetItem *> childrenList = ti->takeChildren();
2761 			foreach (QTreeWidgetItem *ci, ti->takeChildren())
2762 				delete ci;
2763 			QTreeWidgetItem *nameItem = new QTreeWidgetItem(ti);
2764 			nameItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, trWaitingForData);
2765 			nameItem->setText(QMC2_MACHINELIST_COLUMN_ICON, ti->text(QMC2_MACHINELIST_COLUMN_NAME));
2766 		}
2767 		qmc2ExpandedMachineListItems.clear();
2768 		qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(false);
2769 		qmc2MainWindow->treeWidgetHierarchy->setUpdatesEnabled(false);
2770 		qmc2MainWindow->treeWidgetCategoryView->setUpdatesEnabled(false);
2771 		qmc2MainWindow->treeWidgetVersionView->setUpdatesEnabled(false);
2772 		qmc2MainWindow->treeWidgetMachineList->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
2773 		qApp->processEvents();
2774 		qmc2MainWindow->treeWidgetHierarchy->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
2775 		qApp->processEvents();
2776 		if ( qmc2MainWindow->treeWidgetCategoryView->topLevelItemCount() > 0 ) {
2777 			qmc2MainWindow->treeWidgetCategoryView->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
2778 			qApp->processEvents();
2779 		}
2780 		if ( qmc2MainWindow->treeWidgetVersionView->topLevelItemCount() > 0 ) {
2781 			qmc2MainWindow->treeWidgetVersionView->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
2782 			qApp->processEvents();
2783 		}
2784 		qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(true);
2785 		qmc2MainWindow->treeWidgetHierarchy->setUpdatesEnabled(true);
2786 		qmc2MainWindow->treeWidgetCategoryView->setUpdatesEnabled(true);
2787 		qmc2MainWindow->treeWidgetVersionView->setUpdatesEnabled(true);
2788 		qmc2SortingActive = false;
2789 		QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
2790 	}
2791 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "RomStateFilter/Enabled", true).toBool() ){
2792 		if ( doFilter )
2793 			QTimer::singleShot(0, this, SLOT(filter()));
2794 		else {
2795 			QTimer::singleShot(0, this, SLOT(enableWidgets()));
2796 			QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
2797 		}
2798 	} else
2799 		QTimer::singleShot(0, this, SLOT(enableWidgets()));
2800 }
2801 
verifyReadyReadStandardOutput()2802 void MachineList::verifyReadyReadStandardOutput()
2803 {
2804 	// process rom verification output
2805 	char romState;
2806 	QString romStateLong;
2807 	QStringList lines(QString(verifyLastLine + verifyProc->readAllStandardOutput()).split('\n'));
2808 	if ( lines.last().endsWith('\n') )
2809 		verifyLastLine.clear();
2810 	else {
2811 		verifyLastLine = lines.last();
2812 		lines.removeLast();
2813 	}
2814 	if ( !verifyCurrentOnly ) {
2815 		// the progress text may have changed in the meantime...
2816 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
2817 			mainProgressBar->setFormat(tr("ROM check - %p%"));
2818 	}
2819 	bool showROMStatusIcons = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowROMStatusIcons", true).toBool();
2820 	QChar splitChar(' ');
2821 	for (int i = 0; i < lines.count(); i++) {
2822 		if ( lines.at(i).startsWith("romset ") ) {
2823 			QStringList words(lines.at(i).split(splitChar));
2824 			numVerifyRoms++;
2825 			if ( words.count() > 2 ) {
2826 				QString romName(words.at(1));
2827 				romName.remove('\"');
2828 				QTreeWidgetItem *romItem = qmc2MachineListItemHash.value(romName);
2829 				QTreeWidgetItem *hierarchyItem = qmc2HierarchyItemHash.value(romName);
2830 				if ( romItem && hierarchyItem ) {
2831 					QTreeWidgetItem *categoryItem = qmc2CategoryItemHash.value(romName);
2832 					QTreeWidgetItem *versionItem = qmc2VersionItemHash.value(romName);
2833 					bool isBIOS = isBios(romName);
2834 					bool isDev = isDevice(romName);
2835 					if ( words.last() == "good" || lines.at(i).endsWith("has no roms!") ) {
2836 						romState = 'C';
2837 						romStateLong = QObject::tr("correct");
2838 						numCorrectMachines++;
2839 						if ( showROMStatusIcons ) {
2840 							if ( isBIOS ) {
2841 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2842 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2843 								if ( categoryItem )
2844 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2845 								if ( versionItem )
2846 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
2847 							} else if ( isDev ) {
2848 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2849 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2850 								if ( categoryItem )
2851 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2852 								if ( versionItem )
2853 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
2854 							} else {
2855 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2856 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2857 								if ( categoryItem )
2858 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2859 								if ( versionItem )
2860 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
2861 							}
2862 						}
2863 						if ( romItem == qmc2CurrentItem )
2864 							qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorGreen);
2865 					} else if ( words.last() == "bad" ) {
2866 						romState = 'I';
2867 						romStateLong = QObject::tr("incorrect");
2868 						numIncorrectMachines++;
2869 						if ( showROMStatusIcons ) {
2870 							if ( isBIOS ) {
2871 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
2872 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
2873 								if ( categoryItem )
2874 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
2875 								if ( versionItem )
2876 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
2877 							} else if ( isDev ) {
2878 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
2879 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
2880 								if ( categoryItem )
2881 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
2882 								if ( versionItem )
2883 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
2884 							} else {
2885 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
2886 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
2887 								if ( categoryItem )
2888 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
2889 								if ( versionItem )
2890 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
2891 							}
2892 						}
2893 						if ( romItem == qmc2CurrentItem )
2894 							qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorRed);
2895 					} else if ( words.last() == "available" ) {
2896 						romState = 'M';
2897 						romStateLong = QObject::tr("mostly correct");
2898 						numMostlyCorrectMachines++;
2899 						if ( showROMStatusIcons ) {
2900 							if ( isBIOS ) {
2901 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
2902 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
2903 								if ( categoryItem )
2904 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
2905 								if ( versionItem )
2906 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
2907 							} else if ( isDev ) {
2908 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
2909 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
2910 								if ( categoryItem )
2911 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
2912 								if ( versionItem )
2913 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
2914 							} else {
2915 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
2916 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
2917 								if ( categoryItem )
2918 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
2919 								if ( versionItem )
2920 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
2921 							}
2922 						}
2923 						if ( romItem == qmc2CurrentItem )
2924 							qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorYellowGreen);
2925 					} else if ( words.last() == "missing" || words.last() == "found!" ) {
2926 						romState = 'N';
2927 						romStateLong = QObject::tr("not found");
2928 						numNotFoundMachines++;
2929 						if ( showROMStatusIcons ) {
2930 							if ( isBIOS ) {
2931 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2932 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2933 								if ( categoryItem )
2934 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2935 								if ( versionItem )
2936 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
2937 							} else if ( isDev ) {
2938 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2939 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2940 								if ( categoryItem )
2941 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2942 								if ( versionItem )
2943 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
2944 							} else {
2945 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2946 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2947 								if ( categoryItem )
2948 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2949 								if ( versionItem )
2950 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
2951 							}
2952 						}
2953 						if ( romItem == qmc2CurrentItem )
2954 							qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorGrey);
2955 					} else {
2956 						romState = 'U';
2957 						romStateLong = QObject::tr("unknown");
2958 						numUnknownMachines++;
2959 						if ( showROMStatusIcons ) {
2960 							if ( isBIOS ) {
2961 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2962 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2963 								if ( categoryItem )
2964 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2965 								if ( versionItem )
2966 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
2967 							} else if ( isDev ) {
2968 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2969 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2970 								if ( categoryItem )
2971 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2972 								if ( versionItem )
2973 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
2974 							} else {
2975 								romItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2976 								hierarchyItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2977 								if ( categoryItem )
2978 									categoryItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2979 								if ( versionItem )
2980 									versionItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
2981 							}
2982 						}
2983 						if ( romItem == qmc2CurrentItem )
2984 							qmc2MainWindow->labelMachineStatus->setPalette(MainWindow::qmc2StatusColorBlue);
2985 					}
2986 					machineStatusHash.insert(romName, romState);
2987 					foreach (MachineListViewer *v, MainWindow::machineListViewers)
2988 						v->romStatusChanged(romName, romState);
2989 					verifiedList.append(romName);
2990 					if ( verifyCurrentOnly ) {
2991 						qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ROM status for '%1' is '%2'").arg(checkedItem->text(QMC2_MACHINELIST_COLUMN_MACHINE)).arg(romStateLong));
2992 						numUnknownMachines--;
2993 					} else if ( romStateCache.isOpen() )
2994 						tsRomCache << romName << ' ' << romState << '\n';
2995 					if ( m_doFilter )
2996 						filterOne(romItem, romState);
2997 				}
2998 			}
2999 		}
3000 	}
3001 	if ( romStateCache.isOpen() && !verifyCurrentOnly )
3002 		tsRomCache.flush();
3003 	if ( qmc2LoadingInterrupted && verifyProc )
3004 		verifyProc->kill();
3005 	if ( !verifyCurrentOnly )
3006 		mainProgressBar->setValue(numVerifyRoms);
3007 	qmc2MainWindow->labelMachineListStatus->setText(status());
3008 }
3009 
reopenIconCacheDb()3010 void MachineList::reopenIconCacheDb()
3011 {
3012 	QString connectionName(iconCacheDb()->connectionName());
3013 	delete iconCacheDb();
3014 	QSqlDatabase::removeDatabase(connectionName);
3015 	m_iconCacheDb = new IconCacheDatabaseManager(this);
3016 	iconCacheDb()->setSyncMode(QMC2_DB_SYNC_MODE_OFF);
3017 	iconCacheDb()->setJournalMode(QMC2_DB_JOURNAL_MODE_MEMORY);
3018 	connect(iconCacheDb(), SIGNAL(log(const QString &)), qmc2MainWindow, SLOT(logFE(const QString &)));
3019 }
3020 
loadIcon(const QString & machineName,QTreeWidgetItem * item)3021 bool MachineList::loadIcon(const QString &machineName, QTreeWidgetItem *item)
3022 {
3023 	const QIcon &cachedIcon = qmc2IconHash.value(machineName);
3024 	if ( !cachedIcon.isNull() ) {
3025 		// use the cached icon
3026 		if ( item )
3027 			item->setIcon(QMC2_MACHINELIST_COLUMN_ICON, cachedIcon);
3028 		else
3029 			qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(true);
3030 		return true;
3031 	}
3032 	if ( qmc2IconsPreloaded ) {
3033 		// an icon wasn't found
3034 		if ( !item )
3035 			qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(true);
3036 		return false;
3037 	}
3038 	QTime preloadTimer, elapsedTime(0, 0, 0, 0);
3039 	int currentMax = mainProgressBar->maximum();
3040 	QString oldFormat(mainProgressBar->format());
3041 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3042 		mainProgressBar->setFormat(tr("Icon cache - %p%"));
3043 	else
3044 		mainProgressBar->setFormat("%p%");
3045 	bool useIconCacheDb = qmc2Config->value(QMC2_EMULATOR_PREFIX + "IconCacheDatabase/Enabled", true).toBool();
3046 	bool doLoadIcons = true;
3047 	QStringList importPaths;
3048 	switch ( qmc2Options->iconFileType() ) {
3049 		case QMC2_ICON_FILETYPE_ZIP:
3050 			importPaths = qmc2IconFileMap.keys();
3051 			break;
3052 		case QMC2_ICON_FILETYPE_7Z:
3053 			importPaths = qmc2IconFileMap7z.keys();
3054 			break;
3055 #if defined(QMC2_LIBARCHIVE_ENABLED)
3056 		case QMC2_ICON_FILETYPE_ARCHIVE:
3057 			importPaths = qmc2IconArchiveMap.keys();
3058 			break;
3059 #endif
3060 		case QMC2_ICON_FILETYPE_NONE:
3061 		default:
3062 			importPaths = qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/IconDirectory").toString().split(';', QString::SkipEmptyParts);
3063 			break;
3064 	}
3065 	if ( useIconCacheDb )
3066 		doLoadIcons = iconCacheDb()->importRequired(importPaths);
3067 	if ( doLoadIcons ) {
3068 		QByteArray imageData;
3069 		int pendingUpdates = 0;
3070 		if ( useIconCacheDb ) {
3071 			iconCacheDb()->recreateDatabase();
3072 			iconCacheDb()->setEmulatorVersion(emulatorVersion);
3073 			iconCacheDb()->setQmc2Version(XSTR(QMC2_VERSION));
3074 			iconCacheDb()->setIconCacheVersion(QMC2_ICONCACHE_DB_VERSION);
3075 			iconCacheDb()->beginTransaction();
3076 		}
3077 		switch ( qmc2Options->iconFileType() ) {
3078 			case QMC2_ICON_FILETYPE_ZIP:
3079 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("pre-caching icons from ZIP archive"));
3080 				preloadTimer.start();
3081 				foreach (unzFile iconFile, qmc2IconFileMap) {
3082 					unz_global_info unzGlobalInfo;
3083 					if ( unzGetGlobalInfo(iconFile, &unzGlobalInfo) == UNZ_OK ) {
3084 						mainProgressBar->setRange(0, unzGlobalInfo.number_entry);
3085 						mainProgressBar->reset();
3086 						char imageBuffer[QMC2_ZIP_BUFFER_SIZE];
3087 						if ( unzGoToFirstFile(iconFile) == UNZ_OK ) {
3088 							int counter = 0;
3089 							char unzFileName[QMC2_MAX_PATH_LENGTH];
3090 							unz_file_info unzFileInfo;
3091 							do {
3092 								if ( unzGetCurrentFileInfo(iconFile, &unzFileInfo, unzFileName, QMC2_MAX_PATH_LENGTH, 0, 0, 0, 0) == UNZ_OK ) {
3093 									QFileInfo fi(unzFileName);
3094 									imageData.clear();
3095 									if ( unzOpenCurrentFile(iconFile) == UNZ_OK ) {
3096 										int len = 0;
3097 										while ( (len = unzReadCurrentFile(iconFile, &imageBuffer, QMC2_ZIP_BUFFER_SIZE)) > 0 )
3098 											imageData.append(imageBuffer, len);
3099 										unzCloseCurrentFile(iconFile);
3100 										QPixmap iconPixmap;
3101 										if ( iconPixmap.loadFromData(imageData) ) {
3102 											QFileInfo fi2(fi.fileName().toLower());
3103 											QString id(fi2.baseName());
3104 											qmc2IconHash.insert(id, QIcon(iconPixmap));
3105 											if ( useIconCacheDb ) {
3106 												QBuffer imageBuffer(&imageData);
3107 												imageBuffer.open(QIODevice::WriteOnly);
3108 												iconPixmap.save(&imageBuffer, "ICO");
3109 												imageBuffer.close();
3110 												iconCacheDb()->setIconData(id, imageData);
3111 												pendingUpdates++;
3112 											}
3113 										}
3114 									}
3115 								}
3116 								if ( counter++ % QMC2_ICONCACHE_RESPONSIVENESS == 0 )
3117 									mainProgressBar->setValue(counter);
3118 								if ( pendingUpdates >= QMC2_ICONCACHE_COMMIT ) {
3119 									iconCacheDb()->commitTransaction();
3120 									pendingUpdates = 0;
3121 									iconCacheDb()->beginTransaction();
3122 								}
3123 							} while ( unzGoToNextFile(iconFile) != UNZ_END_OF_LIST_OF_FILE );
3124 						}
3125 					}
3126 				}
3127 				mainProgressBar->setValue(mainProgressBar->maximum());
3128 				elapsedTime = elapsedTime.addMSecs(preloadTimer.elapsed());
3129 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (pre-caching icons from ZIP archive, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
3130 				break;
3131 			case QMC2_ICON_FILETYPE_7Z:
3132 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("pre-caching icons from 7z archive"));
3133 				preloadTimer.start();
3134 				foreach (SevenZipFile *sevenZipFile, qmc2IconFileMap7z) {
3135 					mainProgressBar->setRange(0, sevenZipFile->entryList().count());
3136 					mainProgressBar->reset();
3137 					for (int index = 0; index < sevenZipFile->entryList().count(); index++) {
3138 						SevenZipMetaData metaData = sevenZipFile->entryList()[index];
3139 						QFileInfo fi(metaData.name());
3140 						sevenZipFile->read(index, &imageData);
3141 						if ( !sevenZipFile->hasError() ) {
3142 							QPixmap iconPixmap;
3143 							if ( iconPixmap.loadFromData(imageData) ) {
3144 								QFileInfo fi2(fi.fileName().toLower());
3145 								QString id(fi2.baseName());
3146 								qmc2IconHash.insert(id, QIcon(iconPixmap));
3147 								if ( useIconCacheDb ) {
3148 									QBuffer imageBuffer(&imageData);
3149 									imageBuffer.open(QIODevice::WriteOnly);
3150 									iconPixmap.save(&imageBuffer, "ICO");
3151 									imageBuffer.close();
3152 									iconCacheDb()->setIconData(id, imageData);
3153 									pendingUpdates++;
3154 								}
3155 							}
3156 						}
3157 						if ( index % QMC2_ICONCACHE_RESPONSIVENESS == 0 )
3158 							mainProgressBar->setValue(index);
3159 						if ( pendingUpdates >= QMC2_ICONCACHE_COMMIT ) {
3160 							iconCacheDb()->commitTransaction();
3161 							pendingUpdates = 0;
3162 							iconCacheDb()->beginTransaction();
3163 						}
3164 					}
3165 				}
3166 				mainProgressBar->setValue(mainProgressBar->maximum());
3167 				elapsedTime = elapsedTime.addMSecs(preloadTimer.elapsed());
3168 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (pre-caching icons from 7z archive, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
3169 				break;
3170 #if defined(QMC2_LIBARCHIVE_ENABLED)
3171 			case QMC2_ICON_FILETYPE_ARCHIVE:
3172 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("pre-caching icons from archive"));
3173 				preloadTimer.start();
3174 				foreach (ArchiveFile *archiveFile, qmc2IconArchiveMap) {
3175 					mainProgressBar->setRange(0, 0);
3176 					mainProgressBar->reset();
3177 					ArchiveEntryMetaData metaData;
3178 					int counter = 0;
3179 					while ( archiveFile->seekNextEntry(&metaData) ) {
3180 						QFileInfo fi(metaData.name());
3181 						if ( archiveFile->readEntry(imageData) ) {
3182 							QPixmap iconPixmap;
3183 							if ( iconPixmap.loadFromData(imageData) ) {
3184 								QFileInfo fi2(fi.fileName().toLower());
3185 								QString id(fi2.baseName());
3186 								qmc2IconHash.insert(id, QIcon(iconPixmap));
3187 								if ( useIconCacheDb ) {
3188 									QBuffer imageBuffer(&imageData);
3189 									imageBuffer.open(QIODevice::WriteOnly);
3190 									iconPixmap.save(&imageBuffer, "ICO");
3191 									imageBuffer.close();
3192 									iconCacheDb()->setIconData(id, imageData);
3193 									pendingUpdates++;
3194 								}
3195 							}
3196 						}
3197 						if ( counter++ % QMC2_ICONCACHE_RESPONSIVENESS == 0 )
3198 							qApp->processEvents();
3199 						if ( pendingUpdates >= QMC2_ICONCACHE_COMMIT ) {
3200 							iconCacheDb()->commitTransaction();
3201 							pendingUpdates = 0;
3202 							iconCacheDb()->beginTransaction();
3203 						}
3204 					}
3205 				}
3206 				elapsedTime = elapsedTime.addMSecs(preloadTimer.elapsed());
3207 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (pre-caching icons from archive, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
3208 				break;
3209 #endif
3210 			case QMC2_ICON_FILETYPE_NONE:
3211 			default:
3212 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("pre-caching icons from directory"));
3213 				preloadTimer.start();
3214 				foreach(QString icoDir, qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/IconDirectory").toString().split(';', QString::SkipEmptyParts)) {
3215 					mainProgressBar->setRange(0, 0);
3216 					mainProgressBar->reset();
3217 					QDirIterator icoDirIter(icoDir);
3218 					int fileCount = 0;
3219 					while ( icoDirIter.hasNext() ) {
3220 						QFileInfo fi(icoDirIter.next());
3221 						if ( fi.isFile() ) {
3222 							QPixmap iconPixmap;
3223 							if ( iconPixmap.load(fi.absoluteFilePath()) ) {
3224 								QString id(fi.baseName().toLower());
3225 								qmc2IconHash.insert(id, QIcon(iconPixmap));
3226 								if ( useIconCacheDb ) {
3227 									QByteArray imageData;
3228 									QBuffer imageBuffer(&imageData);
3229 									imageBuffer.open(QIODevice::WriteOnly);
3230 									iconPixmap.save(&imageBuffer, "ICO");
3231 									imageBuffer.close();
3232 									iconCacheDb()->setIconData(id, imageData);
3233 									pendingUpdates++;
3234 								}
3235 							}
3236 						}
3237 						if ( fileCount++ % QMC2_ICONCACHE_RESPONSIVENESS == 0 ) {
3238 							mainProgressBar->setValue(fileCount);
3239 							qApp->processEvents();
3240 						}
3241 						if ( pendingUpdates >= QMC2_ICONCACHE_COMMIT ) {
3242 							iconCacheDb()->commitTransaction();
3243 							pendingUpdates = 0;
3244 							iconCacheDb()->beginTransaction();
3245 						}
3246 					}
3247 				}
3248 				mainProgressBar->setValue(mainProgressBar->maximum());
3249 				elapsedTime = elapsedTime.addMSecs(preloadTimer.elapsed());
3250 				qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (pre-caching icons from directory, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
3251 				break;
3252 		}
3253 		iconCacheDb()->commitTransaction();
3254 		QStringList importDates;
3255 		foreach (QString path, importPaths)
3256 			importDates << QString::number(QFileInfo(path).lastModified().toTime_t());
3257 		qmc2Config->setValue(QMC2_EMULATOR_PREFIX + "IconCacheDatabase/ImportPaths", importPaths);
3258 		qmc2Config->setValue(QMC2_EMULATOR_PREFIX + "IconCacheDatabase/ImportDates", importDates);
3259 	} else {
3260 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading icons from cache database"));
3261 		mainProgressBar->setRange(0, iconCacheDb()->iconCacheRowCount());
3262 		mainProgressBar->reset();
3263 		preloadTimer.start();
3264 		int iconCount = 0;
3265 		QString id;
3266 		QByteArray imageData;
3267 		iconCacheDb()->queryIconData();
3268 		while ( iconCacheDb()->nextIconData(&id, &imageData) ) {
3269 			QPixmap iconPixmap;
3270 			if ( iconPixmap.loadFromData(imageData, "ICO") )
3271 				qmc2IconHash.insert(id, QIcon(iconPixmap));
3272 			if ( ++iconCount % QMC2_ICONCACHE_DB_RESPONSIVENESS == 0 ) {
3273 				mainProgressBar->setValue(iconCount);
3274 				qApp->processEvents();
3275 			}
3276 		}
3277 		elapsedTime = elapsedTime.addMSecs(preloadTimer.elapsed());
3278 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading icons from cache database, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
3279 	}
3280 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("%n icon(s) loaded", "", qmc2IconHash.count()));
3281 	qmc2IconsPreloaded = true;
3282 	mainProgressBar->setRange(0, currentMax);
3283 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3284 		mainProgressBar->setFormat(oldFormat);
3285 	else
3286 		mainProgressBar->setFormat("%p%");
3287 	if ( !item )
3288 		qmc2MainWindow->treeWidgetMachineList->setUpdatesEnabled(true);
3289 	mainProgressBar->reset();
3290 	return loadIcon(machineName, item);
3291 }
3292 
loadCategoryIni()3293 void MachineList::loadCategoryIni()
3294 {
3295 	if ( !mergeCategories ) {
3296 		clearCategoryNames();
3297 		categoryHash.clear();
3298 	}
3299 	QTime loadTimer, elapsedTime(0, 0, 0, 0);
3300 	loadTimer.start();
3301 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading category.ini"));
3302 	int currentMax = mainProgressBar->maximum();
3303 	QString oldFormat(mainProgressBar->format());
3304 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3305 		mainProgressBar->setFormat(tr("Category.ini - %p%"));
3306 	else
3307 		mainProgressBar->setFormat("%p%");
3308 	mainProgressBar->reset();
3309 	QFile categoryIniFile(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/CategoryIni").toString());
3310 	int entryCounter = 0;
3311 	if ( categoryIniFile.open(QIODevice::ReadOnly | QIODevice::Text) ) {
3312 		mainProgressBar->setRange(0, categoryIniFile.size());
3313 		QTextStream tsCategoryIni(&categoryIniFile);
3314 		QString categoryName;
3315 		QRegExp rxCategoryName("^\\[.*\\]$");
3316 		QString guiLanguage(qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/Language", "us").toString());
3317 		QString trStart("tr[");
3318 		QChar trEnd(']');
3319 		bool trFound = false;
3320 		while ( !tsCategoryIni.atEnd() ) {
3321 			QString categoryLine(tsCategoryIni.readLine().simplified().trimmed());
3322 			mainProgressBar->setValue(categoryIniFile.pos());
3323 			if ( categoryLine.isEmpty() )
3324 				continue;
3325 			if ( categoryLine.indexOf(rxCategoryName) == 0 ) {
3326 				categoryName = categoryLine.mid(1, categoryLine.length() - 2);
3327 				QHash<QString, QString> translations;
3328 				categoryLine = tsCategoryIni.readLine().simplified().trimmed();
3329 				trFound = false;
3330 				while ( !categoryLine.isEmpty() && categoryLine.startsWith(trStart) ) {
3331 					int endIndex = categoryLine.indexOf(trEnd, 3);
3332 					QString trLanguage(categoryLine.mid(3, endIndex - 3));
3333 					translations.insert(trLanguage, categoryLine.mid(endIndex + 2, categoryLine.length() - endIndex - 2));
3334 					trFound = (trLanguage.compare(guiLanguage) == 0);
3335 					if ( trFound )
3336 						break;
3337 					categoryLine = tsCategoryIni.readLine().simplified().trimmed();
3338 				}
3339 				if ( trFound )
3340 					categoryName = translations.value(guiLanguage);
3341 			} else if ( !categoryName.isEmpty() ) {
3342 				if ( !categoryNames.contains(categoryName) )
3343 					categoryNames.insert(categoryName, new QString(categoryName));
3344 				categoryHash.insert(categoryLine, categoryNames.value(categoryName));
3345 				entryCounter++;
3346 			}
3347 		}
3348 		categoryIniFile.close();
3349 	} else
3350 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ERROR: can't open '%1' for reading -- no category.ini data available").arg(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/CategoryIni").toString()));
3351 	mainProgressBar->setRange(0, currentMax);
3352 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3353 		mainProgressBar->setFormat(oldFormat);
3354 	else
3355 		mainProgressBar->setFormat("%p%");
3356 	elapsedTime = elapsedTime.addMSecs(loadTimer.elapsed());
3357 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading category.ini, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
3358 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("%n category record(s) loaded", "", entryCounter));
3359 }
3360 
createCategoryView()3361 void MachineList::createCategoryView()
3362 {
3363 	if ( creatingCatView || qmc2MainWindow->stackedWidgetView->currentIndex() != QMC2_VIEWCATEGORY_INDEX )
3364 		return;
3365 	qmc2CategoryItemHash.clear();
3366 	if ( qmc2MainWindow->treeWidgetCategoryView->isVisible() ) {
3367 		qmc2MainWindow->treeWidgetCategoryView->setVisible(false);
3368 		((AspectRatioLabel *)qmc2MainWindow->labelCreatingCategoryView)->setLabelText(tr("Loading, please wait..."));
3369 		qmc2MainWindow->labelCreatingCategoryView->setVisible(true);
3370 	}
3371 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
3372 		qmc2MainWindow->loadAnimMovie->start();
3373 	if ( qmc2ReloadActive ) {
3374 		if ( !qmc2LoadingInterrupted )
3375 			QTimer::singleShot(QMC2_RELOAD_POLL_INTERVAL, this, SLOT(createCategoryView()));
3376 		return;
3377 	}
3378 	QTreeWidgetItem *currentlySelectedItem = qmc2CurrentItem;
3379 	creatingCatView = true;
3380 	qmc2MainWindow->treeWidgetCategoryView->setColumnHidden(QMC2_MACHINELIST_COLUMN_CATEGORY, true);
3381 	if ( !qmc2LoadingInterrupted ) {
3382 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
3383 			qmc2MainWindow->loadAnimMovie->start();
3384 		qmc2MainWindow->treeWidgetCategoryView->clear();
3385 		QString oldFormat(mainProgressBar->format());
3386 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3387 			mainProgressBar->setFormat(tr("Category view - %p%"));
3388 		else
3389 			mainProgressBar->setFormat("%p%");
3390 		mainProgressBar->setRange(0, qmc2MainWindow->treeWidgetMachineList->topLevelItemCount());
3391 		mainProgressBar->reset();
3392 		bool showDeviceSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowDeviceSets", true).toBool();
3393 		bool showBiosSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowBiosSets", true).toBool();
3394 		QList<QTreeWidgetItem *> itemList, hideList;
3395 		QHash<QString, QTreeWidgetItem *> itemHash;
3396 		int loadResponse = qmc2MainWindow->treeWidgetMachineList->topLevelItemCount() / QMC2_GENERAL_LOADING_UPDATES;
3397 		if ( loadResponse == 0 )
3398 			loadResponse = 25;
3399 		QHash<QTreeWidgetItem *, int> childCountHash;
3400 		QString trSystemBios(tr("System / BIOS"));
3401 		QString trSystemDevice(tr("System / Device"));
3402 		for (int i = 0; i < qmc2MainWindow->treeWidgetMachineList->topLevelItemCount(); i++) {
3403 			if ( i % loadResponse == 0 ) {
3404 				mainProgressBar->setValue(i);
3405 				qApp->processEvents();
3406 			}
3407 			QTreeWidgetItem *baseItem = qmc2MainWindow->treeWidgetMachineList->topLevelItem(i);
3408 			QString machineName(baseItem->text(QMC2_MACHINELIST_COLUMN_NAME));
3409 			QString category;
3410 			int machineType = int(isBios(machineName)) + int(isDevice(machineName)) * 2; // 0: normal, 1: BIOS, 2: device
3411 			switch ( machineType ) {
3412 				case QMC2_MACHINETYPE_NORMAL:
3413 					category = baseItem->text(QMC2_MACHINELIST_COLUMN_CATEGORY);
3414 					break;
3415 				case QMC2_MACHINETYPE_BIOS:
3416 					category = trSystemBios;
3417 					break;
3418 				case QMC2_MACHINETYPE_DEVICE:
3419 					category = trSystemDevice;
3420 					break;
3421 			}
3422 			QTreeWidgetItem *categoryItem = itemHash.value(category);
3423 			if ( !categoryItem ) {
3424 				categoryItem = new QTreeWidgetItem();
3425 				categoryItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, category);
3426 				itemList.append(categoryItem);
3427 				itemHash.insert(category, categoryItem);
3428 				childCountHash.insert(categoryItem, 0);
3429 			}
3430 			QTreeWidgetItem *machineItem = new MachineListItem(categoryItem);
3431 			childCountHash[categoryItem]++;
3432 			if ( (machineType == QMC2_MACHINETYPE_BIOS && !showBiosSets) || (machineType == QMC2_MACHINETYPE_DEVICE && !showDeviceSets) ) {
3433 				hideList.append(machineItem);
3434 				childCountHash[categoryItem]--;
3435 			}
3436 			machineItem->setFlags(MachineListItem::defaultItemFlags);
3437 			machineItem->setCheckState(QMC2_MACHINELIST_COLUMN_TAG, baseItem->checkState(QMC2_MACHINELIST_COLUMN_TAG));
3438 			machineItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, baseItem->text(QMC2_MACHINELIST_COLUMN_MACHINE));
3439 			machineItem->setText(QMC2_MACHINELIST_COLUMN_YEAR, baseItem->text(QMC2_MACHINELIST_COLUMN_YEAR));
3440 			machineItem->setText(QMC2_MACHINELIST_COLUMN_MANU, baseItem->text(QMC2_MACHINELIST_COLUMN_MANU));
3441 			machineItem->setText(QMC2_MACHINELIST_COLUMN_NAME, baseItem->text(QMC2_MACHINELIST_COLUMN_NAME));
3442 			machineItem->setText(QMC2_MACHINELIST_COLUMN_SRCFILE, baseItem->text(QMC2_MACHINELIST_COLUMN_SRCFILE));
3443 			machineItem->setText(QMC2_MACHINELIST_COLUMN_RTYPES, baseItem->text(QMC2_MACHINELIST_COLUMN_RTYPES));
3444 			machineItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, baseItem->text(QMC2_MACHINELIST_COLUMN_PLAYERS));
3445 			machineItem->setText(QMC2_MACHINELIST_COLUMN_DRVSTAT, baseItem->text(QMC2_MACHINELIST_COLUMN_DRVSTAT));
3446 			machineItem->setText(QMC2_MACHINELIST_COLUMN_VERSION, baseItem->text(QMC2_MACHINELIST_COLUMN_VERSION));
3447 			machineItem->setWhatsThis(QMC2_MACHINELIST_COLUMN_RANK, baseItem->whatsThis(QMC2_MACHINELIST_COLUMN_RANK));
3448 			machineItem->setIcon(QMC2_MACHINELIST_COLUMN_ICON, baseItem->icon(QMC2_MACHINELIST_COLUMN_ICON));
3449 			if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowROMStatusIcons", true).toBool() ) {
3450 				switch ( machineStatusHash.value(machineName) ) {
3451 					case 'C':
3452 						switch ( machineType ) {
3453 							case QMC2_MACHINETYPE_NORMAL:
3454 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
3455 								break;
3456 							case QMC2_MACHINETYPE_BIOS:
3457 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
3458 								break;
3459 							case QMC2_MACHINETYPE_DEVICE:
3460 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
3461 								break;
3462 						}
3463 						break;
3464 					case 'M':
3465 						switch ( machineType ) {
3466 							case QMC2_MACHINETYPE_NORMAL:
3467 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
3468 								break;
3469 							case QMC2_MACHINETYPE_BIOS:
3470 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
3471 								break;
3472 							case QMC2_MACHINETYPE_DEVICE:
3473 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
3474 								break;
3475 						}
3476 						break;
3477 					case 'I':
3478 						switch ( machineType ) {
3479 							case QMC2_MACHINETYPE_NORMAL:
3480 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
3481 								break;
3482 							case QMC2_MACHINETYPE_BIOS:
3483 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
3484 								break;
3485 							case QMC2_MACHINETYPE_DEVICE:
3486 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
3487 								break;
3488 						}
3489 						break;
3490 					case 'N':
3491 						switch ( machineType ) {
3492 							case QMC2_MACHINETYPE_NORMAL:
3493 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
3494 								break;
3495 							case QMC2_MACHINETYPE_BIOS:
3496 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
3497 								break;
3498 							case QMC2_MACHINETYPE_DEVICE:
3499 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
3500 								break;
3501 						}
3502 						break;
3503 					case 'U':
3504 					default:
3505 						switch ( machineType ) {
3506 							case QMC2_MACHINETYPE_NORMAL:
3507 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
3508 								break;
3509 							case QMC2_MACHINETYPE_BIOS:
3510 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
3511 								break;
3512 							case QMC2_MACHINETYPE_DEVICE:
3513 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
3514 								break;
3515 						}
3516 						break;
3517 				}
3518 			}
3519 			qmc2CategoryItemHash.insert(machineName, machineItem);
3520 		}
3521 		foreach (QTreeWidgetItem *item, itemList) {
3522 			if ( childCountHash.contains(item) ) {
3523 				if ( childCountHash.value(item) <= 0 )
3524 					hideList.append(item);
3525 			} else
3526 				hideList.append(item);
3527 		}
3528 		qmc2MainWindow->treeWidgetCategoryView->insertTopLevelItems(0, itemList);
3529 		for (int i = 0; i < hideList.count(); i++)
3530 			hideList.at(i)->setHidden(true);
3531 		qmc2MainWindow->treeWidgetCategoryView->sortItems(QMC2_MACHINELIST_COLUMN_MACHINE, Qt::AscendingOrder); // we want the top-level items to be sorted in any case
3532 		qmc2MainWindow->treeWidgetCategoryView->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
3533 		mainProgressBar->reset();
3534 		mainProgressBar->setFormat(oldFormat);
3535 		if ( qmc2MainWindow->stackedWidgetView->currentIndex() == QMC2_VIEWCATEGORY_INDEX )
3536 			QTimer::singleShot(QMC2_RANK_UPDATE_DELAY, qmc2MainWindow, SLOT(treeWidgetCategoryView_verticalScrollChanged()));
3537 	}
3538 	qmc2MainWindow->loadAnimMovie->setPaused(true);
3539 	qmc2MainWindow->labelCreatingCategoryView->setVisible(false);
3540 	qmc2MainWindow->treeWidgetCategoryView->setVisible(true);
3541 	if ( currentlySelectedItem )
3542 		qmc2CurrentItem = currentlySelectedItem;
3543 	QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
3544 	qmc2MainWindow->treeWidgetCategoryView->setFocus();
3545 	creatingCatView = false;
3546 }
3547 
loadCatverIni()3548 void MachineList::loadCatverIni()
3549 {
3550 	clearCategoryNames();
3551 	categoryHash.clear();
3552 	clearVersionNames();
3553 	versionHash.clear();
3554 	QTime loadTimer, elapsedTime(0, 0, 0, 0);
3555 	loadTimer.start();
3556 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("loading catver.ini"));
3557 	int currentMax = mainProgressBar->maximum();
3558 	QString oldFormat(mainProgressBar->format());
3559 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3560 		mainProgressBar->setFormat(tr("Catver.ini - %p%"));
3561 	else
3562 		mainProgressBar->setFormat("%p%");
3563 	mainProgressBar->reset();
3564 	QFile catverIniFile(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/CatverIni").toString());
3565 	if ( catverIniFile.open(QIODevice::ReadOnly | QIODevice::Text) ) {
3566 		mainProgressBar->setRange(0, catverIniFile.size());
3567 		QTextStream tsCatverIni(&catverIniFile);
3568 		int lineCounter = 0, catVerSwitch = 0;
3569 		QChar splitChar('='), dotChar('.'), zeroChar('0');
3570 		QString catStr("[Category]"), verStr("[VerAdded]");
3571 		while ( !tsCatverIni.atEnd() ) {
3572 			QString catverLine(tsCatverIni.readLine());
3573 			if ( lineCounter++ % QMC2_CATVERINI_LOAD_RESPONSE == 0 ) {
3574 				mainProgressBar->setValue(catverIniFile.pos());
3575 				qApp->processEvents();
3576 			}
3577 			if ( catverLine.isEmpty() )
3578 				continue;
3579 			QStringList tokens(catverLine.split(splitChar, QString::SkipEmptyParts));
3580 			if ( tokens.count() > 1 ) {
3581 				QString token1(tokens.at(1).trimmed());
3582 				switch ( catVerSwitch ) {
3583 					case 1: // category
3584 						if ( !categoryNames.contains(token1) )
3585 							categoryNames.insert(token1, new QString(token1));
3586 						categoryHash.insert(tokens.at(0).trimmed(), categoryNames.value(token1));
3587 						break;
3588 					case 2: // version
3589 						if ( token1.startsWith(dotChar) )
3590 							token1.prepend(zeroChar);
3591 						if ( !versionNames.contains(token1) )
3592 							versionNames.insert(token1, new QString(token1));
3593 						versionHash.insert(tokens.at(0).trimmed(), versionNames.value(token1));
3594 						break;
3595 					default:
3596 						break;
3597 				}
3598 			} else {
3599 				switch ( catVerSwitch ) {
3600 					case 0:
3601 						if ( catverLine.indexOf(catStr) >= 0 )
3602 							catVerSwitch = 1;
3603 						break;
3604 					case 1:
3605 						if ( catverLine.indexOf(verStr) >= 0 )
3606 							catVerSwitch = 2;
3607 						break;
3608 					default:
3609 						break;
3610 				}
3611 			}
3612 		}
3613 		catverIniFile.close();
3614 		mainProgressBar->setValue(mainProgressBar->maximum());
3615 	} else
3616 		qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("ERROR: can't open '%1' for reading -- no catver.ini data available").arg(qmc2Config->value(QMC2_EMULATOR_PREFIX + "FilesAndDirectories/CatverIni").toString()));
3617 	mainProgressBar->setRange(0, currentMax);
3618 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3619 		mainProgressBar->setFormat(oldFormat);
3620 	else
3621 		mainProgressBar->setFormat("%p%");
3622 	elapsedTime = elapsedTime.addMSecs(loadTimer.elapsed());
3623 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("done (loading catver.ini, elapsed time = %1)").arg(elapsedTime.toString("mm:ss.zzz")));
3624 	qmc2MainWindow->log(QMC2_LOG_FRONTEND, tr("%1 category / %2 version records loaded").arg(categoryHash.count()).arg(versionHash.count()));
3625 }
3626 
createVersionView()3627 void MachineList::createVersionView()
3628 {
3629 	if ( creatingVerView || qmc2MainWindow->stackedWidgetView->currentIndex() != QMC2_VIEWVERSION_INDEX )
3630 		return;
3631 	qmc2VersionItemHash.clear();
3632 	if ( qmc2MainWindow->treeWidgetVersionView->isVisible() ) {
3633 		qmc2MainWindow->treeWidgetVersionView->setVisible(false);
3634 		((AspectRatioLabel *)qmc2MainWindow->labelCreatingVersionView)->setLabelText(tr("Loading, please wait..."));
3635 		qmc2MainWindow->labelCreatingVersionView->setVisible(true);
3636 	}
3637 	if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
3638 		qmc2MainWindow->loadAnimMovie->start();
3639 	if ( qmc2ReloadActive ) {
3640 		if ( !qmc2LoadingInterrupted )
3641 			QTimer::singleShot(QMC2_RELOAD_POLL_INTERVAL, this, SLOT(createVersionView()));
3642 		return;
3643 	}
3644 	QTreeWidgetItem *currentlySelectedItem = qmc2CurrentItem;
3645 	creatingVerView = true;
3646 	qmc2MainWindow->treeWidgetVersionView->setColumnHidden(QMC2_MACHINELIST_COLUMN_VERSION, true);
3647 	if ( !qmc2LoadingInterrupted ) {
3648 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ShowLoadingAnimation", true).toBool() )
3649 			qmc2MainWindow->loadAnimMovie->start();
3650 		qmc2MainWindow->treeWidgetVersionView->clear();
3651 		QString oldFormat(mainProgressBar->format());
3652 		if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "GUI/ProgressTexts").toBool() )
3653 			mainProgressBar->setFormat(tr("Version view - %p%"));
3654 		else
3655 			mainProgressBar->setFormat("%p%");
3656 		mainProgressBar->setRange(0, versionHash.count());
3657 		mainProgressBar->reset();
3658 		bool showDeviceSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowDeviceSets", true).toBool();
3659 		bool showBiosSets = qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowBiosSets", true).toBool();
3660 		QList<QTreeWidgetItem *> itemList, hideList;
3661 		QHash<QString, QTreeWidgetItem *> itemHash;
3662 		int loadResponse = numMachines / QMC2_GENERAL_LOADING_UPDATES;
3663 		if ( loadResponse == 0 )
3664 			loadResponse = 25;
3665 		QHash<QTreeWidgetItem *, int> childCountHash;
3666 		for (int i = 0; i < qmc2MainWindow->treeWidgetMachineList->topLevelItemCount(); i++) {
3667 			if ( i % loadResponse == 0 ) {
3668 				mainProgressBar->setValue(i);
3669 				qApp->processEvents();
3670 			}
3671 			QTreeWidgetItem *baseItem = qmc2MainWindow->treeWidgetMachineList->topLevelItem(i);
3672 			QString machineName = baseItem->text(QMC2_MACHINELIST_COLUMN_NAME);
3673 			QString version = baseItem->text(QMC2_MACHINELIST_COLUMN_VERSION);
3674 			QTreeWidgetItem *versionItem = itemHash.value(version);
3675 			if ( !versionItem ) {
3676 				versionItem = new QTreeWidgetItem();
3677 				versionItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, version);
3678 				itemList.append(versionItem);
3679 				itemHash.insert(version, versionItem);
3680 			}
3681 			QTreeWidgetItem *machineItem = new MachineListItem(versionItem);
3682 			int machineType = int(isBios(machineName)) + int(isDevice(machineName)) * 2; // 0: normal, 1: BIOS, 2: device
3683 			childCountHash[versionItem]++;
3684 			if ( (machineType == QMC2_MACHINETYPE_BIOS && !showBiosSets) || (machineType == QMC2_MACHINETYPE_DEVICE && !showDeviceSets) ) {
3685 				hideList.append(machineItem);
3686 				childCountHash[versionItem]--;
3687 			}
3688 			machineItem->setFlags(MachineListItem::defaultItemFlags);
3689 			machineItem->setCheckState(QMC2_MACHINELIST_COLUMN_TAG, baseItem->checkState(QMC2_MACHINELIST_COLUMN_TAG));
3690 			machineItem->setText(QMC2_MACHINELIST_COLUMN_MACHINE, baseItem->text(QMC2_MACHINELIST_COLUMN_MACHINE));
3691 			machineItem->setText(QMC2_MACHINELIST_COLUMN_YEAR, baseItem->text(QMC2_MACHINELIST_COLUMN_YEAR));
3692 			machineItem->setText(QMC2_MACHINELIST_COLUMN_MANU, baseItem->text(QMC2_MACHINELIST_COLUMN_MANU));
3693 			machineItem->setText(QMC2_MACHINELIST_COLUMN_NAME, baseItem->text(QMC2_MACHINELIST_COLUMN_NAME));
3694 			machineItem->setText(QMC2_MACHINELIST_COLUMN_SRCFILE, baseItem->text(QMC2_MACHINELIST_COLUMN_SRCFILE));
3695 			machineItem->setText(QMC2_MACHINELIST_COLUMN_RTYPES, baseItem->text(QMC2_MACHINELIST_COLUMN_RTYPES));
3696 			machineItem->setText(QMC2_MACHINELIST_COLUMN_PLAYERS, baseItem->text(QMC2_MACHINELIST_COLUMN_PLAYERS));
3697 			machineItem->setText(QMC2_MACHINELIST_COLUMN_DRVSTAT, baseItem->text(QMC2_MACHINELIST_COLUMN_DRVSTAT));
3698 			machineItem->setText(QMC2_MACHINELIST_COLUMN_CATEGORY, baseItem->text(QMC2_MACHINELIST_COLUMN_CATEGORY));
3699 			machineItem->setWhatsThis(QMC2_MACHINELIST_COLUMN_RANK, baseItem->whatsThis(QMC2_MACHINELIST_COLUMN_RANK));
3700 			machineItem->setIcon(QMC2_MACHINELIST_COLUMN_ICON, baseItem->icon(QMC2_MACHINELIST_COLUMN_ICON));
3701 			if ( qmc2Config->value(QMC2_FRONTEND_PREFIX + "MachineList/ShowROMStatusIcons", true).toBool() ) {
3702 				switch ( machineStatusHash.value(machineName) ) {
3703 					case 'C':
3704 						switch ( machineType ) {
3705 							case QMC2_MACHINETYPE_NORMAL:
3706 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectImageIcon);
3707 								break;
3708 							case QMC2_MACHINETYPE_BIOS:
3709 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectBIOSImageIcon);
3710 								break;
3711 							case QMC2_MACHINETYPE_DEVICE:
3712 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2CorrectDeviceImageIcon);
3713 								break;
3714 						}
3715 						break;
3716 					case 'M':
3717 						switch ( machineType ) {
3718 							case QMC2_MACHINETYPE_NORMAL:
3719 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectImageIcon);
3720 								break;
3721 							case QMC2_MACHINETYPE_BIOS:
3722 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectBIOSImageIcon);
3723 								break;
3724 							case QMC2_MACHINETYPE_DEVICE:
3725 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2MostlyCorrectDeviceImageIcon);
3726 								break;
3727 						}
3728 						break;
3729 					case 'I':
3730 						switch ( machineType ) {
3731 							case QMC2_MACHINETYPE_NORMAL:
3732 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectImageIcon);
3733 								break;
3734 							case QMC2_MACHINETYPE_BIOS:
3735 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectBIOSImageIcon);
3736 								break;
3737 							case QMC2_MACHINETYPE_DEVICE:
3738 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2IncorrectDeviceImageIcon);
3739 								break;
3740 						}
3741 						break;
3742 					case 'N':
3743 						switch ( machineType ) {
3744 							case QMC2_MACHINETYPE_NORMAL:
3745 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundImageIcon);
3746 								break;
3747 							case QMC2_MACHINETYPE_BIOS:
3748 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundBIOSImageIcon);
3749 								break;
3750 							case QMC2_MACHINETYPE_DEVICE:
3751 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2NotFoundDeviceImageIcon);
3752 								break;
3753 						}
3754 						break;
3755 					case 'U':
3756 					default:
3757 						switch ( machineType ) {
3758 							case QMC2_MACHINETYPE_NORMAL:
3759 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownImageIcon);
3760 								break;
3761 							case QMC2_MACHINETYPE_BIOS:
3762 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownBIOSImageIcon);
3763 								break;
3764 							case QMC2_MACHINETYPE_DEVICE:
3765 								machineItem->setIcon(QMC2_MACHINELIST_COLUMN_MACHINE, qmc2UnknownDeviceImageIcon);
3766 								break;
3767 						}
3768 						break;
3769 				}
3770 			}
3771 			qmc2VersionItemHash.insert(machineName, machineItem);
3772 		}
3773 		foreach (QTreeWidgetItem *item, itemList) {
3774 			if ( childCountHash.contains(item) ) {
3775 				if ( childCountHash.value(item) <= 0 )
3776 					hideList.append(item);
3777 			} else
3778 				hideList.append(item);
3779 		}
3780 		qmc2MainWindow->treeWidgetVersionView->insertTopLevelItems(0, itemList);
3781 		for (int i = 0; i < hideList.count(); i++)
3782 			hideList.at(i)->setHidden(true);
3783 		qmc2MainWindow->treeWidgetVersionView->sortItems(QMC2_MACHINELIST_COLUMN_MACHINE, Qt::AscendingOrder); // we want the top-level items to be sorted in any case
3784 		qmc2MainWindow->treeWidgetVersionView->sortItems(qmc2MainWindow->sortCriteriaLogicalIndex(), qmc2SortOrder);
3785 		mainProgressBar->reset();
3786 		mainProgressBar->setFormat(oldFormat);
3787 		if ( qmc2MainWindow->stackedWidgetView->currentIndex() == QMC2_VIEWVERSION_INDEX )
3788 			QTimer::singleShot(QMC2_RANK_UPDATE_DELAY, qmc2MainWindow, SLOT(treeWidgetVersionView_verticalScrollChanged()));
3789 	}
3790 	qmc2MainWindow->loadAnimMovie->setPaused(true);
3791 	qmc2MainWindow->labelCreatingVersionView->setVisible(false);
3792 	qmc2MainWindow->treeWidgetVersionView->setVisible(true);
3793 	if ( currentlySelectedItem )
3794 		qmc2CurrentItem = currentlySelectedItem;
3795 	QTimer::singleShot(0, qmc2MainWindow, SLOT(scrollToCurrentItem()));
3796 	qmc2MainWindow->treeWidgetVersionView->setFocus();
3797 	creatingVerView = false;
3798 }
3799 
romStatus(const QString & systemName,bool translated)3800 QString MachineList::romStatus(const QString &systemName, bool translated)
3801 {
3802 	switch ( machineStatusHash.value(systemName) ) {
3803 		case 'C':
3804 			return translated ? tr("correct") : "correct";
3805 		case 'M':
3806 			return translated ? tr("mostly correct") : "mostly correct";
3807 		case 'I':
3808 			return translated ? tr("incorrect") : "incorrect";
3809 		case 'N':
3810 			return translated ? tr("not found") : "not found";
3811 		default:
3812 			return translated ? tr("unknown") : "unknown";
3813 	}
3814 }
3815 
lookupDriverName(const QString & systemName)3816 QString MachineList::lookupDriverName(const QString &systemName)
3817 {
3818 	QString driverName(driverNameHash.value(systemName));
3819 	if ( driverName.isEmpty() ) {
3820 		QString xml = xmlDb()->xml(systemName).simplified();
3821 		if ( !xml.isEmpty() ) {
3822 			int startIndex = xml.indexOf("sourcefile=\"");
3823 			if ( startIndex > 0 ) {
3824 				startIndex += 12;
3825 				int endIndex = xml.indexOf("\"", startIndex);
3826 				driverName = xml.mid(startIndex, endIndex - startIndex);
3827 				driverNameHash[systemName] = driverName;
3828 			}
3829 		}
3830 	}
3831 	return driverName;
3832 }
3833 
operator <(const QTreeWidgetItem & otherItem) const3834 bool MachineListItem::operator<(const QTreeWidgetItem &otherItem) const
3835 {
3836 	switch ( qmc2SortCriteria ) {
3837 		case QMC2_SORT_BY_DESCRIPTION:
3838 			return (text(QMC2_MACHINELIST_COLUMN_MACHINE).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_MACHINE), Qt::CaseInsensitive) < 0);
3839 		case QMC2_SORT_BY_ROM_STATE:
3840 			return (qmc2MachineList->machineStatusHash.value(text(QMC2_MACHINELIST_COLUMN_NAME)) < qmc2MachineList->machineStatusHash.value(otherItem.text(QMC2_MACHINELIST_COLUMN_NAME)));
3841 		case QMC2_SORT_BY_TAG:
3842 			return (int(checkState(QMC2_MACHINELIST_COLUMN_TAG)) < int(otherItem.checkState(QMC2_MACHINELIST_COLUMN_TAG)));
3843 		case QMC2_SORT_BY_YEAR:
3844 			return (text(QMC2_MACHINELIST_COLUMN_YEAR).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_YEAR), Qt::CaseInsensitive) < 0);
3845 		case QMC2_SORT_BY_MANUFACTURER:
3846 			return (text(QMC2_MACHINELIST_COLUMN_MANU).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_MANU), Qt::CaseInsensitive) < 0);
3847 		case QMC2_SORT_BY_NAME:
3848 			return (text(QMC2_MACHINELIST_COLUMN_NAME).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_NAME), Qt::CaseInsensitive) < 0);
3849 		case QMC2_SORT_BY_ROMTYPES:
3850 			return (text(QMC2_MACHINELIST_COLUMN_RTYPES).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_RTYPES), Qt::CaseInsensitive) < 0);
3851 		case QMC2_SORT_BY_PLAYERS:
3852 			return (text(QMC2_MACHINELIST_COLUMN_PLAYERS).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_PLAYERS), Qt::CaseInsensitive) < 0);
3853 		case QMC2_SORT_BY_DRVSTAT:
3854 			return (text(QMC2_MACHINELIST_COLUMN_DRVSTAT).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_DRVSTAT), Qt::CaseInsensitive) < 0);
3855 		case QMC2_SORT_BY_SRCFILE:
3856 			return (text(QMC2_MACHINELIST_COLUMN_SRCFILE).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_SRCFILE), Qt::CaseInsensitive) < 0);
3857 		case QMC2_SORT_BY_RANK:
3858 			return (whatsThis(QMC2_MACHINELIST_COLUMN_RANK).toInt() > otherItem.whatsThis(QMC2_MACHINELIST_COLUMN_RANK).toInt());
3859 		case QMC2_SORT_BY_CATEGORY:
3860 			return (text(QMC2_MACHINELIST_COLUMN_CATEGORY).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_CATEGORY), Qt::CaseInsensitive) < 0);
3861 		case QMC2_SORT_BY_VERSION:
3862 			return (text(QMC2_MACHINELIST_COLUMN_VERSION).compare(otherItem.text(QMC2_MACHINELIST_COLUMN_VERSION), Qt::CaseInsensitive) < 0);
3863 		default:
3864 			return false;
3865 	}
3866 }
3867 
isBios()3868 bool MachineListItem::isBios()
3869 {
3870 	return qmc2MachineList->isBios(id());
3871 }
3872 
isDevice()3873 bool MachineListItem::isDevice()
3874 {
3875 	return qmc2MachineList->isDevice(id());
3876 }
3877 
romStatus()3878 char MachineListItem::romStatus()
3879 {
3880 	return qmc2MachineList->machineStatusHash.value(id());
3881 }
3882 
parentId()3883 QString MachineListItem::parentId()
3884 {
3885 	return qmc2ParentHash.value(id());
3886 }
3887