Added rewind + fast forward support

This commit is contained in:
Sour 2019-03-12 12:06:42 -04:00
parent 73913e1f0c
commit 7d068963a4
19 changed files with 657 additions and 52 deletions

View file

@ -24,6 +24,7 @@
#include "SaveStateManager.h"
#include "DebugStats.h"
#include "CartTypes.h"
#include "RewindManager.h"
#include "ConsoleLock.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/Timer.h"
@ -91,6 +92,8 @@ void Console::Run()
_cpu->Exec();
if(previousFrameCount != _ppu->GetFrameCount()) {
_rewindManager->ProcessEndOfFrame();
WaitForLock();
frameLimiter.ProcessFrame();
@ -134,6 +137,7 @@ void Console::Stop()
debugger.reset();
_videoDecoder->StopThread();
_rewindManager.reset();
_cpu.reset();
_ppu.reset();
@ -175,6 +179,9 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile)
_cpu.reset(new Cpu(this));
_memoryManager->IncrementMasterClockValue<170>();
_rewindManager.reset(new RewindManager(shared_from_this()));
_notificationManager->RegisterNotificationListener(_rewindManager);
//if(_debugger) {
//Reset debugger if it was running before
//auto lock = _debuggerLock.AcquireSafe();
@ -263,6 +270,8 @@ void Console::Deserialize(istream &in, uint32_t fileFormatVersion)
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
serializer.Stream(_spc.get());
_notificationManager->SendNotification(ConsoleNotificationType::StateLoaded);
}
shared_ptr<SoundMixer> Console::GetSoundMixer()
@ -295,6 +304,11 @@ shared_ptr<SaveStateManager> Console::GetSaveStateManager()
return _saveStateManager;
}
shared_ptr<RewindManager> Console::GetRewindManager()
{
return _rewindManager;
}
shared_ptr<DebugHud> Console::GetDebugHud()
{
return _debugHud;

View file

@ -21,6 +21,7 @@ class VideoDecoder;
class NotificationManager;
class EmuSettings;
class SaveStateManager;
class RewindManager;
enum class MemoryOperationType;
enum class SnesMemoryType;
enum class EventType;
@ -46,6 +47,7 @@ private:
shared_ptr<DebugHud> _debugHud;
shared_ptr<EmuSettings> _settings;
shared_ptr<SaveStateManager> _saveStateManager;
shared_ptr<RewindManager> _rewindManager;
thread::id _emulationThreadId;
@ -83,7 +85,7 @@ public:
shared_ptr<NotificationManager> GetNotificationManager();
shared_ptr<EmuSettings> GetSettings();
shared_ptr<SaveStateManager> GetSaveStateManager();
shared_ptr<RewindManager> GetRewindManager();
shared_ptr<DebugHud> GetDebugHud();
shared_ptr<Cpu> GetCpu();

View file

@ -105,6 +105,8 @@
<ClInclude Include="PpuTypes.h" />
<ClInclude Include="RamHandler.h" />
<ClInclude Include="RegisterHandlerA.h" />
<ClInclude Include="RewindData.h" />
<ClInclude Include="RewindManager.h" />
<ClInclude Include="RomHandler.h" />
<ClInclude Include="SaveStateManager.h" />
<ClInclude Include="ScaleFilter.h" />
@ -157,6 +159,8 @@
<ClCompile Include="Ppu.cpp" />
<ClCompile Include="PpuTools.cpp" />
<ClCompile Include="RegisterHandlerB.cpp" />
<ClCompile Include="RewindData.cpp" />
<ClCompile Include="RewindManager.cpp" />
<ClCompile Include="SaveStateManager.cpp" />
<ClCompile Include="ScaleFilter.cpp" />
<ClCompile Include="ShortcutKeyHandler.cpp" />

View file

@ -236,6 +236,12 @@
<ClInclude Include="ConsoleLock.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="RewindData.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="RewindManager.h">
<Filter>Misc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -378,6 +384,12 @@
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="ConsoleLock.cpp" />
<ClCompile Include="RewindData.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="RewindManager.cpp">
<Filter>Misc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -129,9 +129,22 @@ vector<KeyCombination> EmuSettings::GetShortcutSupersets(EmulatorShortcut shortc
return _shortcutSupersets[keySetIndex][(uint32_t)shortcut];
}
uint32_t EmuSettings::GetRewindBufferSize()
{
return _preferences.RewindBufferSize;
}
uint32_t EmuSettings::GetEmulationSpeed()
{
return _emulation.EmulationSpeed;
if(CheckFlag(EmulationFlags::MaximumSpeed)) {
return 0;
} else if(CheckFlag(EmulationFlags::Turbo)) {
return _emulation.TurboSpeed;
} else if(CheckFlag(EmulationFlags::Rewind)) {
return _emulation.RewindSpeed;
} else {
return _emulation.EmulationSpeed;
}
}
double EmuSettings::GetAspectRatio()
@ -152,4 +165,32 @@ double EmuSettings::GetAspectRatio()
case VideoAspectRatio::Custom: return _video.CustomAspectRatio;
}
return 0.0;
}
void EmuSettings::SetFlag(EmulationFlags flag)
{
if((_flags & (int)flag) == 0) {
_flags |= (int)flag;
}
}
void EmuSettings::SetFlagState(EmulationFlags flag, bool enabled)
{
if(enabled) {
SetFlag(flag);
} else {
ClearFlag(flag);
}
}
void EmuSettings::ClearFlag(EmulationFlags flag)
{
if((_flags & (int)flag) != 0) {
_flags &= ~(int)flag;
}
}
bool EmuSettings::CheckFlag(EmulationFlags flag)
{
return (_flags & (int)flag) != 0;
}

View file

@ -10,6 +10,8 @@ private:
EmulationConfig _emulation;
PreferencesConfig _preferences;
atomic<uint32_t> _flags;
string _audioDevice;
string _saveFolder;
string _saveStateFolder;
@ -42,6 +44,12 @@ public:
KeyCombination GetShortcutKey(EmulatorShortcut shortcut, int keySetIndex);
vector<KeyCombination> GetShortcutSupersets(EmulatorShortcut shortcut, int keySetIndex);
uint32_t GetRewindBufferSize();
uint32_t GetEmulationSpeed();
double GetAspectRatio();
void SetFlag(EmulationFlags flag);
void SetFlagState(EmulationFlags flag, bool enabled);
void ClearFlag(EmulationFlags flag);
bool CheckFlag(EmulationFlags flag);
};

View file

@ -11,6 +11,7 @@
#include "DmaController.h"
#include "MessageManager.h"
#include "EventType.h"
#include "RewindManager.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/Serializer.h"
@ -1074,8 +1075,9 @@ void Ppu::SendFrame()
uint16_t width = 512;
uint16_t height = _overscanMode ? 478 : 448;
if(_screenInterlace) {
_console->GetVideoDecoder()->UpdateFrameSync(_currentBuffer, width, height, _frameCount);
bool isRewinding = _console->GetRewindManager()->IsRewinding();
if(isRewinding || _screenInterlace) {
_console->GetVideoDecoder()->UpdateFrameSync(_currentBuffer, width, height, _frameCount, isRewinding);
} else {
_console->GetVideoDecoder()->UpdateFrame(_currentBuffer, width, height, _frameCount);
_currentBuffer = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0];

52
Core/RewindData.cpp Normal file
View file

@ -0,0 +1,52 @@
#include "stdafx.h"
#include "RewindData.h"
#include "Console.h"
#include "SaveStateManager.h"
#include "../Utilities/miniz.h"
void RewindData::GetStateData(stringstream &stateData)
{
unsigned long length = OriginalSaveStateSize;
uint8_t* buffer = new uint8_t[length];
uncompress(buffer, &length, SaveStateData.data(), (unsigned long)SaveStateData.size());
stateData.write((char*)buffer, length);
delete[] buffer;
}
void RewindData::LoadState(shared_ptr<Console> &console)
{
if(SaveStateData.size() > 0 && OriginalSaveStateSize > 0) {
unsigned long length = OriginalSaveStateSize;
uint8_t* buffer = new uint8_t[length];
uncompress(buffer, &length, SaveStateData.data(), (unsigned long)SaveStateData.size());
stringstream stream;
stream.write((char*)buffer, length);
stream.seekg(0, ios::beg);
console->Deserialize(stream, SaveStateManager::FileFormatVersion);
delete[] buffer;
}
}
void RewindData::CompressState(string stateData, vector<uint8_t>& compressedState)
{
unsigned long compressedSize = compressBound((unsigned long)stateData.size());
uint8_t* compressedData = new uint8_t[compressedSize];
compress(compressedData, &compressedSize, (unsigned char*)stateData.c_str(), (unsigned long)stateData.size());
compressedState = vector<uint8_t>(compressedData, compressedData + compressedSize);
delete[] compressedData;
}
void RewindData::SaveState(shared_ptr<Console> &console)
{
std::stringstream state;
console->Serialize(state);
string stateData = state.str();
vector<uint8_t> compressedState;
CompressState(stateData, compressedState);
SaveStateData = compressedState;
OriginalSaveStateSize = (uint32_t)stateData.size();
FrameCount = 0;
}

25
Core/RewindData.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "BaseControlDevice.h"
class Console;
class RewindData
{
private:
vector<uint8_t> SaveStateData;
uint32_t OriginalSaveStateSize = 0;
void CompressState(string stateData, vector<uint8_t> &compressedState);
public:
std::deque<ControlDeviceState> InputLogs[BaseControlDevice::PortCount];
int32_t FrameCount = 0;
bool EndOfSegment = false;
void GetStateData(stringstream &stateData);
void LoadState(shared_ptr<Console> &console);
void SaveState(shared_ptr<Console> &console);
};

354
Core/RewindManager.cpp Normal file
View file

@ -0,0 +1,354 @@
#include "stdafx.h"
#include "RewindManager.h"
#include "MessageManager.h"
#include "Console.h"
#include "EmuSettings.h"
#include "ControlManager.h"
#include "VideoRenderer.h"
#include "SoundMixer.h"
#include "BaseControlDevice.h"
RewindManager::RewindManager(shared_ptr<Console> console)
{
_console = console;
_settings = console->GetSettings();
_rewindState = RewindState::Stopped;
_framesToFastForward = 0;
_hasHistory = false;
AddHistoryBlock();
_console->GetControlManager()->RegisterInputProvider(this);
_console->GetControlManager()->RegisterInputRecorder(this);
}
RewindManager::~RewindManager()
{
_console->GetControlManager()->UnregisterInputProvider(this);
_console->GetControlManager()->UnregisterInputRecorder(this);
}
void RewindManager::ClearBuffer()
{
_hasHistory = false;
_history.clear();
_historyBackup.clear();
_currentHistory = RewindData();
_framesToFastForward = 0;
_videoHistory.clear();
_videoHistoryBuilder.clear();
_audioHistory.clear();
_audioHistoryBuilder.clear();
_rewindState = RewindState::Stopped;
_currentHistory = RewindData();
}
void RewindManager::ProcessNotification(ConsoleNotificationType type, void * parameter)
{
if(type == ConsoleNotificationType::PpuFrameDone) {
_hasHistory = _history.size() >= 2;
if(_settings->GetRewindBufferSize() > 0) {
switch(_rewindState) {
case RewindState::Starting:
case RewindState::Started:
case RewindState::Debugging:
_currentHistory.FrameCount--;
break;
case RewindState::Stopping:
_framesToFastForward--;
_currentHistory.FrameCount++;
if(_framesToFastForward == 0) {
for(int i = 0; i < 4; i++) {
size_t numberToRemove = _currentHistory.InputLogs[i].size();
_currentHistory.InputLogs[i] = _historyBackup.front().InputLogs[i];
for(size_t j = 0; j < numberToRemove; j++) {
_currentHistory.InputLogs[i].pop_back();
}
}
_historyBackup.clear();
_rewindState = RewindState::Stopped;
_settings->ClearFlag(EmulationFlags::Rewind);
_settings->ClearFlag(EmulationFlags::MaximumSpeed);
}
break;
case RewindState::Stopped:
_currentHistory.FrameCount++;
break;
}
} else {
ClearBuffer();
}
} else if(type == ConsoleNotificationType::StateLoaded) {
if(_rewindState == RewindState::Stopped) {
//A save state was loaded by the user, mark as the end of the current "segment" (for history viewer)
_currentHistory.EndOfSegment = true;
}
}
}
void RewindManager::AddHistoryBlock()
{
uint32_t maxHistorySize = _settings->GetRewindBufferSize() * 120;
if(maxHistorySize > 0) {
while(_history.size() > maxHistorySize) {
_history.pop_front();
}
if(_currentHistory.FrameCount > 0) {
_history.push_back(_currentHistory);
}
_currentHistory = RewindData();
_currentHistory.SaveState(_console);
}
}
void RewindManager::PopHistory()
{
if(_history.empty() && _currentHistory.FrameCount <= 0) {
StopRewinding();
} else {
if(_currentHistory.FrameCount <= 0) {
_currentHistory = _history.back();
_history.pop_back();
}
_historyBackup.push_front(_currentHistory);
_currentHistory.LoadState(_console);
if(!_audioHistoryBuilder.empty()) {
_audioHistory.insert(_audioHistory.begin(), _audioHistoryBuilder.begin(), _audioHistoryBuilder.end());
_audioHistoryBuilder.clear();
}
}
}
void RewindManager::Start(bool forDebugger)
{
if(_rewindState == RewindState::Stopped && _settings->GetRewindBufferSize() > 0) {
auto lock = _console->AcquireLock();
_rewindState = forDebugger ? RewindState::Debugging : RewindState::Starting;
_videoHistoryBuilder.clear();
_videoHistory.clear();
_audioHistoryBuilder.clear();
_audioHistory.clear();
_historyBackup.clear();
PopHistory();
_console->GetSoundMixer()->StopAudio(true);
_settings->SetFlag(EmulationFlags::MaximumSpeed);
_settings->SetFlag(EmulationFlags::Rewind);
}
}
void RewindManager::ForceStop()
{
if(_rewindState != RewindState::Stopped) {
while(_historyBackup.size() > 1) {
_history.push_back(_historyBackup.front());
_historyBackup.pop_front();
}
_currentHistory = _historyBackup.front();
_historyBackup.clear();
_rewindState = RewindState::Stopped;
_settings->ClearFlag(EmulationFlags::MaximumSpeed);
_settings->ClearFlag(EmulationFlags::Rewind);
}
}
void RewindManager::Stop()
{
if(_rewindState >= RewindState::Starting) {
auto lock = _console->AcquireLock();
if(_rewindState == RewindState::Started) {
//Move back to the save state containing the frame currently shown on the screen
if(_historyBackup.size() > 1) {
_framesToFastForward = (uint32_t)_videoHistory.size() + _historyBackup.front().FrameCount;
do {
_history.push_back(_historyBackup.front());
_framesToFastForward -= _historyBackup.front().FrameCount;
_historyBackup.pop_front();
_currentHistory = _historyBackup.front();
}
while(_framesToFastForward > RewindManager::BufferSize && _historyBackup.size() > 1);
}
} else {
//We started rewinding, but didn't actually visually rewind anything yet
//Move back to the save state containing the frame currently shown on the screen
while(_historyBackup.size() > 1) {
_history.push_back(_historyBackup.front());
_historyBackup.pop_front();
}
_currentHistory = _historyBackup.front();
_framesToFastForward = _historyBackup.front().FrameCount;
}
_currentHistory.LoadState(_console);
if(_framesToFastForward > 0) {
_rewindState = RewindState::Stopping;
_currentHistory.FrameCount = 0;
_settings->SetFlag(EmulationFlags::MaximumSpeed);
} else {
_rewindState = RewindState::Stopped;
_historyBackup.clear();
_settings->ClearFlag(EmulationFlags::MaximumSpeed);
_settings->ClearFlag(EmulationFlags::Rewind);
}
_videoHistoryBuilder.clear();
_videoHistory.clear();
_audioHistoryBuilder.clear();
_audioHistory.clear();
}
}
void RewindManager::ProcessEndOfFrame()
{
if(_rewindState >= RewindState::Starting) {
if(_currentHistory.FrameCount <= 0 && _rewindState != RewindState::Debugging) {
//If we're debugging, we want to keep running the emulation to the end of the next frame (even if it's incomplete)
//Otherwise the emulation might diverge due to missing inputs.
PopHistory();
}
} else if(_currentHistory.FrameCount >= RewindManager::BufferSize) {
AddHistoryBlock();
}
}
void RewindManager::ProcessFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
{
if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
if(!forRewind) {
//Ignore any frames that occur between start of rewind process & first rewinded frame completed
//These are caused by the fact that VideoDecoder is asynchronous - a previous (extra) frame can end up
//in the rewind queue, which causes display glitches
return;
}
_videoHistoryBuilder.push_back(vector<uint32_t>((uint32_t*)frameBuffer, (uint32_t*)frameBuffer + width*height));
if(_videoHistoryBuilder.size() == (size_t)_historyBackup.front().FrameCount) {
for(int i = (int)_videoHistoryBuilder.size() - 1; i >= 0; i--) {
_videoHistory.push_front(_videoHistoryBuilder[i]);
}
_videoHistoryBuilder.clear();
}
if(_rewindState == RewindState::Started || _videoHistory.size() >= RewindManager::BufferSize) {
_rewindState = RewindState::Started;
_settings->ClearFlag(EmulationFlags::MaximumSpeed);
if(!_videoHistory.empty()) {
_console->GetVideoRenderer()->UpdateFrame(_videoHistory.back().data(), width, height);
_videoHistory.pop_back();
}
}
} else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
//Display nothing while resyncing
} else {
_console->GetVideoRenderer()->UpdateFrame(frameBuffer, width, height);
}
}
bool RewindManager::ProcessAudio(int16_t * soundBuffer, uint32_t sampleCount)
{
if(_rewindState == RewindState::Starting || _rewindState == RewindState::Started) {
_audioHistoryBuilder.insert(_audioHistoryBuilder.end(), soundBuffer, soundBuffer + sampleCount * 2);
if(_rewindState == RewindState::Started && _audioHistory.size() > sampleCount * 2) {
for(uint32_t i = 0; i < sampleCount * 2; i++) {
soundBuffer[i] = _audioHistory.back();
_audioHistory.pop_back();
}
return true;
} else {
//Mute while we prepare to rewind
return false;
}
} else if(_rewindState == RewindState::Stopping || _rewindState == RewindState::Debugging) {
//Mute while we resync
return false;
} else {
return true;
}
}
void RewindManager::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
{
if(_settings->GetRewindBufferSize() > 0 && _rewindState == RewindState::Stopped) {
for(shared_ptr<BaseControlDevice> &device : devices) {
_currentHistory.InputLogs[device->GetPort()].push_back(device->GetRawState());
}
}
}
bool RewindManager::SetInput(BaseControlDevice *device)
{
uint8_t port = device->GetPort();
if(!_currentHistory.InputLogs[port].empty() && IsRewinding()) {
ControlDeviceState state = _currentHistory.InputLogs[port].front();
_currentHistory.InputLogs[port].pop_front();
device->SetRawState(state);
return true;
} else {
return false;
}
}
void RewindManager::StartRewinding(bool forDebugger)
{
Start(forDebugger);
}
void RewindManager::StopRewinding(bool forDebugger)
{
if(forDebugger) {
ForceStop();
} else {
Stop();
}
}
bool RewindManager::IsRewinding()
{
return _rewindState != RewindState::Stopped;
}
bool RewindManager::IsStepBack()
{
return _rewindState == RewindState::Debugging;
}
void RewindManager::RewindSeconds(uint32_t seconds)
{
if(_rewindState == RewindState::Stopped) {
uint32_t removeCount = (seconds * 60 / RewindManager::BufferSize) + 1;
auto lock = _console->AcquireLock();
for(uint32_t i = 0; i < removeCount; i++) {
if(!_history.empty()) {
_currentHistory = _history.back();
_history.pop_back();
} else {
break;
}
}
_currentHistory.LoadState(_console);
}
}
bool RewindManager::HasHistory()
{
return _hasHistory;
}
void RewindManager::SendFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind)
{
ProcessFrame(frameBuffer, width, height, forRewind);
}
bool RewindManager::SendAudio(int16_t * soundBuffer, uint32_t sampleCount)
{
return ProcessAudio(soundBuffer, sampleCount);
}

75
Core/RewindManager.h Normal file
View file

@ -0,0 +1,75 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "INotificationListener.h"
#include "RewindData.h"
#include "IInputProvider.h"
#include "IInputRecorder.h"
class Console;
class EmuSettings;
enum class RewindState
{
Stopped = 0,
Stopping = 1,
Starting = 2,
Started = 3,
Debugging = 4
};
class RewindManager : public INotificationListener, public IInputProvider, public IInputRecorder
{
private:
static constexpr int32_t BufferSize = 30; //Number of frames between each save state
shared_ptr<Console> _console;
shared_ptr<EmuSettings> _settings;
bool _hasHistory;
std::deque<RewindData> _history;
std::deque<RewindData> _historyBackup;
RewindData _currentHistory;
RewindState _rewindState;
int32_t _framesToFastForward;
std::deque<vector<uint32_t>> _videoHistory;
vector<vector<uint32_t>> _videoHistoryBuilder;
std::deque<int16_t> _audioHistory;
vector<int16_t> _audioHistoryBuilder;
void AddHistoryBlock();
void PopHistory();
void Start(bool forDebugger);
void Stop();
void ForceStop();
void ProcessFrame(void *frameBuffer, uint32_t width, uint32_t height, bool forRewind);
bool ProcessAudio(int16_t *soundBuffer, uint32_t sampleCount);
void ClearBuffer();
public:
RewindManager(shared_ptr<Console> console);
virtual ~RewindManager();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
void ProcessEndOfFrame();
void RecordInput(vector<shared_ptr<BaseControlDevice>> devices) override;
bool SetInput(BaseControlDevice *device) override;
void StartRewinding(bool forDebugger = false);
void StopRewinding(bool forDebugger = false);
bool IsRewinding();
bool IsStepBack();
void RewindSeconds(uint32_t seconds);
bool HasHistory();
void SendFrame(void *frameBuffer, uint32_t width, uint32_t height, bool forRewind);
bool SendAudio(int16_t *soundBuffer, uint32_t sampleCount);
};

View file

@ -27,7 +27,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define REGS (m.smp_regs [0])
#define REGS_IN (m.smp_regs [1])
void SNES_SPC::save_regs( uint8_t out [reg_count] )
void SNES_SPC::save_regs( uint8_t out [reg_count*2] )
{
// Use current timer counter values
for ( int i = 0; i < timer_count; i++ )
@ -35,6 +35,7 @@ void SNES_SPC::save_regs( uint8_t out [reg_count] )
// Last written values
memcpy( out, REGS, r_t0out );
memcpy( out + reg_count, REGS_IN, reg_count);
}
void SNES_SPC::init_header( void* spc_out )
@ -92,12 +93,15 @@ void SNES_SPC::copy_state( unsigned char** io, copy_func_t copy )
{
// SMP registers
uint8_t out_ports [port_count];
uint8_t regs [reg_count];
uint8_t regs [reg_count*2];
memcpy( out_ports, &REGS [r_cpuio0], sizeof out_ports );
save_regs( regs );
copier.copy( regs, sizeof regs );
copier.copy( out_ports, sizeof out_ports );
load_regs( regs );
memcpy(REGS, regs, reg_count);
memcpy(REGS_IN, regs + reg_count, reg_count);
regs_loaded();
memcpy( &REGS [r_cpuio0], out_ports, sizeof out_ports );
}

View file

@ -1,6 +1,13 @@
#pragma once
#include "stdafx.h"
enum class EmulationFlags
{
Turbo = 1,
Rewind = 2,
MaximumSpeed = 4
};
enum class ScaleFilterType
{
xBRZ = 0,
@ -146,15 +153,17 @@ struct EmulationConfig
struct PreferencesConfig
{
bool ShowFps;
bool ShowFrameCounter;
bool ShowGameTimer;
bool ShowDebugInfo;
bool DisableOsd;
bool ShowFps = false;
bool ShowFrameCounter = false;
bool ShowGameTimer = false;
bool ShowDebugInfo = false;
bool DisableOsd = false;
const char* SaveFolderOverride;
const char* SaveStateFolderOverride;
const char* ScreenshotFolderOverride;
uint32_t RewindBufferSize = 600;
const char* SaveFolderOverride = nullptr;
const char* SaveStateFolderOverride = nullptr;
const char* ScreenshotFolderOverride = nullptr;
};
struct OverscanDimensions

View file

@ -5,6 +5,7 @@
#include "VideoDecoder.h"
#include "ControlManager.h"
#include "Console.h"
#include "RewindManager.h"
#include "NotificationManager.h"
ShortcutKeyHandler::ShortcutKeyHandler(shared_ptr<Console> console)
@ -12,7 +13,6 @@ ShortcutKeyHandler::ShortcutKeyHandler(shared_ptr<Console> console)
_console = console;
_keySetIndex = 0;
_isKeyUp = false;
_keyboardMode = false;
_repeatStarted = false;
_stopThread = false;
@ -60,11 +60,7 @@ bool ShortcutKeyHandler::IsKeyPressed(KeyCombination comb)
bool ShortcutKeyHandler::IsKeyPressed(uint32_t keyCode)
{
if(keyCode >= 0x200 || !_keyboardMode) {
return KeyManager::IsKeyPressed(keyCode);
} else {
return false;
}
return KeyManager::IsKeyPressed(keyCode);
}
bool ShortcutKeyHandler::DetectKeyPress(EmulatorShortcut shortcut)
@ -125,22 +121,21 @@ void ShortcutKeyHandler::CheckMappedKeys()
}
}
//TODO
/*
if(DetectKeyPress(EmulatorShortcut::FastForward)) {
settings->SetFlags(EmulationFlags::Turbo);
settings->SetFlag(EmulationFlags::Turbo);
} else if(DetectKeyRelease(EmulatorShortcut::FastForward)) {
settings->ClearFlags(EmulationFlags::Turbo);
settings->ClearFlag(EmulationFlags::Turbo);
}
if(DetectKeyPress(EmulatorShortcut::ToggleFastForward)) {
if(settings->CheckFlag(EmulationFlags::Turbo)) {
settings->ClearFlags(EmulationFlags::Turbo);
settings->ClearFlag(EmulationFlags::Turbo);
} else {
settings->SetFlags(EmulationFlags::Turbo);
settings->SetFlag(EmulationFlags::Turbo);
}
}
/*
if(DetectKeyPress(EmulatorShortcut::MoveToNextStateSlot)) {
_console->GetSaveStateManager()->MoveToNextSlot();
}
@ -175,8 +170,7 @@ void ShortcutKeyHandler::CheckMappedKeys()
}
if(!isNetplayClient && !isMovieRecording) {
//TODO
/*shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
if(rewindManager) {
if(DetectKeyPress(EmulatorShortcut::ToggleRewind)) {
if(rewindManager->IsRewinding()) {
@ -195,7 +189,7 @@ void ShortcutKeyHandler::CheckMappedKeys()
} else if(DetectKeyPress(EmulatorShortcut::RewindOneMin)) {
rewindManager->RewindSeconds(60);
}
}*/
}
}
}

View file

@ -19,7 +19,6 @@ private:
vector<uint32_t> _pressedKeys;
vector<uint32_t> _lastPressedKeys;
bool _isKeyUp;
bool _keyboardMode;
shared_ptr<Timer> _runSingleFrameRepeatTimer;
bool _repeatStarted;

View file

@ -3,6 +3,7 @@
#include "Console.h"
#include "EmuSettings.h"
#include "SoundResampler.h"
#include "RewindManager.h"
#include "../Utilities/Equalizer.h"
#include "../Utilities/blip_buf.h"
@ -65,11 +66,14 @@ void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount)
}
}
if(cfg.SampleRate == SoundResampler::SpcSampleRate && cfg.DisableDynamicSampleRate) {
_audioDevice->PlayBuffer(samples, sampleCount, cfg.SampleRate, true);
} else {
uint32_t resampledCount = _resampler->Resample(samples, sampleCount, cfg.SampleRate, _sampleBuffer);
_audioDevice->PlayBuffer(_sampleBuffer, resampledCount, cfg.SampleRate, true);
shared_ptr<RewindManager> rewindManager = _console->GetRewindManager();
if(rewindManager && rewindManager->SendAudio(samples, (uint32_t)sampleCount)) {
if(cfg.SampleRate == SoundResampler::SpcSampleRate && cfg.DisableDynamicSampleRate) {
_audioDevice->PlayBuffer(samples, sampleCount, cfg.SampleRate, true);
} else {
uint32_t resampledCount = _resampler->Resample(samples, sampleCount, cfg.SampleRate, _sampleBuffer);
_audioDevice->PlayBuffer(_sampleBuffer, resampledCount, cfg.SampleRate, true);
}
}
_audioDevice->ProcessEndOfFrame();
}

View file

@ -5,6 +5,7 @@
#include "DefaultVideoFilter.h"
#include "NotificationManager.h"
#include "Console.h"
#include "RewindManager.h"
#include "EmuSettings.h"
#include "SettingTypes.h"
#include "NtscFilter.h"
@ -78,7 +79,7 @@ void VideoDecoder::UpdateVideoFilter()
}*/
}
void VideoDecoder::DecodeFrame(bool synchronous)
void VideoDecoder::DecodeFrame(bool forRewind)
{
UpdateVideoFilter();
@ -117,7 +118,7 @@ void VideoDecoder::DecodeFrame(bool synchronous)
_frameChanged = false;
//Rewind manager will take care of sending the correct frame to the video renderer
_console->GetVideoRenderer()->UpdateFrame(outputBuffer, frameInfo.Width, frameInfo.Height);
_console->GetRewindManager()->SendFrame(outputBuffer, frameInfo.Width, frameInfo.Height, forRewind);
}
void VideoDecoder::DecodeThread()
@ -141,13 +142,13 @@ uint32_t VideoDecoder::GetFrameCount()
return _frameCount;
}
void VideoDecoder::UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber)
void VideoDecoder::UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber, bool forRewind)
{
_baseFrameInfo.Width = width;
_baseFrameInfo.Height = height;
_frameNumber = frameNumber;
_ppuOutputBuffer = ppuOutputBuffer;
DecodeFrame(true);
DecodeFrame(forRewind);
_frameCount++;
}

View file

@ -55,7 +55,7 @@ public:
FrameInfo GetFrameInfo();
ScreenSize GetScreenSize(bool ignoreScale);
void UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber);
void UpdateFrameSync(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber, bool forRewind);
void UpdateFrame(uint16_t *ppuOutputBuffer, uint16_t width, uint16_t height, uint32_t frameNumber);
bool IsRunning();

View file

@ -21,6 +21,8 @@ namespace Mesen.GUI.Config
public bool AssociateMsmFiles = false;
public bool AssociateMssFiles = false;
public UInt32 RewindBufferSize = 600;
public bool AlwaysOnTop = false;
public bool ShowFps = false;
@ -128,17 +130,6 @@ namespace Mesen.GUI.Config
FileAssociationHelper.UpdateFileAssociation("mss", this.AssociateMssFiles);
}
ConfigApi.SetPreferences(new InteropPreferencesConfig() {
ShowFps = ShowFps,
ShowFrameCounter = ShowFrameCounter,
ShowGameTimer = ShowGameTimer,
ShowDebugInfo = ShowDebugInfo,
DisableOsd = DisableOsd,
SaveFolderOverride = OverrideSaveDataFolder ? SaveDataFolder : "",
SaveStateFolderOverride = OverrideSaveStateFolder ? SaveStateFolder : "",
ScreenshotFolderOverride = OverrideScreenshotFolder ? ScreenshotFolder : ""
});
Application.OpenForms[0].TopMost = AlwaysOnTop;
ShortcutKeyInfo[] shortcutKeys = new ShortcutKeyInfo[ShortcutKeys1.Count + ShortcutKeys2.Count];
@ -150,6 +141,18 @@ namespace Mesen.GUI.Config
shortcutKeys[i++] = shortcutInfo;
}
ConfigApi.SetShortcutKeys(shortcutKeys, (UInt32)shortcutKeys.Length);
ConfigApi.SetPreferences(new InteropPreferencesConfig() {
ShowFps = ShowFps,
ShowFrameCounter = ShowFrameCounter,
ShowGameTimer = ShowGameTimer,
ShowDebugInfo = ShowDebugInfo,
DisableOsd = DisableOsd,
SaveFolderOverride = OverrideSaveDataFolder ? SaveDataFolder : "",
SaveStateFolderOverride = OverrideSaveStateFolder ? SaveStateFolder : "",
ScreenshotFolderOverride = OverrideScreenshotFolder ? ScreenshotFolder : "",
RewindBufferSize = RewindBufferSize
});
}
}
@ -161,6 +164,8 @@ namespace Mesen.GUI.Config
[MarshalAs(UnmanagedType.I1)] public bool ShowDebugInfo;
[MarshalAs(UnmanagedType.I1)] public bool DisableOsd;
public UInt32 RewindBufferSize;
public string SaveFolderOverride;
public string SaveStateFolderOverride;
public string ScreenshotFolderOverride;