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_activeKeys(0)
15 , m_rom(nullptr)
16{
17#ifdef BUILD_SDL
18 SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
19 GBASDLInitEvents(&m_sdlEvents);
20 SDL_JoystickEventState(SDL_QUERY);
21#endif
22 m_renderer = new GBAVideoSoftwareRenderer;
23 GBAVideoSoftwareRendererCreate(m_renderer);
24 m_renderer->outputBuffer = (color_t*) m_drawContext;
25 m_renderer->outputBufferStride = 256;
26 m_threadContext = {
27 .state = THREAD_INITIALIZED,
28 .debugger = 0,
29 .frameskip = 0,
30 .biosFd = -1,
31 .renderer = &m_renderer->d,
32 .userData = this,
33 .rewindBufferCapacity = 0
34 };
35 m_threadContext.startCallback = [] (GBAThread* context) {
36 GameController* controller = static_cast<GameController*>(context->userData);
37 controller->gameStarted(context);
38 };
39
40 m_threadContext.cleanCallback = [] (GBAThread* context) {
41 GameController* controller = static_cast<GameController*>(context->userData);
42 controller->gameStopped(context);
43 };
44
45 m_threadContext.frameCallback = [] (GBAThread* context) {
46 GameController* controller = static_cast<GameController*>(context->userData);
47 controller->m_pauseMutex.lock();
48 if (controller->m_pauseAfterFrame) {
49 GBAThreadPause(context);
50 controller->m_pauseAfterFrame = false;
51 }
52 controller->m_pauseMutex.unlock();
53 controller->frameAvailable(controller->m_drawContext);
54 };
55
56#ifdef BUILD_SDL
57 connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
58#endif
59}
60
61GameController::~GameController() {
62 if (GBAThreadIsPaused(&m_threadContext)) {
63 GBAThreadUnpause(&m_threadContext);
64 }
65 GBAThreadEnd(&m_threadContext);
66 GBAThreadJoin(&m_threadContext);
67 delete m_renderer;
68}
69
70ARMDebugger* GameController::debugger() {
71 return m_threadContext.debugger;
72}
73
74void GameController::setDebugger(ARMDebugger* debugger) {
75 bool wasPaused = isPaused();
76 setPaused(true);
77 if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
78 GBADetachDebugger(m_threadContext.gba);
79 }
80 m_threadContext.debugger = debugger;
81 if (m_threadContext.debugger && GBAThreadHasStarted(&m_threadContext)) {
82 GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
83 }
84 setPaused(wasPaused);
85}
86
87void GameController::loadGame(const QString& path) {
88 closeGame();
89 m_threadContext.sync.videoFrameWait = 0;
90 m_threadContext.sync.audioWait = 1;
91 m_rom = new QFile(path);
92 if (!m_rom->open(QIODevice::ReadOnly)) {
93 delete m_rom;
94 m_rom = 0;
95 }
96
97 m_pauseAfterFrame = false;
98
99 m_threadContext.fd = m_rom->handle();
100 m_threadContext.fname = path.toLocal8Bit().constData();
101 GBAThreadStart(&m_threadContext);
102}
103
104void GameController::closeGame() {
105 // TODO: Make this threadsafe
106 if (m_threadContext.state >= THREAD_EXITING || m_threadContext.state <= THREAD_INITIALIZED) {
107 return;
108 }
109 GBAThreadEnd(&m_threadContext);
110 GBAThreadJoin(&m_threadContext);
111 if (m_rom) {
112 m_rom->close();
113 delete m_rom;
114 }
115 emit gameStopped(&m_threadContext);
116}
117
118bool GameController::isPaused() {
119 return GBAThreadIsPaused(&m_threadContext);
120}
121
122void GameController::setPaused(bool paused) {
123 if (paused == GBAThreadIsPaused(&m_threadContext)) {
124 return;
125 }
126 if (paused) {
127 GBAThreadPause(&m_threadContext);
128 } else {
129 GBAThreadUnpause(&m_threadContext);
130 }
131}
132
133void GameController::frameAdvance() {
134 m_pauseMutex.lock();
135 m_pauseAfterFrame = true;
136 setPaused(false);
137 m_pauseMutex.unlock();
138}
139
140void GameController::keyPressed(int key) {
141 int mappedKey = 1 << key;
142 m_activeKeys |= mappedKey;
143 updateKeys();
144}
145
146void GameController::keyReleased(int key) {
147 int mappedKey = 1 << key;
148 m_activeKeys &= ~mappedKey;
149 updateKeys();
150}
151
152void GameController::updateKeys() {
153 int activeKeys = m_activeKeys;
154#ifdef BUILD_SDL
155 activeKeys |= m_activeButtons;
156#endif
157 m_threadContext.activeKeys = activeKeys;
158}
159
160#ifdef BUILD_SDL
161void GameController::testSDLEvents() {
162 SDL_Joystick* joystick = m_sdlEvents.joystick;
163 SDL_JoystickUpdate();
164 int numButtons = SDL_JoystickNumButtons(joystick);
165 m_activeButtons = 0;
166 int i;
167 for (i = 0; i < numButtons; ++i) {
168 GBAKey key = GBASDLMapButtonToKey(i);
169 if (key == GBA_KEY_NONE) {
170 continue;
171 }
172 if (SDL_JoystickGetButton(joystick, i)) {
173 m_activeButtons |= 1 << key;
174 }
175 }
176 int numHats = SDL_JoystickNumHats(joystick);
177 for (i = 0; i < numHats; ++i) {
178 int hat = SDL_JoystickGetHat(joystick, i);
179 if (hat & SDL_HAT_UP) {
180 m_activeButtons |= 1 << GBA_KEY_UP;
181 }
182 if (hat & SDL_HAT_LEFT) {
183 m_activeButtons |= 1 << GBA_KEY_LEFT;
184 }
185 if (hat & SDL_HAT_DOWN) {
186 m_activeButtons |= 1 << GBA_KEY_DOWN;
187 }
188 if (hat & SDL_HAT_RIGHT) {
189 m_activeButtons |= 1 << GBA_KEY_RIGHT;
190 }
191 }
192 updateKeys();
193}
194#endif