1 #include <QTranslator>
2 #include <QIcon>
3 #include <QStyleFactory>
4 #include <QTimer>
5 #include <QString>
6 #include <QUrl>
7 #if QT_VERSION < 0x050000
8 #include <QApplication>
9 #else
10 #include <QGuiApplication>
11 #endif
12 
13 #include "arcadesettings.h"
14 #include "tweakedqmlappviewer.h"
15 #include "consolewindow.h"
16 #include "macros.h"
17 #include "joystick.h"
18 #include "keyeventfilter.h"
19 #if defined(QMC2_ARCADE_OS_WIN)
20 #include "../windows_tools.h"
21 #endif
22 #if defined(QMC2_ARCADE_OS_MAC)
23 #include <mach-o/dyld.h>
24 #include <QFileInfo>
25 #include <QDir>
26 #endif
27 
28 ArcadeSettings *globalConfig = 0;
29 ConsoleWindow *consoleWindow = 0;
30 int emulatorMode = QMC2_ARCADE_EMUMODE_MAME;
31 QStringList emulatorModes;
32 QStringList arcadeThemes;
33 QStringList mameThemes;
34 QStringList consoleModes;
35 #if QT_VERSION < 0x050000
36 QStringList graphicsSystems;
37 #endif
38 QStringList argumentList;
39 bool runApp = true;
40 bool debugKeys = false;
41 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
42 bool debugJoy = false;
43 #endif
44 bool debugQt = false;
45 
46 #if QT_VERSION < 0x050000
qtMessageHandler(QtMsgType type,const char * msg)47 void qtMessageHandler(QtMsgType type, const char *msg)
48 #else
49 void qtMessageHandler(QtMsgType type, const QMessageLogContext &, const QString &msg)
50 #endif
51 {
52 	if ( !runApp )
53 		return;
54 
55 	QString msgString;
56 
57 	switch ( type ) {
58 	case QtDebugMsg:
59 		if ( !debugQt )
60 			return;
61 		msgString = "QtDebugMsg: " + QString(msg);
62 		break;
63 	case QtWarningMsg:
64 		msgString = "QtWarningMsg: " + QString(msg);
65 		break;
66 	case QtCriticalMsg:
67 		msgString = "QtCriticalMsg: " + QString(msg);
68 		break;
69 	case QtFatalMsg:
70 		msgString = "QtFatalMsg: " + QString(msg);
71 		break;
72 	default:
73 		return;
74 	}
75 
76 	QMC2_ARCADE_LOG_STR(msgString);
77 }
78 
showHelp()79 void showHelp()
80 {
81 #if defined(QMC2_ARCADE_OS_WIN)
82 	if ( !consoleWindow )
83 		winAllocConsole();
84 #endif
85 
86 	QString defTheme(globalConfig->defaultTheme());
87 	QString defConsole(globalConfig->defaultConsoleType());
88 #if QT_VERSION < 0x050000
89 	QString defGSys(globalConfig->defaultGraphicsSystem());
90 #endif
91 	QString defLang(globalConfig->defaultLanguage());
92 	QString defVideo(globalConfig->defaultVideo());
93 
94 	QStringList themeList;
95 	foreach (QString theme, arcadeThemes) {
96 		if ( defTheme == theme )
97 			themeList << "[" + theme + "]";
98 		else
99 			themeList << theme;
100 	}
101 	QString availableThemes(themeList.join(", "));
102 
103 	QStringList consoleList;
104 	foreach (QString console, consoleModes) {
105 		if ( defConsole == console )
106 			consoleList << "[" + console + "]";
107 		else
108 			consoleList << console;
109 	}
110 	QString availableConsoles(consoleList.join(", "));
111 
112 #if QT_VERSION < 0x050000
113 	QStringList gSysList;
114 	foreach (QString gSys, graphicsSystems) {
115 		if ( defGSys == gSys )
116 			gSysList << "[" + gSys + "]";
117 		else
118 			gSysList << gSys;
119 	}
120 	QString availableGraphicsSystems(gSysList.join(", "));
121 #endif
122 
123 	QStringList langList;
124 	foreach (QString lang, globalConfig->languageMap.keys()) {
125 		if ( defLang == lang )
126 			langList << "[" + lang + "]";
127 		else
128 			langList << lang;
129 	}
130 	QString availableLanguages(langList.join(", "));
131 
132 	QStringList videoList;
133 	foreach (QString v, QStringList() << "on" << "off") {
134 		if ( defVideo == v )
135 			videoList << "[" + v + "]";
136 		else
137 			videoList << v;
138 	}
139 	QString availableVideoSettings(videoList.join(", "));
140 
141 	QString helpMessage;
142 #if QT_VERSION < 0x050000
143 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
144 	helpMessage  = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-graphicssystem <engine>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-nojoy] [-joy <index>] [-debugjoy] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
145 #else
146 	helpMessage  = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-graphicssystem <engine>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
147 #endif
148 #else
149 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
150 	helpMessage  = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-nojoy] [-joy <index>] [-debugjoy] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
151 #else
152 	helpMessage  = "Usage: qmc2-arcade [-theme <theme>] [-console <type>] [-language <lang>] [-video <vdo>] [-config_path <path>] [-fullscreen] [-windowed] [-debugkeys] [-debugqt] [-h|-?|-help]\n\n";
153 #endif
154 #endif
155 	helpMessage += "Option           Meaning                Possible values ([..] = default)\n"
156 		       "---------------  ---------------------  --------------------------------------------------\n";
157 	helpMessage += "-theme           Theme selection        " + availableThemes + "\n";
158 	helpMessage += "-console         Console type           " + availableConsoles + "\n";
159 #if QT_VERSION < 0x050000
160 	helpMessage += "-graphicssystem  Graphics engine        " + availableGraphicsSystems + "\n";
161 #endif
162 	helpMessage += "-language        Language selection     " + availableLanguages + "\n";
163 	helpMessage += "-video           Video snap support     " + availableVideoSettings + "\n";
164 	helpMessage += QString("-config_path     Configuration path     [%1], ...\n").arg(QMC2_ARCADE_DOT_PATH);
165 	helpMessage += "-fullscreen      Full screen display    N/A\n";
166 	helpMessage += "-windowed        Windowed display       N/A\n";
167 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
168 	helpMessage += "-nojoy           Disable joystick       N/A\n";
169 	helpMessage += QString("-joy             Use given joystick     SDL joystick index number [%1]\n").arg(globalConfig->joystickIndex());
170 	helpMessage += "-debugjoy        Debug joy-mapping      N/A\n";
171 #endif
172 	helpMessage += "-debugkeys       Debug key-mapping      N/A\n";
173 	helpMessage += "-debugqt         Log Qt debug messages  N/A\n";
174 
175 #if defined(QMC2_ARCADE_OS_WIN)
176 	if ( !consoleWindow )
177 		helpMessage.remove(helpMessage.length() - 1, 1);
178 #endif
179 
180 	QMC2_ARCADE_LOG_STR_NT(helpMessage);
181 }
182 
upgradeSettings()183 void upgradeSettings()
184 {
185 	/*
186 	QStringList verList = globalConfig->value("Version").toString().split(".", QString::SkipEmptyParts);
187 	if ( verList.count() > 1 ) {
188 		int omv = verList[1].toInt();
189 		int osr = globalConfig->value("SVN_Revision").toInt();
190 		if ( QMC2_ARCADE_TEST_VERSION(omv, 57, osr, 6989) ) {
191 			QStringList oldKeys = QStringList() << "/MAME/DatInfoDatabase/GameInfoImportFiles"
192 							    << "/MAME/DatInfoDatabase/GameInfoImportDates";
193 			QStringList newKeys = QStringList() << "/MAME/DatInfoDatabase/MachineInfoImportFiles"
194 							    << "/MAME/DatInfoDatabase/MachineInfoImportDates";
195 			for (int i = 0; i < oldKeys.count(); i++) {
196 				QString oldKey = oldKeys[i];
197 				QString newKey = newKeys[i];
198 				if ( globalConfig->contains(oldKey) ) {
199 					globalConfig->setValue(newKey, globalConfig->value(oldKey));
200 					globalConfig->remove(oldKey);
201 				}
202 			}
203 		}
204 	}
205 	*/
206 }
207 
208 #if defined(QMC2_ARCADE_OS_WIN)
209 #if defined(TCOD_VISUAL_STUDIO)
SDL_main(int argc,char * argv[])210 int SDL_main(int argc, char *argv[])
211 {
212 	return main(argc, argv);
213 }
214 #endif
215 #if defined(QMC2_ARCADE_MINGW)
216 #undef main
217 #endif
218 #endif
219 
main(int argc,char * argv[])220 int main(int argc, char *argv[])
221 {
222 #if defined(QMC2_ARCADE_OS_MAC)
223 	// this hack ensures that we're using the bundled plugins rather than the ones from a Qt SDK installation
224 	char exec_path[4096];
225 	uint32_t exec_path_size = sizeof(exec_path);
226 	if ( _NSGetExecutablePath(exec_path, &exec_path_size) == 0 ) {
227 		QFileInfo fi(exec_path);
228 		QCoreApplication::addLibraryPath(fi.absoluteDir().absolutePath() + "/../PlugIns");
229 	}
230 #endif
231 
232 	qsrand(QDateTime::currentDateTime().toTime_t());
233 #if QT_VERSION < 0x050000
234 	qInstallMsgHandler(qtMessageHandler);
235 #else
236 	qInstallMessageHandler(qtMessageHandler);
237 #endif
238 
239 	// available emulator-modes, themes, console-modes and graphics-systems
240 	emulatorModes << "mame";
241 	arcadeThemes << "ToxicWaste" << "darkone";
242 	mameThemes << "ToxicWaste" << "darkone";
243 	consoleModes << "terminal" << "window" << "window-minimized" << "none";
244 #if QT_VERSION < 0x050000
245 	graphicsSystems << "raster" << "native" << "opengl" << "opengl1" << "opengl2" << "openvg";
246 #endif
247 
248 	// we have to make a copy of the command line arguments since QApplication's constructor "eats"
249 	// -graphicssystem and its value (and we *really* need to know if it has been set or not!)
250 	for (int i = 0; i < argc; i++)
251 		argumentList << argv[i];
252 
253 #if QT_VERSION < 0x050000 && !defined(QMC2_ARCADE_OS_MAC)
254 	// work-around for a weird style-related issue with GTK (we actually don't need to set a GUI style, but
255 	// somehow Qt and/or GTK do this implicitly, which may cause crashes when the automatically chosen style
256 	// isn't bug-free, so we try to fall back to a safe built-in style to avoid this)
257 	QStringList wantedStyles = QStringList() << "Plastique" << "Cleanlooks" << "Fusion" << "CDE" << "Motif" << "Windows";
258 	QStringList availableStyles = QStyleFactory::keys();
259 	int styleIndex = -1;
260 	foreach (QString style, wantedStyles) {
261 		styleIndex = availableStyles.indexOf(style);
262 		if ( styleIndex >= 0 )
263 			break;
264 	}
265 	if ( styleIndex >= 0 )
266 		QApplication::setStyle(availableStyles[styleIndex]);
267 	QApplication *tempApp = new QApplication(argc, argv);
268 #endif
269 
270 	QCoreApplication::setOrganizationName(QMC2_ARCADE_ORG_NAME);
271 	QCoreApplication::setOrganizationDomain(QMC2_ARCADE_ORG_DOMAIN);
272 	QCoreApplication::setApplicationName(QMC2_ARCADE_APP_NAME);
273 	QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, ArcadeSettings::configPath());
274 
275 #if QT_VERSION < 0x050000
276 	globalConfig = new ArcadeSettings;
277 
278 	QString gSys = globalConfig->defaultGraphicsSystem();
279 	if ( QMC2_ARCADE_CLI_GSYS_VAL )
280 		gSys = QMC2_ARCADE_CLI_GSYS;
281 
282 	delete globalConfig;
283 	globalConfig = 0;
284 
285 #if !defined(QMC2_ARCADE_OS_MAC)
286 	delete tempApp;
287 #endif
288 
289 	if ( !graphicsSystems.contains(gSys) ) {
290 		QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid graphics-system - available graphics-systems: %2").arg(gSys).arg(graphicsSystems.join(", ")));
291 		return 1;
292 	}
293 
294 	QApplication::setGraphicsSystem(gSys);
295 
296 	// create the actual application instance
297 	QScopedPointer<QApplication> app(createApplication(argc, argv));
298 #else
299 	// create the actual application instance
300 	QGuiApplication *app = new QGuiApplication(argc, argv);
301 #endif
302 
303 	if ( !QMC2_ARCADE_CLI_EMU_UNK ) {
304 		emulatorMode = QMC2_ARCADE_EMUMODE_MAME;
305 	} else if ( !emulatorModes.contains(QMC2_ARCADE_CLI_EMU) ) {
306 		QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid emulator-mode - available emulator-modes: %2").arg(QMC2_ARCADE_CLI_EMU).arg(emulatorModes.join(", ")));
307 		return 1;
308 	}
309 
310 	globalConfig = new ArcadeSettings;
311 
312 	QString console(globalConfig->defaultConsoleType());
313 	if ( QMC2_ARCADE_CLI_CONS_VAL )
314 		console = QMC2_ARCADE_CLI_CONS;
315 
316 	if ( console == "window" || console == "window-minimized" ) {
317 		TweakedQmlApplicationViewer::consoleMode = console == "window" ? QMC2_ARCADE_CONSOLE_WIN : QMC2_ARCADE_CONSOLE_WINMIN;
318 		consoleWindow = new ConsoleWindow(0);
319 		if ( TweakedQmlApplicationViewer::consoleMode == QMC2_ARCADE_CONSOLE_WINMIN )
320 			consoleWindow->showMinimized();
321 		else
322 			consoleWindow->show();
323 	} else if ( !consoleModes.contains(console) ) {
324 		QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid console-mode - available console-modes: %2").arg(console).arg(consoleModes.join(", ")));
325 		return 1;
326 	} else
327 		TweakedQmlApplicationViewer::consoleMode = console == "terminal" ? QMC2_ARCADE_CONSOLE_TERM : QMC2_ARCADE_CONSOLE_NONE;
328 
329 	if ( QMC2_ARCADE_CLI_HELP || QMC2_ARCADE_CLI_INVALID ) {
330 		showHelp();
331 		if ( !consoleWindow ) {
332 			delete globalConfig;
333 			return 1;
334 		} else
335 			runApp = false;
336 	}
337 
338 #if defined(QMC2_ARCADE_OS_WIN)
339 	if ( console == "terminal" )
340 		winAllocConsole();
341 #endif
342 
343 	QString theme(globalConfig->defaultTheme());
344 	if ( QMC2_ARCADE_CLI_THEME_VAL )
345 		theme = QMC2_ARCADE_CLI_THEME;
346 
347 	if ( !arcadeThemes.contains(theme) && runApp ) {
348 		QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not valid theme - available themes: %2").arg(theme).arg(arcadeThemes.join(", ")));
349 		if ( !consoleWindow ) {
350 			delete globalConfig;
351 			return 1;
352 		} else
353 			runApp = false;
354 	}
355 
356 	delete globalConfig;
357 	globalConfig = 0;
358 
359 	switch ( emulatorMode ) {
360 	case QMC2_ARCADE_EMUMODE_MAME:
361 	default:
362 		if ( !mameThemes.contains(theme) && runApp ) {
363 			QMC2_ARCADE_LOG_STR_NT(QObject::tr("%1 is not a valid %2 theme - available %2 themes: %3").arg(theme).arg(emulatorModes[QMC2_ARCADE_EMUMODE_MAME]).arg(mameThemes.isEmpty() ? QObject::tr("(none)") : mameThemes.join(", ")));
364 			if ( !consoleWindow )
365 				return 1;
366 			else
367 				runApp = false;
368 		}
369 		break;
370 	}
371 
372 	// create final instance of the global settings object
373 	globalConfig = new ArcadeSettings(theme);
374 	upgradeSettings();
375 	globalConfig->setApplicationVersion(QMC2_ARCADE_APP_VERSION);
376 
377 	// set default font
378 	QString font(globalConfig->defaultFont());
379 	if ( !font.isEmpty() ) {
380 		QFont f;
381 		f.fromString(font);
382 		app->setFont(f);
383 	}
384 
385 	// set language
386 	QString language(globalConfig->defaultLanguage());
387 	if ( QMC2_ARCADE_CLI_LANG_VAL )
388 		language = QMC2_ARCADE_CLI_LANG;
389 	if ( !globalConfig->languageMap.contains(language) ) {
390 		if ( QMC2_ARCADE_CLI_LANG_VAL ) {
391 			QMC2_ARCADE_LOG_STR_NT(QString("%1 is not a valid language - available languages: %2").arg(language).arg(QStringList(globalConfig->languageMap.keys()).join(", ")));
392 			delete globalConfig;
393 			return 1;
394 		} else
395 			language = "us";
396 	}
397 
398 	// load translator
399 	QTranslator qmc2ArcadeTranslator;
400 	if ( qmc2ArcadeTranslator.load(QString("qmc2-arcade_%1").arg(language), ":/translations") )
401 		app->installTranslator(&qmc2ArcadeTranslator);
402 
403 	int returnCode;
404 
405 	if ( runApp ) {
406 		// log banner message
407 		QString bannerMessage = QString("%1 %2 (%3)").
408 				arg(QMC2_ARCADE_APP_TITLE).
409 #if defined(QMC2_ARCADE_SVN_REV)
410 #if QMC2_ARCADE_SVN_REV > 0
411 				arg(QMC2_ARCADE_APP_VERSION + QString(", SVN r%1").arg(XSTR(QMC2_ARCADE_SVN_REV))).
412 #else
413 				arg(QMC2_ARCADE_APP_VERSION).
414 #endif
415 #else
416 				arg(QMC2_ARCADE_APP_VERSION).
417 #endif
418 				arg(QString("Qt") + " " + qVersion() + ", " +
419 				    QObject::tr("emulator-mode: %1").arg(emulatorModes[emulatorMode]) + ", " +
420 				    QObject::tr("console-mode: %1").arg(consoleModes[TweakedQmlApplicationViewer::consoleMode]) + ", " +
421 #if QT_VERSION < 0x050000
422 				    QObject::tr("graphics-system: %1").arg(gSys) + ", " +
423 #endif
424 				    QObject::tr("language: %1").arg(language) + ", " +
425 				    QObject::tr("theme: %1").arg(theme));
426 
427 		QMC2_ARCADE_LOG_STR(bannerMessage);
428 
429 		if ( consoleWindow )
430 			consoleWindow->loadSettings();
431 
432 		// debug options
433 #if defined(QMC2_ARCADE_ENABLE_JOYSTICK)
434 		debugJoy = QMC2_ARCADE_CLI_DEBUG_JOY;
435 #endif
436 		debugKeys = QMC2_ARCADE_CLI_DEBUG_KEYS;
437 		debugQt = QMC2_ARCADE_CLI_DEBUG_QT;
438 
439 		// set up the main QML app viewer window
440 		TweakedQmlApplicationViewer *viewer = new TweakedQmlApplicationViewer();
441 
442 		// install our key-event filter to remap key-sequences, if applicable
443 		KeyEventFilter keyEventFilter(viewer->keySequenceMap);
444 		app->installEventFilter(&keyEventFilter);
445 
446 #if QT_VERSION < 0x050000
447 		viewer->setWindowTitle(QMC2_ARCADE_APP_TITLE + " " + QMC2_ARCADE_APP_VERSION + " [Qt " + qVersion() + "]");
448 		viewer->setWindowIcon(QIcon(QLatin1String(":/images/qmc2-arcade.png")));
449 		viewer->setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
450 		viewer->setStyleSheet("background-color: black");
451 #else
452 		viewer->setTitle(QMC2_ARCADE_APP_TITLE + " " + QMC2_ARCADE_APP_VERSION + " [Qt " + qVersion() + "]");
453 		viewer->winId(); // see QTBUG-33370 QQuickView does not set icon correctly
454 		viewer->setIcon(QIcon(QLatin1String(":/images/qmc2-arcade.png")));
455 		viewer->setColor(QColor(0, 0, 0, 255));
456 #endif
457 
458 		bool initialFullScreen = globalConfig->fullScreen();
459 		if ( QMC2_ARCADE_CLI_FULLSCREEN )
460 			initialFullScreen = true;
461 		else if ( QMC2_ARCADE_CLI_WINDOWED )
462 			initialFullScreen = false;
463 
464 		// setup viewer params
465 		viewer->setInitialFullScreen(initialFullScreen);
466 		viewer->setVideoEnabled(globalConfig->defaultVideo() == "on");
467 		if ( QMC2_ARCADE_CLI_VIDEO_VAL )
468 			viewer->setVideoEnabled(QMC2_ARCADE_CLI_VIDEO == "on");
469 
470 		QMC2_ARCADE_LOG_STR(QObject::tr("Starting QML viewer using theme '%1'").arg(theme) + " (" + QObject::tr("video snaps %1").arg(viewer->videoEnabled() ? QObject::tr("enabled") : QObject::tr("disabled")) + ")");
471 
472 		// load theme
473 		QString themeUrl;
474 #if QT_VERSION < 0x050000
475 		if ( viewer->videoEnabled() )
476 			themeUrl = QString("qrc:/qml/%1/1.1/%1-video.qml").arg(theme);
477 		else
478 			themeUrl = QString("qrc:/qml/%1/1.1/%1.qml").arg(theme);
479 #else
480 		if ( viewer->videoEnabled() )
481 			themeUrl = QString("qrc:/qml/%1/2.0/%1-video.qml").arg(theme);
482 		else
483 			themeUrl = QString("qrc:/qml/%1/2.0/%1.qml").arg(theme);
484 #endif
485 		viewer->setSource(QUrl(themeUrl));
486 
487 		// delayed setup of the initial display mode
488 		QTimer::singleShot(100, viewer, SLOT(displayInit()));
489 
490 		// run the event loop
491 		returnCode = app->exec();
492 
493 		// remove the key-event filter before destroying the viewer, otherwise there's a small possibility
494 		// for an exit-crash because the event-filter uses the key-sequence-map instance from the viewer
495 		app->removeEventFilter(&keyEventFilter);
496 		delete viewer;
497 	} else {
498 		if ( consoleWindow ) {
499 			consoleWindow->loadSettings();
500 			QString consoleMessage(QObject::tr("QML viewer not started - please close the console window to exit"));
501 			QMC2_ARCADE_LOG_STR_NT(QString("-").repeated(consoleMessage.length()));
502 			QMC2_ARCADE_LOG_STR_NT(consoleMessage);
503 			QMC2_ARCADE_LOG_STR_NT(QString("-").repeated(consoleMessage.length()));
504 			consoleWindow->showNormal();
505 			consoleWindow->raise();
506 			app->exec();
507 		}
508 		returnCode = 1;
509 	}
510 
511 	if ( consoleWindow ) {
512 		consoleWindow->saveSettings();
513 		delete consoleWindow;
514 	}
515 
516 	delete globalConfig;
517 
518 	return returnCode;
519 }
520