all repos — mgba @ 5665ac0316df4800bec0e942e4d18ba3fec59310

mGBA Game Boy Advance Emulator

src/platform/qt/GameController.cpp (view raw)

   1/* Copyright (c) 2013-2014 Jeffrey Pfau
   2 *
   3 * This Source Code Form is subject to the terms of the Mozilla Public
   4 * License, v. 2.0. If a copy of the MPL was not distributed with this
   5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
   6#include "GameController.h"
   7
   8#include "AudioProcessor.h"
   9#include "InputController.h"
  10#include "LogController.h"
  11#include "MultiplayerController.h"
  12#include "VFileDevice.h"
  13
  14#include <QCoreApplication>
  15#include <QDateTime>
  16
  17#include <ctime>
  18
  19#include <mgba/core/config.h>
  20#include <mgba/core/directories.h>
  21#include <mgba/core/serialize.h>
  22#include <mgba/core/tile-cache.h>
  23#ifdef M_CORE_GBA
  24#include <mgba/gba/interface.h>
  25#include <mgba/internal/gba/gba.h>
  26#include <mgba/gba/core.h>
  27#include <mgba/internal/gba/renderers/tile-cache.h>
  28#include <mgba/internal/gba/sharkport.h>
  29#endif
  30#ifdef M_CORE_GB
  31#include <mgba/internal/gb/gb.h>
  32#include <mgba/internal/gb/renderers/tile-cache.h>
  33#endif
  34#include <mgba-util/vfs.h>
  35#include "feature/video-logger.h"
  36
  37using namespace QGBA;
  38using namespace std;
  39
  40GameController::GameController(QObject* parent)
  41	: QObject(parent)
  42	, m_drawContext(nullptr)
  43	, m_frontBuffer(nullptr)
  44	, m_threadContext()
  45	, m_activeKeys(0)
  46	, m_inactiveKeys(0)
  47	, m_logLevels(0)
  48	, m_gameOpen(false)
  49	, m_vf(nullptr)
  50	, m_useBios(false)
  51	, m_audioProcessor(AudioProcessor::create())
  52	, m_pauseAfterFrame(false)
  53	, m_sync(true)
  54	, m_videoSync(VIDEO_SYNC)
  55	, m_audioSync(AUDIO_SYNC)
  56	, m_fpsTarget(-1)
  57	, m_turbo(false)
  58	, m_turboForced(false)
  59	, m_turboSpeed(-1)
  60	, m_wasPaused(false)
  61	, m_audioChannels()
  62	, m_videoLayers()
  63	, m_autofire{}
  64	, m_autofireStatus{}
  65	, m_inputController(nullptr)
  66	, m_multiplayer(nullptr)
  67	, m_stream(nullptr)
  68	, m_stateSlot(1)
  69	, m_backupLoadState(nullptr)
  70	, m_backupSaveState(nullptr)
  71	, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC)
  72	, m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC)
  73	, m_preload(false)
  74	, m_override(nullptr)
  75	, m_vl(nullptr)
  76{
  77#ifdef M_CORE_GBA
  78	m_lux.p = this;
  79	m_lux.sample = [](GBALuminanceSource* context) {
  80		GameControllerLux* lux = static_cast<GameControllerLux*>(context);
  81		lux->value = 0xFF - lux->p->m_luxValue;
  82	};
  83
  84	m_lux.readLuminance = [](GBALuminanceSource* context) {
  85		GameControllerLux* lux = static_cast<GameControllerLux*>(context);
  86		return lux->value;
  87	};
  88	setLuminanceLevel(0);
  89#endif
  90
  91	m_threadContext.startCallback = [](mCoreThread* context) {
  92		GameController* controller = static_cast<GameController*>(context->userData);
  93		context->core->setPeripheral(context->core, mPERIPH_ROTATION, controller->m_inputController->rotationSource());
  94		context->core->setPeripheral(context->core, mPERIPH_RUMBLE, controller->m_inputController->rumble());
  95
  96		for (size_t i = 0; i < controller->m_audioChannels.size(); ++i) {
  97			context->core->enableAudioChannel(context->core, i, controller->m_audioChannels[i]);
  98		}
  99		for (size_t i = 0; i < controller->m_videoLayers.size(); ++i) {
 100			context->core->enableVideoLayer(context->core, i, controller->m_videoLayers[i]);
 101		}
 102
 103		switch (context->core->platform(context->core)) {
 104#ifdef M_CORE_GBA
 105		case PLATFORM_GBA:
 106			context->core->setPeripheral(context->core, mPERIPH_GBA_LUMINANCE, &controller->m_lux);
 107			break;
 108#endif
 109		default:
 110			break;
 111		}
 112		controller->m_fpsTarget = context->sync.fpsTarget;
 113
 114		if (controller->m_override) {
 115			controller->m_override->identify(context->core);
 116			controller->m_override->apply(context->core);
 117		}
 118
 119		if (mCoreLoadState(context->core, 0, controller->m_loadStateFlags)) {
 120			mCoreDeleteState(context->core, 0);
 121		}
 122
 123		controller->m_gameOpen = true;
 124		if (controller->m_multiplayer) {
 125			controller->m_multiplayer->attachGame(controller);
 126		}
 127
 128		QString path = controller->m_fname;
 129		if (!controller->m_fsub.isEmpty()) {
 130			path += QDir::separator() + controller->m_fsub;
 131		}
 132		QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(mCoreThread*, context), Q_ARG(const QString&, path));
 133		QMetaObject::invokeMethod(controller, "startAudio");
 134	};
 135
 136	m_threadContext.resetCallback = [](mCoreThread* context) {
 137		GameController* controller = static_cast<GameController*>(context->userData);
 138		for (auto action : controller->m_resetActions) {
 139			action();
 140		}
 141		controller->m_resetActions.clear();
 142
 143		unsigned width, height;
 144		controller->m_threadContext.core->desiredVideoDimensions(controller->m_threadContext.core, &width, &height);
 145		memset(controller->m_frontBuffer, 0xFF, width * height * BYTES_PER_PIXEL);
 146		QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer));
 147		if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
 148			mCoreThreadPauseFromThread(context);
 149			QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(mCoreThread*, context));
 150		}
 151	};
 152
 153	m_threadContext.cleanCallback = [](mCoreThread* context) {
 154		GameController* controller = static_cast<GameController*>(context->userData);
 155
 156		if (controller->m_multiplayer) {
 157			controller->m_multiplayer->detachGame(controller);
 158		}
 159		controller->m_patch = QString();
 160		controller->clearOverride();
 161		controller->endVideoLog();
 162
 163		QMetaObject::invokeMethod(controller->m_audioProcessor, "pause");
 164
 165		QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(mCoreThread*, context));
 166		QMetaObject::invokeMethod(controller, "cleanGame");
 167	};
 168
 169	m_threadContext.frameCallback = [](mCoreThread* context) {
 170		GameController* controller = static_cast<GameController*>(context->userData);
 171		unsigned width, height;
 172		controller->m_threadContext.core->desiredVideoDimensions(controller->m_threadContext.core, &width, &height);
 173		memcpy(controller->m_frontBuffer, controller->m_drawContext, width * height * BYTES_PER_PIXEL);
 174		QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer));
 175
 176		// If no one is using the tile cache, disable it
 177		if (controller->m_tileCache && controller->m_tileCache.unique()) {
 178			switch (controller->platform()) {
 179#ifdef M_CORE_GBA
 180			case PLATFORM_GBA: {
 181				GBA* gba = static_cast<GBA*>(context->core->board);
 182				gba->video.renderer->cache = nullptr;
 183				break;
 184			}
 185#endif
 186#ifdef M_CORE_GB
 187			case PLATFORM_GB: {
 188				GB* gb = static_cast<GB*>(context->core->board);
 189				gb->video.renderer->cache = nullptr;
 190				break;
 191			}
 192#endif
 193			default:
 194				break;
 195			}
 196			controller->m_tileCache.reset();
 197		}
 198
 199
 200		if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
 201			mCoreThreadPauseFromThread(context);
 202			QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(mCoreThread*, context));
 203		}
 204	};
 205
 206	m_threadContext.sleepCallback = [](mCoreThread* context) {
 207		if (!context) {
 208			return;
 209		}
 210		GameController* controller = static_cast<GameController*>(context->userData);
 211		if (!mCoreSaveState(context->core, 0, controller->m_saveStateFlags)) {
 212			return;
 213		}
 214		QMetaObject::invokeMethod(controller, "closeGame");
 215	};
 216
 217	m_threadContext.logger.d.log = [](mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
 218		mThreadLogger* logContext = reinterpret_cast<mThreadLogger*>(logger);
 219		mCoreThread* context = logContext->p;
 220
 221		static const char* savestateMessage = "State %i loaded";
 222		static const char* savestateFailedMessage = "State %i failed to load";
 223		static int biosCat = -1;
 224		static int statusCat = -1;
 225		if (!context) {
 226			return;
 227		}
 228		GameController* controller = static_cast<GameController*>(context->userData);
 229		QString message;
 230		if (biosCat < 0) {
 231			biosCat = mLogCategoryById("gba.bios");
 232		}
 233		if (statusCat < 0) {
 234			statusCat = mLogCategoryById("core.status");
 235		}
 236#ifdef M_CORE_GBA
 237		if (level == mLOG_STUB && category == biosCat) {
 238			va_list argc;
 239			va_copy(argc, args);
 240			int immediate = va_arg(argc, int);
 241			va_end(argc);
 242			QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate));
 243		} else
 244#endif
 245		if (category == statusCat) {
 246			// Slot 0 is reserved for suspend points
 247			if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {
 248				va_list argc;
 249				va_copy(argc, args);
 250				int slot = va_arg(argc, int);
 251				va_end(argc);
 252				if (slot == 0) {
 253					format = "Loaded suspend state";
 254				}
 255			} else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0) {
 256				va_list argc;
 257				va_copy(argc, args);
 258				int slot = va_arg(argc, int);
 259				va_end(argc);
 260				if (slot == 0) {
 261					return;
 262				}
 263			}
 264			message = QString().vsprintf(format, args);
 265			QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
 266		}
 267		if (level == mLOG_FATAL) {
 268			mCoreThreadMarkCrashed(controller->thread());
 269			QMetaObject::invokeMethod(controller, "crashGame", Q_ARG(const QString&, QString().vsprintf(format, args)));
 270		} else if (!(controller->m_logLevels & level)) {
 271			return;
 272		}
 273		message = QString().vsprintf(format, args);
 274		QMetaObject::invokeMethod(controller, "postLog", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message));
 275	};
 276
 277	m_threadContext.userData = this;
 278
 279	connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause()));
 280	connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*)));
 281	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents()));
 282	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateAutofire()));
 283}
 284
 285GameController::~GameController() {
 286	disconnect();
 287	closeGame();
 288	clearMultiplayerController();
 289	delete m_backupLoadState;
 290}
 291
 292void GameController::setMultiplayerController(MultiplayerController* controller) {
 293	if (controller == m_multiplayer) {
 294		return;
 295	}
 296	clearMultiplayerController();
 297	m_multiplayer = controller;
 298	if (isLoaded()) {
 299		mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* thread) {
 300			GameController* controller = static_cast<GameController*>(thread->userData);
 301			controller->m_multiplayer->attachGame(controller);
 302		});
 303	}
 304}
 305
 306void GameController::clearMultiplayerController() {
 307	if (!m_multiplayer) {
 308		return;
 309	}
 310	m_multiplayer->detachGame(this);
 311	m_multiplayer = nullptr;
 312}
 313
 314void GameController::setOverride(Override* override) {
 315	m_override = override;
 316	if (isLoaded()) {
 317		Interrupter interrupter(this);
 318		m_override->identify(m_threadContext.core);
 319	}
 320}
 321
 322void GameController::clearOverride() {
 323	delete m_override;
 324	m_override = nullptr;
 325}
 326
 327void GameController::setConfig(const mCoreConfig* config) {
 328	m_config = config;
 329	if (isLoaded()) {
 330		Interrupter interrupter(this);
 331		mCoreLoadForeignConfig(m_threadContext.core, config);
 332		m_audioSync = m_threadContext.sync.audioWait;
 333		m_videoSync = m_threadContext.sync.videoFrameWait;
 334		m_audioProcessor->setInput(&m_threadContext);
 335	}
 336}
 337
 338#ifdef USE_DEBUGGERS
 339mDebugger* GameController::debugger() {
 340	if (!isLoaded()) {
 341		return nullptr;
 342	}
 343	return m_threadContext.core->debugger;
 344}
 345
 346void GameController::setDebugger(mDebugger* debugger) {
 347	Interrupter interrupter(this);
 348	if (debugger) {
 349		mDebuggerAttach(debugger, m_threadContext.core);
 350	} else {
 351		m_threadContext.core->detachDebugger(m_threadContext.core);
 352	}
 353}
 354#endif
 355
 356void GameController::loadGame(const QString& path) {
 357	closeGame();
 358	QFileInfo info(path);
 359	if (!info.isReadable()) {
 360		QString fname = info.fileName();
 361		QString base = info.path();
 362		if (base.endsWith("/") || base.endsWith(QDir::separator())) {
 363			base.chop(1);
 364		}
 365		VDir* dir = VDirOpenArchive(base.toUtf8().constData());
 366		if (dir) {
 367			VFile* vf = dir->openFile(dir, fname.toUtf8().constData(), O_RDONLY);
 368			if (vf) {
 369				struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf));
 370				uint8_t buffer[2048];
 371				ssize_t read;
 372				while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
 373					vfclone->write(vfclone, buffer, read);
 374				}
 375				vf->close(vf);
 376				vf = vfclone;
 377			}
 378			dir->close(dir);
 379			loadGame(vf, fname, base);
 380		} else {
 381			LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
 382		}
 383		return;
 384	} else {
 385		m_fname = info.canonicalFilePath();
 386		m_fsub = QString();
 387	}
 388	m_vf = nullptr;
 389	openGame();
 390}
 391
 392void GameController::loadGame(VFile* vf, const QString& path, const QString& base) {
 393	closeGame();
 394	QFileInfo info(base);
 395	if (info.isDir()) {
 396		m_fname = QFileInfo(base + '/' + path).canonicalFilePath();
 397		m_fsub = QString();
 398	} else {
 399		m_fname = info.canonicalFilePath();
 400		m_fsub = path;
 401	}
 402	m_vf = vf;
 403	openGame();
 404}
 405
 406void GameController::bootBIOS() {
 407	closeGame();
 408	m_fname = QString();
 409	openGame(true);
 410}
 411
 412void GameController::openGame(bool biosOnly) {
 413	if (m_fname.isEmpty()) {
 414		biosOnly = true;
 415	}
 416	if (isLoaded()) {
 417		// We need to delay if the game is still cleaning up
 418		QTimer::singleShot(10, this, SLOT(openGame()));
 419		return;
 420	} else if(m_gameOpen) {
 421		cleanGame();
 422	}
 423
 424	m_threadContext.core = nullptr;
 425	if (!biosOnly) {
 426		if (m_vf) {
 427			m_threadContext.core = mCoreFindVF(m_vf);
 428		} else {
 429			m_threadContext.core = mCoreFind(m_fname.toUtf8().constData());
 430		}
 431#ifdef M_CORE_GBA
 432	} else {
 433		m_threadContext.core = GBACoreCreate();
 434#endif
 435	}
 436
 437	if (!m_threadContext.core) {
 438		return;
 439	}
 440
 441	m_pauseAfterFrame = false;
 442
 443	if (m_turbo) {
 444		m_threadContext.sync.videoFrameWait = false;
 445		m_threadContext.sync.audioWait = false;
 446	} else {
 447		m_threadContext.sync.videoFrameWait = m_videoSync;
 448		m_threadContext.sync.audioWait = m_audioSync;
 449	}
 450	m_threadContext.core->init(m_threadContext.core);
 451	mCoreInitConfig(m_threadContext.core, nullptr);
 452
 453	unsigned width, height;
 454	m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
 455	m_drawContext = new uint32_t[width * height];
 456	m_frontBuffer = new uint32_t[width * height];
 457
 458	if (m_config) {
 459		mCoreLoadForeignConfig(m_threadContext.core, m_config);
 460	}
 461
 462	QByteArray bytes;
 463	if (!biosOnly) {
 464		bytes = m_fname.toUtf8();
 465		if (m_preload) {
 466			if (m_vf) {
 467				mCorePreloadVF(m_threadContext.core, m_vf);
 468			} else {
 469				mCorePreloadFile(m_threadContext.core, bytes.constData());
 470				mDirectorySetDetachBase(&m_threadContext.core->dirs);
 471			}
 472		} else {
 473			if (m_vf) {
 474				m_threadContext.core->loadROM(m_threadContext.core, m_vf);
 475			} else {
 476				mCoreLoadFile(m_threadContext.core, bytes.constData());
 477				mDirectorySetDetachBase(&m_threadContext.core->dirs);
 478			}
 479		}
 480	} else {
 481		bytes = m_bios.toUtf8();
 482	}
 483	if (bytes.isNull()) {
 484		return;
 485	}
 486
 487	char dirname[PATH_MAX];
 488	separatePath(bytes.constData(), dirname, m_threadContext.core->dirs.baseName, 0);
 489	mDirectorySetAttachBase(&m_threadContext.core->dirs, VDirOpen(dirname));
 490
 491	m_threadContext.core->setVideoBuffer(m_threadContext.core, m_drawContext, width);
 492
 493	m_inputController->recalibrateAxes();
 494	memset(m_drawContext, 0xF8, width * height * 4);
 495
 496	m_threadContext.core->setAVStream(m_threadContext.core, m_stream);
 497
 498	if (!biosOnly) {
 499		mCoreAutoloadSave(m_threadContext.core);
 500		if (!m_patch.isNull()) {
 501			VFile* patch = VFileDevice::open(m_patch, O_RDONLY);
 502			if (patch) {
 503				m_threadContext.core->loadPatch(m_threadContext.core, patch);
 504			}
 505			patch->close(patch);
 506		} else {
 507			mCoreAutoloadPatch(m_threadContext.core);
 508		}
 509	}
 510	m_vf = nullptr;
 511
 512	if (!mCoreThreadStart(&m_threadContext)) {
 513		emit gameFailed();
 514	}
 515}
 516
 517void GameController::loadBIOS(int platform, const QString& path) {
 518	if (m_bios == path) {
 519		return;
 520	}
 521	if (!m_bios.isNull() && m_gameOpen && this->platform() == platform) {
 522		closeGame();
 523		m_bios = path;
 524		openGame();
 525	} else if (!m_gameOpen || m_bios.isNull()) {
 526		m_bios = path;
 527	}
 528}
 529
 530void GameController::loadSave(const QString& path, bool temporary) {
 531	if (!isLoaded()) {
 532		return;
 533	}
 534	m_resetActions.append([this, path, temporary]() {
 535		VFile* vf = VFileDevice::open(path, temporary ? O_RDONLY : O_RDWR);
 536		if (!vf) {
 537			LOG(QT, ERROR) << tr("Failed to open save file: %1").arg(path);
 538			return;
 539		}
 540
 541		if (temporary) {
 542			m_threadContext.core->loadTemporarySave(m_threadContext.core, vf);
 543		} else {
 544			m_threadContext.core->loadSave(m_threadContext.core, vf);
 545		}
 546	});
 547	reset();
 548}
 549
 550void GameController::yankPak() {
 551	if (!m_gameOpen) {
 552		return;
 553	}
 554	Interrupter interrupter(this);
 555	GBAYankROM(static_cast<GBA*>(m_threadContext.core->board));
 556}
 557
 558void GameController::replaceGame(const QString& path) {
 559	if (!m_gameOpen) {
 560		return;
 561	}
 562
 563	QFileInfo info(path);
 564	if (!info.isReadable()) {
 565		LOG(QT, ERROR) << tr("Failed to open game file: %1").arg(path);
 566		return;
 567	}
 568	m_fname = info.canonicalFilePath();
 569	Interrupter interrupter(this);
 570	mDirectorySetDetachBase(&m_threadContext.core->dirs);
 571	mCoreLoadFile(m_threadContext.core, m_fname.toLocal8Bit().constData());
 572}
 573
 574void GameController::loadPatch(const QString& path) {
 575	if (m_gameOpen) {
 576		closeGame();
 577		m_patch = path;
 578		openGame();
 579	} else {
 580		m_patch = path;
 581	}
 582}
 583
 584void GameController::importSharkport(const QString& path) {
 585	if (!isLoaded()) {
 586		return;
 587	}
 588#ifdef M_CORE_GBA
 589	if (platform() != PLATFORM_GBA) {
 590		return;
 591	}
 592	VFile* vf = VFileDevice::open(path, O_RDONLY);
 593	if (!vf) {
 594		LOG(QT, ERROR) << tr("Failed to open snapshot file for reading: %1").arg(path);
 595		return;
 596	}
 597	threadInterrupt();
 598	GBASavedataImportSharkPort(static_cast<GBA*>(m_threadContext.core->board), vf, false);
 599	threadContinue();
 600	vf->close(vf);
 601#endif
 602}
 603
 604void GameController::exportSharkport(const QString& path) {
 605	if (!isLoaded()) {
 606		return;
 607	}
 608#ifdef M_CORE_GBA
 609	if (platform() != PLATFORM_GBA) {
 610		return;
 611	}
 612	VFile* vf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
 613	if (!vf) {
 614		LOG(QT, ERROR) << tr("Failed to open snapshot file for writing: %1").arg(path);
 615		return;
 616	}
 617	threadInterrupt();
 618	GBASavedataExportSharkPort(static_cast<GBA*>(m_threadContext.core->board), vf);
 619	threadContinue();
 620	vf->close(vf);
 621#endif
 622}
 623
 624void GameController::closeGame() {
 625	if (!m_gameOpen) {
 626		return;
 627	}
 628#ifdef USE_DEBUGGERS
 629	setDebugger(nullptr);
 630#endif
 631	if (mCoreThreadIsPaused(&m_threadContext)) {
 632		mCoreThreadUnpause(&m_threadContext);
 633	}
 634	mCoreThreadEnd(&m_threadContext);
 635}
 636
 637void GameController::cleanGame() {
 638	if (!m_gameOpen || mCoreThreadIsActive(&m_threadContext)) {
 639		return;
 640	}
 641
 642	m_audioProcessor->pause();
 643	mCoreThreadJoin(&m_threadContext);
 644
 645	if (m_tileCache) {
 646		mTileCacheDeinit(m_tileCache.get());
 647		m_tileCache.reset();
 648	}
 649
 650	delete[] m_drawContext;
 651	delete[] m_frontBuffer;
 652
 653	m_threadContext.core->deinit(m_threadContext.core);
 654	m_threadContext.core = nullptr;
 655	m_gameOpen = false;
 656}
 657
 658void GameController::crashGame(const QString& crashMessage) {
 659	closeGame();
 660	emit gameCrashed(crashMessage);
 661}
 662
 663bool GameController::isPaused() {
 664	if (!m_gameOpen) {
 665		return false;
 666	}
 667	return mCoreThreadIsPaused(&m_threadContext);
 668}
 669
 670mPlatform GameController::platform() const {
 671	if (!m_gameOpen) {
 672		return PLATFORM_NONE;
 673	}
 674	return m_threadContext.core->platform(m_threadContext.core);
 675}
 676
 677QSize GameController::screenDimensions() const {
 678	if (!m_gameOpen) {
 679		return QSize();
 680	}
 681	unsigned width, height;
 682	m_threadContext.core->desiredVideoDimensions(m_threadContext.core, &width, &height);
 683
 684	return QSize(width, height);
 685}
 686
 687void GameController::setPaused(bool paused) {
 688	if (!isLoaded() || paused == mCoreThreadIsPaused(&m_threadContext)) {
 689		return;
 690	}
 691	m_wasPaused = paused;
 692	if (paused) {
 693		m_pauseAfterFrame.testAndSetRelaxed(false, true);
 694	} else {
 695		mCoreThreadUnpause(&m_threadContext);
 696		startAudio();
 697		emit gameUnpaused(&m_threadContext);
 698	}
 699}
 700
 701void GameController::reset() {
 702	if (!m_gameOpen) {
 703		return;
 704	}
 705	bool wasPaused = isPaused();
 706	setPaused(false);
 707	Interrupter interrupter(this);
 708	mCoreThreadReset(&m_threadContext);
 709	if (wasPaused) {
 710		setPaused(true);
 711	}
 712}
 713
 714void GameController::threadInterrupt() {
 715	if (m_gameOpen) {
 716		mCoreThreadInterrupt(&m_threadContext);
 717	}
 718}
 719
 720void GameController::threadContinue() {
 721	if (m_gameOpen) {
 722		mCoreThreadContinue(&m_threadContext);
 723	}
 724}
 725
 726void GameController::frameAdvance() {
 727	if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) {
 728		setPaused(false);
 729	}
 730}
 731
 732void GameController::setRewind(bool enable, int capacity, bool rewindSave) {
 733	if (m_gameOpen) {
 734		Interrupter interrupter(this);
 735		if (m_threadContext.core->opts.rewindEnable && m_threadContext.core->opts.rewindBufferCapacity > 0) {
 736			mCoreRewindContextDeinit(&m_threadContext.rewind);
 737		}
 738		m_threadContext.core->opts.rewindEnable = enable;
 739		m_threadContext.core->opts.rewindBufferCapacity = capacity;
 740		m_threadContext.core->opts.rewindSave = rewindSave;
 741		if (enable && capacity > 0) {
 742			mCoreRewindContextInit(&m_threadContext.rewind, capacity);
 743			 m_threadContext.rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0;
 744		}
 745	}
 746}
 747
 748void GameController::rewind(int states) {
 749	threadInterrupt();
 750	if (!states) {
 751		states = INT_MAX;
 752	}
 753	for (int i = 0; i < states; ++i) {
 754		if (!mCoreRewindRestore(&m_threadContext.rewind, m_threadContext.core)) {
 755			break;
 756		}
 757	}
 758	threadContinue();
 759	emit frameAvailable(m_drawContext);
 760	emit rewound(&m_threadContext);
 761}
 762
 763void GameController::startRewinding() {
 764	if (!isLoaded()) {
 765		return;
 766	}
 767	if (!m_threadContext.core->opts.rewindEnable) {
 768		return;
 769	}
 770	if (m_multiplayer && m_multiplayer->attached() > 1) {
 771		return;
 772	}
 773	if (m_wasPaused) {
 774		setPaused(false);
 775		m_wasPaused = true;
 776	}
 777	mCoreThreadSetRewinding(&m_threadContext, true);
 778}
 779
 780void GameController::stopRewinding() {
 781	if (!isLoaded()) {
 782		return;
 783	}
 784	mCoreThreadSetRewinding(&m_threadContext, false);
 785	bool signalsBlocked = blockSignals(true);
 786	setPaused(m_wasPaused);
 787	blockSignals(signalsBlocked);
 788}
 789
 790void GameController::keyPressed(int key) {
 791	int mappedKey = 1 << key;
 792	m_activeKeys |= mappedKey;
 793	if (!m_inputController->allowOpposing()) {
 794		if ((m_activeKeys & 0x30) == 0x30) {
 795			m_inactiveKeys |= mappedKey ^ 0x30;
 796			m_activeKeys ^= mappedKey ^ 0x30;
 797		}
 798		if ((m_activeKeys & 0xC0) == 0xC0) {
 799			m_inactiveKeys |= mappedKey ^ 0xC0;
 800			m_activeKeys ^= mappedKey ^ 0xC0;
 801		}
 802	}
 803	updateKeys();
 804}
 805
 806void GameController::keyReleased(int key) {
 807	int mappedKey = 1 << key;
 808	m_activeKeys &= ~mappedKey;
 809	if (!m_inputController->allowOpposing()) {
 810		if (mappedKey & 0x30) {
 811			m_activeKeys |= m_inactiveKeys & (0x30 ^ mappedKey);
 812			m_inactiveKeys &= ~0x30;
 813		}
 814		if (mappedKey & 0xC0) {
 815			m_activeKeys |= m_inactiveKeys & (0xC0 ^ mappedKey);
 816			m_inactiveKeys &= ~0xC0;
 817		}
 818	}
 819	updateKeys();
 820}
 821
 822void GameController::clearKeys() {
 823	m_activeKeys = 0;
 824	m_inactiveKeys = 0;
 825	updateKeys();
 826}
 827
 828void GameController::setAutofire(int key, bool enable) {
 829	if (key >= GBA_KEY_MAX || key < 0) {
 830		return;
 831	}
 832
 833	if (!enable && m_autofireStatus[key]) {
 834		keyReleased(key);
 835	}
 836
 837	m_autofire[key] = enable;
 838	m_autofireStatus[key] = 0;
 839}
 840
 841void GameController::setAudioBufferSamples(int samples) {
 842	if (m_audioProcessor) {
 843		threadInterrupt();
 844		redoSamples(samples);
 845		threadContinue();
 846		m_audioProcessor->setBufferSamples(samples);
 847	}
 848}
 849
 850void GameController::setAudioSampleRate(unsigned rate) {
 851	if (!rate) {
 852		return;
 853	}
 854	if (m_audioProcessor) {
 855		threadInterrupt();
 856		redoSamples(m_audioProcessor->getBufferSamples());
 857		threadContinue();
 858		m_audioProcessor->requestSampleRate(rate);
 859	}
 860}
 861
 862void GameController::setAudioChannelEnabled(int channel, bool enable) {
 863	if (channel > 5 || channel < 0) {
 864		return;
 865	}
 866	m_audioChannels.reserve(channel + 1);
 867	while (m_audioChannels.size() <= channel) {
 868		m_audioChannels.append(true);
 869	}
 870	m_audioChannels[channel] = enable;
 871	if (isLoaded()) {
 872		m_threadContext.core->enableAudioChannel(m_threadContext.core, channel, enable);
 873	}
 874}
 875
 876void GameController::startAudio() {
 877	if (!m_audioProcessor->start()) {
 878		LOG(QT, ERROR) << tr("Failed to start audio processor");
 879		// Don't freeze!
 880		m_audioSync = false;
 881		m_videoSync = true;
 882		m_threadContext.sync.audioWait = false;
 883		m_threadContext.sync.videoFrameWait = true;
 884	}
 885}
 886
 887void GameController::setVideoLayerEnabled(int layer, bool enable) {
 888	if (layer > 4 || layer < 0) {
 889		return;
 890	}
 891	m_videoLayers.reserve(layer + 1);
 892	while (m_videoLayers.size() <= layer) {
 893		m_videoLayers.append(true);
 894	}
 895	m_videoLayers[layer] = enable;
 896	if (isLoaded()) {
 897		m_threadContext.core->enableVideoLayer(m_threadContext.core, layer, enable);
 898	}
 899}
 900
 901void GameController::setFPSTarget(float fps) {
 902	Interrupter interrupter(this);
 903	m_fpsTarget = fps;
 904	m_threadContext.sync.fpsTarget = fps;
 905	if (m_turbo && m_turboSpeed > 0) {
 906		m_threadContext.sync.fpsTarget *= m_turboSpeed;
 907	}
 908	if (m_audioProcessor) {
 909		redoSamples(m_audioProcessor->getBufferSamples());
 910	}
 911}
 912
 913void GameController::setUseBIOS(bool use) {
 914	if (use == m_useBios) {
 915		return;
 916	}
 917	m_useBios = use;
 918	if (m_gameOpen) {
 919		closeGame();
 920		openGame();
 921	}
 922}
 923
 924void GameController::loadState(int slot) {
 925	if (m_fname.isEmpty()) {
 926		// We're in the BIOS
 927		return;
 928	}
 929	if (slot > 0 && slot != m_stateSlot) {
 930		m_stateSlot = slot;
 931		m_backupSaveState.clear();
 932	}
 933	mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
 934		GameController* controller = static_cast<GameController*>(context->userData);
 935		if (!controller->m_backupLoadState) {
 936			controller->m_backupLoadState = VFileMemChunk(nullptr, 0);
 937		}
 938		mCoreLoadStateNamed(context->core, controller->m_backupLoadState, controller->m_saveStateFlags);
 939		if (mCoreLoadState(context->core, controller->m_stateSlot, controller->m_loadStateFlags)) {
 940			controller->frameAvailable(controller->m_drawContext);
 941			controller->stateLoaded(context);
 942		}
 943	});
 944}
 945
 946void GameController::saveState(int slot) {
 947	if (m_fname.isEmpty()) {
 948		// We're in the BIOS
 949		return;
 950	}
 951	if (slot > 0) {
 952		m_stateSlot = slot;
 953	}
 954	mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
 955		GameController* controller = static_cast<GameController*>(context->userData);
 956		VFile* vf = mCoreGetState(context->core, controller->m_stateSlot, false);
 957		if (vf) {
 958			controller->m_backupSaveState.resize(vf->size(vf));
 959			vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
 960			vf->close(vf);
 961		}
 962		mCoreSaveState(context->core, controller->m_stateSlot, controller->m_saveStateFlags);
 963	});
 964}
 965
 966void GameController::loadBackupState() {
 967	if (!m_backupLoadState) {
 968		return;
 969	}
 970
 971	mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
 972		GameController* controller = static_cast<GameController*>(context->userData);
 973		controller->m_backupLoadState->seek(controller->m_backupLoadState, 0, SEEK_SET);
 974		if (mCoreLoadStateNamed(context->core, controller->m_backupLoadState, controller->m_loadStateFlags)) {
 975			mLOG(STATUS, INFO, "Undid state load");
 976			controller->frameAvailable(controller->m_drawContext);
 977			controller->stateLoaded(context);
 978		}
 979		controller->m_backupLoadState->close(controller->m_backupLoadState);
 980		controller->m_backupLoadState = nullptr;
 981	});
 982}
 983
 984void GameController::saveBackupState() {
 985	if (m_backupSaveState.isEmpty()) {
 986		return;
 987	}
 988
 989	mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
 990		GameController* controller = static_cast<GameController*>(context->userData);
 991		VFile* vf = mCoreGetState(context->core, controller->m_stateSlot, true);
 992		if (vf) {
 993			vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size());
 994			vf->close(vf);
 995			mLOG(STATUS, INFO, "Undid state save");
 996		}
 997		controller->m_backupSaveState.clear();
 998	});
 999}
1000
1001void GameController::setTurbo(bool set, bool forced) {
1002	if (m_turboForced && !forced) {
1003		return;
1004	}
1005	if (m_turbo == set && m_turboForced == (set && forced)) {
1006		// Don't interrupt the thread if we don't need to
1007		return;
1008	}
1009	if (!m_sync) {
1010		return;
1011	}
1012	m_turbo = set;
1013	m_turboForced = set && forced;
1014	enableTurbo();
1015}
1016
1017void GameController::setTurboSpeed(float ratio) {
1018	m_turboSpeed = ratio;
1019	enableTurbo();
1020}
1021
1022void GameController::enableTurbo() {
1023	Interrupter interrupter(this);
1024	bool shouldRedoSamples = false;
1025	if (!m_turbo) {
1026		shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget;
1027		m_threadContext.sync.fpsTarget = m_fpsTarget;
1028		m_threadContext.sync.audioWait = m_audioSync;
1029		m_threadContext.sync.videoFrameWait = m_videoSync;
1030	} else if (m_turboSpeed <= 0) {
1031		shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget;
1032		m_threadContext.sync.fpsTarget = m_fpsTarget;
1033		m_threadContext.sync.audioWait = false;
1034		m_threadContext.sync.videoFrameWait = false;
1035	} else {
1036		shouldRedoSamples = m_threadContext.sync.fpsTarget != m_fpsTarget * m_turboSpeed;
1037		m_threadContext.sync.fpsTarget = m_fpsTarget * m_turboSpeed;
1038		m_threadContext.sync.audioWait = true;
1039		m_threadContext.sync.videoFrameWait = false;
1040	}
1041	if (m_audioProcessor && shouldRedoSamples) {
1042		redoSamples(m_audioProcessor->getBufferSamples());
1043	}
1044}
1045
1046void GameController::setSync(bool enable) {
1047	m_turbo = false;
1048	m_turboForced = false;
1049	if (!enable) {
1050		m_threadContext.sync.audioWait = false;
1051		m_threadContext.sync.videoFrameWait = false;
1052	} else {
1053		m_threadContext.sync.audioWait = m_audioSync;
1054		m_threadContext.sync.videoFrameWait = m_videoSync;
1055	}
1056	m_sync = enable;
1057}
1058
1059void GameController::setAudioSync(bool enable) {
1060	m_audioSync = enable;
1061	m_threadContext.sync.audioWait = enable;
1062}
1063
1064void GameController::setVideoSync(bool enable) {
1065	m_videoSync = enable;
1066	m_threadContext.sync.videoFrameWait = enable;
1067}
1068
1069void GameController::setAVStream(mAVStream* stream) {
1070	Interrupter interrupter(this);
1071	m_stream = stream;
1072	if (isLoaded()) {
1073		m_threadContext.core->setAVStream(m_threadContext.core, stream);
1074	}
1075}
1076
1077void GameController::clearAVStream() {
1078	Interrupter interrupter(this);
1079	m_stream = nullptr;
1080	if (isLoaded()) {
1081		m_threadContext.core->setAVStream(m_threadContext.core, nullptr);
1082	}
1083}
1084
1085#ifdef USE_PNG
1086void GameController::screenshot() {
1087	mCoreThreadRunFunction(&m_threadContext, [](mCoreThread* context) {
1088		mCoreTakeScreenshot(context->core);
1089	});
1090}
1091#endif
1092
1093void GameController::reloadAudioDriver() {
1094	int samples = 0;
1095	unsigned sampleRate = 0;
1096	if (m_audioProcessor) {
1097		m_audioProcessor->pause();
1098		samples = m_audioProcessor->getBufferSamples();
1099		sampleRate = m_audioProcessor->sampleRate();
1100		delete m_audioProcessor;
1101	}
1102	m_audioProcessor = AudioProcessor::create();
1103	if (samples) {
1104		m_audioProcessor->setBufferSamples(samples);
1105	}
1106	if (sampleRate) {
1107		m_audioProcessor->requestSampleRate(sampleRate);
1108	}
1109	connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause()));
1110	connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*)));
1111	if (isLoaded()) {
1112		m_audioProcessor->setInput(&m_threadContext);
1113		startAudio();
1114	}
1115}
1116
1117void GameController::setSaveStateExtdata(int flags) {
1118	m_saveStateFlags = flags;
1119}
1120
1121void GameController::setLoadStateExtdata(int flags) {
1122	m_loadStateFlags = flags;
1123}
1124
1125void GameController::setPreload(bool preload) {
1126	m_preload = preload;
1127}
1128
1129void GameController::setLuminanceValue(uint8_t value) {
1130	m_luxValue = value;
1131	value = std::max<int>(value - 0x16, 0);
1132	m_luxLevel = 10;
1133	for (int i = 0; i < 10; ++i) {
1134		if (value < GBA_LUX_LEVELS[i]) {
1135			m_luxLevel = i;
1136			break;
1137		}
1138	}
1139	emit luminanceValueChanged(m_luxValue);
1140}
1141
1142void GameController::setLuminanceLevel(int level) {
1143	int value = 0x16;
1144	level = std::max(0, std::min(10, level));
1145	if (level > 0) {
1146		value += GBA_LUX_LEVELS[level - 1];
1147	}
1148	setLuminanceValue(value);
1149}
1150
1151void GameController::setRealTime() {
1152	if (!isLoaded()) {
1153		return;
1154	}
1155	m_threadContext.core->rtc.override = RTC_NO_OVERRIDE;
1156}
1157
1158void GameController::setFixedTime(const QDateTime& time) {
1159	if (!isLoaded()) {
1160		return;
1161	}
1162	m_threadContext.core->rtc.override = RTC_FIXED;
1163	m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
1164}
1165
1166void GameController::setFakeEpoch(const QDateTime& time) {
1167	if (!isLoaded()) {
1168		return;
1169	}
1170	m_threadContext.core->rtc.override = RTC_FAKE_EPOCH;
1171	m_threadContext.core->rtc.value = time.toMSecsSinceEpoch();
1172}
1173
1174void GameController::updateKeys() {
1175	int activeKeys = m_activeKeys;
1176	activeKeys |= m_activeButtons;
1177	activeKeys &= ~m_inactiveKeys;
1178	if (isLoaded()) {
1179		m_threadContext.core->setKeys(m_threadContext.core, activeKeys);
1180	}
1181}
1182
1183void GameController::redoSamples(int samples) {
1184	if (m_gameOpen && m_threadContext.core) {
1185		m_threadContext.core->setAudioBufferSize(m_threadContext.core, samples);
1186	}
1187	m_audioProcessor->inputParametersChanged();
1188}
1189
1190void GameController::setLogLevel(int levels) {
1191	Interrupter interrupter(this);
1192	m_logLevels = levels;
1193}
1194
1195void GameController::enableLogLevel(int levels) {
1196	Interrupter interrupter(this);
1197	m_logLevels |= levels;
1198}
1199
1200void GameController::disableLogLevel(int levels) {
1201	Interrupter interrupter(this);
1202	m_logLevels &= ~levels;
1203}
1204
1205void GameController::startVideoLog(const QString& path) {
1206	if (!isLoaded() || m_vl) {
1207		return;
1208	}
1209
1210	Interrupter interrupter(this);
1211	m_vlPath = path;
1212	m_vl = mVideoLoggerCreate(m_threadContext.core);
1213}
1214
1215void GameController::endVideoLog() {
1216	if (!m_vl) {
1217		return;
1218	}
1219
1220	Interrupter interrupter(this);
1221	if (isLoaded()) {
1222		VFile* vf = VFileDevice::open(m_vlPath, O_WRONLY | O_CREAT | O_TRUNC);
1223		mVideoLoggerWrite(m_threadContext.core, m_vl, vf);
1224		vf->close(vf);
1225	}
1226	mVideoLoggerDestroy(m_threadContext.core, m_vl);
1227	m_vl = nullptr;
1228}
1229
1230void GameController::pollEvents() {
1231	if (!m_inputController) {
1232		return;
1233	}
1234
1235	m_activeButtons = m_inputController->pollEvents();
1236	updateKeys();
1237}
1238
1239void GameController::updateAutofire() {
1240	// TODO: Move all key events onto the CPU thread...somehow
1241	for (int k = 0; k < GBA_KEY_MAX; ++k) {
1242		if (!m_autofire[k]) {
1243			continue;
1244		}
1245		m_autofireStatus[k] ^= 1;
1246		if (m_autofireStatus[k]) {
1247			keyPressed(k);
1248		} else {
1249			keyReleased(k);
1250		}
1251	}
1252}
1253
1254std::shared_ptr<mTileCache> GameController::tileCache() {
1255	if (m_tileCache) {
1256		return m_tileCache;
1257	}
1258	switch (platform()) {
1259#ifdef M_CORE_GBA
1260	case PLATFORM_GBA: {
1261		Interrupter interrupter(this);
1262		GBA* gba = static_cast<GBA*>(m_threadContext.core->board);
1263		m_tileCache = std::make_shared<mTileCache>();
1264		GBAVideoTileCacheInit(m_tileCache.get());
1265		GBAVideoTileCacheAssociate(m_tileCache.get(), &gba->video);
1266		mTileCacheSetPalette(m_tileCache.get(), 0);
1267		break;
1268	}
1269#endif
1270#ifdef M_CORE_GB
1271	case PLATFORM_GB: {
1272		Interrupter interrupter(this);
1273		GB* gb = static_cast<GB*>(m_threadContext.core->board);
1274		m_tileCache = std::make_shared<mTileCache>();
1275		GBVideoTileCacheInit(m_tileCache.get());
1276		GBVideoTileCacheAssociate(m_tileCache.get(), &gb->video);
1277		mTileCacheSetPalette(m_tileCache.get(), 0);
1278		break;
1279	}
1280#endif
1281	default:
1282		return nullptr;
1283	}
1284	return m_tileCache;
1285}
1286
1287GameController::Interrupter::Interrupter(GameController* parent, bool fromThread)
1288	: m_parent(parent)
1289	, m_fromThread(fromThread)
1290{
1291	if (!m_fromThread) {
1292		m_parent->threadInterrupt();
1293	} else {
1294		mCoreThreadInterruptFromThread(m_parent->thread());
1295	}
1296}
1297
1298GameController::Interrupter::~Interrupter() {
1299	if (!m_fromThread) {
1300		m_parent->threadContinue();
1301	} else {
1302		mCoreThreadContinue(m_parent->thread());
1303	}
1304}