all repos — mgba @ 8ff6d778690fa215f778f7e8b22b59b38f19bd7a

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 "renderers/video-software.h"
 11#include "util/vfs.h"
 12}
 13
 14using namespace QGBA;
 15
 16GameController::GameController(QObject* parent)
 17	: QObject(parent)
 18	, m_drawContext(new uint32_t[256 * 256])
 19	, m_activeKeys(0)
 20	, m_rom(nullptr)
 21	, m_audioThread(new QThread(this))
 22	, m_audioProcessor(new AudioProcessor)
 23{
 24#ifdef BUILD_SDL
 25	SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
 26	GBASDLInitEvents(&m_sdlEvents);
 27	SDL_JoystickEventState(SDL_QUERY);
 28#endif
 29	m_renderer = new GBAVideoSoftwareRenderer;
 30	GBAVideoSoftwareRendererCreate(m_renderer);
 31	m_renderer->outputBuffer = (color_t*) m_drawContext;
 32	m_renderer->outputBufferStride = 256;
 33	m_threadContext = {
 34		.state = THREAD_INITIALIZED,
 35		.debugger = 0,
 36		.frameskip = 0,
 37		.bios = 0,
 38		.renderer = &m_renderer->d,
 39		.userData = this,
 40		.rewindBufferCapacity = 0
 41	};
 42	m_threadContext.startCallback = [] (GBAThread* context) {
 43		GameController* controller = static_cast<GameController*>(context->userData);
 44		controller->m_audioProcessor->setInput(context);
 45		controller->gameStarted(context);
 46	};
 47
 48	m_threadContext.cleanCallback = [] (GBAThread* context) {
 49		GameController* controller = static_cast<GameController*>(context->userData);
 50		controller->gameStopped(context);
 51	};
 52
 53	m_threadContext.frameCallback = [] (GBAThread* context) {
 54		GameController* controller = static_cast<GameController*>(context->userData);
 55		controller->m_pauseMutex.lock();
 56		if (controller->m_pauseAfterFrame) {
 57			GBAThreadPause(context);
 58			controller->m_pauseAfterFrame = false;
 59		}
 60		controller->m_pauseMutex.unlock();
 61		controller->frameAvailable(controller->m_drawContext);
 62	};
 63
 64	m_audioThread->start();
 65	m_audioProcessor->moveToThread(m_audioThread);
 66	connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
 67
 68#ifdef BUILD_SDL
 69	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
 70#endif
 71}
 72
 73GameController::~GameController() {
 74	m_audioProcessor->pause();
 75	m_audioThread->quit();
 76	if (GBAThreadIsPaused(&m_threadContext)) {
 77		GBAThreadUnpause(&m_threadContext);
 78	}
 79	GBAThreadEnd(&m_threadContext);
 80	GBAThreadJoin(&m_threadContext);
 81	delete m_renderer;
 82}
 83
 84ARMDebugger* GameController::debugger() {
 85	return m_threadContext.debugger;
 86}
 87
 88void GameController::setDebugger(ARMDebugger* debugger) {
 89	bool wasPaused = isPaused();
 90	setPaused(true);
 91	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 92		GBADetachDebugger(m_threadContext.gba);
 93	}
 94	m_threadContext.debugger = debugger;
 95	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 96		GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
 97	}
 98	setPaused(wasPaused);
 99}
100
101void GameController::loadGame(const QString& path) {
102	closeGame();
103	m_threadContext.sync.videoFrameWait = 0;
104	m_threadContext.sync.audioWait = 1;
105	m_rom = new QFile(path);
106	if (!m_rom->open(QIODevice::ReadOnly)) {
107		delete m_rom;
108		m_rom = nullptr;
109	}
110
111	m_pauseAfterFrame = false;
112
113	m_threadContext.rom = VFileFromFD(m_rom->handle());
114	m_threadContext.fname = path.toLocal8Bit().constData();
115
116	GBAThreadStart(&m_threadContext);
117}
118
119void GameController::closeGame() {
120	if (!m_rom) {
121		return;
122	}
123	GBAThreadEnd(&m_threadContext);
124	GBAThreadJoin(&m_threadContext);
125	if (m_rom) {
126		m_rom->close();
127		delete m_rom;
128		m_rom = nullptr;
129	}
130	emit gameStopped(&m_threadContext);
131}
132
133bool GameController::isPaused() {
134	return GBAThreadIsPaused(&m_threadContext);
135}
136
137void GameController::setPaused(bool paused) {
138	if (paused == GBAThreadIsPaused(&m_threadContext)) {
139		return;
140	}
141	if (paused) {
142		GBAThreadPause(&m_threadContext);
143		m_audioProcessor->pause();
144	} else {
145		m_audioProcessor->start();
146		GBAThreadUnpause(&m_threadContext);
147	}
148}
149
150void GameController::reset() {
151	GBAThreadReset(&m_threadContext);
152}
153
154void GameController::frameAdvance() {
155	m_pauseMutex.lock();
156	m_pauseAfterFrame = true;
157	setPaused(false);
158	m_pauseMutex.unlock();
159}
160
161void GameController::keyPressed(int key) {
162	int mappedKey = 1 << key;
163	m_activeKeys |= mappedKey;
164	updateKeys();
165}
166
167void GameController::keyReleased(int key) {
168	int mappedKey = 1 << key;
169	m_activeKeys &= ~mappedKey;
170	updateKeys();
171}
172
173void GameController::setAudioBufferSamples(int samples) {
174	GBAThreadInterrupt(&m_threadContext);
175	m_threadContext.audioBuffers = samples;
176	GBAAudioResizeBuffer(&m_threadContext.gba->audio, samples);
177	GBAThreadContinue(&m_threadContext);
178	QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
179}
180
181void GameController::updateKeys() {
182	int activeKeys = m_activeKeys;
183#ifdef BUILD_SDL
184	activeKeys |= m_activeButtons;
185#endif
186	m_threadContext.activeKeys = activeKeys;
187}
188
189#ifdef BUILD_SDL
190void GameController::testSDLEvents() {
191	SDL_Joystick* joystick = m_sdlEvents.joystick;
192	SDL_JoystickUpdate();
193	int numButtons = SDL_JoystickNumButtons(joystick);
194	m_activeButtons = 0;
195	int i;
196	for (i = 0; i < numButtons; ++i) {
197		GBAKey key = GBASDLMapButtonToKey(i);
198		if (key == GBA_KEY_NONE) {
199			continue;
200		}
201		if (SDL_JoystickGetButton(joystick, i)) {
202			m_activeButtons |= 1 << key;
203		}
204	}
205	int numHats = SDL_JoystickNumHats(joystick);
206	for (i = 0; i < numHats; ++i) {
207		int hat = SDL_JoystickGetHat(joystick, i);
208		if (hat & SDL_HAT_UP) {
209			m_activeButtons |= 1 << GBA_KEY_UP;
210		}
211		if (hat & SDL_HAT_LEFT) {
212			m_activeButtons |= 1 << GBA_KEY_LEFT;
213		}
214		if (hat & SDL_HAT_DOWN) {
215			m_activeButtons |= 1 << GBA_KEY_DOWN;
216		}
217		if (hat & SDL_HAT_RIGHT) {
218			m_activeButtons |= 1 << GBA_KEY_RIGHT;
219		}
220	}
221	updateKeys();
222}
223#endif