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 "LogController.h"
11#include "MultiplayerController.h"
12#include "VFileDevice.h"
13
14#include <QCoreApplication>
15#include <QDateTime>
16#include <QThread>
17
18#include <ctime>
19
20extern "C" {
21#include "gba/audio.h"
22#include "gba/context/config.h"
23#include "gba/context/directories.h"
24#include "gba/gba.h"
25#include "gba/serialize.h"
26#include "gba/sharkport.h"
27#include "gba/renderers/video-software.h"
28#include "util/vfs.h"
29}
30
31using namespace QGBA;
32using namespace std;
33
34GameController::GameController(QObject* parent)
35 : QObject(parent)
36 , m_drawContext(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
37 , m_frontBuffer(new uint32_t[VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS])
38 , m_threadContext()
39 , m_activeKeys(0)
40 , m_inactiveKeys(0)
41 , m_logLevels(0)
42 , m_gameOpen(false)
43 , m_useBios(false)
44 , m_audioThread(new QThread(this))
45 , m_audioProcessor(AudioProcessor::create())
46 , m_pauseAfterFrame(false)
47 , m_videoSync(VIDEO_SYNC)
48 , m_audioSync(AUDIO_SYNC)
49 , m_fpsTarget(-1)
50 , m_turbo(false)
51 , m_turboForced(false)
52 , m_turboSpeed(-1)
53 , m_wasPaused(false)
54 , m_audioChannels{ true, true, true, true, true, true }
55 , m_videoLayers{ true, true, true, true, true }
56 , m_autofire{}
57 , m_autofireStatus{}
58 , m_inputController(nullptr)
59 , m_multiplayer(nullptr)
60 , m_stateSlot(1)
61 , m_backupLoadState(nullptr)
62 , m_backupSaveState(nullptr)
63 , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS)
64 , m_loadStateFlags(SAVESTATE_SCREENSHOT)
65{
66 m_renderer = new GBAVideoSoftwareRenderer;
67 GBAVideoSoftwareRendererCreate(m_renderer);
68 m_renderer->outputBuffer = (color_t*) m_drawContext;
69 m_renderer->outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
70
71 GBACheatDeviceCreate(&m_cheatDevice);
72
73 m_threadContext.state = THREAD_INITIALIZED;
74 m_threadContext.debugger = 0;
75 m_threadContext.frameskip = 0;
76 m_threadContext.bios = 0;
77 m_threadContext.renderer = &m_renderer->d;
78 m_threadContext.userData = this;
79 m_threadContext.rewindBufferCapacity = 0;
80 m_threadContext.cheats = &m_cheatDevice;
81 m_threadContext.logLevel = GBA_LOG_ALL;
82 GBADirectorySetInit(&m_threadContext.dirs);
83
84 m_lux.p = this;
85 m_lux.sample = [](GBALuminanceSource* context) {
86 GameControllerLux* lux = static_cast<GameControllerLux*>(context);
87 lux->value = 0xFF - lux->p->m_luxValue;
88 };
89
90 m_lux.readLuminance = [](GBALuminanceSource* context) {
91 GameControllerLux* lux = static_cast<GameControllerLux*>(context);
92 return lux->value;
93 };
94 setLuminanceLevel(0);
95
96 m_threadContext.startCallback = [](GBAThread* context) {
97 GameController* controller = static_cast<GameController*>(context->userData);
98 if (controller->m_audioProcessor) {
99 controller->m_audioProcessor->setInput(context);
100 }
101 context->gba->luminanceSource = &controller->m_lux;
102 GBARTCGenericSourceInit(&controller->m_rtc, context->gba);
103 context->gba->rtcSource = &controller->m_rtc.d;
104 context->gba->rumble = controller->m_inputController->rumble();
105 context->gba->rotationSource = controller->m_inputController->rotationSource();
106 context->gba->audio.forceDisableCh[0] = !controller->m_audioChannels[0];
107 context->gba->audio.forceDisableCh[1] = !controller->m_audioChannels[1];
108 context->gba->audio.forceDisableCh[2] = !controller->m_audioChannels[2];
109 context->gba->audio.forceDisableCh[3] = !controller->m_audioChannels[3];
110 context->gba->audio.forceDisableChA = !controller->m_audioChannels[4];
111 context->gba->audio.forceDisableChB = !controller->m_audioChannels[5];
112 context->gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0];
113 context->gba->video.renderer->disableBG[1] = !controller->m_videoLayers[1];
114 context->gba->video.renderer->disableBG[2] = !controller->m_videoLayers[2];
115 context->gba->video.renderer->disableBG[3] = !controller->m_videoLayers[3];
116 context->gba->video.renderer->disableOBJ = !controller->m_videoLayers[4];
117 controller->m_fpsTarget = context->fpsTarget;
118
119 if (context->dirs.state && GBALoadState(context, context->dirs.state, 0, controller->m_loadStateFlags)) {
120 GBADeleteState(context->gba, context->dirs.state, 0);
121 }
122 QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context));
123 };
124
125 m_threadContext.cleanCallback = [](GBAThread* context) {
126 GameController* controller = static_cast<GameController*>(context->userData);
127 QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(GBAThread*, context));
128 };
129
130 m_threadContext.frameCallback = [](GBAThread* context) {
131 GameController* controller = static_cast<GameController*>(context->userData);
132 memcpy(controller->m_frontBuffer, controller->m_drawContext, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
133 QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer));
134 if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
135 GBAThreadPauseFromThread(context);
136 QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(GBAThread*, context));
137 }
138 };
139
140 m_threadContext.stopCallback = [](GBAThread* context) {
141 if (!context) {
142 return false;
143 }
144 GameController* controller = static_cast<GameController*>(context->userData);
145 if (!GBASaveState(context, context->dirs.state, 0, controller->m_saveStateFlags)) {
146 return false;
147 }
148 QMetaObject::invokeMethod(controller, "closeGame");
149 return true;
150 };
151
152 m_threadContext.logHandler = [](GBAThread* context, enum GBALogLevel level, const char* format, va_list args) {
153 static const char* stubMessage = "Stub software interrupt: %02X";
154 static const char* savestateMessage = "State %i loaded";
155 static const char* savestateFailedMessage = "State %i failed to load";
156 if (!context) {
157 return;
158 }
159 GameController* controller = static_cast<GameController*>(context->userData);
160 if (level == GBA_LOG_STUB && strncmp(stubMessage, format, strlen(stubMessage)) == 0) {
161 va_list argc;
162 va_copy(argc, args);
163 int immediate = va_arg(argc, int);
164 va_end(argc);
165 QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate));
166 } else if (level == GBA_LOG_STATUS) {
167 // Slot 0 is reserved for suspend points
168 if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {
169 va_list argc;
170 va_copy(argc, args);
171 int slot = va_arg(argc, int);
172 va_end(argc);
173 if (slot == 0) {
174 format = "Loaded suspend state";
175 }
176 } else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0) {
177 va_list argc;
178 va_copy(argc, args);
179 int slot = va_arg(argc, int);
180 va_end(argc);
181 if (slot == 0) {
182 return;
183 }
184 }
185 }
186 if (level == GBA_LOG_FATAL) {
187 QMetaObject::invokeMethod(controller, "crashGame", Q_ARG(const QString&, QString().vsprintf(format, args)));
188 } else if (!(controller->m_logLevels & level)) {
189 return;
190 }
191 QString message(QString().vsprintf(format, args));
192 if (level == GBA_LOG_STATUS) {
193 QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
194 }
195 QMetaObject::invokeMethod(controller, "postLog", Q_ARG(int, level), Q_ARG(const QString&, message));
196 };
197
198 connect(&m_rewindTimer, &QTimer::timeout, [this]() {
199 GBARewind(&m_threadContext, 1);
200 emit frameAvailable(m_drawContext);
201 emit rewound(&m_threadContext);
202 });
203 m_rewindTimer.setInterval(100);
204
205 m_audioThread->setObjectName("Audio Thread");
206 m_audioThread->start(QThread::TimeCriticalPriority);
207 m_audioProcessor->moveToThread(m_audioThread);
208 connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
209 connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents()));
210 connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateAutofire()));
211}
212
213GameController::~GameController() {
214 m_audioThread->quit();
215 m_audioThread->wait();
216 disconnect();
217 clearMultiplayerController();
218 closeGame();
219 GBACheatDeviceDestroy(&m_cheatDevice);
220 GBADirectorySetDeinit(&m_threadContext.dirs);
221 delete m_renderer;
222 delete[] m_drawContext;
223 delete[] m_frontBuffer;
224 delete m_backupLoadState;
225}
226
227void GameController::setMultiplayerController(MultiplayerController* controller) {
228 if (controller == m_multiplayer) {
229 return;
230 }
231 clearMultiplayerController();
232 m_multiplayer = controller;
233 controller->attachGame(this);
234}
235
236void GameController::clearMultiplayerController() {
237 if (!m_multiplayer) {
238 return;
239 }
240 m_multiplayer->detachGame(this);
241 m_multiplayer = nullptr;
242}
243
244void GameController::setOverride(const GBACartridgeOverride& override) {
245 m_threadContext.override = override;
246 m_threadContext.hasOverride = true;
247}
248
249void GameController::setOptions(const GBAOptions* opts) {
250 setFrameskip(opts->frameskip);
251 setAudioSync(opts->audioSync);
252 setVideoSync(opts->videoSync);
253 setSkipBIOS(opts->skipBios);
254 setUseBIOS(opts->useBios);
255 setRewind(opts->rewindEnable, opts->rewindBufferCapacity, opts->rewindBufferInterval);
256 setVolume(opts->volume);
257 setMute(opts->mute);
258
259 threadInterrupt();
260 GBADirectorySetMapOptions(&m_threadContext.dirs, opts);
261 m_threadContext.idleOptimization = opts->idleOptimization;
262 threadContinue();
263}
264
265#ifdef USE_GDB_STUB
266ARMDebugger* GameController::debugger() {
267 return m_threadContext.debugger;
268}
269
270void GameController::setDebugger(ARMDebugger* debugger) {
271 threadInterrupt();
272 if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
273 GBADetachDebugger(m_threadContext.gba);
274 }
275 m_threadContext.debugger = debugger;
276 if (m_threadContext.debugger && GBAThreadIsActive(&m_threadContext)) {
277 GBAAttachDebugger(m_threadContext.gba, m_threadContext.debugger);
278 }
279 threadContinue();
280}
281#endif
282
283void GameController::loadGame(const QString& path) {
284 closeGame();
285 QFile file(path);
286 if (!file.open(QIODevice::ReadOnly)) {
287 postLog(GBA_LOG_ERROR, tr("Failed to open game file: %1").arg(path));
288 return;
289 }
290 file.close();
291
292 m_fname = path;
293 openGame();
294}
295
296void GameController::bootBIOS() {
297 closeGame();
298 m_fname = QString();
299 openGame(true);
300}
301
302void GameController::openGame(bool biosOnly) {
303 if (biosOnly && (!m_useBios || m_bios.isNull())) {
304 return;
305 }
306
307 m_gameOpen = true;
308
309 m_pauseAfterFrame = false;
310
311 if (m_turbo) {
312 m_threadContext.sync.videoFrameWait = false;
313 m_threadContext.sync.audioWait = false;
314 } else {
315 m_threadContext.sync.videoFrameWait = m_videoSync;
316 m_threadContext.sync.audioWait = m_audioSync;
317 }
318
319 m_threadContext.bootBios = biosOnly;
320 if (biosOnly) {
321 m_threadContext.fname = nullptr;
322 } else {
323 m_threadContext.fname = strdup(m_fname.toUtf8().constData());
324 GBAThreadLoadROM(&m_threadContext, m_threadContext.fname);
325 }
326
327 if (!m_bios.isNull() && m_useBios) {
328 m_threadContext.bios = VFileDevice::open(m_bios, O_RDONLY);
329 } else {
330 m_threadContext.bios = nullptr;
331 }
332
333 if (!m_patch.isNull()) {
334 m_threadContext.patch = VFileDevice::open(m_patch, O_RDONLY);
335 }
336
337 m_inputController->recalibrateAxes();
338 memset(m_drawContext, 0xF8, VIDEO_VERTICAL_PIXELS * VIDEO_HORIZONTAL_PIXELS * 4);
339
340 if (!GBAThreadStart(&m_threadContext)) {
341 m_gameOpen = false;
342 emit gameFailed();
343 } else if (m_audioProcessor) {
344 startAudio();
345 }
346}
347
348void GameController::loadBIOS(const QString& path) {
349 if (m_bios == path) {
350 return;
351 }
352 m_bios = path;
353 if (m_gameOpen) {
354 closeGame();
355 openGame();
356 }
357}
358
359void GameController::yankPak() {
360 if (!m_gameOpen) {
361 return;
362 }
363 threadInterrupt();
364 GBAYankROM(m_threadContext.gba);
365 threadContinue();
366}
367
368void GameController::replaceGame(const QString& path) {
369 if (!m_gameOpen) {
370 return;
371 }
372
373 m_fname = path;
374 threadInterrupt();
375 m_threadContext.fname = strdup(m_fname.toLocal8Bit().constData());
376 GBAThreadReplaceROM(&m_threadContext, m_threadContext.fname);
377 threadContinue();
378}
379
380void GameController::loadPatch(const QString& path) {
381 if (m_gameOpen) {
382 closeGame();
383 m_patch = path;
384 openGame();
385 } else {
386 m_patch = path;
387 }
388}
389
390void GameController::importSharkport(const QString& path) {
391 if (!isLoaded()) {
392 return;
393 }
394 VFile* vf = VFileDevice::open(path, O_RDONLY);
395 if (!vf) {
396 postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for reading: %1").arg(path));
397 return;
398 }
399 threadInterrupt();
400 GBASavedataImportSharkPort(m_threadContext.gba, vf, false);
401 threadContinue();
402 vf->close(vf);
403}
404
405void GameController::exportSharkport(const QString& path) {
406 if (!isLoaded()) {
407 return;
408 }
409 VFile* vf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC);
410 if (!vf) {
411 postLog(GBA_LOG_ERROR, tr("Failed to open snapshot file for writing: %1").arg(path));
412 return;
413 }
414 threadInterrupt();
415 GBASavedataExportSharkPort(m_threadContext.gba, vf);
416 threadContinue();
417 vf->close(vf);
418}
419
420void GameController::closeGame() {
421 if (!m_gameOpen) {
422 return;
423 }
424 m_gameOpen = false;
425
426 m_rewindTimer.stop();
427 if (GBAThreadIsPaused(&m_threadContext)) {
428 GBAThreadUnpause(&m_threadContext);
429 }
430 m_audioProcessor->pause();
431 GBAThreadEnd(&m_threadContext);
432 GBAThreadJoin(&m_threadContext);
433 // Make sure the event queue clears out before the thread is reused
434 QCoreApplication::processEvents();
435 if (m_threadContext.fname) {
436 free(const_cast<char*>(m_threadContext.fname));
437 m_threadContext.fname = nullptr;
438 }
439
440 m_patch = QString();
441
442 for (size_t i = 0; i < GBACheatSetsSize(&m_cheatDevice.cheats); ++i) {
443 GBACheatSet* set = *GBACheatSetsGetPointer(&m_cheatDevice.cheats, i);
444 GBACheatSetDeinit(set);
445 delete set;
446 }
447 GBACheatSetsClear(&m_cheatDevice.cheats);
448
449 m_gameOpen = false;
450 emit gameStopped(&m_threadContext);
451}
452
453void GameController::crashGame(const QString& crashMessage) {
454 closeGame();
455 emit gameCrashed(crashMessage);
456 emit gameStopped(&m_threadContext);
457}
458
459bool GameController::isPaused() {
460 if (!m_gameOpen) {
461 return false;
462 }
463 return GBAThreadIsPaused(&m_threadContext);
464}
465
466void GameController::setPaused(bool paused) {
467 if (!isLoaded() || m_rewindTimer.isActive() || paused == GBAThreadIsPaused(&m_threadContext)) {
468 return;
469 }
470 if (paused) {
471 m_pauseAfterFrame.testAndSetRelaxed(false, true);
472 } else {
473 GBAThreadUnpause(&m_threadContext);
474 startAudio();
475 emit gameUnpaused(&m_threadContext);
476 }
477}
478
479void GameController::reset() {
480 if (!m_gameOpen) {
481 return;
482 }
483 bool wasPaused = isPaused();
484 setPaused(false);
485 GBAThreadReset(&m_threadContext);
486 if (wasPaused) {
487 setPaused(true);
488 }
489}
490
491void GameController::threadInterrupt() {
492 if (m_gameOpen) {
493 GBAThreadInterrupt(&m_threadContext);
494 }
495}
496
497void GameController::threadContinue() {
498 if (m_gameOpen) {
499 GBAThreadContinue(&m_threadContext);
500 }
501}
502
503void GameController::frameAdvance() {
504 if (m_rewindTimer.isActive()) {
505 return;
506 }
507 if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) {
508 setPaused(false);
509 }
510}
511
512void GameController::setRewind(bool enable, int capacity, int interval) {
513 if (m_gameOpen) {
514 threadInterrupt();
515 GBARewindSettingsChanged(&m_threadContext, enable ? capacity : 0, enable ? interval : 0);
516 threadContinue();
517 } else {
518 if (enable) {
519 m_threadContext.rewindBufferInterval = interval;
520 m_threadContext.rewindBufferCapacity = capacity;
521 } else {
522 m_threadContext.rewindBufferInterval = 0;
523 m_threadContext.rewindBufferCapacity = 0;
524 }
525 }
526}
527
528void GameController::rewind(int states) {
529 threadInterrupt();
530 if (!states) {
531 GBARewindAll(&m_threadContext);
532 } else {
533 GBARewind(&m_threadContext, states);
534 }
535 threadContinue();
536 emit frameAvailable(m_drawContext);
537 emit rewound(&m_threadContext);
538}
539
540void GameController::startRewinding() {
541 if (!m_gameOpen || m_rewindTimer.isActive()) {
542 return;
543 }
544 if (m_multiplayer && m_multiplayer->attached() > 1) {
545 return;
546 }
547 m_wasPaused = isPaused();
548 if (!GBAThreadIsPaused(&m_threadContext)) {
549 GBAThreadPause(&m_threadContext);
550 }
551 m_rewindTimer.start();
552}
553
554void GameController::stopRewinding() {
555 if (!m_rewindTimer.isActive()) {
556 return;
557 }
558 m_rewindTimer.stop();
559 bool signalsBlocked = blockSignals(true);
560 setPaused(m_wasPaused);
561 blockSignals(signalsBlocked);
562}
563
564void GameController::keyPressed(int key) {
565 int mappedKey = 1 << key;
566 m_activeKeys |= mappedKey;
567 if (!m_inputController->allowOpposing()) {
568 if ((m_activeKeys & 0x30) == 0x30) {
569 m_inactiveKeys |= mappedKey ^ 0x30;
570 m_activeKeys ^= mappedKey ^ 0x30;
571 }
572 if ((m_activeKeys & 0xC0) == 0xC0) {
573 m_inactiveKeys |= mappedKey ^ 0xC0;
574 m_activeKeys ^= mappedKey ^ 0xC0;
575 }
576 }
577 updateKeys();
578}
579
580void GameController::keyReleased(int key) {
581 int mappedKey = 1 << key;
582 m_activeKeys &= ~mappedKey;
583 if (!m_inputController->allowOpposing()) {
584 if (mappedKey & 0x30) {
585 m_activeKeys |= m_inactiveKeys & (0x30 ^ mappedKey);
586 m_inactiveKeys &= ~0x30;
587 }
588 if (mappedKey & 0xC0) {
589 m_activeKeys |= m_inactiveKeys & (0xC0 ^ mappedKey);
590 m_inactiveKeys &= ~0xC0;
591 }
592 }
593 updateKeys();
594}
595
596void GameController::clearKeys() {
597 m_activeKeys = 0;
598 m_inactiveKeys = 0;
599 updateKeys();
600}
601
602void GameController::setAutofire(int key, bool enable) {
603 if (key >= GBA_KEY_MAX || key < 0) {
604 return;
605 }
606 m_autofire[key] = enable;
607 m_autofireStatus[key] = 0;
608}
609
610void GameController::setAudioBufferSamples(int samples) {
611 if (m_audioProcessor) {
612 threadInterrupt();
613 redoSamples(samples);
614 threadContinue();
615 QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Qt::BlockingQueuedConnection, Q_ARG(int, samples));
616 }
617}
618
619void GameController::setAudioSampleRate(unsigned rate) {
620 if (!rate) {
621 return;
622 }
623 if (m_audioProcessor) {
624 threadInterrupt();
625 redoSamples(m_audioProcessor->getBufferSamples());
626 threadContinue();
627 QMetaObject::invokeMethod(m_audioProcessor, "requestSampleRate", Q_ARG(unsigned, rate));
628 }
629}
630
631void GameController::setAudioChannelEnabled(int channel, bool enable) {
632 if (channel > 5 || channel < 0) {
633 return;
634 }
635 m_audioChannels[channel] = enable;
636 if (isLoaded()) {
637 switch (channel) {
638 case 0:
639 case 1:
640 case 2:
641 case 3:
642 m_threadContext.gba->audio.forceDisableCh[channel] = !enable;
643 break;
644 case 4:
645 m_threadContext.gba->audio.forceDisableChA = !enable;
646 break;
647 case 5:
648 m_threadContext.gba->audio.forceDisableChB = !enable;
649 break;
650 }
651 }
652}
653
654void GameController::startAudio() {
655 bool started = false;
656 QMetaObject::invokeMethod(m_audioProcessor, "start", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, started));
657 if (!started) {
658 LOG(ERROR) << tr("Failed to start audio processor");
659 // Don't freeze!
660 m_audioSync = false;
661 m_videoSync = true;
662 m_threadContext.sync.audioWait = false;
663 m_threadContext.sync.videoFrameWait = true;
664 }
665}
666
667void GameController::setVideoLayerEnabled(int layer, bool enable) {
668 if (layer > 4 || layer < 0) {
669 return;
670 }
671 m_videoLayers[layer] = enable;
672 if (isLoaded()) {
673 switch (layer) {
674 case 0:
675 case 1:
676 case 2:
677 case 3:
678 m_threadContext.gba->video.renderer->disableBG[layer] = !enable;
679 break;
680 case 4:
681 m_threadContext.gba->video.renderer->disableOBJ = !enable;
682 break;
683 }
684 }
685}
686
687void GameController::setFPSTarget(float fps) {
688 threadInterrupt();
689 m_fpsTarget = fps;
690 m_threadContext.fpsTarget = fps;
691 if (m_turbo && m_turboSpeed > 0) {
692 m_threadContext.fpsTarget *= m_turboSpeed;
693 }
694 if (m_audioProcessor) {
695 redoSamples(m_audioProcessor->getBufferSamples());
696 }
697 threadContinue();
698}
699
700void GameController::setSkipBIOS(bool set) {
701 threadInterrupt();
702 m_threadContext.skipBios = set;
703 threadContinue();
704}
705
706void GameController::setUseBIOS(bool use) {
707 if (use == m_useBios) {
708 return;
709 }
710 m_useBios = use;
711 if (m_gameOpen) {
712 closeGame();
713 openGame();
714 }
715}
716
717void GameController::loadState(int slot) {
718 if (!m_threadContext.fname) {
719 // We're in the BIOS
720 return;
721 }
722 if (slot > 0 && slot != m_stateSlot) {
723 m_stateSlot = slot;
724 m_backupSaveState.clear();
725 }
726 GBARunOnThread(&m_threadContext, [](GBAThread* context) {
727 GameController* controller = static_cast<GameController*>(context->userData);
728 if (!controller->m_backupLoadState) {
729 controller->m_backupLoadState = new GBASerializedState;
730 }
731 GBASerialize(context->gba, controller->m_backupLoadState);
732 if (GBALoadState(context, context->dirs.state, controller->m_stateSlot, controller->m_loadStateFlags)) {
733 controller->frameAvailable(controller->m_drawContext);
734 controller->stateLoaded(context);
735 }
736 });
737}
738
739void GameController::saveState(int slot) {
740 if (!m_threadContext.fname) {
741 // We're in the BIOS
742 return;
743 }
744 if (slot > 0) {
745 m_stateSlot = slot;
746 }
747 GBARunOnThread(&m_threadContext, [](GBAThread* context) {
748 GameController* controller = static_cast<GameController*>(context->userData);
749 VFile* vf = GBAGetState(context->gba, context->dirs.state, controller->m_stateSlot, false);
750 if (vf) {
751 controller->m_backupSaveState.resize(vf->size(vf));
752 vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
753 vf->close(vf);
754 }
755 GBASaveState(context, context->dirs.state, controller->m_stateSlot, controller->m_saveStateFlags);
756 });
757}
758
759void GameController::loadBackupState() {
760 if (!m_backupLoadState) {
761 return;
762 }
763
764 GBARunOnThread(&m_threadContext, [](GBAThread* context) {
765 GameController* controller = static_cast<GameController*>(context->userData);
766 if (GBADeserialize(context->gba, controller->m_backupLoadState)) {
767 GBALog(context->gba, GBA_LOG_STATUS, "Undid state load");
768 controller->frameAvailable(controller->m_drawContext);
769 controller->stateLoaded(context);
770 }
771 delete controller->m_backupLoadState;
772 controller->m_backupLoadState = nullptr;
773 });
774}
775
776void GameController::saveBackupState() {
777 if (m_backupSaveState.isEmpty()) {
778 return;
779 }
780
781 GBARunOnThread(&m_threadContext, [](GBAThread* context) {
782 GameController* controller = static_cast<GameController*>(context->userData);
783 VFile* vf = GBAGetState(context->gba, context->dirs.state, controller->m_stateSlot, true);
784 if (vf) {
785 vf->write(vf, controller->m_backupSaveState.constData(), controller->m_backupSaveState.size());
786 vf->close(vf);
787 GBALog(context->gba, GBA_LOG_STATUS, "Undid state save");
788 }
789 controller->m_backupSaveState.clear();
790 });
791}
792
793void GameController::setVideoSync(bool set) {
794 m_videoSync = set;
795 if (!m_turbo) {
796 threadInterrupt();
797 m_threadContext.sync.videoFrameWait = set;
798 threadContinue();
799 }
800}
801
802void GameController::setAudioSync(bool set) {
803 m_audioSync = set;
804 if (!m_turbo) {
805 threadInterrupt();
806 m_threadContext.sync.audioWait = set;
807 threadContinue();
808 }
809}
810
811void GameController::setFrameskip(int skip) {
812 threadInterrupt();
813 m_threadContext.frameskip = skip;
814 if (isLoaded()) {
815 m_threadContext.gba->video.frameskip = skip;
816 }
817 threadContinue();
818}
819
820void GameController::setVolume(int volume) {
821 threadInterrupt();
822 m_threadContext.volume = volume;
823 if (isLoaded()) {
824 m_threadContext.gba->audio.masterVolume = volume;
825 }
826 threadContinue();
827}
828
829void GameController::setMute(bool mute) {
830 threadInterrupt();
831 m_threadContext.mute = mute;
832 if (isLoaded()) {
833 m_threadContext.gba->audio.masterVolume = mute ? 0 : m_threadContext.volume;
834 }
835 threadContinue();
836}
837
838void GameController::setTurbo(bool set, bool forced) {
839 if (m_turboForced && !forced) {
840 return;
841 }
842 if (m_turbo == set && m_turboForced == forced) {
843 // Don't interrupt the thread if we don't need to
844 return;
845 }
846 m_turbo = set;
847 m_turboForced = set && forced;
848 enableTurbo();
849}
850
851void GameController::setTurboSpeed(float ratio) {
852 m_turboSpeed = ratio;
853 enableTurbo();
854}
855
856void GameController::enableTurbo() {
857 threadInterrupt();
858 if (!m_turbo) {
859 m_threadContext.fpsTarget = m_fpsTarget;
860 m_threadContext.sync.audioWait = m_audioSync;
861 m_threadContext.sync.videoFrameWait = m_videoSync;
862 } else if (m_turboSpeed <= 0) {
863 m_threadContext.fpsTarget = m_fpsTarget;
864 m_threadContext.sync.audioWait = false;
865 m_threadContext.sync.videoFrameWait = false;
866 } else {
867 m_threadContext.fpsTarget = m_fpsTarget * m_turboSpeed;
868 m_threadContext.sync.audioWait = true;
869 m_threadContext.sync.videoFrameWait = false;
870 }
871 if (m_audioProcessor) {
872 redoSamples(m_audioProcessor->getBufferSamples());
873 }
874 threadContinue();
875}
876
877void GameController::setAVStream(GBAAVStream* stream) {
878 threadInterrupt();
879 m_threadContext.stream = stream;
880 if (isLoaded()) {
881 m_threadContext.gba->stream = stream;
882 }
883 threadContinue();
884}
885
886void GameController::clearAVStream() {
887 threadInterrupt();
888 m_threadContext.stream = nullptr;
889 if (isLoaded()) {
890 m_threadContext.gba->stream = nullptr;
891 }
892 threadContinue();
893}
894
895#ifdef USE_PNG
896void GameController::screenshot() {
897 GBARunOnThread(&m_threadContext, GBAThreadTakeScreenshot);
898}
899#endif
900
901void GameController::reloadAudioDriver() {
902 int samples = 0;
903 unsigned sampleRate = 0;
904 if (m_audioProcessor) {
905 QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
906 samples = m_audioProcessor->getBufferSamples();
907 sampleRate = m_audioProcessor->sampleRate();
908 delete m_audioProcessor;
909 }
910 m_audioProcessor = AudioProcessor::create();
911 if (samples) {
912 m_audioProcessor->setBufferSamples(samples);
913 }
914 if (sampleRate) {
915 m_audioProcessor->requestSampleRate(sampleRate);
916 }
917 m_audioProcessor->moveToThread(m_audioThread);
918 connect(this, SIGNAL(gamePaused(GBAThread*)), m_audioProcessor, SLOT(pause()));
919 if (isLoaded()) {
920 m_audioProcessor->setInput(&m_threadContext);
921 startAudio();
922 }
923}
924
925void GameController::setSaveStateExtdata(int flags) {
926 m_saveStateFlags = flags;
927}
928
929void GameController::setLoadStateExtdata(int flags) {
930 m_loadStateFlags = flags;
931}
932
933void GameController::setLuminanceValue(uint8_t value) {
934 m_luxValue = value;
935 value = std::max<int>(value - 0x16, 0);
936 m_luxLevel = 10;
937 for (int i = 0; i < 10; ++i) {
938 if (value < GBA_LUX_LEVELS[i]) {
939 m_luxLevel = i;
940 break;
941 }
942 }
943 emit luminanceValueChanged(m_luxValue);
944}
945
946void GameController::setLuminanceLevel(int level) {
947 int value = 0x16;
948 level = std::max(0, std::min(10, level));
949 if (level > 0) {
950 value += GBA_LUX_LEVELS[level - 1];
951 }
952 setLuminanceValue(value);
953}
954
955void GameController::setRealTime() {
956 m_rtc.override = GBARTCGenericSource::RTC_NO_OVERRIDE;
957}
958
959void GameController::setFixedTime(const QDateTime& time) {
960 m_rtc.override = GBARTCGenericSource::RTC_FIXED;
961 m_rtc.value = time.toMSecsSinceEpoch() / 1000;
962}
963
964void GameController::setFakeEpoch(const QDateTime& time) {
965 m_rtc.override = GBARTCGenericSource::RTC_FAKE_EPOCH;
966 m_rtc.value = time.toMSecsSinceEpoch() / 1000;
967}
968
969void GameController::updateKeys() {
970 int activeKeys = m_activeKeys;
971 activeKeys |= m_activeButtons;
972 activeKeys &= ~m_inactiveKeys;
973 m_threadContext.activeKeys = activeKeys;
974}
975
976void GameController::redoSamples(int samples) {
977#if RESAMPLE_LIBRARY != RESAMPLE_BLIP_BUF
978 float sampleRate = 0x8000;
979 float ratio;
980 if (m_threadContext.gba) {
981 sampleRate = m_threadContext.gba->audio.sampleRate;
982 }
983 ratio = GBAAudioCalculateRatio(sampleRate, m_threadContext.fpsTarget, m_audioProcess->sampleRate());
984 m_threadContext.audioBuffers = ceil(samples / ratio);
985#else
986 m_threadContext.audioBuffers = samples;
987#endif
988 if (m_threadContext.gba) {
989 GBAAudioResizeBuffer(&m_threadContext.gba->audio, m_threadContext.audioBuffers);
990 }
991 QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
992}
993
994void GameController::setLogLevel(int levels) {
995 threadInterrupt();
996 m_logLevels = levels;
997 threadContinue();
998}
999
1000void GameController::enableLogLevel(int levels) {
1001 threadInterrupt();
1002 m_logLevels |= levels;
1003 threadContinue();
1004}
1005
1006void GameController::disableLogLevel(int levels) {
1007 threadInterrupt();
1008 m_logLevels &= ~levels;
1009 threadContinue();
1010}
1011
1012void GameController::pollEvents() {
1013 if (!m_inputController) {
1014 return;
1015 }
1016
1017 m_activeButtons = m_inputController->pollEvents();
1018 updateKeys();
1019}
1020
1021void GameController::updateAutofire() {
1022 // TODO: Move all key events onto the CPU thread...somehow
1023 for (int k = 0; k < GBA_KEY_MAX; ++k) {
1024 if (!m_autofire[k]) {
1025 continue;
1026 }
1027 m_autofireStatus[k] ^= 1;
1028 if (m_autofireStatus[k]) {
1029 keyPressed(k);
1030 } else {
1031 keyReleased(k);
1032 }
1033 }
1034}