all repos — mgba @ 2bfd721ea7a56026ae603ede60f852cd9aab0a24

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#include <QThread>
  17
  18#include <ctime>
  19
  20extern "C" {
  21#include "gba/audio.h"
  22#include "gba/context/config.h"
  23#include "gba/context/directories.h"
  24#include "gba/gba.h"
  25#include "gba/serialize.h"
  26#include "gba/sharkport.h"
  27#include "gba/renderers/video-software.h"
  28#include "util/vfs.h"
  29}
  30
  31using namespace QGBA;
  32using namespace std;
  33
  34GameController::GameController(QObject* parent)
  35	: QObject(parent)
  36	, m_drawContext(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
  37	, m_frontBuffer(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
  38	, m_threadContext()
  39	, m_activeKeys(0)
  40	, m_inactiveKeys(0)
  41	, m_logLevels(0)
  42	, m_gameOpen(false)
  43	, m_useBios(false)
  44	, m_audioThread(new QThread(this))
  45	, m_audioProcessor(AudioProcessor::create())
  46	, m_pauseAfterFrame(false)
  47	, m_videoSync(VIDEO_SYNC)
  48	, m_audioSync(AUDIO_SYNC)
  49	, m_fpsTarget(-1)
  50	, m_turbo(false)
  51	, m_turboForced(false)
  52	, m_turboSpeed(-1)
  53	, m_wasPaused(false)
  54	, m_audioChannels{ true, true, true, true, true, true }
  55	, m_videoLayers{ true, true, true, true, true }
  56	, m_autofire{}
  57	, m_autofireStatus{}
  58	, m_inputController(nullptr)
  59	, m_multiplayer(nullptr)
  60	, m_stateSlot(1)
  61	, m_backupLoadState(nullptr)
  62	, m_backupSaveState(nullptr)
  63	, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS)
  64	, m_loadStateFlags(SAVESTATE_SCREENSHOT)
  65{
  66	m_renderer = new GBAVideoSoftwareRenderer;
  67	GBAVideoSoftwareRendererCreate(m_renderer);
  68	m_renderer->outputBuffer = (color_t*) m_drawContext;
  69	m_renderer->outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
  70
  71	GBACheatDeviceCreate(&m_cheatDevice);
  72
  73	m_threadContext.state = THREAD_INITIALIZED;
  74	m_threadContext.debugger = 0;
  75	m_threadContext.frameskip = 0;
  76	m_threadContext.bios = 0;
  77	m_threadContext.renderer = &m_renderer->d;
  78	m_threadContext.userData = this;
  79	m_threadContext.rewindBufferCapacity = 0;
  80	m_threadContext.cheats = &m_cheatDevice;
  81	m_threadContext.logLevel = GBA_LOG_ALL;
  82	GBADirectorySetInit(&m_threadContext.dirs);
  83
  84	m_lux.p = this;
  85	m_lux.sample = [](GBALuminanceSource* context) {
  86		GameControllerLux* lux = static_cast<GameControllerLux*>(context);
  87		lux->value = 0xFF - lux->p->m_luxValue;
  88	};
  89
  90	m_lux.readLuminance = [](GBALuminanceSource* context) {
  91		GameControllerLux* lux = static_cast<GameControllerLux*>(context);
  92		return lux->value;
  93	};
  94	setLuminanceLevel(0);
  95
  96	m_threadContext.startCallback = [](GBAThread* context) {
  97		GameController* controller = static_cast<GameController*>(context->userData);
  98		if (controller->m_audioProcessor) {
  99			controller->m_audioProcessor->setInput(context);
 100		}
 101		context->gba->luminanceSource = &controller->m_lux;
 102		GBARTCGenericSourceInit(&controller->m_rtc, context->gba);
 103		context->gba->rtcSource = &controller->m_rtc.d;
 104		context->gba->rumble = controller->m_inputController->rumble();
 105		context->gba->rotationSource = controller->m_inputController->rotationSource();
 106		context->gba->audio.forceDisableCh[0] = !controller->m_audioChannels[0];
 107		context->gba->audio.forceDisableCh[1] = !controller->m_audioChannels[1];
 108		context->gba->audio.forceDisableCh[2] = !controller->m_audioChannels[2];
 109		context->gba->audio.forceDisableCh[3] = !controller->m_audioChannels[3];
 110		context->gba->audio.forceDisableChA = !controller->m_audioChannels[4];
 111		context->gba->audio.forceDisableChB = !controller->m_audioChannels[5];
 112		context->gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0];
 113		context->gba->video.renderer->disableBG[1] = !controller->m_videoLayers[1];
 114		context->gba->video.renderer->disableBG[2] = !controller->m_videoLayers[2];
 115		context->gba->video.renderer->disableBG[3] = !controller->m_videoLayers[3];
 116		context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4];
 117		controller->m_fpsTarget = context->fpsTarget;
 118
 119		if (context->dirs.state && GBALoadState(context, context->dirs.state, 0, controller->m_loadStateFlags)) {
 120			GBADeleteState(context->gba, context->dirs.state, 0);
 121		}
 122		QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context));
 123	};
 124
 125	m_threadContext.cleanCallback = [](GBAThread* context) {
 126		GameController* controller = static_cast<GameController*>(context->userData);
 127		QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(GBAThread*, context));
 128	};
 129
 130	m_threadContext.frameCallback = [](GBAThread* context) {
 131		GameController* controller = static_cast<GameController*>(context->userData);
 132		memcpy(controller->m_frontBuffer, controller->m_drawContext, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
 133		QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer));
 134		if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
 135			GBAThreadPauseFromThread(context);
 136			QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(GBAThread*, context));
 137		}
 138	};
 139
 140	m_threadContext.stopCallback = [](GBAThread* context) {
 141		if (!context) {
 142			return false;
 143		}
 144		GameController* controller = static_cast<GameController*>(context->userData);
 145		if (!GBASaveState(context, context->dirs.state, 0, controller->m_saveStateFlags)) {
 146			return false;
 147		}
 148		QMetaObject::invokeMethod(controller, "closeGame");
 149		return true;
 150	};
 151
 152	m_threadContext.logHandler = [](GBAThread* context, enum GBALogLevel level, const char* format, va_list args) {
 153		static const char* stubMessage = "Stub software interrupt: %02X";
 154		static const char* savestateMessage = "State %i loaded";
 155		static const char* savestateFailedMessage = "State %i failed to load";
 156		if (!context) {
 157			return;
 158		}
 159		GameController* controller = static_cast<GameController*>(context->userData);
 160		if (level == GBA_LOG_STUB && strncmp(stubMessage, format, strlen(stubMessage)) == 0) {
 161			va_list argc;
 162			va_copy(argc, args);
 163			int immediate = va_arg(argc, int);
 164			va_end(argc);
 165			QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate));
 166		} else if (level == GBA_LOG_STATUS) {
 167			// Slot 0 is reserved for suspend points
 168			if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {
 169				va_list argc;
 170				va_copy(argc, args);
 171				int slot = va_arg(argc, int);
 172				va_end(argc);
 173				if (slot == 0) {
 174					format = "Loaded suspend state";
 175				}
 176			} else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0) {
 177				va_list argc;
 178				va_copy(argc, args);
 179				int slot = va_arg(argc, int);
 180				va_end(argc);
 181				if (slot == 0) {
 182					return;
 183				}
 184			}
 185		}
 186		if (level == GBA_LOG_FATAL) {
 187			QMetaObject::invokeMethod(controller, "crashGame", Q_ARG(const QString&, QString().vsprintf(format, args)));
 188		} else if (!(controller->m_logLevels & level)) {
 189			return;
 190		}
 191		QString message(QString().vsprintf(format, args));
 192		if (level == GBA_LOG_STATUS) {
 193			QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
 194		}
 195		QMetaObject::invokeMethod(controller, "postLog", Q_ARG(int, level), Q_ARG(const QString&, message));
 196	};
 197
 198	connect(&m_rewindTimer, &QTimer::timeout, [this]() {
 199		GBARewind(&m_threadContext, 1);
 200		emit frameAvailable(m_drawContext);
 201		emit rewound(&m_threadContext);
 202	});
 203	m_rewindTimer.setInterval(100);
 204
 205	m_audioThread->setObjectName("Audio Thread");
 206	m_audioThread->start(QThread::TimeCriticalPriority);
 207	m_audioProcessor->moveToThread(m_audioThread);
 208	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
 209	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents()));
 210	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateAutofire()));
 211}
 212
 213GameController::~GameController() {
 214	m_audioThread->quit();
 215	m_audioThread->wait();
 216	disconnect();
 217	clearMultiplayerController();
 218	closeGame();
 219	GBACheatDeviceDestroy(&m_cheatDevice);
 220	GBADirectorySetDeinit(&m_threadContext.dirs);
 221	delete m_renderer;
 222	delete[] m_drawContext;
 223	delete[] m_frontBuffer;
 224	delete m_backupLoadState;
 225}
 226
 227void GameController::setMultiplayerController(MultiplayerController* controller) {
 228	if (controller == m_multiplayer) {
 229		return;
 230	}
 231	clearMultiplayerController();
 232	m_multiplayer = controller;
 233	controller->attachGame(this);
 234}
 235
 236void GameController::clearMultiplayerController() {
 237	if (!m_multiplayer) {
 238		return;
 239	}
 240	m_multiplayer->detachGame(this);
 241	m_multiplayer = nullptr;
 242}
 243
 244void GameController::setOverride(const GBACartridgeOverride& override) {
 245	m_threadContext.override = override;
 246	m_threadContext.hasOverride = true;
 247}
 248
 249void GameController::setOptions(const GBAOptions* opts) {
 250	setFrameskip(opts->frameskip);
 251	setAudioSync(opts->audioSync);
 252	setVideoSync(opts->videoSync);
 253	setSkipBIOS(opts->skipBios);
 254	setUseBIOS(opts->useBios);
 255	setRewind(opts->rewindEnable, opts->rewindBufferCapacity, opts->rewindBufferInterval);
 256	setVolume(opts->volume);
 257	setMute(opts->mute);
 258
 259	threadInterrupt();
 260	GBADirectorySetMapOptions(&m_threadContext.dirs, opts);
 261	m_threadContext.idleOptimization = opts->idleOptimization;
 262	threadContinue();
 263}
 264
 265#ifdef USE_GDB_STUB
 266ARMDebugger* GameController::debugger() {
 267	return m_threadContext.debugger;
 268}
 269
 270void GameController::setDebugger(ARMDebugger* debugger) {
 271	threadInterrupt();
 272	if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
 273		GBADetachDebugger(m_threadContext.gba);
 274	}
 275	m_threadContext.debugger = debugger;
 276	if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
 277		GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
 278	}
 279	threadContinue();
 280}
 281#endif
 282
 283void GameController::loadGame(const QString& path) {
 284	closeGame();
 285	QFile file(path);
 286	if (!file.open(QIODevice::ReadOnly)) {
 287		postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path));
 288		return;
 289	}
 290	file.close();
 291
 292	m_fname = path;
 293	openGame();
 294}
 295
 296void GameController::bootBIOS() {
 297	closeGame();
 298	m_fname = QString();
 299	openGame(true);
 300}
 301
 302void GameController::openGame(bool biosOnly) {
 303	if (biosOnly && (!m_useBios || m_bios.isNull())) {
 304		return;
 305	}
 306
 307	m_gameOpen = true;
 308
 309	m_pauseAfterFrame = false;
 310
 311	if (m_turbo) {
 312		m_threadContext.sync.videoFrameWait = false;
 313		m_threadContext.sync.audioWait = false;
 314	} else {
 315		m_threadContext.sync.videoFrameWait = m_videoSync;
 316		m_threadContext.sync.audioWait = m_audioSync;
 317	}
 318
 319	m_threadContext.bootBios = biosOnly;
 320	if (biosOnly) {
 321		m_threadContext.fname = nullptr;
 322	} else {
 323		m_threadContext.fname = strdup(m_fname.toUtf8().constData());
 324		GBAThreadLoadROM(&m_threadContext, m_threadContext.fname);
 325	}
 326
 327	if (!m_bios.isNull() && m_useBios) {
 328		m_threadContext.bios = VFileDevice::open(m_bios, O_RDONLY);
 329	} else {
 330		m_threadContext.bios = nullptr;
 331	}
 332
 333	if (!m_patch.isNull()) {
 334		m_threadContext.patch = VFileDevice::open(m_patch, O_RDONLY);
 335	}
 336
 337	m_inputController->recalibrateAxes();
 338	memset(m_drawContext, 0xF8, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * 4);
 339
 340	if (!GBAThreadStart(&m_threadContext)) {
 341		m_gameOpen = false;
 342		emit gameFailed();
 343	} else if (m_audioProcessor) {
 344		startAudio();
 345	}
 346}
 347
 348void GameController::loadBIOS(const QString& path) {
 349	if (m_bios == path) {
 350		return;
 351	}
 352	m_bios = path;
 353	if (m_gameOpen) {
 354		closeGame();
 355		openGame();
 356	}
 357}
 358
 359void GameController::yankPak() {
 360	if (!m_gameOpen) {
 361		return;
 362	}
 363	threadInterrupt();
 364	GBAYankROM(m_threadContext.gba);
 365	threadContinue();
 366}
 367
 368void GameController::replaceGame(const QString& path) {
 369	if (!m_gameOpen) {
 370		return;
 371	}
 372
 373	m_fname = path;
 374	threadInterrupt();
 375	m_threadContext.fname = strdup(m_fname.toLocal8Bit().constData());
 376	GBAThreadReplaceROM(&m_threadContext, m_threadContext.fname);
 377	threadContinue();
 378}
 379
 380void GameController::loadPatch(const QString& path) {
 381	if (m_gameOpen) {
 382		closeGame();
 383		m_patch = path;
 384		openGame();
 385	} else {
 386		m_patch = path;
 387	}
 388}
 389
 390void GameController::importSharkport(const QString& path) {
 391	if (!isLoaded()) {
 392		return;
 393	}
 394	VFile* vf = VFileDevice::open(path, O_RDONLY);
 395	if (!vf) {
 396		postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for reading: %1").arg(path));
 397		return;
 398	}
 399	threadInterrupt();
 400	GBASavedataImportSharkPort(m_threadContext.gba, vf, false);
 401	threadContinue();
 402	vf->close(vf);
 403}
 404
 405void GameController::exportSharkport(const QString& path) {
 406	if (!isLoaded()) {
 407		return;
 408	}
 409	VFile* vf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
 410	if (!vf) {
 411		postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for writing: %1").arg(path));
 412		return;
 413	}
 414	threadInterrupt();
 415	GBASavedataExportSharkPort(m_threadContext.gba, vf);
 416	threadContinue();
 417	vf->close(vf);
 418}
 419
 420void GameController::closeGame() {
 421	if (!m_gameOpen) {
 422		return;
 423	}
 424	m_gameOpen = false;
 425
 426	m_rewindTimer.stop();
 427	if (GBAThreadIsPaused(&m_threadContext)) {
 428		GBAThreadUnpause(&m_threadContext);
 429	}
 430	m_audioProcessor->pause();
 431	GBAThreadEnd(&m_threadContext);
 432	GBAThreadJoin(&m_threadContext);
 433	// Make sure the event queue clears out before the thread is reused
 434	QCoreApplication::processEvents();
 435	if (m_threadContext.fname) {
 436		free(const_cast<char*>(m_threadContext.fname));
 437		m_threadContext.fname = nullptr;
 438	}
 439
 440	m_patch = QString();
 441
 442	for (size_t i = 0; i < GBACheatSetsSize(&m_cheatDevice.cheats); ++i) {
 443		GBACheatSet* set = *GBACheatSetsGetPointer(&m_cheatDevice.cheats, i);
 444		GBACheatSetDeinit(set);
 445		delete set;
 446	}
 447	GBACheatSetsClear(&m_cheatDevice.cheats);
 448
 449	m_gameOpen = false;
 450	emit gameStopped(&m_threadContext);
 451}
 452
 453void GameController::crashGame(const QString& crashMessage) {
 454	closeGame();
 455	emit gameCrashed(crashMessage);
 456	emit gameStopped(&m_threadContext);
 457}
 458
 459bool GameController::isPaused() {
 460	if (!m_gameOpen) {
 461		return false;
 462	}
 463	return GBAThreadIsPaused(&m_threadContext);
 464}
 465
 466void GameController::setPaused(bool paused) {
 467	if (!isLoaded() || m_rewindTimer.isActive() || paused == GBAThreadIsPaused(&m_threadContext)) {
 468		return;
 469	}
 470	if (paused) {
 471		m_pauseAfterFrame.testAndSetRelaxed(false, true);
 472	} else {
 473		GBAThreadUnpause(&m_threadContext);
 474		startAudio();
 475		emit gameUnpaused(&m_threadContext);
 476	}
 477}
 478
 479void GameController::reset() {
 480	if (!m_gameOpen) {
 481		return;
 482	}
 483	bool wasPaused = isPaused();
 484	setPaused(false);
 485	GBAThreadReset(&m_threadContext);
 486	if (wasPaused) {
 487		setPaused(true);
 488	}
 489}
 490
 491void GameController::threadInterrupt() {
 492	if (m_gameOpen) {
 493		GBAThreadInterrupt(&m_threadContext);
 494	}
 495}
 496
 497void GameController::threadContinue() {
 498	if (m_gameOpen) {
 499		GBAThreadContinue(&m_threadContext);
 500	}
 501}
 502
 503void GameController::frameAdvance() {
 504	if (m_rewindTimer.isActive()) {
 505		return;
 506	}
 507	if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) {
 508		setPaused(false);
 509	}
 510}
 511
 512void GameController::setRewind(bool enable, int capacity, int interval) {
 513	if (m_gameOpen) {
 514		threadInterrupt();
 515		GBARewindSettingsChanged(&m_threadContext, enable ? capacity : 0, enable ? interval : 0);
 516		threadContinue();
 517	} else {
 518		if (enable) {
 519			m_threadContext.rewindBufferInterval = interval;
 520			m_threadContext.rewindBufferCapacity = capacity;
 521		} else {
 522			m_threadContext.rewindBufferInterval = 0;
 523			m_threadContext.rewindBufferCapacity = 0;
 524		}
 525	}
 526}
 527
 528void GameController::rewind(int states) {
 529	threadInterrupt();
 530	if (!states) {
 531		GBARewindAll(&m_threadContext);
 532	} else {
 533		GBARewind(&m_threadContext, states);
 534	}
 535	threadContinue();
 536	emit frameAvailable(m_drawContext);
 537	emit rewound(&m_threadContext);
 538}
 539
 540void GameController::startRewinding() {
 541	if (!m_gameOpen || m_rewindTimer.isActive()) {
 542		return;
 543	}
 544	if (m_multiplayer && m_multiplayer->attached() > 1) {
 545		return;
 546	}
 547	m_wasPaused = isPaused();
 548	if (!GBAThreadIsPaused(&m_threadContext)) {
 549		GBAThreadPause(&m_threadContext);
 550	}
 551	m_rewindTimer.start();
 552}
 553
 554void GameController::stopRewinding() {
 555	if (!m_rewindTimer.isActive()) {
 556		return;
 557	}
 558	m_rewindTimer.stop();
 559	bool signalsBlocked = blockSignals(true);
 560	setPaused(m_wasPaused);
 561	blockSignals(signalsBlocked);
 562}
 563
 564void GameController::keyPressed(int key) {
 565	int mappedKey = 1 << key;
 566	m_activeKeys |= mappedKey;
 567	if (!m_inputController->allowOpposing()) {
 568		if ((m_activeKeys & 0x30) == 0x30) {
 569			m_inactiveKeys |= mappedKey ^ 0x30;
 570			m_activeKeys ^= mappedKey ^ 0x30;
 571		}
 572		if ((m_activeKeys & 0xC0) == 0xC0) {
 573			m_inactiveKeys |= mappedKey ^ 0xC0;
 574			m_activeKeys ^= mappedKey ^ 0xC0;
 575		}
 576	}
 577	updateKeys();
 578}
 579
 580void GameController::keyReleased(int key) {
 581	int mappedKey = 1 << key;
 582	m_activeKeys &= ~mappedKey;
 583	if (!m_inputController->allowOpposing()) {
 584		if (mappedKey & 0x30) {
 585			m_activeKeys |= m_inactiveKeys & (0x30 ^ mappedKey);
 586			m_inactiveKeys &= ~0x30;
 587		}
 588		if (mappedKey & 0xC0) {
 589			m_activeKeys |= m_inactiveKeys & (0xC0 ^ mappedKey);
 590			m_inactiveKeys &= ~0xC0;
 591		}
 592	}
 593	updateKeys();
 594}
 595
 596void GameController::clearKeys() {
 597	m_activeKeys = 0;
 598	m_inactiveKeys = 0;
 599	updateKeys();
 600}
 601
 602void GameController::setAutofire(int key, bool enable) {
 603	if (key >= GBA_KEY_MAX || key < 0) {
 604		return;
 605	}
 606	m_autofire[key] = enable;
 607	m_autofireStatus[key] = 0;
 608}
 609
 610void GameController::setAudioBufferSamples(int samples) {
 611	if (m_audioProcessor) {
 612		threadInterrupt();
 613		redoSamples(samples);
 614		threadContinue();
 615		QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Qt::BlockingQueuedConnection, Q_ARG(int, samples));
 616	}
 617}
 618
 619void GameController::setAudioSampleRate(unsigned rate) {
 620	if (!rate) {
 621		return;
 622	}
 623	if (m_audioProcessor) {
 624		threadInterrupt();
 625		redoSamples(m_audioProcessor->getBufferSamples());
 626		threadContinue();
 627		QMetaObject::invokeMethod(m_audioProcessor, "requestSampleRate", Q_ARG(unsigned, rate));
 628	}
 629}
 630
 631void GameController::setAudioChannelEnabled(int channel, bool enable) {
 632	if (channel > 5 || channel < 0) {
 633		return;
 634	}
 635	m_audioChannels[channel] = enable;
 636	if (isLoaded()) {
 637		switch (channel) {
 638		case 0:
 639		case 1:
 640		case 2:
 641		case 3:
 642			m_threadContext.gba->audio.forceDisableCh[channel] = !enable;
 643			break;
 644		case 4:
 645			m_threadContext.gba->audio.forceDisableChA = !enable;
 646			break;
 647		case 5:
 648			m_threadContext.gba->audio.forceDisableChB = !enable;
 649			break;
 650		}
 651	}
 652}
 653
 654void GameController::startAudio() {
 655	bool started = false;
 656	QMetaObject::invokeMethod(m_audioProcessor, "start", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, started));
 657	if (!started) {
 658		LOG(ERROR) << tr("Failed to start audio processor");
 659		// Don't freeze!
 660		m_audioSync = false;
 661		m_videoSync = true;
 662		m_threadContext.sync.audioWait = false;
 663		m_threadContext.sync.videoFrameWait = true;
 664	}
 665}
 666
 667void GameController::setVideoLayerEnabled(int layer, bool enable) {
 668	if (layer > 4 || layer < 0) {
 669		return;
 670	}
 671	m_videoLayers[layer] = enable;
 672	if (isLoaded()) {
 673		switch (layer) {
 674		case 0:
 675		case 1:
 676		case 2:
 677		case 3:
 678			m_threadContext.gba->video.renderer->disableBG[layer] = !enable;
 679			break;
 680		case 4:
 681			m_threadContext.gba->video.renderer->disableOBJ = !enable;
 682			break;
 683		}
 684	}
 685}
 686
 687void GameController::setFPSTarget(float fps) {
 688	threadInterrupt();
 689	m_fpsTarget = fps;
 690	m_threadContext.fpsTarget = fps;
 691	if (m_turbo && m_turboSpeed > 0) {
 692		m_threadContext.fpsTarget *= m_turboSpeed;
 693	}
 694	if (m_audioProcessor) {
 695		redoSamples(m_audioProcessor->getBufferSamples());
 696	}
 697	threadContinue();
 698}
 699
 700void GameController::setSkipBIOS(bool set) {
 701	threadInterrupt();
 702	m_threadContext.skipBios = set;
 703	threadContinue();
 704}
 705
 706void GameController::setUseBIOS(bool use) {
 707	if (use == m_useBios) {
 708		return;
 709	}
 710	m_useBios = use;
 711	if (m_gameOpen) {
 712		closeGame();
 713		openGame();
 714	}
 715}
 716
 717void GameController::loadState(int slot) {
 718	if (!m_threadContext.fname) {
 719		// We're in the BIOS
 720		return;
 721	}
 722	if (slot > 0 && slot != m_stateSlot) {
 723		m_stateSlot = slot;
 724		m_backupSaveState.clear();
 725	}
 726	GBARunOnThread(&m_threadContext, [](GBAThread* context) {
 727		GameController* controller = static_cast<GameController*>(context->userData);
 728		if (!controller->m_backupLoadState) {
 729			controller->m_backupLoadState = new GBASerializedState;
 730		}
 731		GBASerialize(context->gba, controller->m_backupLoadState);
 732		if (GBALoadState(context, context->dirs.state, controller->m_stateSlot, controller->m_loadStateFlags)) {
 733			controller->frameAvailable(controller->m_drawContext);
 734			controller->stateLoaded(context);
 735		}
 736	});
 737}
 738
 739void GameController::saveState(int slot) {
 740	if (!m_threadContext.fname) {
 741		// We're in the BIOS
 742		return;
 743	}
 744	if (slot > 0) {
 745		m_stateSlot = slot;
 746	}
 747	GBARunOnThread(&m_threadContext, [](GBAThread* context) {
 748		GameController* controller = static_cast<GameController*>(context->userData);
 749		VFile* vf = GBAGetState(context->gba, context->dirs.state, controller->m_stateSlot, false);
 750		if (vf) {
 751			controller->m_backupSaveState.resize(vf->size(vf));
 752			vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
 753			vf->close(vf);
 754		}
 755		GBASaveState(context, context->dirs.state, controller->m_stateSlot, controller->m_saveStateFlags);
 756	});
 757}
 758
 759void GameController::loadBackupState() {
 760	if (!m_backupLoadState) {
 761		return;
 762	}
 763
 764	GBARunOnThread(&m_threadContext, [](GBAThread* context) {
 765		GameController* controller = static_cast<GameController*>(context->userData);
 766		if (GBADeserialize(context->gba, controller->m_backupLoadState)) {
 767			GBALog(context->gba, GBA_LOG_STATUS, "Undid state load");
 768			controller->frameAvailable(controller->m_drawContext);
 769			controller->stateLoaded(context);
 770		}
 771		delete controller->m_backupLoadState;
 772		controller->m_backupLoadState = nullptr;
 773	});
 774}
 775
 776void GameController::saveBackupState() {
 777	if (m_backupSaveState.isEmpty()) {
 778		return;
 779	}
 780
 781	GBARunOnThread(&m_threadContext, [](GBAThread* context) {
 782		GameController* controller = static_cast<GameController*>(context->userData);
 783		VFile* vf = GBAGetState(context->gba, context->dirs.state, controller->m_stateSlot, true);
 784		if (vf) {
 785			vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size());
 786			vf->close(vf);
 787			GBALog(context->gba, GBA_LOG_STATUS, "Undid state save");
 788		}
 789		controller->m_backupSaveState.clear();
 790	});
 791}
 792
 793void GameController::setVideoSync(bool set) {
 794	m_videoSync = set;
 795	if (!m_turbo) {
 796		threadInterrupt();
 797		m_threadContext.sync.videoFrameWait = set;
 798		threadContinue();
 799	}
 800}
 801
 802void GameController::setAudioSync(bool set) {
 803	m_audioSync = set;
 804	if (!m_turbo) {
 805		threadInterrupt();
 806		m_threadContext.sync.audioWait = set;
 807		threadContinue();
 808	}
 809}
 810
 811void GameController::setFrameskip(int skip) {
 812	threadInterrupt();
 813	m_threadContext.frameskip = skip;
 814	if (isLoaded()) {
 815		m_threadContext.gba->video.frameskip = skip;
 816	}
 817	threadContinue();
 818}
 819
 820void GameController::setVolume(int volume) {
 821	threadInterrupt();
 822	m_threadContext.volume = volume;
 823	if (isLoaded()) {
 824		m_threadContext.gba->audio.masterVolume = volume;
 825	}
 826	threadContinue();
 827}
 828
 829void GameController::setMute(bool mute) {
 830	threadInterrupt();
 831	m_threadContext.mute = mute;
 832	if (isLoaded()) {
 833		m_threadContext.gba->audio.masterVolume = mute ? 0 : m_threadContext.volume;
 834	}
 835	threadContinue();
 836}
 837
 838void GameController::setTurbo(bool set, bool forced) {
 839	if (m_turboForced && !forced) {
 840		return;
 841	}
 842	if (m_turbo == set && m_turboForced == forced) {
 843		// Don't interrupt the thread if we don't need to
 844		return;
 845	}
 846	m_turbo = set;
 847	m_turboForced = set && forced;
 848	enableTurbo();
 849}
 850
 851void GameController::setTurboSpeed(float ratio) {
 852	m_turboSpeed = ratio;
 853	enableTurbo();
 854}
 855
 856void GameController::enableTurbo() {
 857	threadInterrupt();
 858	if (!m_turbo) {
 859		m_threadContext.fpsTarget = m_fpsTarget;
 860		m_threadContext.sync.audioWait = m_audioSync;
 861		m_threadContext.sync.videoFrameWait = m_videoSync;
 862	} else if (m_turboSpeed <= 0) {
 863		m_threadContext.fpsTarget = m_fpsTarget;
 864		m_threadContext.sync.audioWait = false;
 865		m_threadContext.sync.videoFrameWait = false;
 866	} else {
 867		m_threadContext.fpsTarget = m_fpsTarget * m_turboSpeed;
 868		m_threadContext.sync.audioWait = true;
 869		m_threadContext.sync.videoFrameWait = false;
 870	}
 871	if (m_audioProcessor) {
 872		redoSamples(m_audioProcessor->getBufferSamples());
 873	}
 874	threadContinue();
 875}
 876
 877void GameController::setAVStream(GBAAVStream* stream) {
 878	threadInterrupt();
 879	m_threadContext.stream = stream;
 880	if (isLoaded()) {
 881		m_threadContext.gba->stream = stream;
 882	}
 883	threadContinue();
 884}
 885
 886void GameController::clearAVStream() {
 887	threadInterrupt();
 888	m_threadContext.stream = nullptr;
 889	if (isLoaded()) {
 890		m_threadContext.gba->stream = nullptr;
 891	}
 892	threadContinue();
 893}
 894
 895#ifdef USE_PNG
 896void GameController::screenshot() {
 897	GBARunOnThread(&m_threadContext, GBAThreadTakeScreenshot);
 898}
 899#endif
 900
 901void GameController::reloadAudioDriver() {
 902	int samples = 0;
 903	unsigned sampleRate = 0;
 904	if (m_audioProcessor) {
 905		QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
 906		samples = m_audioProcessor->getBufferSamples();
 907		sampleRate = m_audioProcessor->sampleRate();
 908		delete m_audioProcessor;
 909	}
 910	m_audioProcessor = AudioProcessor::create();
 911	if (samples) {
 912		m_audioProcessor->setBufferSamples(samples);
 913	}
 914	if (sampleRate) {
 915		m_audioProcessor->requestSampleRate(sampleRate);
 916	}
 917	m_audioProcessor->moveToThread(m_audioThread);
 918	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
 919	if (isLoaded()) {
 920		m_audioProcessor->setInput(&m_threadContext);
 921		startAudio();
 922	}
 923}
 924
 925void GameController::setSaveStateExtdata(int flags) {
 926	m_saveStateFlags = flags;
 927}
 928
 929void GameController::setLoadStateExtdata(int flags) {
 930	m_loadStateFlags = flags;
 931}
 932
 933void GameController::setLuminanceValue(uint8_t value) {
 934	m_luxValue = value;
 935	value = std::max<int>(value - 0x16, 0);
 936	m_luxLevel = 10;
 937	for (int i = 0; i < 10; ++i) {
 938		if (value < GBA_LUX_LEVELS[i]) {
 939			m_luxLevel = i;
 940			break;
 941		}
 942	}
 943	emit luminanceValueChanged(m_luxValue);
 944}
 945
 946void GameController::setLuminanceLevel(int level) {
 947	int value = 0x16;
 948	level = std::max(0, std::min(10, level));
 949	if (level > 0) {
 950		value += GBA_LUX_LEVELS[level - 1];
 951	}
 952	setLuminanceValue(value);
 953}
 954
 955void GameController::setRealTime() {
 956	m_rtc.override = GBARTCGenericSource::RTC_NO_OVERRIDE;
 957}
 958
 959void GameController::setFixedTime(const QDateTime& time) {
 960	m_rtc.override = GBARTCGenericSource::RTC_FIXED;
 961	m_rtc.value = time.toMSecsSinceEpoch() / 1000;
 962}
 963
 964void GameController::setFakeEpoch(const QDateTime& time) {
 965	m_rtc.override = GBARTCGenericSource::RTC_FAKE_EPOCH;
 966	m_rtc.value = time.toMSecsSinceEpoch() / 1000;
 967}
 968
 969void GameController::updateKeys() {
 970	int activeKeys = m_activeKeys;
 971	activeKeys |= m_activeButtons;
 972	activeKeys &= ~m_inactiveKeys;
 973	m_threadContext.activeKeys = activeKeys;
 974}
 975
 976void GameController::redoSamples(int samples) {
 977#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
 978	float sampleRate = 0x8000;
 979	float ratio;
 980	if (m_threadContext.gba) {
 981		sampleRate = m_threadContext.gba->audio.sampleRate;
 982	}
 983	ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, m_audioProcess->sampleRate());
 984	m_threadContext.audioBuffers = ceil(samples / ratio);
 985#else
 986	m_threadContext.audioBuffers = samples;
 987#endif
 988	if (m_threadContext.gba) {
 989		GBAAudioResizeBuffer(&m_threadContext.gba->audio, m_threadContext.audioBuffers);
 990	}
 991	QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
 992}
 993
 994void GameController::setLogLevel(int levels) {
 995	threadInterrupt();
 996	m_logLevels = levels;
 997	threadContinue();
 998}
 999
1000void GameController::enableLogLevel(int levels) {
1001	threadInterrupt();
1002	m_logLevels |= levels;
1003	threadContinue();
1004}
1005
1006void GameController::disableLogLevel(int levels) {
1007	threadInterrupt();
1008	m_logLevels &= ~levels;
1009	threadContinue();
1010}
1011
1012void GameController::pollEvents() {
1013	if (!m_inputController) {
1014		return;
1015	}
1016
1017	m_activeButtons = m_inputController->pollEvents();
1018	updateKeys();
1019}
1020
1021void GameController::updateAutofire() {
1022	// TODO: Move all key events onto the CPU thread...somehow
1023	for (int k = 0; k < GBA_KEY_MAX; ++k) {
1024		if (!m_autofire[k]) {
1025			continue;
1026		}
1027		m_autofireStatus[k] ^= 1;
1028		if (m_autofireStatus[k]) {
1029			keyPressed(k);
1030		} else {
1031			keyReleased(k);
1032		}
1033	}
1034}