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