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_rom(nullptr)
15{
16 m_renderer = new GBAVideoSoftwareRenderer;
17 GBAVideoSoftwareRendererCreate(m_renderer);
18 m_renderer->outputBuffer = (color_t*) m_drawContext;
19 m_renderer->outputBufferStride = 256;
20 m_threadContext = {
21 .state = THREAD_INITIALIZED,
22 .debugger = 0,
23 .frameskip = 0,
24 .biosFd = -1,
25 .renderer = &m_renderer->d,
26 .userData = this,
27 .rewindBufferCapacity = 0
28 };
29 m_threadContext.startCallback = [] (GBAThread* context) {
30 GameController* controller = static_cast<GameController*>(context->userData);
31 controller->gameStarted(context);
32 };
33
34 m_threadContext.cleanCallback = [] (GBAThread* context) {
35 GameController* controller = static_cast<GameController*>(context->userData);
36 controller->gameStopped(context);
37 };
38
39 m_threadContext.frameCallback = [] (GBAThread* context) {
40 GameController* controller = static_cast<GameController*>(context->userData);
41 controller->m_pauseMutex.lock();
42 if (controller->m_pauseAfterFrame) {
43 GBAThreadPause(context);
44 controller->m_pauseAfterFrame = false;
45 }
46 controller->m_pauseMutex.unlock();
47 controller->frameAvailable(controller->m_drawContext);
48 };
49}
50
51GameController::~GameController() {
52 if (GBAThreadIsPaused(&m_threadContext)) {
53 GBAThreadUnpause(&m_threadContext);
54 }
55 GBAThreadEnd(&m_threadContext);
56 GBAThreadJoin(&m_threadContext);
57 delete m_renderer;
58}
59
60ARMDebugger* GameController::debugger() {
61 return m_threadContext.debugger;
62}
63
64void GameController::setDebugger(ARMDebugger* debugger) {
65 bool wasPaused = isPaused();
66 setPaused(true);
67 if (m_threadContext.debugger) {
68 GBADetachDebugger(m_threadContext.gba);
69 }
70 m_threadContext.debugger = debugger;
71 if (m_threadContext.debugger) {
72 GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
73 }
74 setPaused(wasPaused);
75}
76
77void GameController::loadGame(const QString& path) {
78 closeGame();
79 m_threadContext.sync.videoFrameWait = 0;
80 m_threadContext.sync.audioWait = 1;
81 m_rom = new QFile(path);
82 if (!m_rom->open(QIODevice::ReadOnly)) {
83 delete m_rom;
84 m_rom = 0;
85 }
86
87 m_pauseAfterFrame = false;
88
89 m_threadContext.fd = m_rom->handle();
90 m_threadContext.fname = path.toLocal8Bit().constData();
91 GBAThreadStart(&m_threadContext);
92}
93
94void GameController::closeGame() {
95 // TODO: Make this threadsafe
96 if (m_threadContext.state >= THREAD_EXITING || m_threadContext.state <= THREAD_INITIALIZED) {
97 return;
98 }
99 GBAThreadEnd(&m_threadContext);
100 GBAThreadJoin(&m_threadContext);
101 if (m_rom) {
102 m_rom->close();
103 delete m_rom;
104 }
105 emit gameStopped(&m_threadContext);
106}
107
108bool GameController::isPaused() {
109 return GBAThreadIsPaused(&m_threadContext);
110}
111
112void GameController::setPaused(bool paused) {
113 if (paused == GBAThreadIsPaused(&m_threadContext)) {
114 return;
115 }
116 if (paused) {
117 GBAThreadPause(&m_threadContext);
118 } else {
119 GBAThreadUnpause(&m_threadContext);
120 }
121}
122
123void GameController::frameAdvance() {
124 m_pauseMutex.lock();
125 m_pauseAfterFrame = true;
126 setPaused(false);
127 m_pauseMutex.unlock();
128}
129
130void GameController::keyPressed(int key) {
131 int mappedKey = 1 << key;
132 m_threadContext.activeKeys |= mappedKey;
133}
134
135void GameController::keyReleased(int key) {
136 int mappedKey = 1 << key;
137 m_threadContext.activeKeys &= ~mappedKey;
138}