all repos — mgba @ c18b9c3a64458dc7f6c0814a4b69b87c10eaefcc

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