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