using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.IO; using Mesen.GUI.Config; using Mesen.GUI.Forms; using Mesen.GUI.Controls; namespace Mesen.GUI.Controls { public partial class ctrlRecentGames : BaseControl { 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; this.tlpTitle.MouseMove += value; } remove { this.tlpPreviousState.MouseMove -= value; this.tlpTitle.MouseMove -= value; } } public new event EventHandler DoubleClick { add { this.tlpPreviousState.DoubleClick += value; this.tlpTitle.DoubleClick += value; } remove { this.tlpPreviousState.DoubleClick -= value; this.tlpTitle.DoubleClick -= value; } } public ctrlRecentGames() { InitializeComponent(); if(IsDesignMode) { return; } DoubleBuffered = true; picPrevGame.Image.RotateFlip(RotateFlipType.RotateNoneFlipX); ThemeHelper.ExcludeFromTheme(this); } private void InitGrid() { int columnCount = 1; if(ClientSize.Width > 850 && ClientSize.Height > 850) { columnCount = 3; } else if(ClientSize.Width > 450 && ClientSize.Height > 450) { columnCount = 2; } if(_recentGames.Count <= 1) { columnCount = 1; } else if(_recentGames.Count <= 4) { columnCount = Math.Min(2, columnCount); } 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; } _columnCount = columnCount; _rowCount = rowCount; _elementsPerPage = elementsPerPage; _controls = new List(); tlpGrid.SuspendLayout(); tlpGrid.ColumnCount = _columnCount; tlpGrid.RowCount = _rowCount; tlpGrid.ColumnStyles.Clear(); tlpGrid.RowStyles.Clear(); tlpGrid.Controls.Clear(); 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 < _rowCount; j++) { for(int i = 0; i < _columnCount; i++) { ctrlRecentGame ctrl = new ctrlRecentGame(); ctrl.OnRecentGameLoaded += RecentGameLoaded; ctrl.Dock = DockStyle.Fill; ctrl.Margin = new Padding(2); tlpGrid.Controls.Add(ctrl, i, j); _controls.Add(ctrl); } } tlpGrid.ResumeLayout(); UpdateGameInfo(); picPrevGame.Visible = _recentGames.Count > _elementsPerPage; picNextGame.Visible = _recentGames.Count > _elementsPerPage; } public int GameCount { get { return _recentGames.Count; } } public GameScreenMode Mode { get; private set; } = GameScreenMode.RecentGames; private bool Pause() { 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; 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 < (mode == GameScreenMode.LoadState ? 11 : 10); 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), SaveSlot = (uint)i+1 }); } if(mode == GameScreenMode.LoadState) { _recentGames.Add(new RecentGameInfo() { FileName = Path.Combine(ConfigManager.RecentGamesFolder, romName + ".rgd"), Name = ResourceHelper.GetMessage("LastSession") }); } } InitGrid(); if(_recentGames.Count == 0) { this.Visible = false; tmrInput.Enabled = false; } else { UpdateGameInfo(); tmrInput.Enabled = true; } picPrevGame.Visible = _recentGames.Count > _elementsPerPage; picNextGame.Visible = _recentGames.Count > _elementsPerPage; this.Visible = true; } public void UpdateGameInfo() { 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; } } protected override void OnResize(EventArgs e) { if(Program.IsMono) { //Fix resize issues picNextGame.Dock = DockStyle.None; picPrevGame.Dock = DockStyle.None; picNextGame.Dock = DockStyle.Fill; picPrevGame.Dock = DockStyle.Fill; } if(this._columnCount > 0) { InitGrid(); } base.OnResize(e); } private void picNextGame_MouseDown(object sender, MouseEventArgs e) { GoToNextPage(); } private void picPrevGame_MouseDown(object sender, MouseEventArgs e) { GoToPreviousPage(); } private void GoToPreviousPage() { if(_currentIndex < _elementsPerPage) { _currentIndex = _recentGames.Count - 1; } else { _currentIndex -= _elementsPerPage; } UpdateGameInfo(); } private bool IsOnLastPage { get { return (_currentIndex / _elementsPerPage) == ((_recentGames.Count - 1) / _elementsPerPage); } } private void GoToNextPage() { if(_currentIndex + _elementsPerPage < _recentGames.Count) { _currentIndex += _elementsPerPage; } else { _currentIndex = IsOnLastPage ? 0 : (_recentGames.Count - 1); } UpdateGameInfo(); } private void RecentGameLoaded(RecentGameInfo gameInfo) { if(this.Mode == GameScreenMode.RecentGames) { 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 && 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) { if(!_waitForRelease) { foreach(KeyMappings mapping in ConfigManager.Config.InputInfo.Controllers[0].Keys) { if(mapping.Left == keyCode) { _waitForRelease = true; if(_currentIndex == 0) { _currentIndex = _recentGames.Count - 1; } else { _currentIndex--; } UpdateGameInfo(); } else if(mapping.Right == keyCode) { _waitForRelease = true; _currentIndex = (_currentIndex + 1) % _recentGames.Count; UpdateGameInfo(); } else if(mapping.Down == keyCode) { _waitForRelease = true; if(_currentIndex + _columnCount < _recentGames.Count) { _currentIndex += _columnCount; } else { _currentIndex = Math.Min(_currentIndex % _columnCount, _recentGames.Count - 1); } UpdateGameInfo(); } else if(mapping.Up == keyCode) { _waitForRelease = true; if(_currentIndex < _columnCount) { _currentIndex = _recentGames.Count - (_columnCount - (_currentIndex % _columnCount)); } else { _currentIndex -= _columnCount; } UpdateGameInfo(); } else if(mapping.A == keyCode || mapping.B == keyCode || mapping.Select == keyCode || mapping.Start == keyCode) { _waitForRelease = true; _controls[_currentIndex % _elementsPerPage].ProcessClick(); } } } } else { _waitForRelease = false; } } } private void picClose_Click(object sender, EventArgs e) { if(_needResume) { InteropEmu.Resume(); } this.Visible = false; } } public class DBTableLayoutPanel : TableLayoutPanel { public DBTableLayoutPanel() { DoubleBuffered = true; } } public class RecentGameInfo { public string FileName { get; set; } public string Name { get; set; } public uint SaveSlot { get; set; } public ResourcePath RomPath { get; set; } } public enum GameScreenMode { RecentGames, LoadState, SaveState } }