src/platform/qt/GameController.cpp (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "GameController.h"
7
8#include "AudioProcessor.h"
9#include "InputController.h"
10#include "MultiplayerController.h"
11
12#include <QDateTime>
13#include <QThread>
14
15#include <ctime>
16
17extern "C" {
18#include "gba/audio.h"
19#include "gba/gba.h"
20#include "gba/serialize.h"
21#include "gba/renderers/video-software.h"
22#include "gba/supervisor/config.h"
23#include "util/vfs.h"
24}
25
26using namespace QGBA;
27using namespace std;
28
29const int GameController::LUX_LEVELS[10] = { 5, 11, 18, 27, 42, 62, 84, 109, 139, 183 };
30
31GameController::GameController(QObject* parent)
32 : QObject(parent)
33 , m_drawContext(new uint32_t[256 * 256])
34 , m_threadContext()
35 , m_activeKeys(0)
36 , m_logLevels(0)
37 , m_gameOpen(false)
38 , m_audioThread(new QThread(this))
39 , m_audioProcessor(AudioProcessor::create())
40 , m_videoSync(VIDEO_SYNC)
41 , m_audioSync(AUDIO_SYNC)
42 , m_turbo(false)
43 , m_turboForced(false)
44 , m_inputController(nullptr)
45 , m_multiplayer(nullptr)
46{
47 m_renderer = new GBAVideoSoftwareRenderer;
48 GBAVideoSoftwareRendererCreate(m_renderer);
49 m_renderer->outputBuffer = (color_t*) m_drawContext;
50 m_renderer->outputBufferStride = 256;
51
52 GBACheatDeviceCreate(&m_cheatDevice);
53
54 m_threadContext.state = THREAD_INITIALIZED;
55 m_threadContext.debugger = 0;
56 m_threadContext.frameskip = 0;
57 m_threadContext.bios = 0;
58 m_threadContext.renderer = &m_renderer->d;
59 m_threadContext.userData = this;
60 m_threadContext.rewindBufferCapacity = 0;
61 m_threadContext.cheats = &m_cheatDevice;
62 m_threadContext.logLevel = -1;
63
64 m_lux.p = this;
65 m_lux.sample = [] (GBALuminanceSource* context) {
66 GameControllerLux* lux = static_cast<GameControllerLux*>(context);
67 lux->value = 0xFF - lux->p->m_luxValue;
68 };
69
70 m_lux.readLuminance = [] (GBALuminanceSource* context) {
71 GameControllerLux* lux = static_cast<GameControllerLux*>(context);
72 return lux->value;
73 };
74 setLuminanceLevel(0);
75
76 m_rtc.p = this;
77 m_rtc.override = GameControllerRTC::NO_OVERRIDE;
78 m_rtc.sample = [] (GBARTCSource* context) { };
79 m_rtc.unixTime = [] (GBARTCSource* context) -> time_t {
80 GameControllerRTC* rtc = static_cast<GameControllerRTC*>(context);
81 switch (rtc->override) {
82 case GameControllerRTC::NO_OVERRIDE:
83 default:
84 return time(nullptr);
85 case GameControllerRTC::FIXED:
86 return rtc->value;
87 case GameControllerRTC::FAKE_EPOCH:
88 return rtc->value + rtc->p->m_threadContext.gba->video.frameCounter * (int64_t) VIDEO_TOTAL_LENGTH / GBA_ARM7TDMI_FREQUENCY;
89 }
90 };
91
92 m_threadContext.startCallback = [] (GBAThread* context) {
93 GameController* controller = static_cast<GameController*>(context->userData);
94 controller->m_audioProcessor->setInput(context);
95 // Override the GBA object's log level to prevent stdout spew
96 context->gba->logLevel = GBA_LOG_FATAL;
97 context->gba->luminanceSource = &controller->m_lux;
98 context->gba->rtcSource = &controller->m_rtc;
99 controller->gameStarted(context);
100 };
101
102 m_threadContext.cleanCallback = [] (GBAThread* context) {
103 GameController* controller = static_cast<GameController*>(context->userData);
104 controller->gameStopped(context);
105 };
106
107 m_threadContext.frameCallback = [] (GBAThread* context) {
108 GameController* controller = static_cast<GameController*>(context->userData);
109 controller->m_pauseMutex.lock();
110 if (controller->m_pauseAfterFrame) {
111 GBAThreadPauseFromThread(context);
112 controller->m_pauseAfterFrame = false;
113 controller->gamePaused(&controller->m_threadContext);
114 }
115 controller->m_pauseMutex.unlock();
116 controller->frameAvailable(controller->m_drawContext);
117 };
118
119 m_threadContext.logHandler = [] (GBAThread* context, enum GBALogLevel level, const char* format, va_list args) {
120 GameController* controller = static_cast<GameController*>(context->userData);
121 if (level == GBA_LOG_FATAL) {
122 QMetaObject::invokeMethod(controller, "crashGame", Q_ARG(const QString&, QString().vsprintf(format, args)));
123 } else if (!(controller->m_logLevels & level)) {
124 return;
125 }
126 controller->postLog(level, QString().vsprintf(format, args));
127 };
128
129 m_audioThread->start(QThread::TimeCriticalPriority);
130 m_audioProcessor->moveToThread(m_audioThread);
131 connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
132 connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
133 connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
134 connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
135
136#ifdef BUILD_SDL
137 connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(testSDLEvents()));
138#endif
139}
140
141GameController::~GameController() {
142 m_audioThread->quit();
143 m_audioThread->wait();
144 disconnect();
145 clearMultiplayerController();
146 closeGame();
147 GBACheatDeviceDestroy(&m_cheatDevice);
148 delete m_renderer;
149 delete[] m_drawContext;
150}
151
152void GameController::setMultiplayerController(std::shared_ptr<MultiplayerController> controller) {
153 if (controller == m_multiplayer) {
154 return;
155 }
156 clearMultiplayerController();
157 m_multiplayer = controller;
158 controller->attachGame(this);
159}
160
161void GameController::clearMultiplayerController() {
162 if (!m_multiplayer) {
163 return;
164 }
165 m_multiplayer->detachGame(this);
166 m_multiplayer.reset();
167}
168
169void GameController::setOverride(const GBACartridgeOverride& override) {
170 m_threadContext.override = override;
171 m_threadContext.hasOverride = true;
172}
173
174void GameController::setOptions(const GBAOptions* opts) {
175 setFrameskip(opts->frameskip);
176 setAudioSync(opts->audioSync);
177 setVideoSync(opts->videoSync);
178 setSkipBIOS(opts->skipBios);
179 setUseBIOS(opts->useBios);
180 setRewind(opts->rewindEnable, opts->rewindBufferCapacity, opts->rewindBufferInterval);
181
182 threadInterrupt();
183 m_threadContext.idleOptimization = opts->idleOptimization;
184 threadContinue();
185}
186
187#ifdef USE_GDB_STUB
188ARMDebugger* GameController::debugger() {
189 return m_threadContext.debugger;
190}
191
192void GameController::setDebugger(ARMDebugger* debugger) {
193 threadInterrupt();
194 if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
195 GBADetachDebugger(m_threadContext.gba);
196 }
197 m_threadContext.debugger = debugger;
198 if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
199 GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
200 }
201 threadContinue();
202}
203#endif
204
205void GameController::loadGame(const QString& path, bool dirmode) {
206 closeGame();
207 if (!dirmode) {
208 QFile file(path);
209 if (!file.open(QIODevice::ReadOnly)) {
210 return;
211 }
212 file.close();
213 }
214
215 m_fname = path;
216 m_dirmode = dirmode;
217 openGame();
218}
219
220void GameController::openGame() {
221 m_gameOpen = true;
222
223 m_pauseAfterFrame = false;
224
225 if (m_turbo) {
226 m_threadContext.sync.videoFrameWait = false;
227 m_threadContext.sync.audioWait = false;
228 } else {
229 m_threadContext.sync.videoFrameWait = m_videoSync;
230 m_threadContext.sync.audioWait = m_audioSync;
231 }
232
233 m_threadContext.gameDir = 0;
234 m_threadContext.fname = strdup(m_fname.toLocal8Bit().constData());
235 if (m_dirmode) {
236 m_threadContext.gameDir = VDirOpen(m_threadContext.fname);
237 m_threadContext.stateDir = m_threadContext.gameDir;
238 } else {
239 m_threadContext.rom = VFileOpen(m_threadContext.fname, O_RDONLY);
240#if USE_LIBZIP
241 if (!m_threadContext.gameDir) {
242 m_threadContext.gameDir = VDirOpenZip(m_threadContext.fname, 0);
243 }
244#endif
245#if USE_LZMA
246 if (!m_threadContext.gameDir) {
247 m_threadContext.gameDir = VDirOpen7z(m_threadContext.fname, 0);
248 }
249#endif
250 }
251
252 if (!m_bios.isNull() &&m_useBios) {
253 m_threadContext.bios = VFileOpen(m_bios.toLocal8Bit().constData(), O_RDONLY);
254 } else {
255 m_threadContext.bios = nullptr;
256 }
257
258 if (!m_patch.isNull()) {
259 m_threadContext.patch = VFileOpen(m_patch.toLocal8Bit().constData(), O_RDONLY);
260 }
261
262 if (!GBAThreadStart(&m_threadContext)) {
263 m_gameOpen = false;
264 emit gameFailed();
265 }
266}
267
268void GameController::loadBIOS(const QString& path) {
269 if (m_bios == path) {
270 return;
271 }
272 m_bios = path;
273 if (m_gameOpen) {
274 closeGame();
275 openGame();
276 }
277}
278
279void GameController::loadPatch(const QString& path) {
280 if (m_gameOpen) {
281 closeGame();
282 m_patch = path;
283 openGame();
284 } else {
285 m_patch = path;
286 }
287}
288
289void GameController::closeGame() {
290 if (!m_gameOpen) {
291 return;
292 }
293 if (GBAThreadIsPaused(&m_threadContext)) {
294 GBAThreadUnpause(&m_threadContext);
295 }
296 GBAThreadEnd(&m_threadContext);
297 GBAThreadJoin(&m_threadContext);
298 if (m_threadContext.fname) {
299 free(const_cast<char*>(m_threadContext.fname));
300 m_threadContext.fname = nullptr;
301 }
302
303 m_patch = QString();
304
305 for (size_t i = 0; i < GBACheatSetsSize(&m_cheatDevice.cheats); ++i) {
306 GBACheatSet* set = *GBACheatSetsGetPointer(&m_cheatDevice.cheats, i);
307 GBACheatSetDeinit(set);
308 delete set;
309 }
310 GBACheatSetsClear(&m_cheatDevice.cheats);
311
312 m_gameOpen = false;
313 emit gameStopped(&m_threadContext);
314}
315
316void GameController::crashGame(const QString& crashMessage) {
317 closeGame();
318 emit gameCrashed(crashMessage);
319}
320
321bool GameController::isPaused() {
322 if (!m_gameOpen) {
323 return false;
324 }
325 return GBAThreadIsPaused(&m_threadContext);
326}
327
328void GameController::setPaused(bool paused) {
329 if (paused == GBAThreadIsPaused(&m_threadContext)) {
330 return;
331 }
332 if (paused) {
333 GBAThreadPause(&m_threadContext);
334 emit gamePaused(&m_threadContext);
335 } else {
336 GBAThreadUnpause(&m_threadContext);
337 emit gameUnpaused(&m_threadContext);
338 }
339}
340
341void GameController::reset() {
342 GBAThreadReset(&m_threadContext);
343}
344
345void GameController::threadInterrupt() {
346 if (m_gameOpen) {
347 GBAThreadInterrupt(&m_threadContext);
348 }
349}
350
351void GameController::threadContinue() {
352 if (m_gameOpen) {
353 GBAThreadContinue(&m_threadContext);
354 }
355}
356
357void GameController::frameAdvance() {
358 m_pauseMutex.lock();
359 m_pauseAfterFrame = true;
360 setPaused(false);
361 m_pauseMutex.unlock();
362}
363
364void GameController::setRewind(bool enable, int capacity, int interval) {
365 if (m_gameOpen) {
366 threadInterrupt();
367 GBARewindSettingsChanged(&m_threadContext, enable ? capacity : 0, enable ? interval : 0);
368 threadContinue();
369 } else {
370 if (enable) {
371 m_threadContext.rewindBufferInterval = interval;
372 m_threadContext.rewindBufferCapacity = capacity;
373 } else {
374 m_threadContext.rewindBufferInterval = 0;
375 m_threadContext.rewindBufferCapacity = 0;
376 }
377 }
378}
379
380void GameController::rewind(int states) {
381 threadInterrupt();
382 if (!states) {
383 GBARewindAll(&m_threadContext);
384 } else {
385 GBARewind(&m_threadContext, states);
386 }
387 threadContinue();
388}
389
390void GameController::keyPressed(int key) {
391 int mappedKey = 1 << key;
392 m_activeKeys |= mappedKey;
393 updateKeys();
394}
395
396void GameController::keyReleased(int key) {
397 int mappedKey = 1 << key;
398 m_activeKeys &= ~mappedKey;
399 updateKeys();
400}
401
402void GameController::clearKeys() {
403 m_activeKeys = 0;
404 updateKeys();
405}
406
407void GameController::setAudioBufferSamples(int samples) {
408 threadInterrupt();
409 redoSamples(samples);
410 threadContinue();
411 QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
412}
413
414void GameController::setFPSTarget(float fps) {
415 threadInterrupt();
416 m_threadContext.fpsTarget = fps;
417 redoSamples(m_audioProcessor->getBufferSamples());
418 threadContinue();
419 QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
420}
421
422void GameController::setSkipBIOS(bool set) {
423 threadInterrupt();
424 m_threadContext.skipBios = set;
425 threadContinue();
426}
427
428void GameController::setUseBIOS(bool use) {
429 threadInterrupt();
430 m_useBios = use;
431 threadContinue();
432}
433
434void GameController::loadState(int slot) {
435 threadInterrupt();
436 GBALoadState(&m_threadContext, m_threadContext.stateDir, slot);
437 threadContinue();
438 emit stateLoaded(&m_threadContext);
439 emit frameAvailable(m_drawContext);
440}
441
442void GameController::saveState(int slot) {
443 threadInterrupt();
444 GBASaveState(&m_threadContext, m_threadContext.stateDir, slot, true);
445 threadContinue();
446}
447
448void GameController::setVideoSync(bool set) {
449 m_videoSync = set;
450 if (!m_turbo) {
451 threadInterrupt();
452 m_threadContext.sync.videoFrameWait = set;
453 threadContinue();
454 }
455}
456
457void GameController::setAudioSync(bool set) {
458 m_audioSync = set;
459 if (!m_turbo) {
460 threadInterrupt();
461 m_threadContext.sync.audioWait = set;
462 threadContinue();
463 }
464}
465
466void GameController::setFrameskip(int skip) {
467 m_threadContext.frameskip = skip;
468}
469
470void GameController::setTurbo(bool set, bool forced) {
471 if (m_turboForced && !forced) {
472 return;
473 }
474 m_turbo = set;
475 m_turboForced = set && forced;
476 threadInterrupt();
477 m_threadContext.sync.audioWait = set ? false : m_audioSync;
478 m_threadContext.sync.videoFrameWait = set ? false : m_videoSync;
479 threadContinue();
480}
481
482void GameController::setAVStream(GBAAVStream* stream) {
483 threadInterrupt();
484 m_threadContext.stream = stream;
485 threadContinue();
486}
487
488void GameController::clearAVStream() {
489 threadInterrupt();
490 m_threadContext.stream = nullptr;
491 threadContinue();
492}
493
494void GameController::reloadAudioDriver() {
495 QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
496 int samples = m_audioProcessor->getBufferSamples();
497 delete m_audioProcessor;
498 m_audioProcessor = AudioProcessor::create();
499 m_audioProcessor->setBufferSamples(samples);
500 m_audioProcessor->moveToThread(m_audioThread);
501 connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
502 connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));
503 connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
504 connect(this, SIGNAL(gameUnpaused(GBAThread*)), m_audioProcessor, SLOT(start()));
505 if (isLoaded()) {
506 m_audioProcessor->setInput(&m_threadContext);
507 QMetaObject::invokeMethod(m_audioProcessor, "start");
508 }
509}
510
511void GameController::setLuminanceValue(uint8_t value) {
512 m_luxValue = value;
513 value = std::max<int>(value - 0x16, 0);
514 m_luxLevel = 10;
515 for (int i = 0; i < 10; ++i) {
516 if (value < LUX_LEVELS[i]) {
517 m_luxLevel = i;
518 break;
519 }
520 }
521 emit luminanceValueChanged(m_luxValue);
522}
523
524void GameController::setLuminanceLevel(int level) {
525 int value = 0x16;
526 level = std::max(0, std::min(10, level));
527 if (level > 0) {
528 value += LUX_LEVELS[level - 1];
529 }
530 setLuminanceValue(value);
531}
532
533void GameController::setRealTime() {
534 m_rtc.override = GameControllerRTC::NO_OVERRIDE;
535}
536
537void GameController::setFixedTime(const QDateTime& time) {
538 m_rtc.override = GameControllerRTC::FIXED;
539 m_rtc.value = time.toMSecsSinceEpoch() / 1000;
540}
541
542void GameController::setFakeEpoch(const QDateTime& time) {
543 m_rtc.override = GameControllerRTC::FAKE_EPOCH;
544 m_rtc.value = time.toMSecsSinceEpoch() / 1000;
545}
546
547void GameController::updateKeys() {
548 int activeKeys = m_activeKeys;
549#ifdef BUILD_SDL
550 activeKeys |= m_activeButtons;
551#endif
552 m_threadContext.activeKeys = activeKeys;
553}
554
555void GameController::redoSamples(int samples) {
556#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
557 float sampleRate = 0x8000;
558 float ratio;
559 if (m_threadContext.gba) {
560 sampleRate = m_threadContext.gba->audio.sampleRate;
561 }
562 ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, 44100);
563 m_threadContext.audioBuffers = ceil(samples / ratio);
564#else
565 m_threadContext.audioBuffers = samples;
566#endif
567 if (m_threadContext.gba) {
568 GBAAudioResizeBuffer(&m_threadContext.gba->audio, m_threadContext.audioBuffers);
569 }
570}
571
572void GameController::setLogLevel(int levels) {
573 threadInterrupt();
574 m_logLevels = levels;
575 threadContinue();
576}
577
578void GameController::enableLogLevel(int levels) {
579 threadInterrupt();
580 m_logLevels |= levels;
581 threadContinue();
582}
583
584void GameController::disableLogLevel(int levels) {
585 threadInterrupt();
586 m_logLevels &= ~levels;
587 threadContinue();
588}
589
590#ifdef BUILD_SDL
591void GameController::testSDLEvents() {
592 if (!m_inputController) {
593 return;
594 }
595
596 m_activeButtons = m_inputController->testSDLEvents();
597 updateKeys();
598}
599#endif