Added rewind functionality

This commit is contained in:
Souryo 2017-04-28 19:54:58 -04:00
parent 8a2f09b0fd
commit 3a6c8ca416
42 changed files with 934 additions and 284 deletions

View File

@ -42,7 +42,6 @@ class APU : public Snapshotable, public IMemoryHandler
private:
__forceinline bool NeedToRun(uint32_t currentCycle);
void Run();
void EndFrame();
static void FrameCounterTick(FrameType type);
@ -53,6 +52,8 @@ class APU : public Snapshotable, public IMemoryHandler
APU(MemoryManager* memoryManager);
~APU();
void EndFrame();
void Reset(bool softReset);
void SetNesModel(NesModel model, bool forceInit = false);

View File

@ -28,6 +28,8 @@ bool AviRecorder::StartRecording(string filename, VideoCodec codec, uint32_t wid
if(!_recording) {
_outputFile = filename;
_sampleRate = audioSampleRate;
_width = width;
_height = height;
_frameBufferLength = height * width * bpp;
_frameBuffer = new uint8_t[_frameBufferLength];
@ -71,12 +73,16 @@ void AviRecorder::StopRecording()
}
}
void AviRecorder::AddFrame(void* frameBuffer)
void AviRecorder::AddFrame(void* frameBuffer, uint32_t width, uint32_t height)
{
if(_recording) {
auto lock = _lock.AcquireSafe();
memcpy(_frameBuffer, frameBuffer, _frameBufferLength);
_waitFrame.Signal();
if(_width != width || _height != height) {
StopRecording();
} else {
auto lock = _lock.AcquireSafe();
memcpy(_frameBuffer, frameBuffer, _frameBufferLength);
_waitFrame.Signal();
}
}
}

View File

@ -22,6 +22,9 @@ private:
uint32_t _frameBufferLength;
uint32_t _sampleRate;
uint32_t _width;
uint32_t _height;
public:
AviRecorder();
virtual ~AviRecorder();
@ -29,9 +32,8 @@ public:
bool StartRecording(string filename, VideoCodec codec, uint32_t width, uint32_t height, uint32_t bpp, uint32_t fps, uint32_t audioSampleRate, uint32_t compressionLevel);
void StopRecording();
void AddFrame(void* frameBuffer);
void AddFrame(void* frameBuffer, uint32_t width, uint32_t height);
void AddSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
void SendAudio();
bool IsRecording();
};

View File

@ -6,6 +6,7 @@
#include "GameClient.h"
#include "GameServerConnection.h"
#include "AutomaticRomTest.h"
#include "RewindManager.h"
BaseControlDevice::BaseControlDevice(uint8_t port)
{
@ -60,7 +61,9 @@ uint8_t BaseControlDevice::ProcessNetPlayState(uint32_t netplayState)
uint8_t BaseControlDevice::GetControlState()
{
GameServerConnection* netPlayDevice = GameServerConnection::GetNetPlayDevice(_port);
if(MovieManager::Playing()) {
if(RewindManager::IsRewinding()) {
_currentState = RewindManager::GetInput(_port);
} else if(MovieManager::Playing()) {
_currentState = MovieManager::GetState(_port);
} else if(GameClient::Connected()) {
_currentState = GameClient::GetControllerState(_port);
@ -79,5 +82,7 @@ uint8_t BaseControlDevice::GetControlState()
//For NetPlay
ControlManager::BroadcastInput(_port, _currentState);
RewindManager::RecordInput(_port, _currentState);
return _currentState;
}

View File

@ -152,6 +152,30 @@ void CPU::IRQ()
}
}
void CPU::BRK() {
Push((uint16_t)(PC() + 1));
uint8_t flags = PS() | PSFlags::Break;
if(_state.NMIFlag) {
Push((uint8_t)flags);
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::NMIVector));
TraceLogger::LogStatic("NMI");
} else {
Push((uint8_t)flags);
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::IRQVector));
TraceLogger::LogStatic("IRQ");
}
//Since we just set the flag to prevent interrupts, do not run one right away after this (fixes nmi_and_brk & nmi_and_irq tests)
_prevRunIrq = false;
}
uint16_t CPU::FetchOperand()
{
switch(_instAddrMode) {

View File

@ -3,7 +3,6 @@
#include "stdafx.h"
#include "MemoryManager.h"
#include "Snapshotable.h"
#include "TraceLogger.h"
#include "EmulationSettings.h"
#include "Types.h"
@ -600,29 +599,7 @@ private:
void SED() { SetFlags(PSFlags::Decimal); }
void SEI() { SetFlags(PSFlags::Interrupt); }
void BRK() {
Push((uint16_t)(PC() + 1));
uint8_t flags = PS() | PSFlags::Break;
if(_state.NMIFlag) {
Push((uint8_t)flags);
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::NMIVector));
TraceLogger::LogStatic("NMI");
} else {
Push((uint8_t)flags);
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::IRQVector));
TraceLogger::LogStatic("IRQ");
}
//Since we just set the flag to prevent interrupts, do not run one right away after this (fixes nmi_and_brk & nmi_and_irq tests)
_prevRunIrq = false;
}
void BRK();
void RTI() {
DummyRead();

View File

@ -18,6 +18,7 @@
#include "NsfMapper.h"
#include "ShortcutKeyHandler.h"
#include "MovieManager.h"
#include "RewindManager.h"
shared_ptr<Console> Console::Instance(new Console());
@ -95,6 +96,8 @@ bool Console::Initialize(string romFilename, stringstream *filestream, string pa
ResetComponents(false);
_rewindManager.reset(new RewindManager());
VideoDecoder::GetInstance()->StartThread();
FolderUtilities::AddKnownGameFolder(FolderUtilities::GetFolderName(romFilename));
@ -337,10 +340,11 @@ void Console::Run()
uint32_t currentFrameNumber = PPU::GetFrameCount();
if(currentFrameNumber != lastFrameNumber) {
_rewindManager->ProcessEndOfFrame();
EmulationSettings::DisableOverclocking(_disableOcNextFrame);
_disableOcNextFrame = false;
lastFrameNumber = currentFrameNumber;
lastFrameNumber = PPU::GetFrameCount();
//Sleep until we're ready to start the next frame
clockTimer.WaitUntil(targetTime);
@ -402,6 +406,7 @@ void Console::Run()
}
}
}
_rewindManager.reset();
SoundMixer::StopAudio();
MovieManager::Stop();
SoundMixer::StopRecording();
@ -509,6 +514,9 @@ void Console::LoadState(istream &loadStream)
void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
{
//Send any unprocessed sound to the SoundMixer - needed for rewind
Instance->_apu->EndFrame();
stringstream stream;
stream.write((char*)buffer, bufferSize);
stream.seekg(0, ios::beg);

View File

@ -13,6 +13,7 @@
class Debugger;
class BaseMapper;
class RewindManager;
class Console
{
@ -22,6 +23,7 @@ class Console
SimpleLock _runLock;
SimpleLock _stopLock;
shared_ptr<RewindManager> _rewindManager;
shared_ptr<CPU> _cpu;
shared_ptr<PPU> _ppu;
unique_ptr<APU> _apu;

View File

@ -449,6 +449,8 @@
<ClInclude Include="CrossFeedFilter.h" />
<ClInclude Include="GoldenFive.h" />
<ClInclude Include="MesenMovie.h" />
<ClInclude Include="RewindData.h" />
<ClInclude Include="RewindManager.h" />
<ClInclude Include="SealieComputing.h" />
<ClInclude Include="UnlD1038.h" />
<ClInclude Include="DaouInfosys.h" />
@ -807,6 +809,8 @@
<ClCompile Include="OekaKidsTablet.cpp" />
<ClCompile Include="Profiler.cpp" />
<ClCompile Include="ReverbFilter.cpp" />
<ClCompile Include="RewindData.cpp" />
<ClCompile Include="RewindManager.cpp" />
<ClCompile Include="RomLoader.cpp" />
<ClCompile Include="ShortcutKeyHandler.cpp" />
<ClCompile Include="Snapshotable.cpp" />

View File

@ -89,6 +89,9 @@
<Filter Include="Movies">
<UniqueIdentifier>{16c0726b-0c54-4fbc-a602-7930348d7f44}</UniqueIdentifier>
</Filter>
<Filter Include="Rewinder">
<UniqueIdentifier>{52b03b24-dd62-4daf-bac8-bb60a555d3d2}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="IAudioDevice.h">
@ -1162,6 +1165,12 @@
<ClInclude Include="FceuxMovie.h">
<Filter>Movies</Filter>
</ClInclude>
<ClInclude Include="RewindData.h">
<Filter>Rewinder</Filter>
</ClInclude>
<ClInclude Include="RewindManager.h">
<Filter>Rewinder</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -1377,5 +1386,11 @@
<ClCompile Include="FceuxMovie.cpp">
<Filter>Movies</Filter>
</ClCompile>
<ClCompile Include="RewindData.cpp">
<Filter>Rewinder</Filter>
</ClCompile>
<ClCompile Include="RewindManager.cpp">
<Filter>Rewinder</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -33,6 +33,9 @@ PpuModel EmulationSettings::_ppuModel = PpuModel::Ppu2C02;
uint32_t EmulationSettings::_emulationSpeed = 100;
uint32_t EmulationSettings::_turboSpeed = 300;
uint32_t EmulationSettings::_rewindSpeed = 100;
uint32_t EmulationSettings::_rewindBufferSize = 300;
bool EmulationSettings::_hasOverclock = false;
uint32_t EmulationSettings::_overclockRate = 100;

View File

@ -242,6 +242,10 @@ struct KeyMappingSet
struct EmulatorKeyMappings
{
uint32_t FastForward;
uint32_t Rewind;
uint32_t RewindTenSecs;
uint32_t RewindOneMin;
uint32_t Pause;
uint32_t Reset;
uint32_t Exit;
@ -356,6 +360,9 @@ private:
static uint32_t _emulationSpeed;
static uint32_t _turboSpeed;
static uint32_t _rewindSpeed;
static uint32_t _rewindBufferSize;
static bool _hasOverclock;
static uint32_t _overclockRate;
@ -641,9 +648,25 @@ public:
}
}
static void SetTurboSpeed(uint32_t turboSpeed)
static void SetTurboRewindSpeed(uint32_t turboSpeed, uint32_t rewindSpeed)
{
_turboSpeed = turboSpeed;
_rewindSpeed = rewindSpeed;
}
static uint32_t GetRewindSpeed()
{
return _rewindSpeed;
}
static void SetRewindBufferSize(uint32_t seconds)
{
_rewindBufferSize = seconds;
}
static uint32_t GetRewindBufferSize()
{
return _rewindBufferSize;
}
static uint32_t GetEmulationSpeed(bool ignoreTurbo = false)

View File

@ -3,6 +3,7 @@
#include "PPU.h"
#include "HdNesPack.h"
#include "VideoDecoder.h"
#include "RewindManager.h"
class HdPpu : public PPU
{
@ -86,7 +87,13 @@ public:
void SendFrame()
{
VideoDecoder::GetInstance()->UpdateFrame(_currentOutputBuffer, _screenTiles);
MessageManager::SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer);
if(RewindManager::IsRewinding()) {
VideoDecoder::GetInstance()->UpdateFrameSync(_currentOutputBuffer, _screenTiles);
} else {
VideoDecoder::GetInstance()->UpdateFrame(_currentOutputBuffer, _screenTiles);
}
_currentOutputBuffer = (_currentOutputBuffer == _outputBuffers[0]) ? _outputBuffers[1] : _outputBuffers[0];
_screenTiles = (_screenTiles == _screenTileBuffers[0]) ? _screenTileBuffers[1] : _screenTileBuffers[0];

View File

@ -16,63 +16,23 @@ class ToastInfo
private:
string _title;
string _message;
uint8_t* _icon;
uint32_t _iconSize;
uint64_t _endTime;
uint64_t _startTime;
uint8_t* ReadFile(string filename, uint32_t &fileSize)
{
ifstream file(filename, ios::in | ios::binary);
if(file) {
file.seekg(0, ios::end);
fileSize = (uint32_t)file.tellg();
file.seekg(0, ios::beg);
uint8_t* buffer = new uint8_t[fileSize];
file.read((char*)buffer, fileSize);
return buffer;
}
return nullptr;
}
uint64_t GetCurrentTime()
{
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
}
public:
ToastInfo(string title, string message, int displayDuration, string iconFile)
ToastInfo(string title, string message, int displayDuration)
{
_title = title;
_message = message;
_icon = ReadFile(iconFile, _iconSize);
_startTime = GetCurrentTime();
_endTime = _startTime + displayDuration;
}
ToastInfo(string title, string message, int displayDuration, uint8_t* iconData, uint32_t iconSize)
{
_title = title;
_message = message;
_iconSize = iconSize;
_icon = new uint8_t[iconSize];
memcpy(_icon, iconData, iconSize);
_startTime = GetCurrentTime();
_endTime = _startTime + displayDuration;
}
~ToastInfo()
{
if(_icon) {
delete _icon;
}
}
string GetToastTitle()
{
return _title;
@ -83,21 +43,6 @@ public:
return _message;
}
uint8_t* GetToastIcon()
{
return _icon;
}
uint32_t GetIconSize()
{
return _iconSize;
}
bool HasIcon()
{
return _iconSize > 0;
}
float GetOpacity()
{
uint64_t currentTime = GetCurrentTime();

View File

@ -5,6 +5,7 @@
#include "VideoDecoder.h"
#include "Debugger.h"
#include "BaseMapper.h"
#include "RewindManager.h"
PPU* PPU::Instance = nullptr;
@ -968,7 +969,11 @@ void PPU::SendFrame()
MessageManager::SendNotification(ConsoleNotificationType::PpuFrameDone, _currentOutputBuffer);
VideoDecoder::GetInstance()->UpdateFrame(_currentOutputBuffer);
if(RewindManager::IsRewinding()) {
VideoDecoder::GetInstance()->UpdateFrameSync(_currentOutputBuffer);
} else {
VideoDecoder::GetInstance()->UpdateFrame(_currentOutputBuffer);
}
//Switch output buffer. VideoDecoder will decode the last frame while we build the new one.
//If VideoDecoder isn't fast enough, UpdateFrame will block until it is ready to accept a new frame.

35
Core/RewindData.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "stdafx.h"
#include "RewindData.h"
#include "Console.h"
#include "../Utilities/miniz.h"
void RewindData::LoadState()
{
unsigned long length = OriginalSaveStateSize;
uint8_t* buffer = new uint8_t[length];
uncompress(buffer, &length, SaveStateData.data(), (unsigned long)SaveStateData.size());
Console::LoadState(buffer, length);
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()
{
std::stringstream state;
Console::SaveState(state);
string stateData = state.str();
vector<uint8_t> compressedState;
CompressState(stateData, compressedState);
SaveStateData = compressedState;
OriginalSaveStateSize = (uint32_t)stateData.size();
FrameCount = 0;
}

19
Core/RewindData.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include "stdafx.h"
#include <deque>
class RewindData
{
private:
vector<uint8_t> SaveStateData;
uint32_t OriginalSaveStateSize;
void CompressState(string stateData, vector<uint8_t> &compressedState);
public:
std::deque<uint8_t> InputLogs[4];
int32_t FrameCount = 0;
void LoadState();
void SaveState();
};

285
Core/RewindManager.cpp Normal file
View File

@ -0,0 +1,285 @@
#include "stdafx.h"
#include "RewindManager.h"
#include "MessageManager.h"
#include "Console.h"
#include "VideoRenderer.h"
#include "SoundMixer.h"
RewindManager* RewindManager::_instance = nullptr;
RewindManager::RewindManager()
{
_instance = this;
_rewindState = RewindState::Stopped;
_framesToFastForward = 0;
AddHistoryBlock();
MessageManager::RegisterNotificationListener(this);
}
RewindManager::~RewindManager()
{
if(_instance == this) {
_instance = nullptr;
}
MessageManager::UnregisterNotificationListener(this);
}
void RewindManager::ProcessNotification(ConsoleNotificationType type, void * parameter)
{
if(type == ConsoleNotificationType::PpuFrameDone) {
if(_rewindState >= RewindState::Starting) {
_currentHistory.FrameCount--;
} else if(_rewindState == 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;
EmulationSettings::SetEmulationSpeed(100);
}
} else {
_currentHistory.FrameCount++;
}
}
}
void RewindManager::AddHistoryBlock()
{
uint32_t maxHistorySize = EmulationSettings::GetRewindBufferSize() * 120;
if(maxHistorySize == 0) {
_history.clear();
} else {
while(_history.size() > maxHistorySize) {
_history.pop_front();
}
if(_currentHistory.FrameCount > 0) {
_history.push_back(_currentHistory);
}
_currentHistory = RewindData();
_currentHistory.SaveState();
}
}
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();
if(!_audioHistoryBuilder.empty()) {
_audioHistory.insert(_audioHistory.begin(), _audioHistoryBuilder.begin(), _audioHistoryBuilder.end());
_audioHistoryBuilder.clear();
}
}
}
void RewindManager::Start()
{
if(_rewindState == RewindState::Stopped && EmulationSettings::GetRewindBufferSize() > 0) {
Console::Pause();
_rewindState = RewindState::Starting;
_videoHistoryBuilder.clear();
_videoHistory.clear();
_audioHistoryBuilder.clear();
_audioHistory.clear();
_historyBackup.clear();
PopHistory();
SoundMixer::StopAudio(true);
EmulationSettings::SetEmulationSpeed(0);
Console::Resume();
}
}
void RewindManager::Stop()
{
if(_rewindState >= RewindState::Starting) {
Console::Pause();
if(_rewindState == RewindState::Started) {
//Move back to the save state containing the frame currently shown on the screen
_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);
} 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();
if(_framesToFastForward > 0) {
_rewindState = RewindState::Stopping;
_currentHistory.FrameCount = 0;
EmulationSettings::SetEmulationSpeed(0);
} else {
_rewindState = RewindState::Stopped;
_historyBackup.clear();
EmulationSettings::SetEmulationSpeed(100);
}
_videoHistoryBuilder.clear();
_videoHistory.clear();
_audioHistoryBuilder.clear();
_audioHistory.clear();
Console::Resume();
}
}
void RewindManager::ProcessEndOfFrame()
{
if(_rewindState >= RewindState::Starting) {
if(_currentHistory.FrameCount <= 0) {
PopHistory();
}
} else if(_currentHistory.FrameCount >= RewindManager::BufferSize) {
AddHistoryBlock();
}
}
void RewindManager::ProcessFrame(void * frameBuffer, uint32_t width, uint32_t height)
{
if(_rewindState >= RewindState::Starting) {
_videoHistoryBuilder.push_back(vector<uint32_t>((uint32_t*)frameBuffer, (uint32_t*)frameBuffer + width*height));
if(_videoHistoryBuilder.size() == _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;
EmulationSettings::SetEmulationSpeed(EmulationSettings::GetRewindSpeed());
if(!_videoHistory.empty()) {
VideoRenderer::GetInstance()->UpdateFrame(_videoHistory.back().data(), width, height);
_videoHistory.pop_back();
}
}
} else if(_rewindState == RewindState::Stopping) {
//Display nothing while resyncing
} else {
VideoRenderer::GetInstance()->UpdateFrame(frameBuffer, width, height);
}
}
bool RewindManager::ProcessAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
{
if(_rewindState >= RewindState::Starting) {
_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) {
//Mute while we resync
return false;
} else {
return true;
}
}
void RewindManager::RecordInput(uint8_t port, uint8_t input)
{
if(_instance && _instance->_rewindState == RewindState::Stopped) {
_instance->_currentHistory.InputLogs[port].push_back(input);
}
}
uint8_t RewindManager::GetInput(uint8_t port)
{
uint8_t value = _instance->_currentHistory.InputLogs[port].front();
_instance->_currentHistory.InputLogs[port].pop_front();
return value;
}
void RewindManager::StartRewinding()
{
if(_instance) {
_instance->Start();
}
}
void RewindManager::StopRewinding()
{
if(_instance) {
_instance->Stop();
}
}
bool RewindManager::IsRewinding()
{
return _instance ? _instance->_rewindState != RewindState::Stopped : false;
}
void RewindManager::RewindSeconds(uint32_t seconds)
{
if(_instance && _instance->_rewindState == RewindState::Stopped) {
uint32_t removeCount = (seconds * 60 / RewindManager::BufferSize) + 1;
Console::Pause();
for(uint32_t i = 0; i < removeCount; i++) {
if(!_instance->_history.empty()) {
_instance->_currentHistory = _instance->_history.back();
_instance->_history.pop_back();
} else {
break;
}
}
_instance->_currentHistory.LoadState();
Console::Resume();
}
}
void RewindManager::SendFrame(void * frameBuffer, uint32_t width, uint32_t height)
{
if(_instance) {
_instance->ProcessFrame(frameBuffer, width, height);
} else {
VideoRenderer::GetInstance()->UpdateFrame(frameBuffer, width, height);
}
}
bool RewindManager::SendAudio(int16_t * soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
{
if(_instance) {
return _instance->ProcessAudio(soundBuffer, sampleCount, sampleRate);
}
return true;
}

58
Core/RewindManager.h Normal file
View File

@ -0,0 +1,58 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "INotificationListener.h"
#include "RewindData.h"
enum class RewindState
{
Stopped = 0,
Stopping = 1,
Starting = 2,
Started = 3
};
class RewindManager : public INotificationListener
{
private:
static const uint32_t BufferSize = 30; //Number of frames between each save state
static RewindManager* _instance;
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();
void Stop();
void ProcessFrame(void *frameBuffer, uint32_t width, uint32_t height);
bool ProcessAudio(int16_t *soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
public:
RewindManager();
~RewindManager();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
void ProcessEndOfFrame();
static void RecordInput(uint8_t port, uint8_t input);
static uint8_t GetInput(uint8_t port);
static void StartRewinding();
static void StopRewinding();
static bool IsRewinding();
static void RewindSeconds(uint32_t seconds);
static void SendFrame(void *frameBuffer, uint32_t width, uint32_t height);
static bool SendAudio(int16_t *soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
};

View File

@ -6,6 +6,7 @@
#include "VsControlManager.h"
#include "FDS.h"
#include "SaveStateManager.h"
#include "RewindManager.h"
ShortcutKeyHandler::ShortcutKeyHandler()
{
@ -48,6 +49,9 @@ bool ShortcutKeyHandler::DetectKeyRelease(uint32_t keyCode)
void ShortcutKeyHandler::CheckMappedKeys(EmulatorKeyMappings mappings)
{
bool isNetplayClient = GameClient::Connected();
bool isMovieActive = MovieManager::Playing() || MovieManager::Recording();
if(DetectKeyPress(mappings.FastForward)) {
EmulationSettings::SetFlags(EmulationFlags::Turbo);
} else if(DetectKeyRelease(mappings.FastForward)) {
@ -66,7 +70,7 @@ void ShortcutKeyHandler::CheckMappedKeys(EmulatorKeyMappings mappings)
VideoDecoder::GetInstance()->TakeScreenshot();
}
if(VsControlManager::GetInstance()) {
if(VsControlManager::GetInstance() && !isNetplayClient && !isMovieActive) {
VsControlManager* manager = VsControlManager::GetInstance();
if(DetectKeyPress(mappings.InsertCoin1)) {
manager->InsertCoin(0);
@ -82,11 +86,11 @@ void ShortcutKeyHandler::CheckMappedKeys(EmulatorKeyMappings mappings)
}
}
if(DetectKeyPress(mappings.SwitchDiskSide)) {
if(DetectKeyPress(mappings.SwitchDiskSide) && !isNetplayClient && !isMovieActive) {
FDS::SwitchDiskSide();
}
if(DetectKeyPress(mappings.InsertNextDisk)) {
if(DetectKeyPress(mappings.InsertNextDisk) && !isNetplayClient && !isMovieActive) {
FDS::InsertNextDisk();
}
@ -102,15 +106,15 @@ void ShortcutKeyHandler::CheckMappedKeys(EmulatorKeyMappings mappings)
SaveStateManager::SaveState();
}
if(DetectKeyPress(mappings.LoadState)) {
if(DetectKeyPress(mappings.LoadState) && !isNetplayClient) {
SaveStateManager::LoadState();
}
if(DetectKeyPress(mappings.Reset)) {
if(DetectKeyPress(mappings.Reset) && !isNetplayClient && !isMovieActive) {
Console::Reset(true);
}
if(DetectKeyPress(mappings.Pause)) {
if(DetectKeyPress(mappings.Pause) && !isNetplayClient) {
if(EmulationSettings::CheckFlag(EmulationFlags::Paused)) {
EmulationSettings::ClearFlags(EmulationFlags::Paused);
} else {
@ -122,7 +126,7 @@ void ShortcutKeyHandler::CheckMappedKeys(EmulatorKeyMappings mappings)
MessageManager::SendNotification(ConsoleNotificationType::RequestExit);
}
if(DetectKeyPress(mappings.ToggleCheats)) {
if(DetectKeyPress(mappings.ToggleCheats) && !isNetplayClient && !isMovieActive) {
MessageManager::SendNotification(ConsoleNotificationType::ToggleCheats);
}
@ -137,6 +141,18 @@ void ShortcutKeyHandler::CheckMappedKeys(EmulatorKeyMappings mappings)
EmulationSettings::SetFlags(EmulationFlags::Paused);
}
}
if(!isNetplayClient && !isMovieActive && !EmulationSettings::CheckFlag(NsfPlayerEnabled)) {
if(DetectKeyPress(mappings.Rewind)) {
RewindManager::StartRewinding();
} else if(DetectKeyRelease(mappings.Rewind)) {
RewindManager::StopRewinding();
} else if(DetectKeyPress(mappings.RewindTenSecs)) {
RewindManager::RewindSeconds(10);
} else if(DetectKeyPress(mappings.RewindOneMin)) {
RewindManager::RewindSeconds(60);
}
}
}
void ShortcutKeyHandler::ProcessKeys(EmulatorKeyMappingSet mappings)

View File

@ -65,7 +65,6 @@ void Snapshotable::Stream(Snapshotable* snapshotable)
void Snapshotable::SaveSnapshot(ostream* file)
{
_stream = new uint8_t[0xFFFFF];
memset((char*)_stream, 0, 0xFFFFF);
_position = 0;
_saving = true;

View File

@ -1,9 +1,10 @@
#include "stdafx.h"
#include "../Utilities/orfanidis_eq.h"
#include "SoundMixer.h"
#include "APU.h"
#include "CPU.h"
#include "VideoDecoder.h"
#include "../Utilities/orfanidis_eq.h"
#include "VideoRenderer.h"
#include "RewindManager.h"
IAudioDevice* SoundMixer::AudioDevice = nullptr;
unique_ptr<WaveRecorder> SoundMixer::_waveRecorder;
@ -102,7 +103,7 @@ void SoundMixer::PlayAudioBuffer(uint32_t time)
}
//Apply low pass filter/volume reduction when in background (based on options)
if(!VideoDecoder::GetInstance()->IsRecording() && !_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) {
if(!VideoRenderer::GetInstance()->IsRecording() && !_waveRecorder && !EmulationSettings::CheckFlag(EmulationFlags::NsfPlayerEnabled) && EmulationSettings::CheckFlag(EmulationFlags::InBackground)) {
if(EmulationSettings::CheckFlag(EmulationFlags::MuteSoundInBackground)) {
_lowPassFilter.ApplyFilter(_outputBuffer, sampleCount, 0, 0);
} else if(EmulationSettings::CheckFlag(EmulationFlags::ReduceSoundInBackground)) {
@ -125,20 +126,22 @@ void SoundMixer::PlayAudioBuffer(uint32_t time)
_crossFeedFilter.ApplyFilter(_outputBuffer, sampleCount, EmulationSettings::GetCrossFeedRatio());
}
if(SoundMixer::AudioDevice && !EmulationSettings::IsPaused()) {
SoundMixer::AudioDevice->PlayBuffer(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true);
}
if(_waveRecorder) {
auto lock = _waveRecorderLock.AcquireSafe();
if(RewindManager::SendAudio(_outputBuffer, (uint32_t)sampleCount, _sampleRate)) {
if(_waveRecorder) {
if(!_waveRecorder->WriteSamples(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true)) {
_waveRecorder.reset();
auto lock = _waveRecorderLock.AcquireSafe();
if(_waveRecorder) {
if(!_waveRecorder->WriteSamples(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true)) {
_waveRecorder.reset();
}
}
}
}
VideoDecoder::GetInstance()->AddRecordingSound(_outputBuffer, (uint32_t)sampleCount, _sampleRate);
VideoRenderer::GetInstance()->AddRecordingSound(_outputBuffer, (uint32_t)sampleCount, _sampleRate);
if(SoundMixer::AudioDevice && !EmulationSettings::IsPaused()) {
SoundMixer::AudioDevice->PlayBuffer(_outputBuffer, (uint32_t)sampleCount, _sampleRate, true);
}
}
if(EmulationSettings::NeedAudioSettingsUpdate()) {
if(EmulationSettings::GetSampleRate() != _sampleRate) {

View File

@ -1,5 +1,4 @@
#include "stdafx.h"
#include "AviRecorder.h"
#include "IRenderingDevice.h"
#include "VideoDecoder.h"
#include "EmulationSettings.h"
@ -9,6 +8,7 @@
#include "HdVideoFilter.h"
#include "ScaleFilter.h"
#include "VideoRenderer.h"
#include "RewindManager.h"
unique_ptr<VideoDecoder> VideoDecoder::Instance;
@ -39,6 +39,11 @@ VideoDecoder::~VideoDecoder()
StopThread();
}
FrameInfo VideoDecoder::GetFrameInfo()
{
return _videoFilter->GetFrameInfo();
}
void VideoDecoder::GetScreenSize(ScreenSize &size, bool ignoreScale)
{
if(_videoFilter) {
@ -94,10 +99,6 @@ void VideoDecoder::UpdateVideoFilter()
case VideoFilterType::HdPack: _videoFilter.reset(new HdVideoFilter()); break;
}
if(_aviRecorder) {
StopRecording();
}
}
}
@ -110,11 +111,6 @@ void VideoDecoder::DecodeFrame()
}
_videoFilter->SendFrame(_ppuOutputBuffer);
shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
if(aviRecorder) {
aviRecorder->AddFrame(_videoFilter->GetOutputBuffer());
}
ScreenSize screenSize;
GetScreenSize(screenSize, true);
if(_previousScale != EmulationSettings::GetVideoScale() || screenSize.Height != _previousScreenSize.Height || screenSize.Width != _previousScreenSize.Width) {
@ -127,7 +123,8 @@ void VideoDecoder::DecodeFrame()
_frameChanged = false;
VideoRenderer::GetInstance()->UpdateFrame(_videoFilter->GetOutputBuffer(), frameInfo.Width, frameInfo.Height);
//Rewind manager will take care of sending the correct frame to the video renderer
RewindManager::SendFrame(_videoFilter->GetOutputBuffer(), frameInfo.Width, frameInfo.Height);
}
void VideoDecoder::DebugDecodeFrame(uint16_t* inputBuffer, uint32_t* outputBuffer, uint32_t length)
@ -158,16 +155,24 @@ uint32_t VideoDecoder::GetFrameCount()
return _frameCount;
}
void VideoDecoder::UpdateFrameSync(void *ppuOutputBuffer, HdPpuPixelInfo *hdPixelInfo)
{
_hdScreenTiles = hdPixelInfo;
_ppuOutputBuffer = (uint16_t*)ppuOutputBuffer;
DecodeFrame();
_frameCount++;
}
void VideoDecoder::UpdateFrame(void *ppuOutputBuffer, HdPpuPixelInfo *hdPixelInfo)
{
if(_frameChanged) {
//Last frame isn't done decoding yet - sometimes Signal() introduces a 25-30ms delay
while(_frameChanged) {
while(_frameChanged) {
//Spin until decode is done
}
//At this point, we are sure that the decode thread is no longer busy
}
_hdScreenTiles = hdPixelInfo;
_ppuOutputBuffer = (uint16_t*)ppuOutputBuffer;
_frameChanged = true;
@ -219,33 +224,3 @@ void VideoDecoder::TakeScreenshot()
_videoFilter->TakeScreenshot();
}
}
void VideoDecoder::StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel)
{
if(_videoFilter) {
shared_ptr<AviRecorder> recorder(new AviRecorder());
FrameInfo frameInfo = _videoFilter->GetFrameInfo();
if(recorder->StartRecording(filename, codec, frameInfo.Width, frameInfo.Height, frameInfo.BitsPerPixel, 60098814, EmulationSettings::GetSampleRate(), compressionLevel)) {
_aviRecorder = recorder;
}
}
}
void VideoDecoder::AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
{
shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
if(aviRecorder) {
aviRecorder->AddSound(soundBuffer, sampleCount, sampleRate);
}
}
void VideoDecoder::StopRecording()
{
_aviRecorder.reset();
}
bool VideoDecoder::IsRecording()
{
return _aviRecorder != nullptr && _aviRecorder->IsRecording();
}

View File

@ -5,12 +5,10 @@ using std::thread;
#include "../Utilities/SimpleLock.h"
#include "../Utilities/AutoResetEvent.h"
#include "../Utilities/AviWriter.h"
#include "EmulationSettings.h"
#include "HdNesPack.h"
#include "FrameInfo.h"
class AviRecorder;
class BaseVideoFilter;
class IRenderingDevice;
@ -30,7 +28,6 @@ private:
HdPpuPixelInfo *_hdScreenTiles = nullptr;
unique_ptr<thread> _decodeThread;
shared_ptr<AviRecorder> _aviRecorder;
AutoResetEvent _waitForFrame;
@ -60,18 +57,15 @@ public:
uint32_t GetFrameCount();
FrameInfo GetFrameInfo();
void GetScreenSize(ScreenSize &size, bool ignoreScale);
void DebugDecodeFrame(uint16_t* inputBuffer, uint32_t* outputBuffer, uint32_t length);
void UpdateFrameSync(void* frameBuffer, HdPpuPixelInfo *screenTiles = nullptr);
void UpdateFrame(void* frameBuffer, HdPpuPixelInfo *screenTiles = nullptr);
bool IsRunning();
void StartThread();
void StopThread();
void StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel);
void AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
void StopRecording();
bool IsRecording();
};

View File

@ -1,6 +1,8 @@
#include "stdafx.h"
#include "IRenderingDevice.h"
#include "VideoRenderer.h"
#include "AviRecorder.h"
#include "VideoDecoder.h"
unique_ptr<VideoRenderer> VideoRenderer::Instance;
@ -60,6 +62,11 @@ void VideoRenderer::RenderThread()
void VideoRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height)
{
shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
if(aviRecorder) {
aviRecorder->AddFrame(frameBuffer, width, height);
}
if(_renderer) {
_renderer->UpdateFrame(frameBuffer, width, height);
_waitForRender.Signal();
@ -78,4 +85,32 @@ void VideoRenderer::UnregisterRenderingDevice(IRenderingDevice *renderer)
StopThread();
_renderer = nullptr;
}
}
void VideoRenderer::StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel)
{
shared_ptr<AviRecorder> recorder(new AviRecorder());
FrameInfo frameInfo = VideoDecoder::GetInstance()->GetFrameInfo();
if(recorder->StartRecording(filename, codec, frameInfo.Width, frameInfo.Height, frameInfo.BitsPerPixel, 60098814, EmulationSettings::GetSampleRate(), compressionLevel)) {
_aviRecorder = recorder;
}
}
void VideoRenderer::AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate)
{
shared_ptr<AviRecorder> aviRecorder = _aviRecorder;
if(aviRecorder) {
aviRecorder->AddSound(soundBuffer, sampleCount, sampleRate);
}
}
void VideoRenderer::StopRecording()
{
_aviRecorder.reset();
}
bool VideoRenderer::IsRecording()
{
return _aviRecorder != nullptr && _aviRecorder->IsRecording();
}

View File

@ -2,8 +2,11 @@
#include "stdafx.h"
#include <thread>
#include "../Utilities/AutoResetEvent.h"
#include "FrameInfo.h"
class IRenderingDevice;
class AviRecorder;
enum class VideoCodec;
class VideoRenderer
{
@ -15,6 +18,9 @@ private:
IRenderingDevice* _renderer = nullptr;
atomic<bool> _stopFlag;
shared_ptr<AviRecorder> _aviRecorder;
FrameInfo type_info;
void RenderThread();
public:
@ -28,4 +34,9 @@ public:
void UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height);
void RegisterRenderingDevice(IRenderingDevice *renderer);
void UnregisterRenderingDevice(IRenderingDevice *renderer);
void StartRecording(string filename, VideoCodec codec, uint32_t compressionLevel);
void AddRecordingSound(int16_t* soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
void StopRecording();
bool IsRecording();
};

View File

@ -37,6 +37,7 @@ namespace Mesen.GUI.Config
[MinMax(0, 500)] public UInt32 EmulationSpeed = 100;
[MinMax(0, 500)] public UInt32 TurboSpeed = 300;
[MinMax(0, 500)] public UInt32 RewindSpeed = 100;
public EmulationInfo()
{
@ -47,7 +48,7 @@ namespace Mesen.GUI.Config
EmulationInfo emulationInfo = ConfigManager.Config.EmulationInfo;
InteropEmu.SetEmulationSpeed(emulationInfo.EmulationSpeed);
InteropEmu.SetTurboSpeed(emulationInfo.TurboSpeed);
InteropEmu.SetTurboRewindSpeed(emulationInfo.TurboSpeed, emulationInfo.RewindSpeed);
InteropEmu.SetFlag(EmulationFlags.Mmc3IrqAltBehavior, emulationInfo.UseAlternativeMmc3Irq);
InteropEmu.SetFlag(EmulationFlags.AllowInvalidInput, emulationInfo.AllowInvalidInput);

View File

@ -51,6 +51,8 @@ namespace Mesen.GUI.Config
public bool DisableGameDatabase = false;
public UInt32 RewindBufferSize = 300;
public PreferenceInfo()
{
}
@ -101,6 +103,8 @@ namespace Mesen.GUI.Config
InteropEmu.NsfSetNsfConfig(preferenceInfo.NsfAutoDetectSilence ? preferenceInfo.NsfAutoDetectSilenceDelay : 0, preferenceInfo.NsfMoveToNextTrackAfterTime ? preferenceInfo.NsfMoveToNextTrackTime : -1, preferenceInfo.NsfDisableApuIrqs);
InteropEmu.SetAutoSaveOptions(preferenceInfo.AutoSave ? (uint)preferenceInfo.AutoSaveDelay : 0, preferenceInfo.AutoSaveNotify);
InteropEmu.SetEmulatorKeys(new EmulatorKeyMappingSet() { KeySet1 = preferenceInfo.EmulatorKeySet1.Value, KeySet2 = preferenceInfo.EmulatorKeySet2.Value });
InteropEmu.SetRewindBufferSize(preferenceInfo.RewindBufferSize);
}
}
}

View File

@ -59,6 +59,9 @@
<Message ID="NoMatchingCheats">The selected cheat file ({0}) contains no cheats that match the selected game.</Message>
<Message ID="EmulatorShortcutMappings_FastForward">Fast Forward</Message>
<Message ID="EmulatorShortcutMappings_Rewind">Rewind</Message>
<Message ID="EmulatorShortcutMappings_RewindTenSecs">Rewind 10 seconds</Message>
<Message ID="EmulatorShortcutMappings_RewindOneMin">Rewind 1 minute</Message>
<Message ID="EmulatorShortcutMappings_IncreaseSpeed">Increase Speed</Message>
<Message ID="EmulatorShortcutMappings_DecreaseSpeed">Decrease Speed</Message>
<Message ID="EmulatorShortcutMappings_Pause">Pause</Message>

View File

@ -286,7 +286,9 @@
<Control ID="lblTurboSpeedHint">% (0 = Velocidad máxima)</Control>
<Control ID="lblTurboSpeed">Velocidad de avance rápido:</Control>
<Control ID="lblEmulationSpeed">Velocidad de emulación:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Velocidad máxima)</Control>
<Control ID="lblRewindSpeed">Rewind Speed:</Control>
<Control ID="tpgAdvanced">Avanzado</Control>
<Control ID="chkUseAlternativeMmc3Irq">Utilizar la versión alternativa de componentes de IRQs de MMC3</Control>
<Control ID="chkAllowInvalidInput">Permitir las entradas inválidas (Arriba+Abajo e Izquierda+Derecha al mismo tiempo)</Control>
@ -358,6 +360,9 @@
<Control ID="grpCloudSaves">Copia de seguridad online</Control>
<Control ID="chkFdsFastForwardOnLoad">Aumentar la velocidad de la emulación de juegos de carga FDS</Control>
<Control ID="lblRewind">Keep rewind data for the last</Control>
<Control ID="lblRewindMinutes">minutes (Memory Usage ≈1MB/min)</Control>
<Control ID="tpgCloudSave">Guardar online</Control>
<Control ID="lblGoogleDriveIntegration">Mesen puede utilizar Google Drive para almacenar las partidas guardadas en su cuenta de Google Drive. Cuando se habilita la integración, todos los datos de copia de seguridad de sus juegos (archivos .sav y partidas guardadas rápidas) son fácilmente accesibles desde cualquier ordenador.</Control>
<Control ID="lblIntegrationOK">La integración con Google Drive está activa.</Control>
@ -578,6 +583,9 @@
<Message ID="NoMatchingCheats">El archivo de trucos seleccionado ({0}) no contiene trucos que coincidan con el juego seleccionado.</Message>
<Message ID="EmulatorShortcutMappings_FastForward">Avance rápido</Message>
<Message ID="EmulatorShortcutMappings_Rewind">Rewind</Message>
<Message ID="EmulatorShortcutMappings_RewindTenSecs">Rewind 10 seconds</Message>
<Message ID="EmulatorShortcutMappings_RewindOneMin">Rewind 1 minute</Message>
<Message ID="EmulatorShortcutMappings_IncreaseSpeed">Aumentar velocidad</Message>
<Message ID="EmulatorShortcutMappings_DecreaseSpeed">Reducir velocidad</Message>
<Message ID="EmulatorShortcutMappings_Pause">Pausa</Message>

View File

@ -286,7 +286,9 @@
<Control ID="lblEmuSpeedHint">% (0 = Vitesse maximale)</Control>
<Control ID="lblEmulationSpeed">Vitesse d'émulation :</Control>
<Control ID="lblTurboSpeedHint">% (0 = Vitesse maximale)</Control>
<Control ID="lblTurboSpeed">Vitesse d'avance rapide:</Control>
<Control ID="lblTurboSpeed">Vitesse d'avance rapide :</Control>
<Control ID="lblRewindSpeedHint">% (0 = Vitesse maximale)</Control>
<Control ID="lblRewindSpeed">Vitesse du rembobinage :</Control>
<Control ID="tpgAdvanced">Avancé</Control>
<Control ID="chkUseAlternativeMmc3Irq">Utiliser la version alternative du comportement des IRQs du MMC3</Control>
@ -350,6 +352,9 @@
<Control ID="chkFdsAutoLoadDisk">Insérer le côté A du disque 1 lors du chargement d'un jeu de FDS</Control>
<Control ID="chkFdsFastForwardOnLoad">Augmenter la vitesse d'émulation pendant le chargement des jeux FDS</Control>
<Control ID="lblRewind">Permettre de rembobiner jusqu'à</Control>
<Control ID="lblRewindMinutes">minutes (Utilise ≈1MB/min)</Control>
<Control ID="tpgShortcuts">Raccourcis</Control>
<Control ID="colAction">Action</Control>
<Control ID="colBinding1">Raccourci #1</Control>
@ -593,6 +598,9 @@
<Message ID="NoMatchingCheats">Le fichier sélectionné ({0}) ne contient aucun code correspondant au jeu sélectionné.</Message>
<Message ID="EmulatorShortcutMappings_FastForward">Avance rapide</Message>
<Message ID="EmulatorShortcutMappings_Rewind">Rembobiner</Message>
<Message ID="EmulatorShortcutMappings_RewindTenSecs">Reculer de 10 secondes</Message>
<Message ID="EmulatorShortcutMappings_RewindOneMin">Reculer d'une minute</Message>
<Message ID="EmulatorShortcutMappings_IncreaseSpeed">Augmenter la vitesse</Message>
<Message ID="EmulatorShortcutMappings_DecreaseSpeed">Réduire la vitesse</Message>
<Message ID="EmulatorShortcutMappings_Pause">Pause</Message>

View File

@ -286,7 +286,9 @@
<Control ID="lblEmuSpeedHint">% (0 = 最高速度)</Control>
<Control ID="lblEmulationSpeed">エミュレーションの速度:</Control>
<Control ID="lblTurboSpeedHint">% (0 = 最高速度)</Control>
<Control ID="lblTurboSpeed">早送りの速度:</Control>
<Control ID="lblTurboSpeed">早送りの速度:</Control>
<Control ID="lblRewindSpeedHint">% (0 = 最高速度)</Control>
<Control ID="lblRewindSpeed">巻き戻しの速度:</Control>
<Control ID="tpgAdvanced">詳細設定</Control>
<Control ID="chkUseAlternativeMmc3Irq">MMC3AのIRQ仕様を使う</Control>
@ -349,6 +351,9 @@
<Control ID="chkFdsAutoLoadDisk">ファミコンディスクシステムのゲームをロードする時に自動的にディスクのA面を入れる</Control>
<Control ID="chkFdsFastForwardOnLoad">ファミコンディスクシステムのゲームをディスクからロードする時に自動的に最高速度にする</Control>
<Control ID="lblRewind">巻き戻し用データの</Control>
<Control ID="lblRewindMinutes">分をキープする (メモリの使用量分に約MB)</Control>
<Control ID="tpgShortcuts"> ショートカットキー </Control>
<Control ID="colAction">機能</Control>
<Control ID="colBinding1">ショートカットキー1</Control>
@ -575,6 +580,9 @@
<Message ID="NoMatchingCheats">このファイル({0})に選択されたゲームに該当するチートコードを見つかりませんでした。</Message>
<Message ID="EmulatorShortcutMappings_FastForward">早送り</Message>
<Message ID="EmulatorShortcutMappings_Rewind">巻き戻す</Message>
<Message ID="EmulatorShortcutMappings_RewindTenSecs">10秒前に戻る</Message>
<Message ID="EmulatorShortcutMappings_RewindOneMin">1分前に戻る</Message>
<Message ID="EmulatorShortcutMappings_IncreaseSpeed">速度を上げる</Message>
<Message ID="EmulatorShortcutMappings_DecreaseSpeed">速度を下げる</Message>
<Message ID="EmulatorShortcutMappings_Pause">ポーズ</Message>

View File

@ -286,7 +286,9 @@
<Control ID="lblTurboSpeedHint">% (0 = Velocidade máxima)</Control>
<Control ID="lblTurboSpeed">Velocidade de avanço rápido:</Control>
<Control ID="lblEmulationSpeed">Velocidade de emulação:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Velocidade máxima)</Control>
<Control ID="lblRewindSpeed">Rewind Speed:</Control>
<Control ID="tpgAdvanced">Avançado</Control>
<Control ID="chkUseAlternativeMmc3Irq">Utilizar a versão alternativa de componentes de IRQs de MMC3</Control>
<Control ID="chkAllowInvalidInput">Permitir as entradas inválidas (Cima+Baixo e Esquerda+Direita ao mesmo tempo)</Control>
@ -357,6 +359,9 @@
<Control ID="chkAutoSaveNotify">Mostrar uma notificação na tela ao fazer a cópia de segurança automática</Control>
<Control ID="grpCloudSaves">Cópia de segurança online</Control>
<Control ID="chkFdsFastForwardOnLoad">Aumentar a velocidade da emulação de jogos ao carregar no FDS</Control>
<Control ID="lblRewind">Keep rewind data for the last</Control>
<Control ID="lblRewindMinutes">minutes (Memory Usage ≈1MB/min)</Control>
<Control ID="tpgCloudSave">Salvar online</Control>
<Control ID="lblGoogleDriveIntegration">Mesen pode utilizar Google Drive para armazenar saves em sua conta do Google Drive. Quando se habilita a integração, todos os dados de cópia de segurança de seus jogos (arquivos .sav e save states) são facilmente acessíveis de qualquer computador.</Control>
@ -576,7 +581,11 @@
<Message ID="InvalidCheatFile">O arquivo selecionado ({0}) não é um arquivo de cheats válido.</Message>
<Message ID="InvalidXmlFile">O arquivo selecionado ({0}) não é um arquivo XML válido.</Message>
<Message ID="NoMatchingCheats">O arquivo de cheats selecionado ({0}) não contém cheats que pertencem ao jogo selecionado.</Message>
<Message ID="EmulatorShortcutMappings_FastForward">Fast Forward</Message>
<Message ID="EmulatorShortcutMappings_Rewind">Rewind</Message>
<Message ID="EmulatorShortcutMappings_RewindTenSecs">Rewind 10 seconds</Message>
<Message ID="EmulatorShortcutMappings_RewindOneMin">Rewind 1 minute</Message>
<Message ID="EmulatorShortcutMappings_IncreaseSpeed">Aumentar velocidade</Message>
<Message ID="EmulatorShortcutMappings_DecreaseSpeed">Reduzir velocidade</Message>
<Message ID="EmulatorShortcutMappings_Pause">Pausar</Message>

View File

@ -287,6 +287,8 @@
<Control ID="lblEmulationSpeed">Скорость эмуляции :</Control>
<Control ID="lblTurboSpeedHint">% (0 = Максимальная скорость)</Control>
<Control ID="lblTurboSpeed">Перемотка:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Максимальная скорость)</Control>
<Control ID="lblRewindSpeed">Rewind Speed:</Control>
<Control ID="tpgAdvanced">Расширенные</Control>
<Control ID="chkUseAlternativeMmc3Irq">Использовать альтернативный IRQ MMC3</Control>
@ -349,6 +351,9 @@
<Control ID="chkFdsAutoLoadDisk">Автоматически вставлять диск 1 стороной А при загрузке FDS</Control>
<Control ID="chkFdsFastForwardOnLoad">Использовать быструю загрузку FDS</Control>
<Control ID="lblRewind">Keep rewind data for the last</Control>
<Control ID="lblRewindMinutes">minutes (Memory Usage ≈1MB/min)</Control>
<Control ID="tpgShortcuts">Горячие клавиши</Control>
<Control ID="colAction">Действие</Control>
<Control ID="colBinding1">Вариант #1</Control>
@ -583,6 +588,9 @@
<Message ID="NoMatchingCheats">Выбранный Cheat файл ({0}) не содержит читов для данной игры.</Message>
<Message ID="EmulatorShortcutMappings_FastForward">Перемотка</Message>
<Message ID="EmulatorShortcutMappings_Rewind">Rewind</Message>
<Message ID="EmulatorShortcutMappings_RewindTenSecs">Rewind 10 seconds</Message>
<Message ID="EmulatorShortcutMappings_RewindOneMin">Rewind 1 minute</Message>
<Message ID="EmulatorShortcutMappings_IncreaseSpeed">Increase Speed</Message>
<Message ID="EmulatorShortcutMappings_DecreaseSpeed">Decrease Speed</Message>
<Message ID="EmulatorShortcutMappings_Pause">Пауза</Message>

View File

@ -287,6 +287,8 @@
<Control ID="lblEmulationSpeed">Швидкість емуляції :</Control>
<Control ID="lblTurboSpeedHint">% (0 = Максимальна швидкiсть)</Control>
<Control ID="lblTurboSpeed">Перемотка:</Control>
<Control ID="lblRewindSpeedHint">% (0 = Максимальна швидкiсть)</Control>
<Control ID="lblRewindSpeed">Rewind Speed:</Control>
<Control ID="tpgAdvanced">Розширені</Control>
<Control ID="chkUseAlternativeMmc3Irq">Використовувати альтернативний IRQ MMC3</Control>
@ -349,6 +351,9 @@
<Control ID="chkFdsAutoLoadDisk">Автоматично вставляти диск 1 стороною А при завантаженні FDS</Control>
<Control ID="chkFdsFastForwardOnLoad">Використовувати швидке завантаження FDS</Control>
<Control ID="lblRewind">Keep rewind data for the last</Control>
<Control ID="lblRewindMinutes">minutes (Memory Usage ≈1MB/min)</Control>
<Control ID="tpgShortcuts">Гарячі клавіші</Control>
<Control ID="colAction">Дія</Control>
<Control ID="colBinding1">Варiант #1</Control>
@ -582,6 +587,9 @@
<Message ID="NoMatchingCheats">Обраний Cheat файл ({0}) не містить читiв для даної гри.</Message>
<Message ID="EmulatorShortcutMappings_FastForward">Перемотка</Message>
<Message ID="EmulatorShortcutMappings_Rewind">Rewind</Message>
<Message ID="EmulatorShortcutMappings_RewindTenSecs">Rewind 10 seconds</Message>
<Message ID="EmulatorShortcutMappings_RewindOneMin">Rewind 1 minute</Message>
<Message ID="EmulatorShortcutMappings_IncreaseSpeed">Збільшити Швидкість</Message>
<Message ID="EmulatorShortcutMappings_DecreaseSpeed">Зменшити Швидкість</Message>
<Message ID="EmulatorShortcutMappings_Pause">Пауза</Message>

View File

@ -84,6 +84,10 @@ namespace Mesen.GUI.Forms.Config
this.chkShowLagCounter = new System.Windows.Forms.CheckBox();
this.btnResetLagCounter = new System.Windows.Forms.Button();
this.tmrUpdateClockRate = new System.Windows.Forms.Timer(this.components);
this.lblRewindSpeed = new System.Windows.Forms.Label();
this.flowLayoutPanel10 = new System.Windows.Forms.FlowLayoutPanel();
this.nudRewindSpeed = new System.Windows.Forms.NumericUpDown();
this.lblRewindSpeedHint = new System.Windows.Forms.Label();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
@ -108,6 +112,8 @@ namespace Mesen.GUI.Forms.Config
((System.ComponentModel.ISupportInitialize)(this.nudExtraScanlinesBeforeNmi)).BeginInit();
this.flowLayoutPanel2.SuspendLayout();
this.flowLayoutPanel7.SuspendLayout();
this.flowLayoutPanel10.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.nudRewindSpeed)).BeginInit();
this.SuspendLayout();
//
// baseConfigPanel
@ -133,7 +139,7 @@ namespace Mesen.GUI.Forms.Config
this.tpgGeneral.Location = new System.Drawing.Point(4, 22);
this.tpgGeneral.Name = "tpgGeneral";
this.tpgGeneral.Padding = new System.Windows.Forms.Padding(3);
this.tpgGeneral.Size = new System.Drawing.Size(525, 273);
this.tpgGeneral.Size = new System.Drawing.Size(525, 302);
this.tpgGeneral.TabIndex = 0;
this.tpgGeneral.Text = "General";
this.tpgGeneral.UseVisualStyleBackColor = true;
@ -147,14 +153,17 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel4.Controls.Add(this.lblTurboSpeed, 0, 1);
this.tableLayoutPanel4.Controls.Add(this.flowLayoutPanel6, 1, 0);
this.tableLayoutPanel4.Controls.Add(this.lblEmulationSpeed, 0, 0);
this.tableLayoutPanel4.Controls.Add(this.lblRewindSpeed, 0, 2);
this.tableLayoutPanel4.Controls.Add(this.flowLayoutPanel10, 1, 2);
this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel4.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
this.tableLayoutPanel4.RowCount = 3;
this.tableLayoutPanel4.RowCount = 4;
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel4.Size = new System.Drawing.Size(519, 267);
this.tableLayoutPanel4.Size = new System.Drawing.Size(519, 296);
this.tableLayoutPanel4.TabIndex = 0;
//
// flowLayoutPanel9
@ -291,9 +300,10 @@ namespace Mesen.GUI.Forms.Config
// chkEnableOamDecay
//
this.chkEnableOamDecay.AutoSize = true;
this.chkEnableOamDecay.Location = new System.Drawing.Point(3, 72);
this.chkEnableOamDecay.Checked = false;
this.chkEnableOamDecay.Location = new System.Drawing.Point(0, 69);
this.chkEnableOamDecay.Name = "chkEnableOamDecay";
this.chkEnableOamDecay.Size = new System.Drawing.Size(150, 17);
this.chkEnableOamDecay.Size = new System.Drawing.Size(243, 23);
this.chkEnableOamDecay.TabIndex = 9;
this.chkEnableOamDecay.Text = "Enable OAM RAM decay";
//
@ -414,7 +424,7 @@ namespace Mesen.GUI.Forms.Config
this.tpgOverclocking.Location = new System.Drawing.Point(4, 22);
this.tpgOverclocking.Name = "tpgOverclocking";
this.tpgOverclocking.Padding = new System.Windows.Forms.Padding(3);
this.tpgOverclocking.Size = new System.Drawing.Size(525, 273);
this.tpgOverclocking.Size = new System.Drawing.Size(525, 302);
this.tpgOverclocking.TabIndex = 2;
this.tpgOverclocking.Text = "Overclocking";
this.tpgOverclocking.UseVisualStyleBackColor = true;
@ -443,7 +453,7 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.Size = new System.Drawing.Size(519, 267);
this.tableLayoutPanel3.Size = new System.Drawing.Size(519, 296);
this.tableLayoutPanel3.TabIndex = 0;
//
// flowLayoutPanel4
@ -739,7 +749,7 @@ namespace Mesen.GUI.Forms.Config
this.flowLayoutPanel7.Location = new System.Drawing.Point(0, 232);
this.flowLayoutPanel7.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel7.Name = "flowLayoutPanel7";
this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 35);
this.flowLayoutPanel7.Size = new System.Drawing.Size(519, 64);
this.flowLayoutPanel7.TabIndex = 12;
//
// chkShowLagCounter
@ -769,6 +779,50 @@ namespace Mesen.GUI.Forms.Config
this.tmrUpdateClockRate.Enabled = true;
this.tmrUpdateClockRate.Tick += new System.EventHandler(this.tmrUpdateClockRate_Tick);
//
// lblRewindSpeed
//
this.lblRewindSpeed.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblRewindSpeed.AutoSize = true;
this.lblRewindSpeed.Location = new System.Drawing.Point(3, 58);
this.lblRewindSpeed.Name = "lblRewindSpeed";
this.lblRewindSpeed.Size = new System.Drawing.Size(80, 13);
this.lblRewindSpeed.TabIndex = 15;
this.lblRewindSpeed.Text = "Rewind Speed:";
//
// flowLayoutPanel10
//
this.flowLayoutPanel10.AutoSize = true;
this.flowLayoutPanel10.Controls.Add(this.nudRewindSpeed);
this.flowLayoutPanel10.Controls.Add(this.lblRewindSpeedHint);
this.flowLayoutPanel10.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel10.Location = new System.Drawing.Point(111, 52);
this.flowLayoutPanel10.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel10.Name = "flowLayoutPanel10";
this.flowLayoutPanel10.Size = new System.Drawing.Size(408, 26);
this.flowLayoutPanel10.TabIndex = 16;
//
// nudRewindSpeed
//
this.nudRewindSpeed.Location = new System.Drawing.Point(3, 3);
this.nudRewindSpeed.Maximum = new decimal(new int[] {
500,
0,
0,
0});
this.nudRewindSpeed.Name = "nudRewindSpeed";
this.nudRewindSpeed.Size = new System.Drawing.Size(48, 20);
this.nudRewindSpeed.TabIndex = 1;
//
// lblRewindSpeedHint
//
this.lblRewindSpeedHint.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblRewindSpeedHint.AutoSize = true;
this.lblRewindSpeedHint.Location = new System.Drawing.Point(57, 6);
this.lblRewindSpeedHint.Name = "lblRewindSpeedHint";
this.lblRewindSpeedHint.Size = new System.Drawing.Size(121, 13);
this.lblRewindSpeedHint.TabIndex = 2;
this.lblRewindSpeedHint.Text = "% (0 = Maximum speed)";
//
// frmEmulationConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -820,6 +874,9 @@ namespace Mesen.GUI.Forms.Config
this.flowLayoutPanel2.PerformLayout();
this.flowLayoutPanel7.ResumeLayout(false);
this.flowLayoutPanel7.PerformLayout();
this.flowLayoutPanel10.ResumeLayout(false);
this.flowLayoutPanel10.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.nudRewindSpeed)).EndInit();
this.ResumeLayout(false);
}
@ -879,5 +936,9 @@ namespace Mesen.GUI.Forms.Config
private ctrlRiskyOption chkDisablePpuReset;
private System.Windows.Forms.CheckBox chkUseNes101Hvc101Behavior;
private Mesen.GUI.Controls.ctrlRiskyOption chkEnableOamDecay;
private System.Windows.Forms.Label lblRewindSpeed;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel10;
private System.Windows.Forms.NumericUpDown nudRewindSpeed;
private System.Windows.Forms.Label lblRewindSpeedHint;
}
}

View File

@ -23,6 +23,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding("EmulationSpeed", nudEmulationSpeed);
AddBinding("TurboSpeed", nudTurboSpeed);
AddBinding("RewindSpeed", nudRewindSpeed);
AddBinding("UseAlternativeMmc3Irq", chkUseAlternativeMmc3Irq);
AddBinding("AllowInvalidInput", chkAllowInvalidInput);

View File

@ -32,18 +32,21 @@ namespace Mesen.GUI.Forms.Config
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(frmPreferences));
this.tlpMain = new System.Windows.Forms.TableLayoutPanel();
this.chkHidePauseOverlay = new System.Windows.Forms.CheckBox();
this.lblPauseBackgroundSettings = new System.Windows.Forms.Label();
this.chkSingleInstance = new System.Windows.Forms.CheckBox();
this.chkAutomaticallyCheckForUpdates = new System.Windows.Forms.CheckBox();
this.chkPauseOnMovieEnd = new System.Windows.Forms.CheckBox();
this.chkAllowBackgroundInput = new System.Windows.Forms.CheckBox();
this.chkPauseWhenInBackground = new System.Windows.Forms.CheckBox();
this.chkAutoLoadIps = new System.Windows.Forms.CheckBox();
this.btnOpenMesenFolder = new System.Windows.Forms.Button();
this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel();
this.lblDisplayLanguage = new System.Windows.Forms.Label();
this.cboDisplayLanguage = new System.Windows.Forms.ComboBox();
this.lblMiscSettings = new System.Windows.Forms.Label();
this.chkAutoLoadIps = new System.Windows.Forms.CheckBox();
this.chkDisplayMovieIcons = new System.Windows.Forms.CheckBox();
this.chkAutoHideMenu = new System.Windows.Forms.CheckBox();
this.chkHidePauseOverlay = new System.Windows.Forms.CheckBox();
this.chkAllowBackgroundInput = new System.Windows.Forms.CheckBox();
this.chkPauseWhenInBackground = new System.Windows.Forms.CheckBox();
this.chkPauseOnMovieEnd = new System.Windows.Forms.CheckBox();
this.tabMain = new System.Windows.Forms.TabControl();
this.tpgGeneral = new System.Windows.Forms.TabPage();
this.tpgShortcuts = new System.Windows.Forms.TabPage();
@ -97,11 +100,12 @@ namespace Mesen.GUI.Forms.Config
this.chkDisableGameDatabase = new Mesen.GUI.Controls.ctrlRiskyOption();
this.chkFdsAutoLoadDisk = new System.Windows.Forms.CheckBox();
this.chkFdsFastForwardOnLoad = new System.Windows.Forms.CheckBox();
this.tmrSyncDateTime = new System.Windows.Forms.Timer(this.components);
this.chkDisplayTitleBarInfo = new System.Windows.Forms.CheckBox();
this.chkAutoHideMenu = new System.Windows.Forms.CheckBox();
this.lblMiscSettings = new System.Windows.Forms.Label();
this.lblPauseBackgroundSettings = new System.Windows.Forms.Label();
this.tmrSyncDateTime = new System.Windows.Forms.Timer(this.components);
this.flowLayoutPanel6 = new System.Windows.Forms.FlowLayoutPanel();
this.nudRewindBufferSize = new System.Windows.Forms.NumericUpDown();
this.lblRewindMinutes = new System.Windows.Forms.Label();
this.lblRewind = new System.Windows.Forms.Label();
this.tlpMain.SuspendLayout();
this.flowLayoutPanel2.SuspendLayout();
this.tabMain.SuspendLayout();
@ -131,6 +135,8 @@ namespace Mesen.GUI.Forms.Config
this.tlpFileFormat.SuspendLayout();
this.tpgAdvanced.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.flowLayoutPanel6.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.nudRewindBufferSize)).BeginInit();
this.SuspendLayout();
//
// baseConfigPanel
@ -176,16 +182,17 @@ namespace Mesen.GUI.Forms.Config
this.tlpMain.Size = new System.Drawing.Size(473, 337);
this.tlpMain.TabIndex = 1;
//
// chkHidePauseOverlay
// lblPauseBackgroundSettings
//
this.chkHidePauseOverlay.AutoSize = true;
this.chkHidePauseOverlay.Location = new System.Drawing.Point(13, 95);
this.chkHidePauseOverlay.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkHidePauseOverlay.Name = "chkHidePauseOverlay";
this.chkHidePauseOverlay.Size = new System.Drawing.Size(133, 17);
this.chkHidePauseOverlay.TabIndex = 20;
this.chkHidePauseOverlay.Text = "Hide the pause screen";
this.chkHidePauseOverlay.UseVisualStyleBackColor = true;
this.lblPauseBackgroundSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblPauseBackgroundSettings.AutoSize = true;
this.lblPauseBackgroundSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblPauseBackgroundSettings.Location = new System.Drawing.Point(0, 79);
this.lblPauseBackgroundSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblPauseBackgroundSettings.Name = "lblPauseBackgroundSettings";
this.lblPauseBackgroundSettings.Size = new System.Drawing.Size(141, 13);
this.lblPauseBackgroundSettings.TabIndex = 23;
this.lblPauseBackgroundSettings.Text = "Pause/Background Settings";
//
// chkSingleInstance
//
@ -207,51 +214,6 @@ namespace Mesen.GUI.Forms.Config
this.chkAutomaticallyCheckForUpdates.Text = "Automatically check for updates";
this.chkAutomaticallyCheckForUpdates.UseVisualStyleBackColor = true;
//
// chkPauseOnMovieEnd
//
this.chkPauseOnMovieEnd.AutoSize = true;
this.chkPauseOnMovieEnd.Location = new System.Drawing.Point(13, 118);
this.chkPauseOnMovieEnd.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkPauseOnMovieEnd.Name = "chkPauseOnMovieEnd";
this.chkPauseOnMovieEnd.Size = new System.Drawing.Size(199, 17);
this.chkPauseOnMovieEnd.TabIndex = 15;
this.chkPauseOnMovieEnd.Text = "Pause when a movie finishes playing";
this.chkPauseOnMovieEnd.UseVisualStyleBackColor = true;
//
// chkAllowBackgroundInput
//
this.chkAllowBackgroundInput.AutoSize = true;
this.chkAllowBackgroundInput.Location = new System.Drawing.Point(13, 164);
this.chkAllowBackgroundInput.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAllowBackgroundInput.Name = "chkAllowBackgroundInput";
this.chkAllowBackgroundInput.Size = new System.Drawing.Size(177, 17);
this.chkAllowBackgroundInput.TabIndex = 14;
this.chkAllowBackgroundInput.Text = "Allow input when in background";
this.chkAllowBackgroundInput.UseVisualStyleBackColor = true;
//
// chkPauseWhenInBackground
//
this.chkPauseWhenInBackground.AutoSize = true;
this.chkPauseWhenInBackground.Location = new System.Drawing.Point(13, 141);
this.chkPauseWhenInBackground.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkPauseWhenInBackground.Name = "chkPauseWhenInBackground";
this.chkPauseWhenInBackground.Size = new System.Drawing.Size(204, 17);
this.chkPauseWhenInBackground.TabIndex = 13;
this.chkPauseWhenInBackground.Text = "Pause emulation when in background";
this.chkPauseWhenInBackground.UseVisualStyleBackColor = true;
this.chkPauseWhenInBackground.CheckedChanged += new System.EventHandler(this.chkPauseWhenInBackground_CheckedChanged);
//
// chkAutoLoadIps
//
this.chkAutoLoadIps.AutoSize = true;
this.chkAutoLoadIps.Location = new System.Drawing.Point(13, 207);
this.chkAutoLoadIps.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAutoLoadIps.Name = "chkAutoLoadIps";
this.chkAutoLoadIps.Size = new System.Drawing.Size(198, 17);
this.chkAutoLoadIps.TabIndex = 9;
this.chkAutoLoadIps.Text = "Automatically load IPS/BPS patches";
this.chkAutoLoadIps.UseVisualStyleBackColor = true;
//
// btnOpenMesenFolder
//
this.btnOpenMesenFolder.AutoSize = true;
@ -292,6 +254,30 @@ namespace Mesen.GUI.Forms.Config
this.cboDisplayLanguage.Size = new System.Drawing.Size(206, 21);
this.cboDisplayLanguage.TabIndex = 1;
//
// lblMiscSettings
//
this.lblMiscSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblMiscSettings.AutoSize = true;
this.lblMiscSettings.Enabled = false;
this.lblMiscSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblMiscSettings.Location = new System.Drawing.Point(0, 191);
this.lblMiscSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblMiscSettings.Name = "lblMiscSettings";
this.lblMiscSettings.Size = new System.Drawing.Size(73, 13);
this.lblMiscSettings.TabIndex = 22;
this.lblMiscSettings.Text = "Misc. Settings";
//
// chkAutoLoadIps
//
this.chkAutoLoadIps.AutoSize = true;
this.chkAutoLoadIps.Location = new System.Drawing.Point(13, 207);
this.chkAutoLoadIps.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAutoLoadIps.Name = "chkAutoLoadIps";
this.chkAutoLoadIps.Size = new System.Drawing.Size(198, 17);
this.chkAutoLoadIps.TabIndex = 9;
this.chkAutoLoadIps.Text = "Automatically load IPS/BPS patches";
this.chkAutoLoadIps.UseVisualStyleBackColor = true;
//
// chkDisplayMovieIcons
//
this.chkDisplayMovieIcons.AutoSize = true;
@ -303,6 +289,62 @@ namespace Mesen.GUI.Forms.Config
this.chkDisplayMovieIcons.Text = "Display play/record icon when playing or recording a movie";
this.chkDisplayMovieIcons.UseVisualStyleBackColor = true;
//
// chkAutoHideMenu
//
this.chkAutoHideMenu.AutoSize = true;
this.chkAutoHideMenu.Location = new System.Drawing.Point(13, 230);
this.chkAutoHideMenu.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAutoHideMenu.Name = "chkAutoHideMenu";
this.chkAutoHideMenu.Size = new System.Drawing.Size(158, 17);
this.chkAutoHideMenu.TabIndex = 21;
this.chkAutoHideMenu.Text = "Automatically hide menu bar";
this.chkAutoHideMenu.UseVisualStyleBackColor = true;
//
// chkHidePauseOverlay
//
this.chkHidePauseOverlay.AutoSize = true;
this.chkHidePauseOverlay.Location = new System.Drawing.Point(13, 95);
this.chkHidePauseOverlay.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkHidePauseOverlay.Name = "chkHidePauseOverlay";
this.chkHidePauseOverlay.Size = new System.Drawing.Size(133, 17);
this.chkHidePauseOverlay.TabIndex = 20;
this.chkHidePauseOverlay.Text = "Hide the pause screen";
this.chkHidePauseOverlay.UseVisualStyleBackColor = true;
//
// chkAllowBackgroundInput
//
this.chkAllowBackgroundInput.AutoSize = true;
this.chkAllowBackgroundInput.Location = new System.Drawing.Point(13, 164);
this.chkAllowBackgroundInput.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAllowBackgroundInput.Name = "chkAllowBackgroundInput";
this.chkAllowBackgroundInput.Size = new System.Drawing.Size(177, 17);
this.chkAllowBackgroundInput.TabIndex = 14;
this.chkAllowBackgroundInput.Text = "Allow input when in background";
this.chkAllowBackgroundInput.UseVisualStyleBackColor = true;
//
// chkPauseWhenInBackground
//
this.chkPauseWhenInBackground.AutoSize = true;
this.chkPauseWhenInBackground.Location = new System.Drawing.Point(13, 141);
this.chkPauseWhenInBackground.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkPauseWhenInBackground.Name = "chkPauseWhenInBackground";
this.chkPauseWhenInBackground.Size = new System.Drawing.Size(204, 17);
this.chkPauseWhenInBackground.TabIndex = 13;
this.chkPauseWhenInBackground.Text = "Pause emulation when in background";
this.chkPauseWhenInBackground.UseVisualStyleBackColor = true;
this.chkPauseWhenInBackground.CheckedChanged += new System.EventHandler(this.chkPauseWhenInBackground_CheckedChanged);
//
// chkPauseOnMovieEnd
//
this.chkPauseOnMovieEnd.AutoSize = true;
this.chkPauseOnMovieEnd.Location = new System.Drawing.Point(13, 118);
this.chkPauseOnMovieEnd.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkPauseOnMovieEnd.Name = "chkPauseOnMovieEnd";
this.chkPauseOnMovieEnd.Size = new System.Drawing.Size(199, 17);
this.chkPauseOnMovieEnd.TabIndex = 15;
this.chkPauseOnMovieEnd.Text = "Pause when a movie finishes playing";
this.chkPauseOnMovieEnd.UseVisualStyleBackColor = true;
//
// tabMain
//
this.tabMain.Controls.Add(this.tpgGeneral);
@ -916,16 +958,17 @@ namespace Mesen.GUI.Forms.Config
this.tableLayoutPanel1.Controls.Add(this.chkFdsAutoLoadDisk, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.chkFdsFastForwardOnLoad, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.chkDisplayTitleBarInfo, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel6, 0, 4);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 5;
this.tableLayoutPanel1.RowCount = 6;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(473, 337);
this.tableLayoutPanel1.TabIndex = 0;
//
@ -959,11 +1002,6 @@ namespace Mesen.GUI.Forms.Config
this.chkFdsFastForwardOnLoad.Text = "Automatically fast forward FDS games when disk or BIOS is loading";
this.chkFdsFastForwardOnLoad.UseVisualStyleBackColor = true;
//
// tmrSyncDateTime
//
this.tmrSyncDateTime.Enabled = true;
this.tmrSyncDateTime.Tick += new System.EventHandler(this.tmrSyncDateTime_Tick);
//
// chkDisplayTitleBarInfo
//
this.chkDisplayTitleBarInfo.AutoSize = true;
@ -974,41 +1012,59 @@ namespace Mesen.GUI.Forms.Config
this.chkDisplayTitleBarInfo.Text = "Display additional information in title bar";
this.chkDisplayTitleBarInfo.UseVisualStyleBackColor = true;
//
// chkAutoHideMenu
// tmrSyncDateTime
//
this.chkAutoHideMenu.AutoSize = true;
this.chkAutoHideMenu.Location = new System.Drawing.Point(13, 230);
this.chkAutoHideMenu.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAutoHideMenu.Name = "chkAutoHideMenu";
this.chkAutoHideMenu.Size = new System.Drawing.Size(158, 17);
this.chkAutoHideMenu.TabIndex = 21;
this.chkAutoHideMenu.Text = "Automatically hide menu bar";
this.chkAutoHideMenu.UseVisualStyleBackColor = true;
this.tmrSyncDateTime.Enabled = true;
this.tmrSyncDateTime.Tick += new System.EventHandler(this.tmrSyncDateTime_Tick);
//
// lblMiscSettings
// flowLayoutPanel6
//
this.lblMiscSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblMiscSettings.AutoSize = true;
this.lblMiscSettings.Enabled = false;
this.lblMiscSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblMiscSettings.Location = new System.Drawing.Point(0, 191);
this.lblMiscSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblMiscSettings.Name = "lblMiscSettings";
this.lblMiscSettings.Size = new System.Drawing.Size(73, 13);
this.lblMiscSettings.TabIndex = 22;
this.lblMiscSettings.Text = "Misc. Settings";
this.flowLayoutPanel6.Controls.Add(this.lblRewind);
this.flowLayoutPanel6.Controls.Add(this.nudRewindBufferSize);
this.flowLayoutPanel6.Controls.Add(this.lblRewindMinutes);
this.flowLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel6.Location = new System.Drawing.Point(0, 92);
this.flowLayoutPanel6.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel6.Name = "flowLayoutPanel6";
this.flowLayoutPanel6.Size = new System.Drawing.Size(473, 23);
this.flowLayoutPanel6.TabIndex = 9;
//
// lblPauseBackgroundSettings
// nudRewindBufferSize
//
this.lblPauseBackgroundSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblPauseBackgroundSettings.AutoSize = true;
this.lblPauseBackgroundSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblPauseBackgroundSettings.Location = new System.Drawing.Point(0, 79);
this.lblPauseBackgroundSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblPauseBackgroundSettings.Name = "lblPauseBackgroundSettings";
this.lblPauseBackgroundSettings.Size = new System.Drawing.Size(141, 13);
this.lblPauseBackgroundSettings.TabIndex = 23;
this.lblPauseBackgroundSettings.Text = "Pause/Background Settings";
this.nudRewindBufferSize.Location = new System.Drawing.Point(151, 3);
this.nudRewindBufferSize.Maximum = new decimal(new int[] {
900,
0,
0,
0});
this.nudRewindBufferSize.Name = "nudRewindBufferSize";
this.nudRewindBufferSize.Size = new System.Drawing.Size(42, 20);
this.nudRewindBufferSize.TabIndex = 1;
this.nudRewindBufferSize.Value = new decimal(new int[] {
300,
0,
0,
0});
//
// lblRewindMinutes
//
this.lblRewindMinutes.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblRewindMinutes.AutoSize = true;
this.lblRewindMinutes.Location = new System.Drawing.Point(199, 6);
this.lblRewindMinutes.Name = "lblRewindMinutes";
this.lblRewindMinutes.Size = new System.Drawing.Size(173, 13);
this.lblRewindMinutes.TabIndex = 2;
this.lblRewindMinutes.Text = "minutes (Memory Usage ≈1MB/min)";
//
// lblRewind
//
this.lblRewind.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblRewind.AutoSize = true;
this.lblRewind.Location = new System.Drawing.Point(3, 6);
this.lblRewind.Name = "lblRewind";
this.lblRewind.Size = new System.Drawing.Size(142, 13);
this.lblRewind.TabIndex = 3;
this.lblRewind.Text = "Keep rewind data for the last";
//
// frmPreferences
//
@ -1066,6 +1122,9 @@ namespace Mesen.GUI.Forms.Config
this.tpgAdvanced.ResumeLayout(false);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.flowLayoutPanel6.ResumeLayout(false);
this.flowLayoutPanel6.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.nudRewindBufferSize)).EndInit();
this.ResumeLayout(false);
}
@ -1143,5 +1202,9 @@ namespace Mesen.GUI.Forms.Config
private System.Windows.Forms.CheckBox chkDisplayTitleBarInfo;
private System.Windows.Forms.Label lblPauseBackgroundSettings;
private System.Windows.Forms.Label lblMiscSettings;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel6;
private System.Windows.Forms.Label lblRewind;
private System.Windows.Forms.NumericUpDown nudRewindBufferSize;
private System.Windows.Forms.Label lblRewindMinutes;
}
}

View File

@ -59,6 +59,8 @@ namespace Mesen.GUI.Forms.Config
AddBinding("AutoHideMenu", chkAutoHideMenu);
AddBinding("DisplayTitleBarInfo", chkDisplayTitleBarInfo);
AddBinding("RewindBufferSize", nudRewindBufferSize);
UpdateCloudDisplay();
}

View File

@ -150,7 +150,8 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void IncreaseEmulationSpeed();
[DllImport(DLLPath)] public static extern void DecreaseEmulationSpeed();
[DllImport(DLLPath)] public static extern UInt32 GetEmulationSpeed();
[DllImport(DLLPath)] public static extern void SetTurboSpeed(UInt32 turboSpeed);
[DllImport(DLLPath)] public static extern void SetTurboRewindSpeed(UInt32 turboSpeed, UInt32 rewindSpeed);
[DllImport(DLLPath)] public static extern void SetRewindBufferSize(UInt32 seconds);
[DllImport(DLLPath)] public static extern void SetOverclockRate(UInt32 overclockRate, [MarshalAs(UnmanagedType.I1)]bool adjustApu);
[DllImport(DLLPath)] public static extern void SetPpuNmiConfig(UInt32 extraScanlinesBeforeNmi, UInt32 extraScanlineAfterNmi);
[DllImport(DLLPath)] public static extern void SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom);
@ -1058,6 +1059,11 @@ namespace Mesen.GUI
public struct EmulatorKeyMappings
{
public UInt32 FastForward;
public UInt32 Rewind;
public UInt32 RewindTenSecs;
public UInt32 RewindOneMin;
public UInt32 Pause;
public UInt32 Reset;
public UInt32 Exit;

View File

@ -9,6 +9,7 @@
#include "../Core/CheatManager.h"
#include "../Core/EmulationSettings.h"
#include "../Core/VideoDecoder.h"
#include "../Core/VideoRenderer.h"
#include "../Core/AutomaticRomTest.h"
#include "../Core/RecordedRomTest.h"
#include "../Core/FDS.h"
@ -306,9 +307,9 @@ namespace InteropEmu {
DllExport bool __stdcall MoviePlaying() { return MovieManager::Playing(); }
DllExport bool __stdcall MovieRecording() { return MovieManager::Recording(); }
DllExport void __stdcall AviRecord(char* filename, VideoCodec codec, uint32_t compressionLevel) { VideoDecoder::GetInstance()->StartRecording(filename, codec, compressionLevel); }
DllExport void __stdcall AviStop() { VideoDecoder::GetInstance()->StopRecording(); }
DllExport bool __stdcall AviIsRecording() { return VideoDecoder::GetInstance()->IsRecording(); }
DllExport void __stdcall AviRecord(char* filename, VideoCodec codec, uint32_t compressionLevel) { VideoRenderer::GetInstance()->StartRecording(filename, codec, compressionLevel); }
DllExport void __stdcall AviStop() { VideoRenderer::GetInstance()->StopRecording(); }
DllExport bool __stdcall AviIsRecording() { return VideoRenderer::GetInstance()->IsRecording(); }
DllExport void __stdcall WaveRecord(char* filename) { SoundMixer::StartRecording(filename); }
DllExport void __stdcall WaveStop() { SoundMixer::StopRecording(); }
@ -384,7 +385,8 @@ namespace InteropEmu {
DllExport void __stdcall IncreaseEmulationSpeed() { EmulationSettings::IncreaseEmulationSpeed(); }
DllExport void __stdcall DecreaseEmulationSpeed() { EmulationSettings::DecreaseEmulationSpeed(); }
DllExport uint32_t __stdcall GetEmulationSpeed() { return EmulationSettings::GetEmulationSpeed(true); }
DllExport void __stdcall SetTurboSpeed(uint32_t turboSpeed) { EmulationSettings::SetTurboSpeed(turboSpeed); }
DllExport void __stdcall SetTurboRewindSpeed(uint32_t turboSpeed, uint32_t rewindSpeed) { EmulationSettings::SetTurboRewindSpeed(turboSpeed, rewindSpeed); }
DllExport void __stdcall SetRewindBufferSize(uint32_t seconds) { EmulationSettings::SetRewindBufferSize(seconds); }
DllExport void __stdcall SetOverclockRate(uint32_t overclockRate, bool adjustApu) { EmulationSettings::SetOverclockRate(overclockRate, adjustApu); }
DllExport void __stdcall SetPpuNmiConfig(uint32_t extraScanlinesBeforeNmi, uint32_t extraScanlinesAfterNmi) { EmulationSettings::SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi); }
DllExport void __stdcall SetVideoScale(double scale) { EmulationSettings::SetVideoScale(scale); }

View File

@ -84,11 +84,12 @@ namespace NES
}
if(_textureBuffer[0]) {
delete[] _textureBuffer[0];
_textureBuffer[0] = nullptr;
}
if(_textureBuffer[1]) {
delete[] _textureBuffer[1];
_textureBuffer[1] = nullptr;
}
if(_samplerState) {
_samplerState->Release();
_samplerState = nullptr;
@ -358,7 +359,7 @@ namespace NES
void Renderer::DisplayMessage(string title, string message)
{
shared_ptr<ToastInfo> toast(new ToastInfo(title, message, 4000, ""));
shared_ptr<ToastInfo> toast(new ToastInfo(title, message, 4000));
_toasts.push_front(toast);
}