mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
Movies: Save/load settings and cheats, fixed bugs/crashes, fixed todos
This commit is contained in:
parent
077e8db3d7
commit
aaf240c070
23 changed files with 1654 additions and 233 deletions
|
@ -14,7 +14,7 @@ public:
|
|||
{
|
||||
_debugger = debugger;
|
||||
|
||||
_needBreak = debugger->GetEmulator()->GetEmulationThreadId() != std::this_thread::get_id();
|
||||
_needBreak = !debugger->GetEmulator()->IsEmulationThread();
|
||||
|
||||
if(_needBreak) {
|
||||
//Only attempt to break if this is done in a thread other than the main emulation thread (and the debugger is active)
|
||||
|
|
|
@ -100,7 +100,7 @@ uint8_t NesApu::ReadRam(uint16_t addr)
|
|||
|
||||
uint8_t NesApu::PeekRam(uint16_t addr)
|
||||
{
|
||||
if(_console->GetEmulator()->GetEmulationThreadId() == std::this_thread::get_id()) {
|
||||
if(_console->GetEmulator()->IsEmulationThread()) {
|
||||
//Only run the Apu (to catch up) if we're running this in the emulation thread (not 100% accurate, but we can't run the Apu from any other thread without locking)
|
||||
Run();
|
||||
}
|
||||
|
|
|
@ -135,8 +135,8 @@ LoadRomResult NesConsole::LoadRom(VirtualFile& romFile)
|
|||
}
|
||||
}
|
||||
|
||||
//If in DB or a NES 2.0 file, auto-configure the inputs (if option is enabled)
|
||||
if(GetNesConfig().AutoConfigureInput && (romData.Info.IsInDatabase || romData.Info.IsNes20Header) && romData.Info.InputType != GameInputType::Unspecified) {
|
||||
if(GetNesConfig().AutoConfigureInput && romData.Info.InputType != GameInputType::Unspecified) {
|
||||
//Auto-configure the inputs (if option is enabled)
|
||||
InitializeInputDevices(romData.Info.InputType, romData.Info.System);
|
||||
}
|
||||
|
||||
|
@ -152,14 +152,6 @@ LoadRomResult NesConsole::LoadRom(VirtualFile& romFile)
|
|||
_controlManager.reset(new NesControlManager(this));
|
||||
}
|
||||
|
||||
/*
|
||||
//Temporarely disable battery saves to prevent battery files from being created for the wrong game (for Battle Box & Turbo File)
|
||||
_batteryManager->SetSaveEnabled(false);
|
||||
*/
|
||||
|
||||
//Re-enable battery saves
|
||||
/*_batteryManager->SetSaveEnabled(true);
|
||||
*/
|
||||
if(_hdData && (!_hdData->Tiles.empty() || !_hdData->Backgrounds.empty())) {
|
||||
_ppu.reset(new HdNesPpu(this, _hdData.get()));
|
||||
} else if(dynamic_cast<NsfMapper*>(_mapper.get())) {
|
||||
|
|
|
@ -70,7 +70,7 @@ void GameClient::ProcessNotification(ConsoleNotificationType type, void* paramet
|
|||
{
|
||||
if(type == ConsoleNotificationType::GameLoaded &&
|
||||
std::this_thread::get_id() != _clientThread->get_id() &&
|
||||
std::this_thread::get_id() != _emu->GetEmulationThreadId()
|
||||
!_emu->IsEmulationThread()
|
||||
) {
|
||||
//Disconnect if the client tried to manually load a game
|
||||
//A deadlock occurs if this is called from the emulation thread while a network message is being processed
|
||||
|
|
|
@ -65,7 +65,6 @@ Emulator::Emulator() :
|
|||
_isRunAheadFrame = false;
|
||||
_lockCounter = 0;
|
||||
_threadPaused = false;
|
||||
_lockCounter = 0;
|
||||
|
||||
_debugRequestCount = 0;
|
||||
_allowDebuggerRequest = true;
|
||||
|
@ -147,8 +146,6 @@ void Emulator::Run()
|
|||
}
|
||||
}
|
||||
|
||||
_movieManager->Stop();
|
||||
|
||||
_emulationThreadId = thread::id();
|
||||
|
||||
if(_runLock.IsLockedByCurrentThread()) {
|
||||
|
@ -283,6 +280,7 @@ void Emulator::Stop(bool sendNotification, bool preventRecentGameSave, bool save
|
|||
_notificationManager->SendNotification(ConsoleNotificationType::BeforeEmulationStop);
|
||||
}
|
||||
|
||||
_movieManager->Stop();
|
||||
_videoDecoder->StopThread();
|
||||
_videoRenderer->StopThread();
|
||||
_rewindManager.reset();
|
||||
|
@ -330,7 +328,7 @@ void Emulator::PowerCycle()
|
|||
|
||||
bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, bool forPowerCycle)
|
||||
{
|
||||
if(GetEmulationThreadId() == std::this_thread::get_id()) {
|
||||
if(IsEmulationThread()) {
|
||||
_threadPaused = true;
|
||||
}
|
||||
|
||||
|
@ -367,7 +365,11 @@ bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom,
|
|||
//Make sure the battery is saved to disk before we load another game (or reload the same game)
|
||||
_console->SaveBattery();
|
||||
}
|
||||
|
||||
|
||||
if(!forPowerCycle) {
|
||||
_movieManager->Stop();
|
||||
}
|
||||
|
||||
unique_ptr<IConsole> console;
|
||||
LoadRomResult result = LoadRomResult::UnknownType;
|
||||
TryLoadRom<NesConsole>(romFile, result, console);
|
||||
|
@ -1013,6 +1015,11 @@ thread::id Emulator::GetEmulationThreadId()
|
|||
return _emulationThreadId;
|
||||
}
|
||||
|
||||
bool Emulator::IsEmulationThread()
|
||||
{
|
||||
return _emulationThreadId == std::this_thread::get_id();
|
||||
}
|
||||
|
||||
void Emulator::RegisterMemory(MemoryType type, void* memory, uint32_t size)
|
||||
{
|
||||
_consoleMemory[(int)type] = { memory, size };
|
||||
|
|
|
@ -226,6 +226,7 @@ public:
|
|||
bool IsDebugging();
|
||||
|
||||
thread::id GetEmulationThreadId();
|
||||
bool IsEmulationThread();
|
||||
|
||||
void RegisterMemory(MemoryType type, void* memory, uint32_t size);
|
||||
ConsoleMemoryInfo GetMemory(MemoryType type);
|
||||
|
|
|
@ -45,6 +45,7 @@ std::unordered_map<string, string> MessageManager::_enResources = {
|
|||
{ "Lag", u8"Lag" },
|
||||
{ "Mapper", u8"Mapper: %1, SubMapper: %2" },
|
||||
{ "MovieEnded", u8"Movie ended." },
|
||||
{ "MovieStopped", u8"Movie stopped." },
|
||||
{ "MovieInvalid", u8"Invalid movie file." },
|
||||
{ "MovieMissingRom", u8"Missing ROM required (%1) to play movie." },
|
||||
{ "MovieNewerVersion", u8"Cannot load movies created by a more recent version of Mesen. Please download the latest version." },
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "Utilities/StringUtilities.h"
|
||||
#include "Utilities/HexUtilities.h"
|
||||
#include "Utilities/VirtualFile.h"
|
||||
#include "Utilities/magic_enum.hpp"
|
||||
#include "Utilities/Serializer.h"
|
||||
|
||||
MesenMovie::MesenMovie(Emulator* emu, bool forTest)
|
||||
{
|
||||
|
@ -30,19 +32,30 @@ MesenMovie::~MesenMovie()
|
|||
void MesenMovie::Stop()
|
||||
{
|
||||
if(_playing) {
|
||||
bool isEndOfMovie = _lastPollCounter >= _inputData.size();
|
||||
|
||||
if(!_forTest) {
|
||||
MessageManager::DisplayMessage("Movies", "MovieEnded");
|
||||
MessageManager::DisplayMessage("Movies", isEndOfMovie ? "MovieEnded" : "MovieStopped");
|
||||
}
|
||||
|
||||
if(_emu->GetSettings()->GetPreferences().PauseOnMovieEnd) {
|
||||
_emu->Pause();
|
||||
}
|
||||
if(!_emu->IsEmulationThread()) {
|
||||
EmuSettings* settings = _emu->GetSettings();
|
||||
if(isEndOfMovie && settings->GetPreferences().PauseOnMovieEnd) {
|
||||
_emu->Pause();
|
||||
}
|
||||
_emu->GetCheatManager()->SetCheats(_originalCheats);
|
||||
|
||||
_emu->GetCheatManager()->SetCheats(_originalCheats);
|
||||
Serializer backup(0, false, false);
|
||||
backup.LoadFrom(_emuSettingsBackup);
|
||||
backup.Stream(*settings, "", -1);
|
||||
}
|
||||
|
||||
_playing = false;
|
||||
}
|
||||
_emu->GetControlManager()->UnregisterInputProvider(this);
|
||||
|
||||
if(_emu->GetControlManager()) {
|
||||
_emu->GetControlManager()->UnregisterInputProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool MesenMovie::SetInput(BaseControlDevice *device)
|
||||
|
@ -59,6 +72,7 @@ bool MesenMovie::SetInput(BaseControlDevice *device)
|
|||
_deviceIndex = 0;
|
||||
}
|
||||
} else {
|
||||
//End of input data reached (movie end)
|
||||
_emu->GetMovieManager()->Stop();
|
||||
}
|
||||
return true;
|
||||
|
@ -114,40 +128,33 @@ bool MesenMovie::Play(VirtualFile &file)
|
|||
|
||||
_deviceIndex = 0;
|
||||
|
||||
ParseSettings(settingsData);
|
||||
auto emuLock = _emu->AcquireLock();
|
||||
|
||||
_emu->Lock();
|
||||
|
||||
_emu->GetBatteryManager()->SetBatteryProvider(shared_from_this());
|
||||
_emu->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
|
||||
ApplySettings();
|
||||
ParseSettings(settingsData);
|
||||
|
||||
//TODO
|
||||
//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);
|
||||
|
||||
BaseControlManager *controlManager = _emu->GetControlManager();
|
||||
if(controlManager) {
|
||||
//ControlManager can be empty if no game is loaded
|
||||
controlManager->SetPollCounter(0);
|
||||
if(LoadInt(_settings, MovieKeys::MovieFormatVersion, 0) < 2) {
|
||||
MessageManager::DisplayMessage("Movies", "MovieIncompatibleVersion");
|
||||
return false;
|
||||
}
|
||||
|
||||
//bool gameLoaded = LoadGame();
|
||||
//TODO
|
||||
//_console->GetSettings()->SetFlagState(EmulationFlags::AutoConfigureInput, autoConfigureInput);
|
||||
|
||||
/*if(!gameLoaded) {
|
||||
_console->Unlock();
|
||||
if(!ApplySettings(settingsData)) {
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
|
||||
_emu->GetBatteryManager()->SetBatteryProvider(shared_from_this());
|
||||
_emu->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
|
||||
|
||||
_emu->PowerCycle();
|
||||
|
||||
//Re-apply settings - power cycling can alter some (e.g auto-configure input types, etc.)
|
||||
ApplySettings(settingsData);
|
||||
|
||||
_originalCheats = _emu->GetCheatManager()->GetCheats();
|
||||
|
||||
controlManager->UpdateControlDevices();
|
||||
if(!_forTest) {
|
||||
_emu->PowerCycle();
|
||||
} else {
|
||||
BaseControlManager *controlManager = _emu->GetControlManager();
|
||||
|
||||
if(_forTest) {
|
||||
//TODO to validate test behavior
|
||||
controlManager->RegisterInputProvider(this);
|
||||
}
|
||||
|
||||
|
@ -156,17 +163,14 @@ bool MesenMovie::Play(VirtualFile &file)
|
|||
stringstream saveStateData;
|
||||
if(_reader->GetStream("SaveState.mss", saveStateData)) {
|
||||
if(!_emu->GetSaveStateManager()->LoadState(saveStateData, true)) {
|
||||
_emu->Resume();
|
||||
return false;
|
||||
} else {
|
||||
_emu->GetControlManager()->SetPollCounter(0);
|
||||
}
|
||||
}
|
||||
|
||||
controlManager->UpdateControlDevices();
|
||||
controlManager->SetPollCounter(0);
|
||||
_playing = true;
|
||||
|
||||
_emu->Unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -203,66 +207,30 @@ void MesenMovie::ParseSettings(stringstream &data)
|
|||
}
|
||||
}
|
||||
|
||||
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;*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void MesenMovie::ApplySettings()
|
||||
bool MesenMovie::ApplySettings(istream& settingsData)
|
||||
{
|
||||
EmuSettings* settings = _emu->GetSettings();
|
||||
EmulationConfig emuConfig = settings->GetEmulationConfig();
|
||||
|
||||
settingsData.clear();
|
||||
settingsData.seekg(0, std::ios::beg);
|
||||
Serializer s(0, false, true);
|
||||
s.LoadFrom(settingsData);
|
||||
|
||||
//TODO
|
||||
/*InputConfig inputConfig = settings->GetInputConfig();
|
||||
ConsoleType consoleType = {};
|
||||
s.Stream(consoleType, "emu.consoleType", -1);
|
||||
|
||||
inputConfig.Controllers[0].Type = FromString(LoadString(_settings, MovieKeys::Controller1), ControllerTypeNames, ControllerType::None);
|
||||
inputConfig.Controllers[1].Type = FromString(LoadString(_settings, MovieKeys::Controller2), ControllerTypeNames, ControllerType::None);
|
||||
inputConfig.Controllers[2].Type = FromString(LoadString(_settings, MovieKeys::Controller3), ControllerTypeNames, ControllerType::None);
|
||||
inputConfig.Controllers[3].Type = FromString(LoadString(_settings, MovieKeys::Controller4), ControllerTypeNames, ControllerType::None);
|
||||
inputConfig.Controllers[4].Type = FromString(LoadString(_settings, MovieKeys::Controller5), ControllerTypeNames, ControllerType::None);
|
||||
emuConfig.Region = FromString(LoadString(_settings, MovieKeys::Region), ConsoleRegionNames, ConsoleRegion::Ntsc);
|
||||
*/
|
||||
|
||||
//TODO
|
||||
/*
|
||||
if(!_forTest) {
|
||||
emuConfig.RamPowerOnState = FromString(LoadString(_settings, MovieKeys::RamPowerOnState), RamStateNames, RamState::AllOnes);
|
||||
if(consoleType != _emu->GetConsoleType()) {
|
||||
return false;
|
||||
}
|
||||
emuConfig.PpuExtraScanlinesAfterNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi);
|
||||
emuConfig.PpuExtraScanlinesBeforeNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi);
|
||||
emuConfig.GsuClockSpeed = LoadInt(_settings, MovieKeys::GsuClockSpeed, 100);*/
|
||||
|
||||
//settings->SetEmulationConfig(emuConfig);
|
||||
//settings->SetInputConfig(inputConfig);
|
||||
Serializer backup(0, true, false);
|
||||
backup.Stream(*settings, "", -1);
|
||||
backup.SaveTo(_emuSettingsBackup);
|
||||
|
||||
//Load settings
|
||||
s.Stream(*settings, "", -1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t MesenMovie::LoadInt(std::unordered_map<string, string> &settings, string name, uint32_t defaultValue)
|
||||
|
@ -324,12 +292,14 @@ bool MesenMovie::LoadCheat(string cheatData, CheatCode &code)
|
|||
vector<string> data = StringUtilities::Split(cheatData, ' ');
|
||||
|
||||
if(data.size() == 2) {
|
||||
//TODO
|
||||
//code.Address = HexUtilities::FromHex(data[0]);
|
||||
//code.Value = HexUtilities::FromHex(data[1]);
|
||||
return true;
|
||||
} else {
|
||||
MessageManager::Log("[Movie] Invalid cheat definition: " + cheatData);
|
||||
auto cheatType = magic_enum::enum_cast<CheatType>(data[0]);
|
||||
if(cheatType.has_value() && data[1].size() <= 15) {
|
||||
code.Type = cheatType.value();
|
||||
memcpy(code.Code, data[1].c_str(), data[1].size() + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MessageManager::Log("[Movie] Invalid cheat definition: " + cheatData);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ struct CheatCode;
|
|||
class MesenMovie : public IMovie, public INotificationListener, public IBatteryProvider, public std::enable_shared_from_this<MesenMovie>
|
||||
{
|
||||
private:
|
||||
Emulator* _emu;
|
||||
Emulator* _emu = nullptr;
|
||||
|
||||
VirtualFile _movieFile;
|
||||
unique_ptr<ZipReader> _reader;
|
||||
|
@ -23,15 +23,14 @@ private:
|
|||
vector<vector<string>> _inputData;
|
||||
vector<string> _cheats;
|
||||
vector<CheatCode> _originalCheats;
|
||||
std::unordered_map<string, string> _settings;
|
||||
stringstream _emuSettingsBackup;
|
||||
unordered_map<string, string> _settings;
|
||||
string _filename;
|
||||
bool _forTest;
|
||||
bool _forTest = false;
|
||||
|
||||
private:
|
||||
void ParseSettings(stringstream &data);
|
||||
void ApplySettings();
|
||||
bool LoadGame();
|
||||
void Stop();
|
||||
bool ApplySettings(istream& settingsData);
|
||||
|
||||
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);
|
||||
|
@ -45,6 +44,8 @@ public:
|
|||
virtual ~MesenMovie();
|
||||
|
||||
bool Play(VirtualFile &file) override;
|
||||
void Stop() override;
|
||||
|
||||
bool SetInput(BaseControlDevice* device) override;
|
||||
bool IsPlaying() override;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ void MovieManager::Record(RecordMovieOptions options)
|
|||
{
|
||||
shared_ptr<MovieRecorder> recorder(new MovieRecorder(_emu));
|
||||
if(recorder->Record(options)) {
|
||||
_recorder = recorder;
|
||||
_recorder.reset(recorder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ void MovieManager::Play(VirtualFile file, bool forTest)
|
|||
}
|
||||
|
||||
if(player && player->Play(file)) {
|
||||
_player = player;
|
||||
_player.reset(player);
|
||||
if(!forTest) {
|
||||
MessageManager::DisplayMessage("Movies", "MoviePlaying", file.GetFileName());
|
||||
}
|
||||
|
@ -48,6 +48,10 @@ void MovieManager::Play(VirtualFile file, bool forTest)
|
|||
|
||||
void MovieManager::Stop()
|
||||
{
|
||||
shared_ptr<IMovie> player = _player.lock();
|
||||
if(player) {
|
||||
player->Stop();
|
||||
}
|
||||
_player.reset();
|
||||
_recorder.reset();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Shared/Interfaces/IInputProvider.h"
|
||||
#include "Shared/Movies/MovieTypes.h"
|
||||
#include "Shared/Movies/MovieRecorder.h"
|
||||
#include "Utilities/safe_ptr.h"
|
||||
|
||||
class VirtualFile;
|
||||
class Emulator;
|
||||
|
@ -11,7 +12,8 @@ class Emulator;
|
|||
class IMovie : public IInputProvider
|
||||
{
|
||||
public:
|
||||
virtual bool Play(VirtualFile &file) = 0;
|
||||
virtual bool Play(VirtualFile& file) = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual bool IsPlaying() = 0;
|
||||
};
|
||||
|
||||
|
@ -19,8 +21,8 @@ class MovieManager
|
|||
{
|
||||
private:
|
||||
Emulator* _emu;
|
||||
shared_ptr<IMovie> _player;
|
||||
shared_ptr<MovieRecorder> _recorder;
|
||||
safe_ptr<IMovie> _player;
|
||||
safe_ptr<MovieRecorder> _recorder;
|
||||
|
||||
public:
|
||||
MovieManager(Emulator* emu);
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "Shared/Movies/MovieRecorder.h"
|
||||
#include "Shared/BatteryManager.h"
|
||||
#include "Shared/CheatManager.h"
|
||||
#include "Utilities/Serializer.h"
|
||||
#include "Utilities/magic_enum.hpp"
|
||||
|
||||
MovieRecorder::MovieRecorder(Emulator* emu)
|
||||
{
|
||||
|
@ -72,9 +74,6 @@ bool MovieRecorder::Record(RecordMovieOptions options)
|
|||
void MovieRecorder::GetGameSettings(stringstream &out)
|
||||
{
|
||||
EmuSettings* settings = _emu->GetSettings();
|
||||
EmulationConfig emuConfig = settings->GetEmulationConfig();
|
||||
InputConfig inputConfig = settings->GetInputConfig();
|
||||
|
||||
WriteString(out, MovieKeys::MesenVersion, settings->GetVersionString());
|
||||
WriteInt(out, MovieKeys::MovieFormatVersion, MovieRecorder::MovieFormatVersion);
|
||||
|
||||
|
@ -91,43 +90,18 @@ void MovieRecorder::GetGameSettings(stringstream &out)
|
|||
WriteString(out, MovieKeys::PatchedRomSha1, romFile.GetSha1Hash());
|
||||
}
|
||||
|
||||
ConsoleRegion region = _emu->GetRegion();
|
||||
switch(region) {
|
||||
case ConsoleRegion::Auto:
|
||||
case ConsoleRegion::Ntsc: WriteString(out, MovieKeys::Region, "NTSC"); break;
|
||||
Serializer s(0, true, true);
|
||||
ConsoleType consoleType = _emu->GetConsoleType();
|
||||
s.Stream(consoleType, "emu.consoleType", -1);
|
||||
s.Stream(*settings, "", -1);
|
||||
|
||||
case ConsoleRegion::Pal: WriteString(out, MovieKeys::Region, "PAL"); break;
|
||||
}
|
||||
std::stringstream settingsOut;
|
||||
s.SaveTo(settingsOut, 0);
|
||||
|
||||
/*WriteString(out, MovieKeys::Controller1, ControllerTypeNames[(int)inputConfig.Controllers[0].Type]);
|
||||
WriteString(out, MovieKeys::Controller2, ControllerTypeNames[(int)inputConfig.Controllers[1].Type]);
|
||||
WriteString(out, MovieKeys::Controller3, ControllerTypeNames[(int)inputConfig.Controllers[2].Type]);
|
||||
WriteString(out, MovieKeys::Controller4, ControllerTypeNames[(int)inputConfig.Controllers[3].Type]);
|
||||
WriteString(out, MovieKeys::Controller5, ControllerTypeNames[(int)inputConfig.Controllers[4].Type]);*/
|
||||
|
||||
switch(_emu->GetConsoleType()) {
|
||||
case ConsoleType::Snes: {
|
||||
SnesConfig snesCfg = settings->GetSnesConfig();
|
||||
WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, snesCfg.PpuExtraScanlinesBeforeNmi);
|
||||
WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, snesCfg.PpuExtraScanlinesAfterNmi);
|
||||
WriteInt(out, MovieKeys::GsuClockSpeed, snesCfg.GsuClockSpeed);
|
||||
|
||||
switch(snesCfg.RamPowerOnState) {
|
||||
case RamState::AllZeros: WriteString(out, MovieKeys::RamPowerOnState, "AllZeros"); break;
|
||||
case RamState::AllOnes: WriteString(out, MovieKeys::RamPowerOnState, "AllOnes"); break;
|
||||
case RamState::Random: WriteString(out, MovieKeys::RamPowerOnState, "AllOnes"); break; //TODO: Random memory isn't supported for movies yet
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
//TODO
|
||||
break;
|
||||
}
|
||||
out << settingsOut.str();
|
||||
|
||||
for(CheatCode &code : _emu->GetCheatManager()->GetCheats()) {
|
||||
//TODO
|
||||
out << "Cheat " << string(code.Code) << "\n";
|
||||
out << "Cheat " << magic_enum::enum_name(code.Type) << " " << string(code.Code) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,6 +186,8 @@ void MovieRecorder::ProcessNotification(ConsoleNotificationType type, void *para
|
|||
_emu->GetControlManager()->RegisterInputRecorder(this);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO history viewer
|
||||
/*
|
||||
bool MovieRecorder::CreateMovie(string movieFile, std::deque<RewindData> &data, uint32_t startPosition, uint32_t endPosition)
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ class Emulator;
|
|||
class MovieRecorder : public INotificationListener, public IInputRecorder, public IBatteryRecorder, public IBatteryProvider, public std::enable_shared_from_this<MovieRecorder>
|
||||
{
|
||||
private:
|
||||
static const uint32_t MovieFormatVersion = 1;
|
||||
static const uint32_t MovieFormatVersion = 2;
|
||||
|
||||
Emulator* _emu;
|
||||
string _filename;
|
||||
|
|
|
@ -17,28 +17,6 @@ struct RecordMovieOptions
|
|||
RecordMovieFrom RecordFrom = RecordMovieFrom::StartWithoutSaveData;
|
||||
};
|
||||
|
||||
const vector<string> ConsoleRegionNames = {
|
||||
"Auto",
|
||||
"NTSC",
|
||||
"PAL"
|
||||
};
|
||||
|
||||
//TODO
|
||||
const vector<string> ControllerTypeNames = {
|
||||
"None",
|
||||
"SnesController",
|
||||
"SnesMouse",
|
||||
"SuperScope",
|
||||
"Multitap",
|
||||
"NesController"
|
||||
};
|
||||
|
||||
const vector<string> RamStateNames = {
|
||||
"AllZeros",
|
||||
"AllOnes",
|
||||
"Random"
|
||||
};
|
||||
|
||||
namespace MovieKeys
|
||||
{
|
||||
constexpr const char* MesenVersion = "MesenVersion";
|
||||
|
@ -48,16 +26,4 @@ namespace MovieKeys
|
|||
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* Controller5 = "Controller5";
|
||||
constexpr const char* ExtraScanlinesBeforeNmi = "ExtraScanlinesBeforeNmi";
|
||||
constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi";
|
||||
constexpr const char* RamPowerOnState = "RamPowerOnState";
|
||||
constexpr const char* InputPollScanline = "InputPollScanline";
|
||||
constexpr const char* GsuClockSpeed = "GsuClockSpeed";
|
||||
};
|
|
@ -160,7 +160,6 @@ struct AudioConfig
|
|||
uint32_t AudioPlayerSilenceDelay = 3;
|
||||
};
|
||||
|
||||
//Update ControllerTypeNames when changing this
|
||||
enum class ControllerType
|
||||
{
|
||||
None,
|
||||
|
|
|
@ -537,11 +537,8 @@ namespace Mesen.ViewModels
|
|||
new MainMenuAction() {
|
||||
ActionType = ActionType.Stop,
|
||||
IsEnabled = () => IsGameRunning && (RecordApi.MovieRecording() || RecordApi.MoviePlaying()),
|
||||
OnClick = async () => {
|
||||
string? filename = await FileDialogHelper.OpenFile(ConfigManager.MovieFolder, wnd, FileDialogHelper.MesenMovieExt);
|
||||
if(filename != null) {
|
||||
RecordApi.MovieStop();
|
||||
}
|
||||
OnClick = () => {
|
||||
RecordApi.MovieStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,10 +61,11 @@ namespace Mesen.Windows
|
|||
new("nes_ntsc", "blargg", "LGPL", "http://slack.net/~ant/"),
|
||||
new("snes_ntsc", "blargg", "LGPL", "http://slack.net/~ant/"),
|
||||
new("stb_vorbis", "", "Public domain", "https://github.com/nothings/stb"),
|
||||
new("emu2413.c (Mitsukata Okazaki)", "Mitsukata Okazaki", "?", ""),
|
||||
new("emu2413", "Mitsukata Okazaki", "MIT", "https://github.com/digital-sound-antiques/emu2413"),
|
||||
new("SDD-1 Decomp. (Andreas Naive)", "Andreas Naive", "Public domain", ""),
|
||||
new("LED Icons", "", "CC BY 4.0", "http://led24.de"),
|
||||
new("SDL2", "", "zlib", "https://www.libsdl.org/")
|
||||
new("SDL2", "", "zlib", "https://www.libsdl.org/"),
|
||||
new("magic_enum", "", "MIT", "https://github.com/Neargye/magic_enum")
|
||||
};
|
||||
|
||||
LibraryList.Sort((a, b) => a.Name.CompareTo(b.Name));
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
#include "ISerializable.h"
|
||||
#include "miniz.h"
|
||||
|
||||
Serializer::Serializer(uint32_t version, bool forSave)
|
||||
Serializer::Serializer(uint32_t version, bool forSave, bool useTextFormat)
|
||||
{
|
||||
_version = version;
|
||||
_saving = forSave;
|
||||
_useTextFormat = useTextFormat;
|
||||
if(forSave) {
|
||||
_data.reserve(0x50000);
|
||||
}
|
||||
|
@ -19,6 +20,10 @@ bool Serializer::LoadFrom(istream &file)
|
|||
return false;
|
||||
}
|
||||
|
||||
if(_useTextFormat) {
|
||||
return LoadFromTextFormat(file);
|
||||
}
|
||||
|
||||
char value = 0;
|
||||
file.get(value);
|
||||
bool isCompressed = value == 1;
|
||||
|
@ -95,24 +100,83 @@ bool Serializer::LoadFrom(istream &file)
|
|||
return _values.size() > 0;
|
||||
}
|
||||
|
||||
bool Serializer::LoadFromTextFormat(istream& file)
|
||||
{
|
||||
uint32_t pos = (uint32_t)file.tellg();
|
||||
file.seekg(0, std::ios::end);
|
||||
uint32_t stateSize = (uint32_t)file.tellg() - pos;
|
||||
file.seekg(pos, std::ios::beg);
|
||||
|
||||
_data = vector<uint8_t>(stateSize, 0);
|
||||
file.read((char*)_data.data(), stateSize);
|
||||
|
||||
uint32_t size = (uint32_t)_data.size();
|
||||
uint32_t i = 0;
|
||||
string key;
|
||||
while(i < size) {
|
||||
key.clear();
|
||||
for(uint32_t j = i; j < size; j++) {
|
||||
if(_data[j] == ' ') {
|
||||
key.append((char*)&_data[i], j - i);
|
||||
break;
|
||||
} else if(_data[j] < ' ' || _data[j] >= 127) {
|
||||
//invalid characters in key, state is invalid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(key.empty()) {
|
||||
//invalid
|
||||
return false;
|
||||
}
|
||||
|
||||
i += (uint32_t)key.size() + 1;
|
||||
if(i >= size - 4) {
|
||||
//invalid
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t valueSize = 0;
|
||||
for(uint32_t j = i; j < size; j++) {
|
||||
if(_data[j] == '\n') {
|
||||
valueSize = j - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i + valueSize > size) {
|
||||
//invalid
|
||||
return false;
|
||||
}
|
||||
|
||||
_values.emplace(key, SerializeValue(&_data[i], valueSize));
|
||||
|
||||
i += valueSize + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Serializer::SaveTo(ostream& file, int compressionLevel)
|
||||
{
|
||||
bool isCompressed = compressionLevel > 0;
|
||||
file.put((char)isCompressed);
|
||||
|
||||
if(isCompressed) {
|
||||
unsigned long compressedSize = compressBound((unsigned long)_data.size());
|
||||
uint8_t* compressedData = new uint8_t[compressedSize];
|
||||
compress2(compressedData, &compressedSize, (unsigned char*)_data.data(), (unsigned long)_data.size(), compressionLevel);
|
||||
|
||||
uint32_t size = (uint32_t)compressedSize;
|
||||
uint32_t originalSize = (uint32_t)_data.size();
|
||||
file.write((char*)&originalSize, sizeof(uint32_t));
|
||||
file.write((char*)&size, sizeof(uint32_t));
|
||||
file.write((char*)compressedData, compressedSize);
|
||||
delete[] compressedData;
|
||||
} else {
|
||||
if(_useTextFormat) {
|
||||
file.write((char*)_data.data(), _data.size());
|
||||
} else {
|
||||
bool isCompressed = compressionLevel > 0;
|
||||
file.put((char)isCompressed);
|
||||
|
||||
if(isCompressed) {
|
||||
unsigned long compressedSize = compressBound((unsigned long)_data.size());
|
||||
uint8_t* compressedData = new uint8_t[compressedSize];
|
||||
compress2(compressedData, &compressedSize, (unsigned char*)_data.data(), (unsigned long)_data.size(), compressionLevel);
|
||||
|
||||
uint32_t size = (uint32_t)compressedSize;
|
||||
uint32_t originalSize = (uint32_t)_data.size();
|
||||
file.write((char*)&originalSize, sizeof(uint32_t));
|
||||
file.write((char*)&size, sizeof(uint32_t));
|
||||
file.write((char*)compressedData, compressedSize);
|
||||
delete[] compressedData;
|
||||
} else {
|
||||
file.write((char*)_data.data(), _data.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,4 +196,3 @@ void Serializer::PopNamePrefix()
|
|||
{
|
||||
_prefixes.pop_back();
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "Utilities/ISerializable.h"
|
||||
#include "Utilities/FastString.h"
|
||||
#include "Utilities/magic_enum.hpp"
|
||||
|
||||
class Serializer;
|
||||
|
||||
|
@ -42,8 +43,11 @@ private:
|
|||
|
||||
uint32_t _version = 0;
|
||||
bool _saving = false;
|
||||
bool _useTextFormat = false;
|
||||
|
||||
private:
|
||||
bool LoadFromTextFormat(istream& file);
|
||||
|
||||
string GetKey(const char* name, int index)
|
||||
{
|
||||
string key;
|
||||
|
@ -110,8 +114,45 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void WriteTextFormat(string key, T& value)
|
||||
{
|
||||
//Write key
|
||||
_data.insert(_data.end(), key.begin(), key.end());
|
||||
_data.push_back(' ');
|
||||
if constexpr(std::is_enum<T>::value) {
|
||||
auto enumStr = magic_enum::enum_name(value);
|
||||
_data.insert(_data.end(), enumStr.begin(), enumStr.end());
|
||||
} else if constexpr(std::is_same<T, bool>::value) {
|
||||
string boolStr = value ? "true" : "false";
|
||||
_data.insert(_data.end(), boolStr.begin(), boolStr.end());
|
||||
} else {
|
||||
auto valueStr = std::to_string(value);
|
||||
_data.insert(_data.end(), valueStr.begin(), valueStr.end());
|
||||
}
|
||||
_data.push_back('\n');
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ReadTextFormat(SerializeValue& savedValue, T& value)
|
||||
{
|
||||
string textValue(savedValue.DataPtr, savedValue.DataPtr + savedValue.Size);
|
||||
if constexpr(std::is_enum<T>::value) {
|
||||
auto enumValue = magic_enum::enum_cast<T>(textValue);
|
||||
if(enumValue.has_value()) {
|
||||
value = enumValue.value();
|
||||
}
|
||||
} else if constexpr(std::is_same<T, bool>::value) {
|
||||
value = textValue == "true";
|
||||
} else {
|
||||
if(textValue.find_first_not_of("0123456789") == string::npos) {
|
||||
value = (T)std::stol(textValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Serializer(uint32_t version, bool forSave);
|
||||
Serializer(uint32_t version, bool forSave, bool useTextFormat = false);
|
||||
|
||||
uint32_t GetVersion() { return _version; }
|
||||
bool IsSaving() { return _saving; }
|
||||
|
@ -138,23 +179,31 @@ public:
|
|||
}
|
||||
|
||||
if(_saving) {
|
||||
//Write key
|
||||
_data.insert(_data.end(), key.begin(), key.end());
|
||||
_data.push_back(0);
|
||||
if(_useTextFormat) {
|
||||
WriteTextFormat(key, value);
|
||||
} else {
|
||||
//Write key
|
||||
_data.insert(_data.end(), key.begin(), key.end());
|
||||
_data.push_back(0);
|
||||
|
||||
//Write value size
|
||||
WriteValue((uint32_t)sizeof(T));
|
||||
//Write value size
|
||||
WriteValue((uint32_t)sizeof(T));
|
||||
|
||||
//Write value
|
||||
WriteValue(value);
|
||||
//Write value
|
||||
WriteValue(value);
|
||||
}
|
||||
} else {
|
||||
auto result = _values.find(key);
|
||||
if(result != _values.end()) {
|
||||
SerializeValue& savedValue = result->second;
|
||||
if(savedValue.Size >= sizeof(T)) {
|
||||
ReadValue(value, savedValue.DataPtr);
|
||||
if(_useTextFormat) {
|
||||
ReadTextFormat(savedValue, value);
|
||||
} else {
|
||||
value = (T)0;
|
||||
if(savedValue.Size >= sizeof(T)) {
|
||||
ReadValue(value, savedValue.DataPtr);
|
||||
} else {
|
||||
value = (T)0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = (T)0;
|
||||
|
|
|
@ -202,6 +202,7 @@
|
|||
<ClInclude Include="HQX\hqx.h" />
|
||||
<ClInclude Include="ISerializable.h" />
|
||||
<ClInclude Include="KreedSaiEagle\SaiEagle.h" />
|
||||
<ClInclude Include="magic_enum.hpp" />
|
||||
<ClInclude Include="md5.h" />
|
||||
<ClInclude Include="miniz.h" />
|
||||
<ClInclude Include="AutoResetEvent.h" />
|
||||
|
|
|
@ -172,6 +172,7 @@
|
|||
<ClInclude Include="CRC32.h" />
|
||||
<ClInclude Include="md5.h" />
|
||||
<ClInclude Include="sha1.h" />
|
||||
<ClInclude Include="magic_enum.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="xBRZ\xbrz.cpp">
|
||||
|
|
1378
Utilities/magic_enum.hpp
Normal file
1378
Utilities/magic_enum.hpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -52,3 +52,15 @@ public:
|
|||
return _ptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool operator==(const safe_ptr<T>& ptr, nullptr_t)
|
||||
{
|
||||
return !(bool)ptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool operator!=(const safe_ptr<T>& ptr, nullptr_t)
|
||||
{
|
||||
return (bool)ptr;
|
||||
}
|
Loading…
Add table
Reference in a new issue