Remove Rewind code and Movie code

This commit is contained in:
twinaphex 2022-04-03 22:26:22 +02:00
parent 8c77855c6b
commit 8836d84954
36 changed files with 3 additions and 3255 deletions

View File

@ -1,225 +0,0 @@
#include "stdafx.h"
#include "ControlManager.h"
#include "SystemActionManager.h"
#include "FdsSystemActionManager.h"
#include "VsSystemActionManager.h"
#include "BizhawkMovie.h"
#include "VsControlManager.h"
#include "Console.h"
#include "BatteryManager.h"
#include "NotificationManager.h"
BizhawkMovie::BizhawkMovie(shared_ptr<Console> console)
{
_console = console;
_originalPowerOnState = _console->GetSettings()->GetRamPowerOnState();
}
BizhawkMovie::~BizhawkMovie()
{
Stop();
}
void BizhawkMovie::Stop()
{
if(_isPlaying) {
MessageManager::DisplayMessage("Movies", "MovieEnded");
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MovieEnded);
if(_console->GetSettings()->CheckFlag(EmulationFlags::PauseOnMovieEnd)) {
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
}
_console->GetSettings()->SetRamPowerOnState(_originalPowerOnState);
_isPlaying = false;
}
_console->GetControlManager()->UnregisterInputProvider(this);
}
bool BizhawkMovie::SetInput(BaseControlDevice *device)
{
SystemActionManager* actionManager = dynamic_cast<SystemActionManager*>(device);
int32_t pollCounter = _console->GetControlManager()->GetPollCounter();
if(actionManager) {
if(pollCounter < (int32_t)_systemActionByFrame.size()) {
uint32_t systemAction = _systemActionByFrame[pollCounter];
if(systemAction & 0x01) {
actionManager->SetBit(SystemActionManager::Buttons::PowerButton);
}
if(systemAction & 0x02) {
actionManager->SetBit(SystemActionManager::Buttons::ResetButton);
}
VsSystemActionManager* vsActionManager = dynamic_cast<VsSystemActionManager*>(device);
if(vsActionManager) {
if(systemAction & 0x04) {
actionManager->SetBit(VsSystemActionManager::VsButtons::InsertCoin1);
}
if(systemAction & 0x08) {
actionManager->SetBit(VsSystemActionManager::VsButtons::InsertCoin2);
}
if(systemAction & 0x10) {
actionManager->SetBit(VsSystemActionManager::VsButtons::ServiceButton);
}
}
FdsSystemActionManager* fdsActionManager = dynamic_cast<FdsSystemActionManager*>(device);
if(fdsActionManager) {
//FDS timings between NesHawk & Mesen are currently significantly different
//So FDS games will always go out of sync
if(systemAction & 0x04) {
fdsActionManager->SetBit(FdsSystemActionManager::FdsButtons::EjectDiskButton);
}
if(systemAction >= 8) {
systemAction >>= 3;
uint32_t diskNumber = 0;
while(!(systemAction & 0x01)) {
systemAction >>= 1;
diskNumber++;
}
fdsActionManager->SetBit(FdsSystemActionManager::FdsButtons::InsertDisk1 + diskNumber);
}
}
}
} else {
int port = device->GetPort();
StandardController* controller = dynamic_cast<StandardController*>(device);
if(controller) {
if(pollCounter < (int32_t)_dataByFrame[port].size()) {
controller->SetTextState(_dataByFrame[port][pollCounter]);
} else {
Stop();
}
}
}
return true;
}
bool BizhawkMovie::InitializeGameData(ZipReader &reader)
{
stringstream fileData;
if(!reader.GetStream("Header.txt", fileData)) {
return false;
}
_console->GetControlManager()->SetPollCounter(0);
while(!fileData.eof()) {
string line;
std::getline(fileData, line);
if(line.compare(0, 4, "SHA1", 4) == 0) {
if(line.size() >= 45) {
HashInfo hashInfo;
hashInfo.Sha1 = line.substr(5, 40);
if(_console->LoadMatchingRom("", hashInfo)) {
return true;
}
}
} else if(line.compare(0, 3, "MD5", 3) == 0) {
if(line.size() >= 36) {
HashInfo hashInfo;
hashInfo.PrgChrMd5 = line.substr(4, 32);
std::transform(hashInfo.PrgChrMd5.begin(), hashInfo.PrgChrMd5.end(), hashInfo.PrgChrMd5.begin(), ::toupper);
if(_console->LoadMatchingRom("", hashInfo)) {
return true;
}
}
}
}
return false;
}
bool BizhawkMovie::InitializeInputData(ZipReader &reader)
{
stringstream inputData;
if(!reader.GetStream("Input Log.txt", inputData)) {
return false;
}
int systemActionCount = 2;
shared_ptr<FdsSystemActionManager> fdsActionManager = _console->GetSystemActionManager<FdsSystemActionManager>();
if(fdsActionManager) {
//Eject disk + Insert Disk #XX
systemActionCount += fdsActionManager->GetSideCount() + 1;
} else {
shared_ptr<VsSystemActionManager> vsActionManager = _console->GetSystemActionManager<VsSystemActionManager>();
if(vsActionManager) {
//Insert coin 1, 2 + service button
systemActionCount += 3;
}
}
while(!inputData.eof()) {
string line;
std::getline(inputData, line);
if(line.size() > 0 && line[0] == '|') {
line.erase(std::remove(line.begin(), line.end(), '|'), line.end());
line = line.substr(0, line.size() - 1);
//Read power/reset/FDS/VS/etc. commands
uint32_t systemAction = 0;
for(int i = 0; i < systemActionCount; i++) {
if(line[i] != '.') {
systemAction |= (1 << i);
}
}
_systemActionByFrame.push_back(systemAction);
line = line.substr(systemActionCount);
int port = 0;
while(line.size() >= 8) {
_dataByFrame[port].push_back(line.substr(0, 8));
line = line.substr(8);
port++;
}
while(port < 4) {
_dataByFrame[port].push_back("........");
port++;
}
}
}
return _dataByFrame[0].size() > 0;
}
bool BizhawkMovie::Play(VirtualFile &file)
{
_console->Pause();
ZipReader reader;
std::stringstream ss;
file.ReadFile(ss);
reader.LoadArchive(ss);
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
_console->GetSettings()->SetRamPowerOnState(RamPowerOnState::AllOnes);
_console->GetBatteryManager()->SetBatteryProvider(shared_from_this());
if(InitializeInputData(reader) && InitializeGameData(reader)) {
//NesHawk initializes memory to 1s
_isPlaying = true;
}
_console->Resume();
return _isPlaying;
}
bool BizhawkMovie::IsPlaying()
{
return _isPlaying;
}
void BizhawkMovie::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputProvider(this);
}
}
vector<uint8_t> BizhawkMovie::LoadBattery(string extension)
{
return vector<uint8_t>();
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MovieManager.h"
#include "../Utilities/ZipReader.h"
#include "INotificationListener.h"
#include "BatteryManager.h"
class VirtualFile;
class Console;
class BizhawkMovie : public IMovie, public INotificationListener, public IBatteryProvider, public std::enable_shared_from_this<BizhawkMovie>
{
private:
bool InitializeGameData(ZipReader &reader);
bool InitializeInputData(ZipReader &reader);
void Stop();
protected:
shared_ptr<Console> _console;
vector<uint32_t> _systemActionByFrame;
vector<string> _dataByFrame[4];
bool _isPlaying = false;
RamPowerOnState _originalPowerOnState;
public:
BizhawkMovie(shared_ptr<Console>);
virtual ~BizhawkMovie();
bool SetInput(BaseControlDevice *device) override;
bool Play(VirtualFile &file) override;
bool IsPlaying() override;
// Inherited via INotificationListener
virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override;
virtual vector<uint8_t> LoadBattery(string extension) override;
};

View File

@ -21,7 +21,6 @@
#include "NsfPpu.h"
#include "SoundMixer.h"
#include "NsfMapper.h"
#include "MovieManager.h"
#include "SaveStateManager.h"
#include "HdPackBuilder.h"
#include "HdAudioDevice.h"
@ -39,7 +38,6 @@
#include "VideoDecoder.h"
#include "VideoRenderer.h"
#include "NotificationManager.h"
#include "HistoryViewer.h"
#include "ConsolePauseHelper.h"
#include "EventManager.h"
@ -65,7 +63,6 @@ Console::Console(shared_ptr<Console> master, EmulationSettings* initialSettings)
Console::~Console()
{
MovieManager::Stop();
}
void Console::Init()
@ -266,7 +263,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile, bool forP
_patchFilename = patchFile;
//Changed game, stop all recordings
MovieManager::Stop();
_soundMixer->StopRecording();
StopRecordingHdPack();
}
@ -468,11 +464,6 @@ CheatManager* Console::GetCheatManager()
return _cheatManager.get();
}
HistoryViewer* Console::GetHistoryViewer()
{
return _historyViewer.get();
}
VirtualFile Console::GetRomPath()
{
return static_cast<VirtualFile>(_romFilepath);
@ -640,9 +631,6 @@ void Console::Run()
RunSlaveCpu();
}
if(_historyViewer) {
_historyViewer->ProcessEndOfFrame();
}
_settings->DisableOverclocking(_disableOcNextFrame || IsNsf());
_disableOcNextFrame = false;
@ -734,7 +722,6 @@ void Console::Run()
StopRecordingHdPack();
MovieManager::Stop();
_soundMixer->StopRecording();
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);

View File

@ -6,7 +6,6 @@
#include "VirtualFile.h"
class BaseMapper;
class HistoryViewer;
class APU;
class CPU;
class PPU;
@ -47,8 +46,6 @@ private:
SimpleLock _stopLock;
atomic<uint32_t> _pauseCounter;
shared_ptr<HistoryViewer> _historyViewer;
shared_ptr<CPU> _cpu;
shared_ptr<PPU> _ppu;
shared_ptr<APU> _apu;
@ -127,7 +124,6 @@ public:
ControlManager* GetControlManager();
MemoryManager* GetMemoryManager();
CheatManager* GetCheatManager();
HistoryViewer* GetHistoryViewer();
bool LoadMatchingRom(string romName, HashInfo hashInfo);
string FindMatchingRom(string romName, HashInfo hashInfo);

View File

@ -3,7 +3,6 @@
#include "BaseMapper.h"
#include "EmulationSettings.h"
#include "Console.h"
#include "GameServerConnection.h"
#include "MemoryManager.h"
#include "IKeyManager.h"
#include "IInputProvider.h"
@ -290,12 +289,6 @@ void ControlManager::UpdateInputState()
//log += "|" + device->GetTextState();
}
if(!_console->GetSettings()->IsRunAheadFrame()) {
for(IInputRecorder* recorder : _inputRecorders) {
recorder->RecordInput(_controlDevices);
}
}
//Used by VS System games
RemapControllerButtons();

View File

@ -3,7 +3,6 @@
#include <algorithm>
#include "stdafx.h"
#include "MessageManager.h"
#include "GameClient.h"
#include "KeyManager.h"
#include "../Utilities/SimpleLock.h"

View File

@ -6,7 +6,6 @@
#include "FdsAudio.h"
#include "MemoryManager.h"
#include "BatteryManager.h"
#include "MovieManager.h"
void FDS::InitMapper()
{
@ -553,5 +552,5 @@ bool FDS::IsDiskInserted()
bool FDS::IsAutoInsertDiskEnabled()
{
return !_disableAutoInsertDisk && _settings->CheckFlag(EmulationFlags::FdsAutoInsertDisk) && !MovieManager::Playing() && !MovieManager::Recording();
}
return !_disableAutoInsertDisk && _settings->CheckFlag(EmulationFlags::FdsAutoInsertDisk);
}

View File

@ -1,79 +0,0 @@
#include "stdafx.h"
#include <algorithm>
#include "../Utilities/StringUtilities.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/Base64.h"
#include "ControlManager.h"
#include "FceuxMovie.h"
#include "Console.h"
#include "NotificationManager.h"
bool FceuxMovie::InitializeData(stringstream &filestream)
{
bool result = false;
_dataByFrame[0].push_back("");
_dataByFrame[1].push_back("");
_dataByFrame[2].push_back("");
_dataByFrame[3].push_back("");
_console->GetControlManager()->SetPollCounter(0);
while(!filestream.eof()) {
string line;
std::getline(filestream, line);
if(line.compare(0, 19, "romChecksum base64:", 19) == 0) {
vector<uint8_t> md5array = Base64::Decode(line.substr(19, line.size() - 20));
HashInfo hashInfo;
hashInfo.PrgChrMd5 = HexUtilities::ToHex(md5array);
_console->GetSettings()->SetRamPowerOnState(RamPowerOnState::AllZeros);
if(_console->LoadMatchingRom("", hashInfo)) {
result = true;
} else {
return false;
}
} else if(line.size() > 0 && line[0] == '|') {
vector<string> lineData = StringUtilities::Split(line.substr(1), '|');
if(lineData.size() == 0) {
continue;
}
//Read power/reset/FDS/VS/etc. commands
uint32_t systemAction = 0;
try {
systemAction = (uint32_t)std::atol(lineData[0].c_str());
} catch(std::exception&) {
}
_systemActionByFrame.push_back(systemAction);
//Only supports regular controllers (up to 4 of them)
for(size_t i = 1; i < lineData.size() && i < 5; i++) {
if(lineData[i].size() >= 8) {
string data = lineData[i].substr(3, 1) + lineData[i].substr(2, 1) + lineData[i].substr(1, 1) + lineData[i].substr(0, 1);
_dataByFrame[i - 1].push_back(data + lineData[i].substr(4, 4));
} else {
_dataByFrame[i - 1].push_back("");
}
}
}
}
return result;
}
bool FceuxMovie::Play(VirtualFile &file)
{
_console->Pause();
std::stringstream ss;
file.ReadFile(ss);
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
_console->GetBatteryManager()->SetBatteryProvider(shared_from_this());
if(InitializeData(ss)) {
_console->Reset(false);
_isPlaying = true;
}
_console->Resume();
return _isPlaying;
}

View File

@ -1,16 +0,0 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/ZipReader.h"
#include "MovieManager.h"
#include "BizhawkMovie.h"
class FceuxMovie : public BizhawkMovie
{
private:
bool InitializeData(stringstream &filestream);
public:
using BizhawkMovie::BizhawkMovie;
bool Play(VirtualFile &file) override;
};

View File

@ -1,112 +0,0 @@
#include "stdafx.h"
#include <thread>
using std::thread;
#include "MessageManager.h"
#include "GameClient.h"
#include "Console.h"
#include "NotificationManager.h"
#include "../Utilities/Socket.h"
#include "ClientConnectionData.h"
#include "GameClientConnection.h"
shared_ptr<GameClient> GameClient::_instance;
GameClient::GameClient(shared_ptr<Console> console)
{
_console = console;
_stop = false;
}
GameClient::~GameClient()
{
_stop = true;
if(_clientThread) {
_clientThread->join();
}
}
bool GameClient::Connected()
{
shared_ptr<GameClient> instance = _instance;
return instance ? instance->_connected : false;
}
void GameClient::Connect(shared_ptr<Console> console, ClientConnectionData &connectionData)
{
_instance.reset(new GameClient(console));
console->GetNotificationManager()->RegisterNotificationListener(_instance);
shared_ptr<GameClient> instance = _instance;
if(instance) {
instance->PrivateConnect(connectionData);
instance->_clientThread.reset(new thread(&GameClient::Exec, instance.get()));
}
}
void GameClient::Disconnect()
{
_instance.reset();
}
shared_ptr<GameClientConnection> GameClient::GetConnection()
{
shared_ptr<GameClient> instance = _instance;
return instance ? instance->_connection : nullptr;
}
void GameClient::PrivateConnect(ClientConnectionData &connectionData)
{
_stop = false;
shared_ptr<Socket> socket(new Socket());
if(socket->Connect(connectionData.Host.c_str(), connectionData.Port)) {
_connection.reset(new GameClientConnection(_console, socket, connectionData));
_console->GetNotificationManager()->RegisterNotificationListener(_connection);
_connected = true;
} else {
MessageManager::DisplayMessage("NetPlay", "CouldNotConnect");
_connected = false;
}
}
void GameClient::Exec()
{
if(_connected) {
while(!_stop) {
if(!_connection->ConnectionError()) {
_connection->ProcessMessages();
_connection->SendInput();
} else {
_connected = false;
_connection->Shutdown();
_connection.reset();
break;
}
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
}
}
}
void GameClient::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
}
void GameClient::SelectController(uint8_t port)
{
shared_ptr<GameClientConnection> connection = GetConnection();
if(connection) {
connection->SelectController(port);
}
}
uint8_t GameClient::GetAvailableControllers()
{
shared_ptr<GameClientConnection> connection = GetConnection();
return connection ? connection->GetAvailableControllers() : 0;
}
uint8_t GameClient::GetControllerPort()
{
shared_ptr<GameClientConnection> connection = GetConnection();
return connection ? connection->GetControllerPort() : GameConnection::SpectatorPort;
}

View File

@ -1,42 +0,0 @@
#pragma once
#include "stdafx.h"
#include <thread>
#include "INotificationListener.h"
using std::thread;
class Socket;
class GameClientConnection;
class ClientConnectionData;
class Console;
class GameClient : public INotificationListener
{
private:
static shared_ptr<GameClient> _instance;
shared_ptr<Console> _console;
unique_ptr<thread> _clientThread;
atomic<bool> _stop;
shared_ptr<GameClientConnection> _connection;
bool _connected = false;
static shared_ptr<GameClientConnection> GetConnection();
void PrivateConnect(ClientConnectionData &connectionData);
void Exec();
public:
GameClient(shared_ptr<Console> console);
virtual ~GameClient();
static bool Connected();
static void Connect(shared_ptr<Console> console, ClientConnectionData &connectionData);
static void Disconnect();
static void SelectController(uint8_t port);
static uint8_t GetControllerPort();
static uint8_t GetAvailableControllers();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
};

View File

@ -1,270 +0,0 @@
#include "stdafx.h"
#include "GameClientConnection.h"
#include "HandShakeMessage.h"
#include "InputDataMessage.h"
#include "MovieDataMessage.h"
#include "GameInformationMessage.h"
#include "SaveStateMessage.h"
#include "Console.h"
#include "EmulationSettings.h"
#include "ControlManager.h"
#include "ClientConnectionData.h"
#include "StandardController.h"
#include "Zapper.h"
#include "ArkanoidController.h"
#include "BandaiHyperShot.h"
#include "SelectControllerMessage.h"
#include "PlayerListMessage.h"
#include "ForceDisconnectMessage.h"
#include "ServerInformationMessage.h"
#include "NotificationManager.h"
GameClientConnection::GameClientConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, ClientConnectionData &connectionData) : GameConnection(console, socket)
{
_connectionData = connectionData;
_shutdown = false;
_enableControllers = false;
_minimumQueueSize = 3;
MessageManager::DisplayMessage("NetPlay", "ConnectedToServer");
}
GameClientConnection::~GameClientConnection()
{
Shutdown();
}
void GameClientConnection::Shutdown()
{
if(!_shutdown) {
_shutdown = true;
DisableControllers();
ControlManager* controlManager = _console->GetControlManager();
if(controlManager) {
controlManager->UnregisterInputProvider(this);
}
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::DisconnectedFromServer);
MessageManager::DisplayMessage("NetPlay", "ConnectionLost");
_console->GetSettings()->ClearFlags(EmulationFlags::ForceMaxSpeed);
}
}
void GameClientConnection::SendHandshake()
{
HandShakeMessage message(_connectionData.PlayerName, HandShakeMessage::GetPasswordHash(_connectionData.Password, _serverSalt), _connectionData.Spectator);
SendNetMessage(message);
}
void GameClientConnection::SendControllerSelection(uint8_t port)
{
SelectControllerMessage message(port);
SendNetMessage(message);
}
void GameClientConnection::ClearInputData()
{
LockHandler lock = _writeLock.AcquireSafe();
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
_inputSize[i] = 0;
_inputData[i].clear();
}
}
void GameClientConnection::ProcessMessage(NetMessage* message)
{
GameInformationMessage* gameInfo;
switch(message->GetType()) {
case MessageType::ServerInformation:
_serverSalt = ((ServerInformationMessage*)message)->GetHashSalt();
SendHandshake();
break;
case MessageType::SaveState:
if(_gameLoaded) {
DisableControllers();
_console->Pause();
ClearInputData();
((SaveStateMessage*)message)->LoadState(_console);
_enableControllers = true;
InitControlDevice();
_console->Resume();
}
break;
case MessageType::MovieData:
if(_gameLoaded) {
PushControllerState(((MovieDataMessage*)message)->GetPortNumber(), ((MovieDataMessage*)message)->GetInputState());
}
break;
case MessageType::ForceDisconnect:
MessageManager::DisplayMessage("NetPlay", ((ForceDisconnectMessage*)message)->GetMessage());
break;
case MessageType::PlayerList:
_playerList = ((PlayerListMessage*)message)->GetPlayerList();
break;
case MessageType::GameInformation:
DisableControllers();
_console->Pause();
gameInfo = (GameInformationMessage*)message;
if(gameInfo->GetPort() != _controllerPort) {
_controllerPort = gameInfo->GetPort();
if(_controllerPort == GameConnection::SpectatorPort) {
MessageManager::DisplayMessage("NetPlay", "ConnectedAsSpectator");
} else {
MessageManager::DisplayMessage("NetPlay", "ConnectedAsPlayer", std::to_string(_controllerPort + 1));
}
}
ClearInputData();
_gameLoaded = AttemptLoadGame(gameInfo->GetRomFilename(), gameInfo->GetCrc32Hash());
if(gameInfo->IsPaused()) {
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
} else {
_console->GetSettings()->ClearFlags(EmulationFlags::Paused);
}
_console->Resume();
break;
default:
break;
}
}
bool GameClientConnection::AttemptLoadGame(string filename, uint32_t crc32Hash)
{
if(filename.size() > 0) {
HashInfo hashInfo;
hashInfo.Crc32 = crc32Hash;
if(_console->LoadMatchingRom(filename, hashInfo)) {
return true;
} else {
MessageManager::DisplayMessage("NetPlay", "CouldNotFindRom");
return false;
}
}
return false;
}
void GameClientConnection::PushControllerState(uint8_t port, ControlDeviceState state)
{
LockHandler lock = _writeLock.AcquireSafe();
_inputData[port].push_back(state);
_inputSize[port]++;
if(_inputData[port].size() >= _minimumQueueSize) {
_waitForInput[port].Signal();
}
}
void GameClientConnection::DisableControllers()
{
//Used to prevent deadlocks when client is trying to fill its buffer while the host changes the current game/settings/etc. (i.e situations where we need to call Console::Pause())
ClearInputData();
_enableControllers = false;
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
_waitForInput[i].Signal();
}
}
bool GameClientConnection::SetInput(BaseControlDevice *device)
{
if(_enableControllers) {
uint8_t port = device->GetPort();
while(_inputSize[port] == 0) {
_waitForInput[port].Wait();
if(port == 0 && _minimumQueueSize < 10) {
//Increase buffer size - reduces freezes at the cost of additional lag
_minimumQueueSize++;
}
if(_shutdown || !_enableControllers) {
return true;
}
}
LockHandler lock = _writeLock.AcquireSafe();
ControlDeviceState state = _inputData[port].front();
_inputData[port].pop_front();
_inputSize[port]--;
if(_inputData[port].size() > _minimumQueueSize) {
//Too much data, catch up
_console->GetSettings()->SetFlags(EmulationFlags::ForceMaxSpeed);
} else {
_console->GetSettings()->ClearFlags(EmulationFlags::ForceMaxSpeed);
_console->GetSettings()->SetEmulationSpeed(100);
}
device->SetRawState(state);
}
return true;
}
void GameClientConnection::InitControlDevice()
{
if(_controllerPort == BaseControlDevice::ExpDevicePort) {
_newControlDevice = ControlManager::CreateExpansionDevice(_console->GetSettings()->GetExpansionDevice(), _console);
} else {
//Pretend we are using port 0 (to use player 1's keybindings during netplay)
_newControlDevice = ControlManager::CreateControllerDevice(_console->GetSettings()->GetControllerType(_controllerPort), 0, _console);
}
}
void GameClientConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
if(type == ConsoleNotificationType::ConfigChanged) {
InitControlDevice();
} else if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputProvider(this);
}
}
void GameClientConnection::SendInput()
{
if(_gameLoaded) {
if(_newControlDevice) {
_controlDevice = _newControlDevice;
_newControlDevice.reset();
}
ControlDeviceState inputState;
if(_controlDevice) {
_controlDevice->SetStateFromInput();
inputState = _controlDevice->GetRawState();
}
if(_lastInputSent != inputState) {
InputDataMessage message(inputState);
SendNetMessage(message);
_lastInputSent = inputState;
}
}
}
void GameClientConnection::SelectController(uint8_t port)
{
SendControllerSelection(port);
}
uint8_t GameClientConnection::GetAvailableControllers()
{
uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1;
for(PlayerInfo &playerInfo : _playerList) {
if(playerInfo.ControllerPort < BaseControlDevice::PortCount) {
availablePorts &= ~(1 << playerInfo.ControllerPort);
}
}
return availablePorts;
}
uint8_t GameClientConnection::GetControllerPort()
{
return _controllerPort;
}

View File

@ -1,61 +0,0 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "GameConnection.h"
#include "../Utilities/AutoResetEvent.h"
#include "../Utilities/SimpleLock.h"
#include "BaseControlDevice.h"
#include "IInputProvider.h"
#include "ControlDeviceState.h"
#include "ClientConnectionData.h"
class Console;
class GameClientConnection : public GameConnection, public INotificationListener, public IInputProvider
{
private:
std::deque<ControlDeviceState> _inputData[BaseControlDevice::PortCount];
atomic<uint32_t> _inputSize[BaseControlDevice::PortCount];
AutoResetEvent _waitForInput[BaseControlDevice::PortCount];
SimpleLock _writeLock;
atomic<bool> _shutdown;
atomic<bool> _enableControllers;
atomic<uint32_t> _minimumQueueSize;
vector<PlayerInfo> _playerList;
shared_ptr<BaseControlDevice> _controlDevice;
shared_ptr<BaseControlDevice> _newControlDevice;
ControlDeviceState _lastInputSent;
bool _gameLoaded = false;
uint8_t _controllerPort = GameConnection::SpectatorPort;
ClientConnectionData _connectionData;
string _serverSalt;
private:
void SendHandshake();
void SendControllerSelection(uint8_t port);
void ClearInputData();
void PushControllerState(uint8_t port, ControlDeviceState state);
void DisableControllers();
bool AttemptLoadGame(string filename, uint32_t crc32Hash);
protected:
void ProcessMessage(NetMessage* message) override;
public:
GameClientConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, ClientConnectionData &connectionData);
virtual ~GameClientConnection();
void Shutdown();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
bool SetInput(BaseControlDevice *device) override;
void InitControlDevice();
void SendInput();
void SelectController(uint8_t port);
uint8_t GetAvailableControllers();
uint8_t GetControllerPort();
};

View File

@ -1,99 +0,0 @@
#include "stdafx.h"
#include "GameConnection.h"
#include "HandShakeMessage.h"
#include "InputDataMessage.h"
#include "MovieDataMessage.h"
#include "GameInformationMessage.h"
#include "SaveStateMessage.h"
#include "PlayerListMessage.h"
#include "SelectControllerMessage.h"
#include "ClientConnectionData.h"
#include "ForceDisconnectMessage.h"
#include "ServerInformationMessage.h"
GameConnection::GameConnection(shared_ptr<Console> console, shared_ptr<Socket> socket)
{
_console = console;
_socket = socket;
}
void GameConnection::ReadSocket()
{
auto lock = _socketLock.AcquireSafe();
int bytesReceived = _socket->Recv((char*)_readBuffer + _readPosition, 0x40000 - _readPosition, 0);
if(bytesReceived > 0) {
_readPosition += bytesReceived;
}
}
bool GameConnection::ExtractMessage(void *buffer, uint32_t &messageLength)
{
messageLength = _readBuffer[0] | (_readBuffer[1] << 8) | (_readBuffer[2] << 16) | (_readBuffer[3] << 24);
if(messageLength > 1000000) {
MessageManager::Log("[Netplay] Invalid data received, closing connection.");
Disconnect();
return false;
}
int packetLength = messageLength + sizeof(messageLength);
if(_readPosition >= packetLength) {
memcpy(buffer, _readBuffer+sizeof(messageLength), messageLength);
memmove(_readBuffer, _readBuffer + packetLength, _readPosition - packetLength);
_readPosition -= packetLength;
return true;
}
return false;
}
NetMessage* GameConnection::ReadMessage()
{
ReadSocket();
if(_readPosition > 4) {
uint32_t messageLength;
if(ExtractMessage(_messageBuffer, messageLength)) {
switch((MessageType)_messageBuffer[0]) {
case MessageType::HandShake: return new HandShakeMessage(_messageBuffer, messageLength);
case MessageType::SaveState: return new SaveStateMessage(_messageBuffer, messageLength);
case MessageType::InputData: return new InputDataMessage(_messageBuffer, messageLength);
case MessageType::MovieData: return new MovieDataMessage(_messageBuffer, messageLength);
case MessageType::GameInformation: return new GameInformationMessage(_messageBuffer, messageLength);
case MessageType::PlayerList: return new PlayerListMessage(_messageBuffer, messageLength);
case MessageType::SelectController: return new SelectControllerMessage(_messageBuffer, messageLength);
case MessageType::ForceDisconnect: return new ForceDisconnectMessage(_messageBuffer, messageLength);
case MessageType::ServerInformation: return new ServerInformationMessage(_messageBuffer, messageLength);
}
}
}
return nullptr;
}
void GameConnection::SendNetMessage(NetMessage &message)
{
auto lock = _socketLock.AcquireSafe();
message.Send(*_socket.get());
}
void GameConnection::Disconnect()
{
auto lock = _socketLock.AcquireSafe();
_socket->Close();
}
bool GameConnection::ConnectionError()
{
return _socket->ConnectionError();
}
void GameConnection::ProcessMessages()
{
NetMessage* message;
while((message = ReadMessage()) != nullptr) {
//Loop until all messages have been processed
message->Initialize();
ProcessMessage(message);
delete message;
}
}

View File

@ -1,45 +0,0 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
class Socket;
class NetMessage;
class Console;
struct PlayerInfo
{
string Name;
uint8_t ControllerPort;
bool IsHost;
};
class GameConnection
{
protected:
shared_ptr<Socket> _socket;
shared_ptr<Console> _console;
uint8_t _readBuffer[0x40000] = {};
uint8_t _messageBuffer[0x40000] = {};
int _readPosition = 0;
SimpleLock _socketLock;
private:
void ReadSocket();
bool ExtractMessage(void *buffer, uint32_t &messageLength);
NetMessage* ReadMessage();
virtual void ProcessMessage(NetMessage* message) = 0;
protected:
void Disconnect();
public:
static constexpr uint8_t SpectatorPort = 0xFF;
GameConnection(shared_ptr<Console> console, shared_ptr<Socket> socket);
bool ConnectionError();
void ProcessMessages();
void SendNetMessage(NetMessage &message);
};

View File

@ -1,242 +0,0 @@
#include "stdafx.h"
#include <thread>
using std::thread;
#include "MessageManager.h"
#include "GameServer.h"
#include "Console.h"
#include "ControlManager.h"
#include "../Utilities/Socket.h"
#include "PlayerListMessage.h"
#include "NotificationManager.h"
shared_ptr<GameServer> GameServer::Instance;
GameServer::GameServer(shared_ptr<Console> console, uint16_t listenPort, string password, string hostPlayerName)
{
_console = console;
_stop = false;
_port = listenPort;
_password = password;
_hostPlayerName = hostPlayerName;
_hostControllerPort = 0;
//If a game is already running, register ourselves as an input recorder/provider right away
RegisterServerInput();
}
GameServer::~GameServer()
{
_stop = true;
_serverThread->join();
Stop();
ControlManager* controlManager = _console->GetControlManager();
if(controlManager) {
controlManager->UnregisterInputRecorder(this);
controlManager->UnregisterInputProvider(this);
}
}
void GameServer::RegisterServerInput()
{
ControlManager* controlManager = _console->GetControlManager();
if(controlManager) {
controlManager->RegisterInputRecorder(this);
controlManager->RegisterInputProvider(this);
}
}
void GameServer::AcceptConnections()
{
while(true) {
shared_ptr<Socket> socket = _listener->Accept();
if(!socket->ConnectionError()) {
auto connection = shared_ptr<GameServerConnection>(new GameServerConnection(_console, socket, _password));
_console->GetNotificationManager()->RegisterNotificationListener(connection);
_openConnections.push_back(connection);
} else {
break;
}
}
_listener->Listen(10);
}
void GameServer::UpdateConnections()
{
vector<shared_ptr<GameServerConnection>> connectionsToRemove;
for(shared_ptr<GameServerConnection> connection : _openConnections) {
if(connection->ConnectionError()) {
connectionsToRemove.push_back(connection);
} else {
connection->ProcessMessages();
}
}
for(shared_ptr<GameServerConnection> gameConnection : connectionsToRemove) {
_openConnections.remove(gameConnection);
}
}
list<shared_ptr<GameServerConnection>> GameServer::GetConnectionList()
{
if(GameServer::Started()) {
return Instance->_openConnections;
} else {
return list<shared_ptr<GameServerConnection>>();
}
}
bool GameServer::SetInput(BaseControlDevice *device)
{
uint8_t port = device->GetPort();
GameServerConnection* connection = GameServerConnection::GetNetPlayDevice(port);
if(connection) {
//Device is controlled by a client
device->SetRawState(connection->GetState());
return true;
}
//Host is controlling this device
return false;
}
void GameServer::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
{
for(shared_ptr<BaseControlDevice> &device : devices) {
for(shared_ptr<GameServerConnection> connection : _openConnections) {
if(!connection->ConnectionError()) {
//Send movie stream
connection->SendMovieData(device->GetPort(), device->GetRawState());
}
}
}
}
void GameServer::ProcessNotification(ConsoleNotificationType type, void * parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
//Register the server as an input provider/recorder
RegisterServerInput();
}
}
void GameServer::Exec()
{
_listener.reset(new Socket());
_listener->Bind(_port);
_listener->Listen(10);
_stop = false;
_initialized = true;
MessageManager::DisplayMessage("NetPlay" , "ServerStarted", std::to_string(_port));
while(!_stop) {
AcceptConnections();
UpdateConnections();
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
}
}
void GameServer::Stop()
{
_initialized = false;
_listener.reset();
MessageManager::DisplayMessage("NetPlay", "ServerStopped");
}
void GameServer::StartServer(shared_ptr<Console> console, uint16_t port, string password, string hostPlayerName)
{
Instance.reset(new GameServer(console, port, password, hostPlayerName));
console->GetNotificationManager()->RegisterNotificationListener(Instance);
Instance->_serverThread.reset(new thread(&GameServer::Exec, Instance.get()));
}
void GameServer::StopServer()
{
if(Instance) {
Instance.reset();
}
}
bool GameServer::Started()
{
if(Instance) {
return Instance->_initialized;
} else {
return false;
}
}
string GameServer::GetHostPlayerName()
{
if(GameServer::Started()) {
return Instance->_hostPlayerName;
}
return "";
}
uint8_t GameServer::GetHostControllerPort()
{
if(GameServer::Started()) {
return Instance->_hostControllerPort;
}
return GameConnection::SpectatorPort;
}
void GameServer::SetHostControllerPort(uint8_t port)
{
if(GameServer::Started()) {
Instance->_console->Pause();
if(port == GameConnection::SpectatorPort || GetAvailableControllers() & (1 << port)) {
//Port is available
Instance->_hostControllerPort = port;
SendPlayerList();
}
Instance->_console->Resume();
}
}
uint8_t GameServer::GetAvailableControllers()
{
uint8_t availablePorts = (1 << BaseControlDevice::PortCount) - 1;
for(PlayerInfo &playerInfo : GetPlayerList()) {
if(playerInfo.ControllerPort < BaseControlDevice::PortCount) {
availablePorts &= ~(1 << playerInfo.ControllerPort);
}
}
return availablePorts;
}
vector<PlayerInfo> GameServer::GetPlayerList()
{
vector<PlayerInfo> playerList;
PlayerInfo playerInfo;
playerInfo.Name = GetHostPlayerName();
playerInfo.ControllerPort = GetHostControllerPort();
playerInfo.IsHost = true;
playerList.push_back(playerInfo);
for(shared_ptr<GameServerConnection> &connection : GetConnectionList()) {
playerInfo.Name = connection->GetPlayerName();
playerInfo.ControllerPort = connection->GetControllerPort();
playerInfo.IsHost = false;
playerList.push_back(playerInfo);
}
return playerList;
}
void GameServer::SendPlayerList()
{
vector<PlayerInfo> playerList = GetPlayerList();
for(shared_ptr<GameServerConnection> &connection : GetConnectionList()) {
//Send player list update to all connections
PlayerListMessage message(playerList);
connection->SendNetMessage(message);
}
}

View File

@ -1,58 +0,0 @@
#pragma once
#include "stdafx.h"
#include <thread>
#include "GameServerConnection.h"
#include "INotificationListener.h"
#include "IInputProvider.h"
#include "IInputRecorder.h"
using std::thread;
class Console;
class GameServer : public IInputRecorder, public IInputProvider, public INotificationListener
{
private:
static shared_ptr<GameServer> Instance;
shared_ptr<Console> _console;
unique_ptr<thread> _serverThread;
atomic<bool> _stop;
unique_ptr<Socket> _listener;
uint16_t _port;
string _password;
list<shared_ptr<GameServerConnection>> _openConnections;
bool _initialized = false;
string _hostPlayerName;
uint8_t _hostControllerPort;
void AcceptConnections();
void UpdateConnections();
void Exec();
void Stop();
public:
GameServer(shared_ptr<Console> console, uint16_t port, string password, string hostPlayerName);
virtual ~GameServer();
void RegisterServerInput();
static void StartServer(shared_ptr<Console> console, uint16_t port, string password, string hostPlayerName);
static void StopServer();
static bool Started();
static string GetHostPlayerName();
static uint8_t GetHostControllerPort();
static void SetHostControllerPort(uint8_t port);
static uint8_t GetAvailableControllers();
static vector<PlayerInfo> GetPlayerList();
static void SendPlayerList();
static list<shared_ptr<GameServerConnection>> GetConnectionList();
bool SetInput(BaseControlDevice *device) override;
void RecordInput(vector<shared_ptr<BaseControlDevice>> devices) override;
// Inherited via INotificationListener
virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override;
};

View File

@ -1,248 +0,0 @@
#include "stdafx.h"
#include <random>
#include "MessageManager.h"
#include "GameServerConnection.h"
#include "HandShakeMessage.h"
#include "InputDataMessage.h"
#include "MovieDataMessage.h"
#include "GameInformationMessage.h"
#include "SaveStateMessage.h"
#include "Console.h"
#include "ControlManager.h"
#include "ClientConnectionData.h"
#include "EmulationSettings.h"
#include "StandardController.h"
#include "SelectControllerMessage.h"
#include "PlayerListMessage.h"
#include "GameServer.h"
#include "ForceDisconnectMessage.h"
#include "BaseControlDevice.h"
#include "ServerInformationMessage.h"
GameServerConnection* GameServerConnection::_netPlayDevices[BaseControlDevice::PortCount] = { };
GameServerConnection::GameServerConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, string serverPassword) : GameConnection(console, socket)
{
//Server-side connection
_serverPassword = serverPassword;
_controllerPort = GameConnection::SpectatorPort;
SendServerInformation();
}
GameServerConnection::~GameServerConnection()
{
if(!_playerName.empty()) {
MessageManager::DisplayMessage("NetPlay", _playerName + " (Player " + std::to_string(_controllerPort + 1) + ") disconnected.");
}
UnregisterNetPlayDevice(this);
}
void GameServerConnection::SendServerInformation()
{
std::random_device rd;
std::mt19937 engine(rd());
std::uniform_int_distribution<> dist((int)' ', (int)'~');
string hash(50, ' ');
for(int i = 0; i < 50; i++) {
int random = dist(engine);
hash[i] = (char)random;
}
_connectionHash = hash;
ServerInformationMessage message(hash);
SendNetMessage(message);
}
void GameServerConnection::SendGameInformation()
{
_console->Pause();
RomInfo romInfo = _console->GetRomInfo();
GameInformationMessage gameInfo(romInfo.RomName, romInfo.Hash.Crc32, _controllerPort, _console->GetSettings()->CheckFlag(EmulationFlags::Paused));
SendNetMessage(gameInfo);
SaveStateMessage saveState(_console);
SendNetMessage(saveState);
_console->Resume();
}
void GameServerConnection::SendMovieData(uint8_t port, ControlDeviceState state)
{
if(_handshakeCompleted) {
MovieDataMessage message(state, port);
SendNetMessage(message);
}
}
void GameServerConnection::SendForceDisconnectMessage(string disconnectMessage)
{
ForceDisconnectMessage message(disconnectMessage);
SendNetMessage(message);
Disconnect();
}
void GameServerConnection::PushState(ControlDeviceState state)
{
if(_inputData.size() == 0 || state != _inputData.back()) {
_inputData.clear();
_inputData.push_back(state);
}
}
ControlDeviceState GameServerConnection::GetState()
{
size_t inputBufferSize = _inputData.size();
ControlDeviceState stateData;
if(inputBufferSize > 0) {
stateData = _inputData.front();
if(inputBufferSize > 1) {
//Always keep the last one the client sent, it will be used until a new one is received
_inputData.pop_front();
}
}
return stateData;
}
void GameServerConnection::ProcessHandshakeResponse(HandShakeMessage* message)
{
//Send the game's current state to the client and register the controller
if(message->IsValid()) {
if(message->CheckPassword(_serverPassword, _connectionHash)) {
_console->Pause();
_controllerPort = message->IsSpectator() ? GameConnection::SpectatorPort : GetFirstFreeControllerPort();
_playerName = message->GetPlayerName();
string playerPortMessage = _controllerPort == GameConnection::SpectatorPort ? "Spectator" : "Player " + std::to_string(_controllerPort + 1);
MessageManager::DisplayMessage("NetPlay", _playerName + " (" + playerPortMessage + ") connected.");
if(_console->GetRomInfo().RomName.size() > 0) {
SendGameInformation();
}
_handshakeCompleted = true;
RegisterNetPlayDevice(this, _controllerPort);
GameServer::SendPlayerList();
_console->Resume();
} else {
SendForceDisconnectMessage("The password you provided did not match - you have been disconnected.");
}
} else {
SendForceDisconnectMessage("Server is using a different version of Mesen (" + EmulationSettings::GetMesenVersionString() + ") - you have been disconnected.");
MessageManager::DisplayMessage("NetPlay", + "NetplayVersionMismatch", message->GetPlayerName());
}
}
void GameServerConnection::ProcessMessage(NetMessage* message)
{
switch(message->GetType()) {
case MessageType::HandShake:
ProcessHandshakeResponse((HandShakeMessage*)message);
break;
case MessageType::InputData:
if(!_handshakeCompleted) {
SendForceDisconnectMessage("Handshake has not been completed - invalid packet");
return;
}
PushState(((InputDataMessage*)message)->GetInputState());
break;
case MessageType::SelectController:
if(!_handshakeCompleted) {
SendForceDisconnectMessage("Handshake has not been completed - invalid packet");
return;
}
SelectControllerPort(((SelectControllerMessage*)message)->GetPortNumber());
break;
default:
break;
}
}
void GameServerConnection::SelectControllerPort(uint8_t port)
{
_console->Pause();
if(port == GameConnection::SpectatorPort) {
//Client wants to be a spectator, make sure we are not using any controller
UnregisterNetPlayDevice(this);
_controllerPort = port;
} else {
GameServerConnection* netPlayDevice = GetNetPlayDevice(port);
if(netPlayDevice == this) {
//Nothing to do, we're already this player
} else if(netPlayDevice == nullptr) {
//This port is free, we can switch
UnregisterNetPlayDevice(this);
RegisterNetPlayDevice(this, port);
_controllerPort = port;
} else {
//Another player is using this port, we can't use it
}
}
SendGameInformation();
GameServer::SendPlayerList();
_console->Resume();
}
void GameServerConnection::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
switch(type) {
case ConsoleNotificationType::GamePaused:
case ConsoleNotificationType::GameResumed:
case ConsoleNotificationType::GameReset:
case ConsoleNotificationType::StateLoaded:
case ConsoleNotificationType::CheatAdded:
case ConsoleNotificationType::ConfigChanged:
case ConsoleNotificationType::GameInitCompleted:
SendGameInformation();
break;
default:
break;
}
}
void GameServerConnection::RegisterNetPlayDevice(GameServerConnection* device, uint8_t port)
{
GameServerConnection::_netPlayDevices[port] = device;
}
void GameServerConnection::UnregisterNetPlayDevice(GameServerConnection* device)
{
if(device != nullptr) {
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
if(GameServerConnection::_netPlayDevices[i] == device) {
GameServerConnection::_netPlayDevices[i] = nullptr;
break;
}
}
}
}
GameServerConnection* GameServerConnection::GetNetPlayDevice(uint8_t port)
{
return GameServerConnection::_netPlayDevices[port];
}
uint8_t GameServerConnection::GetFirstFreeControllerPort()
{
uint8_t hostPost = GameServer::GetHostControllerPort();
for(int i = 0; i < BaseControlDevice::PortCount; i++) {
if(hostPost != i && GameServerConnection::_netPlayDevices[i] == nullptr) {
return i;
}
}
return GameConnection::SpectatorPort;
}
string GameServerConnection::GetPlayerName()
{
return _playerName;
}
uint8_t GameServerConnection::GetControllerPort()
{
return _controllerPort;
}

View File

@ -1,52 +0,0 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "GameConnection.h"
#include "INotificationListener.h"
#include "BaseControlDevice.h"
#include "ControlDeviceState.h"
class HandShakeMessage;
class GameServerConnection : public GameConnection, public INotificationListener
{
private:
static GameServerConnection* _netPlayDevices[BaseControlDevice::PortCount];
list<ControlDeviceState> _inputData;
string _playerName;
int _controllerPort;
string _connectionHash;
string _serverPassword;
bool _handshakeCompleted = false;
void PushState(ControlDeviceState state);
void SendServerInformation();
void SendGameInformation();
void SelectControllerPort(uint8_t port);
void SendForceDisconnectMessage(string disconnectMessage);
void ProcessHandshakeResponse(HandShakeMessage* message);
static void RegisterNetPlayDevice(GameServerConnection* connection, uint8_t port);
static void UnregisterNetPlayDevice(GameServerConnection* device);
static uint8_t GetFirstFreeControllerPort();
protected:
void ProcessMessage(NetMessage* message) override;
public:
GameServerConnection(shared_ptr<Console> console, shared_ptr<Socket> socket, string serverPassword);
virtual ~GameServerConnection();
ControlDeviceState GetState();
void SendMovieData(uint8_t port, ControlDeviceState state);
string GetPlayerName();
uint8_t GetControllerPort();
virtual void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
static GameServerConnection* GetNetPlayDevice(uint8_t port);
};

View File

@ -1,164 +0,0 @@
#include "stdafx.h"
#include "HistoryViewer.h"
#include "RewindData.h"
#include "Console.h"
#include "BaseControlDevice.h"
#include "SoundMixer.h"
#include "NotificationManager.h"
#include "RomData.h"
#include "MovieRecorder.h"
#include "SaveStateManager.h"
HistoryViewer::HistoryViewer(shared_ptr<Console> console)
{
_console = console;
_position = 0;
_pollCounter = 0;
}
HistoryViewer::~HistoryViewer()
{
}
void HistoryViewer::SetHistoryData(std::deque<RewindData> &history)
{
_history = history;
_console->GetControlManager()->UnregisterInputProvider(this);
_console->GetControlManager()->RegisterInputProvider(this);
SeekTo(0);
}
uint32_t HistoryViewer::GetHistoryLength()
{
//Returns history length in number of frames
return (uint32_t)(_history.size() * HistoryViewer::BufferSize);
}
void HistoryViewer::GetHistorySegments(uint32_t *segmentBuffer, uint32_t &bufferSize)
{
uint32_t segmentIndex = 0;
for(size_t i = 0; i < _history.size(); i++) {
if(_history[i].EndOfSegment) {
segmentBuffer[segmentIndex] = (uint32_t)i;
segmentIndex++;
if(segmentIndex == bufferSize) {
//Reached buffer size, can't return any more values
break;
}
}
}
bufferSize = segmentIndex;
}
uint32_t HistoryViewer::GetPosition()
{
return _position;
}
void HistoryViewer::SeekTo(uint32_t seekPosition)
{
//Seek to the specified position
if(seekPosition < _history.size()) {
_console->Pause();
bool wasPaused = _console->GetSettings()->CheckFlag(EmulationFlags::Paused);
_console->GetSettings()->ClearFlags(EmulationFlags::Paused);
_position = seekPosition;
RewindData rewindData = _history[_position];
rewindData.LoadState(_console);
_pollCounter = 0;
if(wasPaused) {
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
}
_console->Resume();
}
}
bool HistoryViewer::CreateSaveState(string outputFile, uint32_t position)
{
if(position < _history.size()) {
std::stringstream stateData;
_console->GetSaveStateManager()->GetSaveStateHeader(stateData);
_history[position].GetStateData(stateData);
ofstream output(outputFile, ios::binary);
if(output) {
output << stateData.rdbuf();
output.close();
return true;
}
}
return false;
}
bool HistoryViewer::SaveMovie(string movieFile, uint32_t startPosition, uint32_t endPosition)
{
//Take a savestate to be able to restore it after generating the movie file
//(the movie generation uses the console's inputs, which could affect the emulation otherwise)
stringstream state;
_console->Pause();
_console->SaveState(state);
//Convert the rewind data to a .mmo file
unique_ptr<MovieRecorder> recorder(new MovieRecorder(_console));
bool result = recorder->CreateMovie(movieFile, _history, startPosition, endPosition);
//Resume the state and resume
_console->LoadState(state);
_console->Resume();
return result;
}
void HistoryViewer::ResumeGameplay(shared_ptr<Console> console, uint32_t resumePosition)
{
console->Pause();
if(_console->GetRomInfo().Hash.Sha1 != console->GetRomInfo().Hash.Sha1) {
//Load game on the main window if they aren't the same
console->Initialize(_console->GetRomPath(), _console->GetPatchFile());
}
if(resumePosition < _history.size()) {
_history[resumePosition].LoadState(console);
} else {
_history[_history.size() - 1].LoadState(console);
}
console->Resume();
}
bool HistoryViewer::SetInput(BaseControlDevice *device)
{
uint8_t port = device->GetPort();
if(_position < _history.size()) {
std::deque<ControlDeviceState> &stateData = _history[_position].InputLogs[port];
if(_pollCounter < stateData.size()) {
ControlDeviceState state = stateData[_pollCounter];
device->SetRawState(state);
}
}
if(port == 0 && _pollCounter < 30) {
_pollCounter++;
}
return true;
}
void HistoryViewer::ProcessEndOfFrame()
{
if(_pollCounter == HistoryViewer::BufferSize) {
_pollCounter = 0;
_position++;
if(_position >= _history.size()) {
//Reached the end of history data
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
return;
}
RewindData rewindData = _history[_position];
rewindData.LoadState(_console);
}
}

View File

@ -1,39 +0,0 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "IInputProvider.h"
#include "RewindData.h"
class Console;
class HistoryViewer : public IInputProvider
{
private:
static constexpr int32_t BufferSize = 30; //Number of frames between each save state
shared_ptr<Console> _console;
std::deque<RewindData> _history;
uint32_t _position;
uint32_t _pollCounter;
public:
HistoryViewer(shared_ptr<Console> console);
virtual ~HistoryViewer();
void SetHistoryData(std::deque<RewindData> &history);
uint32_t GetHistoryLength();
void GetHistorySegments(uint32_t * segmentBuffer, uint32_t &bufferSize);
uint32_t GetPosition();
void SeekTo(uint32_t seekPosition);
bool CreateSaveState(string outputFile, uint32_t position);
bool SaveMovie(string movieFile, uint32_t startPosition, uint32_t endPosition);
void ResumeGameplay(shared_ptr<Console> console, uint32_t resumePosition);
void ProcessEndOfFrame();
// Inherited via IInputProvider
bool SetInput(BaseControlDevice * device) override;
};

View File

@ -2,9 +2,3 @@
#include "stdafx.h"
class BaseControlDevice;
class IInputRecorder
{
public:
virtual void RecordInput(vector<shared_ptr<BaseControlDevice>> devices) = 0;
};

View File

@ -1,340 +0,0 @@
#include "stdafx.h"
#include "../Utilities/ZipReader.h"
#include "../Utilities/StringUtilities.h"
#include "../Utilities/HexUtilities.h"
#include "MesenMovie.h"
#include "MessageManager.h"
#include "ControlManager.h"
#include "BaseControlDevice.h"
#include "Console.h"
#include "SaveStateManager.h"
#include "CheatManager.h"
#include "MovieRecorder.h"
#include "BatteryManager.h"
#include "VirtualFile.h"
#include "NotificationManager.h"
#include "RomData.h"
MesenMovie::MesenMovie(shared_ptr<Console> console)
{
_console = console;
}
MesenMovie::~MesenMovie()
{
Stop();
}
void MesenMovie::Stop()
{
if(_playing) {
MessageManager::DisplayMessage("Movies", "MovieEnded");
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MovieEnded);
if(_console->GetSettings()->CheckFlag(EmulationFlags::PauseOnMovieEnd)) {
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
}
_playing = false;
}
_console->GetSettings()->SetInputPollScanline(241);
_console->GetControlManager()->UnregisterInputProvider(this);
}
bool MesenMovie::SetInput(BaseControlDevice *device)
{
uint32_t inputRowIndex = _console->GetControlManager()->GetPollCounter();
if(_inputData.size() > inputRowIndex && _inputData[inputRowIndex].size() > _deviceIndex) {
device->SetTextState(_inputData[inputRowIndex][_deviceIndex]);
_deviceIndex++;
if(_deviceIndex >= _inputData[inputRowIndex].size()) {
//Move to the next frame's data
_deviceIndex = 0;
}
} else {
Stop();
}
return true;
}
bool MesenMovie::IsPlaying()
{
return _playing;
}
vector<uint8_t> MesenMovie::LoadBattery(string extension)
{
vector<uint8_t> batteryData;
_reader->ExtractFile("Battery" + extension, batteryData);
return batteryData;
}
void MesenMovie::ProcessNotification(ConsoleNotificationType type, void * parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputProvider(this);
}
}
bool MesenMovie::Play(VirtualFile &file)
{
_movieFile = file;
std::stringstream ss;
file.ReadFile(ss);
_reader.reset(new ZipReader());
_reader->LoadArchive(ss);
stringstream settingsData, inputData;
if(!_reader->GetStream("GameSettings.txt", settingsData)) {
MessageManager::Log("[Movie] File not found: GameSettings.txt");
return false;
}
if(!_reader->GetStream("Input.txt", inputData)) {
MessageManager::Log("[Movie] File not found: Input.txt");
return false;
}
while(inputData) {
string line;
std::getline(inputData, line);
if(line.substr(0, 1) == "|") {
_inputData.push_back(StringUtilities::Split(line.substr(1), '|'));
}
}
_deviceIndex = 0;
ParseSettings(settingsData);
_console->Pause();
_console->GetBatteryManager()->SetBatteryProvider(shared_from_this());
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
ApplySettings();
//Disable auto-configure input option (otherwise the movie file's input types are ignored)
bool autoConfigureInput = _console->GetSettings()->CheckFlag(EmulationFlags::AutoConfigureInput);
_console->GetSettings()->ClearFlags(EmulationFlags::AutoConfigureInput);
ControlManager *controlManager = _console->GetControlManager();
if(controlManager) {
//ControlManager can be empty if no game is loaded
controlManager->SetPollCounter(0);
}
bool gameLoaded = LoadGame();
_console->GetSettings()->SetFlagState(EmulationFlags::AutoConfigureInput, autoConfigureInput);
if(!gameLoaded) {
_console->Resume();
return false;
}
stringstream saveStateData;
if(_reader->GetStream("SaveState.mst", saveStateData)) {
if(!_console->GetSaveStateManager()->LoadState(saveStateData, true)) {
_console->Resume();
return false;
} else {
_console->GetControlManager()->SetPollCounter(0);
}
}
_playing = true;
_console->Resume();
return true;
}
template<typename T>
T FromString(string name, const vector<string> &enumNames, T defaultValue)
{
for(size_t i = 0; i < enumNames.size(); i++) {
if(name == enumNames[i]) {
return (T)i;
}
}
return defaultValue;
}
void MesenMovie::ParseSettings(stringstream &data)
{
while(!data.eof()) {
string line;
std::getline(data, line);
if(!line.empty()) {
size_t index = line.find_first_of(' ');
if(index != string::npos) {
string name = line.substr(0, index);
string value = line.substr(index + 1);
if(name == "Cheat") {
_cheats.push_back(value);
} else {
_settings[name] = value;
}
}
}
}
}
bool MesenMovie::LoadGame()
{
string mesenVersion = LoadString(_settings, MovieKeys::MesenVersion);
string gameFile = LoadString(_settings, MovieKeys::GameFile);
string sha1Hash = LoadString(_settings, MovieKeys::Sha1);
//string patchFile = LoadString(_settings, MovieKeys::PatchFile);
//string patchFileSha1 = LoadString(_settings, MovieKeys::PatchFileSha1);
//string patchedRomSha1 = LoadString(_settings, MovieKeys::PatchedRomSha1);
if(_console->GetSettings()->CheckFlag(EmulationFlags::AllowMismatchingSaveState) && _console->GetRomInfo().RomName == gameFile) {
//Loaded game has the right name, and we don't want to validate the hash values
_console->PowerCycle();
return true;
}
HashInfo hashInfo;
hashInfo.Sha1 = sha1Hash;
VirtualFile romFile = _console->FindMatchingRom(gameFile, hashInfo);
bool gameLoaded = false;
if(romFile.IsValid()) {
VirtualFile patchFile(_movieFile.GetFilePath(), "PatchData.dat");
if(patchFile.IsValid()) {
gameLoaded = _console->Initialize(romFile, patchFile);
} else {
gameLoaded = _console->Initialize(romFile);
}
}
return gameLoaded;
}
void MesenMovie::ApplySettings()
{
EmulationSettings* settings = _console->GetSettings();
NesModel region = FromString(LoadString(_settings, MovieKeys::Region), NesModelNames, NesModel::NTSC);
ConsoleType consoleType = FromString(LoadString(_settings, MovieKeys::ConsoleType), ConsoleTypeNames, ConsoleType::Nes);
ControllerType controller1 = FromString(LoadString(_settings, MovieKeys::Controller1), ControllerTypeNames, ControllerType::None);
ControllerType controller2 = FromString(LoadString(_settings, MovieKeys::Controller2), ControllerTypeNames, ControllerType::None);
ControllerType controller3 = FromString(LoadString(_settings, MovieKeys::Controller3), ControllerTypeNames, ControllerType::None);
ControllerType controller4 = FromString(LoadString(_settings, MovieKeys::Controller4), ControllerTypeNames, ControllerType::None);
ExpansionPortDevice expansionDevice = FromString<ExpansionPortDevice>(LoadString(_settings, MovieKeys::ExpansionDevice), ExpansionPortDeviceNames, ExpansionPortDevice::None);
settings->SetNesModel(region);
settings->SetConsoleType(consoleType);
settings->SetControllerType(0, controller1);
settings->SetControllerType(1, controller2);
settings->SetControllerType(2, controller3);
settings->SetControllerType(3, controller4);
settings->SetExpansionDevice(expansionDevice);
uint32_t ramPowerOnState = LoadInt(_settings, MovieKeys::RamPowerOnState);
if(ramPowerOnState == 0xFF) {
settings->SetRamPowerOnState(RamPowerOnState::AllOnes);
} else {
settings->SetRamPowerOnState(RamPowerOnState::AllZeros);
}
settings->SetInputPollScanline(LoadInt(_settings, MovieKeys::InputPollScanline, 240));
settings->SetZapperDetectionRadius(LoadInt(_settings, MovieKeys::ZapperDetectionRadius));
settings->SetPpuNmiConfig(
LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi),
LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi)
);
settings->SetFlagState(EmulationFlags::DisablePpu2004Reads, LoadBool(_settings, MovieKeys::DisablePpu2004Reads));
settings->SetFlagState(EmulationFlags::DisablePaletteRead, LoadBool(_settings, MovieKeys::DisablePaletteRead));
settings->SetFlagState(EmulationFlags::DisableOamAddrBug, LoadBool(_settings, MovieKeys::DisableOamAddrBug));
settings->SetFlagState(EmulationFlags::UseNes101Hvc101Behavior, LoadBool(_settings, MovieKeys::UseNes101Hvc101Behavior));
settings->SetFlagState(EmulationFlags::EnableOamDecay, LoadBool(_settings, MovieKeys::EnableOamDecay));
settings->SetFlagState(EmulationFlags::DisablePpuReset, LoadBool(_settings, MovieKeys::DisablePpuReset));
settings->SetDipSwitches(HexUtilities::FromHex(LoadString(_settings, MovieKeys::DipSwitches)));
LoadCheats();
}
uint32_t MesenMovie::LoadInt(std::unordered_map<string, string> &settings, string name, uint32_t defaultValue)
{
auto result = settings.find(name);
if(result != settings.end()) {
try {
return (uint32_t)std::stoul(result->second);
} catch(std::exception&) {
MessageManager::Log("[Movies] Invalid value for tag: " + name);
return defaultValue;
}
} else {
return defaultValue;
}
}
bool MesenMovie::LoadBool(std::unordered_map<string, string> &settings, string name)
{
auto result = settings.find(name);
if(result != settings.end()) {
if(result->second == "true") {
return true;
} else if(result->second == "false") {
return false;
} else {
MessageManager::Log("[Movies] Invalid value for tag: " + name);
return false;
}
} else {
return false;
}
}
string MesenMovie::LoadString(std::unordered_map<string, string> &settings, string name)
{
auto result = settings.find(name);
if(result != settings.end()) {
return result->second;
} else {
return "";
}
}
void MesenMovie::LoadCheats()
{
vector<CodeInfo> cheats;
for(string cheatData : _cheats) {
CodeInfo code;
if(LoadCheat(cheatData, code)) {
cheats.push_back(code);
}
}
_console->GetCheatManager()->SetCheats(cheats);
}
bool MesenMovie::LoadCheat(string cheatData, CodeInfo &code)
{
vector<string> data = StringUtilities::Split(cheatData, ' ');
if(data.size() >= 3) {
uint32_t address = HexUtilities::FromHex(data[0]);
uint8_t value = HexUtilities::FromHex(data[1]);
bool relativeAddress = data[2] == "true";
int32_t compareValue = data.size() > 3 ? HexUtilities::FromHex(data[3]) : -1;
code.Address = address;
code.Value = value;
code.IsRelativeAddress = relativeAddress;
code.CompareValue = compareValue;
return true;
} else {
MessageManager::Log("[Movie] Invalid cheat definition: " + cheatData);
}
return false;
}

View File

@ -1,52 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MovieManager.h"
#include "VirtualFile.h"
#include "BatteryManager.h"
#include "INotificationListener.h"
class ZipReader;
class Console;
struct CodeInfo;
class MesenMovie : public IMovie, public INotificationListener, public IBatteryProvider, public std::enable_shared_from_this<MesenMovie>
{
private:
shared_ptr<Console> _console;
VirtualFile _movieFile;
shared_ptr<ZipReader> _reader;
bool _playing = false;
size_t _deviceIndex = 0;
vector<vector<string>> _inputData;
vector<string> _cheats;
std::unordered_map<string, string> _settings;
string _filename;
private:
void ParseSettings(stringstream &data);
void ApplySettings();
bool LoadGame();
void Stop();
uint32_t LoadInt(std::unordered_map<string, string> &settings, string name, uint32_t defaultValue = 0);
bool LoadBool(std::unordered_map<string, string> &settings, string name);
string LoadString(std::unordered_map<string, string> &settings, string name);
void LoadCheats();
bool LoadCheat(string cheatData, CodeInfo &code);
public:
MesenMovie(shared_ptr<Console> console);
virtual ~MesenMovie();
bool Play(VirtualFile &file) override;
bool SetInput(BaseControlDevice* device) override;
bool IsPlaying() override;
// Inherited via IBatteryProvider
virtual vector<uint8_t> LoadBattery(string extension) override;
// Inherited via INotificationListener
virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override;
};

View File

@ -1,37 +0,0 @@
#pragma once
#include "stdafx.h"
#include "NetMessage.h"
#include "ControlDeviceState.h"
class MovieDataMessage : public NetMessage
{
private:
uint8_t _portNumber;
ControlDeviceState _inputState;
protected:
virtual void ProtectedStreamState()
{
Stream<uint8_t>(_portNumber);
StreamArray(_inputState.State);
}
public:
MovieDataMessage(void* buffer, uint32_t length) : NetMessage(buffer, length) { }
MovieDataMessage(ControlDeviceState state, uint8_t port) : NetMessage(MessageType::MovieData)
{
_portNumber = port;
_inputState = state;
}
uint8_t GetPortNumber()
{
return _portNumber;
}
ControlDeviceState GetInputState()
{
return _inputState;
}
};

View File

@ -1,70 +0,0 @@
#include "stdafx.h"
#include "../Utilities/FolderUtilities.h"
#include "MovieManager.h"
#include "MesenMovie.h"
#include "BizhawkMovie.h"
#include "FceuxMovie.h"
#include "MovieRecorder.h"
#include "VirtualFile.h"
shared_ptr<IMovie> MovieManager::_player;
shared_ptr<MovieRecorder> MovieManager::_recorder;
void MovieManager::Record(RecordMovieOptions options, shared_ptr<Console> console)
{
shared_ptr<MovieRecorder> recorder(new MovieRecorder(console));
if(recorder->Record(options)) {
_recorder = recorder;
}
}
void MovieManager::Play(VirtualFile file, shared_ptr<Console> console)
{
vector<uint8_t> fileData;
if(file.IsValid() && file.ReadFile(fileData)) {
shared_ptr<IMovie> player;
if(memcmp(fileData.data(), "MMO", 3) == 0) {
//Old movie format, no longer supported
MessageManager::DisplayMessage("Movies", "MovieIncompatibleVersion");
} else if(memcmp(fileData.data(), "PK", 2) == 0) {
//Mesen or Bizhawk movie
ZipReader reader;
reader.LoadArchive(fileData);
vector<string> files = reader.GetFileList();
if(std::find(files.begin(), files.end(), "GameSettings.txt") != files.end()) {
player.reset(new MesenMovie(console));
} else {
player.reset(new BizhawkMovie(console));
}
} else if(memcmp(fileData.data(), "ver", 3) == 0) {
player.reset(new FceuxMovie(console));
}
if(player && player->Play(file)) {
_player = player;
MessageManager::DisplayMessage("Movies", "MoviePlaying", file.GetFileName());
}
}
}
void MovieManager::Stop()
{
_player.reset();
if(_recorder) {
_recorder.reset();
}
}
bool MovieManager::Playing()
{
shared_ptr<IMovie> player = _player;
return player && player->IsPlaying();
}
bool MovieManager::Recording()
{
return _recorder != nullptr;
}

View File

@ -1,31 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MessageManager.h"
#include "EmulationSettings.h"
#include "IInputProvider.h"
#include "Types.h"
class MovieRecorder;
class VirtualFile;
class Console;
class IMovie : public IInputProvider
{
public:
virtual bool Play(VirtualFile &file) = 0;
virtual bool IsPlaying() = 0;
};
class MovieManager
{
private:
static shared_ptr<IMovie> _player;
static shared_ptr<MovieRecorder> _recorder;
public:
static void Record(RecordMovieOptions options, shared_ptr<Console> console);
static void Play(VirtualFile file, shared_ptr<Console> console);
static void Stop();
static bool Playing();
static bool Recording();
};

View File

@ -1,265 +0,0 @@
#include "stdafx.h"
#include <deque>
#include "../Utilities/HexUtilities.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/ZipWriter.h"
#include "MovieRecorder.h"
#include "ControlManager.h"
#include "BaseControlDevice.h"
#include "Console.h"
#include "CheatManager.h"
#include "VirtualFile.h"
#include "SaveStateManager.h"
#include "NotificationManager.h"
#include "RomData.h"
#include "RewindData.h"
MovieRecorder::MovieRecorder(shared_ptr<Console> console)
{
_console = console;
}
MovieRecorder::~MovieRecorder()
{
Stop();
}
bool MovieRecorder::Record(RecordMovieOptions options)
{
_filename = options.Filename;
_author = options.Author;
_description = options.Description;
_writer.reset(new ZipWriter());
_inputData = stringstream();
_saveStateData = stringstream();
_hasSaveState = false;
if(!_writer->Initialize(_filename)) {
_writer.reset();
return false;
} else {
_console->Pause();
if(options.RecordFrom == RecordMovieFrom::StartWithoutSaveData) {
_console->GetBatteryManager()->SetBatteryProvider(shared_from_this());
}
//Save existing battery files
if(options.RecordFrom == RecordMovieFrom::StartWithSaveData) {
_console->GetBatteryManager()->SetBatteryRecorder(shared_from_this());
}
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
if(options.RecordFrom == RecordMovieFrom::CurrentState) {
_console->GetControlManager()->RegisterInputRecorder(this);
_console->GetSaveStateManager()->SaveState(_saveStateData);
_hasSaveState = true;
} else {
_console->PowerCycle();
}
_console->GetBatteryManager()->SetBatteryRecorder(nullptr);
_console->Resume();
MessageManager::DisplayMessage("Movies", "MovieRecordingTo", FolderUtilities::GetFilename(_filename, true));
return true;
}
}
void MovieRecorder::GetGameSettings(stringstream &out)
{
EmulationSettings* settings = _console->GetSettings();
NesModel model = _console->GetModel();
WriteString(out, MovieKeys::MesenVersion, settings->GetMesenVersionString());
WriteInt(out, MovieKeys::MovieFormatVersion, MovieRecorder::MovieFormatVersion);
VirtualFile romFile = _console->GetRomPath();
WriteString(out, MovieKeys::GameFile, romFile.GetFileName());
WriteString(out, MovieKeys::Sha1, romFile.GetSha1Hash());
VirtualFile patchFile = _console->GetPatchFile();
if(patchFile.IsValid()) {
WriteString(out, MovieKeys::PatchFile, patchFile.GetFileName());
WriteString(out, MovieKeys::PatchFileSha1, patchFile.GetSha1Hash());
WriteString(out, MovieKeys::PatchedRomSha1, _console->GetRomInfo().Hash.Sha1);
}
switch(model) {
case NesModel::Auto: break; //Console::GetModel() will never return Auto.
case NesModel::NTSC: WriteString(out, MovieKeys::Region, "NTSC"); break;
case NesModel::PAL: WriteString(out, MovieKeys::Region, "PAL"); break;
case NesModel::Dendy: WriteString(out, MovieKeys::Region, "Dendy"); break;
}
switch(settings->GetConsoleType()) {
case ConsoleType::Nes: WriteString(out, MovieKeys::ConsoleType, "NES"); break;
case ConsoleType::Famicom: WriteString(out, MovieKeys::ConsoleType, "Famicom"); break;
}
WriteString(out, MovieKeys::Controller1, ControllerTypeNames[(int)settings->GetControllerType(0)]);
WriteString(out, MovieKeys::Controller2, ControllerTypeNames[(int)settings->GetControllerType(1)]);
if(settings->CheckFlag(EmulationFlags::HasFourScore)) {
WriteString(out, MovieKeys::Controller3, ControllerTypeNames[(int)settings->GetControllerType(2)]);
WriteString(out, MovieKeys::Controller4, ControllerTypeNames[(int)settings->GetControllerType(3)]);
}
if(settings->GetConsoleType() == ConsoleType::Famicom) {
WriteString(out, MovieKeys::ExpansionDevice, ExpansionPortDeviceNames[(int)settings->GetExpansionDevice()]);
}
WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, settings->GetPpuExtraScanlinesBeforeNmi());
WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, settings->GetPpuExtraScanlinesAfterNmi());
WriteInt(out, MovieKeys::InputPollScanline, settings->GetInputPollScanline());
WriteBool(out, MovieKeys::DisablePpu2004Reads, settings->CheckFlag(EmulationFlags::DisablePpu2004Reads));
WriteBool(out, MovieKeys::DisablePaletteRead, settings->CheckFlag(EmulationFlags::DisablePaletteRead));
WriteBool(out, MovieKeys::DisableOamAddrBug, settings->CheckFlag(EmulationFlags::DisableOamAddrBug));
WriteBool(out, MovieKeys::UseNes101Hvc101Behavior, settings->CheckFlag(EmulationFlags::UseNes101Hvc101Behavior));
WriteBool(out, MovieKeys::EnableOamDecay, settings->CheckFlag(EmulationFlags::EnableOamDecay));
WriteBool(out, MovieKeys::DisablePpuReset, settings->CheckFlag(EmulationFlags::DisablePpuReset));
WriteInt(out, MovieKeys::ZapperDetectionRadius, settings->GetZapperDetectionRadius());
switch(settings->GetRamPowerOnState()) {
case RamPowerOnState::AllZeros: WriteInt(out, MovieKeys::RamPowerOnState, 0x00); break;
case RamPowerOnState::AllOnes: WriteInt(out, MovieKeys::RamPowerOnState, 0xFF); break;
case RamPowerOnState::Random: WriteInt(out, MovieKeys::RamPowerOnState, -1); break; //TODO: Shouldn't be used for movies
}
if(_console->GetDipSwitchCount() > 0) {
WriteString(out, MovieKeys::DipSwitches, HexUtilities::ToHex(settings->GetDipSwitches()));
}
for(CodeInfo &code : _console->GetCheatManager()->GetCheats()) {
WriteCheat(out, code);
}
}
void MovieRecorder::WriteCheat(stringstream &out, CodeInfo &code)
{
out << "Cheat " <<
HexUtilities::ToHex(code.Address) << " " <<
HexUtilities::ToHex(code.Value) << " " <<
(code.IsRelativeAddress ? "true" : "false") << " " <<
(code.CompareValue < 0 ? HexUtilities::ToHex((uint8_t)code.CompareValue) : "") << "\n";
}
void MovieRecorder::WriteString(stringstream &out, string name, string value)
{
out << name << " " << value << "\n";
}
void MovieRecorder::WriteInt(stringstream &out, string name, uint32_t value)
{
out << name << " " << std::to_string(value) << "\n";
}
void MovieRecorder::WriteBool(stringstream &out, string name, bool enabled)
{
out << name << " " << (enabled ? "true" : "false") << "\n";
}
bool MovieRecorder::Stop()
{
if(_writer) {
_console->GetControlManager()->UnregisterInputRecorder(this);
_writer->AddFile(_inputData, "Input.txt");
stringstream out;
GetGameSettings(out);
_writer->AddFile(out, "GameSettings.txt");
if(!_author.empty() || !_description.empty()) {
stringstream movieInfo;
WriteString(movieInfo, "Author", _author);
movieInfo << "Description\n" << _description;
_writer->AddFile(movieInfo, "MovieInfo.txt");
}
VirtualFile patchFile = _console->GetPatchFile();
vector<uint8_t> patchData;
if(patchFile.IsValid() && patchFile.ReadFile(patchData)) {
_writer->AddFile(patchData, "PatchData.dat");
}
if(_hasSaveState) {
_writer->AddFile(_saveStateData, "SaveState.mst");
}
for(auto kvp : _batteryData) {
_writer->AddFile(kvp.second, "Battery" + kvp.first);
}
bool result = _writer->Save();
if(result) {
MessageManager::DisplayMessage("Movies", "MovieSaved", FolderUtilities::GetFilename(_filename, true));
}
return result;
}
return false;
}
void MovieRecorder::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
{
for(shared_ptr<BaseControlDevice> &device : devices) {
_inputData << ("|" + device->GetTextState());
}
_inputData << "\n";
}
void MovieRecorder::OnLoadBattery(string extension, vector<uint8_t> batteryData)
{
_batteryData[extension] = batteryData;
}
vector<uint8_t> MovieRecorder::LoadBattery(string extension)
{
return vector<uint8_t>();
}
void MovieRecorder::ProcessNotification(ConsoleNotificationType type, void *parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputRecorder(this);
}
}
bool MovieRecorder::CreateMovie(string movieFile, std::deque<RewindData> &data, uint32_t startPosition, uint32_t endPosition)
{
_filename = movieFile;
_writer.reset(new ZipWriter());
if(startPosition < data.size() && endPosition <= data.size() && _writer->Initialize(_filename)) {
vector<shared_ptr<BaseControlDevice>> devices = _console->GetControlManager()->GetControlDevices();
if(startPosition > 0 || _console->GetRomInfo().HasBattery || _console->GetSettings()->GetRamPowerOnState() == RamPowerOnState::Random) {
//Create a movie from a savestate if we don't start from the beginning (or if the game has save ram, or if the power on ram state is random)
_hasSaveState = true;
_saveStateData = stringstream();
_console->GetSaveStateManager()->GetSaveStateHeader(_saveStateData);
data[startPosition].GetStateData(_saveStateData);
}
_inputData = stringstream();
for(uint32_t i = startPosition; i < endPosition; i++) {
RewindData rewindData = data[i];
for(uint32_t i = 0; i < 30; i++) {
for(shared_ptr<BaseControlDevice> &device : devices) {
uint8_t port = device->GetPort();
if(i < rewindData.InputLogs[port].size()) {
device->SetRawState(rewindData.InputLogs[port][i]);
_inputData << ("|" + device->GetTextState());
}
}
_inputData << "\n";
}
}
//Write the movie file
return Stop();
}
return false;
}

View File

@ -1,86 +0,0 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include <unordered_map>
#include "IInputRecorder.h"
#include "BatteryManager.h"
#include "Types.h"
#include "INotificationListener.h"
class ZipWriter;
class Console;
class RewindData;
struct CodeInfo;
class MovieRecorder : public INotificationListener, public IInputRecorder, public IBatteryRecorder, public IBatteryProvider, public std::enable_shared_from_this<MovieRecorder>
{
private:
static const uint32_t MovieFormatVersion = 1;
shared_ptr<Console> _console;
string _filename;
string _author;
string _description;
unique_ptr<ZipWriter> _writer;
std::unordered_map<string, vector<uint8_t>> _batteryData;
stringstream _inputData;
bool _hasSaveState = false;
stringstream _saveStateData;
void GetGameSettings(stringstream &out);
void WriteCheat(stringstream &out, CodeInfo &code);
void WriteString(stringstream &out, string name, string value);
void WriteInt(stringstream &out, string name, uint32_t value);
void WriteBool(stringstream &out, string name, bool enabled);
public:
MovieRecorder(shared_ptr<Console> console);
virtual ~MovieRecorder();
bool Record(RecordMovieOptions options);
bool Stop();
bool CreateMovie(string movieFile, std::deque<RewindData> &data, uint32_t startPosition, uint32_t endPosition);
// Inherited via IInputRecorder
void RecordInput(vector<shared_ptr<BaseControlDevice>> devices) override;
// Inherited via IBatteryRecorder
virtual void OnLoadBattery(string extension, vector<uint8_t> batteryData) override;
// Inherited via IBatteryProvider
virtual vector<uint8_t> LoadBattery(string extension) override;
// Inherited via INotificationListener
virtual void ProcessNotification(ConsoleNotificationType type, void *parameter) override;
};
namespace MovieKeys
{
constexpr const char* MesenVersion = "MesenVersion";
constexpr const char* MovieFormatVersion = "MovieFormatVersion";
constexpr const char* GameFile = "GameFile";
constexpr const char* Sha1 = "SHA1";
constexpr const char* PatchFile = "PatchFile";
constexpr const char* PatchFileSha1 = "PatchFileSHA1";
constexpr const char* PatchedRomSha1 = "PatchedRomSHA1";
constexpr const char* Region = "Region";
constexpr const char* ConsoleType = "ConsoleType";
constexpr const char* Controller1 = "Controller1";
constexpr const char* Controller2 = "Controller2";
constexpr const char* Controller3 = "Controller3";
constexpr const char* Controller4 = "Controller4";
constexpr const char* ExpansionDevice = "ExpansionDevice";
constexpr const char* ExtraScanlinesBeforeNmi = "ExtraScanlinesBeforeNmi";
constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi";
constexpr const char* DisablePpu2004Reads = "DisablePpu2004Reads";
constexpr const char* DisablePaletteRead = "DisablePaletteRead";
constexpr const char* DisableOamAddrBug = "DisableOamAddrBug";
constexpr const char* UseNes101Hvc101Behavior = "UseNes101Hvc101Behavior";
constexpr const char* EnableOamDecay = "EnableOamDecay";
constexpr const char* DisablePpuReset = "DisablePpuReset";
constexpr const char* ZapperDetectionRadius = "ZapperDetectionRadius";
constexpr const char* RamPowerOnState = "RamPowerOnState";
constexpr const char* DipSwitches = "DipSwitches";
constexpr const char* InputPollScanline = "InputPollScanline";
};

View File

@ -1,46 +0,0 @@
#include "stdafx.h"
#include "RewindData.h"
#include "Console.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());
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(shared_ptr<Console> &console)
{
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;
}

View File

@ -1,25 +0,0 @@
#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);
};

View File

@ -1,382 +0,0 @@
#include "stdafx.h"
#include "RewindManager.h"
#include "MessageManager.h"
#include "Console.h"
#include "VideoRenderer.h"
#include "SoundMixer.h"
#include "BaseControlDevice.h"
#include "HistoryViewer.h"
RewindManager::RewindManager(shared_ptr<Console> console)
{
_console = console;
_settings = console->GetSettings();
_rewindState = RewindState::Stopped;
_framesToFastForward = 0;
_hasHistory = false;
AddHistoryBlock();
Initialize();
}
RewindManager::~RewindManager()
{
_console->GetControlManager()->UnregisterInputProvider(this);
_console->GetControlManager()->UnregisterInputRecorder(this);
}
void RewindManager::Initialize()
{
_console->GetControlManager()->RegisterInputProvider(this);
_console->GetControlManager()->RegisterInputRecorder(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(_settings->IsRunAheadFrame()) {
return;
}
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->ClearFlags(EmulationFlags::Rewind);
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
}
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) {
if(_history.empty() && !forDebugger) {
//No history to rewind
return;
}
_console->Pause();
_rewindState = forDebugger ? RewindState::Debugging : RewindState::Starting;
_videoHistoryBuilder.clear();
_videoHistory.clear();
_audioHistoryBuilder.clear();
_audioHistory.clear();
_historyBackup.clear();
if(_history.empty()) {
_currentHistory.LoadState(_console);
} else {
PopHistory();
}
_settings->SetFlags(EmulationFlags::ForceMaxSpeed);
_settings->SetFlags(EmulationFlags::Rewind);
_console->Resume();
}
}
void RewindManager::ForceStop()
{
if(_rewindState != RewindState::Stopped) {
while(_historyBackup.size() > 1) {
_history.push_back(_historyBackup.front());
_historyBackup.pop_front();
}
if(!_historyBackup.empty()) {
_currentHistory = _historyBackup.front();
}
_historyBackup.clear();
_rewindState = RewindState::Stopped;
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
_settings->ClearFlags(EmulationFlags::Rewind);
}
}
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
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->SetFlags(EmulationFlags::ForceMaxSpeed);
} else {
_rewindState = RewindState::Stopped;
_historyBackup.clear();
_settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
_settings->ClearFlags(EmulationFlags::Rewind);
}
_videoHistoryBuilder.clear();
_videoHistory.clear();
_audioHistoryBuilder.clear();
_audioHistory.clear();
_console->Resume();
}
}
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->ClearFlags(EmulationFlags::ForceMaxSpeed);
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, uint32_t sampleRate)
{
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;
_console->Pause();
for(uint32_t i = 0; i < removeCount; i++) {
if(!_history.empty()) {
_currentHistory = _history.back();
_history.pop_back();
} else {
break;
}
}
_currentHistory.LoadState(_console);
_console->Resume();
}
}
bool RewindManager::HasHistory()
{
return _hasHistory;
}
void RewindManager::CopyHistory(shared_ptr<HistoryViewer> destHistoryViewer)
{
destHistoryViewer->SetHistoryData(_history);
}
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, uint32_t sampleRate)
{
return ProcessAudio(soundBuffer, sampleCount, sampleRate);
}

View File

@ -1,78 +0,0 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include "INotificationListener.h"
#include "RewindData.h"
#include "IInputProvider.h"
#include "IInputRecorder.h"
class Console;
class HistoryViewer;
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;
EmulationSettings* _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, uint32_t sampleRate);
void ClearBuffer();
public:
RewindManager(shared_ptr<Console> console);
virtual ~RewindManager();
void Initialize();
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 CopyHistory(shared_ptr<HistoryViewer> destHistoryViewer);
void SendFrame(void *frameBuffer, uint32_t width, uint32_t height, bool forRewind);
bool SendAudio(int16_t *soundBuffer, uint32_t sampleCount, uint32_t sampleRate);
};

View File

@ -5,7 +5,6 @@
#include "EmulationSettings.h"
#include "VideoDecoder.h"
#include "Debugger.h"
#include "MovieManager.h"
#include "RomData.h"
#include "DefaultVideoFilter.h"
#include "PPU.h"
@ -97,10 +96,6 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
}
}
//Stop any movie that might have been playing/recording if a state is loaded
//(Note: Loading a state is disabled in the UI while a movie is playing/recording)
MovieManager::Stop();
_console->LoadState(stream, fileFormatVersion);
return true;

View File

@ -6,7 +6,6 @@
#include "VsZapper.h"
#include <assert.h>
#include "StandardController.h"
#include "MovieManager.h"
#include "IInputProvider.h"
class BaseControlDevice;
@ -68,4 +67,4 @@ public:
// Inherited via IInputProvider
virtual bool SetInput(BaseControlDevice* device) override;
};
};

View File

@ -28,7 +28,6 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/BaseVideoFilter.cpp \
$(CORE_DIR)/BatteryManager.cpp \
$(CORE_DIR)/BisqwitNtscFilter.cpp \
$(CORE_DIR)/BizhawkMovie.cpp \
$(CORE_DIR)/Breakpoint.cpp \
$(CORE_DIR)/CheatManager.cpp \
$(CORE_DIR)/CodeDataLogger.cpp \
@ -46,22 +45,15 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/EmulationSettings.cpp \
$(CORE_DIR)/EventManager.cpp \
$(CORE_DIR)/ExpressionEvaluator.cpp \
$(CORE_DIR)/FceuxMovie.cpp \
$(CORE_DIR)/FDS.cpp \
$(CORE_DIR)/FdsLoader.cpp \
$(CORE_DIR)/GameClient.cpp \
$(CORE_DIR)/GameClientConnection.cpp \
$(CORE_DIR)/GameConnection.cpp \
$(CORE_DIR)/GameDatabase.cpp \
$(CORE_DIR)/GameServer.cpp \
$(CORE_DIR)/GameServerConnection.cpp \
$(CORE_DIR)/HdAudioDevice.cpp \
$(CORE_DIR)/HdNesPack.cpp \
$(CORE_DIR)/HdPackBuilder.cpp \
$(CORE_DIR)/HdPackLoader.cpp \
$(CORE_DIR)/HdPpu.cpp \
$(CORE_DIR)/HdVideoFilter.cpp \
$(CORE_DIR)/HistoryViewer.cpp \
$(CORE_DIR)/iNesLoader.cpp \
$(CORE_DIR)/KeyManager.cpp \
$(CORE_DIR)/LabelManager.cpp \
@ -69,10 +61,7 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/MemoryAccessCounter.cpp \
$(CORE_DIR)/MemoryDumper.cpp \
$(CORE_DIR)/MemoryManager.cpp \
$(CORE_DIR)/MesenMovie.cpp \
$(CORE_DIR)/MessageManager.cpp \
$(CORE_DIR)/MovieManager.cpp \
$(CORE_DIR)/MovieRecorder.cpp \
$(CORE_DIR)/NESHeader.cpp \
$(CORE_DIR)/NotificationManager.cpp \
$(CORE_DIR)/NsfLoader.cpp \
@ -83,8 +72,6 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/OggReader.cpp \
$(CORE_DIR)/PPU.cpp \
$(CORE_DIR)/ReverbFilter.cpp \
$(CORE_DIR)/RewindData.cpp \
$(CORE_DIR)/RewindManager.cpp \
$(CORE_DIR)/RomLoader.cpp \
$(CORE_DIR)/RotateFilter.cpp \
$(CORE_DIR)/SaveStateManager.cpp \