all repos — mgba @ f628f5bafcb5dedcbaa6a56c1e41cc086f2ceafa

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