all repos — mgba @ 61ddffbcae2b1b213c13811754121e831608dc78

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 "MultiplayerController.h"
 11#include "VFileDevice.h"
 12
 13#include <QDateTime>
 14#include <QThread>
 15
 16#include <ctime>
 17
 18extern "C" {
 19#include "gba/audio.h"
 20#include "gba/gba.h"
 21#include "gba/serialize.h"
 22#include "gba/sharkport.h"
 23#include "gba/renderers/video-software.h"
 24#include "gba/supervisor/config.h"
 25#include "util/vfs.h"
 26}
 27
 28using namespace QGBA;
 29using namespace std;
 30
 31const int GameController::LUX_LEVELS[10] = { 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 };
 32
 33GameController::GameController(QObject* parent)
 34	: QObject(parent)
 35	, m_drawContext(new uint32_t[256 * 256])
 36	, m_threadContext()
 37	, m_activeKeys(0)
 38	, m_inactiveKeys(0)
 39	, m_logLevels(0)
 40	, m_gameOpen(false)
 41	, m_audioThread(new QThread(this))
 42	, m_audioProcessor(AudioProcessor::create())
 43	, m_pauseAfterFrame(false)
 44	, m_videoSync(VIDEO_SYNC)
 45	, m_audioSync(AUDIO_SYNC)
 46	, m_fpsTarget(-1)
 47	, m_turbo(false)
 48	, m_turboForced(false)
 49	, m_turboSpeed(-1)
 50	, m_wasPaused(false)
 51	, m_inputController(nullptr)
 52	, m_multiplayer(nullptr)
 53	, m_stateSlot(1)
 54{
 55	m_renderer = new GBAVideoSoftwareRenderer;
 56	GBAVideoSoftwareRendererCreate(m_renderer);
 57	m_renderer->outputBuffer = (color_t*) m_drawContext;
 58	m_renderer->outputBufferStride = 256;
 59
 60	GBACheatDeviceCreate(&m_cheatDevice);
 61
 62	m_threadContext.state = THREAD_INITIALIZED;
 63	m_threadContext.debugger = 0;
 64	m_threadContext.frameskip = 0;
 65	m_threadContext.bios = 0;
 66	m_threadContext.renderer = &m_renderer->d;
 67	m_threadContext.userData = this;
 68	m_threadContext.rewindBufferCapacity = 0;
 69	m_threadContext.cheats = &m_cheatDevice;
 70	m_threadContext.logLevel = GBA_LOG_ALL;
 71
 72	m_lux.p = this;
 73	m_lux.sample = [](GBALuminanceSource* context) {
 74		GameControllerLux* lux = static_cast<GameControllerLux*>(context);
 75		lux->value = 0xFF - lux->p->m_luxValue;
 76	};
 77
 78	m_lux.readLuminance = [](GBALuminanceSource* context) {
 79		GameControllerLux* lux = static_cast<GameControllerLux*>(context);
 80		return lux->value;
 81	};
 82	setLuminanceLevel(0);
 83
 84	m_threadContext.startCallback = [](GBAThread* context) {
 85		GameController* controller = static_cast<GameController*>(context->userData);
 86		controller->m_audioProcessor->setInput(context);
 87		context->gba->luminanceSource = &controller->m_lux;
 88		GBARTCGenericSourceInit(&controller->m_rtc, context->gba);
 89		context->gba->rtcSource = &controller->m_rtc.d;
 90		context->gba->rumble = controller->m_inputController->rumble();
 91		context->gba->rotationSource = controller->m_inputController->rotationSource();
 92		controller->m_fpsTarget = context->fpsTarget;
 93		controller->gameStarted(context);
 94	};
 95
 96	m_threadContext.cleanCallback = [](GBAThread* context) {
 97		GameController* controller = static_cast<GameController*>(context->userData);
 98		controller->gameStopped(context);
 99	};
100
101	m_threadContext.frameCallback = [](GBAThread* context) {
102		GameController* controller = static_cast<GameController*>(context->userData);
103		if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
104			GBAThreadPauseFromThread(context);
105			controller->gamePaused(&controller->m_threadContext);
106		}
107		if (GBASyncDrawingFrame(&controller->m_threadContext.sync)) {
108			controller->frameAvailable(controller->m_drawContext);
109		} else {
110			controller->frameAvailable(nullptr);
111		}
112	};
113
114	m_threadContext.logHandler = [](GBAThread* context, enum GBALogLevel level, const char* format, va_list args) {
115		static const char* stubMessage = "Stub software interrupt";
116		if (!context) {
117			return;
118		}
119		GameController* controller = static_cast<GameController*>(context->userData);
120		if (level == GBA_LOG_STUB && strncmp(stubMessage, format, strlen(stubMessage)) == 0) {
121			va_list argc;
122			va_copy(argc, args);
123			int immediate = va_arg(argc, int);
124			va_end(argc);
125			controller->unimplementedBiosCall(immediate);
126		}
127		if (level == GBA_LOG_FATAL) {
128			QMetaObject::invokeMethod(controller, "crashGame", Q_ARG(const QString&, QString().vsprintf(format, args)));
129		} else if (!(controller->m_logLevels & level)) {
130			return;
131		}
132		QString message(QString().vsprintf(format, args));
133		if (level == GBA_LOG_STATUS) {
134			controller->statusPosted(message);
135		}
136		controller->postLog(level, message);
137	};
138
139	connect(&m_rewindTimer, &QTimer::timeout, [this]() {
140		GBARewind(&m_threadContext, 1);
141		emit frameAvailable(m_drawContext);
142		emit rewound(&m_threadContext);
143	});
144	m_rewindTimer.setInterval(100);
145
146	m_audioThread->setObjectName("Audio Thread");
147	m_audioThread->start(QThread::TimeCriticalPriority);
148	m_audioProcessor->moveToThread(m_audioThread);
149	connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
150	connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
151	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
152	connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
153	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents()));
154}
155
156GameController::~GameController() {
157	m_audioThread->quit();
158	m_audioThread->wait();
159	disconnect();
160	clearMultiplayerController();
161	closeGame();
162	GBACheatDeviceDestroy(&m_cheatDevice);
163	delete m_renderer;
164	delete[] m_drawContext;
165}
166
167void GameController::setMultiplayerController(MultiplayerController* controller) {
168	if (controller == m_multiplayer) {
169		return;
170	}
171	clearMultiplayerController();
172	m_multiplayer = controller;
173	controller->attachGame(this);
174}
175
176void GameController::clearMultiplayerController() {
177	if (!m_multiplayer) {
178		return;
179	}
180	m_multiplayer->detachGame(this);
181	m_multiplayer = nullptr;
182}
183
184void GameController::setOverride(const GBACartridgeOverride& override) {
185	m_threadContext.override = override;
186	m_threadContext.hasOverride = true;
187}
188
189void GameController::setOptions(const GBAOptions* opts) {
190	setFrameskip(opts->frameskip);
191	setAudioSync(opts->audioSync);
192	setVideoSync(opts->videoSync);
193	setSkipBIOS(opts->skipBios);
194	setUseBIOS(opts->useBios);
195	setRewind(opts->rewindEnable, opts->rewindBufferCapacity, opts->rewindBufferInterval);
196	setVolume(opts->volume);
197	setMute(opts->mute);
198
199	threadInterrupt();
200	m_threadContext.idleOptimization = opts->idleOptimization;
201	threadContinue();
202}
203
204#ifdef USE_GDB_STUB
205ARMDebugger* GameController::debugger() {
206	return m_threadContext.debugger;
207}
208
209void GameController::setDebugger(ARMDebugger* debugger) {
210	threadInterrupt();
211	if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
212		GBADetachDebugger(m_threadContext.gba);
213	}
214	m_threadContext.debugger = debugger;
215	if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
216		GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
217	}
218	threadContinue();
219}
220#endif
221
222void GameController::loadGame(const QString& path, bool dirmode) {
223	closeGame();
224	if (!dirmode) {
225		QFile file(path);
226		if (!file.open(QIODevice::ReadOnly)) {
227			postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path));
228			return;
229		}
230		file.close();
231	}
232
233	m_fname = path;
234	m_dirmode = dirmode;
235	openGame();
236}
237
238void GameController::bootBIOS() {
239	closeGame();
240	m_fname = QString();
241	m_dirmode = false;
242	openGame(true);
243}
244
245void GameController::openGame(bool biosOnly) {
246	if (biosOnly && (!m_useBios || m_bios.isNull())) {
247		return;
248	}
249
250	m_gameOpen = true;
251
252	m_pauseAfterFrame = false;
253
254	if (m_turbo) {
255		m_threadContext.sync.videoFrameWait = false;
256		m_threadContext.sync.audioWait = false;
257	} else {
258		m_threadContext.sync.videoFrameWait = m_videoSync;
259		m_threadContext.sync.audioWait = m_audioSync;
260	}
261
262	m_threadContext.gameDir = 0;
263	m_threadContext.bootBios = biosOnly;
264	if (biosOnly) {
265		m_threadContext.fname = nullptr;
266	} else {
267		m_threadContext.fname = strdup(m_fname.toUtf8().constData());
268		if (m_dirmode) {
269			m_threadContext.gameDir = VDirOpen(m_threadContext.fname);
270			m_threadContext.stateDir = m_threadContext.gameDir;
271		} else {
272			GBAThreadLoadROM(&m_threadContext, m_threadContext.fname);
273		}
274	}
275
276	if (!m_bios.isNull() && m_useBios) {
277		m_threadContext.bios = VFileDevice::open(m_bios, O_RDONLY);
278	} else {
279		m_threadContext.bios = nullptr;
280	}
281
282	if (!m_patch.isNull()) {
283		m_threadContext.patch = VFileDevice::open(m_patch, O_RDONLY);
284	}
285
286	m_inputController->recalibrateAxes();
287
288	if (!GBAThreadStart(&m_threadContext)) {
289		m_gameOpen = false;
290		emit gameFailed();
291	}
292}
293
294void GameController::loadBIOS(const QString& path) {
295	if (m_bios == path) {
296		return;
297	}
298	m_bios = path;
299	if (m_gameOpen) {
300		closeGame();
301		openGame();
302	}
303}
304
305void GameController::yankPak() {
306	if (!m_gameOpen) {
307		return;
308	}
309	threadInterrupt();
310	GBAYankROM(m_threadContext.gba);
311	threadContinue();
312}
313
314void GameController::replaceGame(const QString& path) {
315	if (!m_gameOpen) {
316		return;
317	}
318
319	m_fname = path;
320	threadInterrupt();
321	m_threadContext.fname = strdup(m_fname.toLocal8Bit().constData());
322	GBAThreadReplaceROM(&m_threadContext, m_threadContext.fname);
323	threadContinue();
324}
325
326void GameController::loadPatch(const QString& path) {
327	if (m_gameOpen) {
328		closeGame();
329		m_patch = path;
330		openGame();
331	} else {
332		m_patch = path;
333	}
334}
335
336void GameController::importSharkport(const QString& path) {
337	if (!m_gameOpen) {
338		return;
339	}
340	VFile* vf = VFileDevice::open(path, O_RDONLY);
341	if (!vf) {
342		postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for reading: %1").arg(path));
343		return;
344	}
345	threadInterrupt();
346	GBASavedataImportSharkPort(m_threadContext.gba, vf, false);
347	threadContinue();
348	vf->close(vf);
349}
350
351void GameController::exportSharkport(const QString& path) {
352	if (!m_gameOpen) {
353		return;
354	}
355	VFile* vf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
356	if (!vf) {
357		postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for writing: %1").arg(path));
358		return;
359	}
360	threadInterrupt();
361	GBASavedataExportSharkPort(m_threadContext.gba, vf);
362	threadContinue();
363	vf->close(vf);
364}
365
366void GameController::closeGame() {
367	if (!m_gameOpen) {
368		return;
369	}
370	m_rewindTimer.stop();
371	if (GBAThreadIsPaused(&m_threadContext)) {
372		GBAThreadUnpause(&m_threadContext);
373	}
374	GBAThreadEnd(&m_threadContext);
375	GBAThreadJoin(&m_threadContext);
376	if (m_threadContext.fname) {
377		free(const_cast<char*>(m_threadContext.fname));
378		m_threadContext.fname = nullptr;
379	}
380
381	m_patch = QString();
382
383	for (size_t i = 0; i < GBACheatSetsSize(&m_cheatDevice.cheats); ++i) {
384		GBACheatSet* set = *GBACheatSetsGetPointer(&m_cheatDevice.cheats, i);
385		GBACheatSetDeinit(set);
386		delete set;
387	}
388	GBACheatSetsClear(&m_cheatDevice.cheats);
389
390	m_gameOpen = false;
391	emit gameStopped(&m_threadContext);
392}
393
394void GameController::crashGame(const QString& crashMessage) {
395	closeGame();
396	emit gameCrashed(crashMessage);
397	emit gameStopped(&m_threadContext);
398}
399
400bool GameController::isPaused() {
401	if (!m_gameOpen) {
402		return false;
403	}
404	return GBAThreadIsPaused(&m_threadContext);
405}
406
407void GameController::setPaused(bool paused) {
408	if (!m_gameOpen || m_rewindTimer.isActive() || paused == GBAThreadIsPaused(&m_threadContext)) {
409		return;
410	}
411	if (paused) {
412		GBAThreadPause(&m_threadContext);
413		emit gamePaused(&m_threadContext);
414	} else {
415		GBAThreadUnpause(&m_threadContext);
416		emit gameUnpaused(&m_threadContext);
417	}
418}
419
420void GameController::reset() {
421	GBAThreadReset(&m_threadContext);
422}
423
424void GameController::threadInterrupt() {
425	if (m_gameOpen) {
426		GBAThreadInterrupt(&m_threadContext);
427	}
428}
429
430void GameController::threadContinue() {
431	if (m_gameOpen) {
432		GBAThreadContinue(&m_threadContext);
433	}
434}
435
436void GameController::frameAdvance() {
437	if (m_rewindTimer.isActive()) {
438		return;
439	}
440	if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) {
441		setPaused(false);
442	}
443}
444
445void GameController::setRewind(bool enable, int capacity, int interval) {
446	if (m_gameOpen) {
447		threadInterrupt();
448		GBARewindSettingsChanged(&m_threadContext, enable ? capacity : 0, enable ? interval : 0);
449		threadContinue();
450	} else {
451		if (enable) {
452			m_threadContext.rewindBufferInterval = interval;
453			m_threadContext.rewindBufferCapacity = capacity;
454		} else {
455			m_threadContext.rewindBufferInterval = 0;
456			m_threadContext.rewindBufferCapacity = 0;
457		}
458	}
459}
460
461void GameController::rewind(int states) {
462	threadInterrupt();
463	if (!states) {
464		GBARewindAll(&m_threadContext);
465	} else {
466		GBARewind(&m_threadContext, states);
467	}
468	threadContinue();
469	emit frameAvailable(m_drawContext);
470	emit rewound(&m_threadContext);
471}
472
473void GameController::startRewinding() {
474	if (!m_gameOpen || m_rewindTimer.isActive()) {
475		return;
476	}
477	m_wasPaused = isPaused();
478	setPaused(true);
479	m_rewindTimer.start();
480}
481
482void GameController::stopRewinding() {
483	if (!m_rewindTimer.isActive()) {
484		return;
485	}
486	m_rewindTimer.stop();
487	setPaused(m_wasPaused);
488}
489
490void GameController::keyPressed(int key) {
491	int mappedKey = 1 << key;
492	m_activeKeys |= mappedKey;
493	if (!m_inputController->allowOpposing()) {
494		if ((m_activeKeys & 0x30) == 0x30) {
495			m_inactiveKeys |= mappedKey ^ 0x30;
496			m_activeKeys ^= mappedKey ^ 0x30;
497		}
498		if ((m_activeKeys & 0xC0) == 0xC0) {
499			m_inactiveKeys |= mappedKey ^ 0xC0;
500			m_activeKeys ^= mappedKey ^ 0xC0;
501		}
502	}
503	updateKeys();
504}
505
506void GameController::keyReleased(int key) {
507	int mappedKey = 1 << key;
508	m_activeKeys &= ~mappedKey;
509	if (!m_inputController->allowOpposing()) {
510		if (mappedKey & 0x30) {
511			m_activeKeys |= m_inactiveKeys & (0x30 ^ mappedKey);
512			m_inactiveKeys &= ~0x30;
513		}
514		if (mappedKey & 0xC0) {
515			m_activeKeys |= m_inactiveKeys & (0xC0 ^ mappedKey);
516			m_inactiveKeys &= ~0xC0;
517		}
518	}
519	updateKeys();
520}
521
522void GameController::clearKeys() {
523	m_activeKeys = 0;
524	m_inactiveKeys = 0;
525	updateKeys();
526}
527
528void GameController::setAudioBufferSamples(int samples) {
529	threadInterrupt();
530	redoSamples(samples);
531	threadContinue();
532	QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
533}
534
535void GameController::setFPSTarget(float fps) {
536	threadInterrupt();
537	m_fpsTarget = fps;
538	m_threadContext.fpsTarget = fps;
539	if (m_turbo && m_turboSpeed > 0) {
540		m_threadContext.fpsTarget *= m_turboSpeed;
541	}
542	redoSamples(m_audioProcessor->getBufferSamples());
543	threadContinue();
544}
545
546void GameController::setSkipBIOS(bool set) {
547	threadInterrupt();
548	m_threadContext.skipBios = set;
549	threadContinue();
550}
551
552void GameController::setUseBIOS(bool use) {
553	threadInterrupt();
554	m_useBios = use;
555	threadContinue();
556}
557
558void GameController::loadState(int slot) {
559	if (slot > 0) {
560		m_stateSlot = slot;
561	}
562	GBARunOnThread(&m_threadContext, [](GBAThread* context) {
563		GameController* controller = static_cast<GameController*>(context->userData);
564		if (GBALoadState(context, context->stateDir, controller->m_stateSlot)) {
565			controller->frameAvailable(controller->m_drawContext);
566			controller->stateLoaded(context);
567		}
568	});
569}
570
571void GameController::saveState(int slot) {
572	if (slot > 0) {
573		m_stateSlot = slot;
574	}
575	GBARunOnThread(&m_threadContext, [](GBAThread* context) {
576		GameController* controller = static_cast<GameController*>(context->userData);
577		GBASaveState(context, context->stateDir, controller->m_stateSlot, true);
578	});
579}
580
581void GameController::setVideoSync(bool set) {
582	m_videoSync = set;
583	if (!m_turbo) {
584		threadInterrupt();
585		m_threadContext.sync.videoFrameWait = set;
586		threadContinue();
587	}
588}
589
590void GameController::setAudioSync(bool set) {
591	m_audioSync = set;
592	if (!m_turbo) {
593		threadInterrupt();
594		m_threadContext.sync.audioWait = set;
595		threadContinue();
596	}
597}
598
599void GameController::setFrameskip(int skip) {
600	m_threadContext.frameskip = skip;
601}
602
603void GameController::setVolume(int volume) {
604	threadInterrupt();
605	m_threadContext.volume = volume;
606	if (m_gameOpen) {
607		m_threadContext.gba->audio.masterVolume = volume;
608	}
609	threadContinue();
610}
611
612void GameController::setMute(bool mute) {
613	threadInterrupt();
614	m_threadContext.mute = mute;
615	if (m_gameOpen) {
616		m_threadContext.gba->audio.masterVolume = mute ? 0 : m_threadContext.volume;
617	}
618	threadContinue();
619}
620
621void GameController::setTurbo(bool set, bool forced) {
622	if (m_turboForced && !forced) {
623		return;
624	}
625	if (m_turbo == set && m_turboForced == forced) {
626		// Don't interrupt the thread if we don't need to
627		return;
628	}
629	m_turbo = set;
630	m_turboForced = set && forced;
631	enableTurbo();
632}
633
634void GameController::setTurboSpeed(float ratio) {
635	m_turboSpeed = ratio;
636	enableTurbo();
637}
638
639void GameController::enableTurbo() {
640	threadInterrupt();
641	if (!m_turbo) {
642		m_threadContext.fpsTarget = m_fpsTarget;
643		m_threadContext.sync.audioWait = m_audioSync;
644		m_threadContext.sync.videoFrameWait = m_videoSync;
645	} else if (m_turboSpeed <= 0) {
646		m_threadContext.fpsTarget = m_fpsTarget;
647		m_threadContext.sync.audioWait = false;
648		m_threadContext.sync.videoFrameWait = false;
649	} else {
650		m_threadContext.fpsTarget = m_fpsTarget * m_turboSpeed;
651		m_threadContext.sync.audioWait = true;
652		m_threadContext.sync.videoFrameWait = false;
653	}
654	redoSamples(m_audioProcessor->getBufferSamples());
655	threadContinue();
656}
657
658void GameController::setAVStream(GBAAVStream* stream) {
659	threadInterrupt();
660	m_threadContext.stream = stream;
661	if (m_gameOpen) {
662		m_threadContext.gba->stream = stream;
663	}
664	threadContinue();
665}
666
667void GameController::clearAVStream() {
668	threadInterrupt();
669	m_threadContext.stream = nullptr;
670	if (m_gameOpen) {
671		m_threadContext.gba->stream = nullptr;
672	}
673	threadContinue();
674}
675
676#ifdef USE_PNG
677void GameController::screenshot() {
678	GBARunOnThread(&m_threadContext, GBAThreadTakeScreenshot);
679}
680#endif
681
682void GameController::reloadAudioDriver() {
683	QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
684	int samples = m_audioProcessor->getBufferSamples();
685	delete m_audioProcessor;
686	m_audioProcessor = AudioProcessor::create();
687	m_audioProcessor->setBufferSamples(samples);
688	m_audioProcessor->moveToThread(m_audioThread);
689	connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
690	connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
691	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
692	connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
693	if (isLoaded()) {
694		m_audioProcessor->setInput(&m_threadContext);
695		QMetaObject::invokeMethod(m_audioProcessor, "start");
696	}
697}
698
699void GameController::setLuminanceValue(uint8_t value) {
700	m_luxValue = value;
701	value = std::max<int>(value - 0x16, 0);
702	m_luxLevel = 10;
703	for (int i = 0; i < 10; ++i) {
704		if (value < LUX_LEVELS[i]) {
705			m_luxLevel = i;
706			break;
707		}
708	}
709	emit luminanceValueChanged(m_luxValue);
710}
711
712void GameController::setLuminanceLevel(int level) {
713	int value = 0x16;
714	level = std::max(0, std::min(10, level));
715	if (level > 0) {
716		value += LUX_LEVELS[level - 1];
717	}
718	setLuminanceValue(value);
719}
720
721void GameController::setRealTime() {
722	m_rtc.override = GBARTCGenericSource::RTC_NO_OVERRIDE;
723}
724
725void GameController::setFixedTime(const QDateTime& time) {
726	m_rtc.override = GBARTCGenericSource::RTC_FIXED;
727	m_rtc.value = time.toMSecsSinceEpoch() / 1000;
728}
729
730void GameController::setFakeEpoch(const QDateTime& time) {
731	m_rtc.override = GBARTCGenericSource::RTC_FAKE_EPOCH;
732	m_rtc.value = time.toMSecsSinceEpoch() / 1000;
733}
734
735void GameController::updateKeys() {
736	int activeKeys = m_activeKeys;
737	activeKeys |= m_activeButtons;
738	activeKeys &= ~m_inactiveKeys;
739	m_threadContext.activeKeys = activeKeys;
740}
741
742void GameController::redoSamples(int samples) {
743#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
744	float sampleRate = 0x8000;
745	float ratio;
746	if (m_threadContext.gba) {
747		sampleRate = m_threadContext.gba->audio.sampleRate;
748	}
749	ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, 44100);
750	m_threadContext.audioBuffers = ceil(samples / ratio);
751#else
752	m_threadContext.audioBuffers = samples;
753#endif
754	if (m_threadContext.gba) {
755		GBAAudioResizeBuffer(&m_threadContext.gba->audio, m_threadContext.audioBuffers);
756	}
757	QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
758}
759
760void GameController::setLogLevel(int levels) {
761	threadInterrupt();
762	m_logLevels = levels;
763	threadContinue();
764}
765
766void GameController::enableLogLevel(int levels) {
767	threadInterrupt();
768	m_logLevels |= levels;
769	threadContinue();
770}
771
772void GameController::disableLogLevel(int levels) {
773	threadInterrupt();
774	m_logLevels &= ~levels;
775	threadContinue();
776}
777
778void GameController::pollEvents() {
779	if (!m_inputController) {
780		return;
781	}
782
783	m_activeButtons = m_inputController->pollEvents();
784	updateKeys();
785}