all repos — mgba @ ad12bdde9d34ecd477b33e02d02d8d41c115b817

mGBA Game Boy Advance Emulator

src/platform/qt/GameController.cpp (view raw)

  1#include "GameController.h"
  2
  3#include "AudioProcessor.h"
  4
  5#include <QThread>
  6
  7extern "C" {
  8#include "gba.h"
  9#include "gba-audio.h"
 10#include "gba-serialize.h"
 11#include "renderers/video-software.h"
 12#include "util/vfs.h"
 13}
 14
 15using namespace QGBA;
 16
 17GameController::GameController(QObject* parent)
 18	: QObject(parent)
 19	, m_drawContext(new uint32_t[256 * 256])
 20	, m_threadContext()
 21	, m_activeKeys(0)
 22	, m_gameOpen(false)
 23	, m_audioThread(new QThread(this))
 24	, m_audioProcessor(new AudioProcessor)
 25{
 26	m_renderer = new GBAVideoSoftwareRenderer;
 27	GBAVideoSoftwareRendererCreate(m_renderer);
 28	m_renderer->outputBuffer = (color_t*) m_drawContext;
 29	m_renderer->outputBufferStride = 256;
 30	m_threadContext.state = THREAD_INITIALIZED;
 31	m_threadContext.debugger = 0;
 32	m_threadContext.frameskip = 0;
 33	m_threadContext.bios = 0;
 34	m_threadContext.renderer = &m_renderer->d;
 35	m_threadContext.userData = this;
 36	m_threadContext.rewindBufferCapacity = 0;
 37	m_threadContext.logLevel = -1;
 38
 39	GBAInputMapInit(&m_threadContext.inputMap);
 40
 41#ifdef BUILD_SDL
 42	SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_NOPARACHUTE);
 43	m_sdlEvents.bindings = &m_threadContext.inputMap;
 44	GBASDLInitEvents(&m_sdlEvents);
 45	SDL_JoystickEventState(SDL_QUERY);
 46#endif
 47
 48	m_threadContext.startCallback = [] (GBAThread* context) {
 49		GameController* controller = static_cast<GameController*>(context->userData);
 50		controller->m_audioProcessor->setInput(context);
 51		controller->gameStarted(context);
 52	};
 53
 54	m_threadContext.cleanCallback = [] (GBAThread* context) {
 55		GameController* controller = static_cast<GameController*>(context->userData);
 56		controller->gameStopped(context);
 57	};
 58
 59	m_threadContext.frameCallback = [] (GBAThread* context) {
 60		GameController* controller = static_cast<GameController*>(context->userData);
 61		controller->m_pauseMutex.lock();
 62		if (controller->m_pauseAfterFrame) {
 63			GBAThreadPauseFromThread(context);
 64			controller->m_pauseAfterFrame = false;
 65			controller->gamePaused(&controller->m_threadContext);
 66		}
 67		controller->m_pauseMutex.unlock();
 68		controller->frameAvailable(controller->m_drawContext);
 69	};
 70
 71	m_threadContext.logHandler = [] (GBAThread* context, enum GBALogLevel level, const char* format, va_list args) {
 72		GameController* controller = static_cast<GameController*>(context->userData);
 73		controller->postLog(level, QString().vsprintf(format, args));
 74	};
 75
 76	m_audioThread->start(QThread::TimeCriticalPriority);
 77	m_audioProcessor->moveToThread(m_audioThread);
 78	connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
 79	connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
 80	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
 81	connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
 82
 83#ifdef BUILD_SDL
 84	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
 85#endif
 86}
 87
 88GameController::~GameController() {
 89	m_audioThread->quit();
 90	m_audioThread->wait();
 91	if (GBAThreadIsPaused(&m_threadContext)) {
 92		GBAThreadUnpause(&m_threadContext);
 93	}
 94	disconnect();
 95	closeGame();
 96	delete m_renderer;
 97}
 98
 99ARMDebugger* GameController::debugger() {
100	return m_threadContext.debugger;
101}
102
103void GameController::setDebugger(ARMDebugger* debugger) {
104	bool wasPaused = isPaused();
105	setPaused(true);
106	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
107		GBADetachDebugger(m_threadContext.gba);
108	}
109	m_threadContext.debugger = debugger;
110	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
111		GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
112	}
113	setPaused(wasPaused);
114}
115
116void GameController::loadGame(const QString& path, bool dirmode) {
117	closeGame();
118	m_threadContext.sync.videoFrameWait = 0;
119	m_threadContext.sync.audioWait = 1;
120	if (!dirmode) {
121		QFile file(path);
122		if (!file.open(QIODevice::ReadOnly)) {
123			return;
124		}
125		file.close();
126	}
127	m_gameOpen = true;
128
129	m_pauseAfterFrame = false;
130
131	m_threadContext.fname = strdup(path.toLocal8Bit().constData());
132	if (dirmode) {
133		m_threadContext.gameDir = VDirOpen(m_threadContext.fname);
134		m_threadContext.stateDir = m_threadContext.gameDir;
135	} else {
136		m_threadContext.rom = VFileOpen(m_threadContext.fname, O_RDONLY);
137#if ENABLE_LIBZIP
138		m_threadContext.gameDir = VDirOpenZip(m_threadContext.fname, 0);
139#endif
140	}
141
142	if (!m_bios.isNull()) {
143		m_threadContext.bios = VFileOpen(m_bios.toLocal8Bit().constData(), O_RDONLY);
144	}
145
146	GBAThreadStart(&m_threadContext);
147}
148
149void GameController::loadBIOS(const QString& path) {
150	m_bios = path;
151}
152
153void GameController::closeGame() {
154	if (!m_gameOpen) {
155		return;
156	}
157	GBAThreadEnd(&m_threadContext);
158	GBAThreadJoin(&m_threadContext);
159	if (m_threadContext.fname) {
160		free(const_cast<char*>(m_threadContext.fname));
161		m_threadContext.fname = nullptr;
162	}
163
164	m_gameOpen = false;
165	emit gameStopped(&m_threadContext);
166}
167
168bool GameController::isPaused() {
169	return GBAThreadIsPaused(&m_threadContext);
170}
171
172void GameController::setPaused(bool paused) {
173	if (paused == GBAThreadIsPaused(&m_threadContext)) {
174		return;
175	}
176	if (paused) {
177		GBAThreadPause(&m_threadContext);
178		emit gamePaused(&m_threadContext);
179	} else {
180		GBAThreadUnpause(&m_threadContext);
181		emit gameUnpaused(&m_threadContext);
182	}
183}
184
185void GameController::reset() {
186	GBAThreadReset(&m_threadContext);
187}
188
189void GameController::frameAdvance() {
190	m_pauseMutex.lock();
191	m_pauseAfterFrame = true;
192	setPaused(false);
193	m_pauseMutex.unlock();
194}
195
196void GameController::keyPressed(int key) {
197	int mappedKey = 1 << key;
198	m_activeKeys |= mappedKey;
199	updateKeys();
200}
201
202void GameController::keyReleased(int key) {
203	int mappedKey = 1 << key;
204	m_activeKeys &= ~mappedKey;
205	updateKeys();
206}
207
208void GameController::setAudioBufferSamples(int samples) {
209	GBAThreadInterrupt(&m_threadContext);
210	m_threadContext.audioBuffers = samples;
211	GBAAudioResizeBuffer(&m_threadContext.gba->audio, samples);
212	GBAThreadContinue(&m_threadContext);
213	QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
214}
215
216void GameController::setFPSTarget(float fps) {
217	GBAThreadInterrupt(&m_threadContext);
218	m_threadContext.fpsTarget = fps;
219	GBAThreadContinue(&m_threadContext);
220	QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
221}
222
223void GameController::loadState(int slot) {
224	GBAThreadInterrupt(&m_threadContext);
225	GBALoadState(m_threadContext.gba, m_threadContext.stateDir, slot);
226	GBAThreadContinue(&m_threadContext);
227	emit stateLoaded(&m_threadContext);
228	emit frameAvailable(m_drawContext);
229}
230
231void GameController::saveState(int slot) {
232	GBAThreadInterrupt(&m_threadContext);
233	GBASaveState(m_threadContext.gba, m_threadContext.stateDir, slot, true);
234	GBAThreadContinue(&m_threadContext);
235}
236
237void GameController::updateKeys() {
238	int activeKeys = m_activeKeys;
239#ifdef BUILD_SDL
240	activeKeys |= m_activeButtons;
241#endif
242	m_threadContext.activeKeys = activeKeys;
243}
244
245#ifdef BUILD_SDL
246void GameController::testSDLEvents() {
247	SDL_Joystick* joystick = m_sdlEvents.joystick;
248	SDL_JoystickUpdate();
249	int numButtons = SDL_JoystickNumButtons(joystick);
250	m_activeButtons = 0;
251	int i;
252	for (i = 0; i < numButtons; ++i) {
253		GBAKey key = GBAInputMapKey(&m_threadContext.inputMap, SDL_BINDING_BUTTON, i);
254		if (key == GBA_KEY_NONE) {
255			continue;
256		}
257		if (SDL_JoystickGetButton(joystick, i)) {
258			m_activeButtons |= 1 << key;
259		}
260	}
261	int numHats = SDL_JoystickNumHats(joystick);
262	for (i = 0; i < numHats; ++i) {
263		int hat = SDL_JoystickGetHat(joystick, i);
264		if (hat & SDL_HAT_UP) {
265			m_activeButtons |= 1 << GBA_KEY_UP;
266		}
267		if (hat & SDL_HAT_LEFT) {
268			m_activeButtons |= 1 << GBA_KEY_LEFT;
269		}
270		if (hat & SDL_HAT_DOWN) {
271			m_activeButtons |= 1 << GBA_KEY_DOWN;
272		}
273		if (hat & SDL_HAT_RIGHT) {
274			m_activeButtons |= 1 << GBA_KEY_RIGHT;
275		}
276	}
277	updateKeys();
278}
279#endif