all repos — mgba @ edc6de046743bfdc777d46eeb9e5ce482b74b9d7

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}
  7
  8using namespace QGBA;
  9
 10GameController::GameController(QObject* parent)
 11	: QObject(parent)
 12	, m_drawContext(new uint32_t[256 * 256])
 13	, m_audioContext(nullptr)
 14	, m_rom(nullptr)
 15{
 16	m_renderer = new GBAVideoSoftwareRenderer;
 17	GBAVideoSoftwareRendererCreate(m_renderer);
 18	m_renderer->outputBuffer = (color_t*) m_drawContext;
 19	m_renderer->outputBufferStride = 256;
 20	m_threadContext = {
 21		.state = THREAD_INITIALIZED,
 22		.debugger = 0,
 23		.frameskip = 0,
 24		.biosFd = -1,
 25		.renderer = &m_renderer->d,
 26		.userData = this,
 27		.rewindBufferCapacity = 0
 28	};
 29	m_threadContext.startCallback = [] (GBAThread* context) {
 30		GameController* controller = static_cast<GameController*>(context->userData);
 31		controller->gameStarted(context);
 32	};
 33
 34	m_threadContext.cleanCallback = [] (GBAThread* context) {
 35		GameController* controller = static_cast<GameController*>(context->userData);
 36		controller->gameStopped(context);
 37	};
 38
 39	m_threadContext.frameCallback = [] (GBAThread* context) {
 40		GameController* controller = static_cast<GameController*>(context->userData);
 41		controller->m_pauseMutex.lock();
 42		if (controller->m_pauseAfterFrame) {
 43			GBAThreadPause(context);
 44			controller->m_pauseAfterFrame = false;
 45		}
 46		controller->m_pauseMutex.unlock();
 47		controller->frameAvailable(controller->m_drawContext);
 48	};
 49}
 50
 51GameController::~GameController() {
 52	if (GBAThreadIsPaused(&m_threadContext)) {
 53		GBAThreadUnpause(&m_threadContext);
 54	}
 55	GBAThreadEnd(&m_threadContext);
 56	GBAThreadJoin(&m_threadContext);
 57	delete m_renderer;
 58}
 59
 60ARMDebugger* GameController::debugger() {
 61	return m_threadContext.debugger;
 62}
 63
 64void GameController::setDebugger(ARMDebugger* debugger) {
 65	bool wasPaused = isPaused();
 66	setPaused(true);
 67	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 68		GBADetachDebugger(m_threadContext.gba);
 69	}
 70	m_threadContext.debugger = debugger;
 71	if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
 72		GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
 73	}
 74	setPaused(wasPaused);
 75}
 76
 77void GameController::loadGame(const QString& path) {
 78	closeGame();
 79	m_threadContext.sync.videoFrameWait = 0;
 80	m_threadContext.sync.audioWait = 1;
 81	m_rom = new QFile(path);
 82	if (!m_rom->open(QIODevice::ReadOnly)) {
 83		delete m_rom;
 84		m_rom = 0;
 85	}
 86
 87	m_pauseAfterFrame = false;
 88
 89	m_threadContext.fd = m_rom->handle();
 90	m_threadContext.fname = path.toLocal8Bit().constData();
 91	GBAThreadStart(&m_threadContext);
 92}
 93
 94void GameController::closeGame() {
 95	// TODO: Make this threadsafe
 96	if (m_threadContext.state >= THREAD_EXITING || m_threadContext.state <= THREAD_INITIALIZED) {
 97		return;
 98	}
 99	GBAThreadEnd(&m_threadContext);
100	GBAThreadJoin(&m_threadContext);
101	if (m_rom) {
102		m_rom->close();
103		delete m_rom;
104	}
105	emit gameStopped(&m_threadContext);
106}
107
108bool GameController::isPaused() {
109	return GBAThreadIsPaused(&m_threadContext);
110}
111
112void GameController::setPaused(bool paused) {
113	if (paused == GBAThreadIsPaused(&m_threadContext)) {
114		return;
115	}
116	if (paused) {
117		GBAThreadPause(&m_threadContext);
118	} else {
119		GBAThreadUnpause(&m_threadContext);
120	}
121}
122
123void GameController::frameAdvance() {
124	m_pauseMutex.lock();
125	m_pauseAfterFrame = true;
126	setPaused(false);
127	m_pauseMutex.unlock();
128}
129
130void GameController::keyPressed(int key) {
131	int mappedKey = 1 << key;
132	m_threadContext.activeKeys |= mappedKey;
133}
134
135void GameController::keyReleased(int key) {
136	int mappedKey = 1 << key;
137	m_threadContext.activeKeys &= ~mappedKey;
138}