1 #include <qglobal.h>
2 
3 #if QT_VERSION < 0x050000
4 #include <QApplication>
5 #include <QGraphicsObject>
6 #include <QDeclarativeContext>
7 #include <QDeclarativeEngine>
8 #else
9 #include <QMetaType>
10 #include <QGuiApplication>
11 #include <QtQml>
12 #endif
13 #include <QFileInfo>
14 #include <QFile>
15 #include <QTextStream>
16 #include <QPaintEngine>
17 #include <QDesktopServices>
18 #include <QUrl>
19 #include <QHash>
20 #include <QMap>
21 
22 #include <algorithm> // std::sort()
23 
24 #include "tweakedqmlappviewer.h"
25 #include "arcadesettings.h"
26 #include "machineobject.h"
27 #include "consolewindow.h"
28 #include "macros.h"
29 #if QT_VERSION < 0x050000
30 #include "wheel.h"
31 #endif
32 #include "pointer.h"
33 #include "keysequences.h"
34 
35 extern ArcadeSettings *globalConfig;
36 extern ConsoleWindow *consoleWindow;
37 extern int emulatorMode;
38 extern QStringList emulatorModeNames;
39 extern QStringList mameThemes;
40 extern QStringList arcadeThemes;
41 extern QStringList consoleModes;
42 #if QT_VERSION < 0x050000
43 extern QStringList graphicsSystems;
44 #endif
45 
46 int TweakedQmlApplicationViewer::consoleMode = QMC2_ARCADE_CONSOLE_TERM;
47 
48 #if QT_VERSION < 0x050000
TweakedQmlApplicationViewer(QWidget * parent)49 TweakedQmlApplicationViewer::TweakedQmlApplicationViewer(QWidget *parent)
50 	: QmlApplicationViewer(parent)
51 	#else
52 TweakedQmlApplicationViewer::TweakedQmlApplicationViewer(QWindow *parent)
53 	: QQuickView(parent)
54 	#endif
55 {
56 	m_initialized = m_initialFullScreen = m_videoEnabled = windowModeSwitching = false;
57 	m_currentSystemArtworkIndex = m_currentSoftwareArtworkIndex = -2;
58 	numFrames = 0;
59 
60 	QStringList keySequences;
61 	QMC2_ARCADE_ADD_COMMON_KEYSEQUENCES(keySequences);
62 	switch ( themeIndex() ) {
63 	case QMC2_ARCADE_THEME_TOXICWASTE:
64 		QMC2_ARCADE_ADD_TOXIXCWASTE_KEYSEQUENCES(keySequences);
65 		break;
66 	case QMC2_ARCADE_THEME_DARKONE:
67 		QMC2_ARCADE_ADD_DARKONE_KEYSEQUENCES(keySequences);
68 		break;
69 	}
70 	keySequenceMap = new KeySequenceMap(keySequences);
71 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
72 	joyFunctionMap = new JoyFunctionMap(keySequences);
73 	joystickManager = new JoystickManager(joyFunctionMap);
74 #endif
75 
76 	infoClasses << "sysinfo" << "emuinfo" << "softinfo";
77 	videoSnapAllowedFormatExtensions << ".mp4" << ".avi";
78 
79 #if QT_VERSION < 0x050000
80 	cliParams << "theme" << "graphicssystem" << "console" << "language" << "video";
81 #else
82 	cliParams << "theme" << "console" << "language" << "video";
83 #endif
84 	switch ( emulatorMode ) {
85 	case QMC2_ARCADE_EMUMODE_MAME:
86 	default:
87 		cliAllowedParameterValues["theme"] = mameThemes;
88 		break;
89 	}
90 #if QT_VERSION < 0x050000
91 	cliAllowedParameterValues["graphicssystem"] = graphicsSystems;
92 #endif
93 	cliAllowedParameterValues["console"] = consoleModes;
94 	cliAllowedParameterValues["language"] = globalConfig->languageMap.keys();
95 	cliAllowedParameterValues["video"] = QStringList() << "on" << "off";
96 	cliParameterDescriptions["theme"] = tr("Theme");
97 #if QT_VERSION < 0x050000
98 	cliParameterDescriptions["graphicssystem"] = tr("Graphics system");
99 #endif
100 	cliParameterDescriptions["console"] = tr("Console mode");
101 	cliParameterDescriptions["language"] = tr("Language");
102 	cliParameterDescriptions["video"] = tr("Video snaps");
103 
104 #if QT_VERSION < 0x050000
105 	qmlRegisterType<WheelArea>("Wheel", 1, 0, "WheelArea");
106 #endif
107 	qmlRegisterType<CursorShapeArea>("Pointer", 1, 0, "CursorShapeArea");
108 
109 	processManager = new ProcessManager(this);
110 	processManager->createTemplateList();
111 	connect(processManager, SIGNAL(emulatorStarted(int)), this, SIGNAL(emulatorStarted(int)));
112 	connect(processManager, SIGNAL(emulatorFinished(int)), this, SIGNAL(emulatorFinished(int)));
113 
114 #if QT_VERSION < 0x050000
115 	setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
116 	setResizeMode(QDeclarativeView::SizeRootObjectToView);
117 	imageProvider = new ImageProvider(QDeclarativeImageProvider::Image);
118 #else
119 	setResizeMode(QQuickView::SizeRootObjectToView);
120 	imageProvider = new ImageProvider(QQuickImageProvider::Image);
121 #endif
122 
123 	connect(imageProvider, SIGNAL(imageDataUpdated(const QString &)), this, SLOT(imageDataUpdate(const QString &)), Qt::DirectConnection);
124 	engine()->addImageProvider(QString("qmc2"), imageProvider);
125 
126 	infoProvider = new InfoProvider();
127 
128 	engine()->addImportPath(QDir::fromNativeSeparators(XSTR(QMC2_ARCADE_QML_IMPORT_PATH)));
129 	rootContext()->setContextProperty("viewer", this);
130 
131 	// theme-specific initialization
132 	switch ( themeIndex() ) {
133 	case QMC2_ARCADE_THEME_TOXICWASTE:
134 		loadMachineList();
135 		break;
136 	case QMC2_ARCADE_THEME_DARKONE:
137 		// propagate empty gameList to QML
138 		rootContext()->setContextProperty("machineListModel", QVariant::fromValue(gameList));
139 		rootContext()->setContextProperty("machineListModelCount", gameList.count());
140 		break;
141 	}
142 
143 #if QT_VERSION >= 0x050000
144 	connect(this, SIGNAL(frameSwapped()), this, SLOT(frameBufferSwapped()));
145 	connect(engine(), SIGNAL(quit()), this, SLOT(handleQuit()));
146 #endif
147 
148 	connect(&frameCheckTimer, SIGNAL(timeout()), this, SLOT(fpsReady()));
149 }
150 
~TweakedQmlApplicationViewer()151 TweakedQmlApplicationViewer::~TweakedQmlApplicationViewer()
152 {
153 	if ( m_initialized )
154 		saveSettings();
155 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
156 	delete joystickManager;
157 	delete joyFunctionMap;
158 #endif
159 	delete keySequenceMap;
160 }
161 
themeIndex()162 int TweakedQmlApplicationViewer::themeIndex()
163 {
164 	return arcadeThemes.indexOf(globalConfig->arcadeTheme);
165 }
166 
logString(const QString & s)167 void TweakedQmlApplicationViewer::logString(const QString &s)
168 {
169 	if ( consoleMode != QMC2_ARCADE_CONSOLE_NONE ) {
170 		if ( !consoleWindow ) {
171 			printf("%s: %s\n", QTime::currentTime().toString("hh:mm:ss.zzz").toUtf8().constData(), s.toUtf8().constData());
172 			fflush(stdout);
173 		} else
174 			consoleWindow->appendPlainText(QTime::currentTime().toString("hh:mm:ss.zzz") + ": " + s);
175 	}
176 }
177 
logStringNoTime(const QString & s)178 void TweakedQmlApplicationViewer::logStringNoTime(const QString &s)
179 {
180 	if ( consoleMode != QMC2_ARCADE_CONSOLE_NONE ) {
181 		if ( !consoleWindow ) {
182 			printf("%s\n", s.toUtf8().constData());
183 			fflush(stdout);
184 		} else
185 			consoleWindow->appendPlainText(s);
186 	}
187 }
188 
logCString(const char * s)189 void TweakedQmlApplicationViewer::logCString(const char *s)
190 {
191 	if ( consoleMode != QMC2_ARCADE_CONSOLE_NONE ) {
192 		if ( !consoleWindow ) {
193 			printf("%s: %s\n", (const char *)QTime::currentTime().toString("hh:mm:ss.zzz").toUtf8(), (const char *)s);
194 			fflush(stdout);
195 		} else
196 			consoleWindow->appendPlainText(QTime::currentTime().toString("hh:mm:ss.zzz") + ": " + QString(s));
197 	}
198 }
199 
logCStringNoTime(const char * s)200 void TweakedQmlApplicationViewer::logCStringNoTime(const char *s)
201 {
202 	if ( consoleMode != QMC2_ARCADE_CONSOLE_NONE ) {
203 		if ( !consoleWindow ) {
204 			printf("%s\n", (const char *)s);
205 			fflush(stdout);
206 		} else
207 			consoleWindow->appendPlainText(QString(s));
208 	}
209 }
210 
displayInit()211 void TweakedQmlApplicationViewer::displayInit()
212 {
213 	if ( initialFullScreen() )
214 		switchToFullScreen(true);
215 	else
216 		switchToWindowed(true);
217 	if ( rootObject() )
218 		frameCheckTimer.start(QMC2_ARCADE_FPS_UPDATE_INTERVAL);
219 }
220 
fpsReady()221 void TweakedQmlApplicationViewer::fpsReady()
222 {
223 	rootObject()->setProperty("fps", numFrames);
224 	numFrames = 0;
225 }
226 
loadSettings()227 void TweakedQmlApplicationViewer::loadSettings()
228 {
229 	QMC2_ARCADE_LOG_STR(tr("Loading global and theme-specific settings"));
230 
231 	// load global arcade settings
232 	rootObject()->setProperty("version", globalConfig->applicationVersion());
233 	rootObject()->setProperty("qtVersion", qVersion());
234 	QStringList systemArtworkList = customSystemArtwork();
235 	int systemArtworkIndex = -1;
236 
237 	// load theme-specific arcade settings
238 	switch ( themeIndex() ) {
239 	case QMC2_ARCADE_THEME_TOXICWASTE:
240 		rootObject()->setProperty("fpsVisible", globalConfig->fpsVisible());
241 		rootObject()->setProperty("showBackgroundAnimation", globalConfig->showBackgroundAnimation());
242 		rootObject()->setProperty("showShaderEffect", globalConfig->showShaderEffect());
243 		rootObject()->setProperty("animateInForeground", globalConfig->animateInForeground());
244 		rootObject()->setProperty("fullScreen", globalConfig->fullScreen());
245 		rootObject()->setProperty("secondaryImageType", globalConfig->secondaryImageType());
246 		systemArtworkIndex = systemArtworkList.indexOf(globalConfig->secondaryImageType());
247 		if ( systemArtworkIndex >= 0 )
248 			m_currentSystemArtworkIndex = systemArtworkIndex;
249 		rootObject()->setProperty("cabinetFlipped", globalConfig->cabinetFlipped());
250 		rootObject()->setProperty("lastIndex", globalConfig->lastIndex() < gameList.count() ? globalConfig->lastIndex() : 0);
251 		rootObject()->setProperty("menuHidden", globalConfig->menuHidden());
252 		rootObject()->setProperty("confirmQuit", globalConfig->confirmQuit());
253 		rootObject()->setProperty("machineCardPage", globalConfig->machineCardPage());
254 		rootObject()->setProperty("preferencesTab", globalConfig->preferencesTab());
255 		rootObject()->setProperty("autoPositionOverlay", globalConfig->autoPositionOverlay());
256 		rootObject()->setProperty("overlayScale", QMC2_ARCADE_MAX(0.0, QMC2_ARCADE_MIN(10.0, globalConfig->overlayScale())));
257 		rootObject()->setProperty("overlayOffsetX", globalConfig->overlayOffsetX());
258 		rootObject()->setProperty("overlayOffsetY", globalConfig->overlayOffsetY());
259 		rootObject()->setProperty("overlayOpacity", globalConfig->overlayOpacity());
260 		rootObject()->setProperty("backgroundOpacity", globalConfig->backgroundOpacity());
261 		rootObject()->setProperty("machineListOpacity", globalConfig->machineListOpacity());
262 		rootObject()->setProperty("cabinetImageType", globalConfig->cabinetImageType());
263 		rootObject()->setProperty("autoStopAnimations", globalConfig->autoStopAnimations());
264 		if ( videoEnabled() ) {
265 			rootObject()->setProperty("videoPlayerVolume", QMC2_ARCADE_MAX(0.0, QMC2_ARCADE_MIN(1.0, globalConfig->videoPlayerVolume())));
266 			rootObject()->setProperty("videoAutoPlayTimeout", QMC2_ARCADE_MAX(-1, QMC2_ARCADE_MIN(60, globalConfig->videoAutoPlayTimeout())) * 1000);
267 		}
268 		break;
269 	case QMC2_ARCADE_THEME_DARKONE:
270 		rootObject()->setProperty("lastIndex", globalConfig->lastIndex());
271 		rootObject()->setProperty("dataTypePrimary", globalConfig->dataTypePrimary());
272 		rootObject()->setProperty("dataTypeSecondary", globalConfig->dataTypeSecondary());
273 		rootObject()->setProperty("fullScreen", globalConfig->fullScreen());
274 		rootObject()->setProperty("listHidden", globalConfig->listHidden());
275 		rootObject()->setProperty("toolbarHidden", globalConfig->toolbarHidden());
276 		rootObject()->setProperty("fpsVisible", globalConfig->fpsVisible());
277 		rootObject()->setProperty("sortByName", globalConfig->sortByName());
278 		rootObject()->setProperty("screenLight", globalConfig->screenLight());
279 		rootObject()->setProperty("screenLightOpacity", globalConfig->screenLightOpacity());
280 		rootObject()->setProperty("backLight", globalConfig->backLight());
281 		rootObject()->setProperty("backLightOpacity", globalConfig->backLightOpacity());
282 		rootObject()->setProperty("toolbarAutoHide", globalConfig->toolbarAutoHide());
283 		rootObject()->setProperty("launchFlash", globalConfig->launchFlash());
284 		rootObject()->setProperty("launchZoom", globalConfig->launchZoom());
285 		rootObject()->setProperty("overlayScale", QMC2_ARCADE_MAX(0.33, globalConfig->overlayScale()));
286 		rootObject()->setProperty("lightTimeout", QMC2_ARCADE_MAX(5.0, globalConfig->lightTimeout()));
287 		rootObject()->setProperty("colourScheme", globalConfig->colourScheme());
288 		if ( videoEnabled() ) {
289 			rootObject()->setProperty("videoPlayerVolume", QMC2_ARCADE_MAX(0.0, QMC2_ARCADE_MIN(1.0, globalConfig->videoPlayerVolume())));
290 			rootObject()->setProperty("videoAutoPlayTimeout", QMC2_ARCADE_MAX(-1, QMC2_ARCADE_MIN(60, globalConfig->videoAutoPlayTimeout())) * 1000);
291 		}
292 		break;
293 	}
294 	m_initialized = true;
295 }
296 
saveSettings()297 void TweakedQmlApplicationViewer::saveSettings()
298 {
299 	QMC2_ARCADE_LOG_STR(tr("Saving global and theme-specific settings"));
300 
301 	// save global arcade settings
302 	if ( isFullScreen() ) {
303 		globalConfig->setViewerGeometry(savedGeometry);
304 		globalConfig->setViewerMaximized(savedMaximized);
305 	} else {
306 		globalConfig->setViewerGeometry(saveGeometry());
307 		globalConfig->setViewerMaximized(isMaximized());
308 	}
309 
310 	// save theme-specific arcade settings
311 	switch ( themeIndex() ) {
312 	case QMC2_ARCADE_THEME_TOXICWASTE:
313 		globalConfig->setFpsVisible(rootObject()->property("fpsVisible").toBool());
314 		globalConfig->setShowBackgroundAnimation(rootObject()->property("showBackgroundAnimation").toBool());
315 		globalConfig->setShowShaderEffect(rootObject()->property("showShaderEffect").toBool());
316 		globalConfig->setAnimateInForeground(rootObject()->property("animateInForeground").toBool());
317 		globalConfig->setFullScreen(rootObject()->property("fullScreen").toBool());
318 		globalConfig->setSecondaryImageType(rootObject()->property("secondaryImageType").toString());
319 		globalConfig->setCabinetFlipped(rootObject()->property("cabinetFlipped").toBool());
320 		globalConfig->setLastIndex(rootObject()->property("lastIndex").toInt());
321 		globalConfig->setMenuHidden(rootObject()->property("menuHidden").toBool());
322 		globalConfig->setConfirmQuit(rootObject()->property("confirmQuit").toBool());
323 		globalConfig->setMachineCardPage(rootObject()->property("machineCardPage").toInt());
324 		globalConfig->setPreferencesTab(rootObject()->property("preferencesTab").toInt());
325 		globalConfig->setAutoPositionOverlay(rootObject()->property("autoPositionOverlay").toBool());
326 		globalConfig->setOverlayScale(rootObject()->property("overlayScale").toDouble());
327 		globalConfig->setOverlayOffsetX(rootObject()->property("overlayOffsetX").toDouble());
328 		globalConfig->setOverlayOffsetY(rootObject()->property("overlayOffsetY").toDouble());
329 		globalConfig->setOverlayOpacity(rootObject()->property("overlayOpacity").toDouble());
330 		globalConfig->setBackgroundOpacity(rootObject()->property("backgroundOpacity").toDouble());
331 		globalConfig->setMachineListOpacity(rootObject()->property("machineListOpacity").toDouble());
332 		globalConfig->setCabinetImageType(rootObject()->property("cabinetImageType").toString());
333 		globalConfig->setAutoStopAnimations(rootObject()->property("autoStopAnimations").toBool());
334 		if ( videoEnabled() ) {
335 			globalConfig->setVideoPlayerVolume(rootObject()->property("videoPlayerVolume").toDouble());
336 			globalConfig->setVideoAutoPlayTimeout(rootObject()->property("videoAutoPlayTimeout").toInt() / 1000);
337 		}
338 		break;
339 	case QMC2_ARCADE_THEME_DARKONE:
340 		globalConfig->setLastIndex(rootObject()->property("lastIndex").toInt());
341 		globalConfig->setDataTypePrimary(rootObject()->property("dataTypePrimary").toString());
342 		globalConfig->setDataTypeSecondary(rootObject()->property("dataTypeSecondary").toString());
343 		globalConfig->setToolbarHidden(rootObject()->property("toolbarHidden").toBool());
344 		globalConfig->setListHidden(rootObject()->property("listHidden").toBool());
345 		globalConfig->setFullScreen(rootObject()->property("fullScreen").toBool());
346 		globalConfig->setFpsVisible(rootObject()->property("fpsVisible").toBool());
347 		globalConfig->setSortByName(rootObject()->property("sortByName").toBool());
348 		globalConfig->setScreenLight(rootObject()->property("screenLight").toBool());
349 		globalConfig->setScreenLightOpacity(rootObject()->property("screenLightOpacity").toDouble());
350 		globalConfig->setBackLight(rootObject()->property("backLight").toBool());
351 		globalConfig->setBackLightOpacity(rootObject()->property("backLightOpacity").toDouble());
352 		globalConfig->setToolbarAutoHide(rootObject()->property("toolbarAutoHide").toBool());
353 		globalConfig->setLaunchFlash(rootObject()->property("launchFlash").toBool());
354 		globalConfig->setLaunchZoom(rootObject()->property("launchZoom").toBool());
355 		globalConfig->setOverlayScale(rootObject()->property("overlayScale").toDouble());
356 		globalConfig->setLightTimeout(rootObject()->property("lightTimeout").toDouble());
357 		globalConfig->setColourScheme(rootObject()->property("colourScheme").toString());
358 		if ( videoEnabled() ) {
359 			globalConfig->setVideoPlayerVolume(rootObject()->property("videoPlayerVolume").toDouble());
360 			globalConfig->setVideoAutoPlayTimeout(rootObject()->property("videoAutoPlayTimeout").toInt() / 1000);
361 		}
362 		break;
363 	}
364 }
365 
switchToFullScreen(bool initially)366 void TweakedQmlApplicationViewer::switchToFullScreen(bool initially)
367 {
368 	if ( windowModeSwitching )
369 		return;
370 	windowModeSwitching = true;
371 	QMC2_ARCADE_LOG_STR(tr("Activating full-screen display"));
372 	if ( initially ) {
373 		savedGeometry = globalConfig->viewerGeometry();
374 		savedMaximized = globalConfig->viewerMaximized();
375 	} else {
376 		savedGeometry = saveGeometry();
377 		savedMaximized = isMaximized();
378 	}
379 	showFullScreen();
380 	windowModeSwitching = false;
381 }
382 
switchToWindowed(bool initially)383 void TweakedQmlApplicationViewer::switchToWindowed(bool initially)
384 {
385 	if ( windowModeSwitching )
386 		return;
387 	windowModeSwitching = true;
388 	QMC2_ARCADE_LOG_STR(tr("Activating windowed display"));
389 	if ( initially ) {
390 		savedGeometry = globalConfig->viewerGeometry();
391 		savedMaximized = globalConfig->viewerMaximized();
392 	}
393 	restoreGeometry(savedGeometry);
394 	if ( savedMaximized )
395 		showMaximized();
396 	else
397 		showNormal();
398 	windowModeSwitching = false;
399 }
400 
romStateText(int status)401 QString TweakedQmlApplicationViewer::romStateText(int status)
402 {
403 	switch ( status ) {
404 	case QMC2_ARCADE_ROMSTATE_C:
405 		return tr("correct");
406 	case QMC2_ARCADE_ROMSTATE_M:
407 		return tr("mostly correct");
408 	case QMC2_ARCADE_ROMSTATE_I:
409 		return tr("incorrect");
410 	case QMC2_ARCADE_ROMSTATE_N:
411 		return tr("not found");
412 	case QMC2_ARCADE_ROMSTATE_U:
413 	default:
414 		return tr("unknown");
415 	}
416 }
417 
romStateCharToInt(char status)418 int TweakedQmlApplicationViewer::romStateCharToInt(char status)
419 {
420 	switch ( status ) {
421 	case 'C':
422 		return QMC2_ARCADE_ROMSTATE_C;
423 	case 'M':
424 		return QMC2_ARCADE_ROMSTATE_M;
425 	case 'I':
426 		return QMC2_ARCADE_ROMSTATE_I;
427 	case 'N':
428 		return QMC2_ARCADE_ROMSTATE_N;
429 	case 'U':
430 	default:
431 		return QMC2_ARCADE_ROMSTATE_U;
432 	}
433 }
434 
loadMachineList()435 void TweakedQmlApplicationViewer::loadMachineList()
436 {
437 	QString gameListCachePath;
438 	gameList.clear();
439 	m_parentHash.clear();
440 
441 	if ( globalConfig->useFilteredList() ) {
442 		gameListCachePath = QFileInfo(globalConfig->filteredListFile()).absoluteFilePath();
443 		if ( !QFileInfo(gameListCachePath).exists() || !QFileInfo(gameListCachePath).isReadable() ) {
444 			QMC2_ARCADE_LOG_STR(tr("WARNING: filtered list file '%1' doesn't exist or isn't accessible, falling back to the full %2").arg(gameListCachePath).arg(tr("machine list")));
445 			gameListCachePath = QFileInfo(globalConfig->gameListCacheFile()).absoluteFilePath();
446 		}
447 	} else
448 		gameListCachePath = QFileInfo(globalConfig->gameListCacheFile()).absoluteFilePath();
449 
450 	QHash<QString, char> rscHash;
451 
452 	QMC2_ARCADE_LOG_STR(tr("Loading %1 from '%2'").arg(tr("machine list")).arg(QDir::toNativeSeparators(gameListCachePath)));
453 
454 	QString romStateCachePath = QFileInfo(globalConfig->romStateCacheFile()).absoluteFilePath();
455 	QFile romStateCache(romStateCachePath);
456 	if ( romStateCache.exists() ) {
457 		if ( romStateCache.open(QIODevice::ReadOnly | QIODevice::Text) ) {
458 			QTextStream tsRomCache(&romStateCache);
459 			int lineCounter = 0;
460 			while ( !tsRomCache.atEnd() ) {
461 				QString line = tsRomCache.readLine();
462 				if ( !line.isEmpty() && !line.startsWith("#") ) {
463 					QStringList words = line.split(" ");
464 					rscHash[words[0]] = words[1].at(0).toLatin1();
465 				}
466 				if ( lineCounter++ % QMC2_ARCADE_LOAD_RESPONSE == 0 )
467 					qApp->processEvents();
468 			}
469 		} else
470 			QMC2_ARCADE_LOG_STR(tr("WARNING: Can't open ROM state cache file '%1', please check permissions").arg(QDir::toNativeSeparators(romStateCachePath)));
471 	} else
472 		QMC2_ARCADE_LOG_STR(tr("WARNING: The ROM state cache file '%1' doesn't exist, please run main front-end executable to create it").arg(QDir::toNativeSeparators(romStateCachePath)));
473 
474 	QFile gameListCache(gameListCachePath);
475 	if ( gameListCache.exists() ) {
476 		if ( gameListCache.open(QIODevice::ReadOnly | QIODevice::Text) ) {
477 			QTextStream tsGameListCache(&gameListCache);
478 			tsGameListCache.readLine();
479 			tsGameListCache.readLine();
480 			int lineCounter = 0;
481 			while ( !tsGameListCache.atEnd() ) {
482 				QStringList words = tsGameListCache.readLine().split("\t");
483 				if ( words[QMC2_ARCADE_MLC_DEVICE] != "1" ) {
484 					QString gameId = words[QMC2_ARCADE_MLC_ID];
485 					QString parentId = words[QMC2_ARCADE_MLC_PARENT];
486 					gameList.append(new MachineObject(gameId, parentId, words[QMC2_ARCADE_MLC_DESCRIPTION], romStateCharToInt(rscHash[gameId])));
487 					m_parentHash.insert(gameId, parentId);
488 				}
489 				if ( lineCounter++ % QMC2_ARCADE_LOAD_RESPONSE == 0 )
490 					qApp->processEvents();
491 			}
492 		} else
493 			QMC2_ARCADE_LOG_STR(tr("FATAL: Can't open %1 cache file '%2', please check permissions").arg(tr("machine list")).arg(QDir::toNativeSeparators(gameListCachePath)));
494 	} else
495 		QMC2_ARCADE_LOG_STR(tr("FATAL: The %1 cache file '%2' doesn't exist, please run main front-end executable to create it").arg(tr("machine list")).arg(QDir::toNativeSeparators(gameListCachePath)));
496 
497 	if ( globalConfig->sortByName() )
498 		std::sort(gameList.begin(), gameList.end(), MachineObject::lessThan);
499 
500 	// propagate gameList to QML
501 	rootContext()->setContextProperty("machineListModel", QVariant::fromValue(gameList));
502 	rootContext()->setContextProperty("machineListModelCount", gameList.count());
503 
504 	QMC2_ARCADE_LOG_STR(QString(tr("Done (loading %1 from '%2')").arg(tr("machine list")) + " - " + tr("%n non-device set(s) loaded", "", gameList.count())).arg(QDir::toNativeSeparators(gameListCachePath)));
505 }
506 
launchEmulator(QString id)507 void TweakedQmlApplicationViewer::launchEmulator(QString id)
508 {
509 	QMC2_ARCADE_LOG_STR(tr("Starting emulator #%1 for %2 ID '%3'").arg(processManager->highestProcessID()).arg(tr("machine")).arg(id));
510 	processManager->startEmulator(id);
511 }
512 
loadImage(const QString & id)513 QString TweakedQmlApplicationViewer::loadImage(const QString &id)
514 {
515 	return imageProvider->loadImage(id);
516 }
517 
requestInfo(const QString & id,const QString & infoClass)518 QString TweakedQmlApplicationViewer::requestInfo(const QString &id, const QString &infoClass)
519 {
520 	QString infoText;
521 
522 	switch ( infoClasses.indexOf(infoClass) ) {
523 	case QMC2_ARCADE_INFO_CLASS_MACHINE:
524 		infoText = infoProvider->requestInfo(id, InfoProvider::InfoClassMachine);
525 		break;
526 	case QMC2_ARCADE_INFO_CLASS_EMU:
527 		infoText = infoProvider->requestInfo(id, InfoProvider::InfoClassEmu);
528 		break;
529 	case QMC2_ARCADE_INFO_CLASS_SOFT:
530 		infoText = infoProvider->requestInfo(id, InfoProvider::InfoClassSoft);
531 		break;
532 	default:
533 		QMC2_ARCADE_LOG_STR(tr("WARNING: TweakedQmlApplicationViewer::requestInfo(): unsupported info class '%1'").arg(infoClass));
534 		return QString("<p>" + tr("no info available") + "</p>");
535 	}
536 
537 	if ( infoText.isEmpty() ) {
538 		QString pI = parentId(id);
539 		if ( !pI.isEmpty() ) {
540 			switch ( infoClasses.indexOf(infoClass) ) {
541 			case QMC2_ARCADE_INFO_CLASS_MACHINE:
542 				infoText = infoProvider->requestInfo(pI, InfoProvider::InfoClassMachine);
543 				break;
544 			case QMC2_ARCADE_INFO_CLASS_EMU:
545 				infoText = infoProvider->requestInfo(pI, InfoProvider::InfoClassEmu);
546 				break;
547 			case QMC2_ARCADE_INFO_CLASS_SOFT:
548 				infoText = infoProvider->requestInfo(pI, InfoProvider::InfoClassSoft);
549 				break;
550 			}
551 		}
552 	}
553 
554 	if ( infoText.isEmpty() )
555 		infoText = "<p>" + tr("no info available") + "</p>";
556 
557 	return infoText;
558 }
559 
videoSnapUrl(const QString & id)560 QString TweakedQmlApplicationViewer::videoSnapUrl(const QString &id)
561 {
562 	if ( m_videoSnapUrlCache.contains(id) )
563 		return m_videoSnapUrlCache[id];
564 	foreach (QString videoSnapFolder, globalConfig->videoSnapFolder().split(";", QString::SkipEmptyParts)) {
565 		foreach (QString formatExtension, videoSnapAllowedFormatExtensions) {
566 			QFileInfo fi(QDir::cleanPath(videoSnapFolder + "/" + id + formatExtension));
567 			if ( fi.exists() && fi.isReadable() ) {
568 				QString videoSnapUrl = fi.absoluteFilePath();
569 #if defined(QMC2_ARCADE_OS_WIN)
570 				videoSnapUrl.prepend("file:///");
571 #else
572 				videoSnapUrl.prepend("file://");
573 #endif
574 				m_videoSnapUrlCache[id] = videoSnapUrl;
575 				return videoSnapUrl;
576 			}
577 		}
578 		// parent fallback
579 		if ( globalConfig->parentFallback("vdo") ) {
580 			QString pI = parentId(id);
581 			if ( !pI.isEmpty() ) {
582 				foreach (QString formatExtension, videoSnapAllowedFormatExtensions) {
583 					QFileInfo fi(QDir::cleanPath(videoSnapFolder + "/" + pI + formatExtension));
584 					if ( fi.exists() && fi.isReadable() ) {
585 						QString videoSnapUrl = fi.absoluteFilePath();
586 #if defined(QMC2_ARCADE_OS_WIN)
587 						videoSnapUrl.prepend("file:///");
588 #else
589 						videoSnapUrl.prepend("file://");
590 #endif
591 						m_videoSnapUrlCache[id] = videoSnapUrl;
592 						return videoSnapUrl;
593 					}
594 				}
595 			}
596 		}
597 	}
598 	return QString();
599 }
600 
findIndex(QString pattern,int startIndex)601 int TweakedQmlApplicationViewer::findIndex(QString pattern, int startIndex)
602 {
603 	if ( pattern.isEmpty() )
604 		return startIndex;
605 
606 	int foundIndex = startIndex;
607 	bool indexFound = false;
608 
609 	QRegExp wildcard(pattern, Qt::CaseInsensitive, QRegExp::Wildcard);
610 	QRegExp regexp(pattern, Qt::CaseInsensitive, QRegExp::RegExp);
611 
612 	for (int i = startIndex + 1; i < gameList.count() && !indexFound; i++) {
613 		QString description = ((MachineObject *)gameList[i])->description();
614 		QString id = ((MachineObject *)gameList[i])->id();
615 		if ( description.indexOf(wildcard, 0) >= 0 || id.indexOf(wildcard, 0) >= 0 ) {
616 			foundIndex = i;
617 			indexFound = true;
618 		} else if ( regexp.indexIn(description, 0) >= 0 || regexp.indexIn(id, 0) >= 0 ) {
619 			foundIndex = i;
620 			indexFound = true;
621 		}
622 	}
623 
624 	for (int i = 0; i < startIndex && !indexFound; i++) {
625 		QString description = ((MachineObject *)gameList[i])->description();
626 		QString id = ((MachineObject *)gameList[i])->id();
627 		if ( description.indexOf(wildcard, 0) >= 0 || id.indexOf(wildcard, 0) >= 0 ) {
628 			foundIndex = i;
629 			indexFound = true;
630 		} else if ( regexp.indexIn(description, 0) >= 0 || regexp.indexIn(id, 0) >= 0 ) {
631 			foundIndex = i;
632 			indexFound = true;
633 		}
634 	}
635 
636 	return foundIndex;
637 }
638 
log(QString message)639 void TweakedQmlApplicationViewer::log(QString message)
640 {
641 	QMC2_ARCADE_LOG_STR(message);
642 }
643 
cliParamNames()644 QStringList TweakedQmlApplicationViewer::cliParamNames()
645 {
646 	return cliAllowedParameterValues.keys();
647 }
648 
cliParamDescription(QString param)649 QString TweakedQmlApplicationViewer::cliParamDescription(QString param)
650 {
651 	return cliParameterDescriptions[param];
652 }
653 
cliParamValue(QString param)654 QString TweakedQmlApplicationViewer::cliParamValue(QString param)
655 {
656 	switch ( cliParams.indexOf(param) ) {
657 	case QMC2_ARCADE_PARAM_THEME:
658 		return globalConfig->defaultTheme();
659 #if QT_VERSION < 0x050000
660 	case QMC2_ARCADE_PARAM_GRASYS:
661 		return globalConfig->defaultGraphicsSystem();
662 #endif
663 	case QMC2_ARCADE_PARAM_CONSOLE:
664 		return globalConfig->defaultConsoleType();
665 	case QMC2_ARCADE_PARAM_LANGUAGE:
666 		return globalConfig->defaultLanguage();
667 	case QMC2_ARCADE_PARAM_VIDEO:
668 		return globalConfig->defaultVideo();
669 	default:
670 		return QString();
671 	}
672 }
673 
cliParamAllowedValues(QString param)674 QStringList TweakedQmlApplicationViewer::cliParamAllowedValues(QString param)
675 {
676 	return cliAllowedParameterValues[param];
677 }
678 
setCliParamValue(QString param,QString value)679 void TweakedQmlApplicationViewer::setCliParamValue(QString param, QString value)
680 {
681 	switch ( cliParams.indexOf(param) ) {
682 	case QMC2_ARCADE_PARAM_THEME:
683 		globalConfig->setDefaultTheme(value);
684 		break;
685 #if QT_VERSION < 0x050000
686 	case QMC2_ARCADE_PARAM_GRASYS:
687 		globalConfig->setDefaultGraphicsSystem(value);
688 		break;
689 #endif
690 	case QMC2_ARCADE_PARAM_CONSOLE:
691 		globalConfig->setDefaultConsoleType(value);
692 		break;
693 	case QMC2_ARCADE_PARAM_LANGUAGE:
694 		globalConfig->setDefaultLanguage(value);
695 		break;
696 	case QMC2_ARCADE_PARAM_VIDEO:
697 		globalConfig->setDefaultVideo(value);
698 		break;
699 	}
700 }
701 
linkActivated(QString link)702 void TweakedQmlApplicationViewer::linkActivated(QString link)
703 {
704 	QDesktopServices::openUrl(QUrl::fromUserInput(link));
705 }
706 
emuMode()707 QString TweakedQmlApplicationViewer::emuMode()
708 {
709 	switch ( emulatorMode ) {
710 	case QMC2_ARCADE_EMUMODE_MAME:
711 	default:
712 		return "mame";
713 	}
714 }
715 
iconCacheDatabaseEnabled()716 bool TweakedQmlApplicationViewer::iconCacheDatabaseEnabled()
717 {
718 	return globalConfig->iconCacheDatabaseEnabled();
719 }
720 
parentId(QString id)721 QString TweakedQmlApplicationViewer::parentId(QString id)
722 {
723 	if ( m_parentHash.contains(id) )
724 		return m_parentHash[id];
725 	else
726 		return QString();
727 }
728 
customSystemArtwork()729 QStringList TweakedQmlApplicationViewer::customSystemArtwork()
730 {
731 	return globalConfig->customSystemArtworkNames();
732 }
733 
customSoftwareArtwork()734 QStringList TweakedQmlApplicationViewer::customSoftwareArtwork()
735 {
736 	return globalConfig->customSoftwareArtworkNames();
737 }
738 
nextCustomSytemArtwork()739 QString TweakedQmlApplicationViewer::nextCustomSytemArtwork()
740 {
741 	QString artwork;
742 	QStringList artworkList = customSystemArtwork();
743 	if ( m_currentSystemArtworkIndex == -2 )
744 		m_currentSystemArtworkIndex = 0;
745 	else
746 		m_currentSystemArtworkIndex++;
747 	if ( m_currentSystemArtworkIndex >= 0 && m_currentSystemArtworkIndex < artworkList.count() )
748 		artwork = artworkList[m_currentSystemArtworkIndex];
749 	else {
750 		m_currentSystemArtworkIndex = -2;
751 		return QString();
752 	}
753 	return artwork;
754 }
755 
previousCustomSytemArtwork()756 QString TweakedQmlApplicationViewer::previousCustomSytemArtwork()
757 {
758 	QString artwork;
759 	QStringList artworkList = customSystemArtwork();
760 	if ( m_currentSystemArtworkIndex == -2 )
761 		m_currentSystemArtworkIndex = artworkList.count() - 1;
762 	else
763 		m_currentSystemArtworkIndex--;
764 	if ( m_currentSystemArtworkIndex >= 0 && m_currentSystemArtworkIndex < artworkList.count() )
765 		artwork = artworkList[m_currentSystemArtworkIndex];
766 	else {
767 		m_currentSystemArtworkIndex = -2;
768 		return QString();
769 	}
770 	return artwork;
771 }
772 
nextCustomSoftwareArtwork()773 QString TweakedQmlApplicationViewer::nextCustomSoftwareArtwork()
774 {
775 	QStringList artworkList = customSoftwareArtwork();
776 	if ( m_currentSoftwareArtworkIndex == -2 )
777 		m_currentSoftwareArtworkIndex = 0;
778 	else
779 		m_currentSoftwareArtworkIndex++;
780 	if ( m_currentSoftwareArtworkIndex >= 0 && m_currentSoftwareArtworkIndex < artworkList.count() )
781 		return artworkList[m_currentSoftwareArtworkIndex];
782 	else {
783 		m_currentSoftwareArtworkIndex = -2;
784 		return QString();
785 	}
786 }
787 
previousCustomSoftwareArtwork()788 QString TweakedQmlApplicationViewer::previousCustomSoftwareArtwork()
789 {
790 	QStringList artworkList = customSoftwareArtwork();
791 	if ( m_currentSoftwareArtworkIndex == -2 )
792 		m_currentSoftwareArtworkIndex = artworkList.count() - 1;
793 	else
794 		m_currentSoftwareArtworkIndex--;
795 	if ( m_currentSoftwareArtworkIndex >= 0 && m_currentSoftwareArtworkIndex < artworkList.count() )
796 		return artworkList[m_currentSoftwareArtworkIndex];
797 	else {
798 		m_currentSoftwareArtworkIndex = -2;
799 		return QString();
800 	}
801 }
802 
803 #if QT_VERSION >= 0x050000
handleQuit()804 void TweakedQmlApplicationViewer::handleQuit()
805 {
806 	QMC2_ARCADE_LOG_STR(tr("Stopping QML viewer"));
807 
808 	if ( consoleWindow ) {
809 		QString consoleMessage(tr("QML viewer stopped - please close the console window to exit"));
810 		QMC2_ARCADE_LOG_STR(QString("-").repeated(consoleMessage.length()));
811 		QMC2_ARCADE_LOG_STR(consoleMessage);
812 		QMC2_ARCADE_LOG_STR(QString("-").repeated(consoleMessage.length()));
813 		consoleWindow->showNormal();
814 		consoleWindow->raise();
815 	}
816 
817 	close();
818 }
819 #else
paintEvent(QPaintEvent * e)820 void TweakedQmlApplicationViewer::paintEvent(QPaintEvent *e)
821 {
822 	QmlApplicationViewer::paintEvent(e);
823 	numFrames++;
824 }
825 
closeEvent(QCloseEvent * e)826 void TweakedQmlApplicationViewer::closeEvent(QCloseEvent *e)
827 {
828 	QMC2_ARCADE_LOG_STR(tr("Stopping QML viewer"));
829 
830 	if ( consoleWindow ) {
831 		QString consoleMessage(tr("QML viewer stopped - please close the console window to exit"));
832 		QMC2_ARCADE_LOG_STR(QString("-").repeated(consoleMessage.length()));
833 		QMC2_ARCADE_LOG_STR(consoleMessage);
834 		QMC2_ARCADE_LOG_STR(QString("-").repeated(consoleMessage.length()));
835 		consoleWindow->showNormal();
836 		consoleWindow->raise();
837 	}
838 	e->accept();
839 }
840 #endif
841