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