From 61fbae131cdeed9f9a468a48b779c789a689a4cd Mon Sep 17 00:00:00 2001 From: Sour Date: Sat, 9 Jul 2022 19:39:35 -0400 Subject: [PATCH] Debugger: Added controllers tool --- Core/Debugger/DebugTypes.h | 21 +++++ Core/Debugger/Debugger.cpp | 18 ++++ Core/Debugger/Debugger.h | 5 + Core/Debugger/IDebugger.h | 1 + Core/Gameboy/Debugger/GbDebugger.cpp | 21 +++++ Core/Gameboy/Debugger/GbDebugger.h | 2 + Core/NES/Debugger/NesDebugger.cpp | 23 ++++- Core/NES/Debugger/NesDebugger.h | 2 + Core/PCE/Debugger/PceDebugger.cpp | 23 ++++- Core/PCE/Debugger/PceDebugger.h | 1 + Core/SNES/Debugger/SnesDebugger.cpp | 27 +++++- Core/SNES/Debugger/SnesDebugger.h | 1 + Core/Shared/BaseControlManager.cpp | 43 +++++++++ Core/Shared/BaseControlManager.h | 2 + Core/Shared/ControllerHub.h | 38 +++++++- InteropDLL/DebugApiWrapper.cpp | 5 +- NewUI/Debugger/DebuggerDockFactory.cs | 5 +- NewUI/Debugger/Utilities/ContextMenuAction.cs | 1 + .../ViewModels/ControllerInputViewModel.cs | 92 +++++++++++++++++++ .../ViewModels/ControllerListViewModel.cs | 28 ++++++ .../ViewModels/DebuggerWindowViewModel.cs | 30 ++++-- .../Debugger/Views/ControllerInputView.axaml | 71 ++++++++++++++ .../Views/ControllerInputView.axaml.cs | 25 +++++ NewUI/Debugger/Views/ControllerListView.axaml | 34 +++++++ .../Views/ControllerListView.axaml.cs | 25 +++++ NewUI/Debugger/Windows/DebuggerWindow.axaml | 3 + NewUI/Interop/DebugApi.cs | 33 +++++++ NewUI/Localization/resources.en.xml | 1 + NewUI/NewUI.csproj | 6 ++ 29 files changed, 570 insertions(+), 17 deletions(-) create mode 100644 NewUI/Debugger/ViewModels/ControllerInputViewModel.cs create mode 100644 NewUI/Debugger/ViewModels/ControllerListViewModel.cs create mode 100644 NewUI/Debugger/Views/ControllerInputView.axaml create mode 100644 NewUI/Debugger/Views/ControllerInputView.axaml.cs create mode 100644 NewUI/Debugger/Views/ControllerListView.axaml create mode 100644 NewUI/Debugger/Views/ControllerListView.axaml.cs diff --git a/Core/Debugger/DebugTypes.h b/Core/Debugger/DebugTypes.h index cb11865f..d4187824 100644 --- a/Core/Debugger/DebugTypes.h +++ b/Core/Debugger/DebugTypes.h @@ -428,3 +428,24 @@ struct CpuInstructionProgress uint32_t LastOpCode = 0; MemoryOperationInfo LastMemOperation = {}; }; + +struct DebugControllerState +{ + bool A; + bool B; + bool X; + bool Y; + bool L; + bool R; + bool Up; + bool Down; + bool Left; + bool Right; + bool Select; + bool Start; + + bool HasPressedButton() + { + return A || B || X || Y || L || R || Up || Down || Left || Right || Select || Start; + } +}; diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index 6ba8e381..3242e27b 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -35,6 +35,7 @@ #include "Gameboy/GbTypes.h" #include "PCE/Debugger/PceDebugger.h" #include "PCE/PceTypes.h" +#include "Shared/BaseControlManager.h" #include "Shared/EmuSettings.h" #include "Shared/Audio/SoundMixer.h" #include "Shared/NotificationManager.h" @@ -358,6 +359,10 @@ void Debugger::ProcessEvent(EventType type) switch(type) { default: break; + case EventType::InputPolled: + _debuggers[(int)_mainCpuType].Debugger->ProcessInputOverrides(_inputOverrides); + break; + case EventType::StartFrame: { _emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh, (void*)_mainCpuType); BaseEventManager* evtMgr = GetEventManager(_mainCpuType); @@ -664,6 +669,19 @@ void Debugger::SetBreakpoints(Breakpoint breakpoints[], uint32_t length) } } +void Debugger::SetInputOverrides(uint32_t index, DebugControllerState state) +{ + _inputOverrides[index] = state; +} + +void Debugger::GetAvailableInputOverrides(uint8_t* availableIndexes) +{ + BaseControlManager* controlManager = _console->GetControlManager(); + for(int i = 0; i < 8; i++) { + availableIndexes[i] = controlManager->GetControlDeviceByIndex(i) != nullptr; + } +} + void Debugger::Log(string message) { auto lock = _logLock.AcquireSafe(); diff --git a/Core/Debugger/Debugger.h b/Core/Debugger/Debugger.h index ba617df4..e2ffbf33 100644 --- a/Core/Debugger/Debugger.h +++ b/Core/Debugger/Debugger.h @@ -81,6 +81,8 @@ private: atomic _breakRequestCount; atomic _suspendRequestCount; + DebugControllerState _inputOverrides[8] = {}; + bool _waitForBreakResume = false; void Reset(); @@ -150,6 +152,9 @@ public: void SetBreakpoints(Breakpoint breakpoints[], uint32_t length); + void SetInputOverrides(uint32_t index, DebugControllerState state); + void GetAvailableInputOverrides(uint8_t* availableIndexes); + void Log(string message); string GetLog(); diff --git a/Core/Debugger/IDebugger.h b/Core/Debugger/IDebugger.h index 96e19770..99f653ca 100644 --- a/Core/Debugger/IDebugger.h +++ b/Core/Debugger/IDebugger.h @@ -32,6 +32,7 @@ public: virtual void ProcessConfigChange() {} virtual void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) {} + virtual void ProcessInputOverrides(DebugControllerState inputOverrides[8]) {} virtual void DrawPartialFrame() { } diff --git a/Core/Gameboy/Debugger/GbDebugger.cpp b/Core/Gameboy/Debugger/GbDebugger.cpp index 338f9632..aa017513 100644 --- a/Core/Gameboy/Debugger/GbDebugger.cpp +++ b/Core/Gameboy/Debugger/GbDebugger.cpp @@ -1,6 +1,7 @@ #include "stdafx.h" #include "Gameboy/Gameboy.h" #include "Gameboy/GbMemoryManager.h" +#include "Gameboy/Input/GbController.h" #include "Gameboy/Debugger/DummyGbCpu.h" #include "Gameboy/Debugger/GbDebugger.h" #include "Gameboy/Debugger/GameboyDisUtils.h" @@ -26,6 +27,7 @@ #include "SNES/BaseCartridge.h" #include "Shared/EmuSettings.h" #include "Shared/Emulator.h" +#include "Shared/BaseControlManager.h" #include "MemoryOperationType.h" GbDebugger::GbDebugger(Debugger* debugger) @@ -416,3 +418,22 @@ void GbDebugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption s file.close(); } } + +void GbDebugger::ProcessInputOverrides(DebugControllerState inputOverrides[8]) +{ + BaseControlManager* controlManager = _gameboy->GetControlManager(); + for(int i = 0; i < 8; i++) { + shared_ptr controller = std::dynamic_pointer_cast(controlManager->GetControlDeviceByIndex(i)); + if(controller && inputOverrides[i].HasPressedButton()) { + controller->SetBitValue(GbController::Buttons::A, inputOverrides[i].A); + controller->SetBitValue(GbController::Buttons::B, inputOverrides[i].B); + controller->SetBitValue(GbController::Buttons::Select, inputOverrides[i].Select); + controller->SetBitValue(GbController::Buttons::Start, inputOverrides[i].Start); + controller->SetBitValue(GbController::Buttons::Up, inputOverrides[i].Up); + controller->SetBitValue(GbController::Buttons::Down, inputOverrides[i].Down); + controller->SetBitValue(GbController::Buttons::Left, inputOverrides[i].Left); + controller->SetBitValue(GbController::Buttons::Right, inputOverrides[i].Right); + } + } + controlManager->RefreshHubState(); +} diff --git a/Core/Gameboy/Debugger/GbDebugger.h b/Core/Gameboy/Debugger/GbDebugger.h index 6798e6a2..b9e53e91 100644 --- a/Core/Gameboy/Debugger/GbDebugger.h +++ b/Core/Gameboy/Debugger/GbDebugger.h @@ -71,6 +71,8 @@ public: void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption); + void ProcessInputOverrides(DebugControllerState inputOverrides[8]); + void SetProgramCounter(uint32_t addr) override; uint32_t GetProgramCounter(bool getInstPc) override; diff --git a/Core/NES/Debugger/NesDebugger.cpp b/Core/NES/Debugger/NesDebugger.cpp index 9ee18b71..5423fec7 100644 --- a/Core/NES/Debugger/NesDebugger.cpp +++ b/Core/NES/Debugger/NesDebugger.cpp @@ -16,6 +16,7 @@ #include "NES/NesPpu.h" #include "NES/BaseMapper.h" #include "NES/NesMemoryManager.h" +#include "NES/Input/NesController.h" #include "NES/Mappers/NsfMapper.h" #include "NES/Debugger/DummyNesCpu.h" #include "NES/Debugger/NesCodeDataLogger.h" @@ -31,6 +32,7 @@ #include "Utilities/CRC32.h" #include "Shared/EmuSettings.h" #include "Shared/SettingTypes.h" +#include "Shared/BaseControlManager.h" #include "Shared/Emulator.h" #include "MemoryOperationType.h" @@ -472,4 +474,23 @@ void NesDebugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption file.write((char*)output.data(), output.size()); file.close(); } -} \ No newline at end of file +} + +void NesDebugger::ProcessInputOverrides(DebugControllerState inputOverrides[8]) +{ + BaseControlManager* controlManager = _console->GetControlManager(); + for(int i = 0; i < 8; i++) { + shared_ptr controller = std::dynamic_pointer_cast(controlManager->GetControlDeviceByIndex(i)); + if(controller && inputOverrides[i].HasPressedButton()) { + controller->SetBitValue(NesController::Buttons::A, inputOverrides[i].A); + controller->SetBitValue(NesController::Buttons::B, inputOverrides[i].B); + controller->SetBitValue(NesController::Buttons::Select, inputOverrides[i].Select); + controller->SetBitValue(NesController::Buttons::Start, inputOverrides[i].Start); + controller->SetBitValue(NesController::Buttons::Up, inputOverrides[i].Up); + controller->SetBitValue(NesController::Buttons::Down, inputOverrides[i].Down); + controller->SetBitValue(NesController::Buttons::Left, inputOverrides[i].Left); + controller->SetBitValue(NesController::Buttons::Right, inputOverrides[i].Right); + } + } + controlManager->RefreshHubState(); +} diff --git a/Core/NES/Debugger/NesDebugger.h b/Core/NES/Debugger/NesDebugger.h index 54905226..4045cdc3 100644 --- a/Core/NES/Debugger/NesDebugger.h +++ b/Core/NES/Debugger/NesDebugger.h @@ -76,6 +76,8 @@ public: void ProcessPpuWrite(uint16_t addr, uint8_t value, MemoryType memoryType); void ProcessPpuCycle(); + void ProcessInputOverrides(DebugControllerState inputOverrides[8]); + void Run() override; void Step(int32_t stepCount, StepType type) override; diff --git a/Core/PCE/Debugger/PceDebugger.cpp b/Core/PCE/Debugger/PceDebugger.cpp index f7e9ae00..9daf5a33 100644 --- a/Core/PCE/Debugger/PceDebugger.cpp +++ b/Core/PCE/Debugger/PceDebugger.cpp @@ -16,6 +16,7 @@ #include "PCE/PceVce.h" #include "PCE/PceVpc.h" #include "PCE/PceMemoryManager.h" +#include "PCE/Input/PceController.h" #include "PCE/Debugger/PceDebugger.h" #include "PCE/Debugger/PceTraceLogger.h" #include "PCE/Debugger/PceVdcTools.h" @@ -26,6 +27,7 @@ #include "Utilities/HexUtilities.h" #include "Utilities/FolderUtilities.h" #include "Utilities/Patches/IpsPatcher.h" +#include "Shared/BaseControlManager.h" #include "Shared/EmuSettings.h" #include "Shared/SettingTypes.h" #include "Shared/Emulator.h" @@ -427,4 +429,23 @@ void PceDebugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption file.write((char*)output.data(), output.size()); file.close(); } -} \ No newline at end of file +} + +void PceDebugger::ProcessInputOverrides(DebugControllerState inputOverrides[8]) +{ + BaseControlManager* controlManager = _console->GetControlManager(); + for(int i = 0; i < 8; i++) { + shared_ptr controller = std::dynamic_pointer_cast(controlManager->GetControlDeviceByIndex(i)); + if(controller && inputOverrides[i].HasPressedButton()) { + controller->SetBitValue(PceController::Buttons::I, inputOverrides[i].A); + controller->SetBitValue(PceController::Buttons::II, inputOverrides[i].B); + controller->SetBitValue(PceController::Buttons::Select, inputOverrides[i].Select); + controller->SetBitValue(PceController::Buttons::Run, inputOverrides[i].Start); + controller->SetBitValue(PceController::Buttons::Up, inputOverrides[i].Up); + controller->SetBitValue(PceController::Buttons::Down, inputOverrides[i].Down); + controller->SetBitValue(PceController::Buttons::Left, inputOverrides[i].Left); + controller->SetBitValue(PceController::Buttons::Right, inputOverrides[i].Right); + } + } + controlManager->RefreshHubState(); +} diff --git a/Core/PCE/Debugger/PceDebugger.h b/Core/PCE/Debugger/PceDebugger.h index ec78c1ff..d837e008 100644 --- a/Core/PCE/Debugger/PceDebugger.h +++ b/Core/PCE/Debugger/PceDebugger.h @@ -90,6 +90,7 @@ public: ITraceLogger* GetTraceLogger() override; PpuTools* GetPpuTools() override; void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption); + void ProcessInputOverrides(DebugControllerState inputOverrides[8]); CallstackManager* GetCallstackManager() override; IAssembler* GetAssembler() override; BaseEventManager* GetEventManager() override; diff --git a/Core/SNES/Debugger/SnesDebugger.cpp b/Core/SNES/Debugger/SnesDebugger.cpp index f52a80db..6bba39df 100644 --- a/Core/SNES/Debugger/SnesDebugger.cpp +++ b/Core/SNES/Debugger/SnesDebugger.cpp @@ -7,6 +7,7 @@ #include "SNES/Spc.h" #include "SNES/SnesPpu.h" #include "SNES/MemoryMappings.h" +#include "SNES/Input/SnesController.h" #include "SNES/Debugger/DummySnesCpu.h" #include "SNES/Debugger/SnesDisUtils.h" #include "SNES/Debugger/SnesCodeDataLogger.h" @@ -28,6 +29,7 @@ #include "Debugger/Debugger.h" #include "Debugger/CodeDataLogger.h" #include "Shared/SettingTypes.h" +#include "Shared/BaseControlManager.h" #include "Shared/EmuSettings.h" #include "Shared/Emulator.h" #include "Utilities/HexUtilities.h" @@ -513,4 +515,27 @@ void SnesDebugger::SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption file.write((char*)output.data(), output.size()); file.close(); } -} \ No newline at end of file +} + +void SnesDebugger::ProcessInputOverrides(DebugControllerState inputOverrides[8]) +{ + BaseControlManager* controlManager = _console->GetControlManager(); + for(int i = 0; i < 8; i++) { + shared_ptr controller = std::dynamic_pointer_cast(controlManager->GetControlDeviceByIndex(i)); + if(controller && inputOverrides[i].HasPressedButton()) { + controller->SetBitValue(SnesController::Buttons::A, inputOverrides[i].A); + controller->SetBitValue(SnesController::Buttons::B, inputOverrides[i].B); + controller->SetBitValue(SnesController::Buttons::X, inputOverrides[i].X); + controller->SetBitValue(SnesController::Buttons::Y, inputOverrides[i].Y); + controller->SetBitValue(SnesController::Buttons::L, inputOverrides[i].L); + controller->SetBitValue(SnesController::Buttons::R, inputOverrides[i].R); + controller->SetBitValue(SnesController::Buttons::Select, inputOverrides[i].Select); + controller->SetBitValue(SnesController::Buttons::Start, inputOverrides[i].Start); + controller->SetBitValue(SnesController::Buttons::Up, inputOverrides[i].Up); + controller->SetBitValue(SnesController::Buttons::Down, inputOverrides[i].Down); + controller->SetBitValue(SnesController::Buttons::Left, inputOverrides[i].Left); + controller->SetBitValue(SnesController::Buttons::Right, inputOverrides[i].Right); + } + } + controlManager->RefreshHubState(); +} diff --git a/Core/SNES/Debugger/SnesDebugger.h b/Core/SNES/Debugger/SnesDebugger.h index 705a1897..31c731d8 100644 --- a/Core/SNES/Debugger/SnesDebugger.h +++ b/Core/SNES/Debugger/SnesDebugger.h @@ -116,4 +116,5 @@ public: void GetPpuState(BaseState& state) override; void SetPpuState(BaseState& state) override; void SaveRomToDisk(string filename, bool saveAsIps, CdlStripOption stripOption); + void ProcessInputOverrides(DebugControllerState inputOverrides[8]); }; \ No newline at end of file diff --git a/Core/Shared/BaseControlManager.cpp b/Core/Shared/BaseControlManager.cpp index b71b0c31..b3cbbb63 100644 --- a/Core/Shared/BaseControlManager.cpp +++ b/Core/Shared/BaseControlManager.cpp @@ -3,6 +3,7 @@ #include "Shared/Emulator.h" #include "Shared/EmuSettings.h" #include "Shared/KeyManager.h" +#include "Shared/ControllerHub.h" #include "Shared/Interfaces/IKeyManager.h" #include "Shared/Interfaces/IInputProvider.h" #include "Shared/Interfaces/IInputRecorder.h" @@ -80,6 +81,48 @@ shared_ptr BaseControlManager::GetControlDevice(uint8_t port) return nullptr; } +shared_ptr BaseControlManager::GetControlDeviceByIndex(uint8_t index) +{ + auto lock = _deviceLock.AcquireSafe(); + + int counter = 0; + for(size_t i = 0; i < _controlDevices.size(); i++) { + if(_controlDevices[i] && _controlDevices[i]->GetPort() <= 2) { + shared_ptr hub = std::dynamic_pointer_cast(_controlDevices[i]); + if(hub) { + int portCount = hub->GetHubPortCount(); + for(int i = 0; i < portCount; i++) { + shared_ptr device = hub->GetController(i); + if(counter == index) { + return device; + } + counter++; + } + } else { + if(counter == index) { + return _controlDevices[i]; + } + counter++; + } + } + } + return nullptr; +} + +void BaseControlManager::RefreshHubState() +{ + auto lock = _deviceLock.AcquireSafe(); + + for(size_t i = 0; i < _controlDevices.size(); i++) { + if(_controlDevices[i] && _controlDevices[i]->GetPort() <= 2) { + shared_ptr hub = std::dynamic_pointer_cast(_controlDevices[i]); + if(hub) { + hub->RefreshHubState(); + } + } + } +} + vector> BaseControlManager::GetControlDevices() { return _controlDevices; diff --git a/Core/Shared/BaseControlManager.h b/Core/Shared/BaseControlManager.h index ccc0f1dd..346c0610 100644 --- a/Core/Shared/BaseControlManager.h +++ b/Core/Shared/BaseControlManager.h @@ -56,6 +56,8 @@ public: vector GetPortStates(); shared_ptr GetControlDevice(uint8_t port); + shared_ptr GetControlDeviceByIndex(uint8_t index); + void RefreshHubState(); vector> GetControlDevices(); template diff --git a/Core/Shared/ControllerHub.h b/Core/Shared/ControllerHub.h index 17035ab6..afe4b9fb 100644 --- a/Core/Shared/ControllerHub.h +++ b/Core/Shared/ControllerHub.h @@ -10,8 +10,16 @@ #include "Utilities/Serializer.h" #include "Utilities/StringUtilities.h" +class IControllerHub +{ +public: + virtual void RefreshHubState() = 0; + virtual int GetHubPortCount() = 0; + virtual shared_ptr GetController(int index) = 0; +}; + template -class ControllerHub : public BaseControlDevice +class ControllerHub : public BaseControlDevice, public IControllerHub { protected: shared_ptr _ports[HubPortCount]; @@ -40,7 +48,11 @@ protected: uint8_t ReadPort(int i) { - return _ports[i]->ReadRam(0x4016); + if(_ports[i]) { + return _ports[i]->ReadRam(0x4016); + } else { + return 0; + } } public: @@ -67,7 +79,6 @@ public: break; } } - } void WriteRam(uint16_t addr, uint8_t value) override @@ -160,4 +171,25 @@ public: return false; } + + void RefreshHubState() override + { + //Used when the connected devices are updated by code (e.g by the debugger) + _state.State.clear(); + UpdateStateFromPorts(); + } + + int GetHubPortCount() override + { + return HubPortCount; + } + + shared_ptr GetController(int index) override + { + if(index >= HubPortCount) { + return nullptr; + } + + return _ports[index]; + } }; diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index 143d8877..07d33aec 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -83,7 +83,10 @@ extern "C" DllExport void __stdcall StopLogTraceToFile() { WithDebugger(void, GetTraceLogFileSaver()->StopLogging()); } DllExport void __stdcall SetBreakpoints(Breakpoint breakpoints[], uint32_t length) { WithDebugger(void, SetBreakpoints(breakpoints, length)); } - + + DllExport void __stdcall SetInputOverrides(uint32_t index, DebugControllerState state) { WithDebugger(void, SetInputOverrides(index, state)); } + DllExport void __stdcall GetAvailableInputOverrides(uint8_t* availableIndexes) { WithDebugger(void, GetAvailableInputOverrides(availableIndexes)); } + DllExport void __stdcall GetTokenList(CpuType cpuType, char* tokenList) { WithDebugger(void, GetTokenList(cpuType, tokenList)); } DllExport int32_t __stdcall EvaluateExpression(const char* expression, CpuType cpuType, EvalResultType* resultType, bool useCache) { return WithDebugger(int32_t, EvaluateExpression(expression, cpuType, *resultType, useCache)); } diff --git a/NewUI/Debugger/DebuggerDockFactory.cs b/NewUI/Debugger/DebuggerDockFactory.cs index 247c79d9..ead13e9f 100644 --- a/NewUI/Debugger/DebuggerDockFactory.cs +++ b/NewUI/Debugger/DebuggerDockFactory.cs @@ -28,6 +28,7 @@ namespace Mesen.Debugger public ToolContainerViewModel LabelListTool { get; private set; } public ToolContainerViewModel FunctionListTool { get; private set; } public ToolContainerViewModel FindResultListTool { get; private set; } + public ToolContainerViewModel ControllerListTool { get; private set; } private DockEntryDefinition? _savedRootDef; @@ -45,6 +46,7 @@ namespace Mesen.Debugger LabelListTool = new("Labels"); FunctionListTool = new("Functions"); FindResultListTool = new("Find Results"); + ControllerListTool = new("Controllers"); _savedRootDef = savedRootDef; } @@ -82,7 +84,7 @@ namespace Mesen.Debugger new MesenProportionalDockSplitter(), new ToolDock { Proportion = 0.5, - VisibleDockables = CreateList(LabelListTool, FunctionListTool, FindResultListTool) + VisibleDockables = CreateList(LabelListTool, FunctionListTool, FindResultListTool, ControllerListTool) } ) } @@ -200,6 +202,7 @@ namespace Mesen.Debugger case nameof(LabelListViewModel): return LabelListTool; case nameof(FunctionListViewModel): return FunctionListTool; case nameof(FindResultListViewModel): return FindResultListTool; + case nameof(ControllerListViewModel): return ControllerListTool; } break; } diff --git a/NewUI/Debugger/Utilities/ContextMenuAction.cs b/NewUI/Debugger/Utilities/ContextMenuAction.cs index 03cd9360..04fd3528 100644 --- a/NewUI/Debugger/Utilities/ContextMenuAction.cs +++ b/NewUI/Debugger/Utilities/ContextMenuAction.cs @@ -319,6 +319,7 @@ namespace Mesen.Debugger.Utilities ShowFunctionList, ShowLabelList, ShowCallStack, + ShowControllers, ShowSettingsPanel, ShowMemoryMappings, diff --git a/NewUI/Debugger/ViewModels/ControllerInputViewModel.cs b/NewUI/Debugger/ViewModels/ControllerInputViewModel.cs new file mode 100644 index 00000000..8c9f9d39 --- /dev/null +++ b/NewUI/Debugger/ViewModels/ControllerInputViewModel.cs @@ -0,0 +1,92 @@ +using Avalonia; +using Avalonia.Collections; +using Avalonia.Controls; +using Avalonia.Controls.Selection; +using Avalonia.Media; +using Dock.Model.ReactiveUI.Controls; +using Mesen.Config; +using Mesen.Debugger.Labels; +using Mesen.Debugger.Utilities; +using Mesen.Debugger.Windows; +using Mesen.Interop; +using Mesen.Utilities; +using Mesen.ViewModels; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Mesen.Debugger.ViewModels +{ + public class ControllerInputViewModel : ViewModelBase + { + [Reactive] public bool ButtonA { get; set; } + [Reactive] public bool ButtonB { get; set; } + [Reactive] public bool ButtonX { get; set; } + [Reactive] public bool ButtonY { get; set; } + [Reactive] public bool ButtonL { get; set; } + [Reactive] public bool ButtonR { get; set; } + [Reactive] public bool ButtonUp { get; set; } + [Reactive] public bool ButtonDown { get; set; } + [Reactive] public bool ButtonLeft { get; set; } + [Reactive] public bool ButtonRight { get; set; } + [Reactive] public bool ButtonSelect { get; set; } + [Reactive] public bool ButtonStart { get; set; } + + public int ControllerIndex { get; } + public bool IsSnes { get; } + + [Obsolete("For designer only")] + public ControllerInputViewModel() : this(ConsoleType.Snes, 0) { } + + public ControllerInputViewModel(ConsoleType consoleType, int index) + { + ControllerIndex = index + 1; + IsSnes = consoleType == ConsoleType.Snes; + + if(Design.IsDesignMode) { + return; + } + + PropertyChanged += ControllerInputViewModel_PropertyChanged; + } + + private void ControllerInputViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + DebugApi.SetInputOverrides((uint)ControllerIndex - 1, new DebugControllerState() { + A = ButtonA, + B = ButtonB, + X = ButtonX, + Y = ButtonY, + L = ButtonL, + R = ButtonR, + Up = ButtonUp, + Down = ButtonDown, + Left = ButtonLeft, + Right = ButtonRight, + Select = ButtonSelect, + Start = ButtonStart + }); + } + + public void OnButtonClick(object param) + { + if(param is string buttonName) { + switch(buttonName) { + case "A": ButtonA = !ButtonA; break; + case "B": ButtonB = !ButtonB; break; + case "X": ButtonX = !ButtonX; break; + case "Y": ButtonY = !ButtonY; break; + case "L": ButtonL = !ButtonL; break; + case "R": ButtonR = !ButtonR; break; + case "Up": ButtonUp = !ButtonUp; break; + case "Down": ButtonDown = !ButtonDown; break; + case "Left": ButtonLeft = !ButtonLeft; break; + case "Right": ButtonRight = !ButtonRight; break; + case "Select": ButtonSelect = !ButtonSelect; break; + case "Start": ButtonStart = !ButtonStart; break; + } + } + } + } +} diff --git a/NewUI/Debugger/ViewModels/ControllerListViewModel.cs b/NewUI/Debugger/ViewModels/ControllerListViewModel.cs new file mode 100644 index 00000000..cefe393c --- /dev/null +++ b/NewUI/Debugger/ViewModels/ControllerListViewModel.cs @@ -0,0 +1,28 @@ +using Avalonia.Controls; +using Mesen.Interop; +using Mesen.ViewModels; +using ReactiveUI.Fody.Helpers; +using System; +using System.Collections.Generic; + +namespace Mesen.Debugger.ViewModels +{ + public class ControllerListViewModel : ViewModelBase + { + [Reactive] public List Controllers { get; set; } = new(); + + [Obsolete("For designer only")] + public ControllerListViewModel() : this(ConsoleType.Snes) { } + + public ControllerListViewModel(ConsoleType consoleType) + { + if(Design.IsDesignMode) { + return; + } + + foreach(int index in DebugApi.GetAvailableInputOverrides()) { + Controllers.Add(new ControllerInputViewModel(consoleType, index)); + } + } + } +} diff --git a/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs b/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs index 12277a49..a0ff3d34 100644 --- a/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs +++ b/NewUI/Debugger/ViewModels/DebuggerWindowViewModel.cs @@ -49,6 +49,7 @@ namespace Mesen.Debugger.ViewModels [Reactive] public SourceViewViewModel? SourceView { get; private set; } [Reactive] public MemoryMappingViewModel? MemoryMappings { get; private set; } [Reactive] public FindResultListViewModel FindResultList { get; private set; } + [Reactive] public ControllerListViewModel ControllerList { get; private set; } [Reactive] public DebuggerDockFactory DockFactory { get; private set; } [Reactive] public IRootDock DockLayout { get; private set; } @@ -78,13 +79,16 @@ namespace Mesen.Debugger.ViewModels DebugApi.InitializeDebugger(); } + ConsoleType consoleType; if(Design.IsDesignMode) { CpuType = CpuType.Snes; + consoleType = ConsoleType.Snes; } else { RomInfo romInfo = EmuApi.GetRomInfo(); + consoleType = romInfo.ConsoleType; if(cpuType != null) { CpuType = cpuType.Value; - if(romInfo.ConsoleType.GetMainCpuType() != CpuType) { + if(consoleType.GetMainCpuType() != CpuType) { Title = ResourceHelper.GetEnumText(CpuType) + " Debugger"; Icon = new WindowIcon(ImageUtilities.BitmapFromAsset("Assets/" + CpuType.ToString() + "Debugger.png")); IsMainCpuDebugger = false; @@ -104,6 +108,7 @@ namespace Mesen.Debugger.ViewModels BreakpointList = new BreakpointListViewModel(CpuType, this); LabelList = new LabelListViewModel(CpuType, this); FindResultList = new FindResultListViewModel(this); + ControllerList = new ControllerListViewModel(consoleType); if(CpuType.SupportsFunctionList()) { FunctionList = new FunctionListViewModel(CpuType, this); } @@ -130,6 +135,7 @@ namespace Mesen.Debugger.ViewModels DockFactory.CallStackTool.Model = CallStack; DockFactory.WatchListTool.Model = WatchList; DockFactory.FindResultListTool.Model = FindResultList; + DockFactory.ControllerListTool.Model = ControllerList; DockFactory.DisassemblyTool.Model = Disassembly; DockFactory.SourceViewTool.Model = null; DockFactory.StatusTool.Model = ConsoleStatus; @@ -508,11 +514,6 @@ namespace Mesen.Debugger.ViewModels OnClick = () => cfg.ShowMemoryMappings = !cfg.ShowMemoryMappings }, new ContextMenuSeparator(), - new ContextMenuAction() { - ActionType = ActionType.ShowWatchList, - IsSelected = () => IsToolVisible(DockFactory.WatchListTool), - OnClick = () => ToggleTool(DockFactory.WatchListTool) - }, new ContextMenuAction() { ActionType = ActionType.ShowBreakpointList, IsSelected = () => IsToolVisible(DockFactory.BreakpointListTool), @@ -523,6 +524,17 @@ namespace Mesen.Debugger.ViewModels IsSelected = () => IsToolVisible(DockFactory.CallStackTool), OnClick = () => ToggleTool(DockFactory.CallStackTool) }, + new ContextMenuAction() { + ActionType = ActionType.ShowConsoleStatus, + IsSelected = () => IsToolVisible(DockFactory.StatusTool), + OnClick = () => ToggleTool(DockFactory.StatusTool) + }, + new ContextMenuAction() { + ActionType = ActionType.ShowControllers, + IsVisible = () => FunctionList != null, + IsSelected = () => IsToolVisible(DockFactory.ControllerListTool), + OnClick = () => ToggleTool(DockFactory.ControllerListTool) + }, new ContextMenuAction() { ActionType = ActionType.ShowFunctionList, IsVisible = () => FunctionList != null, @@ -535,9 +547,9 @@ namespace Mesen.Debugger.ViewModels OnClick = () => ToggleTool(DockFactory.LabelListTool) }, new ContextMenuAction() { - ActionType = ActionType.ShowConsoleStatus, - IsSelected = () => IsToolVisible(DockFactory.StatusTool), - OnClick = () => ToggleTool(DockFactory.StatusTool) + ActionType = ActionType.ShowWatchList, + IsSelected = () => IsToolVisible(DockFactory.WatchListTool), + OnClick = () => ToggleTool(DockFactory.WatchListTool) }, new ContextMenuSeparator(), new ContextMenuAction() { diff --git a/NewUI/Debugger/Views/ControllerInputView.axaml b/NewUI/Debugger/Views/ControllerInputView.axaml new file mode 100644 index 00000000..9bb7d3f0 --- /dev/null +++ b/NewUI/Debugger/Views/ControllerInputView.axaml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + +