all repos — mgba @ 7f592f78e87d6bbbae650d51c0d08f6919d91ed3

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 && GBAThreadHasStarted(&m_threadContext)) {
174		GBADetachDebugger(m_threadContext.gba);
175	}
176	m_threadContext.debugger = debugger;
177	if (m_threadContext.debugger && GBAThreadHasStarted(&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 ENABLE_LIBZIP
220		if (!m_threadContext.gameDir) {
221			m_threadContext.gameDir = VDirOpenZip(m_threadContext.fname, 0);
222		}
223#endif
224#if ENABLE_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	m_patch = path;
258	if (m_gameOpen) {
259		closeGame();
260		openGame();
261	}
262}
263
264void GameController::closeGame() {
265	if (!m_gameOpen) {
266		return;
267	}
268	if (GBAThreadIsPaused(&m_threadContext)) {
269		GBAThreadUnpause(&m_threadContext);
270	}
271	GBAThreadEnd(&m_threadContext);
272	GBAThreadJoin(&m_threadContext);
273	if (m_threadContext.fname) {
274		free(const_cast<char*>(m_threadContext.fname));
275		m_threadContext.fname = nullptr;
276	}
277
278	m_patch = QString();
279
280	for (size_t i = 0; i < GBACheatSetsSize(&m_cheatDevice.cheats); ++i) {
281		GBACheatSet* set = *GBACheatSetsGetPointer(&m_cheatDevice.cheats, i);
282		GBACheatSetDeinit(set);
283		delete set;
284	}
285	GBACheatSetsClear(&m_cheatDevice.cheats);
286
287	m_gameOpen = false;
288	emit gameStopped(&m_threadContext);
289}
290
291void GameController::crashGame(const QString& crashMessage) {
292	closeGame();
293	emit gameCrashed(crashMessage);
294}
295
296bool GameController::isPaused() {
297	if (!m_gameOpen) {
298		return false;
299	}
300	return GBAThreadIsPaused(&m_threadContext);
301}
302
303void GameController::setPaused(bool paused) {
304	if (paused == GBAThreadIsPaused(&m_threadContext)) {
305		return;
306	}
307	if (paused) {
308		GBAThreadPause(&m_threadContext);
309		emit gamePaused(&m_threadContext);
310	} else {
311		GBAThreadUnpause(&m_threadContext);
312		emit gameUnpaused(&m_threadContext);
313	}
314}
315
316void GameController::reset() {
317	GBAThreadReset(&m_threadContext);
318}
319
320void GameController::threadInterrupt() {
321	if (m_gameOpen) {
322		GBAThreadInterrupt(&m_threadContext);
323	}
324}
325
326void GameController::threadContinue() {
327	if (m_gameOpen) {
328		GBAThreadContinue(&m_threadContext);
329	}
330}
331
332void GameController::frameAdvance() {
333	m_pauseMutex.lock();
334	m_pauseAfterFrame = true;
335	setPaused(false);
336	m_pauseMutex.unlock();
337}
338
339void GameController::setRewind(bool enable, int capacity, int interval) {
340	if (m_gameOpen) {
341		threadInterrupt();
342		GBARewindSettingsChanged(&m_threadContext, enable ? capacity : 0, enable ? interval : 0);
343		threadContinue();
344	} else {
345		if (enable) {
346			m_threadContext.rewindBufferInterval = interval;
347			m_threadContext.rewindBufferCapacity = capacity;
348		} else {
349			m_threadContext.rewindBufferInterval = 0;
350			m_threadContext.rewindBufferCapacity = 0;
351		}
352	}
353}
354
355void GameController::rewind(int states) {
356	threadInterrupt();
357	if (!states) {
358		GBARewindAll(&m_threadContext);
359	} else {
360		GBARewind(&m_threadContext, states);
361	}
362	threadContinue();
363}
364
365void GameController::keyPressed(int key) {
366	int mappedKey = 1 << key;
367	m_activeKeys |= mappedKey;
368	updateKeys();
369}
370
371void GameController::keyReleased(int key) {
372	int mappedKey = 1 << key;
373	m_activeKeys &= ~mappedKey;
374	updateKeys();
375}
376
377void GameController::clearKeys() {
378	m_activeKeys = 0;
379	updateKeys();
380}
381
382void GameController::setAudioBufferSamples(int samples) {
383	threadInterrupt();
384	redoSamples(samples);
385	threadContinue();
386	QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
387}
388
389void GameController::setFPSTarget(float fps) {
390	threadInterrupt();
391	m_threadContext.fpsTarget = fps;
392	redoSamples(m_audioProcessor->getBufferSamples());
393	threadContinue();
394	QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
395}
396
397void GameController::setSkipBIOS(bool set) {
398	threadInterrupt();
399	m_threadContext.skipBios = set;
400	threadContinue();
401}
402
403void GameController::loadState(int slot) {
404	threadInterrupt();
405	GBALoadState(&m_threadContext, m_threadContext.stateDir, slot);
406	threadContinue();
407	emit stateLoaded(&m_threadContext);
408	emit frameAvailable(m_drawContext);
409}
410
411void GameController::saveState(int slot) {
412	threadInterrupt();
413	GBASaveState(&m_threadContext, m_threadContext.stateDir, slot, true);
414	threadContinue();
415}
416
417void GameController::setVideoSync(bool set) {
418	m_videoSync = set;
419	if (!m_turbo) {
420		threadInterrupt();
421		m_threadContext.sync.videoFrameWait = set;
422		threadContinue();
423	}
424}
425
426void GameController::setAudioSync(bool set) {
427	m_audioSync = set;
428	if (!m_turbo) {
429		threadInterrupt();
430		m_threadContext.sync.audioWait = set;
431		threadContinue();
432	}
433}
434
435void GameController::setFrameskip(int skip) {
436	m_threadContext.frameskip = skip;
437}
438
439void GameController::setTurbo(bool set, bool forced) {
440	if (m_turboForced && !forced) {
441		return;
442	}
443	m_turbo = set;
444	m_turboForced = set && forced;
445	threadInterrupt();
446	m_threadContext.sync.audioWait = set ? false : m_audioSync;
447	m_threadContext.sync.videoFrameWait = set ? false : m_videoSync;
448	threadContinue();
449}
450
451void GameController::setAVStream(GBAAVStream* stream) {
452	threadInterrupt();
453	m_threadContext.stream = stream;
454	threadContinue();
455}
456
457void GameController::clearAVStream() {
458	threadInterrupt();
459	m_threadContext.stream = nullptr;
460	threadContinue();
461}
462
463void GameController::reloadAudioDriver() {
464	m_audioProcessor->pause();
465	delete m_audioProcessor;
466	m_audioProcessor = AudioProcessor::create();
467	m_audioProcessor->moveToThread(m_audioThread);
468	connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
469	connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
470	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
471	connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
472	if (isLoaded()) {
473		m_audioProcessor->setInput(&m_threadContext);
474		QMetaObject::invokeMethod(m_audioProcessor, "start");
475	}
476}
477
478void GameController::setLuminanceValue(uint8_t value) {
479	m_luxValue = value;
480	value = std::max<int>(value - 0x16, 0);
481	m_luxLevel = 10;
482	for (int i = 0; i < 10; ++i) {
483		if (value < LUX_LEVELS[i]) {
484			m_luxLevel = i;
485			break;
486		}
487	}
488}
489
490void GameController::setLuminanceLevel(int level) {
491	int value = 0x16;
492	level = std::max(0, std::min(10, level));
493	if (level > 0) {
494		value += LUX_LEVELS[level - 1];
495	}
496	setLuminanceValue(value);
497}
498
499void GameController::setRealTime() {
500	m_rtc.override = GameControllerRTC::NO_OVERRIDE;
501}
502
503void GameController::setFixedTime(const QDateTime& time) {
504	m_rtc.override = GameControllerRTC::FIXED;
505	m_rtc.value = time.toMSecsSinceEpoch() / 1000;
506}
507
508void GameController::setFakeEpoch(const QDateTime& time) {
509	m_rtc.override = GameControllerRTC::FAKE_EPOCH;
510	m_rtc.value = time.toMSecsSinceEpoch() / 1000;
511}
512
513void GameController::updateKeys() {
514	int activeKeys = m_activeKeys;
515#ifdef BUILD_SDL
516	activeKeys |= m_activeButtons;
517#endif
518	m_threadContext.activeKeys = activeKeys;
519}
520
521void GameController::redoSamples(int samples) {
522#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
523	float sampleRate = 0x8000;
524	float ratio;
525	if (m_threadContext.gba) {
526		sampleRate = m_threadContext.gba->audio.sampleRate;
527	}
528	ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, 44100);
529	m_threadContext.audioBuffers = ceil(samples / ratio);
530#else
531	m_threadContext.audioBuffers = samples;
532#endif
533	if (m_threadContext.gba) {
534		GBAAudioResizeBuffer(&m_threadContext.gba->audio, m_threadContext.audioBuffers);
535	}
536}
537
538void GameController::setLogLevel(int levels) {
539	threadInterrupt();
540	m_logLevels = levels;
541	threadContinue();
542}
543
544void GameController::enableLogLevel(int levels) {
545	threadInterrupt();
546	m_logLevels |= levels;
547	threadContinue();
548}
549
550void GameController::disableLogLevel(int levels) {
551	threadInterrupt();
552	m_logLevels &= ~levels;
553	threadContinue();
554}
555
556#ifdef BUILD_SDL
557void GameController::testSDLEvents() {
558	if (!m_inputController) {
559		return;
560	}
561
562	m_activeButtons = m_inputController->testSDLEvents();
563	updateKeys();
564}
565#endif