all repos — mgba @ 51b8c862b969390582cb39b3ac98239cdea9a4b1

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