all repos — mgba @ e9a2b2a57c4f478fb3600fe9d87e1b97804d73de

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