all repos — mgba @ a77f7f0be23b333891bba627730d47f5b9e23d25

mGBA Game Boy Advance Emulator

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

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