all repos — mgba @ b0662fe76625ba45ebdf5bf387e1673c030fe5b0

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	connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
 68	connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
 69	connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
 70
 71#ifdef BUILD_SDL
 72	connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
 73#endif
 74}
 75
 76GameController::~GameController() {
 77	m_audioThread->quit();
 78	if (GBAThreadIsPaused(&m_threadContext)) {
 79		GBAThreadUnpause(&m_threadContext);
 80	}
 81	GBAThreadEnd(&m_threadContext);
 82	GBAThreadJoin(&m_threadContext);
 83	delete m_renderer;
 84}
 85
 86ARMDebugger* GameController::debugger() {
 87	return m_threadContext.debugger;
 88}
 89
 90void GameController::setDebugger(ARMDebugger* debugger) {
 91	bool wasPaused = isPaused();
 92	setPaused(true);
 93	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 94		GBADetachDebugger(m_threadContext.gba);
 95	}
 96	m_threadContext.debugger = debugger;
 97	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 98		GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
 99	}
100	setPaused(wasPaused);
101}
102
103void GameController::loadGame(const QString& path) {
104	closeGame();
105	m_threadContext.sync.videoFrameWait = 0;
106	m_threadContext.sync.audioWait = 1;
107	m_rom = new QFile(path);
108	if (!m_rom->open(QIODevice::ReadOnly)) {
109		delete m_rom;
110		m_rom = nullptr;
111	}
112
113	m_pauseAfterFrame = false;
114
115	m_threadContext.rom = VFileFromFD(m_rom->handle());
116	m_threadContext.fname = path.toLocal8Bit().constData();
117
118	GBAThreadStart(&m_threadContext);
119}
120
121void GameController::closeGame() {
122	if (!m_rom) {
123		return;
124	}
125	GBAThreadEnd(&m_threadContext);
126	GBAThreadJoin(&m_threadContext);
127	if (m_rom) {
128		m_rom->close();
129		delete m_rom;
130		m_rom = nullptr;
131	}
132	emit gameStopped(&m_threadContext);
133}
134
135bool GameController::isPaused() {
136	return GBAThreadIsPaused(&m_threadContext);
137}
138
139void GameController::setPaused(bool paused) {
140	if (paused == GBAThreadIsPaused(&m_threadContext)) {
141		return;
142	}
143	if (paused) {
144		GBAThreadPause(&m_threadContext);
145		emit gamePaused(&m_threadContext);
146	} else {
147		GBAThreadUnpause(&m_threadContext);
148		emit gameUnpaused(&m_threadContext);
149	}
150}
151
152void GameController::reset() {
153	GBAThreadReset(&m_threadContext);
154}
155
156void GameController::frameAdvance() {
157	m_pauseMutex.lock();
158	m_pauseAfterFrame = true;
159	setPaused(false);
160	m_pauseMutex.unlock();
161}
162
163void GameController::keyPressed(int key) {
164	int mappedKey = 1 << key;
165	m_activeKeys |= mappedKey;
166	updateKeys();
167}
168
169void GameController::keyReleased(int key) {
170	int mappedKey = 1 << key;
171	m_activeKeys &= ~mappedKey;
172	updateKeys();
173}
174
175void GameController::setAudioBufferSamples(int samples) {
176	GBAThreadInterrupt(&m_threadContext);
177	m_threadContext.audioBuffers = samples;
178	GBAAudioResizeBuffer(&m_threadContext.gba->audio, samples);
179	GBAThreadContinue(&m_threadContext);
180	QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
181}
182
183void GameController::updateKeys() {
184	int activeKeys = m_activeKeys;
185#ifdef BUILD_SDL
186	activeKeys |= m_activeButtons;
187#endif
188	m_threadContext.activeKeys = activeKeys;
189}
190
191#ifdef BUILD_SDL
192void GameController::testSDLEvents() {
193	SDL_Joystick* joystick = m_sdlEvents.joystick;
194	SDL_JoystickUpdate();
195	int numButtons = SDL_JoystickNumButtons(joystick);
196	m_activeButtons = 0;
197	int i;
198	for (i = 0; i < numButtons; ++i) {
199		GBAKey key = GBASDLMapButtonToKey(i);
200		if (key == GBA_KEY_NONE) {
201			continue;
202		}
203		if (SDL_JoystickGetButton(joystick, i)) {
204			m_activeButtons |= 1 << key;
205		}
206	}
207	int numHats = SDL_JoystickNumHats(joystick);
208	for (i = 0; i < numHats; ++i) {
209		int hat = SDL_JoystickGetHat(joystick, i);
210		if (hat & SDL_HAT_UP) {
211			m_activeButtons |= 1 << GBA_KEY_UP;
212		}
213		if (hat & SDL_HAT_LEFT) {
214			m_activeButtons |= 1 << GBA_KEY_LEFT;
215		}
216		if (hat & SDL_HAT_DOWN) {
217			m_activeButtons |= 1 << GBA_KEY_DOWN;
218		}
219		if (hat & SDL_HAT_RIGHT) {
220			m_activeButtons |= 1 << GBA_KEY_RIGHT;
221		}
222	}
223	updateKeys();
224}
225#endif