all repos — mgba @ 9b6fc4482555e1a9e06b088604ebaabbc8325e2b

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