all repos — mgba @ 943b805306a362a0c20bbe6d7d4ee660cc75f036

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