diff --git a/Core/GameDatabase.cpp b/Core/GameDatabase.cpp index 4aa724ed..64c76264 100644 --- a/Core/GameDatabase.cpp +++ b/Core/GameDatabase.cpp @@ -56,6 +56,9 @@ void GameDatabase::InitDatabase() _gameDatabase[gameInfo.Crc] = gameInfo; } } + + MessageManager::Log(); + MessageManager::Log("[DB] Initialized - " + std::to_string(_gameDatabase.size()) + " games in DB"); } } @@ -145,7 +148,9 @@ void GameDatabase::UpdateRomData(uint32_t romCrc, RomData &romData) InitDatabase(); auto result = _gameDatabase.find(romCrc); + if(result != _gameDatabase.end()) { + MessageManager::Log("[DB] Game found in database"); GameInfo info = result->second; romData.MapperID = info.MapperID; @@ -160,6 +165,18 @@ void GameDatabase::UpdateRomData(uint32_t romCrc, RomData &romData) romData.MirroringType = info.Mirroring.compare("h") == 0 ? MirroringType::Horizontal : MirroringType::Vertical; } + MessageManager::Log("[DB] Mapper: " + std::to_string(romData.MapperID) + " Sub: " + std::to_string(romData.SubMapperID)); + MessageManager::Log("[DB] System : " + info.System); + if(!info.Mirroring.empty()) { + MessageManager::Log("[DB] Mirroring: " + string(info.Mirroring.compare("h") == 0 ? "Horizontal" : "Vertical")); + } + MessageManager::Log("[DB] PRG ROM: " + std::to_string(info.PrgRomSize) + " KB"); + MessageManager::Log("[DB] CHR ROM: " + std::to_string(info.ChrRomSize) + " KB"); + if(info.ChrRamSize > 0) { + MessageManager::Log("[DB] CHR RAM: " + std::to_string(info.ChrRamSize) + " KB"); + } + MessageManager::Log("[DB] Battery: " + string(info.HasBattery ? "Yes" : "No")); + #ifdef _DEBUG MessageManager::DisplayMessage("DB", "Mapper: " + std::to_string(romData.MapperID) + " Sub: " + std::to_string(romData.SubMapperID) + " System: " + info.System); #endif diff --git a/Core/MessageManager.cpp b/Core/MessageManager.cpp index dc862cf9..257c74c5 100644 --- a/Core/MessageManager.cpp +++ b/Core/MessageManager.cpp @@ -178,6 +178,8 @@ std::unordered_map MessageManager::_jaResources = { { "SynchronizationCompleted", u8"同期完了。" }, }; +std::list MessageManager::_log; +SimpleLock MessageManager::_logLock; IMessageManager* MessageManager::_messageManager = nullptr; vector MessageManager::_notificationListeners; @@ -240,6 +242,29 @@ void MessageManager::DisplayToast(string title, string message, uint8_t* iconDat MessageManager::_messageManager->DisplayToast(shared_ptr(new ToastInfo(title, message, 4000, iconData, iconSize))); } } + +void MessageManager::Log(string message) +{ + _logLock.AcquireSafe(); + if(message.empty()) { + message = "------------------------------------------------------"; + } + if(_log.size() >= 1000) { + _log.pop_front(); + } + _log.push_back(message); +} + +string MessageManager::GetLog() +{ + _logLock.AcquireSafe(); + stringstream ss; + for(string &msg : _log) { + ss << msg << "\n"; + } + return ss.str(); +} + void MessageManager::RegisterNotificationListener(INotificationListener* notificationListener) { MessageManager::_notificationListeners.push_back(notificationListener); diff --git a/Core/MessageManager.h b/Core/MessageManager.h index ff5642d8..0c00cdd4 100644 --- a/Core/MessageManager.h +++ b/Core/MessageManager.h @@ -5,6 +5,7 @@ #include "IMessageManager.h" #include "INotificationListener.h" #include +#include "../Utilities/SimpleLock.h" class MessageManager { @@ -14,6 +15,9 @@ private: static std::unordered_map _enResources; static std::unordered_map _frResources; static std::unordered_map _jaResources; + + static SimpleLock _logLock; + static std::list _log; public: static string Localize(string key); @@ -22,6 +26,9 @@ public: static void DisplayMessage(string title, string message, string param1 = "", string param2 = ""); static void DisplayToast(string title, string message, uint8_t* iconData, uint32_t iconSize); + static void Log(string message = ""); + static string GetLog(); + static void RegisterNotificationListener(INotificationListener* notificationListener); static void UnregisterNotificationListener(INotificationListener* notificationListener); static void SendNotification(ConsoleNotificationType type, void* parameter = nullptr); diff --git a/Core/RomLoader.cpp b/Core/RomLoader.cpp index 315a090b..418bd0e9 100644 --- a/Core/RomLoader.cpp +++ b/Core/RomLoader.cpp @@ -97,6 +97,8 @@ bool RomLoader::LoadFromMemory(uint8_t* buffer, size_t length, string romName) fileData = IpsPatcher::PatchBuffer(_ipsFilename, fileData); } + MessageManager::Log(""); + MessageManager::Log("Loading rom: " + romName); if(memcmp(buffer, "NES\x1a", 4) == 0) { iNesLoader loader; _romData = loader.LoadRom(fileData); @@ -104,6 +106,7 @@ bool RomLoader::LoadFromMemory(uint8_t* buffer, size_t length, string romName) FdsLoader loader; _romData = loader.LoadRom(fileData, _filename); } else { + MessageManager::Log("Invalid rom file."); _romData.Error = true; } diff --git a/Core/iNesLoader.cpp b/Core/iNesLoader.cpp index 5a351692..5ab8374b 100644 --- a/Core/iNesLoader.cpp +++ b/Core/iNesLoader.cpp @@ -42,6 +42,23 @@ RomData iNesLoader::LoadRom(vector& romFile) buffer += header.GetPrgSize(); romData.ChrRom.insert(romData.ChrRom.end(), buffer, buffer + header.GetChrSize()); + if(romData.IsNes20Header) { + MessageManager::Log("[iNes] NES 2.0 file: Yes"); + } + MessageManager::Log("[iNes] Mapper: " + std::to_string(romData.MapperID) + " Sub:" + std::to_string(romData.SubMapperID)); + MessageManager::Log("[iNes] PRG ROM: " + std::to_string(romData.PrgRom.size()/1024) + " KB"); + MessageManager::Log("[iNes] CHR ROM: " + std::to_string(romData.ChrRom.size()/1024) + " KB"); + if(romData.ChrRamSize > 0) { + MessageManager::Log("[iNes] CHR RAM: " + std::to_string(romData.ChrRamSize) + " KB"); + } else if(romData.ChrRom.size() == 0) { + MessageManager::Log("[iNes] CHR RAM: 8 KB"); + } + MessageManager::Log("[iNes] Mirroring: " + string(romData.MirroringType == MirroringType::Horizontal ? "Horizontal" : romData.MirroringType == MirroringType::Vertical ? "Vertical" : "Four Screens")); + MessageManager::Log("[iNes] Battery: " + string(romData.HasBattery ? "Yes" : "No")); + if(romData.HasTrainer) { + MessageManager::Log("[iNes] Trainer: Yes"); + } + if(!EmulationSettings::CheckFlag(EmulationFlags::DisableGameDatabase) && header.GetRomHeaderVersion() != RomHeaderVersion::Nes2_0) { GameDatabase::UpdateRomData(romCrc, romData); } diff --git a/GUI.NET/Forms/frmLogWindow.Designer.cs b/GUI.NET/Forms/frmLogWindow.Designer.cs new file mode 100644 index 00000000..f54c252e --- /dev/null +++ b/GUI.NET/Forms/frmLogWindow.Designer.cs @@ -0,0 +1,108 @@ +namespace Mesen.GUI.Forms +{ + partial class frmLogWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.btnClose = new System.Windows.Forms.Button(); + this.txtLog = new System.Windows.Forms.TextBox(); + this.tmrRefresh = new System.Windows.Forms.Timer(this.components); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + 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.txtLog, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.btnClose, 1, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 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(480, 377); + this.tableLayoutPanel1.TabIndex = 0; + // + // btnClose + // + this.btnClose.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnClose.Location = new System.Drawing.Point(407, 349); + this.btnClose.Name = "btnClose"; + this.btnClose.Size = new System.Drawing.Size(70, 25); + this.btnClose.TabIndex = 0; + this.btnClose.Text = "Close"; + this.btnClose.UseVisualStyleBackColor = true; + this.btnClose.Click += new System.EventHandler(this.btnClose_Click); + // + // txtLog + // + this.txtLog.BackColor = System.Drawing.Color.White; + this.tableLayoutPanel1.SetColumnSpan(this.txtLog, 2); + this.txtLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtLog.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtLog.Location = new System.Drawing.Point(3, 3); + this.txtLog.Multiline = true; + this.txtLog.Name = "txtLog"; + this.txtLog.ReadOnly = true; + this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtLog.Size = new System.Drawing.Size(474, 340); + this.txtLog.TabIndex = 1; + // + // tmrRefresh + // + this.tmrRefresh.Enabled = true; + this.tmrRefresh.Tick += new System.EventHandler(this.tmrRefresh_Tick); + // + // frmLogWindow + // + this.AcceptButton = this.btnClose; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnClose; + this.ClientSize = new System.Drawing.Size(480, 377); + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "frmLogWindow"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Log Window"; + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TextBox txtLog; + private System.Windows.Forms.Button btnClose; + private System.Windows.Forms.Timer tmrRefresh; + } +} \ No newline at end of file diff --git a/GUI.NET/Forms/frmLogWindow.cs b/GUI.NET/Forms/frmLogWindow.cs new file mode 100644 index 00000000..9be1e76c --- /dev/null +++ b/GUI.NET/Forms/frmLogWindow.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public partial class frmLogWindow : BaseForm + { + private string _currentLog; + public frmLogWindow() + { + InitializeComponent(); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + } + + protected override void OnShown(EventArgs e) + { + base.OnShown(e); + UpdateLog(InteropEmu.GetLog()); + } + + private void UpdateLog(string log) + { + _currentLog = log; + txtLog.Text = _currentLog; + txtLog.SelectionLength = 0; + txtLog.SelectionStart = txtLog.Text.Length; + txtLog.ScrollToCaret(); + } + + private void btnClose_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void tmrRefresh_Tick(object sender, EventArgs e) + { + string newLog = InteropEmu.GetLog(); + if(_currentLog != newLog) { + UpdateLog(newLog); + } + } + } +} diff --git a/GUI.NET/Forms/frmLogWindow.resx b/GUI.NET/Forms/frmLogWindow.resx new file mode 100644 index 00000000..b8422f0d --- /dev/null +++ b/GUI.NET/Forms/frmLogWindow.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 107, 17 + + \ No newline at end of file diff --git a/GUI.NET/Forms/frmMain.Designer.cs b/GUI.NET/Forms/frmMain.Designer.cs index e518038d..13943e4a 100644 --- a/GUI.NET/Forms/frmMain.Designer.cs +++ b/GUI.NET/Forms/frmMain.Designer.cs @@ -155,6 +155,7 @@ namespace Mesen.GUI.Forms this.mnuCheckForUpdates = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuLogWindow = new System.Windows.Forms.ToolStripMenuItem(); this.panelRenderer.SuspendLayout(); this.menuStrip.SuspendLayout(); this.SuspendLayout(); @@ -857,6 +858,7 @@ namespace Mesen.GUI.Forms this.toolStripMenuItem12, this.mnuTests, this.mnuDebugger, + this.mnuLogWindow, this.toolStripMenuItem1, this.mnuTakeScreenshot}); this.mnuTools.Name = "mnuTools"; @@ -1178,6 +1180,13 @@ namespace Mesen.GUI.Forms this.mnuAbout.Text = "About"; this.mnuAbout.Click += new System.EventHandler(this.mnuAbout_Click); // + // mnuLogWindow + // + this.mnuLogWindow.Name = "mnuLogWindow"; + this.mnuLogWindow.Size = new System.Drawing.Size(185, 22); + this.mnuLogWindow.Text = "Log Window"; + this.mnuLogWindow.Click += new System.EventHandler(this.mnuLogWindow_Click); + // // frmMain // this.AllowDrop = true; @@ -1328,6 +1337,7 @@ namespace Mesen.GUI.Forms private System.Windows.Forms.ToolStripMenuItem mnuWaveStop; private System.Windows.Forms.ToolStripMenuItem mnuScale5x; private System.Windows.Forms.ToolStripMenuItem mnuScale6x; + private System.Windows.Forms.ToolStripMenuItem mnuLogWindow; } } diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 55a79376..3f2b4052 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -25,6 +25,7 @@ namespace Mesen.GUI.Forms private InteropEmu.NotificationListener _notifListener; private Thread _emuThread; private frmDebugger _debugger; + private frmLogWindow _logWindow; private string _romToLoad = null; private string _currentGame = null; private bool _customSize = false; @@ -1330,5 +1331,21 @@ namespace Mesen.GUI.Forms ConfigManager.Config.ApplyConfig(); ConfigManager.ApplyChanges(); } + + private void mnuLogWindow_Click(object sender, EventArgs e) + { + if(_logWindow == null) { + _logWindow = new frmLogWindow(); + _logWindow.StartPosition = FormStartPosition.Manual; + _logWindow.Left = this.Left + (this.Width - _logWindow.Width) / 2; + _logWindow.Top = this.Top + (this.Height - _logWindow.Height) / 2; + _logWindow.Show(); + _logWindow.FormClosed += (object a, FormClosedEventArgs b) => { + _logWindow = null; + }; + } else { + _logWindow.Focus(); + } + } } } diff --git a/GUI.NET/GUI.NET.csproj b/GUI.NET/GUI.NET.csproj index 98f8c829..ab6431af 100644 --- a/GUI.NET/GUI.NET.csproj +++ b/GUI.NET/GUI.NET.csproj @@ -445,6 +445,12 @@ frmDownloadProgress.cs + + Form + + + frmLogWindow.cs + Form @@ -601,6 +607,9 @@ frmDownloadProgress.cs + + frmLogWindow.cs + frmMain.cs diff --git a/GUI.NET/InteropEmu.cs b/GUI.NET/InteropEmu.cs index 16e3be84..21d3480f 100644 --- a/GUI.NET/InteropEmu.cs +++ b/GUI.NET/InteropEmu.cs @@ -73,6 +73,7 @@ namespace Mesen.GUI [DllImport(DLLPath)] public static extern void UnregisterNotificationCallback(IntPtr notificationListener); [DllImport(DLLPath)] public static extern void DisplayMessage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string title, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string message, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string param1 = null); + [DllImport(DLLPath, EntryPoint= "GetLog")] private static extern IntPtr GetLogWrapper(); [DllImport(DLLPath)] public static extern void MoviePlay([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename); [DllImport(DLLPath)] public static extern void MovieRecord([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(UTF8Marshaler))]string filename, [MarshalAs(UnmanagedType.I1)]bool reset); @@ -304,6 +305,7 @@ namespace Mesen.GUI return paleteData; } + public static string GetLog() { return PtrToStringUtf8(InteropEmu.GetLogWrapper()).Replace("\n", Environment.NewLine); } public static string GetKeyName(UInt32 key) { return PtrToStringUtf8(InteropEmu.GetKeyNameWrapper(key)); } public static List GetAudioDevices() { diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 279ded7f..418836a9 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -23,6 +23,7 @@ SoundManager *_soundManager = nullptr; HWND _windowHandle = nullptr; HWND _viewerHandle = nullptr; string _returnString; +string _logString; AutoRomTest *_autoRomTest = nullptr; typedef void (__stdcall *NotificationListenerCallback)(int); @@ -225,6 +226,11 @@ namespace InteropEmu { DllExport void __stdcall UnregisterNotificationCallback(INotificationListener *listener) { MessageManager::UnregisterNotificationListener(listener); } DllExport void __stdcall DisplayMessage(char* title, char* message, char* param1) { MessageManager::DisplayMessage(title, message, param1 ? param1 : ""); } + DllExport const char* __stdcall GetLog() + { + _logString = MessageManager::GetLog(); + return _logString.c_str(); + } DllExport void __stdcall SaveState(uint32_t stateIndex) { SaveStateManager::SaveState(stateIndex); } DllExport uint32_t __stdcall LoadState(uint32_t stateIndex) { return SaveStateManager::LoadState(stateIndex); }