HistoryViewer - WIP

This commit is contained in:
Sour 2018-07-11 18:07:13 -04:00
parent 18b7206dc9
commit 57e509c606
22 changed files with 701 additions and 42 deletions

View file

@ -6,11 +6,11 @@
#include "VideoDecoder.h"
#include "PPU.h"
BaseRenderer::BaseRenderer(shared_ptr<Console> console)
BaseRenderer::BaseRenderer(shared_ptr<Console> console, bool registerAsMessageManager)
{
_console = console;
if(console->IsMaster()) {
if(registerAsMessageManager) {
//Only display messages on the master CPU's screen
MessageManager::RegisterMessageManager(this);
}

View file

@ -27,7 +27,7 @@ protected:
uint32_t _screenHeight = 0;
uint32_t _renderedFrameCount = 0;
BaseRenderer(shared_ptr<Console> console);
BaseRenderer(shared_ptr<Console> console, bool registerAsMessageManager);
bool IsMessageShown();

View file

@ -44,6 +44,7 @@
#include "VideoRenderer.h"
#include "DebugHud.h"
#include "NotificationManager.h"
#include "HistoryViewer.h"
Console::Console(shared_ptr<Console> master)
{
@ -480,6 +481,11 @@ RewindManager* Console::GetRewindManager()
return _rewindManager.get();
}
HistoryViewer* Console::GetHistoryViewer()
{
return _historyViewer.get();
}
VirtualFile Console::GetRomPath()
{
return static_cast<VirtualFile>(_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<int, std::milli>(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<Console> 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.

View file

@ -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> _rewindManager;
shared_ptr<HistoryViewer> _historyViewer;
shared_ptr<CPU> _cpu;
shared_ptr<PPU> _ppu;
shared_ptr<APU> _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<Console> sourceConsole);
uint8_t* GetRamBuffer(DebugMemoryType memoryType, uint32_t &size, int32_t &startAddr);

View file

@ -537,6 +537,7 @@
<ClInclude Include="FdsSystemActionManager.h" />
<ClInclude Include="Gkcx1.h" />
<ClInclude Include="HdPackConditions.h" />
<ClInclude Include="HistoryViewer.h" />
<ClInclude Include="IBarcodeReader.h" />
<ClInclude Include="IBattery.h" />
<ClInclude Include="IInputProvider.h" />
@ -970,6 +971,7 @@
<ClCompile Include="HdPackBuilder.cpp" />
<ClCompile Include="HdPackLoader.cpp" />
<ClCompile Include="HdPpu.cpp" />
<ClCompile Include="HistoryViewer.cpp" />
<ClCompile Include="KeyManager.cpp" />
<ClCompile Include="LuaApi.cpp" />
<ClCompile Include="LuaCallHelper.cpp" />

View file

@ -1468,6 +1468,9 @@
<ClInclude Include="BmcK3046.h">
<Filter>Nes\Mappers\Unif</Filter>
</ClInclude>
<ClInclude Include="HistoryViewer.h">
<Filter>Rewinder</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -1755,5 +1758,8 @@
<ClCompile Include="NESHeader.cpp">
<Filter>Nes\RomLoader</Filter>
</ClCompile>
<ClCompile Include="HistoryViewer.cpp">
<Filter>Rewinder</Filter>
</ClCompile>
</ItemGroup>
</Project>

87
Core/HistoryViewer.cpp Normal file
View file

@ -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 = console;
_position = 0;
_pollCounter = 0;
}
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);
}
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<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->SetPauseStatus(true);
return;
}
RewindData rewindData = _history[_position];
rewindData.LoadState(_console);
}
}

32
Core/HistoryViewer.h Normal file
View file

@ -0,0 +1,32 @@
#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);
void SetHistoryData(std::deque<RewindData> &history);
uint32_t GetHistoryLength();
uint32_t GetPosition();
void SeekTo(uint32_t seekPosition);
void ProcessEndOfFrame();
// Inherited via IInputProvider
virtual bool SetInput(BaseControlDevice * device) override;
};

View file

@ -5,6 +5,7 @@
#include "VideoRenderer.h"
#include "SoundMixer.h"
#include "BaseControlDevice.h"
#include "HistoryViewer.h"
RewindManager::RewindManager(shared_ptr<Console> console)
{
@ -331,6 +332,11 @@ void RewindManager::RewindSeconds(uint32_t seconds)
}
}
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);

View file

@ -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<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);
};

132
GUI.NET/Forms/frmHistoryViewer.Designer.cs generated Normal file
View file

@ -0,0 +1,132 @@
namespace Mesen.GUI.Forms
{
partial class frmHistoryViewer
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View file

@ -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")
);
}
}
}

View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="tmrUpdatePosition.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>107, 17</value>
</metadata>
</root>

View file

@ -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;
}
}

View file

@ -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<string> gameFolders = ConfigManager.Config.RecentFiles.Select(recentFile => recentFile.RomFile.Folder.ToLowerInvariant()).Distinct();
@ -376,4 +391,5 @@ namespace Mesen.GUI.Forms
}
}
}
}

View file

@ -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<string> _luaScriptsToLoad = new List<string>();
@ -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

View file

@ -1103,6 +1103,12 @@
<Compile Include="Forms\frmHelp.Designer.cs">
<DependentUpon>frmHelp.cs</DependentUpon>
</Compile>
<Compile Include="Forms\frmHistoryViewer.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\frmHistoryViewer.Designer.cs">
<DependentUpon>frmHistoryViewer.cs</DependentUpon>
</Compile>
<Compile Include="Forms\frmInputBarcode.cs">
<SubType>Form</SubType>
</Compile>
@ -1662,6 +1668,9 @@
<EmbeddedResource Include="Forms\frmHelp.resx">
<DependentUpon>frmHelp.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\frmHistoryViewer.resx">
<DependentUpon>frmHistoryViewer.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\frmInputBarcode.resx">
<DependentUpon>frmInputBarcode.cs</DependentUpon>
</EmbeddedResource>

View file

@ -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);

View file

@ -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> _shortcutKeyHandler;
unique_ptr<IRenderingDevice> _dualRenderer;
unique_ptr<IAudioDevice> _dualSoundManager;
shared_ptr<Console> _historyConsole;
unique_ptr<IRenderingDevice> _historyRenderer;
unique_ptr<IAudioDevice> _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) {

View file

@ -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(), "");

View file

@ -13,7 +13,7 @@
using namespace DirectX;
Renderer::Renderer(shared_ptr<Console> console, HWND hWnd) : BaseRenderer(console)
Renderer::Renderer(shared_ptr<Console> console, HWND hWnd, bool registerAsMessageManager) : BaseRenderer(console, registerAsMessageManager)
{
_hWnd = hWnd;

View file

@ -93,7 +93,7 @@ private:
HRESULT CreateSamplerState();
public:
Renderer(shared_ptr<Console> console, HWND hWnd);
Renderer(shared_ptr<Console> console, HWND hWnd, bool registerAsMessageManager);
~Renderer();
void SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight);