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