all repos — mgba @ 688be6948bedf51f1cabf6404b6dec8116e04599

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