Core: Put back rewind

This commit is contained in:
Jeffrey Pfau 2016-08-28 20:38:24 -07:00
parent e9d83bafe3
commit 56722324ef
19 changed files with 232 additions and 136 deletions

View File

@ -307,7 +307,6 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
_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 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
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);

View File

@ -25,7 +25,6 @@ struct mCoreOptions {
int frameskip;
bool rewindEnable;
int rewindBufferCapacity;
int rewindBufferInterval;
float fpsTarget;
size_t audioBuffers;
unsigned sampleRate;

View File

@ -145,6 +145,9 @@ void mCoreTakeScreenshot(struct mCore* core);
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);

88
src/core/rewind.c Normal file
View File

@ -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;
}

31
src/core/rewind.h Normal file
View File

@ -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

View File

@ -109,6 +109,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
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 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
_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);
@ -170,6 +174,10 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
_changeState(threadContext, THREAD_SHUTDOWN, false);
}
if (core->opts.rewindEnable) {
mCoreRewindContextDeinit(&threadContext->rewind);
}
if (threadContext->cleanCallback) {
threadContext->cleanCallback(threadContext);
}
@ -422,6 +430,18 @@ void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
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 @@ struct mCoreThread* mCoreThreadGet(void) {
}
#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) {

View File

@ -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 @@ typedef void (*ThreadCallback)(struct mCoreThread* threadContext);
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 @@ struct mCoreThread {
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

View File

@ -175,6 +175,8 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
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 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
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);

View File

@ -800,7 +800,8 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
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 @@ void GBAFrameEnded(struct GBA* gba) {
}
struct mCoreThread* thread = mCoreThreadGet();
if (!thread) {
return;
}
if (thread->frameCallback) {
thread->frameCallback(thread);
}
mCoreThreadFrameEnded(thread);
// TODO: Put back RR
}

View File

@ -225,5 +225,3 @@ struct GBASerializedState* GBAAllocateState(void) {
void GBADeallocateState(struct GBASerializedState* state) {
mappedMemoryFree(state, sizeof(struct GBASerializedState));
}
// TODO: Put back rewind

View File

@ -112,8 +112,7 @@ ConfigController::ConfigController(QObject* parent)
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);

View File

@ -234,13 +234,6 @@ GameController::GameController(QObject* parent)
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 @@ void GameController::closeGame() {
}
m_gameOpen = false;
m_rewindTimer.stop();
if (mCoreThreadIsPaused(&m_threadContext)) {
mCoreThreadUnpause(&m_threadContext);
}
@ -578,7 +570,7 @@ QSize GameController::screenDimensions() const {
}
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::threadContinue() {
}
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::setRewind(bool enable, int capacity, int interval) {
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 @@ void GameController::rewind(int states) {
}
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);

View File

@ -116,7 +116,7 @@ public slots:
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 @@ private:
bool m_turbo;
bool m_turboForced;
float m_turboSpeed;
QTimer m_rewindTimer;
bool m_wasPaused;
bool m_audioChannels[6];

View File

@ -165,14 +165,6 @@ void SettingsView::selectBios() {
}
}
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 @@ void SettingsView::updateConfig() {
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 @@ void SettingsView::reloadConfig() {
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);
@ -283,10 +273,6 @@ void SettingsView::reloadConfig() {
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_ui.idleOptimization->setCurrentIndex(0);

View File

@ -30,7 +30,6 @@ signals:
private slots:
void selectBios();
void recalculateRewind();
void updateConfig();
void reloadConfig();

View File

@ -6,8 +6,8 @@
<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 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 @@
<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 @@
<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 @@
</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 @@
</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 @@
</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"/>
<widget class="QSpinBox" name="rewindCapacity">
<property name="maximum">
<number>3600</number>
</property>
</widget>
</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>
<property name="maximum">
<double>999.990000000000009</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_26">
<property name="text">
<string>seconds</string>
<string>frames</string>
</property>
</widget>
</item>

View File

@ -1375,17 +1375,12 @@ void Window::setupMenu(QMenuBar* menubar) {
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");

View File

@ -51,6 +51,7 @@ int main(int argc, char** argv) {
struct mCoreOptions opts = {
.useBios = true,
.rewindEnable = true,
.rewindBufferCapacity = 600,
.audioBuffers = 512,
.videoSync = false,
.audioSync = true,

View File

@ -406,6 +406,9 @@ static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer*
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:
@ -423,9 +426,6 @@ static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer*
context->frameCallback = _pauseAfterFrame;
mCoreThreadUnpause(context);
return;
case SDLK_BACKQUOTE:
// TODO: Put back rewind
return;
#ifdef BUILD_PANDORA
case SDLK_ESCAPE:
mCoreThreadEnd(context);