all repos — mgba @ 56722324efdf8d5c9675f7658a35b0d05e9f8594

mGBA Game Boy Advance Emulator

Core: Put back rewind
Jeffrey Pfau jeffrey@endrift.com
Sun, 28 Aug 2016 20:38:24 -0700
commit

56722324efdf8d5c9675f7658a35b0d05e9f8594

parent

e9d83bafe38af7bdc149bf02877a32a1a3a9c20e

M src/core/config.csrc/core/config.c

@@ -307,7 +307,6 @@ _lookupIntValue(config, "logLevel", &opts->logLevel);

_lookupIntValue(config, "frameskip", &opts->frameskip); _lookupIntValue(config, "volume", &opts->volume); _lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity); - _lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval); _lookupFloatValue(config, "fpsTarget", &opts->fpsTarget); unsigned audioBuffers; if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {

@@ -363,7 +362,6 @@ ConfigurationSetIntValue(&config->defaultsTable, 0, "logLevel", opts->logLevel);

ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable); ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity); - ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval); ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget); ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers); ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
M src/core/config.hsrc/core/config.h

@@ -25,7 +25,6 @@ int logLevel;

int frameskip; bool rewindEnable; int rewindBufferCapacity; - int rewindBufferInterval; float fpsTarget; size_t audioBuffers; unsigned sampleRate;
M src/core/core.hsrc/core/core.h

@@ -145,6 +145,9 @@

struct mCore* mCoreFindVF(struct VFile* vf); enum mPlatform mCoreIsCompatible(struct VFile* vf); +bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags); +bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags); + void mCoreInitConfig(struct mCore* core, const char* port); void mCoreLoadConfig(struct mCore* core); void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config);
A src/core/rewind.c

@@ -0,0 +1,88 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "rewind.h" + +#include "core/core.h" +#include "util/patch-fast.h" +#include "util/vfs.h" + +DEFINE_VECTOR(mCoreRewindPatches, struct PatchFast); + +void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries) { + mCoreRewindPatchesInit(&context->patchMemory, entries); + size_t e; + for (e = 0; e < entries; ++e) { + initPatchFast(mCoreRewindPatchesAppend(&context->patchMemory)); + } + context->previousState = VFileMemChunk(0, 0); + context->currentState = VFileMemChunk(0, 0); + context->size = 0; +} + +void mCoreRewindContextDeinit(struct mCoreRewindContext* context) { + context->previousState->close(context->previousState); + context->currentState->close(context->currentState); + size_t s; + for (s = 0; s < mCoreRewindPatchesSize(&context->patchMemory); ++s) { + deinitPatchFast(mCoreRewindPatchesGetPointer(&context->patchMemory, s)); + } + mCoreRewindPatchesDeinit(&context->patchMemory); +} + +void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) { + struct VFile* nextState = context->previousState; + ++context->current; + if (context->size < mCoreRewindPatchesSize(&context->patchMemory)) { + ++context->size; + } + if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) { + context->current = 0; + } + mCoreSaveStateNamed(core, nextState, 0); + struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); + size_t size2 = nextState->size(nextState); + size_t size = context->currentState->size(context->currentState); + if (size2 > size) { + context->currentState->truncate(context->currentState, size2); + size = size2; + } + void* current = context->currentState->map(context->currentState, size, MAP_READ); + void* next = nextState->map(nextState, size, MAP_READ); + diffPatchFast(patch, current, next, size); + context->currentState->unmap(context->currentState, current, size); + nextState->unmap(next, nextState, size); + context->previousState = context->currentState; + context->currentState = nextState; +} + +bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) { + if (!context->size) { + return false; + } + --context->size; + + struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); + size_t size2 = context->previousState->size(context->previousState); + size_t size = context->currentState->size(context->currentState); + if (size2 < size) { + size = size2; + } + void* current = context->currentState->map(context->currentState, size, MAP_READ); + void* previous = context->previousState->map(context->previousState, size, MAP_WRITE); + patch->d.applyPatch(&patch->d, current, size, previous, size); + context->currentState->unmap(context->currentState, current, size); + context->previousState->unmap(context->previousState, previous, size); + mCoreLoadStateNamed(core, context->previousState, 0); + struct VFile* nextState = context->previousState; + context->previousState = context->currentState; + context->currentState = nextState; + + if (context->current == 0) { + context->current = mCoreRewindPatchesSize(&context->patchMemory); + } + --context->current; + return true; +}
A src/core/rewind.h

@@ -0,0 +1,31 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef M_CORE_REWIND_H +#define M_CORE_REWIND_H + +#include "util/common.h" + +#include "util/vector.h" + +DECLARE_VECTOR(mCoreRewindPatches, struct PatchFast); + +struct VFile; +struct mCoreRewindContext { + struct mCoreRewindPatches patchMemory; + size_t current; + size_t size; + struct VFile* previousState; + struct VFile* currentState; +}; + +void mCoreRewindContextInit(struct mCoreRewindContext*, size_t entries); +void mCoreRewindContextDeinit(struct mCoreRewindContext*); + +struct mCore; +void mCoreRewindAppend(struct mCoreRewindContext*, struct mCore*); +bool mCoreRewindRestore(struct mCoreRewindContext*, struct mCore*); + +#endif
M src/core/thread.csrc/core/thread.c

@@ -109,6 +109,10 @@ struct mCore* core = threadContext->core;

core->setSync(core, &threadContext->sync); core->reset(core); + if (core->opts.rewindEnable) { + mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity); + } + _changeState(threadContext, THREAD_RUNNING, true); if (threadContext->startCallback) {

@@ -126,14 +130,14 @@ if (debugger->state == DEBUGGER_SHUTDOWN) {

_changeState(threadContext, THREAD_EXITING, false); } } else { - while (threadContext->state == THREAD_RUNNING) { + while (threadContext->state <= THREAD_MAX_RUNNING) { core->runLoop(core); } } int resetScheduled = 0; MutexLock(&threadContext->stateMutex); - while (threadContext->state > THREAD_RUNNING && threadContext->state < THREAD_EXITING) { + while (threadContext->state > THREAD_MAX_RUNNING && threadContext->state < THREAD_EXITING) { if (threadContext->state == THREAD_PAUSING) { threadContext->state = THREAD_PAUSED; ConditionWake(&threadContext->stateCond);

@@ -168,6 +172,10 @@ }

while (threadContext->state < THREAD_SHUTDOWN) { _changeState(threadContext, THREAD_SHUTDOWN, false); + } + + if (core->opts.rewindEnable) { + mCoreRewindContextDeinit(&threadContext->rewind); } if (threadContext->cleanCallback) {

@@ -422,6 +430,18 @@

mCoreSyncSetVideoSync(&threadContext->sync, frameOn); } +void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) { + MutexLock(&threadContext->stateMutex); + _waitOnInterrupt(threadContext); + if (rewinding && threadContext->state == THREAD_RUNNING) { + threadContext->state = THREAD_REWINDING; + } + if (!rewinding && threadContext->state == THREAD_REWINDING) { + threadContext->state = THREAD_RUNNING; + } + MutexUnlock(&threadContext->stateMutex); +} + #ifdef USE_PTHREADS struct mCoreThread* mCoreThreadGet(void) { pthread_once(&_contextOnce, _createTLS);

@@ -438,10 +458,41 @@ return NULL;

} #endif +void mCoreThreadFrameStarted(struct mCoreThread* thread) { + if (!thread) { + return; + } + if (thread->core->opts.rewindEnable && thread->state != THREAD_REWINDING) { + mCoreRewindAppend(&thread->rewind, thread->core); + } else if (thread->state == THREAD_REWINDING) { + if (!mCoreRewindRestore(&thread->rewind, thread->core)) { + mCoreRewindAppend(&thread->rewind, thread->core); + } + } +} + +void mCoreThreadFrameEnded(struct mCoreThread* thread) { + if (!thread) { + return; + } + if (thread->frameCallback) { + thread->frameCallback(thread); + } +} + #else struct mCoreThread* mCoreThreadGet(void) { return NULL; } + +void mCoreThreadFrameStarted(struct mCoreThread* thread) { + UNUSED(thread); +} + +void mCoreThreadFrameEnded(struct mCoreThread* thread) { + UNUSED(thread); +} + #endif static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
M src/core/thread.hsrc/core/thread.h

@@ -9,6 +9,7 @@

#include "util/common.h" #include "core/log.h" +#include "core/rewind.h" #include "core/sync.h" #include "util/threading.h"

@@ -20,6 +21,8 @@

enum mCoreThreadState { THREAD_INITIALIZED = -1, THREAD_RUNNING = 0, + THREAD_REWINDING, + THREAD_MAX_RUNNING = THREAD_REWINDING, THREAD_INTERRUPTED, THREAD_INTERRUPTING, THREAD_PAUSED,

@@ -61,6 +64,7 @@ void* userData;

void (*run)(struct mCoreThread*); struct mCoreSync sync; + struct mCoreRewindContext rewind; }; bool mCoreThreadStart(struct mCoreThread* threadContext);

@@ -84,7 +88,13 @@ bool mCoreThreadIsPaused(struct mCoreThread* threadContext);

void mCoreThreadTogglePause(struct mCoreThread* threadContext); void mCoreThreadPauseFromThread(struct mCoreThread* threadContext); +void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool); + struct mCoreThread* mCoreThreadGet(void); + +void mCoreThreadFrameStarted(struct mCoreThread*); +void mCoreThreadFrameEnded(struct mCoreThread*); + struct mLogger* mCoreThreadLogger(void); #endif
M src/gb/video.csrc/gb/video.c

@@ -175,6 +175,8 @@ }

if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) { video->p->memory.rotation->sample(video->p->memory.rotation); } + struct mCoreThread* thread = mCoreThreadGet(); + mCoreThreadFrameStarted(thread); break; case 2: _cleanOAM(video, video->ly);

@@ -204,9 +206,7 @@ if (video->nextFrame <= 0) {

if (video->p->cpu->executionState == LR35902_CORE_FETCH) { GBFrameEnded(video->p); struct mCoreThread* thread = mCoreThreadGet(); - if (thread && thread->frameCallback) { - thread->frameCallback(thread); - } + mCoreThreadFrameEnded(thread); video->nextFrame = GB_VIDEO_TOTAL_LENGTH; } else { video->nextFrame = 4 - ((video->p->cpu->executionState + 1) & 3);
M src/gba/gba.csrc/gba/gba.c

@@ -800,7 +800,8 @@

void GBAFrameStarted(struct GBA* gba) { UNUSED(gba); - // TODO: Put back rewind + struct mCoreThread* thread = mCoreThreadGet(); + mCoreThreadFrameStarted(thread); } void GBAFrameEnded(struct GBA* gba) {

@@ -831,13 +832,7 @@ GBAHardwarePlayerUpdate(gba);

} struct mCoreThread* thread = mCoreThreadGet(); - if (!thread) { - return; - } - - if (thread->frameCallback) { - thread->frameCallback(thread); - } + mCoreThreadFrameEnded(thread); // TODO: Put back RR }
M src/gba/serialize.csrc/gba/serialize.c

@@ -225,5 +225,3 @@

void GBADeallocateState(struct GBASerializedState* state) { mappedMemoryFree(state, sizeof(struct GBASerializedState)); } - -// TODO: Put back rewind
M src/platform/qt/ConfigController.cppsrc/platform/qt/ConfigController.cpp

@@ -112,8 +112,7 @@ m_opts.sampleRate = 44100;

m_opts.volume = 0x100; m_opts.logLevel = mLOG_WARN | mLOG_ERROR | mLOG_FATAL; m_opts.rewindEnable = false; - m_opts.rewindBufferInterval = 0; - m_opts.rewindBufferCapacity = 0; + m_opts.rewindBufferCapacity = 60; m_opts.useBios = true; m_opts.suspendScreensaver = true; mCoreConfigLoad(&m_config);
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -234,13 +234,6 @@ };

m_threadContext.userData = this; - connect(&m_rewindTimer, &QTimer::timeout, [this]() { - // TODO: Put rewind back - emit frameAvailable(m_drawContext); - emit rewound(&m_threadContext); - }); - m_rewindTimer.setInterval(100); - m_audioThread->setObjectName("Audio Thread"); m_audioThread->start(QThread::TimeCriticalPriority); m_audioProcessor->moveToThread(m_audioThread);

@@ -529,7 +522,6 @@ return;

} m_gameOpen = false; - m_rewindTimer.stop(); if (mCoreThreadIsPaused(&m_threadContext)) { mCoreThreadUnpause(&m_threadContext); }

@@ -578,7 +570,7 @@ return QSize(width, height);

} void GameController::setPaused(bool paused) { - if (!isLoaded() || m_rewindTimer.isActive() || paused == mCoreThreadIsPaused(&m_threadContext)) { + if (!isLoaded() || paused == mCoreThreadIsPaused(&m_threadContext)) { return; } if (paused) {

@@ -617,15 +609,12 @@ }

} void GameController::frameAdvance() { - if (m_rewindTimer.isActive()) { - return; - } if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) { setPaused(false); } } -void GameController::setRewind(bool enable, int capacity, int interval) { +void GameController::setRewind(bool enable, int capacity) { if (m_gameOpen) { threadInterrupt(); // TODO: Put back rewind

@@ -638,9 +627,12 @@

void GameController::rewind(int states) { threadInterrupt(); if (!states) { - // TODO: Put back rewind - } else { - // TODO: Put back rewind + states = INT_MAX; + } + for (int i = 0; i < states; ++i) { + if (!mCoreRewindRestore(&m_threadContext.rewind, m_threadContext.core)) { + break; + } } threadContinue(); emit frameAvailable(m_drawContext);

@@ -648,24 +640,21 @@ emit rewound(&m_threadContext);

} void GameController::startRewinding() { - if (!m_gameOpen || m_rewindTimer.isActive()) { + if (!m_gameOpen) { return; } if (m_multiplayer && m_multiplayer->attached() > 1) { return; } - m_wasPaused = isPaused(); - if (!mCoreThreadIsPaused(&m_threadContext)) { - mCoreThreadPause(&m_threadContext); + m_wasPaused = mCoreThreadIsPaused(&m_threadContext); + if (m_wasPaused) { + mCoreThreadUnpause(&m_threadContext); } - m_rewindTimer.start(); + mCoreThreadSetRewinding(&m_threadContext, true); } void GameController::stopRewinding() { - if (!m_rewindTimer.isActive()) { - return; - } - m_rewindTimer.stop(); + mCoreThreadSetRewinding(&m_threadContext, false); bool signalsBlocked = blockSignals(true); setPaused(m_wasPaused); blockSignals(signalsBlocked);
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -116,7 +116,7 @@ void closeGame();

void setPaused(bool paused); void reset(); void frameAdvance(); - void setRewind(bool enable, int capacity, int interval); + void setRewind(bool enable, int capacity); void rewind(int states = 0); void startRewinding(); void stopRewinding();

@@ -202,7 +202,6 @@ float m_fpsTarget;

bool m_turbo; bool m_turboForced; float m_turboSpeed; - QTimer m_rewindTimer; bool m_wasPaused; bool m_audioChannels[6];
M src/platform/qt/SettingsView.cppsrc/platform/qt/SettingsView.cpp

@@ -165,14 +165,6 @@ m_ui.bios->setText(filename);

} } -void SettingsView::recalculateRewind() { - int interval = m_ui.rewindInterval->value(); - int capacity = m_ui.rewindCapacity->value(); - double duration = m_ui.fpsTarget->value(); - m_ui.rewindDuration->setValue(interval * capacity / duration); - -} - void SettingsView::updateConfig() { saveSetting("bios", m_ui.bios); saveSetting("useBios", m_ui.useBios);

@@ -187,7 +179,6 @@ saveSetting("lockAspectRatio", m_ui.lockAspectRatio);

saveSetting("volume", m_ui.volume); saveSetting("mute", m_ui.mute); saveSetting("rewindEnable", m_ui.rewind); - saveSetting("rewindBufferInterval", m_ui.rewindInterval); saveSetting("rewindBufferCapacity", m_ui.rewindCapacity); saveSetting("resampleVideo", m_ui.resampleVideo); saveSetting("allowOpposingDirections", m_ui.allowOpposingDirections);

@@ -262,7 +253,6 @@ loadSetting("lockAspectRatio", m_ui.lockAspectRatio);

loadSetting("volume", m_ui.volume); loadSetting("mute", m_ui.mute); loadSetting("rewindEnable", m_ui.rewind); - loadSetting("rewindBufferInterval", m_ui.rewindInterval); loadSetting("rewindBufferCapacity", m_ui.rewindCapacity); loadSetting("resampleVideo", m_ui.resampleVideo); loadSetting("allowOpposingDirections", m_ui.allowOpposingDirections);

@@ -282,10 +272,6 @@ m_ui.fastForwardUnbounded->setChecked(false);

m_ui.fastForwardRatio->setEnabled(true); m_ui.fastForwardRatio->setValue(fastForwardRatio); } - - connect(m_ui.rewindInterval, SIGNAL(valueChanged(int)), this, SLOT(recalculateRewind())); - connect(m_ui.rewindCapacity, SIGNAL(valueChanged(int)), this, SLOT(recalculateRewind())); - connect(m_ui.fpsTarget, SIGNAL(valueChanged(double)), this, SLOT(recalculateRewind())); QString idleOptimization = loadSetting("idleOptimization"); if (idleOptimization == "ignore") {
M src/platform/qt/SettingsView.hsrc/platform/qt/SettingsView.h

@@ -30,7 +30,6 @@ void pathsChanged();

private slots: void selectBios(); - void recalculateRewind(); void updateConfig(); void reloadConfig();
M src/platform/qt/SettingsView.uisrc/platform/qt/SettingsView.ui

@@ -6,8 +6,8 @@ <property name="geometry">

<rect> <x>0</x> <y>0</y> - <width>544</width> - <height>425</height> + <width>548</width> + <height>431</height> </rect> </property> <property name="sizePolicy">

@@ -72,7 +72,7 @@ </item>

<item row="1" column="1"> <widget class="QStackedWidget" name="stackedWidget"> <property name="currentIndex"> - <number>0</number> + <number>2</number> </property> <widget class="QWidget" name="stackedWidgetPage1"> <layout class="QFormLayout" name="formLayout">

@@ -110,7 +110,7 @@ <widget class="QComboBox" name="audioBufferSize">

<property name="editable"> <bool>true</bool> </property> - <property name="currentText" stdset="0"> + <property name="currentText"> <string>1536</string> </property> <property name="currentIndex">

@@ -176,7 +176,7 @@ <widget class="QComboBox" name="sampleRate">

<property name="editable"> <bool>true</bool> </property> - <property name="currentText" stdset="0"> + <property name="currentText"> <string>44100</string> </property> <property name="currentIndex">

@@ -487,13 +487,6 @@ <enum>Qt::Horizontal</enum>

</property> </widget> </item> - <item row="7" column="1"> - <widget class="QCheckBox" name="allowOpposingDirections"> - <property name="text"> - <string>Allow opposing input directions</string> - </property> - </widget> - </item> <item row="8" column="1"> <widget class="QCheckBox" name="suspendScreensaver"> <property name="text">

@@ -537,6 +530,13 @@ </property>

</item> </widget> </item> + <item row="7" column="1"> + <widget class="QCheckBox" name="allowOpposingDirections"> + <property name="text"> + <string>Allow opposing input directions</string> + </property> + </widget> + </item> </layout> </widget> <widget class="QWidget" name="page_2">

@@ -634,70 +634,25 @@ </property>

</widget> </item> <item row="10" column="0"> - <widget class="QLabel" name="label_4"> - <property name="text"> - <string>Create rewind state:</string> - </property> - </widget> - </item> - <item row="10" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_12"> - <item> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>Every</string> - </property> - </widget> - </item> - <item> - <widget class="QSpinBox" name="rewindInterval"/> - </item> - <item> - <widget class="QLabel" name="label_6"> - <property name="text"> - <string>frames</string> - </property> - </widget> - </item> - </layout> - </item> - <item row="11" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> <string>Rewind history:</string> </property> </widget> </item> - <item row="11" column="1"> + <item row="10" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_13"> <item> - <widget class="QSpinBox" name="rewindCapacity"/> - </item> - <item> - <widget class="QLabel" name="label_7"> - <property name="text"> - <string>states</string> - </property> - </widget> - </item> - </layout> - </item> - <item row="12" column="1"> - <layout class="QHBoxLayout" name="horizontalLayout_5"> - <item> - <widget class="QDoubleSpinBox" name="rewindDuration"> - <property name="enabled"> - <bool>false</bool> - </property> + <widget class="QSpinBox" name="rewindCapacity"> <property name="maximum"> - <double>999.990000000000009</double> + <number>3600</number> </property> </widget> </item> <item> - <widget class="QLabel" name="label_26"> + <widget class="QLabel" name="label_7"> <property name="text"> - <string>seconds</string> + <string>frames</string> </property> </widget> </item>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -1375,17 +1375,12 @@ }, this);

ConfigOption* rewindEnable = m_config->addOption("rewindEnable"); rewindEnable->connect([this](const QVariant& value) { - m_controller->setRewind(value.toBool(), m_config->getOption("rewindBufferCapacity").toInt(), m_config->getOption("rewindBufferInterval").toInt()); + m_controller->setRewind(value.toBool(), m_config->getOption("rewindBufferCapacity").toInt()); }, this); ConfigOption* rewindBufferCapacity = m_config->addOption("rewindBufferCapacity"); rewindBufferCapacity->connect([this](const QVariant& value) { - m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), value.toInt(), m_config->getOption("rewindBufferInterval").toInt()); - }, this); - - ConfigOption* rewindBufferInterval = m_config->addOption("rewindBufferInterval"); - rewindBufferInterval->connect([this](const QVariant& value) { - m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), m_config->getOption("rewindBufferCapacity").toInt(), value.toInt()); + m_controller->setRewind(m_config->getOption("rewindEnable").toInt(), value.toInt()); }, this); ConfigOption* allowOpposingDirections = m_config->addOption("allowOpposingDirections");
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -51,6 +51,7 @@

struct mCoreOptions opts = { .useBios = true, .rewindEnable = true, + .rewindBufferCapacity = 600, .audioBuffers = 512, .videoSync = false, .audioSync = true,
M src/platform/sdl/sdl-events.csrc/platform/sdl/sdl-events.c

@@ -406,6 +406,9 @@ if (event->keysym.sym == SDLK_TAB) {

context->sync.audioWait = event->type != SDL_KEYDOWN; return; } + if (event->keysym.sym == SDLK_BACKQUOTE) { + mCoreThreadSetRewinding(context, event->type == SDL_KEYDOWN); + } if (event->type == SDL_KEYDOWN) { switch (event->keysym.sym) { case SDLK_F11:

@@ -422,9 +425,6 @@ case SDLK_BACKSLASH:

mCoreThreadPause(context); context->frameCallback = _pauseAfterFrame; mCoreThreadUnpause(context); - return; - case SDLK_BACKQUOTE: - // TODO: Put back rewind return; #ifdef BUILD_PANDORA case SDLK_ESCAPE: