diff --git a/Core/BaseVideoFilter.cpp b/Core/BaseVideoFilter.cpp index 8053d373..ca57d29a 100644 --- a/Core/BaseVideoFilter.cpp +++ b/Core/BaseVideoFilter.cpp @@ -69,7 +69,7 @@ uint32_t* BaseVideoFilter::GetOutputBuffer() return _outputBuffer; } -void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream) +void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream, bool rawScreenshot) { uint32_t* pngBuffer; FrameInfo frameInfo; @@ -87,22 +87,24 @@ void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename pngBuffer = frameBuffer; - uint32_t rotationAngle = _console->GetSettings()->GetScreenRotation(); - shared_ptr rotateFilter; - if(rotationAngle > 0) { - rotateFilter.reset(new RotateFilter(rotationAngle)); - pngBuffer = rotateFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height); - frameInfo = rotateFilter->GetFrameInfo(frameInfo); - } + if(!rawScreenshot) { + uint32_t rotationAngle = _console->GetSettings()->GetScreenRotation(); + shared_ptr rotateFilter; + if(rotationAngle > 0) { + rotateFilter.reset(new RotateFilter(rotationAngle)); + pngBuffer = rotateFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height); + frameInfo = rotateFilter->GetFrameInfo(frameInfo); + } - shared_ptr scaleFilter = ScaleFilter::GetScaleFilter(filterType); - if(scaleFilter) { - pngBuffer = scaleFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetPictureSettings().ScanlineIntensity); - frameInfo = scaleFilter->GetFrameInfo(frameInfo); - } + shared_ptr scaleFilter = ScaleFilter::GetScaleFilter(filterType); + if(scaleFilter) { + pngBuffer = scaleFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetPictureSettings().ScanlineIntensity); + frameInfo = scaleFilter->GetFrameInfo(frameInfo); + } - VideoHud hud; - hud.DrawHud(_console, pngBuffer, frameInfo, _console->GetSettings()->GetOverscanDimensions()); + VideoHud hud; + hud.DrawHud(_console, pngBuffer, frameInfo, _console->GetSettings()->GetOverscanDimensions()); + } if(!filename.empty()) { PNGHelper::WritePNG(filename, pngBuffer, frameInfo.Width, frameInfo.Height); diff --git a/Core/BaseVideoFilter.h b/Core/BaseVideoFilter.h index 8ef7bb5c..ed9ad630 100644 --- a/Core/BaseVideoFilter.h +++ b/Core/BaseVideoFilter.h @@ -32,7 +32,7 @@ public: uint32_t* GetOutputBuffer(); void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber); void TakeScreenshot(string romName, VideoFilterType filterType); - void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr); + void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr, bool rawScreenshot = false); virtual OverscanDimensions GetOverscan(); virtual FrameInfo GetFrameInfo() = 0; diff --git a/Core/EmulationSettings.h b/Core/EmulationSettings.h index 20c9e261..47be9568 100644 --- a/Core/EmulationSettings.h +++ b/Core/EmulationSettings.h @@ -472,6 +472,7 @@ enum class EmulatorShortcut SaveStateSlot9, SaveStateSlot10, SaveStateToFile, + SaveStateDialog, LoadStateSlot1, LoadStateSlot2, @@ -485,6 +486,7 @@ enum class EmulatorShortcut LoadStateSlot10, LoadStateSlotAuto, LoadStateFromFile, + LoadStateDialog, LoadLastSession, diff --git a/Core/SaveStateManager.cpp b/Core/SaveStateManager.cpp index 66af9c01..24bc58f5 100644 --- a/Core/SaveStateManager.cpp +++ b/Core/SaveStateManager.cpp @@ -79,6 +79,12 @@ void SaveStateManager::GetSaveStateHeader(ostream &stream) string sha1Hash = romInfo.Hash.Sha1; stream.write(sha1Hash.c_str(), sha1Hash.size()); + std::stringstream screenshotStream; + _console->GetVideoDecoder()->TakeScreenshot(screenshotStream, true); + uint32_t screenshotLength = (uint32_t)screenshotStream.tellp(); + stream.write((char*)&screenshotLength, sizeof(uint32_t)); + stream.write(screenshotStream.str().c_str(), screenshotLength); + string romName = romInfo.RomName; uint32_t nameLength = (uint32_t)romName.size(); stream.write((char*)&nameLength, sizeof(uint32_t)); @@ -151,6 +157,13 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired) char hash[41] = {}; stream.read(hash, 40); + if(fileFormatVersion >= 13) { + //Skip screenshot data + uint32_t screenshotLength = 0; + stream.read((char*)&screenshotLength, sizeof(uint32_t)); + stream.seekg(screenshotLength, std::ios::cur); + } + uint32_t nameLength = 0; stream.read((char*)&nameLength, sizeof(uint32_t)); @@ -229,7 +242,7 @@ void SaveStateManager::SaveRecentGame(string romName, string romPath, string pat writer.Initialize(FolderUtilities::CombinePath(FolderUtilities::GetRecentGamesFolder(), filename)); std::stringstream pngStream; - _console->GetVideoDecoder()->TakeScreenshot(pngStream); + _console->GetVideoDecoder()->TakeScreenshot(pngStream, true); writer.AddFile(pngStream, "Screenshot.png"); std::stringstream stateStream; @@ -270,4 +283,45 @@ void SaveStateManager::LoadRecentGame(string filename, bool resetGame) _console->Stop(); } _console->Resume(); +} + +int32_t SaveStateManager::GetSaveStatePreview(string saveStatePath, uint8_t* pngData) +{ + ifstream stream(saveStatePath, ios::binary); + + if(!stream) { + return -1; + } + + char header[3]; + stream.read(header, 3); + if(memcmp(header, "MST", 3) == 0) { + uint32_t emuVersion = 0; + + stream.read((char*)&emuVersion, sizeof(emuVersion)); + if(emuVersion > EmulationSettings::GetMesenVersion()) { + return -1; + } + + uint32_t fileFormatVersion = 0; + stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion)); + if(fileFormatVersion <= 12) { + return -1; + } + + //Skip some header fields + stream.seekg(43, ios::cur); + + uint32_t screenshotLength = 0; + stream.read((char*)&screenshotLength, sizeof(screenshotLength)); + + if(screenshotLength > 0) { + stream.read((char*)pngData, screenshotLength); + return screenshotLength; + } + + return -1; + } + + return -1; } \ No newline at end of file diff --git a/Core/SaveStateManager.h b/Core/SaveStateManager.h index 4d04012c..3f8b2bef 100644 --- a/Core/SaveStateManager.h +++ b/Core/SaveStateManager.h @@ -14,7 +14,7 @@ private: string GetStateFilepath(int stateIndex); public: - static constexpr uint32_t FileFormatVersion = 12; + static constexpr uint32_t FileFormatVersion = 13; SaveStateManager(shared_ptr console); @@ -35,6 +35,8 @@ public: void SaveRecentGame(string romName, string romPath, string patchPath); void LoadRecentGame(string filename, bool resetGame); + int32_t GetSaveStatePreview(string saveStatePath, uint8_t* pngData); + void SelectSaveSlot(int slotIndex); void MoveToNextSlot(); void MoveToPreviousSlot(); diff --git a/Core/VideoDecoder.cpp b/Core/VideoDecoder.cpp index 350cb56d..8954a0e3 100644 --- a/Core/VideoDecoder.cpp +++ b/Core/VideoDecoder.cpp @@ -245,9 +245,14 @@ void VideoDecoder::TakeScreenshot() } } -void VideoDecoder::TakeScreenshot(std::stringstream &stream) +void VideoDecoder::TakeScreenshot(std::stringstream &stream, bool rawScreenshot) { - if(_videoFilter) { - _videoFilter->TakeScreenshot(_videoFilterType, "", &stream); + if(rawScreenshot) { + //Take screenshot without NTSC filter on + DefaultVideoFilter filter(_console); + filter.SendFrame(_ppuOutputBuffer, 0); + filter.TakeScreenshot(_videoFilterType, "", &stream, rawScreenshot); + } else if(_videoFilter) { + _videoFilter->TakeScreenshot(_videoFilterType, "", &stream, rawScreenshot); } } diff --git a/Core/VideoDecoder.h b/Core/VideoDecoder.h index 4badae2d..7b9f29b4 100644 --- a/Core/VideoDecoder.h +++ b/Core/VideoDecoder.h @@ -61,7 +61,7 @@ public: void DecodeFrame(bool synchronous = false); void TakeScreenshot(); - void TakeScreenshot(std::stringstream &stream); + void TakeScreenshot(std::stringstream &stream, bool rawScreenshot = false); uint32_t GetFrameCount(); diff --git a/GUI.NET/Controls/ctrlRecentGame.Designer.cs b/GUI.NET/Controls/ctrlRecentGame.Designer.cs index 70764684..1e7e742f 100644 --- a/GUI.NET/Controls/ctrlRecentGame.Designer.cs +++ b/GUI.NET/Controls/ctrlRecentGame.Designer.cs @@ -28,9 +28,9 @@ private void InitializeComponent() { this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.picPreviousState = new Mesen.GUI.Controls.GamePreviewBox(); this.lblGameName = new System.Windows.Forms.Label(); this.lblSaveDate = new System.Windows.Forms.Label(); - this.picPreviousState = new Mesen.GUI.Controls.GamePreviewBox(); this.tableLayoutPanel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picPreviousState)).BeginInit(); this.SuspendLayout(); @@ -53,36 +53,12 @@ this.tableLayoutPanel1.Size = new System.Drawing.Size(158, 93); this.tableLayoutPanel1.TabIndex = 0; // - // lblGameName - // - this.lblGameName.AutoEllipsis = true; - this.lblGameName.BackColor = System.Drawing.Color.Transparent; - this.lblGameName.Dock = System.Windows.Forms.DockStyle.Fill; - this.lblGameName.ForeColor = System.Drawing.Color.White; - this.lblGameName.Location = new System.Drawing.Point(3, 64); - this.lblGameName.Name = "lblGameName"; - this.lblGameName.Size = new System.Drawing.Size(152, 16); - this.lblGameName.TabIndex = 12; - this.lblGameName.Text = "Game Name"; - this.lblGameName.TextAlign = System.Drawing.ContentAlignment.TopCenter; - // - // lblSaveDate - // - this.lblSaveDate.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.lblSaveDate.AutoSize = true; - this.lblSaveDate.BackColor = System.Drawing.Color.Transparent; - this.lblSaveDate.ForeColor = System.Drawing.Color.White; - this.lblSaveDate.Location = new System.Drawing.Point(64, 80); - this.lblSaveDate.Name = "lblSaveDate"; - this.lblSaveDate.Size = new System.Drawing.Size(30, 13); - this.lblSaveDate.TabIndex = 13; - this.lblSaveDate.Text = "Date"; - // // picPreviousState // this.picPreviousState.BackColor = System.Drawing.Color.Black; this.picPreviousState.Cursor = System.Windows.Forms.Cursors.Hand; this.picPreviousState.Dock = System.Windows.Forms.DockStyle.Fill; + this.picPreviousState.Highlight = false; this.picPreviousState.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; this.picPreviousState.Location = new System.Drawing.Point(0, 0); this.picPreviousState.Margin = new System.Windows.Forms.Padding(0); @@ -94,6 +70,31 @@ this.picPreviousState.Visible = false; this.picPreviousState.Click += new System.EventHandler(this.picPreviousState_Click); // + // lblGameName + // + this.lblGameName.AutoEllipsis = true; + this.lblGameName.BackColor = System.Drawing.Color.Transparent; + this.lblGameName.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblGameName.ForeColor = System.Drawing.Color.White; + this.lblGameName.Location = new System.Drawing.Point(3, 64); + this.lblGameName.Name = "lblGameName"; + this.lblGameName.Size = new System.Drawing.Size(152, 16); + this.lblGameName.TabIndex = 12; + this.lblGameName.Text = "Game Name"; + this.lblGameName.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // lblSaveDate + // + this.lblSaveDate.BackColor = System.Drawing.Color.Transparent; + this.lblSaveDate.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblSaveDate.ForeColor = System.Drawing.Color.White; + this.lblSaveDate.Location = new System.Drawing.Point(3, 80); + this.lblSaveDate.Name = "lblSaveDate"; + this.lblSaveDate.Size = new System.Drawing.Size(152, 13); + this.lblSaveDate.TabIndex = 13; + this.lblSaveDate.Text = "Date"; + this.lblSaveDate.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // // ctrlRecentGame // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -103,7 +104,6 @@ this.Name = "ctrlRecentGame"; this.Size = new System.Drawing.Size(158, 93); this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); ((System.ComponentModel.ISupportInitialize)(this.picPreviousState)).EndInit(); this.ResumeLayout(false); diff --git a/GUI.NET/Controls/ctrlRecentGame.cs b/GUI.NET/Controls/ctrlRecentGame.cs index 30427aad..129c5436 100644 --- a/GUI.NET/Controls/ctrlRecentGame.cs +++ b/GUI.NET/Controls/ctrlRecentGame.cs @@ -11,6 +11,8 @@ using System.IO; using System.IO.Compression; using Mesen.GUI.Config; using static Mesen.GUI.Controls.ctrlRecentGames; +using System.Drawing.Drawing2D; +using Mesen.GUI.Forms; namespace Mesen.GUI.Controls { @@ -24,6 +26,8 @@ namespace Mesen.GUI.Controls InitializeComponent(); } + public GameScreenMode Mode { get; set; } + public RecentGameInfo RecentGame { get { return _recentGame; } @@ -43,33 +47,52 @@ namespace Mesen.GUI.Controls _recentGame = value; - lblGameName.Text = Path.GetFileNameWithoutExtension(_recentGame.FileName); - lblSaveDate.Text = new FileInfo(_recentGame.FileName).LastWriteTime.ToString(); + if(this.Mode == GameScreenMode.RecentGames) { + lblGameName.Text = !string.IsNullOrEmpty(value.Name) ? value.Name : Path.GetFileNameWithoutExtension(_recentGame.FileName); + lblGameName.Visible = true; + } else { + lblGameName.Text = value.Name; + lblGameName.Visible = !string.IsNullOrEmpty(value.Name); + } + + bool fileExists = File.Exists(_recentGame.FileName); + if(fileExists) { + lblSaveDate.Text = new FileInfo(_recentGame.FileName).LastWriteTime.ToString(); + } else { + lblSaveDate.Text = ResourceHelper.GetMessage("EmptyState"); + picPreviousState.Image = null; + } + this.Enabled = fileExists || this.Mode == GameScreenMode.SaveState; - lblGameName.Visible = true; lblSaveDate.Visible = true; picPreviousState.Visible = true; - Task.Run(() => { - Image img = null; - try { - ZipArchive zip = new ZipArchive(new MemoryStream(File.ReadAllBytes(value.FileName))); - ZipArchiveEntry entry = zip.GetEntry("Screenshot.png"); - if(entry != null) { - using(Stream stream = entry.Open()) { - img = Image.FromStream(stream); + if(fileExists) { + Task.Run(() => { + Image img = null; + try { + if(this.Mode != GameScreenMode.RecentGames && Path.GetExtension(value.FileName) == ".mst") { + img = InteropEmu.GetSaveStatePreview(value.FileName); + } else { + ZipArchive zip = new ZipArchive(new MemoryStream(File.ReadAllBytes(value.FileName))); + ZipArchiveEntry entry = zip.GetEntry("Screenshot.png"); + if(entry != null) { + using(Stream stream = entry.Open()) { + img = Image.FromStream(stream); + } + } + using(StreamReader sr = new StreamReader(zip.GetEntry("RomInfo.txt").Open())) { + sr.ReadLine(); //skip first line (rom name) + value.RomPath = sr.ReadLine(); + } } - } - using(StreamReader sr = new StreamReader(zip.GetEntry("RomInfo.txt").Open())) { - string romName = sr.ReadLine(); - value.RomPath = sr.ReadLine(); - } - } catch { } + } catch { } - this.BeginInvoke((Action)(() => { - picPreviousState.Image = img; - })); - }); + this.BeginInvoke((Action)(() => { + picPreviousState.Image = img; + })); + }); + } } } @@ -80,8 +103,78 @@ namespace Mesen.GUI.Controls private void picPreviousState_Click(object sender, EventArgs e) { - InteropEmu.LoadRecentGame(_recentGame.FileName, ConfigManager.Config.PreferenceInfo.GameSelectionScreenResetGame); + ProcessClick(); + } + + public void ProcessClick() + { + if(!this.Enabled) { + return; + } + + if(Path.GetExtension(_recentGame.FileName) == ".rgd") { + InteropEmu.LoadRecentGame(_recentGame.FileName, ConfigManager.Config.PreferenceInfo.GameSelectionScreenResetGame); + } else { + switch(this.Mode) { + case GameScreenMode.LoadState: InteropEmu.LoadStateFile(_recentGame.FileName); break; + case GameScreenMode.SaveState: InteropEmu.SaveStateFile(_recentGame.FileName); break; + } + } OnRecentGameLoaded?.Invoke(_recentGame); } } + + public class GamePreviewBox : PictureBox + { + public InterpolationMode InterpolationMode { get; set; } + private bool _hovered = false; + private bool _highlight = false; + + public GamePreviewBox() + { + DoubleBuffered = true; + InterpolationMode = InterpolationMode.Default; + } + + public bool Highlight + { + get { return _highlight; } + set + { + _highlight = value; + this.Invalidate(); + } + } + + protected override void OnEnabledChanged(EventArgs e) + { + base.OnEnabledChanged(e); + _hovered = false; + this.Invalidate(); + } + + protected override void OnMouseEnter(EventArgs e) + { + base.OnMouseEnter(e); + _hovered = true; + this.Invalidate(); + } + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + _hovered = false; + this.Invalidate(); + } + + protected override void OnPaint(PaintEventArgs pe) + { + pe.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + base.OnPaint(pe); + + using(Pen pen = new Pen(_hovered && this.Enabled ? Color.DeepSkyBlue : (_highlight ? Color.DodgerBlue : Color.DimGray), 2)) { + pe.Graphics.DrawRectangle(pen, 1, 1, this.Width - 2, this.Height - 2); + } + } + } } diff --git a/GUI.NET/Controls/ctrlRecentGames.Designer.cs b/GUI.NET/Controls/ctrlRecentGames.Designer.cs index 1a856e84..bb28b8d5 100644 --- a/GUI.NET/Controls/ctrlRecentGames.Designer.cs +++ b/GUI.NET/Controls/ctrlRecentGames.Designer.cs @@ -29,13 +29,20 @@ { this.components = new System.ComponentModel.Container(); this.tlpPreviousState = new Mesen.GUI.Controls.DBTableLayoutPanel(); + this.baseControl2 = new Mesen.GUI.Controls.BaseControl(); this.picNextGame = new System.Windows.Forms.PictureBox(); this.picPrevGame = new System.Windows.Forms.PictureBox(); + this.tlpGrid = new Mesen.GUI.Controls.DBTableLayoutPanel(); + this.tlpTitle = new Mesen.GUI.Controls.DBTableLayoutPanel(); + this.lblScreenTitle = new System.Windows.Forms.Label(); + this.picClose = new System.Windows.Forms.PictureBox(); + this.baseControl1 = new Mesen.GUI.Controls.BaseControl(); this.tmrInput = new System.Windows.Forms.Timer(this.components); - this.tlpGrid = new DBTableLayoutPanel(); this.tlpPreviousState.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picNextGame)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.picPrevGame)).BeginInit(); + this.tlpTitle.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picClose)).BeginInit(); this.SuspendLayout(); // // tlpPreviousState @@ -45,29 +52,37 @@ this.tlpPreviousState.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tlpPreviousState.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tlpPreviousState.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tlpPreviousState.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpPreviousState.Controls.Add(this.baseControl2, 2, 0); this.tlpPreviousState.Controls.Add(this.picNextGame, 2, 1); this.tlpPreviousState.Controls.Add(this.picPrevGame, 0, 1); this.tlpPreviousState.Controls.Add(this.tlpGrid, 1, 1); + this.tlpPreviousState.Controls.Add(this.tlpTitle, 1, 0); + this.tlpPreviousState.Controls.Add(this.baseControl1, 0, 0); this.tlpPreviousState.Dock = System.Windows.Forms.DockStyle.Fill; this.tlpPreviousState.Location = new System.Drawing.Point(0, 0); this.tlpPreviousState.Name = "tlpPreviousState"; this.tlpPreviousState.RowCount = 3; - this.tlpPreviousState.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 10F)); + this.tlpPreviousState.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tlpPreviousState.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tlpPreviousState.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); - this.tlpPreviousState.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tlpPreviousState.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 10F)); this.tlpPreviousState.Size = new System.Drawing.Size(272, 236); this.tlpPreviousState.TabIndex = 9; // + // baseControl2 + // + this.baseControl2.Location = new System.Drawing.Point(242, 3); + this.baseControl2.Name = "baseControl2"; + this.baseControl2.Size = new System.Drawing.Size(6, 6); + this.baseControl2.TabIndex = 18; + // // picNextGame // this.picNextGame.Cursor = System.Windows.Forms.Cursors.Hand; this.picNextGame.Dock = System.Windows.Forms.DockStyle.Right; this.picNextGame.Image = global::Mesen.GUI.Properties.Resources.Play; - this.picNextGame.Location = new System.Drawing.Point(242, 13); + this.picNextGame.Location = new System.Drawing.Point(242, 35); this.picNextGame.Name = "picNextGame"; - this.picNextGame.Size = new System.Drawing.Size(27, 200); + this.picNextGame.Size = new System.Drawing.Size(27, 188); this.picNextGame.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; this.picNextGame.TabIndex = 11; this.picNextGame.TabStop = false; @@ -78,34 +93,87 @@ this.picPrevGame.Cursor = System.Windows.Forms.Cursors.Hand; this.picPrevGame.Dock = System.Windows.Forms.DockStyle.Left; this.picPrevGame.Image = global::Mesen.GUI.Properties.Resources.Play; - this.picPrevGame.Location = new System.Drawing.Point(3, 13); + this.picPrevGame.Location = new System.Drawing.Point(3, 35); this.picPrevGame.Name = "picPrevGame"; - this.picPrevGame.Size = new System.Drawing.Size(27, 200); + this.picPrevGame.Size = new System.Drawing.Size(27, 188); this.picPrevGame.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; this.picPrevGame.TabIndex = 12; this.picPrevGame.TabStop = false; this.picPrevGame.MouseDown += new System.Windows.Forms.MouseEventHandler(this.picPrevGame_MouseDown); // - // tmrInput - // - this.tmrInput.Interval = 50; - this.tmrInput.Tick += new System.EventHandler(this.tmrInput_Tick); - // // tlpGrid // + this.tlpGrid.BackColor = System.Drawing.Color.Black; this.tlpGrid.ColumnCount = 2; this.tlpGrid.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tlpGrid.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tlpGrid.Dock = System.Windows.Forms.DockStyle.Fill; - this.tlpGrid.Location = new System.Drawing.Point(33, 10); - this.tlpGrid.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0); + this.tlpGrid.Location = new System.Drawing.Point(33, 32); + this.tlpGrid.Margin = new System.Windows.Forms.Padding(0); this.tlpGrid.Name = "tlpGrid"; this.tlpGrid.RowCount = 2; this.tlpGrid.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); this.tlpGrid.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); - this.tlpGrid.Size = new System.Drawing.Size(206, 206); + this.tlpGrid.Size = new System.Drawing.Size(206, 194); this.tlpGrid.TabIndex = 13; // + // tlpTitle + // + this.tlpTitle.BackColor = System.Drawing.Color.Black; + this.tlpTitle.ColumnCount = 2; + this.tlpTitle.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpTitle.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tlpTitle.Controls.Add(this.lblScreenTitle, 0, 0); + this.tlpTitle.Controls.Add(this.picClose, 1, 0); + this.tlpTitle.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpTitle.Location = new System.Drawing.Point(33, 0); + this.tlpTitle.Margin = new System.Windows.Forms.Padding(0); + this.tlpTitle.Name = "tlpTitle"; + this.tlpTitle.RowCount = 1; + this.tlpTitle.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpTitle.Size = new System.Drawing.Size(206, 32); + this.tlpTitle.TabIndex = 16; + // + // lblScreenTitle + // + this.lblScreenTitle.Anchor = System.Windows.Forms.AnchorStyles.None; + this.lblScreenTitle.AutoSize = true; + this.lblScreenTitle.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblScreenTitle.ForeColor = System.Drawing.Color.White; + this.lblScreenTitle.Location = new System.Drawing.Point(58, 7); + this.lblScreenTitle.Margin = new System.Windows.Forms.Padding(35, 0, 3, 0); + this.lblScreenTitle.Name = "lblScreenTitle"; + this.lblScreenTitle.Size = new System.Drawing.Size(89, 18); + this.lblScreenTitle.TabIndex = 14; + this.lblScreenTitle.Text = "Load State"; + this.lblScreenTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // picClose + // + this.picClose.Anchor = System.Windows.Forms.AnchorStyles.None; + this.picClose.Cursor = System.Windows.Forms.Cursors.Hand; + this.picClose.Image = global::Mesen.GUI.Properties.Resources.CloseWhite; + this.picClose.Location = new System.Drawing.Point(174, 0); + this.picClose.Margin = new System.Windows.Forms.Padding(0); + this.picClose.Name = "picClose"; + this.picClose.Padding = new System.Windows.Forms.Padding(8); + this.picClose.Size = new System.Drawing.Size(32, 32); + this.picClose.TabIndex = 15; + this.picClose.TabStop = false; + this.picClose.Click += new System.EventHandler(this.picClose_Click); + // + // baseControl1 + // + this.baseControl1.Location = new System.Drawing.Point(3, 3); + this.baseControl1.Name = "baseControl1"; + this.baseControl1.Size = new System.Drawing.Size(6, 6); + this.baseControl1.TabIndex = 17; + // + // tmrInput + // + this.tmrInput.Interval = 50; + this.tmrInput.Tick += new System.EventHandler(this.tmrInput_Tick); + // // ctrlRecentGames // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -117,6 +185,9 @@ this.tlpPreviousState.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.picNextGame)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.picPrevGame)).EndInit(); + this.tlpTitle.ResumeLayout(false); + this.tlpTitle.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picClose)).EndInit(); this.ResumeLayout(false); } @@ -128,5 +199,10 @@ private System.Windows.Forms.PictureBox picPrevGame; private System.Windows.Forms.Timer tmrInput; private DBTableLayoutPanel tlpGrid; - } + private System.Windows.Forms.Label lblScreenTitle; + private System.Windows.Forms.PictureBox picClose; + private DBTableLayoutPanel tlpTitle; + private BaseControl baseControl2; + private BaseControl baseControl1; + } } diff --git a/GUI.NET/Controls/ctrlRecentGames.cs b/GUI.NET/Controls/ctrlRecentGames.cs index 72cab161..b2fe1742 100644 --- a/GUI.NET/Controls/ctrlRecentGames.cs +++ b/GUI.NET/Controls/ctrlRecentGames.cs @@ -9,9 +9,6 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.IO; using Mesen.GUI.Config; -using System.Drawing.Text; -using System.Drawing.Drawing2D; -using System.IO.Compression; using Mesen.GUI.Forms; using Mesen.GUI.Controls; @@ -19,29 +16,30 @@ namespace Mesen.GUI.Controls { public partial class ctrlRecentGames : BaseControl { - private int _elementsPerRow = 0; + private int _columnCount = 0; + private int _rowCount = 0; private int _elementsPerPage = 0; + private bool _needResume = false; + private int _currentIndex = 0; + private List _recentGames = new List(); + private List _controls = new List(); + public delegate void RecentGameLoadedHandler(RecentGameInfo gameInfo); public event RecentGameLoadedHandler OnRecentGameLoaded; public new event MouseEventHandler MouseMove { - add { this.tlpPreviousState.MouseMove += value; } - remove { this.tlpPreviousState.MouseMove -= value; } + add { this.tlpPreviousState.MouseMove += value; this.tlpTitle.MouseMove += value; } + remove { this.tlpPreviousState.MouseMove -= value; this.tlpTitle.MouseMove -= value; } } public new event EventHandler DoubleClick { - add { this.tlpPreviousState.DoubleClick += value; } - remove { this.tlpPreviousState.DoubleClick -= value; } + add { this.tlpPreviousState.DoubleClick += value; this.tlpTitle.DoubleClick += value; } + remove { this.tlpPreviousState.DoubleClick -= value; this.tlpTitle.DoubleClick -= value; } } - private bool _initialized = false; - private int _currentIndex = 0; - private List _recentGames = new List(); - private List _controls = new List(); - public ctrlRecentGames() { InitializeComponent(); @@ -56,40 +54,50 @@ namespace Mesen.GUI.Controls private void InitGrid() { - int elementsPerRow = 1; + int columnCount = 1; if(ClientSize.Width > 850 && ClientSize.Height > 850) { - elementsPerRow = 3; + columnCount = 3; } else if(ClientSize.Width > 450 && ClientSize.Height > 450) { - elementsPerRow = 2; + columnCount = 2; } if(_recentGames.Count <= 1) { - elementsPerRow = 1; + columnCount = 1; } else if(_recentGames.Count <= 4) { - elementsPerRow = Math.Min(2, elementsPerRow); + columnCount = Math.Min(2, columnCount); } - if(_elementsPerRow == elementsPerRow) { + int elementsPerPage = columnCount * columnCount; + int rowCount = columnCount; + + if(Mode != GameScreenMode.RecentGames) { + elementsPerPage = 12; + columnCount = 4; + rowCount = 3; + } + + if(_columnCount == columnCount && _elementsPerPage == elementsPerPage && _rowCount == rowCount) { return; } - _elementsPerRow = elementsPerRow; - _elementsPerPage = elementsPerRow * elementsPerRow; + _columnCount = columnCount; + _rowCount = rowCount; + _elementsPerPage = elementsPerPage; _controls = new List(); tlpGrid.SuspendLayout(); - tlpGrid.ColumnCount = _elementsPerRow; - tlpGrid.RowCount = _elementsPerRow; + tlpGrid.ColumnCount = _columnCount; + tlpGrid.RowCount = _rowCount; tlpGrid.ColumnStyles.Clear(); tlpGrid.RowStyles.Clear(); tlpGrid.Controls.Clear(); - for(int j = 0; j < _elementsPerRow; j++) { - tlpGrid.RowStyles.Add(new RowStyle(SizeType.Percent, 100F / _elementsPerRow)); - tlpGrid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F / _elementsPerRow)); + for(int j = 0; j < _columnCount; j++) { + tlpGrid.RowStyles.Add(new RowStyle(SizeType.Percent, 100F / _columnCount)); + tlpGrid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F / _columnCount)); } - for(int j = 0; j < _elementsPerRow; j++) { - for(int i = 0; i < _elementsPerRow; i++) { + for(int j = 0; j < _rowCount; j++) { + for(int i = 0; i < _columnCount; i++) { ctrlRecentGame ctrl = new ctrlRecentGame(); ctrl.OnRecentGameLoaded += RecentGameLoaded; ctrl.Dock = DockStyle.Fill; @@ -105,45 +113,59 @@ namespace Mesen.GUI.Controls picNextGame.Visible = _recentGames.Count > _elementsPerPage; } - public new bool Visible - { - get { return base.Visible; } - set - { - if(value && ((_initialized && _recentGames.Count == 0) || ConfigManager.Config.PreferenceInfo.DisableGameSelectionScreen)) { - value = false; - } - - if(value != base.Visible) { - if(value && !_initialized) { - //We just re-enabled the screen, initialize it - Initialize(); - } - InitGrid(); - base.Visible = value; - tmrInput.Enabled = value; - } - } - } - public int GameCount { get { return _recentGames.Count; } } - public void Initialize() + public GameScreenMode Mode { get; private set; } = GameScreenMode.RecentGames; + + private bool Pause() { - _initialized = true; + if(!InteropEmu.IsPaused()) { + InteropEmu.Pause(); + return true; + } + return false; + } + + public void ShowScreen(GameScreenMode mode) + { + if(mode == GameScreenMode.RecentGames && ConfigManager.Config.PreferenceInfo.DisableGameSelectionScreen) { + this.Visible = false; + return; + } else if(mode != GameScreenMode.RecentGames && Mode == mode && this.Visible) { + this.Visible = false; + if(_needResume) { + InteropEmu.Resume(); + } + return; + } + + Mode = mode; _recentGames = new List(); _currentIndex = 0; - List files = Directory.GetFiles(ConfigManager.RecentGamesFolder, "*.rgd").OrderByDescending((file) => new FileInfo(file).LastWriteTime).ToList(); - for(int i = 0; i < files.Count && _recentGames.Count < 36; i++) { - try { - RecentGameInfo info = new RecentGameInfo(); - info.FileName = files[i]; - _recentGames.Add(info); - } catch { } + if(mode == GameScreenMode.RecentGames) { + _needResume = false; + tlpTitle.Visible = false; + List files = Directory.GetFiles(ConfigManager.RecentGamesFolder, "*.rgd").OrderByDescending((file) => new FileInfo(file).LastWriteTime).ToList(); + for(int i = 0; i < files.Count && _recentGames.Count < 36; i++) { + _recentGames.Add(new RecentGameInfo() { FileName = files[i] }); + } + } else { + if(!this.Visible) { + _needResume = Pause(); + } + + lblScreenTitle.Text = mode == GameScreenMode.LoadState ? ResourceHelper.GetMessage("LoadStateDialog") : ResourceHelper.GetMessage("SaveStateDialog"); + tlpTitle.Visible = true; + + string romName = InteropEmu.GetRomInfo().GetRomName(); + for(int i = 0; i < 11; i++) { + _recentGames.Add(new RecentGameInfo() { FileName = Path.Combine(ConfigManager.SaveStateFolder, romName + "_" + (i + 1) + ".mst"), Name = i == 10 ? ResourceHelper.GetMessage("AutoSave") : ResourceHelper.GetMessage("SlotNumber", i+1) }); + } + _recentGames.Add(new RecentGameInfo() { FileName = Path.Combine(ConfigManager.RecentGamesFolder, romName + ".rgd"), Name = ResourceHelper.GetMessage("LastSession") }); } InitGrid(); @@ -158,18 +180,16 @@ namespace Mesen.GUI.Controls picPrevGame.Visible = _recentGames.Count > _elementsPerPage; picNextGame.Visible = _recentGames.Count > _elementsPerPage; + this.Visible = true; } public void UpdateGameInfo() { - if(!_initialized) { - return; - } - int count = _recentGames.Count; int pageStart = _currentIndex / _elementsPerPage * _elementsPerPage; for(int i = 0; i < _elementsPerPage; i++) { + _controls[i].Mode = Mode; _controls[i].RecentGame = count > pageStart + i ? _recentGames[pageStart + i] : null; _controls[i].Highlight = (_currentIndex % _elementsPerPage) == i; } @@ -185,7 +205,7 @@ namespace Mesen.GUI.Controls picPrevGame.Dock = DockStyle.Fill; } - if(this._elementsPerRow > 0) { + if(this._columnCount > 0) { InitGrid(); } base.OnResize(e); @@ -223,22 +243,24 @@ namespace Mesen.GUI.Controls UpdateGameInfo(); } - private void LoadSelectedGame() - { - InteropEmu.LoadRecentGame(_recentGames[_currentIndex].FileName, ConfigManager.Config.PreferenceInfo.GameSelectionScreenResetGame); - OnRecentGameLoaded?.Invoke(_recentGames[_currentIndex]); - } - private void RecentGameLoaded(RecentGameInfo gameInfo) { OnRecentGameLoaded?.Invoke(gameInfo); + if(this._needResume) { + InteropEmu.Resume(); + } + this.Visible = false; } private bool _waitForRelease = false; private void tmrInput_Tick(object sender, EventArgs e) { //Use player 1's controls to navigate the recent game selection screen - if(Application.OpenForms.Count > 0 && Application.OpenForms[0].ContainsFocus && !InteropEmu.IsRunning()) { + if(Application.OpenForms.Count > 0 && Application.OpenForms[0].ContainsFocus && this.Visible) { + if(Mode != GameScreenMode.RecentGames && !InteropEmu.IsPaused()) { + this.Visible = false; + return; + } List keyCodes = InteropEmu.GetPressedKeys(); uint keyCode = keyCodes.Count > 0 ? keyCodes[0] : 0; if(keyCode > 0) { @@ -258,23 +280,23 @@ namespace Mesen.GUI.Controls UpdateGameInfo(); } else if(mapping.Down == keyCode) { _waitForRelease = true; - if(_currentIndex + _elementsPerRow < _recentGames.Count) { - _currentIndex += _elementsPerRow; + if(_currentIndex + _columnCount < _recentGames.Count) { + _currentIndex += _columnCount; } else { - _currentIndex = IsOnLastPage ? 0 : (_recentGames.Count - 1); + _currentIndex = Math.Min(_currentIndex % _columnCount, _recentGames.Count - 1); } UpdateGameInfo(); } else if(mapping.Up == keyCode) { _waitForRelease = true; - if(_currentIndex < _elementsPerRow) { - _currentIndex = _recentGames.Count - 1; + if(_currentIndex < _columnCount) { + _currentIndex = _recentGames.Count - (_columnCount - (_currentIndex % _columnCount)); } else { - _currentIndex -= _elementsPerRow; + _currentIndex -= _columnCount; } UpdateGameInfo(); } else if(mapping.A == keyCode || mapping.B == keyCode || mapping.Select == keyCode || mapping.Start == keyCode) { _waitForRelease = true; - LoadSelectedGame(); + _controls[_currentIndex % _elementsPerPage].ProcessClick(); } } } @@ -283,6 +305,14 @@ namespace Mesen.GUI.Controls } } } + + private void picClose_Click(object sender, EventArgs e) + { + if(_needResume) { + InteropEmu.Resume(); + } + this.Visible = false; + } } public class DBTableLayoutPanel : TableLayoutPanel @@ -293,56 +323,17 @@ namespace Mesen.GUI.Controls } } - public class GamePreviewBox : PictureBox - { - public InterpolationMode InterpolationMode { get; set; } - private bool _hovered = false; - private bool _highlight = false; - - public GamePreviewBox() - { - DoubleBuffered = true; - InterpolationMode = InterpolationMode.Default; - } - - public bool Highlight - { - get { return _highlight; } - set - { - _highlight = value; - this.Invalidate(); - } - } - - protected override void OnMouseEnter(EventArgs e) - { - base.OnMouseEnter(e); - _hovered = true; - this.Invalidate(); - } - - protected override void OnMouseLeave(EventArgs e) - { - base.OnMouseLeave(e); - _hovered = false; - this.Invalidate(); - } - - protected override void OnPaint(PaintEventArgs pe) - { - pe.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - base.OnPaint(pe); - - using(Pen pen = new Pen(_hovered ? Color.DeepSkyBlue : (_highlight ? Color.DodgerBlue : Color.DimGray), 2)) { - pe.Graphics.DrawRectangle(pen, 1, 1, this.Width - 2, this.Height - 2); - } - } - } - public class RecentGameInfo { public string FileName { get; set; } + public string Name { get; set; } public ResourcePath RomPath { get; set; } } + + public enum GameScreenMode + { + RecentGames, + LoadState, + SaveState + } } diff --git a/GUI.NET/Dependencies/resources.ca.xml b/GUI.NET/Dependencies/resources.ca.xml index b3d6ec3e..277e6de9 100644 --- a/GUI.NET/Dependencies/resources.ca.xml +++ b/GUI.NET/Dependencies/resources.ca.xml @@ -762,6 +762,12 @@ Partides guardades de Mesen (*.mst)|*.mst|Tots els fitxers (*.*)|*.* Fitxers de cassets Family Basic (*.fbt)|*.fbt|Tots els fitxers (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + Carrega des d'un fitxer... Desa al fitxer... @@ -921,6 +927,7 @@ Desa la partida - Posició 9 Desa la partida - Posició 10 Desa la partida a un fitxer + Open Save State Menu Carrega la partida - Posició 1 Carrega la partida - Posició 2 Carrega la partida - Posició 3 @@ -933,6 +940,7 @@ Carrega la partida - Posició 10 Load State - Auto Save Slot Carrega la partida des d'un fitxer + Open Load State Menu Load Last Session Select Save Slot 1 diff --git a/GUI.NET/Dependencies/resources.en.xml b/GUI.NET/Dependencies/resources.en.xml index 1ffe2b87..695bcc07 100644 --- a/GUI.NET/Dependencies/resources.en.xml +++ b/GUI.NET/Dependencies/resources.en.xml @@ -792,6 +792,12 @@ Mesen Savestates (*.mst)|*.mst|All files (*.*)|*.* Family Basic Tape files (*.fbt)|*.fbt|All Files (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + Load from file... Save to file... @@ -955,6 +961,7 @@ Save State - Slot 9 Save State - Slot 10 Save State to File + Open Save State Menu Load State - Slot 1 Load State - Slot 2 Load State - Slot 3 @@ -967,6 +974,7 @@ Load State - Slot 10 Load State - Auto Save Slot Load State from File + Open Load State Menu Load Last Session Select Save Slot 1 diff --git a/GUI.NET/Dependencies/resources.es.xml b/GUI.NET/Dependencies/resources.es.xml index 2ccfaac6..efe2b786 100644 --- a/GUI.NET/Dependencies/resources.es.xml +++ b/GUI.NET/Dependencies/resources.es.xml @@ -779,6 +779,12 @@ Partidas de juegos de Mesen (*.mst)|*.mst|Todos los archivos (*.*)|*.* Archivos Family Basic Tape (*.fbt)|*.fbt|Todos los archivos (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + Cargar desde archivo... Guardar en archivo... @@ -938,6 +944,7 @@ Guardar partida - Hueco 9 Guardar partida - Hueco 10 Guardar partida en archivo + Open Save State Menu Cargar partida - Hueco 1 Cargar partida - Hueco 2 Cargar partida - Hueco 3 @@ -950,6 +957,7 @@ Cargar partida - Hueco 10 Cargar partida - Hueco de auto guardado Cargar partida de archivo + Open Load State Menu Cargar última sesión Select Save Slot 1 diff --git a/GUI.NET/Dependencies/resources.fr.xml b/GUI.NET/Dependencies/resources.fr.xml index 2d0fc934..2f6d5d50 100644 --- a/GUI.NET/Dependencies/resources.fr.xml +++ b/GUI.NET/Dependencies/resources.fr.xml @@ -792,6 +792,12 @@ Sauvegardes d'états Mesen (*.mst)|*.mst|Tous les fichiers (*.*)|*.* Fichiers de cassettes Family Basic (*.fbt)|*.fbt|Tous les fichiers (*.*)|*.* + Session précédente + Sauvegarde auto. + Position #{0} + Menu de chargement rapide + Menu de sauvegarde rapide + Charger à partir d'un fichier... Sauvegarder dans un fichier... @@ -844,7 +850,7 @@ [inconnu] Le package Redistribuable Visual C++ pour Visual Studio 2015 n'a pas été installé correctement. - <aucune sauvegarde> + <vide> Une erreur s'est produite lors de la recherche de mises-à-jour. Vérifier votre connexion internet et essayez à nouveau. Détails de l'erreur : {0} Les mises-à-jour automatiques ne sont pas disponibles avec votre build - s.v.p télécharger la dernière version du code et recompiler Mesen pour avoir accès à la version la plus récente. Un bios est requis pour ce jeu. Nom du fichier : {0} Taille : {1} octets Voulez-vous choisir le fichier bios maintenant? @@ -952,6 +958,7 @@ Sauvegarde d'état - Position 9 Sauvegarde d'état - Position 10 Sauvergarde l'état dans un fichier + Ouvrir le menu de sauvegarde rapide Chargement d'état - Position 1 Chargement d'état - Position 2 Chargement d'état - Position 3 @@ -964,6 +971,7 @@ Chargement d'état - Position 10 Chargement d'état - Sauvegarde auto Charger l'état à partir d'un fichier + Ouvrir le menu de chargement rapide Charger la session précédente Choisir position de sauvegarde 1 diff --git a/GUI.NET/Dependencies/resources.it.xml b/GUI.NET/Dependencies/resources.it.xml index c0087c43..2a704434 100644 --- a/GUI.NET/Dependencies/resources.it.xml +++ b/GUI.NET/Dependencies/resources.it.xml @@ -792,6 +792,12 @@ Mesen Savestati (*.mst)|*.mst|All files (*.*)|*.* Family Basic Tape file (*.fbt)|*.fbt|All Files (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + Carica da file... Salva su file... @@ -954,6 +960,7 @@ Salva Stato - Slot 9 Salva Stato - Slot 10 Salva Stato su File + Open Save State Menu Carica Stato - Slot 1 Carica Stato - Slot 2 Carica Stato - Slot 3 @@ -966,6 +973,7 @@ Carica Stato - Slot 10 Carica Stato - Slot Salvataggio Automatico Carica Stato da File + Open Load State Menu Carica Ultima Sessione Select Save Slot 1 diff --git a/GUI.NET/Dependencies/resources.ja.xml b/GUI.NET/Dependencies/resources.ja.xml index 39cd60c2..c906827c 100644 --- a/GUI.NET/Dependencies/resources.ja.xml +++ b/GUI.NET/Dependencies/resources.ja.xml @@ -780,6 +780,12 @@ Mesenのクイックセーブデータ (*.mst)|*.mst|すべてのファイル (*.*)|*.* Family Basicテープファイル (*.fbt)|*.fbt|すべてのファイル (*.*)|*.* + 前セッション + オートクイックセーブ + スロット {0} + クイックロードメニュー + クイックセーブメニュー + ファイルからロードする… ファイルに保存する… @@ -940,6 +946,7 @@ クイックセーブスロット9に保存する クイックセーブスロット10に保存する クイックセーブデータをファイルに保存する + クイックセーブメニューを開く クイックセーブスロット1からロードする クイックセーブスロット2からロードする クイックセーブスロット3からロードする @@ -952,6 +959,7 @@ クイックセーブスロット10からロードする 自動クイックセーブスロットからロードする クイックセーブデータをファイルからロードする + クイックロードメニューを開く 前セッションをロードする クイックセーブスロット1を選ぶ diff --git a/GUI.NET/Dependencies/resources.pt.xml b/GUI.NET/Dependencies/resources.pt.xml index faa708ce..55e023ff 100644 --- a/GUI.NET/Dependencies/resources.pt.xml +++ b/GUI.NET/Dependencies/resources.pt.xml @@ -792,6 +792,12 @@ Estados salvos do Mesen (*.mst)|*.mst|Todos os arquivos (*.*)|*.* Arquivos Family Basic Tape (*.fbt)|*.fbt|Todos os arquivos (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + Carregar de um arquivo... Salvar para arquivo... @@ -954,6 +960,7 @@ Salvar estado - Compartimento 9 Salvar estado - Compartimento 10 Salvar estado para arquivo + Open Save State Menu Carregar estado - Compartimento 1 Carregar estado - Compartimento 2 Carregar estado - Compartimento 3 @@ -966,6 +973,7 @@ Carregar estado - Compartimento 10 Carregar estado - Compartimento de salvamento automático Carregar estado de arquivo + Open Load State Menu Carregar a última sessão Select Save Slot 1 diff --git a/GUI.NET/Dependencies/resources.ru.xml b/GUI.NET/Dependencies/resources.ru.xml index d6d2641f..ea9908b7 100644 --- a/GUI.NET/Dependencies/resources.ru.xml +++ b/GUI.NET/Dependencies/resources.ru.xml @@ -780,6 +780,12 @@ Mesen Savestates (*.mst)|*.mst|All Files (*.*)|*.* Family Basic Tape files (*.fbt)|*.fbt|All Files (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + Load from file... Save to file... @@ -940,6 +946,7 @@ Save State - Slot 9 Save State - Slot 10 Save State to File + Open Save State Menu Load State - Slot 1 Load State - Slot 2 Load State - Slot 3 @@ -952,6 +959,7 @@ Load State - Slot 10 Load State - Auto Save Slot Load State from File + Open Load State Menu Load Last Session Select Save Slot 1 diff --git a/GUI.NET/Dependencies/resources.uk.xml b/GUI.NET/Dependencies/resources.uk.xml index d7923f1b..a8fbe7ad 100644 --- a/GUI.NET/Dependencies/resources.uk.xml +++ b/GUI.NET/Dependencies/resources.uk.xml @@ -780,6 +780,12 @@ Mesen Savestates (*.mst)|*.mst|All Files (*.*)|*.* Family Basic Tape files (*.fbt)|*.fbt|All Files (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + Завантажитти з файлу... Зберегти в файл... @@ -940,6 +946,7 @@ Збереження стану - Слот 9 Збереження стану - Слот 10 Зберегти стан в файл + Open Save State Menu Завантаження стану - Слот 1 Завантаження стану - Слот 2 Завантаження стану - Слот 3 @@ -952,6 +959,7 @@ Завантаження стану - Слот 10 Завантаження стану - Автозавантаження Завантажити стан з файлу + Open Load State Menu Завантажити останню сесію Select Save Slot 1 diff --git a/GUI.NET/Dependencies/resources.zh.xml b/GUI.NET/Dependencies/resources.zh.xml index 75d80827..f46d281d 100644 --- a/GUI.NET/Dependencies/resources.zh.xml +++ b/GUI.NET/Dependencies/resources.zh.xml @@ -805,6 +805,12 @@ Mesen 进度 (*.mst)|*.mst|所有文件 (*.*)|*.* Family Basic 磁带文件 (*.fbt)|*.fbt|所有文件 (*.*)|*.* + Last Session + Auto-save + Slot #{0} + Load State Menu + Save State Menu + 从文件载入... 保存到文件... @@ -961,6 +967,7 @@ 保存进度 9 保存进度 10 进度另存为 + Open Save State Menu 载入进度 1 载入进度 2 载入进度 3 @@ -973,6 +980,7 @@ 载入进度 10 载入自动进度 载入进度文件 + Open Load State Menu 载入上次会话 选择进度槽 1 diff --git a/GUI.NET/Forms/Config/ctrlEmulatorShortcuts.cs b/GUI.NET/Forms/Config/ctrlEmulatorShortcuts.cs index e48b5d4c..e95f4783 100644 --- a/GUI.NET/Forms/Config/ctrlEmulatorShortcuts.cs +++ b/GUI.NET/Forms/Config/ctrlEmulatorShortcuts.cs @@ -104,6 +104,7 @@ namespace Mesen.GUI.Forms.Config EmulatorShortcut.SaveStateSlot9, EmulatorShortcut.SaveStateSlot10, EmulatorShortcut.SaveStateToFile, + EmulatorShortcut.SaveStateDialog, EmulatorShortcut.LoadStateSlot1, EmulatorShortcut.LoadStateSlot2, @@ -117,6 +118,7 @@ namespace Mesen.GUI.Forms.Config EmulatorShortcut.LoadStateSlot10, EmulatorShortcut.LoadStateSlotAuto, EmulatorShortcut.LoadStateFromFile, + EmulatorShortcut.LoadStateDialog, EmulatorShortcut.SelectSaveSlot1, EmulatorShortcut.SelectSaveSlot2, diff --git a/GUI.NET/Forms/frmMain.File.cs b/GUI.NET/Forms/frmMain.File.cs index 22cc99c2..8a6072eb 100644 --- a/GUI.NET/Forms/frmMain.File.cs +++ b/GUI.NET/Forms/frmMain.File.cs @@ -63,14 +63,23 @@ namespace Mesen.GUI.Forms menu.DropDownItems.Add("-"); addSaveStateInfo(NumberOfSaveSlots+1); menu.DropDownItems.Add("-"); + + ToolStripMenuItem loadDialog = new ToolStripMenuItem(ResourceHelper.GetMessage("LoadStateDialog"), Resources.SplitView); + menu.DropDownItems.Add(loadDialog); + BindShortcut(loadDialog, EmulatorShortcut.LoadStateDialog, () => _emuThread != null && !InteropEmu.IsConnected() && !InteropEmu.IsNsf()); + ToolStripMenuItem loadFromFile = new ToolStripMenuItem(ResourceHelper.GetMessage("LoadFromFile"), Resources.FolderOpen); menu.DropDownItems.Add(loadFromFile); BindShortcut(loadFromFile, EmulatorShortcut.LoadStateFromFile); } else { menu.DropDownItems.Add("-"); + ToolStripMenuItem saveDialog = new ToolStripMenuItem(ResourceHelper.GetMessage("SaveStateDialog"), Resources.SplitView); + menu.DropDownItems.Add(saveDialog); + BindShortcut(saveDialog, EmulatorShortcut.SaveStateDialog, () => _emuThread != null && !InteropEmu.IsConnected() && !InteropEmu.IsNsf()); + ToolStripMenuItem saveToFile = new ToolStripMenuItem(ResourceHelper.GetMessage("SaveToFile"), Resources.Floppy); menu.DropDownItems.Add(saveToFile); - BindShortcut(saveToFile, EmulatorShortcut.SaveStateToFile); + BindShortcut(saveToFile, EmulatorShortcut.SaveStateToFile); } } diff --git a/GUI.NET/Forms/frmMain.Options.cs b/GUI.NET/Forms/frmMain.Options.cs index 757195c1..54bac919 100644 --- a/GUI.NET/Forms/frmMain.Options.cs +++ b/GUI.NET/Forms/frmMain.Options.cs @@ -145,8 +145,9 @@ namespace Mesen.GUI.Forms ResourceHelper.ApplyResources(this); UpdateMenus(); InitializeNsfMode(); - ctrlRecentGames.Visible = _emuThread == null; - ctrlRecentGames.UpdateGameInfo(); + if(_emuThread == null) { + ShowRecentGames(); + } TopMost = ConfigManager.Config.PreferenceInfo.AlwaysOnTop; FormBorderStyle = ConfigManager.Config.PreferenceInfo.DisableMouseResize ? FormBorderStyle.Fixed3D : FormBorderStyle.Sizable; } else { diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 5e917d48..81e41889 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -14,6 +14,7 @@ using System.Threading.Tasks; using System.Windows.Forms; using System.Xml; using Mesen.GUI.Config; +using Mesen.GUI.Controls; using Mesen.GUI.Debugger; using Mesen.GUI.Forms.Cheats; using Mesen.GUI.Forms.Config; @@ -53,7 +54,6 @@ namespace Mesen.GUI.Forms private object _loadRomLock = new object(); private int _romLoadCounter = 0; private bool _showUpgradeMessage = false; - private float _yFactor = 1; private bool _enableResize = false; private bool _overrideWindowSize = false; private bool _shuttingDown = false; @@ -301,8 +301,9 @@ namespace Mesen.GUI.Forms ProcessFullscreenSwitch(CommandLineHelper.PreprocessCommandLineArguments(_commandLineArgs, true)); - ctrlRecentGames.Initialize(); - ctrlRecentGames.Visible = _emuThread == null; + if(_emuThread == null) { + ShowRecentGames(); + } } protected override void OnClosing(CancelEventArgs e) @@ -460,15 +461,15 @@ namespace Mesen.GUI.Forms } } - protected override void ScaleControl(SizeF factor, BoundsSpecified specified) - { - _yFactor = factor.Height; - base.ScaleControl(factor, specified); - } - private void ResizeRecentGames(object sender, EventArgs e) + { + ctrlRecentGames.Height = this.ClientSize.Height - ctrlRecentGames.Top - (ctrlRecentGames.Mode == GameScreenMode.RecentGames ? 25 : 0); + } + + private void ShowRecentGames() { ctrlRecentGames.Height = this.ClientSize.Height - ctrlRecentGames.Top - 25; + ctrlRecentGames.ShowScreen(GameScreenMode.RecentGames); } private void frmMain_Resize(object sender, EventArgs e) @@ -707,7 +708,7 @@ namespace Mesen.GUI.Forms case InteropEmu.ConsoleNotificationType.EmulationStopped: InitializeNsfMode(); this.BeginInvoke((Action)(() => { - ctrlRecentGames.Visible = true; + ShowRecentGames(); })); break; @@ -718,10 +719,6 @@ namespace Mesen.GUI.Forms _hdPackEditorWindow.Close(); } if(e.Parameter == IntPtr.Zero) { - if(!ConfigManager.Config.PreferenceInfo.DisableGameSelectionScreen) { - ctrlRecentGames.Initialize(); - } - //We are completely stopping the emulation, close fullscreen mode StopExclusiveFullscreenMode(); } @@ -983,6 +980,24 @@ namespace Mesen.GUI.Forms case EmulatorShortcut.LoadStateFromFile: LoadStateFromFile(); break; case EmulatorShortcut.SaveStateToFile: SaveStateToFile(); break; + case EmulatorShortcut.LoadStateDialog: + if(_frmFullscreenRenderer != null) { + this.SetFullscreenState(false); + restoreFullscreen = false; + } + ctrlRecentGames.ShowScreen(GameScreenMode.LoadState); + ctrlRecentGames.Height = this.ClientSize.Height - ctrlRecentGames.Top; + break; + + case EmulatorShortcut.SaveStateDialog: + if(_frmFullscreenRenderer != null) { + this.SetFullscreenState(false); + restoreFullscreen = false; + } + ctrlRecentGames.ShowScreen(GameScreenMode.SaveState); + ctrlRecentGames.Height = this.ClientSize.Height - ctrlRecentGames.Top; + break; + case EmulatorShortcut.SaveStateSlot1: SaveState(1); break; case EmulatorShortcut.SaveStateSlot2: SaveState(2); break; case EmulatorShortcut.SaveStateSlot3: SaveState(3); break; diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 38ce50dc..b8878dc9 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -1965,6 +1965,7 @@ + diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 00a83dc3..ee30400d 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.IO.Compression; using System.Linq; @@ -147,6 +148,21 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void SaveStateFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filepath); [DllImport(DLLPath)] public static extern void LoadStateFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string filepath); [DllImport(DLLPath)] public static extern Int64 GetStateInfo(UInt32 stateIndex); + [DllImport(DLLPath, EntryPoint = "GetSaveStatePreview")] private static extern Int32 GetSaveStatePreviewWrapper([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string saveStatePath, [Out]byte[] imgData); + public static Image GetSaveStatePreview(string saveStatePath) + { + if(File.Exists(saveStatePath)) { + byte[] buffer = new byte[new FileInfo(saveStatePath).Length]; + Int32 size = InteropEmu.GetSaveStatePreviewWrapper(saveStatePath, buffer); + if(size > 0) { + Array.Resize(ref buffer, size); + using(MemoryStream stream = new MemoryStream(buffer)) { + return Image.FromStream(stream); + } + } + } + return null; + } [DllImport(DLLPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IsNsf(); [DllImport(DLLPath)] public static extern void NsfSelectTrack(Byte trackNumber); @@ -1976,6 +1992,7 @@ namespace Mesen.GUI SaveStateSlot9, SaveStateSlot10, SaveStateToFile, + SaveStateDialog, LoadStateSlot1, LoadStateSlot2, @@ -1989,6 +2006,7 @@ namespace Mesen.GUI LoadStateSlot10, LoadStateSlotAuto, LoadStateFromFile, + LoadStateDialog, LoadLastSession, diff --git a/GUI.NET/Properties/Resources.Designer.cs b/GUI.NET/Properties/Resources.Designer.cs index 97483bf3..cda9883c 100644 --- a/GUI.NET/Properties/Resources.Designer.cs +++ b/GUI.NET/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Mesen.GUI.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -190,6 +190,16 @@ namespace Mesen.GUI.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CloseWhite { + get { + object obj = ResourceManager.GetObject("CloseWhite", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/GUI.NET/Properties/Resources.resx b/GUI.NET/Properties/Resources.resx index feeae36b..fb7a74ae 100644 --- a/GUI.NET/Properties/Resources.resx +++ b/GUI.NET/Properties/Resources.resx @@ -460,4 +460,7 @@ ..\Resources\MoveUp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\CloseWhite.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/GUI.NET/Resources/CloseWhite.png b/GUI.NET/Resources/CloseWhite.png new file mode 100644 index 00000000..615b33b5 Binary files /dev/null and b/GUI.NET/Resources/CloseWhite.png differ diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 51cce9d7..ed38a952 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -546,6 +546,9 @@ namespace InteropEmu { DllExport void __stdcall SaveStateFile(char* filepath) { _console->GetSaveStateManager()->SaveState(filepath); } DllExport void __stdcall LoadStateFile(char* filepath) { _console->GetSaveStateManager()->LoadState(filepath); } DllExport int64_t __stdcall GetStateInfo(uint32_t stateIndex) { return _console->GetSaveStateManager()->GetStateInfo(stateIndex); } + + DllExport int32_t __stdcall GetSaveStatePreview(char* saveStatePath, uint8_t* pngData) { return _console->GetSaveStateManager()->GetSaveStatePreview(saveStatePath, pngData); } + DllExport void __stdcall MoviePlay(char* filename) { MovieManager::Play(string(filename), _console); }