diff --git a/Core/BaseRenderer.cpp b/Core/BaseRenderer.cpp index b1fd9394..3c8d2f5f 100644 --- a/Core/BaseRenderer.cpp +++ b/Core/BaseRenderer.cpp @@ -6,11 +6,11 @@ #include "VideoDecoder.h" #include "PPU.h" -BaseRenderer::BaseRenderer(shared_ptr console) +BaseRenderer::BaseRenderer(shared_ptr console, bool registerAsMessageManager) { _console = console; - if(console->IsMaster()) { + if(registerAsMessageManager) { //Only display messages on the master CPU's screen MessageManager::RegisterMessageManager(this); } diff --git a/Core/BaseRenderer.h b/Core/BaseRenderer.h index 6eed5ceb..fc7b8073 100644 --- a/Core/BaseRenderer.h +++ b/Core/BaseRenderer.h @@ -27,7 +27,7 @@ protected: uint32_t _screenHeight = 0; uint32_t _renderedFrameCount = 0; - BaseRenderer(shared_ptr console); + BaseRenderer(shared_ptr console, bool registerAsMessageManager); bool IsMessageShown(); diff --git a/Core/Console.cpp b/Core/Console.cpp index 517b7dac..aec7856a 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -44,6 +44,7 @@ #include "VideoRenderer.h" #include "DebugHud.h" #include "NotificationManager.h" +#include "HistoryViewer.h" Console::Console(shared_ptr master) { @@ -480,6 +481,11 @@ RewindManager* Console::GetRewindManager() return _rewindManager.get(); } +HistoryViewer* Console::GetHistoryViewer() +{ + return _historyViewer.get(); +} + VirtualFile Console::GetRomPath() { return static_cast(_romFilepath); @@ -707,6 +713,9 @@ void Console::Run() } lastFrameTimer.Reset(); + if(_historyViewer) { + _historyViewer->ProcessEndOfFrame(); + } _rewindManager->ProcessEndOfFrame(); EmulationSettings::DisableOverclocking(_disableOcNextFrame || NsfMapper::GetInstance()); _disableOcNextFrame = false; @@ -724,7 +733,7 @@ void Console::Run() _runLock.Acquire(); } - bool paused = EmulationSettings::IsPaused(); + bool paused = EmulationSettings::IsPaused() || _paused; if(paused && !_stop) { _notificationManager->SendNotification(ConsoleNotificationType::GamePaused); @@ -741,7 +750,7 @@ void Console::Run() while(paused && !_stop) { //Sleep until emulation is resumed std::this_thread::sleep_for(std::chrono::duration(30)); - paused = EmulationSettings::IsPaused(); + paused = EmulationSettings::IsPaused() || _paused; } if(EmulationSettings::CheckFlag(EmulationFlags::DebuggerWindowEnabled)) { @@ -792,6 +801,7 @@ void Console::Run() MessageManager::DisplayMessage("Error", "GameCrash", ex.what()); } + _paused = false; _running = false; _notificationManager->SendNotification(ConsoleNotificationType::BeforeEmulationStop); @@ -853,6 +863,16 @@ bool Console::IsPaused() } } +bool Console::GetPauseStatus() +{ + return _paused; +} + +void Console::SetPauseStatus(bool paused) +{ + _paused = paused; +} + void Console::UpdateNesModel(bool sendNotification) { bool configChanged = false; @@ -1234,6 +1254,18 @@ bool Console::IsRecordingTapeFile() return false; } +void Console::CopyRewindData(shared_ptr sourceConsole) +{ + sourceConsole->Pause(); + Pause(); + + _historyViewer.reset(new HistoryViewer(shared_from_this())); + sourceConsole->_rewindManager->CopyHistory(_historyViewer); + + Resume(); + sourceConsole->Resume(); +} + uint8_t* Console::GetRamBuffer(DebugMemoryType memoryType, uint32_t &size, int32_t &startAddr) { //Only used by libretro port for achievements - should not be used by anything else. diff --git a/Core/Console.h b/Core/Console.h index a8f71a08..2da70499 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -7,6 +7,7 @@ class BaseMapper; class RewindManager; +class HistoryViewer; class APU; class CPU; class PPU; @@ -46,6 +47,8 @@ private: SimpleLock _debuggerLock; shared_ptr _rewindManager; + shared_ptr _historyViewer; + shared_ptr _cpu; shared_ptr _ppu; shared_ptr _apu; @@ -78,6 +81,7 @@ private: string _romFilepath; string _patchFilename; + bool _paused = false; bool _stop = false; bool _running = false; int32_t _stopCode = 0; @@ -119,7 +123,8 @@ public: ControlManager* GetControlManager(); MemoryManager* GetMemoryManager(); CheatManager* GetCheatManager(); - RewindManager * GetRewindManager(); + RewindManager* GetRewindManager(); + HistoryViewer* GetHistoryViewer(); bool LoadMatchingRom(string romName, HashInfo hashInfo); string FindMatchingRom(string romName, HashInfo hashInfo); @@ -188,6 +193,9 @@ public: bool IsRunning(); bool IsPaused(); + bool GetPauseStatus(); + void SetPauseStatus(bool paused); + void SetNextFrameOverclockStatus(bool disabled); bool IsDebuggerAttached(); @@ -197,6 +205,8 @@ public: void StartRecordingHdPack(string saveFolder, ScaleFilterType filterType, uint32_t scale, uint32_t flags, uint32_t chrRamBankSize); void StopRecordingHdPack(); + + void CopyRewindData(shared_ptr sourceConsole); uint8_t* GetRamBuffer(DebugMemoryType memoryType, uint32_t &size, int32_t &startAddr); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 5cdae6d2..0db3a1ed 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -537,6 +537,7 @@ + @@ -970,6 +971,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index bc186139..62177a8b 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1468,6 +1468,9 @@ Nes\Mappers\Unif + + Rewinder + @@ -1755,5 +1758,8 @@ Nes\RomLoader + + Rewinder + \ No newline at end of file diff --git a/Core/HistoryViewer.cpp b/Core/HistoryViewer.cpp new file mode 100644 index 00000000..fa1dbde6 --- /dev/null +++ b/Core/HistoryViewer.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "HistoryViewer.h" +#include "RewindData.h" +#include "Console.h" +#include "BaseControlDevice.h" +#include "SoundMixer.h" +#include "NotificationManager.h" + +HistoryViewer::HistoryViewer(shared_ptr console) +{ + _console = console; + _position = 0; + _pollCounter = 0; +} + +void HistoryViewer::SetHistoryData(std::deque &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); +} + +uint32_t HistoryViewer::GetPosition() +{ + return _position; +} + +void HistoryViewer::SeekTo(uint32_t seekPosition) +{ + //Seek to the specified position, in seconds + uint32_t index = (uint32_t)(seekPosition * 60 / HistoryViewer::BufferSize); + if(index < _history.size()) { + _console->Pause(); + + bool wasPaused = _console->GetPauseStatus(); + _console->SetPauseStatus(false); + _position = index; + RewindData rewindData = _history[_position]; + rewindData.LoadState(_console); + + _console->GetSoundMixer()->StopAudio(true); + _pollCounter = 0; + _console->SetPauseStatus(wasPaused); + + _console->Resume(); + } +} + +bool HistoryViewer::SetInput(BaseControlDevice *device) +{ + uint8_t port = device->GetPort(); + std::deque &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->SetPauseStatus(true); + return; + } + + RewindData rewindData = _history[_position]; + rewindData.LoadState(_console); + } +} diff --git a/Core/HistoryViewer.h b/Core/HistoryViewer.h new file mode 100644 index 00000000..c1f60500 --- /dev/null +++ b/Core/HistoryViewer.h @@ -0,0 +1,32 @@ +#pragma once +#include "stdafx.h" +#include +#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; + std::deque _history; + uint32_t _position; + uint32_t _pollCounter; + +public: + HistoryViewer(shared_ptr console); + + void SetHistoryData(std::deque &history); + + uint32_t GetHistoryLength(); + uint32_t GetPosition(); + void SeekTo(uint32_t seekPosition); + + void ProcessEndOfFrame(); + + // Inherited via IInputProvider + virtual bool SetInput(BaseControlDevice * device) override; +}; \ No newline at end of file diff --git a/Core/RewindManager.cpp b/Core/RewindManager.cpp index 7208a9e0..4eab812c 100644 --- a/Core/RewindManager.cpp +++ b/Core/RewindManager.cpp @@ -5,6 +5,7 @@ #include "VideoRenderer.h" #include "SoundMixer.h" #include "BaseControlDevice.h" +#include "HistoryViewer.h" RewindManager::RewindManager(shared_ptr console) { @@ -331,6 +332,11 @@ void RewindManager::RewindSeconds(uint32_t seconds) } } +void RewindManager::CopyHistory(shared_ptr destHistoryViewer) +{ + destHistoryViewer->SetHistoryData(_history); +} + void RewindManager::SendFrame(void * frameBuffer, uint32_t width, uint32_t height, bool forRewind) { ProcessFrame(frameBuffer, width, height, forRewind); diff --git a/Core/RewindManager.h b/Core/RewindManager.h index 334b6319..5201cc70 100644 --- a/Core/RewindManager.h +++ b/Core/RewindManager.h @@ -7,6 +7,7 @@ #include "IInputRecorder.h" class Console; +class HistoryViewer; enum class RewindState { @@ -64,6 +65,8 @@ public: bool IsStepBack(); void RewindSeconds(uint32_t seconds); + void CopyHistory(shared_ptr destHistoryViewer); + void SendFrame(void *frameBuffer, uint32_t width, uint32_t height, bool forRewind); bool SendAudio(int16_t *soundBuffer, uint32_t sampleCount, uint32_t sampleRate); }; \ No newline at end of file diff --git a/GUI.NET/Forms/frmHistoryViewer.Designer.cs b/GUI.NET/Forms/frmHistoryViewer.Designer.cs new file mode 100644 index 00000000..0688c5d9 --- /dev/null +++ b/GUI.NET/Forms/frmHistoryViewer.Designer.cs @@ -0,0 +1,132 @@ +namespace Mesen.GUI.Forms +{ + partial class frmHistoryViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.ctrlRenderer = new System.Windows.Forms.Panel(); + this.trkPosition = new System.Windows.Forms.TrackBar(); + this.btnPausePlay = new System.Windows.Forms.Button(); + this.lblPosition = new System.Windows.Forms.Label(); + this.tmrUpdatePosition = new System.Windows.Forms.Timer(this.components); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trkPosition)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.ctrlRenderer, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.trkPosition, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.btnPausePlay, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.lblPosition, 2, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(520, 540); + this.tableLayoutPanel1.TabIndex = 0; + // + // ctrlRenderer + // + this.ctrlRenderer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.tableLayoutPanel1.SetColumnSpan(this.ctrlRenderer, 3); + this.ctrlRenderer.Location = new System.Drawing.Point(3, 3); + this.ctrlRenderer.Name = "ctrlRenderer"; + this.ctrlRenderer.Size = new System.Drawing.Size(514, 482); + this.ctrlRenderer.TabIndex = 0; + // + // trkPosition + // + this.trkPosition.Dock = System.Windows.Forms.DockStyle.Top; + this.trkPosition.LargeChange = 10; + this.trkPosition.Location = new System.Drawing.Point(56, 492); + this.trkPosition.Name = "trkPosition"; + this.trkPosition.Size = new System.Drawing.Size(406, 45); + this.trkPosition.TabIndex = 1; + this.trkPosition.TickFrequency = 10; + this.trkPosition.TickStyle = System.Windows.Forms.TickStyle.Both; + this.trkPosition.ValueChanged += new System.EventHandler(this.trkPosition_ValueChanged); + // + // btnPausePlay + // + this.btnPausePlay.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.btnPausePlay.Image = global::Mesen.GUI.Properties.Resources.Play; + this.btnPausePlay.Location = new System.Drawing.Point(3, 496); + this.btnPausePlay.Name = "btnPausePlay"; + this.btnPausePlay.Size = new System.Drawing.Size(47, 36); + this.btnPausePlay.TabIndex = 2; + this.btnPausePlay.Click += new System.EventHandler(this.btnPausePlay_Click); + // + // lblPosition + // + this.lblPosition.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.lblPosition.AutoSize = true; + this.lblPosition.Location = new System.Drawing.Point(468, 508); + this.lblPosition.MinimumSize = new System.Drawing.Size(49, 13); + this.lblPosition.Name = "lblPosition"; + this.lblPosition.Size = new System.Drawing.Size(49, 13); + this.lblPosition.TabIndex = 3; + this.lblPosition.Text = "77:77:77"; + this.lblPosition.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // tmrUpdatePosition + // + this.tmrUpdatePosition.Interval = 500; + this.tmrUpdatePosition.Tick += new System.EventHandler(this.tmrUpdatePosition_Tick); + // + // frmHistoryViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(520, 540); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "frmHistoryViewer"; + this.Text = "History Viewer"; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trkPosition)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Panel ctrlRenderer; + private System.Windows.Forms.TrackBar trkPosition; + private System.Windows.Forms.Button btnPausePlay; + private System.Windows.Forms.Timer tmrUpdatePosition; + private System.Windows.Forms.Label lblPosition; + } +} \ No newline at end of file diff --git a/GUI.NET/Forms/frmHistoryViewer.cs b/GUI.NET/Forms/frmHistoryViewer.cs new file mode 100644 index 00000000..2b1c9fe1 --- /dev/null +++ b/GUI.NET/Forms/frmHistoryViewer.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public partial class frmHistoryViewer : BaseForm + { + private Thread _emuThread; + private bool _paused = true; + + public frmHistoryViewer() + { + InitializeComponent(); + + InteropEmu.InitializeHistoryViewer(this.Handle, ctrlRenderer.Handle); + trkPosition.Maximum = (int)(InteropEmu.GetHistoryViewerTotalFrameCount() / 60); + UpdatePositionLabel(0); + InteropEmu.SetHistoryViewerPauseStatus(true); + StartEmuThread(); + tmrUpdatePosition.Start(); + } + + protected override void OnClosing(CancelEventArgs e) + { + tmrUpdatePosition.Stop(); + InteropEmu.ReleaseHistoryViewer(); + base.OnClosing(e); + } + + private void StartEmuThread() + { + if(_emuThread == null) { + _emuThread = new Thread(() => { + try { + InteropEmu.RunHistoryViewer(); + _emuThread = null; + } catch(Exception ex) { + MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, ex.ToString()); + _emuThread = null; + } + }); + _emuThread.Start(); + } + } + + private void btnPausePlay_Click(object sender, EventArgs e) + { + if(trkPosition.Value == trkPosition.Maximum) { + InteropEmu.SetHistoryViewerPosition(0); + } + InteropEmu.SetHistoryViewerPauseStatus(!_paused); + } + + private void trkPosition_ValueChanged(object sender, EventArgs e) + { + InteropEmu.SetHistoryViewerPosition((UInt32)trkPosition.Value); + } + + private void tmrUpdatePosition_Tick(object sender, EventArgs e) + { + _paused = InteropEmu.GetHistoryViewerPauseStatus(); + if(_paused) { + btnPausePlay.Image = Properties.Resources.Play; + } else { + btnPausePlay.Image = Properties.Resources.Pause; + } + + UInt32 positionInSeconds = InteropEmu.GetHistoryViewerPosition() / 2; + UpdatePositionLabel(positionInSeconds); + + if(positionInSeconds <= trkPosition.Maximum) { + trkPosition.ValueChanged -= trkPosition_ValueChanged; + trkPosition.Value = (int)positionInSeconds; + trkPosition.ValueChanged += trkPosition_ValueChanged; + } + } + + private void UpdatePositionLabel(uint positionInSeconds) + { + TimeSpan currentPosition = new TimeSpan(0, 0, (int)positionInSeconds); + TimeSpan totalLength = new TimeSpan(0, 0, trkPosition.Maximum); + lblPosition.Text = ( + currentPosition.Minutes.ToString("00") + ":" + currentPosition.Seconds.ToString("00") + + " / " + + totalLength.Minutes.ToString("00") + ":" + totalLength.Seconds.ToString("00") + ); + } + } +} diff --git a/GUI.NET/Forms/frmHistoryViewer.resx b/GUI.NET/Forms/frmHistoryViewer.resx new file mode 100644 index 00000000..2e85e889 --- /dev/null +++ b/GUI.NET/Forms/frmHistoryViewer.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 107, 17 + + \ No newline at end of file diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index ec3cad4e..45bdf88c 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -66,6 +66,8 @@ namespace Mesen.GUI.Forms this.mnuGameConfig = new System.Windows.Forms.ToolStripMenuItem(); this.mnuInsertCoin1 = new System.Windows.Forms.ToolStripMenuItem(); this.mnuInsertCoin2 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInsertCoin3 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuInsertCoin4 = new System.Windows.Forms.ToolStripMenuItem(); this.sepBarcode = new System.Windows.Forms.ToolStripSeparator(); this.mnuInputBarcode = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTapeRecorder = new System.Windows.Forms.ToolStripMenuItem(); @@ -211,8 +213,7 @@ namespace Mesen.GUI.Forms this.mnuReportBug = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuInsertCoin3 = new System.Windows.Forms.ToolStripMenuItem(); - this.mnuInsertCoin4 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHistoryViewer = new System.Windows.Forms.ToolStripMenuItem(); this.panelRenderer.SuspendLayout(); this.panelInfo.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picIcon)).BeginInit(); @@ -541,6 +542,22 @@ namespace Mesen.GUI.Forms this.mnuInsertCoin2.Text = "Insert Coin (2)"; this.mnuInsertCoin2.Visible = false; // + // mnuInsertCoin3 + // + this.mnuInsertCoin3.Image = global::Mesen.GUI.Properties.Resources.coins; + this.mnuInsertCoin3.Name = "mnuInsertCoin3"; + this.mnuInsertCoin3.Size = new System.Drawing.Size(221, 22); + this.mnuInsertCoin3.Text = "Insert Coin (3 - DualSystem)"; + this.mnuInsertCoin3.Visible = false; + // + // mnuInsertCoin4 + // + this.mnuInsertCoin4.Image = global::Mesen.GUI.Properties.Resources.coins; + this.mnuInsertCoin4.Name = "mnuInsertCoin4"; + this.mnuInsertCoin4.Size = new System.Drawing.Size(221, 22); + this.mnuInsertCoin4.Text = "Insert Coin (4 - DualSystem)"; + this.mnuInsertCoin4.Visible = false; + // // sepBarcode // this.sepBarcode.Name = "sepBarcode"; @@ -627,7 +644,7 @@ namespace Mesen.GUI.Forms this.mnuShowFPS}); this.mnuEmulationSpeed.Image = global::Mesen.GUI.Properties.Resources.Speed; this.mnuEmulationSpeed.Name = "mnuEmulationSpeed"; - this.mnuEmulationSpeed.Size = new System.Drawing.Size(135, 22); + this.mnuEmulationSpeed.Size = new System.Drawing.Size(152, 22); this.mnuEmulationSpeed.Text = "Speed"; this.mnuEmulationSpeed.DropDownOpening += new System.EventHandler(this.mnuEmulationSpeed_DropDownOpening); // @@ -721,7 +738,7 @@ namespace Mesen.GUI.Forms this.mnuFullscreen}); this.mnuVideoScale.Image = global::Mesen.GUI.Properties.Resources.Fullscreen; this.mnuVideoScale.Name = "mnuVideoScale"; - this.mnuVideoScale.Size = new System.Drawing.Size(135, 22); + this.mnuVideoScale.Size = new System.Drawing.Size(152, 22); this.mnuVideoScale.Text = "Video Size"; // // mnuScale1x @@ -809,7 +826,7 @@ namespace Mesen.GUI.Forms this.mnuBilinearInterpolation}); this.mnuVideoFilter.Image = global::Mesen.GUI.Properties.Resources.VideoFilter; this.mnuVideoFilter.Name = "mnuVideoFilter"; - this.mnuVideoFilter.Size = new System.Drawing.Size(135, 22); + this.mnuVideoFilter.Size = new System.Drawing.Size(152, 22); this.mnuVideoFilter.Text = "Video Filter"; // // mnuNoneFilter @@ -1039,7 +1056,7 @@ namespace Mesen.GUI.Forms this.mnuRegionDendy}); this.mnuRegion.Image = global::Mesen.GUI.Properties.Resources.Globe; this.mnuRegion.Name = "mnuRegion"; - this.mnuRegion.Size = new System.Drawing.Size(135, 22); + this.mnuRegion.Size = new System.Drawing.Size(152, 22); this.mnuRegion.Text = "Region"; // // mnuRegionAuto @@ -1073,13 +1090,13 @@ namespace Mesen.GUI.Forms // toolStripMenuItem10 // this.toolStripMenuItem10.Name = "toolStripMenuItem10"; - this.toolStripMenuItem10.Size = new System.Drawing.Size(132, 6); + this.toolStripMenuItem10.Size = new System.Drawing.Size(149, 6); // // mnuAudioConfig // this.mnuAudioConfig.Image = global::Mesen.GUI.Properties.Resources.Audio; this.mnuAudioConfig.Name = "mnuAudioConfig"; - this.mnuAudioConfig.Size = new System.Drawing.Size(135, 22); + this.mnuAudioConfig.Size = new System.Drawing.Size(152, 22); this.mnuAudioConfig.Text = "Audio"; this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click); // @@ -1087,7 +1104,7 @@ namespace Mesen.GUI.Forms // this.mnuInput.Image = global::Mesen.GUI.Properties.Resources.Controller; this.mnuInput.Name = "mnuInput"; - this.mnuInput.Size = new System.Drawing.Size(135, 22); + this.mnuInput.Size = new System.Drawing.Size(152, 22); this.mnuInput.Text = "Input"; this.mnuInput.Click += new System.EventHandler(this.mnuInput_Click); // @@ -1095,7 +1112,7 @@ namespace Mesen.GUI.Forms // this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.Video; this.mnuVideoConfig.Name = "mnuVideoConfig"; - this.mnuVideoConfig.Size = new System.Drawing.Size(135, 22); + this.mnuVideoConfig.Size = new System.Drawing.Size(152, 22); this.mnuVideoConfig.Text = "Video"; this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click); // @@ -1103,20 +1120,20 @@ namespace Mesen.GUI.Forms // this.mnuEmulationConfig.Image = global::Mesen.GUI.Properties.Resources.DipSwitches; this.mnuEmulationConfig.Name = "mnuEmulationConfig"; - this.mnuEmulationConfig.Size = new System.Drawing.Size(135, 22); + this.mnuEmulationConfig.Size = new System.Drawing.Size(152, 22); this.mnuEmulationConfig.Text = "Emulation"; this.mnuEmulationConfig.Click += new System.EventHandler(this.mnuEmulationConfig_Click); // // toolStripMenuItem11 // this.toolStripMenuItem11.Name = "toolStripMenuItem11"; - this.toolStripMenuItem11.Size = new System.Drawing.Size(132, 6); + this.toolStripMenuItem11.Size = new System.Drawing.Size(149, 6); // // mnuPreferences // this.mnuPreferences.Image = global::Mesen.GUI.Properties.Resources.Cog; this.mnuPreferences.Name = "mnuPreferences"; - this.mnuPreferences.Size = new System.Drawing.Size(135, 22); + this.mnuPreferences.Size = new System.Drawing.Size(152, 22); this.mnuPreferences.Text = "Preferences"; this.mnuPreferences.Click += new System.EventHandler(this.mnuPreferences_Click); // @@ -1125,6 +1142,7 @@ namespace Mesen.GUI.Forms this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.mnuNetPlay, this.mnuMovies, + this.mnuHistoryViewer, this.mnuCheats, this.toolStripMenuItem22, this.mnuSoundRecorder, @@ -1694,21 +1712,13 @@ namespace Mesen.GUI.Forms this.mnuAbout.Text = "About"; this.mnuAbout.Click += new System.EventHandler(this.mnuAbout_Click); // - // mnuInsertCoin3 + // mnuHistoryViewer // - this.mnuInsertCoin3.Image = global::Mesen.GUI.Properties.Resources.coins; - this.mnuInsertCoin3.Name = "mnuInsertCoin3"; - this.mnuInsertCoin3.Size = new System.Drawing.Size(221, 22); - this.mnuInsertCoin3.Text = "Insert Coin (3 - DualSystem)"; - this.mnuInsertCoin3.Visible = false; - // - // mnuInsertCoin4 - // - this.mnuInsertCoin4.Image = global::Mesen.GUI.Properties.Resources.coins; - this.mnuInsertCoin4.Name = "mnuInsertCoin4"; - this.mnuInsertCoin4.Size = new System.Drawing.Size(221, 22); - this.mnuInsertCoin4.Text = "Insert Coin (4 - DualSystem)"; - this.mnuInsertCoin4.Visible = false; + this.mnuHistoryViewer.Image = global::Mesen.GUI.Properties.Resources.Speed; + this.mnuHistoryViewer.Name = "mnuHistoryViewer"; + this.mnuHistoryViewer.Size = new System.Drawing.Size(182, 22); + this.mnuHistoryViewer.Text = "History Viewer"; + this.mnuHistoryViewer.Click += new System.EventHandler(this.mnuHistoryViewer_Click); // // frmMain // @@ -1921,6 +1931,7 @@ namespace Mesen.GUI.Forms private System.Windows.Forms.ToolStripMenuItem mnuDebugDualSystemSecondaryCpu; private System.Windows.Forms.ToolStripMenuItem mnuInsertCoin3; private System.Windows.Forms.ToolStripMenuItem mnuInsertCoin4; + private System.Windows.Forms.ToolStripMenuItem mnuHistoryViewer; } } diff --git a/GUI.NET/Forms/frmMain.Tools.cs b/GUI.NET/Forms/frmMain.Tools.cs index 505fba13..0580da07 100644 --- a/GUI.NET/Forms/frmMain.Tools.cs +++ b/GUI.NET/Forms/frmMain.Tools.cs @@ -98,7 +98,22 @@ namespace Mesen.GUI.Forms _cheatListWindow.Focus(); } } - + + private void mnuHistoryViewer_Click(object sender, EventArgs e) + { + if(_historyViewerWindow == null) { + _historyViewerWindow = new frmHistoryViewer(); + _historyViewerWindow.Show(); + _historyViewerWindow.FormClosed += (s, evt) => { + _historyViewerWindow = null; + }; + } else { + _historyViewerWindow.WindowState = FormWindowState.Normal; + _historyViewerWindow.BringToFront(); + _historyViewerWindow.Focus(); + } + } + private void LoadRandomGame() { IEnumerable gameFolders = ConfigManager.Config.RecentFiles.Select(recentFile => recentFile.RomFile.Folder.ToLowerInvariant()).Distinct(); @@ -376,4 +391,5 @@ namespace Mesen.GUI.Forms } } } + } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index c7222911..fc31bbe8 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -30,6 +30,7 @@ namespace Mesen.GUI.Forms private Thread _emuThread; private frmLogWindow _logWindow; private frmCheatList _cheatListWindow; + private frmHistoryViewer _historyViewerWindow; private frmHdPackEditor _hdPackEditorWindow; private ResourcePath? _currentRomPath = null; List _luaScriptsToLoad = new List(); @@ -274,9 +275,11 @@ namespace Mesen.GUI.Forms } _shuttingDown = true; - if(_frmFullscreenRenderer != null) { - _frmFullscreenRenderer.Close(); - } + _logWindow?.Close(); + _historyViewerWindow?.Close(); + _cheatListWindow?.Close(); + _hdPackEditorWindow?.Close(); + _frmFullscreenRenderer?.Close(); //Stop menu update timer, and process all pending events before stopping the core //This prevents some rare crashes on shutdown diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 440abffe..aa9c4124 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -1103,6 +1103,12 @@ frmHelp.cs + + Form + + + frmHistoryViewer.cs + Form @@ -1662,6 +1668,9 @@ frmHelp.cs + + frmHistoryViewer.cs + frmInputBarcode.cs diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 39017925..28273f48 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -26,6 +26,16 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void InitializeDualSystem(IntPtr windowHandle, IntPtr viewerHandle); [DllImport(DLLPath)] public static extern void ReleaseDualSystemAudioVideo(); + [DllImport(DLLPath)] public static extern void InitializeHistoryViewer(IntPtr windowHandle, IntPtr viewerHandle); + [DllImport(DLLPath)] public static extern void ReleaseHistoryViewer(); + [DllImport(DLLPath)] public static extern void RunHistoryViewer(); + [DllImport(DLLPath)] public static extern void StopHistoryViewer(); + [DllImport(DLLPath)] public static extern UInt32 GetHistoryViewerTotalFrameCount(); + [DllImport(DLLPath)] public static extern void SetHistoryViewerPosition(UInt32 seekPosition); + [DllImport(DLLPath)] public static extern UInt32 GetHistoryViewerPosition(); + [DllImport(DLLPath)] public static extern void SetHistoryViewerPauseStatus([MarshalAs(UnmanagedType.I1)]bool paused); + [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool GetHistoryViewerPauseStatus(); + [DllImport(DLLPath)] public static extern void SetDisplayLanguage(Language lang); [DllImport(DLLPath)] public static extern void SetFullscreenMode([MarshalAs(UnmanagedType.I1)]bool fullscreen, IntPtr windowHandle, UInt32 monitorWidth, UInt32 monitorHeight); diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 2a841227..b7195be1 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -11,6 +11,7 @@ #include "../Core/EmulationSettings.h" #include "../Core/VideoDecoder.h" #include "../Core/VideoRenderer.h" +#include "../Core/HistoryViewer.h" #include "../Core/AutomaticRomTest.h" #include "../Core/RecordedRomTest.h" #include "../Core/FDS.h" @@ -49,6 +50,10 @@ unique_ptr _shortcutKeyHandler; unique_ptr _dualRenderer; unique_ptr _dualSoundManager; +shared_ptr _historyConsole; +unique_ptr _historyRenderer; +unique_ptr _historySoundManager; + void* _windowHandle = nullptr; void* _viewerHandle = nullptr; string _returnString; @@ -131,7 +136,7 @@ namespace InteropEmu { if(!noVideo) { #ifdef _WIN32 - _renderer.reset(new Renderer(_console, (HWND)_viewerHandle)); + _renderer.reset(new Renderer(_console, (HWND)_viewerHandle, true)); #else _renderer.reset(new SdlRenderer(_console, _viewerHandle)); #endif @@ -163,7 +168,7 @@ namespace InteropEmu { if(slaveConsole){ _console->Pause(); #ifdef _WIN32 - _dualRenderer.reset(new Renderer(slaveConsole, (HWND)viewerHandle)); + _dualRenderer.reset(new Renderer(slaveConsole, (HWND)viewerHandle, false)); _dualSoundManager.reset(new SoundManager(slaveConsole, (HWND)windowHandle)); #else _dualRenderer.reset(new SdlRenderer(slaveConsole, viewerHandle)); @@ -181,6 +186,76 @@ namespace InteropEmu { _console->Resume(); } + DllExport void __stdcall InitializeHistoryViewer(void *windowHandle, void *viewerHandle) + { + _historyConsole.reset(new Console()); + _historyConsole->Init(); + _historyConsole->Initialize(_console->GetRomPath(), _console->GetPatchFile()); + _historyConsole->CopyRewindData(_console); + + #ifdef _WIN32 + _historyRenderer.reset(new Renderer(_historyConsole, (HWND)viewerHandle, false)); + _historySoundManager.reset(new SoundManager(_historyConsole, (HWND)windowHandle)); + #else + _historyRenderer.reset(new SdlRenderer(_historyConsole, viewerHandle)); + _historySoundManager.reset(new SdlSoundManager(_historyConsole)); + #endif + } + + DllExport void __stdcall ReleaseHistoryViewer(void *windowHandle, void *viewerHandle) + { + _historyConsole->Stop(); + _historyConsole->Release(true); + _historyRenderer.reset(); + _historySoundManager.reset(); + _historyConsole.reset(); + } + + DllExport void __stdcall RunHistoryViewer() + { + if(_historyConsole) { + _historyConsole->Run(); + } + } + + DllExport void __stdcall SetHistoryViewerPauseStatus(bool paused) + { + if(_historyConsole) { + _historyConsole->SetPauseStatus(paused); + } + } + + DllExport bool __stdcall GetHistoryViewerPauseStatus() + { + if(_historyConsole) { + return _historyConsole->GetPauseStatus(); + } + return true; + } + + DllExport uint32_t __stdcall GetHistoryViewerTotalFrameCount() + { + if(_historyConsole) { + return _historyConsole->GetHistoryViewer()->GetHistoryLength(); + } + return 0; + } + + DllExport void __stdcall SetHistoryViewerPosition(uint32_t seekPosition) + { + if(_historyConsole) { + _historyConsole->GetHistoryViewer()->SeekTo(seekPosition); + } + } + + DllExport uint32_t __stdcall GetHistoryViewerPosition() + { + if(_historyConsole) { + return _historyConsole->GetHistoryViewer()->GetPosition(); + } + return 0; + } + DllExport void __stdcall SetFullscreenMode(bool fullscreen, void *windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) { if(_renderer) { @@ -188,7 +263,6 @@ namespace InteropEmu { } } - DllExport bool __stdcall IsRunning() { return _console->IsRunning(); } DllExport int32_t __stdcall GetStopCode() { return _console->GetStopCode(); } @@ -271,6 +345,7 @@ namespace InteropEmu { DllExport void __stdcall Resume() { EmulationSettings::ClearFlags(EmulationFlags::Paused); } DllExport bool __stdcall IsPaused() { return EmulationSettings::CheckFlag(EmulationFlags::Paused); } + DllExport void __stdcall Stop() { if(_console) { diff --git a/PGOHelper/PGOHelper.cpp b/PGOHelper/PGOHelper.cpp index ba0a5da4..89f5db16 100644 --- a/PGOHelper/PGOHelper.cpp +++ b/PGOHelper/PGOHelper.cpp @@ -49,6 +49,7 @@ enum class VideoFilterType extern "C" { void __stdcall SetFlags(uint64_t flags); void __stdcall SetVideoFilter(VideoFilterType filter); + void __stdcall InitDll(); void __stdcall InitializeEmu(const char* homeFolder, void*, void*, bool, bool, bool); void __stdcall LoadROM(const char* filename, const char* patchFile); void __stdcall Run(); @@ -86,6 +87,7 @@ int main(int argc, char* argv[]) string homeFolder = "../PGOMesenHome"; + InitDll(); SetFlags(0x8000000000000000 | 0x20); //EmulationFlags::ConsoleMode | UseHdPacks InitializeEmu(homeFolder.c_str(), nullptr, nullptr, false, false, false); LoadROM(testRoms[0].c_str(), ""); diff --git a/Windows/Renderer.cpp b/Windows/Renderer.cpp index fbc1d53d..513f2942 100644 --- a/Windows/Renderer.cpp +++ b/Windows/Renderer.cpp @@ -13,7 +13,7 @@ using namespace DirectX; -Renderer::Renderer(shared_ptr console, HWND hWnd) : BaseRenderer(console) +Renderer::Renderer(shared_ptr console, HWND hWnd, bool registerAsMessageManager) : BaseRenderer(console, registerAsMessageManager) { _hWnd = hWnd; diff --git a/Windows/Renderer.h b/Windows/Renderer.h index 79fb257b..84400ccb 100644 --- a/Windows/Renderer.h +++ b/Windows/Renderer.h @@ -93,7 +93,7 @@ private: HRESULT CreateSamplerState(); public: - Renderer(shared_ptr console, HWND hWnd); + Renderer(shared_ptr console, HWND hWnd, bool registerAsMessageManager); ~Renderer(); void SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight);