all repos — mgba @ 03984ce0859bcd47118bf406cccc1333076e013d

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