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