diff --git a/Core/Console.cpp b/Core/Console.cpp index 6ab12332..bd775b4e 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -374,8 +374,16 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile) GetDebugger(); } + ResetComponents(false); + //Reset components before creating rewindmanager, otherwise the first save state it takes will be invalid + _rewindManager.reset(new RewindManager(shared_from_this())); + _notificationManager->RegisterNotificationListener(_rewindManager); + + //Poll controller input after creating rewind manager, to make sure it catches the first frame's input + _controlManager->UpdateInputState(); + #ifndef LIBRETRO //Don't use auto-save manager for libretro //Only enable auto-save for the master console (VS Dualsystem) @@ -383,9 +391,6 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile) _autoSaveManager.reset(new AutoSaveManager(shared_from_this())); } #endif - _rewindManager.reset(new RewindManager(shared_from_this())); - _notificationManager->RegisterNotificationListener(_rewindManager); - _videoDecoder->StartThread(); FolderUtilities::AddKnownGameFolder(romFile.GetFolderPath()); @@ -590,8 +595,6 @@ void Console::ResetComponents(bool softReset) debugger->Resume(); } } - - _controlManager->UpdateInputState(); } void Console::Stop(int stopCode) diff --git a/Core/ControlManager.cpp b/Core/ControlManager.cpp index e6c15568..d21ee49d 100644 --- a/Core/ControlManager.cpp +++ b/Core/ControlManager.cpp @@ -99,6 +99,11 @@ shared_ptr ControlManager::GetControlDevice(uint8_t port) return nullptr; } +vector> ControlManager::GetControlDevices() +{ + return _controlDevices; +} + void ControlManager::RegisterControlDevice(shared_ptr controlDevice) { _controlDevices.push_back(controlDevice); diff --git a/Core/ControlManager.h b/Core/ControlManager.h index e947b9f6..c26f4ef2 100644 --- a/Core/ControlManager.h +++ b/Core/ControlManager.h @@ -66,6 +66,7 @@ public: vector GetPortStates(); shared_ptr GetControlDevice(uint8_t port); + vector> GetControlDevices(); bool HasKeyboard(); static shared_ptr CreateControllerDevice(ControllerType type, uint8_t port, shared_ptr console); diff --git a/Core/HistoryViewer.cpp b/Core/HistoryViewer.cpp index ea98a444..b0f21d82 100644 --- a/Core/HistoryViewer.cpp +++ b/Core/HistoryViewer.cpp @@ -5,6 +5,8 @@ #include "BaseControlDevice.h" #include "SoundMixer.h" #include "NotificationManager.h" +#include "RomData.h" +#include "MovieRecorder.h" HistoryViewer::HistoryViewer(shared_ptr console) { @@ -29,6 +31,23 @@ uint32_t HistoryViewer::GetHistoryLength() return (uint32_t)(_history.size() * HistoryViewer::BufferSize); } +void HistoryViewer::GetHistorySegments(uint32_t *segmentBuffer, uint32_t &bufferSize) +{ + int segmentIndex = 0; + for(int i = 0; i < _history.size(); i++) { + if(_history[i].EndOfSegment) { + segmentBuffer[segmentIndex] = i; + segmentIndex++; + + if(segmentIndex == bufferSize) { + //Reached buffer size, can't return any more values + break; + } + } + } + bufferSize = segmentIndex; +} + uint32_t HistoryViewer::GetPosition() { return _position; @@ -36,14 +55,13 @@ uint32_t HistoryViewer::GetPosition() 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()) { + //Seek to the specified position + if(seekPosition < _history.size()) { _console->Pause(); bool wasPaused = _console->GetSettings()->CheckFlag(EmulationFlags::Paused); _console->GetSettings()->ClearFlags(EmulationFlags::Paused); - _position = index; + _position = seekPosition; RewindData rewindData = _history[_position]; rewindData.LoadState(_console); @@ -58,6 +76,39 @@ void HistoryViewer::SeekTo(uint32_t seekPosition) } } +bool HistoryViewer::SaveMovie(string movieFile, uint32_t startPosition, uint32_t endPosition) +{ + //Take a savestate to be able to restore it after generating the movie file + //(the movie generation uses the console's inputs, which could affect the emulation otherwise) + stringstream state; + _console->Pause(); + _console->SaveState(state); + + //Convert the rewind data to a .mmo file + unique_ptr recorder(new MovieRecorder(_console)); + bool result = recorder->CreateMovie(movieFile, _history, startPosition, endPosition); + + //Resume the state and resume + _console->LoadState(state); + _console->Resume(); + return result; +} + +void HistoryViewer::ResumeGameplay(shared_ptr console, uint32_t resumePosition) +{ + console->Pause(); + if(_console->GetRomInfo().Hash.Sha1 != console->GetRomInfo().Hash.Sha1) { + //Load game on the main window if they aren't the same + console->Initialize(_console->GetRomPath(), _console->GetPatchFile()); + } + if(resumePosition < _history.size()) { + _history[resumePosition].LoadState(console); + } else { + _history[_history.size() - 1].LoadState(console); + } + console->Resume(); +} + bool HistoryViewer::SetInput(BaseControlDevice *device) { uint8_t port = device->GetPort(); diff --git a/Core/HistoryViewer.h b/Core/HistoryViewer.h index c1f60500..6bf4ada1 100644 --- a/Core/HistoryViewer.h +++ b/Core/HistoryViewer.h @@ -22,8 +22,13 @@ public: void SetHistoryData(std::deque &history); uint32_t GetHistoryLength(); + void GetHistorySegments(uint32_t * segmentBuffer, uint32_t &bufferSize); uint32_t GetPosition(); void SeekTo(uint32_t seekPosition); + + bool SaveMovie(string movieFile, uint32_t startPosition, uint32_t endPosition); + + void ResumeGameplay(shared_ptr console, uint32_t resumePosition); void ProcessEndOfFrame(); diff --git a/Core/MovieRecorder.cpp b/Core/MovieRecorder.cpp index c3dbf377..a20e7c8d 100644 --- a/Core/MovieRecorder.cpp +++ b/Core/MovieRecorder.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include #include "../Utilities/HexUtilities.h" #include "../Utilities/FolderUtilities.h" #include "../Utilities/ZipWriter.h" @@ -11,6 +12,7 @@ #include "SaveStateManager.h" #include "NotificationManager.h" #include "RomData.h" +#include "RewindData.h" MovieRecorder::MovieRecorder(shared_ptr console) { @@ -228,3 +230,39 @@ void MovieRecorder::ProcessNotification(ConsoleNotificationType type, void *para _console->GetControlManager()->RegisterInputRecorder(this); } } + +bool MovieRecorder::CreateMovie(string movieFile, std::deque &data, uint32_t startPosition, uint32_t endPosition) +{ + _filename = movieFile; + _writer.reset(new ZipWriter()); + if(startPosition < data.size() && endPosition <= data.size() && _writer->Initialize(_filename)) { + vector> devices = _console->GetControlManager()->GetControlDevices(); + + if(startPosition > 0) { + _hasSaveState = true; + _saveStateData = stringstream(); + _console->GetSaveStateManager()->GetSaveStateHeader(_saveStateData); + data[startPosition].GetStateData(_saveStateData); + } + + _inputData = stringstream(); + + for(uint32_t i = startPosition; i < endPosition; i++) { + RewindData rewindData = data[i]; + for(int i = 0; i < 30; i++) { + for(shared_ptr &device : devices) { + uint8_t port = device->GetPort(); + if(i < rewindData.InputLogs[port].size()) { + device->SetRawState(rewindData.InputLogs[port][i]); + _inputData << ("|" + device->GetTextState()); + } + } + _inputData << "\n"; + } + } + + //Write the movie file + return Stop(); + } + return false; +} \ No newline at end of file diff --git a/Core/MovieRecorder.h b/Core/MovieRecorder.h index 0c54267f..5d1072d6 100644 --- a/Core/MovieRecorder.h +++ b/Core/MovieRecorder.h @@ -1,5 +1,6 @@ #pragma once #include "stdafx.h" +#include #include #include "IInputRecorder.h" #include "BatteryManager.h" @@ -8,6 +9,7 @@ class ZipWriter; class Console; +class RewindData; struct CodeInfo; class MovieRecorder : public INotificationListener, public IInputRecorder, public IBatteryRecorder, public IBatteryProvider, public std::enable_shared_from_this @@ -22,7 +24,7 @@ private: unique_ptr _writer; std::unordered_map> _batteryData; stringstream _inputData; - bool _hasSaveState; + bool _hasSaveState = false; stringstream _saveStateData; void GetGameSettings(stringstream &out); @@ -38,6 +40,9 @@ public: bool Record(RecordMovieOptions options); bool Stop(); + bool CreateMovie(string movieFile, std::deque &data, uint32_t startPosition, uint32_t endPosition); + + // Inherited via IInputRecorder void RecordInput(vector> devices) override; // Inherited via IBatteryRecorder @@ -47,7 +52,7 @@ public: virtual vector LoadBattery(string extension) override; // Inherited via INotificationListener - virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override; + virtual void ProcessNotification(ConsoleNotificationType type, void *parameter) override; }; namespace MovieKeys diff --git a/Core/RewindData.cpp b/Core/RewindData.cpp index c2997ae8..4379eed9 100644 --- a/Core/RewindData.cpp +++ b/Core/RewindData.cpp @@ -3,6 +3,15 @@ #include "Console.h" #include "../Utilities/miniz.h" +void RewindData::GetStateData(stringstream &stateData) +{ + unsigned long length = OriginalSaveStateSize; + uint8_t* buffer = new uint8_t[length]; + uncompress(buffer, &length, SaveStateData.data(), (unsigned long)SaveStateData.size()); + stateData.write((char*)buffer, length); + delete[] buffer; +} + void RewindData::LoadState(shared_ptr &console) { if(SaveStateData.size() > 0 && OriginalSaveStateSize > 0) { diff --git a/Core/RewindData.h b/Core/RewindData.h index 28520e4f..f47894d3 100644 --- a/Core/RewindData.h +++ b/Core/RewindData.h @@ -16,6 +16,9 @@ private: public: std::deque InputLogs[BaseControlDevice::PortCount]; int32_t FrameCount = 0; + bool EndOfSegment = false; + + void GetStateData(stringstream &stateData); void LoadState(shared_ptr &console); void SaveState(shared_ptr &console); diff --git a/Core/RewindManager.cpp b/Core/RewindManager.cpp index 5185bcde..0c47f4bb 100644 --- a/Core/RewindManager.cpp +++ b/Core/RewindManager.cpp @@ -75,6 +75,11 @@ void RewindManager::ProcessNotification(ConsoleNotificationType type, void * par } else { ClearBuffer(); } + } else if(type == ConsoleNotificationType::StateLoaded) { + if(_rewindState == RewindState::Stopped) { + //A save state was loaded by the user, mark as the end of the current "segment" (for history viewer) + _currentHistory.EndOfSegment = true; + } } } diff --git a/Core/SaveStateManager.cpp b/Core/SaveStateManager.cpp index ffe66f7c..e0f000ad 100644 --- a/Core/SaveStateManager.cpp +++ b/Core/SaveStateManager.cpp @@ -58,7 +58,7 @@ bool SaveStateManager::LoadState() return LoadState(_lastIndex); } -void SaveStateManager::SaveState(ostream &stream) +void SaveStateManager::GetSaveStateHeader(ostream &stream) { uint32_t emuVersion = EmulationSettings::GetMesenVersion(); uint32_t formatVersion = SaveStateManager::FileFormatVersion; @@ -69,7 +69,7 @@ void SaveStateManager::SaveState(ostream &stream) RomInfo romInfo = _console->GetRomInfo(); stream.write((char*)&romInfo.MapperID, sizeof(uint16_t)); stream.write((char*)&romInfo.SubMapperID, sizeof(uint8_t)); - + string sha1Hash = romInfo.Hash.Sha1; stream.write(sha1Hash.c_str(), sha1Hash.size()); @@ -77,7 +77,11 @@ void SaveStateManager::SaveState(ostream &stream) uint32_t nameLength = (uint32_t)romName.size(); stream.write((char*)&nameLength, sizeof(uint32_t)); stream.write(romName.c_str(), romName.size()); +} +void SaveStateManager::SaveState(ostream &stream) +{ + GetSaveStateHeader(stream); _console->SaveState(stream); } diff --git a/Core/SaveStateManager.h b/Core/SaveStateManager.h index 58a3c4c3..834378bf 100644 --- a/Core/SaveStateManager.h +++ b/Core/SaveStateManager.h @@ -23,6 +23,8 @@ public: void SaveState(); bool LoadState(); + void GetSaveStateHeader(ostream & stream); + void SaveState(ostream &stream); bool SaveState(string filepath); void SaveState(int stateIndex, bool displayMessage = true); diff --git a/Core/SystemActionManager.h b/Core/SystemActionManager.h index cca76ff4..12f53c06 100644 --- a/Core/SystemActionManager.h +++ b/Core/SystemActionManager.h @@ -73,6 +73,7 @@ public: if(console) { if(IsPressed(SystemActionManager::Buttons::ResetButton)) { console->ResetComponents(true); + console->GetControlManager()->UpdateInputState(); } if(IsPressed(SystemActionManager::Buttons::PowerButton)) { console->PowerCycle(); diff --git a/GUI.NET/Config/Configuration.cs b/GUI.NET/Config/Configuration.cs index 08c2fbee..fbbfe99f 100644 --- a/GUI.NET/Config/Configuration.cs +++ b/GUI.NET/Config/Configuration.cs @@ -31,6 +31,7 @@ namespace Mesen.GUI.Config public DebugInfo DebugInfo; public AviRecordInfo AviRecordInfo; public MovieRecordInfo MovieRecordInfo; + public HistoryViewerInfo HistoryViewerInfo; public List GameSpecificSettings; public Point? WindowLocation; public Size? WindowSize; @@ -50,6 +51,7 @@ namespace Mesen.GUI.Config DebugInfo = new DebugInfo(); AviRecordInfo = new AviRecordInfo(); MovieRecordInfo = new MovieRecordInfo(); + HistoryViewerInfo = new HistoryViewerInfo(); GameSpecificSettings = new List(); } diff --git a/GUI.NET/Config/HistoryViewerInfo.cs b/GUI.NET/Config/HistoryViewerInfo.cs new file mode 100644 index 00000000..9ee8da52 --- /dev/null +++ b/GUI.NET/Config/HistoryViewerInfo.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Config +{ + public class HistoryViewerInfo + { + public int Volume = 25; + public Point? WindowLocation; + public Size? WindowSize; + } +} diff --git a/GUI.NET/Forms/MesenMsgBox.cs b/GUI.NET/Forms/MesenMsgBox.cs index 080c906d..d76b6102 100644 --- a/GUI.NET/Forms/MesenMsgBox.cs +++ b/GUI.NET/Forms/MesenMsgBox.cs @@ -28,7 +28,7 @@ namespace Mesen.GUI.Forms })); return result; } else { - return MessageBox.Show(mainForm, ResourceHelper.GetMessage(text, args), "Mesen", buttons, icon); + return MessageBox.Show(ResourceHelper.GetMessage(text, args), "Mesen", buttons, icon); } } } diff --git a/GUI.NET/Forms/frmHistoryViewer.Designer.cs b/GUI.NET/Forms/frmHistoryViewer.Designer.cs index 0688c5d9..8e00d58b 100644 --- a/GUI.NET/Forms/frmHistoryViewer.Designer.cs +++ b/GUI.NET/Forms/frmHistoryViewer.Designer.cs @@ -29,50 +29,61 @@ { 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.pnlRenderer = new System.Windows.Forms.Panel(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.ctrlRenderer = new System.Windows.Forms.Panel(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.lblVolume = new System.Windows.Forms.Label(); + this.trkVolume = new System.Windows.Forms.TrackBar(); this.tmrUpdatePosition = new System.Windows.Forms.Timer(this.components); + this.menuStrip2 = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuImportMovie = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuExportMovie = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuResumeGameplay = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuClose = new System.Windows.Forms.ToolStripMenuItem(); this.tableLayoutPanel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.trkPosition)).BeginInit(); + this.pnlRenderer.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel3.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trkVolume)).BeginInit(); + this.menuStrip2.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 // - this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnCount = 4; 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.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); 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.Controls.Add(this.pnlRenderer, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel3, 3, 1); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 24); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 1; + this.tableLayoutPanel1.RowCount = 2; 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.Size = new System.Drawing.Size(532, 477); 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.Location = new System.Drawing.Point(56, 429); this.trkPosition.Name = "trkPosition"; - this.trkPosition.Size = new System.Drawing.Size(406, 45); + this.trkPosition.Size = new System.Drawing.Size(312, 45); this.trkPosition.TabIndex = 1; this.trkPosition.TickFrequency = 10; this.trkPosition.TickStyle = System.Windows.Forms.TickStyle.Both; @@ -82,7 +93,7 @@ // 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.Location = new System.Drawing.Point(3, 433); this.btnPausePlay.Name = "btnPausePlay"; this.btnPausePlay.Size = new System.Drawing.Size(47, 36); this.btnPausePlay.TabIndex = 2; @@ -92,7 +103,7 @@ // this.lblPosition.Anchor = System.Windows.Forms.AnchorStyles.Right; this.lblPosition.AutoSize = true; - this.lblPosition.Location = new System.Drawing.Point(468, 508); + this.lblPosition.Location = new System.Drawing.Point(374, 445); this.lblPosition.MinimumSize = new System.Drawing.Size(49, 13); this.lblPosition.Name = "lblPosition"; this.lblPosition.Size = new System.Drawing.Size(49, 13); @@ -100,33 +111,196 @@ this.lblPosition.Text = "77:77:77"; this.lblPosition.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // + // pnlRenderer + // + this.pnlRenderer.BackColor = System.Drawing.Color.Black; + this.pnlRenderer.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.tableLayoutPanel1.SetColumnSpan(this.pnlRenderer, 4); + this.pnlRenderer.Controls.Add(this.tableLayoutPanel2); + this.pnlRenderer.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlRenderer.Location = new System.Drawing.Point(3, 3); + this.pnlRenderer.Name = "pnlRenderer"; + this.pnlRenderer.Size = new System.Drawing.Size(526, 420); + this.pnlRenderer.TabIndex = 0; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 1; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel2.Controls.Add(this.ctrlRenderer, 0, 0); + this.tableLayoutPanel2.Cursor = System.Windows.Forms.Cursors.Hand; + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(524, 418); + this.tableLayoutPanel2.TabIndex = 0; + this.tableLayoutPanel2.MouseClick += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseClick); + // + // ctrlRenderer + // + this.ctrlRenderer.Anchor = System.Windows.Forms.AnchorStyles.None; + this.ctrlRenderer.Location = new System.Drawing.Point(134, 89); + this.ctrlRenderer.Name = "ctrlRenderer"; + this.ctrlRenderer.Size = new System.Drawing.Size(256, 240); + this.ctrlRenderer.TabIndex = 0; + this.ctrlRenderer.MouseClick += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseClick); + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.ColumnCount = 1; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Controls.Add(this.lblVolume, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.trkVolume, 0, 0); + this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel3.Location = new System.Drawing.Point(429, 429); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 2; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 31F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.Size = new System.Drawing.Size(100, 45); + this.tableLayoutPanel3.TabIndex = 4; + // + // lblVolume + // + this.lblVolume.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.lblVolume.AutoSize = true; + this.lblVolume.Location = new System.Drawing.Point(25, 31); + this.lblVolume.MinimumSize = new System.Drawing.Size(49, 13); + this.lblVolume.Name = "lblVolume"; + this.lblVolume.Size = new System.Drawing.Size(49, 13); + this.lblVolume.TabIndex = 8; + this.lblVolume.Text = "Volume"; + this.lblVolume.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // trkVolume + // + this.trkVolume.Dock = System.Windows.Forms.DockStyle.Fill; + this.trkVolume.Location = new System.Drawing.Point(0, 0); + this.trkVolume.Margin = new System.Windows.Forms.Padding(0); + this.trkVolume.Maximum = 100; + this.trkVolume.Name = "trkVolume"; + this.trkVolume.Size = new System.Drawing.Size(100, 31); + this.trkVolume.TabIndex = 7; + this.trkVolume.TickFrequency = 10; + this.trkVolume.ValueChanged += new System.EventHandler(this.trkVolume_ValueChanged); + // // tmrUpdatePosition // - this.tmrUpdatePosition.Interval = 500; + this.tmrUpdatePosition.Interval = 150; this.tmrUpdatePosition.Tick += new System.EventHandler(this.tmrUpdatePosition_Tick); // + // menuStrip2 + // + this.menuStrip2.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem}); + this.menuStrip2.Location = new System.Drawing.Point(0, 0); + this.menuStrip2.Name = "menuStrip2"; + this.menuStrip2.Size = new System.Drawing.Size(532, 24); + this.menuStrip2.TabIndex = 1; + this.menuStrip2.Text = "menuStrip2"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuImportMovie, + this.mnuExportMovie, + this.toolStripMenuItem1, + this.mnuResumeGameplay, + this.toolStripMenuItem2, + this.mnuClose}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Text = "File"; + this.fileToolStripMenuItem.DropDownOpening += new System.EventHandler(this.fileToolStripMenuItem_DropDownOpening); + // + // mnuImportMovie + // + this.mnuImportMovie.Image = global::Mesen.GUI.Properties.Resources.Import; + this.mnuImportMovie.Name = "mnuImportMovie"; + this.mnuImportMovie.Size = new System.Drawing.Size(171, 22); + this.mnuImportMovie.Text = "Import movie"; + // + // mnuExportMovie + // + this.mnuExportMovie.Image = global::Mesen.GUI.Properties.Resources.Export; + this.mnuExportMovie.Name = "mnuExportMovie"; + this.mnuExportMovie.Size = new System.Drawing.Size(171, 22); + this.mnuExportMovie.Text = "Export movie"; + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(168, 6); + // + // mnuResumeGameplay + // + this.mnuResumeGameplay.Image = global::Mesen.GUI.Properties.Resources.Play; + this.mnuResumeGameplay.Name = "mnuResumeGameplay"; + this.mnuResumeGameplay.Size = new System.Drawing.Size(171, 22); + this.mnuResumeGameplay.Text = "Resume gameplay"; + this.mnuResumeGameplay.Click += new System.EventHandler(this.mnuResumeGameplay_Click); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(168, 6); + // + // mnuClose + // + this.mnuClose.Image = global::Mesen.GUI.Properties.Resources.Exit; + this.mnuClose.Name = "mnuClose"; + this.mnuClose.Size = new System.Drawing.Size(171, 22); + this.mnuClose.Text = "Close"; + this.mnuClose.Click += new System.EventHandler(this.mnuClose_Click); + // // 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.ClientSize = new System.Drawing.Size(532, 501); this.Controls.Add(this.tableLayoutPanel1); + this.Controls.Add(this.menuStrip2); + this.MinimumSize = new System.Drawing.Size(331, 384); this.Name = "frmHistoryViewer"; this.Text = "History Viewer"; this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.trkPosition)).EndInit(); + this.pnlRenderer.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel3.ResumeLayout(false); + this.tableLayoutPanel3.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trkVolume)).EndInit(); + this.menuStrip2.ResumeLayout(false); + this.menuStrip2.PerformLayout(); this.ResumeLayout(false); + this.PerformLayout(); } #endregion private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; - private System.Windows.Forms.Panel ctrlRenderer; + private System.Windows.Forms.Panel pnlRenderer; private System.Windows.Forms.TrackBar trkPosition; private System.Windows.Forms.Button btnPausePlay; private System.Windows.Forms.Timer tmrUpdatePosition; private System.Windows.Forms.Label lblPosition; + private System.Windows.Forms.MenuStrip menuStrip2; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuImportMovie; + private System.Windows.Forms.ToolStripMenuItem mnuExportMovie; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem mnuClose; + private System.Windows.Forms.ToolStripMenuItem mnuResumeGameplay; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Panel ctrlRenderer; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Label lblVolume; + private System.Windows.Forms.TrackBar trkVolume; } } \ No newline at end of file diff --git a/GUI.NET/Forms/frmHistoryViewer.cs b/GUI.NET/Forms/frmHistoryViewer.cs index 34489b9a..21752b1c 100644 --- a/GUI.NET/Forms/frmHistoryViewer.cs +++ b/GUI.NET/Forms/frmHistoryViewer.cs @@ -1,4 +1,5 @@ -using System; +using Mesen.GUI.Config; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -21,24 +22,54 @@ namespace Mesen.GUI.Forms InitializeComponent(); } + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + if(ConfigManager.Config.HistoryViewerInfo.WindowSize.HasValue) { + this.Size = ConfigManager.Config.HistoryViewerInfo.WindowSize.Value; + } + if(ConfigManager.Config.HistoryViewerInfo.WindowLocation.HasValue) { + this.StartPosition = FormStartPosition.Manual; + this.Location = ConfigManager.Config.HistoryViewerInfo.WindowLocation.Value; + } + + trkVolume.Value = ConfigManager.Config.HistoryViewerInfo.Volume; + } + + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + + ConfigManager.Config.HistoryViewerInfo.WindowLocation = this.WindowState == FormWindowState.Normal ? this.Location : this.RestoreBounds.Location; + ConfigManager.Config.HistoryViewerInfo.WindowSize = this.WindowState == FormWindowState.Normal ? this.Size : this.RestoreBounds.Size; + ConfigManager.Config.HistoryViewerInfo.Volume = trkVolume.Value; + ConfigManager.ApplyChanges(); + } + protected override void OnShown(EventArgs e) { base.OnShown(e); - InteropEmu.InitializeHistoryViewer(this.Handle, ctrlRenderer.Handle); - trkPosition.Maximum = (int)(InteropEmu.GetHistoryViewerTotalFrameCount() / 60); + InteropEmu.HistoryViewerInitialize(this.Handle, ctrlRenderer.Handle); + trkPosition.Maximum = (int)(InteropEmu.HistoryViewerGetHistoryLength() / 60); UpdatePositionLabel(0); - InteropEmu.Pause(InteropEmu.ConsoleId.HistoryViewer); StartEmuThread(); + InteropEmu.Resume(InteropEmu.ConsoleId.HistoryViewer); tmrUpdatePosition.Start(); btnPausePlay.Focus(); + + UpdateScale(); + this.Resize += (s, evt) => { + UpdateScale(); + }; } protected override void OnClosing(CancelEventArgs e) { tmrUpdatePosition.Stop(); - InteropEmu.ReleaseHistoryViewer(); + InteropEmu.HistoryViewerRelease(); base.OnClosing(e); } @@ -47,7 +78,7 @@ namespace Mesen.GUI.Forms if(_emuThread == null) { _emuThread = new Thread(() => { try { - InteropEmu.RunHistoryViewer(); + InteropEmu.HistoryViewerRun(); _emuThread = null; } catch(Exception ex) { MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, ex.ToString()); @@ -58,10 +89,10 @@ namespace Mesen.GUI.Forms } } - private void btnPausePlay_Click(object sender, EventArgs e) + private void TogglePause() { if(trkPosition.Value == trkPosition.Maximum) { - InteropEmu.SetHistoryViewerPosition(0); + InteropEmu.HistoryViewerSetPosition(0); } if(_paused) { InteropEmu.Resume(InteropEmu.ConsoleId.HistoryViewer); @@ -72,11 +103,27 @@ namespace Mesen.GUI.Forms private void trkPosition_ValueChanged(object sender, EventArgs e) { - InteropEmu.SetHistoryViewerPosition((UInt32)trkPosition.Value); + InteropEmu.HistoryViewerSetPosition((UInt32)trkPosition.Value * 2); + } + + private void UpdateScale() + { + Size dimensions = pnlRenderer.ClientSize; + InteropEmu.ScreenSize size = InteropEmu.GetScreenSize(true, InteropEmu.ConsoleId.HistoryViewer); + + double verticalScale = (double)dimensions.Height / size.Height; + double horizontalScale = (double)dimensions.Width / size.Width; + double scale = Math.Min(verticalScale, horizontalScale); + InteropEmu.SetVideoScale(scale, InteropEmu.ConsoleId.HistoryViewer); } private void tmrUpdatePosition_Tick(object sender, EventArgs e) { + InteropEmu.ScreenSize size = InteropEmu.GetScreenSize(false, InteropEmu.ConsoleId.HistoryViewer); + if(size.Width != ctrlRenderer.ClientSize.Width || size.Height != ctrlRenderer.ClientSize.Height) { + ctrlRenderer.ClientSize = new Size(size.Width, size.Height); + } + _paused = InteropEmu.IsPaused(InteropEmu.ConsoleId.HistoryViewer); if(_paused) { btnPausePlay.Image = Properties.Resources.Play; @@ -84,7 +131,7 @@ namespace Mesen.GUI.Forms btnPausePlay.Image = Properties.Resources.Pause; } - UInt32 positionInSeconds = InteropEmu.GetHistoryViewerPosition() / 2; + UInt32 positionInSeconds = InteropEmu.HistoryViewerGetPosition() / 2; UpdatePositionLabel(positionInSeconds); if(positionInSeconds <= trkPosition.Maximum) { @@ -104,5 +151,69 @@ namespace Mesen.GUI.Forms totalLength.Minutes.ToString("00") + ":" + totalLength.Seconds.ToString("00") ); } + + private void mnuClose_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void mnuResumeGameplay_Click(object sender, EventArgs e) + { + InteropEmu.HistoryViewerResumeGameplay(InteropEmu.HistoryViewerGetPosition()); + } + + private void fileToolStripMenuItem_DropDownOpening(object sender, EventArgs e) + { + mnuExportMovie.DropDownItems.Clear(); + + List segments = new List(InteropEmu.HistoryViewerGetSegments()); + UInt32 segmentStart = 0; + segments.Add(InteropEmu.HistoryViewerGetHistoryLength() / 30); + + for(int i = 0; i < segments.Count; i++) { + if(segments[i] - segmentStart > 4) { + //Only list segments that are at least 2 seconds long + UInt32 segStart = segmentStart; + UInt32 segEnd = segments[i]; + TimeSpan start = new TimeSpan(0, 0, (int)(segmentStart) / 2); + TimeSpan end = new TimeSpan(0, 0, (int)(segEnd / 2)); + + ToolStripMenuItem item = new ToolStripMenuItem("Segment #" + (mnuExportMovie.DropDownItems.Count + 1).ToString() + ", " + start.ToString() + " - " + end.ToString()); + item.Click += (s, evt) => { + SaveFileDialog sfd = new SaveFileDialog(); + sfd.SetFilter(ResourceHelper.GetMessage("FilterMovie")); + sfd.InitialDirectory = ConfigManager.MovieFolder; + sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + ".mmo"; + if(sfd.ShowDialog() == DialogResult.OK) { + if(!InteropEmu.HistoryViewerSaveMovie(sfd.FileName, segStart, segEnd)) { + MessageBox.Show("An error occurred while trying to save the movie file.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + }; + mnuExportMovie.DropDownItems.Add(item); + } + segmentStart = segments[i] + 1; + } + + mnuImportMovie.Visible = false; + mnuExportMovie.Enabled = mnuExportMovie.HasDropDownItems; + } + + private void btnPausePlay_Click(object sender, EventArgs e) + { + TogglePause(); + } + + private void ctrlRenderer_MouseClick(object sender, MouseEventArgs e) + { + if(e.Button == MouseButtons.Left) { + TogglePause(); + } + } + + private void trkVolume_ValueChanged(object sender, EventArgs e) + { + InteropEmu.SetMasterVolume(trkVolume.Value / 10d, 0, InteropEmu.ConsoleId.HistoryViewer); + } } } diff --git a/GUI.NET/Forms/frmHistoryViewer.resx b/GUI.NET/Forms/frmHistoryViewer.resx index 2e85e889..d5ffb8b1 100644 --- a/GUI.NET/Forms/frmHistoryViewer.resx +++ b/GUI.NET/Forms/frmHistoryViewer.resx @@ -123,4 +123,7 @@ 107, 17 + + 261, 17 + \ No newline at end of file diff --git a/GUI.NET/Forms/frmMain.Tools.cs b/GUI.NET/Forms/frmMain.Tools.cs index d25ee8be..94db6367 100644 --- a/GUI.NET/Forms/frmMain.Tools.cs +++ b/GUI.NET/Forms/frmMain.Tools.cs @@ -103,7 +103,7 @@ namespace Mesen.GUI.Forms { if(_historyViewerWindow == null) { _historyViewerWindow = new frmHistoryViewer(); - _historyViewerWindow.Show(); + _historyViewerWindow.Show(sender, this); _historyViewerWindow.FormClosed += (s, evt) => { _historyViewerWindow = null; }; diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index cc884829..ccbce71d 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -443,7 +443,7 @@ namespace Mesen.GUI.Forms double verticalScale = (double)dimensions.Height / size.Height; double horizontalScale = (double)dimensions.Width / size.Width; double scale = Math.Min(verticalScale, horizontalScale); - if(ConfigManager.Config.VideoInfo.FullscreenForceIntegerScale) { + if(_fullscreenMode && ConfigManager.Config.VideoInfo.FullscreenForceIntegerScale) { scale = Math.Floor(scale); } UpdateScaleMenu(scale); @@ -1089,7 +1089,7 @@ namespace Mesen.GUI.Forms mnuStopMovie.Enabled = running && !netPlay && (moviePlaying || movieRecording); mnuRecordMovie.Enabled = running && !moviePlaying && !movieRecording && !isNetPlayClient; mnuGameConfig.Enabled = !moviePlaying && !movieRecording; - mnuHistoryViewer.Enabled = running; + mnuHistoryViewer.Enabled = running && !InteropEmu.IsNsf(); bool waveRecording = InteropEmu.WaveIsRecording(); mnuWaveRecord.Enabled = running && !waveRecording; diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index aa9c4124..d8e57bb3 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -241,6 +241,7 @@ + diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 4209f9b9..8a472d15 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -26,13 +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 HistoryViewerInitialize(IntPtr windowHandle, IntPtr viewerHandle); + [DllImport(DLLPath)] public static extern void HistoryViewerRelease(); + [DllImport(DLLPath)] public static extern void HistoryViewerRun(); + [DllImport(DLLPath)] public static extern void HistoryViewerStop(); + [DllImport(DLLPath)] public static extern UInt32 HistoryViewerGetHistoryLength(); + [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool HistoryViewerSaveMovie([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string movieFile, UInt32 startPosition, UInt32 endPosition); + [DllImport(DLLPath)] public static extern void HistoryViewerSetPosition(UInt32 seekPosition); + [DllImport(DLLPath)] public static extern void HistoryViewerResumeGameplay(UInt32 seekPosition); + [DllImport(DLLPath)] public static extern UInt32 HistoryViewerGetPosition(); + [DllImport(DLLPath, EntryPoint = "HistoryViewerGetSegments")] public static extern void HistoryViewerGetSegmentsWrapper(IntPtr segmentBuffer, ref UInt32 bufferSize); [DllImport(DLLPath)] public static extern void SetDisplayLanguage(Language lang); @@ -173,7 +176,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] private static extern void SetFlags(EmulationFlags flags); [DllImport(DLLPath)] private static extern void ClearFlags(EmulationFlags flags); [DllImport(DLLPath)] public static extern void SetRamPowerOnState(RamPowerOnState state); - [DllImport(DLLPath)] public static extern void SetMasterVolume(double volume, double volumeReduction); + [DllImport(DLLPath)] public static extern void SetMasterVolume(double volume, double volumeReduction, ConsoleId consoleId = ConsoleId.Master); [DllImport(DLLPath)] public static extern void SetChannelVolume(AudioChannel channel, double volume); [DllImport(DLLPath)] public static extern void SetChannelPanning(AudioChannel channel, double panning); [DllImport(DLLPath)] public static extern void SetEqualizerFilterType(EqualizerFilterType filter); @@ -198,7 +201,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void SetOverclockRate(UInt32 overclockRate, [MarshalAs(UnmanagedType.I1)]bool adjustApu); [DllImport(DLLPath)] public static extern void SetPpuNmiConfig(UInt32 extraScanlinesBeforeNmi, UInt32 extraScanlineAfterNmi); [DllImport(DLLPath)] public static extern void SetOverscanDimensions(UInt32 left, UInt32 right, UInt32 top, UInt32 bottom); - [DllImport(DLLPath)] public static extern void SetVideoScale(double scale); + [DllImport(DLLPath)] public static extern void SetVideoScale(double scale, ConsoleId consoleId = ConsoleId.Master); [DllImport(DLLPath)] public static extern void SetScreenRotation(UInt32 angle); [DllImport(DLLPath)] public static extern void SetExclusiveRefreshRate(UInt32 refreshRate); [DllImport(DLLPath)] public static extern void SetVideoAspectRatio(VideoAspectRatio aspectRatio, double customRatio); @@ -213,7 +216,7 @@ namespace Mesen.GUI [DllImport(DLLPath, EntryPoint = "GetRgbPalette")] private static extern void GetRgbPaletteWrapper(IntPtr paletteBuffer); - [DllImport(DLLPath, EntryPoint = "GetScreenSize")] private static extern void GetScreenSizeWrapper(out ScreenSize size, [MarshalAs(UnmanagedType.I1)]bool ignoreScale); + [DllImport(DLLPath, EntryPoint = "GetScreenSize")] private static extern void GetScreenSizeWrapper(ConsoleId consoleId, out ScreenSize size, [MarshalAs(UnmanagedType.I1)]bool ignoreScale); [DllImport(DLLPath, EntryPoint = "GetAudioDevices")] private static extern IntPtr GetAudioDevicesWrapper(); [DllImport(DLLPath)] public static extern void SetAudioDevice(string audioDevice); @@ -619,6 +622,7 @@ namespace Mesen.GUI return callstack; } + [DllImport(DLLPath)] private static extern Int32 DebugGetFunctionEntryPointCount(); [DllImport(DLLPath, EntryPoint = "DebugGetFunctionEntryPoints")] private static extern void DebugGetFunctionEntryPointsWrapper(IntPtr callstackAbsolute, Int32 maxCount); public static Int32[] DebugGetFunctionEntryPoints() @@ -724,13 +728,29 @@ namespace Mesen.GUI return new RomInfo(romInfo); } - public static ScreenSize GetScreenSize(bool ignoreScale) + public static ScreenSize GetScreenSize(bool ignoreScale, ConsoleId consoleId = ConsoleId.Master) { ScreenSize size; - GetScreenSizeWrapper(out size, ignoreScale); + GetScreenSizeWrapper(consoleId, out size, ignoreScale); return size; } + public static UInt32[] HistoryViewerGetSegments() + { + UInt32[] segmentBuffer = new UInt32[InteropEmu.HistoryViewerGetHistoryLength() / 30]; + UInt32 bufferSize = (UInt32)segmentBuffer.Length; + + GCHandle hSegmentBuffer = GCHandle.Alloc(segmentBuffer, GCHandleType.Pinned); + try { + InteropEmu.HistoryViewerGetSegmentsWrapper(hSegmentBuffer.AddrOfPinnedObject(), ref bufferSize); + } finally { + hSegmentBuffer.Free(); + } + Array.Resize(ref segmentBuffer, (int)bufferSize); + + return segmentBuffer; + } + public static void SetFlag(EmulationFlags flag, bool value) { if(value) { diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 449956de..768ee137 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -189,13 +189,18 @@ namespace InteropEmu { _console->Resume(); } - DllExport void __stdcall InitializeHistoryViewer(void *windowHandle, void *viewerHandle) + DllExport void __stdcall HistoryViewerInitialize(void *windowHandle, void *viewerHandle) { _historyConsole.reset(new Console(nullptr, _settings)); _historyConsole->Init(); _historyConsole->Initialize(_console->GetRomPath(), _console->GetPatchFile()); _historyConsole->CopyRewindData(_console); + //Force some settings + _historyConsole->GetSettings()->SetEmulationSpeed(100); + _historyConsole->GetSettings()->SetVideoScale(2); + _historyConsole->GetSettings()->ClearFlags(EmulationFlags::InBackground | EmulationFlags::Rewind | EmulationFlags::ForceMaxSpeed); + #ifdef _WIN32 _historyRenderer.reset(new Renderer(_historyConsole, (HWND)viewerHandle, false)); _historySoundManager.reset(new SoundManager(_historyConsole, (HWND)windowHandle)); @@ -205,7 +210,7 @@ namespace InteropEmu { #endif } - DllExport void __stdcall ReleaseHistoryViewer(void *windowHandle, void *viewerHandle) + DllExport void __stdcall HistoryViewerRelease(void *windowHandle, void *viewerHandle) { _historyConsole->Stop(); _historyConsole->Release(true); @@ -214,14 +219,14 @@ namespace InteropEmu { _historyConsole.reset(); } - DllExport void __stdcall RunHistoryViewer() + DllExport void __stdcall HistoryViewerRun() { if(_historyConsole) { _historyConsole->Run(); } } - DllExport uint32_t __stdcall GetHistoryViewerTotalFrameCount() + DllExport uint32_t __stdcall HistoryViewerGetHistoryLength() { if(_historyConsole) { return _historyConsole->GetHistoryViewer()->GetHistoryLength(); @@ -229,14 +234,36 @@ namespace InteropEmu { return 0; } - DllExport void __stdcall SetHistoryViewerPosition(uint32_t seekPosition) + DllExport void __stdcall HistoryViewerGetSegments(uint32_t* segmentBuffer, uint32_t &bufferSize) + { + if(_historyConsole) { + _historyConsole->GetHistoryViewer()->GetHistorySegments(segmentBuffer, bufferSize); + } + } + + DllExport bool __stdcall HistoryViewerSaveMovie(const char* movieFile, uint32_t startPosition, uint32_t endPosition) + { + if(_historyConsole) { + return _historyConsole->GetHistoryViewer()->SaveMovie(movieFile, startPosition, endPosition); + } + return false; + } + + DllExport void __stdcall HistoryViewerResumeGameplay(uint32_t resumeAtSecond) + { + if(_historyConsole) { + _historyConsole->GetHistoryViewer()->ResumeGameplay(_console, resumeAtSecond); + } + } + + DllExport void __stdcall HistoryViewerSetPosition(uint32_t seekPosition) { if(_historyConsole) { _historyConsole->GetHistoryViewer()->SeekTo(seekPosition); } } - DllExport uint32_t __stdcall GetHistoryViewerPosition() + DllExport uint32_t __stdcall HistoryViewerGetPosition() { if(_historyConsole) { return _historyConsole->GetHistoryViewer()->GetPosition(); @@ -576,7 +603,7 @@ namespace InteropEmu { DllExport void __stdcall SetEqualizerFilterType(EqualizerFilterType filter) { _settings->SetEqualizerFilterType(filter); } DllExport void __stdcall SetBandGain(uint32_t band, double gain) { _settings->SetBandGain(band, gain); } DllExport void __stdcall SetEqualizerBands(double *bands, uint32_t bandCount) { _settings->SetEqualizerBands(bands, bandCount); } - DllExport void __stdcall SetMasterVolume(double volume, double volumeReduction) { _settings->SetMasterVolume(volume, volumeReduction); } + DllExport void __stdcall SetMasterVolume(double volume, double volumeReduction, ConsoleId consoleId) { GetConsoleById(consoleId)->GetSettings()->SetMasterVolume(volume, volumeReduction); } DllExport void __stdcall SetSampleRate(uint32_t sampleRate) { _settings->SetSampleRate(sampleRate); } DllExport void __stdcall SetAudioLatency(uint32_t msLatency) { _settings->SetAudioLatency(msLatency); } DllExport void __stdcall SetStereoFilter(StereoFilter stereoFilter) { _settings->SetStereoFilter(stereoFilter); } @@ -596,7 +623,7 @@ namespace InteropEmu { DllExport void __stdcall SetRewindBufferSize(uint32_t seconds) { _settings->SetRewindBufferSize(seconds); } DllExport void __stdcall SetOverclockRate(uint32_t overclockRate, bool adjustApu) { _settings->SetOverclockRate(overclockRate, adjustApu); } DllExport void __stdcall SetPpuNmiConfig(uint32_t extraScanlinesBeforeNmi, uint32_t extraScanlinesAfterNmi) { _settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi); } - DllExport void __stdcall SetVideoScale(double scale) { _settings->SetVideoScale(scale); } + DllExport void __stdcall SetVideoScale(double scale, ConsoleId consoleId) { GetConsoleById(consoleId)->GetSettings()->SetVideoScale(scale); } DllExport void __stdcall SetScreenRotation(uint32_t angle) { _settings->SetScreenRotation(angle); } DllExport void __stdcall SetExclusiveRefreshRate(uint32_t angle) { _settings->SetExclusiveRefreshRate(angle); } DllExport void __stdcall SetVideoAspectRatio(VideoAspectRatio aspectRatio, double customRatio) { _settings->SetVideoAspectRatio(aspectRatio, customRatio); } @@ -619,7 +646,7 @@ namespace InteropEmu { DllExport void __stdcall SetAudioDevice(char* audioDevice) { if(_soundManager) { _soundManager->SetAudioDevice(audioDevice); } } - DllExport void __stdcall GetScreenSize(ScreenSize &size, bool ignoreScale) { _console->GetVideoDecoder()->GetScreenSize(size, ignoreScale); } + DllExport void __stdcall GetScreenSize(ConsoleId consoleId, ScreenSize &size, bool ignoreScale) { GetConsoleById(consoleId)->GetVideoDecoder()->GetScreenSize(size, ignoreScale); } DllExport void __stdcall InputBarcode(uint64_t barCode, int32_t digitCount) { _console->InputBarcode(barCode, digitCount); }