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