From 6d22b920b8ab0391e0aca1c6a344c419aa29ab1c Mon Sep 17 00:00:00 2001 From: Sour Date: Fri, 15 Feb 2019 21:33:13 -0500 Subject: [PATCH] Debugger: Added hex editor --- Core/BaseCartridge.h | 47 + Core/Console.cpp | 16 +- Core/Console.h | 7 + Core/Core.vcxproj | 7 + Core/Core.vcxproj.filters | 21 + Core/DebugTypes.h | 13 + Core/Debugger.cpp | 21 +- Core/Debugger.h | 8 +- Core/IMemoryHandler.h | 14 + Core/INotificationListener.h | 12 +- Core/MemoryDumper.cpp | 113 + Core/MemoryDumper.h | 28 + Core/MemoryManager.h | 57 +- Core/NotificationManager.cpp | 50 + Core/NotificationManager.h | 18 + Core/Ppu.cpp | 21 +- Core/Ppu.h | 13 +- Core/stdafx.h | 2 + InteropDLL/DebugApiWrapper.cpp | 9 + InteropDLL/EmuApiWrapper.cpp | 42 +- InteropDLL/InteropDLL.vcxproj | 2 + InteropDLL/InteropDLL.vcxproj.filters | 6 + InteropDLL/InteropNotificationListener.h | 26 + InteropDLL/InteropNotificationListeners.h | 38 + UI/Debugger/Config/DebugInfo.cs | 45 + UI/Debugger/Config/DebuggerShortcutsConfig.cs | 388 ++ UI/Debugger/Config/HexEditorInfo.cs | 67 + .../Config/TraceLoggerInfo.cs} | 72 +- UI/Debugger/Controls/ComboBoxWithSeparator.cs | 59 + UI/Debugger/Controls/ctrlDbgShortcuts.cs | 68 + .../Controls/ctrlDbgShortcuts.designer.cs | 175 + UI/Debugger/Controls/ctrlDbgShortcuts.resx | 126 + UI/Debugger/Controls/ctrlHexViewer.cs | 457 ++ .../Controls/ctrlHexViewer.designer.cs | 389 ++ UI/Debugger/Controls/ctrlHexViewer.resx | 126 + UI/Debugger/Controls/ctrlTextbox.cs | 8 +- UI/Debugger/HexBox/BuiltInContextMenu.cs | 220 + UI/Debugger/HexBox/ByteCharConverters.cs | 128 + UI/Debugger/HexBox/ByteCollection.cs | 127 + UI/Debugger/HexBox/BytePositionInfo.cs | 28 + UI/Debugger/HexBox/DataBlock.cs | 42 + UI/Debugger/HexBox/DataMap.cs | 318 ++ UI/Debugger/HexBox/DynamicByteProvider.cs | 226 + UI/Debugger/HexBox/FileDataBlock.cs | 96 + UI/Debugger/HexBox/FindOptions.cs | 95 + UI/Debugger/HexBox/HexBox.cs | 4244 +++++++++++++++++ UI/Debugger/HexBox/HexBox.resx | 126 + UI/Debugger/HexBox/HexCasing.cs | 21 + UI/Debugger/HexBox/IByteColorProvider.cs | 18 + UI/Debugger/HexBox/IByteProvider.cs | 81 + UI/Debugger/HexBox/MemoryDataBlock.cs | 87 + UI/Debugger/HexBox/NativeMethods.cs | 14 + UI/Debugger/HexBox/StaticByteProvider.cs | 25 + UI/Debugger/HexBox/TblByteCharConverter.cs | 120 + UI/Debugger/HexBox/Util.cs | 44 + UI/Debugger/TblLoader.cs | 61 + UI/Debugger/frmDbgPreferences.cs | 161 + UI/Debugger/frmDbgPreferences.designer.cs | 188 + UI/Debugger/frmDbgPreferences.resx | 123 + UI/Debugger/frmDbgShortcutGetKey.cs | 62 + UI/Debugger/frmDbgShortcutGetKey.designer.cs | 114 + UI/Debugger/frmDbgShortcutGetKey.resx | 123 + UI/Debugger/frmFadeSpeed.cs | 33 + UI/Debugger/frmFadeSpeed.designer.cs | 125 + UI/Debugger/frmFadeSpeed.resx | 123 + UI/Debugger/frmGoToLine.cs | 37 + UI/Debugger/frmGoToLine.designer.cs | 101 + UI/Debugger/frmGoToLine.resx | 120 + UI/Debugger/frmMemoryTools.cs | 693 +++ UI/Debugger/frmMemoryTools.designer.cs | 876 ++++ UI/Debugger/frmMemoryTools.resx | 126 + UI/Debugger/frmTraceLogger.cs | 45 +- UI/Forms/EntityBinder.cs | 26 +- UI/Forms/frmMain.Designer.cs | 15 + UI/Forms/frmMain.cs | 52 +- UI/Interop/DebugApi.cs | 25 + UI/Interop/EmuApi.cs | 3 + UI/Interop/NotificationListener.cs | 61 + UI/UI.csproj | 98 +- 79 files changed, 11834 insertions(+), 188 deletions(-) create mode 100644 Core/BaseCartridge.h create mode 100644 Core/DebugTypes.h create mode 100644 Core/IMemoryHandler.h create mode 100644 Core/MemoryDumper.cpp create mode 100644 Core/MemoryDumper.h create mode 100644 Core/NotificationManager.cpp create mode 100644 Core/NotificationManager.h create mode 100644 InteropDLL/InteropNotificationListener.h create mode 100644 InteropDLL/InteropNotificationListeners.h create mode 100644 UI/Debugger/Config/DebugInfo.cs create mode 100644 UI/Debugger/Config/DebuggerShortcutsConfig.cs create mode 100644 UI/Debugger/Config/HexEditorInfo.cs rename UI/{Config/DebugInfo.cs => Debugger/Config/TraceLoggerInfo.cs} (55%) create mode 100644 UI/Debugger/Controls/ComboBoxWithSeparator.cs create mode 100644 UI/Debugger/Controls/ctrlDbgShortcuts.cs create mode 100644 UI/Debugger/Controls/ctrlDbgShortcuts.designer.cs create mode 100644 UI/Debugger/Controls/ctrlDbgShortcuts.resx create mode 100644 UI/Debugger/Controls/ctrlHexViewer.cs create mode 100644 UI/Debugger/Controls/ctrlHexViewer.designer.cs create mode 100644 UI/Debugger/Controls/ctrlHexViewer.resx create mode 100644 UI/Debugger/HexBox/BuiltInContextMenu.cs create mode 100644 UI/Debugger/HexBox/ByteCharConverters.cs create mode 100644 UI/Debugger/HexBox/ByteCollection.cs create mode 100644 UI/Debugger/HexBox/BytePositionInfo.cs create mode 100644 UI/Debugger/HexBox/DataBlock.cs create mode 100644 UI/Debugger/HexBox/DataMap.cs create mode 100644 UI/Debugger/HexBox/DynamicByteProvider.cs create mode 100644 UI/Debugger/HexBox/FileDataBlock.cs create mode 100644 UI/Debugger/HexBox/FindOptions.cs create mode 100644 UI/Debugger/HexBox/HexBox.cs create mode 100644 UI/Debugger/HexBox/HexBox.resx create mode 100644 UI/Debugger/HexBox/HexCasing.cs create mode 100644 UI/Debugger/HexBox/IByteColorProvider.cs create mode 100644 UI/Debugger/HexBox/IByteProvider.cs create mode 100644 UI/Debugger/HexBox/MemoryDataBlock.cs create mode 100644 UI/Debugger/HexBox/NativeMethods.cs create mode 100644 UI/Debugger/HexBox/StaticByteProvider.cs create mode 100644 UI/Debugger/HexBox/TblByteCharConverter.cs create mode 100644 UI/Debugger/HexBox/Util.cs create mode 100644 UI/Debugger/TblLoader.cs create mode 100644 UI/Debugger/frmDbgPreferences.cs create mode 100644 UI/Debugger/frmDbgPreferences.designer.cs create mode 100644 UI/Debugger/frmDbgPreferences.resx create mode 100644 UI/Debugger/frmDbgShortcutGetKey.cs create mode 100644 UI/Debugger/frmDbgShortcutGetKey.designer.cs create mode 100644 UI/Debugger/frmDbgShortcutGetKey.resx create mode 100644 UI/Debugger/frmFadeSpeed.cs create mode 100644 UI/Debugger/frmFadeSpeed.designer.cs create mode 100644 UI/Debugger/frmFadeSpeed.resx create mode 100644 UI/Debugger/frmGoToLine.cs create mode 100644 UI/Debugger/frmGoToLine.designer.cs create mode 100644 UI/Debugger/frmGoToLine.resx create mode 100644 UI/Debugger/frmMemoryTools.cs create mode 100644 UI/Debugger/frmMemoryTools.designer.cs create mode 100644 UI/Debugger/frmMemoryTools.resx create mode 100644 UI/Interop/NotificationListener.cs diff --git a/Core/BaseCartridge.h b/Core/BaseCartridge.h new file mode 100644 index 0000000..2ddcc28 --- /dev/null +++ b/Core/BaseCartridge.h @@ -0,0 +1,47 @@ +#pragma once +#include "stdafx.h" +#include "IMemoryHandler.h" +#include "../Utilities/VirtualFile.h" + +class BaseCartridge : public IMemoryHandler +{ +private: + uint8_t* _prgRom = nullptr; + uint8_t* _saveRam = nullptr; + + uint32_t _prgRomSize = 0; + uint32_t _saveRamSize = 0; + +public: + static shared_ptr CreateCartridge(VirtualFile romFile, VirtualFile patchFile) + { + if(romFile.IsValid()) { + vector romData; + romFile.ReadFile(romData); + + shared_ptr cart(new BaseCartridge()); + cart->_prgRomSize = (uint32_t)romData.size(); + cart->_prgRom = new uint8_t[cart->_prgRomSize]; + memcpy(cart->_prgRom, romData.data(), cart->_prgRomSize); + + return cart; + } else { + return nullptr; + } + } + + uint8_t Read(uint32_t addr) override + { + uint8_t bank = (addr >> 16) & 0x7F; + return _prgRom[((bank * 0x8000) | (addr & 0x7FFF)) & (_prgRomSize - 1)]; + } + + void Write(uint32_t addr, uint8_t value) override + { + } + + uint8_t* DebugGetPrgRom() { return _prgRom; } + uint8_t* DebugGetSaveRam() { return _saveRam; } + uint32_t DebugGetPrgRomSize() { return _prgRomSize; } + uint32_t DebugGetSaveRamSize() { return _saveRamSize; } +}; diff --git a/Core/Console.cpp b/Core/Console.cpp index 1430016..a2172d5 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -3,6 +3,7 @@ #include "Cpu.h" #include "MemoryManager.h" #include "Debugger.h" +#include "NotificationManager.h" #include "VideoDecoder.h" #include "VideoRenderer.h" #include "DebugHud.h" @@ -11,6 +12,7 @@ void Console::Initialize() { + _notificationManager.reset(new NotificationManager()); _videoDecoder.reset(new VideoDecoder(shared_from_this())); _videoRenderer.reset(new VideoRenderer(shared_from_this())); _debugHud.reset(new DebugHud()); @@ -57,12 +59,12 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile) shared_ptr cart = BaseCartridge::CreateCartridge(romFile, patchFile); if(cart) { _ppu.reset(new Ppu(shared_from_this())); - + _cart = cart; _memoryManager.reset(new MemoryManager()); _memoryManager->Initialize(cart, shared_from_this()); _cpu.reset(new Cpu(_memoryManager)); - _debugger.reset(new Debugger(_cpu, _ppu, _memoryManager)); + _debugger.reset(new Debugger(shared_from_this())); } } @@ -76,6 +78,11 @@ shared_ptr Console::GetVideoDecoder() return _videoDecoder; } +shared_ptr Console::GetNotificationManager() +{ + return _notificationManager; +} + shared_ptr Console::GetDebugHud() { return _debugHud; @@ -91,6 +98,11 @@ shared_ptr Console::GetPpu() return _ppu; } +shared_ptr Console::GetCartridge() +{ + return _cart; +} + shared_ptr Console::GetMemoryManager() { return _memoryManager; diff --git a/Core/Console.h b/Core/Console.h index 6455a78..85765e1 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -5,11 +5,13 @@ class Cpu; class Ppu; +class BaseCartridge; class MemoryManager; class Debugger; class DebugHud; class VideoRenderer; class VideoDecoder; +class NotificationManager; enum class MemoryOperationType; class Console : public std::enable_shared_from_this @@ -18,8 +20,10 @@ private: shared_ptr _cpu; shared_ptr _ppu; shared_ptr _memoryManager; + shared_ptr _cart; shared_ptr _debugger; + shared_ptr _notificationManager; shared_ptr _videoRenderer; shared_ptr _videoDecoder; shared_ptr _debugHud; @@ -37,10 +41,13 @@ public: shared_ptr GetVideoRenderer(); shared_ptr GetVideoDecoder(); + shared_ptr GetNotificationManager(); + shared_ptr GetDebugHud(); shared_ptr GetCpu(); shared_ptr GetPpu(); + shared_ptr GetCartridge(); shared_ptr GetMemoryManager(); shared_ptr GetDebugger(bool allowStart = true); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index d2b7c04..dbdcdeb 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -43,6 +43,7 @@ + @@ -51,6 +52,7 @@ + @@ -64,12 +66,15 @@ + + + @@ -91,7 +96,9 @@ + + Create diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 1a0340c..babe9ab 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -101,6 +101,21 @@ SNES + + Debugger + + + Debugger + + + SNES + + + Interfaces + + + Misc + @@ -155,6 +170,12 @@ SNES + + Debugger + + + Misc + diff --git a/Core/DebugTypes.h b/Core/DebugTypes.h new file mode 100644 index 0000000..8b14d78 --- /dev/null +++ b/Core/DebugTypes.h @@ -0,0 +1,13 @@ +#pragma once +#include "stdafx.h" + +enum class SnesMemoryType +{ + CpuMemory, + PrgRom, + WorkRam, + SaveRam, + VideoRam, + SpriteRam, + CGRam, +}; \ No newline at end of file diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 2acb53a..52875f7 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -1,18 +1,22 @@ #include "stdafx.h" #include "Debugger.h" +#include "Console.h" #include "Cpu.h" #include "Ppu.h" #include "CpuTypes.h" #include "DisassemblyInfo.h" #include "TraceLogger.h" +#include "MemoryDumper.h" #include "../Utilities/HexUtilities.h" -Debugger::Debugger(shared_ptr cpu, shared_ptr ppu, shared_ptr memoryManager) +Debugger::Debugger(shared_ptr console) { - _cpu = cpu; - _ppu = ppu; - _memoryManager = memoryManager; - _traceLogger.reset(new TraceLogger(this, memoryManager)); + _cpu = console->GetCpu(); + _ppu = console->GetPpu(); + _memoryManager = console->GetMemoryManager(); + + _traceLogger.reset(new TraceLogger(this, _memoryManager)); + _memoryDumper.reset(new MemoryDumper(_ppu, _memoryManager, console->GetCartridge())); _cpuStepCount = 0; } @@ -72,4 +76,9 @@ void Debugger::GetState(DebugState *state) shared_ptr Debugger::GetTraceLogger() { return _traceLogger; -} \ No newline at end of file +} + +shared_ptr Debugger::GetMemoryDumper() +{ + return _memoryDumper; +} diff --git a/Core/Debugger.h b/Core/Debugger.h index e087c03..f6e03bc 100644 --- a/Core/Debugger.h +++ b/Core/Debugger.h @@ -3,12 +3,15 @@ #include "CpuTypes.h" #include "PpuTypes.h" +class Console; class Cpu; class Ppu; +class BaseCartridge; class MemoryManager; enum class MemoryOperationType; class TraceLogger; +class MemoryDumper; //class Disassembler; struct DebugState @@ -24,14 +27,16 @@ private: shared_ptr _cpu; shared_ptr _ppu; shared_ptr _memoryManager; + shared_ptr _baseCartridge; shared_ptr _traceLogger; + shared_ptr _memoryDumper; //unique_ptr _disassembler; atomic _cpuStepCount; public: - Debugger(shared_ptr cpu, shared_ptr ppu, shared_ptr memoryManager); + Debugger(shared_ptr console); ~Debugger(); void ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type); @@ -44,4 +49,5 @@ public: void GetState(DebugState *state); shared_ptr GetTraceLogger(); + shared_ptr GetMemoryDumper(); }; \ No newline at end of file diff --git a/Core/IMemoryHandler.h b/Core/IMemoryHandler.h new file mode 100644 index 0000000..c798b73 --- /dev/null +++ b/Core/IMemoryHandler.h @@ -0,0 +1,14 @@ +#pragma once +#include "stdafx.h" + +class IMemoryHandler +{ +public: + virtual uint8_t Read(uint32_t addr) = 0; + virtual void Write(uint32_t addr, uint8_t value) = 0; + + //virtual void GetMemoryRanges(MemoryRanges &ranges) = 0; + //virtual uint8_t PeekRAM(uint16_t addr) { return 0; } + + virtual ~IMemoryHandler() {} +}; \ No newline at end of file diff --git a/Core/INotificationListener.h b/Core/INotificationListener.h index dbac61c..8b986b9 100644 --- a/Core/INotificationListener.h +++ b/Core/INotificationListener.h @@ -10,12 +10,12 @@ enum class ConsoleNotificationType GameResumed = 4, GameStopped = 5, CodeBreak = 6, - PpuFrameDone = 9, - ResolutionChanged = 11, - ConfigChanged = 13, - ExecuteShortcut = 16, - EmulationStopped = 17, - BeforeEmulationStop = 19, + PpuFrameDone = 7, + ResolutionChanged = 8, + ConfigChanged = 9, + ExecuteShortcut = 10, + EmulationStopped = 11, + BeforeEmulationStop = 12, }; class INotificationListener diff --git a/Core/MemoryDumper.cpp b/Core/MemoryDumper.cpp new file mode 100644 index 0000000..5018b93 --- /dev/null +++ b/Core/MemoryDumper.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" +#include "Debugger.h" +#include "MemoryManager.h" +#include "Ppu.h" +#include "MemoryDumper.h" +#include "BaseCartridge.h" +#include "VideoDecoder.h" +#include "DebugTypes.h" + +MemoryDumper::MemoryDumper(shared_ptr ppu, shared_ptr memoryManager, shared_ptr cartridge) +{ + _ppu = ppu; + _memoryManager = memoryManager; + _cartridge = cartridge; +} + +void MemoryDumper::SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t length) +{ + if(length > GetMemorySize(type)) { + return; + } + + switch(type) { + case SnesMemoryType::CpuMemory: + break; + + case SnesMemoryType::PrgRom: memcpy(_cartridge->DebugGetPrgRom(), buffer, length); break; + case SnesMemoryType::WorkRam: memcpy(_memoryManager->DebugGetWorkRam(), buffer, length); break; + case SnesMemoryType::SaveRam: memcpy(_cartridge->DebugGetSaveRam(), buffer, length); break; + case SnesMemoryType::VideoRam: memcpy(_ppu->GetVideoRam(), buffer, length); break; + case SnesMemoryType::SpriteRam: memcpy(_ppu->GetSpriteRam(), buffer, length); break; + case SnesMemoryType::CGRam: memcpy(_ppu->GetCgRam(), buffer, length); break; + } +} + +uint32_t MemoryDumper::GetMemorySize(SnesMemoryType type) +{ + switch(type) { + case SnesMemoryType::CpuMemory: return 0x1000000; + case SnesMemoryType::PrgRom: return _cartridge->DebugGetPrgRomSize(); + case SnesMemoryType::WorkRam: return MemoryManager::WorkRamSize; + case SnesMemoryType::SaveRam: return _cartridge->DebugGetSaveRamSize(); + case SnesMemoryType::VideoRam: return Ppu::VideoRamSize; + case SnesMemoryType::SpriteRam: return Ppu::SpriteRamSize; + case SnesMemoryType::CGRam: return Ppu::CgRamSize; + } + return 0; +} + +void MemoryDumper::GetMemoryState(SnesMemoryType type, uint8_t *buffer) +{ + switch(type) { + case SnesMemoryType::CpuMemory: + for(int i = 0; i <= 0xFFFFFF; i++) { + buffer[i] = _memoryManager->Peek(i); + } + break; + + case SnesMemoryType::PrgRom: memcpy(buffer, _cartridge->DebugGetPrgRom(), _cartridge->DebugGetPrgRomSize()); break; + case SnesMemoryType::WorkRam: memcpy(buffer, _memoryManager->DebugGetWorkRam(), MemoryManager::WorkRamSize); break; + case SnesMemoryType::SaveRam: memcpy(buffer, _cartridge->DebugGetSaveRam(), _cartridge->DebugGetSaveRamSize()); break; + case SnesMemoryType::VideoRam: memcpy(buffer, _ppu->GetVideoRam(), Ppu::VideoRamSize); break; + case SnesMemoryType::SpriteRam: memcpy(buffer, _ppu->GetSpriteRam(), Ppu::SpriteRamSize); break; + case SnesMemoryType::CGRam: memcpy(buffer, _ppu->GetCgRam(), Ppu::CgRamSize); break; + } +} + +void MemoryDumper::SetMemoryValues(SnesMemoryType memoryType, uint32_t address, uint8_t* data, uint32_t length) +{ + for(uint32_t i = 0; i < length; i++) { + SetMemoryValue(memoryType, address+i, data[i], true); + } +} + +void MemoryDumper::SetMemoryValue(SnesMemoryType memoryType, uint32_t address, uint8_t value, bool disableSideEffects) +{ + if(address >= GetMemorySize(memoryType)) { + return; + } + + switch(memoryType) { + case SnesMemoryType::CpuMemory: _memoryManager->Write(address, value, MemoryOperationType::Write); break; + + case SnesMemoryType::PrgRom: _cartridge->DebugGetPrgRom()[address] = value; break; + case SnesMemoryType::WorkRam: _memoryManager->DebugGetWorkRam()[address] = value; break; + case SnesMemoryType::SaveRam: _cartridge->DebugGetSaveRam()[address] = value; break; + + case SnesMemoryType::VideoRam: _ppu->GetVideoRam()[address & (Ppu::VideoRamSize - 1)] = value; + case SnesMemoryType::SpriteRam: _ppu->GetSpriteRam()[address % Ppu::SpriteRamSize] = value; break; + case SnesMemoryType::CGRam: _ppu->GetCgRam()[address & (Ppu::CgRamSize - 1)] = value; break; + } +} + +uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address, bool disableSideEffects) +{ + if(address >= GetMemorySize(memoryType)) { + return 0; + } + + switch(memoryType) { + case SnesMemoryType::CpuMemory: return _memoryManager->Peek(address); + + case SnesMemoryType::PrgRom: return _cartridge->DebugGetPrgRom()[address]; + case SnesMemoryType::WorkRam: return _memoryManager->DebugGetWorkRam()[address]; + case SnesMemoryType::SaveRam: return _cartridge->DebugGetSaveRam()[address]; + + case SnesMemoryType::VideoRam: return _ppu->GetVideoRam()[address & (Ppu::VideoRamSize - 1)]; + case SnesMemoryType::SpriteRam: return _ppu->GetSpriteRam()[address % Ppu::SpriteRamSize]; + case SnesMemoryType::CGRam: return _ppu->GetCgRam()[address & (Ppu::CgRamSize - 1)]; + } + + return 0; +} diff --git a/Core/MemoryDumper.h b/Core/MemoryDumper.h new file mode 100644 index 0000000..144dd54 --- /dev/null +++ b/Core/MemoryDumper.h @@ -0,0 +1,28 @@ +#pragma once +#include "stdafx.h" +#include +#include "DebugTypes.h" + +class MemoryManager; +class BaseCartridge; +class Ppu; +enum class SnesMemoryType; + +class MemoryDumper +{ +private: + shared_ptr _ppu; + shared_ptr _memoryManager; + shared_ptr _cartridge; + +public: + MemoryDumper(shared_ptr ppu, shared_ptr memoryManager, shared_ptr cartridge); + + uint32_t GetMemorySize(SnesMemoryType type); + void GetMemoryState(SnesMemoryType type, uint8_t *buffer); + + uint8_t GetMemoryValue(SnesMemoryType memoryType, uint32_t address, bool disableSideEffects = true); + void SetMemoryValue(SnesMemoryType memoryType, uint32_t address, uint8_t value, bool disableSideEffects = true); + void SetMemoryValues(SnesMemoryType memoryType, uint32_t address, uint8_t* data, uint32_t length); + void SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t length); +}; \ No newline at end of file diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index ca6e92b..d305d61 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -3,55 +3,9 @@ #include "Console.h" #include "Ppu.h" #include "DmaController.h" +#include "BaseCartridge.h" +#include "IMemoryHandler.h" #include "../Utilities/HexUtilities.h" -#include "../Utilities/VirtualFile.h" - -class IMemoryHandler -{ -public: - virtual uint8_t Read(uint32_t addr) = 0; - virtual void Write(uint32_t addr, uint8_t value) = 0; - - //virtual void GetMemoryRanges(MemoryRanges &ranges) = 0; - //virtual uint8_t PeekRAM(uint16_t addr) { return 0; } - - virtual ~IMemoryHandler() {} -}; - -class BaseCartridge : public IMemoryHandler -{ -private: - size_t _prgRomSize; - uint8_t* _prgRom; - -public: - static shared_ptr CreateCartridge(VirtualFile romFile, VirtualFile patchFile) - { - if(romFile.IsValid()) { - vector romData; - romFile.ReadFile(romData); - - shared_ptr cart(new BaseCartridge()); - cart->_prgRomSize = romData.size(); - cart->_prgRom = new uint8_t[cart->_prgRomSize]; - memcpy(cart->_prgRom, romData.data(), cart->_prgRomSize); - - return cart; - } else { - return nullptr; - } - } - - uint8_t Read(uint32_t addr) override - { - uint8_t bank = (addr >> 16) & 0x7F; - return _prgRom[((bank * 0x8000) | (addr & 0x7FFF)) & (_prgRomSize - 1)]; - } - - void Write(uint32_t addr, uint8_t value) override - { - } -}; class CpuRegisterHandler : public IMemoryHandler { @@ -104,6 +58,9 @@ public: class MemoryManager { +public: + constexpr static uint32_t WorkRamSize = 0x20000; + private: shared_ptr _console; @@ -133,7 +90,7 @@ public: _cpuRegisterHandler.reset(new CpuRegisterHandler(console)); memset(_handlers, 0, sizeof(_handlers)); - _workRam = new uint8_t[128 * 1024]; + _workRam = new uint8_t[MemoryManager::WorkRamSize]; //memset(_workRam, 0, 128 * 1024); for(uint32_t i = 0; i < 128 * 1024; i += 0x1000) { @@ -270,4 +227,6 @@ public: //std::cout << "Write - missing handler: $" << HexUtilities::ToHex(addr) << " = " << HexUtilities::ToHex(value) << std::endl; } } + + uint8_t* DebugGetWorkRam() { return _workRam; } }; \ No newline at end of file diff --git a/Core/NotificationManager.cpp b/Core/NotificationManager.cpp new file mode 100644 index 0000000..aa5f50c --- /dev/null +++ b/Core/NotificationManager.cpp @@ -0,0 +1,50 @@ +#include "stdafx.h" +#include +#include "NotificationManager.h" + +void NotificationManager::RegisterNotificationListener(shared_ptr notificationListener) +{ + auto lock = _lock.AcquireSafe(); + + for(weak_ptr listener : _listeners) { + if(listener.lock() == notificationListener) { + //This listener is already registered, do nothing + return; + } + } + + _listeners.push_back(notificationListener); +} + +void NotificationManager::CleanupNotificationListeners() +{ + auto lock = _lock.AcquireSafe(); + + //Remove expired listeners + _listeners.erase( + std::remove_if( + _listeners.begin(), + _listeners.end(), + [](weak_ptr ptr) { return ptr.expired(); } + ), + _listeners.end() + ); +} + +void NotificationManager::SendNotification(ConsoleNotificationType type, void* parameter) +{ + vector> listeners; + { + auto lock = _lock.AcquireSafe(); + CleanupNotificationListeners(); + listeners = _listeners; + } + + //Iterate on a copy without using a lock + for(weak_ptr notificationListener : listeners) { + shared_ptr listener = notificationListener.lock(); + if(listener) { + listener->ProcessNotification(type, parameter); + } + } +} diff --git a/Core/NotificationManager.h b/Core/NotificationManager.h new file mode 100644 index 0000000..e38a39d --- /dev/null +++ b/Core/NotificationManager.h @@ -0,0 +1,18 @@ +#pragma once +#include "stdafx.h" +#include "INotificationListener.h" +#include "../Utilities/SimpleLock.h" + +class NotificationManager +{ +private: + SimpleLock _lock; + vector> _listenersToAdd; + vector> _listeners; + + void CleanupNotificationListeners(); + +public: + void RegisterNotificationListener(shared_ptr notificationListener); + void SendNotification(ConsoleNotificationType type, void* parameter = nullptr); +}; diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index e70fb99..96cd955 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -4,13 +4,14 @@ #include "MemoryManager.h" #include "Cpu.h" #include "VideoDecoder.h" +#include "NotificationManager.h" Ppu::Ppu(shared_ptr console) { _console = console; - _vram = new uint8_t[0x10000]; - memset(_vram, 0, 0x10000); + _vram = new uint8_t[Ppu::VideoRamSize]; + memset(_vram, 0, Ppu::VideoRamSize); _layerConfig[0] = {}; _layerConfig[1] = {}; @@ -95,10 +96,26 @@ void Ppu::SendFrame() } } + _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuFrameDone); _currentBuffer = _currentBuffer == _outputBuffers[0] ? _outputBuffers[1] : _outputBuffers[0]; _console->GetVideoDecoder()->UpdateFrame(_currentBuffer, _frameCount); } +uint8_t* Ppu::GetVideoRam() +{ + return _vram; +} + +uint8_t* Ppu::GetCgRam() +{ + return _cgram; +} + +uint8_t* Ppu::GetSpriteRam() +{ + return _spriteRam; +} + uint8_t Ppu::Read(uint16_t addr) { switch(addr) { diff --git a/Core/Ppu.h b/Core/Ppu.h index 0982921..56c2f85 100644 --- a/Core/Ppu.h +++ b/Core/Ppu.h @@ -6,6 +6,11 @@ class Console; class Ppu { +public: + constexpr static uint32_t SpriteRamSize = 544; + constexpr static uint32_t CgRamSize = 512; + constexpr static uint32_t VideoRamSize = 0x10000; + private: shared_ptr _console; @@ -22,7 +27,9 @@ private: bool _vramAddrIncrementOnSecondReg; uint16_t _cgramAddress; - uint8_t _cgram[512]; + uint8_t _cgram[Ppu::CgRamSize]; + + uint8_t _spriteRam[Ppu::SpriteRamSize]; uint16_t *_outputBuffers[2]; uint16_t *_currentBuffer; @@ -39,6 +46,10 @@ public: void SendFrame(); + uint8_t* GetVideoRam(); + uint8_t* GetCgRam(); + uint8_t* GetSpriteRam(); + uint8_t Read(uint16_t addr); void Write(uint32_t addr, uint8_t value); }; \ No newline at end of file diff --git a/Core/stdafx.h b/Core/stdafx.h index 3598d11..c67ae48 100644 --- a/Core/stdafx.h +++ b/Core/stdafx.h @@ -17,6 +17,7 @@ #include #include #include +#include #include "../Utilities/UTF8Util.h" @@ -44,6 +45,7 @@ using std::string; using std::atomic_flag; using std::atomic; using std::thread; +using std::deque; #ifdef _DEBUG #pragma comment(lib, "C:\\Code\\Mesen-S\\bin\\x64\\Debug\\Utilities.lib") diff --git a/InteropDLL/DebugApiWrapper.cpp b/InteropDLL/DebugApiWrapper.cpp index 4e5ce9c..1246a36 100644 --- a/InteropDLL/DebugApiWrapper.cpp +++ b/InteropDLL/DebugApiWrapper.cpp @@ -2,6 +2,8 @@ #include "../Core/Console.h" #include "../Core/Debugger.h" #include "../Core/TraceLogger.h" +#include "../Core/MemoryDumper.h" +#include "../Core/DebugTypes.h" extern shared_ptr _console; shared_ptr _debugger; @@ -45,4 +47,11 @@ extern "C" DllExport const char* GetExecutionTrace(uint32_t lineCount) { return GetDebugger()->GetTraceLogger()->GetExecutionTrace(lineCount); } DllExport void __stdcall GetState(DebugState *state) { GetDebugger()->GetState(state); } + + DllExport void __stdcall SetMemoryState(SnesMemoryType type, uint8_t *buffer, int32_t length) { GetDebugger()->GetMemoryDumper()->SetMemoryState(type, buffer, length); } + DllExport uint32_t __stdcall GetMemorySize(SnesMemoryType type) { return GetDebugger()->GetMemoryDumper()->GetMemorySize(type); } + DllExport void __stdcall GetMemoryState(SnesMemoryType type, uint8_t *buffer) { GetDebugger()->GetMemoryDumper()->GetMemoryState(type, buffer); } + DllExport uint8_t __stdcall GetMemoryValue(SnesMemoryType type, uint32_t address) { return GetDebugger()->GetMemoryDumper()->GetMemoryValue(type, address); } + DllExport void __stdcall SetMemoryValue(SnesMemoryType type, uint32_t address, uint8_t value) { return GetDebugger()->GetMemoryDumper()->SetMemoryValue(type, address, value); } + DllExport void __stdcall SetMemoryValues(SnesMemoryType type, uint32_t address, uint8_t* data, int32_t length) { return GetDebugger()->GetMemoryDumper()->SetMemoryValues(type, address, data, length); } }; \ No newline at end of file diff --git a/InteropDLL/EmuApiWrapper.cpp b/InteropDLL/EmuApiWrapper.cpp index e185c55..bbc65a5 100644 --- a/InteropDLL/EmuApiWrapper.cpp +++ b/InteropDLL/EmuApiWrapper.cpp @@ -3,8 +3,8 @@ #include "../Core/MessageManager.h" #include "../Core/INotificationListener.h" #include "../Core/KeyManager.h" -#include "../Utilities/SimpleLock.h" #include "../Utilities/ArchiveReader.h" +#include "InteropNotificationListeners.h" #ifdef _WIN32 #include "../Windows/Renderer.h" @@ -26,31 +26,9 @@ void* _viewerHandle = nullptr; string _returnString; string _logString; shared_ptr _console; -SimpleLock _externalNotificationListenerLock; -vector> _externalNotificationListeners; - -typedef void (__stdcall *NotificationListenerCallback)(int, void*); +InteropNotificationListeners _listeners; namespace InteropEmu { - class InteropNotificationListener : public INotificationListener - { - NotificationListenerCallback _callback; - public: - InteropNotificationListener(NotificationListenerCallback callback) - { - _callback = callback; - } - - virtual ~InteropNotificationListener() - { - } - - void ProcessNotification(ConsoleNotificationType type, void* parameter) - { - _callback((int)type, parameter); - } - }; - extern "C" { DllExport bool __stdcall TestDll() { @@ -192,24 +170,12 @@ namespace InteropEmu { DllExport INotificationListener* __stdcall RegisterNotificationCallback(NotificationListenerCallback callback) { - auto lock = _externalNotificationListenerLock.AcquireSafe(); - auto listener = shared_ptr(new InteropNotificationListener(callback)); - _externalNotificationListeners.push_back(listener); - //_console->GetNotificationManager()->RegisterNotificationListener(listener); - return listener.get(); + return _listeners.RegisterNotificationCallback(callback, _console); } DllExport void __stdcall UnregisterNotificationCallback(INotificationListener *listener) { - auto lock = _externalNotificationListenerLock.AcquireSafe(); - _externalNotificationListeners.erase( - std::remove_if( - _externalNotificationListeners.begin(), - _externalNotificationListeners.end(), - [=](shared_ptr ptr) { return ptr.get() == listener; } - ), - _externalNotificationListeners.end() - ); + _listeners.UnregisterNotificationCallback(listener); } DllExport void __stdcall DisplayMessage(char* title, char* message, char* param1) { MessageManager::DisplayMessage(title, message, param1 ? param1 : ""); } diff --git a/InteropDLL/InteropDLL.vcxproj b/InteropDLL/InteropDLL.vcxproj index c543ee5..35700a9 100644 --- a/InteropDLL/InteropDLL.vcxproj +++ b/InteropDLL/InteropDLL.vcxproj @@ -448,6 +448,8 @@ + + diff --git a/InteropDLL/InteropDLL.vcxproj.filters b/InteropDLL/InteropDLL.vcxproj.filters index 2206318..46010e8 100644 --- a/InteropDLL/InteropDLL.vcxproj.filters +++ b/InteropDLL/InteropDLL.vcxproj.filters @@ -14,6 +14,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/InteropDLL/InteropNotificationListener.h b/InteropDLL/InteropNotificationListener.h new file mode 100644 index 0000000..e6d5791 --- /dev/null +++ b/InteropDLL/InteropNotificationListener.h @@ -0,0 +1,26 @@ +#pragma once +#include "stdafx.h" +#include "../Core/INotificationListener.h" +#include "../Core/NotificationManager.h" + +typedef void(__stdcall *NotificationListenerCallback)(int, void*); + +class InteropNotificationListener : public INotificationListener +{ + NotificationListenerCallback _callback; + +public: + InteropNotificationListener(NotificationListenerCallback callback) + { + _callback = callback; + } + + virtual ~InteropNotificationListener() + { + } + + void ProcessNotification(ConsoleNotificationType type, void* parameter) + { + _callback((int)type, parameter); + } +}; \ No newline at end of file diff --git a/InteropDLL/InteropNotificationListeners.h b/InteropDLL/InteropNotificationListeners.h new file mode 100644 index 0000000..30520f9 --- /dev/null +++ b/InteropDLL/InteropNotificationListeners.h @@ -0,0 +1,38 @@ +#pragma once +#include "stdafx.h" +#include "../Core/INotificationListener.h" +#include "../Core/NotificationManager.h" +#include "../Core/Console.h" +#include "../Utilities/SimpleLock.h" +#include "InteropNotificationListener.h" + +typedef void(__stdcall *NotificationListenerCallback)(int, void*); + +class InteropNotificationListeners +{ + SimpleLock _externalNotificationListenerLock; + vector> _externalNotificationListeners; + +public: + INotificationListener* RegisterNotificationCallback(NotificationListenerCallback callback, shared_ptr console) + { + auto lock = _externalNotificationListenerLock.AcquireSafe(); + auto listener = shared_ptr(new InteropNotificationListener(callback)); + _externalNotificationListeners.push_back(listener); + console->GetNotificationManager()->RegisterNotificationListener(listener); + return listener.get(); + } + + void UnregisterNotificationCallback(INotificationListener *listener) + { + auto lock = _externalNotificationListenerLock.AcquireSafe(); + _externalNotificationListeners.erase( + std::remove_if( + _externalNotificationListeners.begin(), + _externalNotificationListeners.end(), + [=](shared_ptr ptr) { return ptr.get() == listener; } + ), + _externalNotificationListeners.end() + ); + } +}; \ No newline at end of file diff --git a/UI/Debugger/Config/DebugInfo.cs b/UI/Debugger/Config/DebugInfo.cs new file mode 100644 index 0000000..784d62f --- /dev/null +++ b/UI/Debugger/Config/DebugInfo.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; +using Mesen.GUI.Debugger; +using Mesen.GUI.Controls; +using Mesen.GUI.Utilities; + +namespace Mesen.GUI.Config +{ + public class DebugInfo + { + public DebuggerShortcutsConfig Shortcuts = new DebuggerShortcutsConfig(); + public TraceLoggerInfo TraceLogger = new TraceLoggerInfo(); + public HexEditorInfo HexEditor = new HexEditorInfo(); + + public bool ShowSelectionLength = false; + + public XmlColor EventViewerBreakpointColor = ColorTranslator.FromHtml("#1898E4"); + public XmlColor CodeOpcodeColor = Color.FromArgb(22, 37, 37); + public XmlColor CodeLabelDefinitionColor = Color.Blue; + public XmlColor CodeImmediateColor = Color.Chocolate; + public XmlColor CodeAddressColor = Color.DarkRed; + public XmlColor CodeCommentColor = Color.Green; + public XmlColor CodeEffectiveAddressColor = Color.SteelBlue; + + + public DebugInfo() + { + } + } + + public enum RefreshSpeed + { + Low = 0, + Normal = 1, + High = 2 + } +} diff --git a/UI/Debugger/Config/DebuggerShortcutsConfig.cs b/UI/Debugger/Config/DebuggerShortcutsConfig.cs new file mode 100644 index 0000000..6279047 --- /dev/null +++ b/UI/Debugger/Config/DebuggerShortcutsConfig.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; +using System.Windows.Forms; +using Mesen.GUI.Forms; +using static Mesen.GUI.Forms.BaseForm; + +namespace Mesen.GUI.Config +{ + public class DebuggerShortcutsConfig + { + //Shared + [ShortcutName("Increase Font Size")] + public XmlKeys IncreaseFontSize = Keys.Control | Keys.Oemplus; + [ShortcutName("Decrease Font Size")] + public XmlKeys DecreaseFontSize = Keys.Control | Keys.OemMinus; + [ShortcutName("Reset Font Size")] + public XmlKeys ResetFontSize = Keys.Control | Keys.D0; + + [ShortcutName("Go To...")] + public XmlKeys GoTo = Keys.Control | Keys.G; + + [ShortcutName("Find")] + public XmlKeys Find = Keys.Control | Keys.F; + [ShortcutName("Find Next")] + public XmlKeys FindNext = Keys.F3; + [ShortcutName("Find Previous")] + public XmlKeys FindPrev = Keys.Shift | Keys.F3; + + [ShortcutName("Undo")] + public XmlKeys Undo = Keys.Control | Keys.Z; + [ShortcutName("Copy")] + public XmlKeys Copy = Keys.Control | Keys.C; + [ShortcutName("Cut")] + public XmlKeys Cut = Keys.Control | Keys.X; + [ShortcutName("Paste")] + public XmlKeys Paste = Keys.Control | Keys.V; + [ShortcutName("Select All")] + public XmlKeys SelectAll = Keys.Control | Keys.A; + + [ShortcutName("Refresh")] + public XmlKeys Refresh = Keys.F5; + + [ShortcutName("Mark Selection as Code")] + public XmlKeys MarkAsCode = Keys.Control | Keys.D1; + [ShortcutName("Mark Selection as Data")] + public XmlKeys MarkAsData = Keys.Control | Keys.D2; + [ShortcutName("Mark Selection as Unidentified Code/Data")] + public XmlKeys MarkAsUnidentified = Keys.Control | Keys.D3; + + [ShortcutName("Go to All")] + public XmlKeys GoToAll = Keys.Control | Keys.Oemcomma; + + [ShortcutName("PPU Viewer: Toggle View")] + public XmlKeys PpuViewer_ToggleView = Keys.Control | Keys.Q; + [ShortcutName("PPU Viewer: Toggle Zoom")] + public XmlKeys PpuViewer_ToggleZoom = Keys.Control | Keys.W; + + [ShortcutName("Edit in Memory Viewer")] + public XmlKeys CodeWindow_EditInMemoryViewer = Keys.F1; + [ShortcutName("View in disassembly")] + public XmlKeys MemoryViewer_ViewInDisassembly = Keys.None; + + [ShortcutName("Open APU Viewer")] + public XmlKeys OpenApuViewer = Keys.Control | Keys.U; + [ShortcutName("Open Assembler")] + public XmlKeys OpenAssembler = Keys.Control | Keys.K; + [ShortcutName("Open Debugger")] + public XmlKeys OpenDebugger = Keys.Control | Keys.D; + [ShortcutName("Open Event Viewer")] + public XmlKeys OpenEventViewer = Keys.Control | Keys.E; + [ShortcutName("Open Memory Tools")] + public XmlKeys OpenMemoryTools = Keys.Control | Keys.M; + [ShortcutName("Open PPU Viewer")] + public XmlKeys OpenPpuViewer = Keys.Control | Keys.P; + [ShortcutName("Open Performance Profiler")] + public XmlKeys OpenProfiler = Keys.Control | Keys.Y; + [ShortcutName("Open Script Window")] + public XmlKeys OpenScriptWindow = Keys.Control | Keys.N; + [ShortcutName("Open Trace Logger")] + public XmlKeys OpenTraceLogger = Keys.Control | Keys.J; + [ShortcutName("Open Text Hooker")] + public XmlKeys OpenTextHooker = Keys.Control | Keys.H; + [ShortcutName("Open Watch Window")] + public XmlKeys OpenWatchWindow = Keys.Control | Keys.W; + + [ShortcutName("Open Nametabler Viewer (Compact)")] + public XmlKeys OpenNametableViewer = Keys.Control | Keys.D1; + [ShortcutName("Open CHR Viewer (Compact)")] + public XmlKeys OpenChrViewer = Keys.Control | Keys.D2; + [ShortcutName("Open Sprite Viewer (Compact)")] + public XmlKeys OpenSpriteViewer = Keys.Control | Keys.D3; + [ShortcutName("Open Palette Viewer (Compact)")] + public XmlKeys OpenPaletteViewer = Keys.Control | Keys.D4; + + //Debugger window + [ShortcutName("Reset")] + public XmlKeys Reset = Keys.Control | Keys.R; + [ShortcutName("Power Cycle")] + public XmlKeys PowerCycle = Keys.Control | Keys.T; + + [ShortcutName("Continue")] + public XmlKeys Continue = Keys.F5; + [ShortcutName("Break")] + public XmlKeys Break = Keys.Control | Keys.Alt | Keys.Cancel; + [ShortcutName("Toggle Break/Continue")] + public XmlKeys ToggleBreakContinue = Keys.Escape; + [ShortcutName("Step Into")] + public XmlKeys StepInto = Keys.F11; + [ShortcutName("Step Over")] + public XmlKeys StepOver = Keys.F10; + [ShortcutName("Step Out")] + public XmlKeys StepOut = Keys.Shift | Keys.F11; + [ShortcutName("Step Back")] + public XmlKeys StepBack = Keys.Shift | Keys.F10; + + [ShortcutName("Run one CPU Cycle")] + public XmlKeys RunCpuCycle = Keys.None; + [ShortcutName("Run one PPU Cycle")] + public XmlKeys RunPpuCycle = Keys.F6; + [ShortcutName("Run one scanline")] + public XmlKeys RunPpuScanline = Keys.F7; + [ShortcutName("Run one frame")] + public XmlKeys RunPpuFrame = Keys.F8; + + [ShortcutName("Break In...")] + public XmlKeys BreakIn = Keys.Control | Keys.B; + [ShortcutName("Break On...")] + public XmlKeys BreakOn = Keys.Alt | Keys.B; + + [ShortcutName("Find Occurrences")] + public XmlKeys FindOccurrences = Keys.Control | Keys.Shift | Keys.F; + [ShortcutName("Go To Program Counter")] + public XmlKeys GoToProgramCounter = Keys.Alt | Keys.Multiply; + + [ShortcutName("Toggle Verified Data Display")] + public XmlKeys ToggleVerifiedData = Keys.Alt | Keys.D1; + [ShortcutName("Toggle Unidentified Code/Data Display")] + public XmlKeys ToggleUnidentifiedCodeData = Keys.Alt | Keys.D2; + + [ShortcutName("Code Window: Set Next Statement")] + public XmlKeys CodeWindow_SetNextStatement = Keys.Control | Keys.Shift | Keys.F10; + [ShortcutName("Code Window: Edit Subroutine")] + public XmlKeys CodeWindow_EditSubroutine = Keys.F4; + [ShortcutName("Code Window: Edit Selected Code")] + public XmlKeys CodeWindow_EditSelectedCode = Keys.None; + [ShortcutName("Code Window: Edit Source File (Source View)")] + public XmlKeys CodeWindow_EditSourceFile = Keys.F4; + [ShortcutName("Code Window: Edit Label")] + public XmlKeys CodeWindow_EditLabel = Keys.F2; + [ShortcutName("Code Window: Navigate Back")] + public XmlKeys CodeWindow_NavigateBack = Keys.Alt | Keys.Left; + [ShortcutName("Code Window: Navigate Forward")] + public XmlKeys CodeWindow_NavigateForward = Keys.Alt | Keys.Right; + [ShortcutName("Code Window: Toggle Breakpoint")] + public XmlKeys CodeWindow_ToggleBreakpoint = Keys.F9; + [ShortcutName("Code Window: Disable/Enable Breakpoint")] + public XmlKeys CodeWindow_DisableEnableBreakpoint = Keys.Control | Keys.F9; + [ShortcutName("Code Window: Switch View (Disassembly / Source View)")] + public XmlKeys CodeWindow_SwitchView = Keys.Control | Keys.Q; + + [ShortcutName("Function List: Edit Label")] + public XmlKeys FunctionList_EditLabel = Keys.F2; + [ShortcutName("Function List: Add Breakpoint")] + public XmlKeys FunctionList_AddBreakpoint = Keys.None; + [ShortcutName("Function List: Find Occurrences")] + public XmlKeys FunctionList_FindOccurrences = Keys.None; + + [ShortcutName("Label List: Add Label")] + public XmlKeys LabelList_Add = Keys.Insert; + [ShortcutName("Label List: Edit Label")] + public XmlKeys LabelList_Edit = Keys.F2; + [ShortcutName("Label List: Delete Label")] + public XmlKeys LabelList_Delete = Keys.Delete; + [ShortcutName("Label List: Add Breakpoint")] + public XmlKeys LabelList_AddBreakpoint = Keys.None; + [ShortcutName("Label List: Add to Watch")] + public XmlKeys LabelList_AddToWatch = Keys.None; + [ShortcutName("Label List: Find Occurrences")] + public XmlKeys LabelList_FindOccurrences = Keys.None; + [ShortcutName("Label List: View in CPU Memory")] + public XmlKeys LabelList_ViewInCpuMemory = Keys.None; + [ShortcutName("Label List: View in [memory type]")] + public XmlKeys LabelList_ViewInMemoryType = Keys.None; + + [ShortcutName("Breakpoint List: Add Breakpoint")] + public XmlKeys BreakpointList_Add = Keys.Insert; + [ShortcutName("Breakpoint List: Edit Breakpoint")] + public XmlKeys BreakpointList_Edit = Keys.F2; + [ShortcutName("Breakpoint List: Go To Location")] + public XmlKeys BreakpointList_GoToLocation = Keys.None; + [ShortcutName("Breakpoint List: Delete Breakpoint")] + public XmlKeys BreakpointList_Delete = Keys.Delete; + + [ShortcutName("Watch List: Delete")] + public XmlKeys WatchList_Delete = Keys.Delete; + [ShortcutName("Watch List: Move Up")] + public XmlKeys WatchList_MoveUp = Keys.Control | Keys.Up; + [ShortcutName("Watch List: Move Down")] + public XmlKeys WatchList_MoveDown = Keys.Control | Keys.Down; + + [ShortcutName("Save Rom")] + public XmlKeys SaveRom = Keys.Control | Keys.S; + [ShortcutName("Save Rom As...")] + public XmlKeys SaveRomAs = Keys.None; + [ShortcutName("Save edits as IPS patch...")] + public XmlKeys SaveEditAsIps = Keys.None; + [ShortcutName("Revert PRG/CHR changes")] + public XmlKeys RevertPrgChrChanges = Keys.None; + + //Memory Tools + [ShortcutName("Freeze")] + public XmlKeys MemoryViewer_Freeze = Keys.Control | Keys.Q; + [ShortcutName("Unfreeze")] + public XmlKeys MemoryViewer_Unfreeze = Keys.Control | Keys.W; + [ShortcutName("Add to Watch")] + public XmlKeys MemoryViewer_AddToWatch = Keys.None; + [ShortcutName("Edit Breakpoint")] + public XmlKeys MemoryViewer_EditBreakpoint = Keys.None; + [ShortcutName("Edit Label")] + public XmlKeys MemoryViewer_EditLabel = Keys.None; + [ShortcutName("Import")] + public XmlKeys MemoryViewer_Import = Keys.Control | Keys.O; + [ShortcutName("Export")] + public XmlKeys MemoryViewer_Export = Keys.Control | Keys.S; + [ShortcutName("View in CPU/PPU Memory")] + public XmlKeys MemoryViewer_ViewInCpuMemory = Keys.None; + [ShortcutName("View in [memory type]")] + public XmlKeys MemoryViewer_ViewInMemoryType = Keys.None; + + //Script Window + [ShortcutName("Open Script")] + public XmlKeys ScriptWindow_OpenScript = Keys.Control | Keys.N; + [ShortcutName("Save Script")] + public XmlKeys ScriptWindow_SaveScript = Keys.Control | Keys.S; + [ShortcutName("Run Script")] + public XmlKeys ScriptWindow_RunScript = Keys.F5; + [ShortcutName("Stop Script")] + public XmlKeys ScriptWindow_StopScript = Keys.Escape; + + public static string GetShortcutDisplay(Keys keys) + { + if(keys == Keys.None) { + return ""; + } else { + string keyString = new KeysConverter().ConvertToString(keys); + return keyString.Replace("+None", "").Replace("Oemcomma", ",").Replace("Oemplus", "+").Replace("Oemtilde", "Tilde").Replace("OemMinus", "-").Replace("Cancel", "Break").Replace("Escape", "Esc"); + } + } + + private static Dictionary, string> _bindings = new Dictionary, string>(); + private static Dictionary, WeakReference> _parents = new Dictionary, WeakReference>(); + public static void RegisterMenuItem(ToolStripMenuItem item, Control parent, string fieldName) + { + var weakRef = new WeakReference(item); + _bindings[weakRef] = fieldName; + _parents[weakRef] = new WeakReference(parent); + + //Remove old references + var dictCopy = new Dictionary, string>(_bindings); + + //Iterate on a copy to avoid "collection was modified" error + foreach(var kvp in dictCopy) { + ToolStripMenuItem menuItem; + if(!kvp.Key.TryGetTarget(out menuItem)) { + _bindings.Remove(kvp.Key); + _parents.Remove(kvp.Key); + } + } + } + + public static void UpdateMenus() + { + foreach(WeakReference itemRef in _bindings.Keys) { + ToolStripMenuItem item; + if(itemRef.TryGetTarget(out item)) { + string fieldName = _bindings[itemRef]; + Control parent; + _parents[itemRef].TryGetTarget(out parent); + if(parent != null) { + UpdateShortcutItem(item, parent, fieldName); + } + } + } + } + + public static void ClearProcessCmdKeyHandler(ToolStripMenuItem item, Control parent) + { + Form parentForm = parent.FindForm(); + if(parentForm is BaseForm) { + (parentForm as BaseForm).OnProcessCmdKey -= ((ShortcutInfo)item.Tag).KeyHandler; + } + ((ShortcutInfo)item.Tag).KeyHandler = null; + } + + public static void UpdateShortcutItem(ToolStripMenuItem item, Control parent, string fieldName) + { + if(item.Tag == null) { + item.Tag = new ShortcutInfo() { KeyHandler = null, ShortcutKey = fieldName }; + } else if(((ShortcutInfo)item.Tag).KeyHandler != null) { + ClearProcessCmdKeyHandler(item, parent); + } + + Keys keys = (XmlKeys)typeof(DebuggerShortcutsConfig).GetField(fieldName).GetValue(ConfigManager.Config.Debug.Shortcuts); + if((keys != Keys.None && !ToolStripManager.IsValidShortcut(keys)) || Program.IsMono) { + //Support normally invalid shortcut keys as a shortcut + item.ShortcutKeys = Keys.None; + item.ShortcutKeyDisplayString = GetShortcutDisplay(keys); + + Form parentForm = parent.FindForm(); + if(parentForm is BaseForm) { + ProcessCmdKeyHandler onProcessCmdKeyHandler = (Keys keyData, ref bool processed) => { + if(!processed && item.Enabled && parent.ContainsFocus && keyData == keys) { + item.PerformClick(); + processed = true; + } + }; + + ((ShortcutInfo)item.Tag).KeyHandler = onProcessCmdKeyHandler; + (parentForm as BaseForm).OnProcessCmdKey += onProcessCmdKeyHandler; + } + } else { + item.ShortcutKeys = keys; + item.ShortcutKeyDisplayString = GetShortcutDisplay(keys); + } + } + } + + public static class ToolStripMenuItemExtensions + { + public static void InitShortcut(this ToolStripMenuItem item, Control parent, string fieldName) + { + DebuggerShortcutsConfig.UpdateShortcutItem(item, parent, fieldName); + DebuggerShortcutsConfig.RegisterMenuItem(item, parent, fieldName); + } + } + + public class ShortcutInfo + { + public string ShortcutKey; + public ProcessCmdKeyHandler KeyHandler; + } + + public class XmlKeys + { + private Keys _keys = Keys.None; + + public XmlKeys() { } + public XmlKeys(Keys k) { _keys = k; } + + public static implicit operator Keys(XmlKeys k) + { + return k._keys; + } + + public static implicit operator XmlKeys(Keys k) + { + return new XmlKeys(k); + } + + [XmlAttribute] + public string Value + { + get { return _keys.ToString(); } + set + { + try { + Enum.TryParse(value, out _keys); + } catch(Exception) { + _keys = Keys.None; + } + } + } + } + + public class ShortcutNameAttribute : Attribute + { + public string Name { get; private set; } + + public ShortcutNameAttribute(string name) + { + this.Name = name; + } + } +} diff --git a/UI/Debugger/Config/HexEditorInfo.cs b/UI/Debugger/Config/HexEditorInfo.cs new file mode 100644 index 0000000..7dfb47e --- /dev/null +++ b/UI/Debugger/Config/HexEditorInfo.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using System.Xml.Serialization; +using Mesen.GUI.Debugger; +using Mesen.GUI.Controls; +using Mesen.GUI.Utilities; + +namespace Mesen.GUI.Config +{ + public class HexEditorInfo + { + public bool HighDensityTextMode = false; + public bool EnablePerByteNavigation = false; + public bool ByteEditingMode = false; + public bool AutoRefresh = true; + public RefreshSpeed AutoRefreshSpeed = RefreshSpeed.Normal; + public bool IgnoreRedundantWrites = false; + public bool HighlightCurrentRowColumn = true; + public int ColumnCount = 2; + + public string FontFamily = BaseControl.MonospaceFontFamily; + public FontStyle FontStyle = FontStyle.Regular; + public float FontSize = BaseControl.DefaultFontSize; + public int TextZoom = 100; + + public bool ShowCharacters = true; + public bool ShowLabelInfo = true; + public bool HighlightExecution = true; + public bool HighlightWrites = true; + public bool HighlightReads = true; + public int FadeSpeed = 300; + public bool HideUnusedBytes = false; + public bool HideReadBytes = false; + public bool HideWrittenBytes = false; + public bool HideExecutedBytes = false; + public bool HighlightBreakpoints = false; + public bool HighlightLabelledBytes = false; + public bool HighlightChrDrawnBytes = false; + public bool HighlightChrReadBytes = false; + public bool HighlightCodeBytes = false; + public bool HighlightDataBytes = false; + public bool HighlightDmcDataBytes = false; + + public XmlColor ReadColor = Color.Blue; + public XmlColor WriteColor = Color.Red; + public XmlColor ExecColor = Color.Green; + public XmlColor LabelledByteColor = Color.LightPink; + public XmlColor CodeByteColor = Color.DarkSeaGreen; + public XmlColor DataByteColor = Color.LightSteelBlue; + + public SnesMemoryType MemoryType = SnesMemoryType.CpuMemory; + + public Size WindowSize = new Size(0, 0); + public Point WindowLocation; + + public HexEditorInfo() + { + } + } +} diff --git a/UI/Config/DebugInfo.cs b/UI/Debugger/Config/TraceLoggerInfo.cs similarity index 55% rename from UI/Config/DebugInfo.cs rename to UI/Debugger/Config/TraceLoggerInfo.cs index e1d3699..7bd02f4 100644 --- a/UI/Config/DebugInfo.cs +++ b/UI/Debugger/Config/TraceLoggerInfo.cs @@ -14,6 +14,36 @@ using Mesen.GUI.Utilities; namespace Mesen.GUI.Config { + public class TraceLoggerInfo + { + public TraceLoggerOptions LogOptions; + public bool AutoRefresh = true; + public int LineCount = 1000; + + public Size WindowSize = new Size(0, 0); + public Point WindowLocation; + public string FontFamily = BaseControl.MonospaceFontFamily; + public FontStyle FontStyle = FontStyle.Regular; + public float FontSize = BaseControl.DefaultFontSize; + public int TextZoom = 100; + + public TraceLoggerInfo() + { + LogOptions = new TraceLoggerOptions() { + ShowByteCode = true, + ShowCpuCycles = true, + ShowEffectiveAddresses = true, + ShowExtraInfo = true, + ShowPpuFrames = false, + ShowPpuCycles = true, + ShowPpuScanline = true, + ShowRegisters = true, + UseLabels = false, + StatusFormat = StatusFlagFormat.Hexadecimal + }; + } + } + public class TraceLoggerOptions { public bool ShowByteCode; @@ -29,53 +59,13 @@ namespace Mesen.GUI.Config public bool UseLabels; public bool ExtendZeroPage; public bool UseWindowsEol = !Program.IsMono; - + public StatusFlagFormat StatusFormat; public bool OverrideFormat; public string Format; } - public class DebugInfo - { - public bool ShowSelectionLength = false; - - public XmlColor EventViewerBreakpointColor = ColorTranslator.FromHtml("#1898E4"); - - public XmlColor AssemblerOpcodeColor = Color.FromArgb(22, 37, 37); - public XmlColor AssemblerLabelDefinitionColor = Color.Blue; - public XmlColor AssemblerImmediateColor = Color.Chocolate; - public XmlColor AssemblerAddressColor = Color.DarkRed; - public XmlColor AssemblerCommentColor = Color.Green; - public XmlColor CodeEffectiveAddressColor = Color.SteelBlue; - - public TraceLoggerOptions TraceLoggerOptions; - public bool TraceAutoRefresh = true; - public int TraceLineCount = 1000; - public Size TraceLoggerSize = new Size(0, 0); - public Point TraceLoggerLocation; - public string TraceFontFamily = BaseControl.MonospaceFontFamily; - public FontStyle TraceFontStyle = FontStyle.Regular; - public float TraceFontSize = BaseControl.DefaultFontSize; - public int TraceTextZoom = 100; - - public DebugInfo() - { - TraceLoggerOptions = new TraceLoggerOptions() { - ShowByteCode = true, - ShowCpuCycles = true, - ShowEffectiveAddresses = true, - ShowExtraInfo = true, - ShowPpuFrames = false, - ShowPpuCycles = true, - ShowPpuScanline = true, - ShowRegisters = true, - UseLabels = false, - StatusFormat = StatusFlagFormat.Hexadecimal - }; - } - } - public enum StatusFlagFormat { Hexadecimal = 0, diff --git a/UI/Debugger/Controls/ComboBoxWithSeparator.cs b/UI/Debugger/Controls/ComboBoxWithSeparator.cs new file mode 100644 index 0000000..2aa755d --- /dev/null +++ b/UI/Debugger/Controls/ComboBoxWithSeparator.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger.Controls +{ + public class ComboBoxWithSeparator : ComboBox + { + private int _previousSelectedIndex = 0; + + public ComboBoxWithSeparator() : base() + { + this.DrawMode = DrawMode.OwnerDrawFixed; + } + + protected override void OnDrawItem(DrawItemEventArgs e) + { + base.OnDrawItem(e); + + if(e.Index >= 0) { + var item = this.Items[e.Index]; + if(item.ToString() == "-") { + e.Graphics.FillRectangle(SystemBrushes.ControlLightLight, e.Bounds); + e.Graphics.DrawLine(Pens.DarkGray, e.Bounds.X + 2, e.Bounds.Y + e.Bounds.Height / 2, e.Bounds.Right - 2, e.Bounds.Y + e.Bounds.Height / 2); + } else { + e.DrawBackground(); + + if(e.State == DrawItemState.Focus) { + e.DrawFocusRectangle(); + } + using(Brush brush = new SolidBrush(e.ForeColor)) { + e.Graphics.DrawString(item.ToString(), e.Font, brush, e.Bounds); + } + } + } else { + e.DrawBackground(); + } + } + + protected override void OnSelectedIndexChanged(EventArgs e) + { + if(this.SelectedItem.ToString() == "-") { + if(_previousSelectedIndex > this.SelectedIndex) { + this.SelectedIndex--; + } else { + this.SelectedIndex++; + } + return; + } + + this._previousSelectedIndex = this.SelectedIndex; + base.OnSelectedIndexChanged(e); + } + } +} diff --git a/UI/Debugger/Controls/ctrlDbgShortcuts.cs b/UI/Debugger/Controls/ctrlDbgShortcuts.cs new file mode 100644 index 0000000..6bf8499 --- /dev/null +++ b/UI/Debugger/Controls/ctrlDbgShortcuts.cs @@ -0,0 +1,68 @@ +using System; +using System.ComponentModel; +using System.Windows.Forms; +using Mesen.GUI.Config; +using System.Reflection; +using Mesen.GUI.Controls; +using System.Collections.Generic; + +namespace Mesen.GUI.Debugger +{ + public partial class ctrlDbgShortcuts : BaseControl + { + private FieldInfo[] _shortcuts; + + public ctrlDbgShortcuts() + { + InitializeComponent(); + } + + public FieldInfo[] Shortcuts + { + set + { + _shortcuts = value; + InitializeGrid(); + } + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + this.colShortcut.Width = (int)(this.Width / 2.1); + this.colKeys.Width = (int)(this.Width / 2.1); + } + + public void InitializeGrid() + { + gridShortcuts.Rows.Clear(); + + foreach(FieldInfo shortcut in _shortcuts) { + int i = gridShortcuts.Rows.Add(); + gridShortcuts.Rows[i].Cells[0].Tag = shortcut; + gridShortcuts.Rows[i].Cells[0].Value = shortcut.GetCustomAttribute()?.Name ?? shortcut.Name; + gridShortcuts.Rows[i].Cells[1].Value = DebuggerShortcutsConfig.GetShortcutDisplay(((XmlKeys)shortcut.GetValue(ConfigManager.Config.Debug.Shortcuts))); + } + } + + private void gridShortcuts_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e) + { + //Right-click on buttons to clear mappings + if(gridShortcuts.Columns[e.ColumnIndex] is DataGridViewButtonColumn && e.RowIndex >= 0) { + DataGridViewButtonCell button = gridShortcuts.Rows[e.RowIndex].Cells[e.ColumnIndex] as DataGridViewButtonCell; + if(button != null) { + if(e.Button == MouseButtons.Right) { + button.Value = ""; + _shortcuts[e.RowIndex].SetValue(ConfigManager.Config.Debug.Shortcuts, (XmlKeys)Keys.None); + } else if(e.Button == MouseButtons.Left) { + using(frmDbgShortcutGetKey frm = new frmDbgShortcutGetKey()) { + frm.ShowDialog(); + button.Value = DebuggerShortcutsConfig.GetShortcutDisplay(frm.ShortcutKeys); + _shortcuts[e.RowIndex].SetValue(ConfigManager.Config.Debug.Shortcuts, (XmlKeys)frm.ShortcutKeys); + } + } + } + } + } + } +} diff --git a/UI/Debugger/Controls/ctrlDbgShortcuts.designer.cs b/UI/Debugger/Controls/ctrlDbgShortcuts.designer.cs new file mode 100644 index 0000000..be6e0cd --- /dev/null +++ b/UI/Debugger/Controls/ctrlDbgShortcuts.designer.cs @@ -0,0 +1,175 @@ +namespace Mesen.GUI.Debugger +{ + partial class ctrlDbgShortcuts + { + /// + /// 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 Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.gridShortcuts = new System.Windows.Forms.DataGridView(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.pnlConflictWarning = new System.Windows.Forms.Panel(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.picWarning = new System.Windows.Forms.PictureBox(); + this.lblShortcutWarning = new System.Windows.Forms.Label(); + this.colShortcut = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.colKeys = new System.Windows.Forms.DataGridViewButtonColumn(); + ((System.ComponentModel.ISupportInitialize)(this.gridShortcuts)).BeginInit(); + this.tableLayoutPanel1.SuspendLayout(); + this.pnlConflictWarning.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picWarning)).BeginInit(); + this.SuspendLayout(); + // + // gridShortcuts + // + this.gridShortcuts.AllowUserToAddRows = false; + this.gridShortcuts.AllowUserToDeleteRows = false; + this.gridShortcuts.AllowUserToResizeColumns = false; + this.gridShortcuts.AllowUserToResizeRows = false; + this.gridShortcuts.BackgroundColor = System.Drawing.SystemColors.ControlLightLight; + this.gridShortcuts.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.gridShortcuts.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.colShortcut, + this.colKeys}); + this.gridShortcuts.Dock = System.Windows.Forms.DockStyle.Fill; + this.gridShortcuts.Location = new System.Drawing.Point(0, 32); + this.gridShortcuts.Margin = new System.Windows.Forms.Padding(0); + this.gridShortcuts.MultiSelect = false; + this.gridShortcuts.Name = "gridShortcuts"; + this.gridShortcuts.RowHeadersVisible = false; + this.gridShortcuts.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.gridShortcuts.Size = new System.Drawing.Size(448, 216); + this.gridShortcuts.TabIndex = 2; + this.gridShortcuts.CellMouseDown += new System.Windows.Forms.DataGridViewCellMouseEventHandler(this.gridShortcuts_CellMouseDown); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.pnlConflictWarning, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.gridShortcuts, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(448, 248); + this.tableLayoutPanel1.TabIndex = 3; + // + // pnlConflictWarning + // + this.pnlConflictWarning.BackColor = System.Drawing.Color.WhiteSmoke; + this.pnlConflictWarning.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pnlConflictWarning.Controls.Add(this.tableLayoutPanel2); + this.pnlConflictWarning.Dock = System.Windows.Forms.DockStyle.Fill; + this.pnlConflictWarning.Location = new System.Drawing.Point(0, 0); + this.pnlConflictWarning.Margin = new System.Windows.Forms.Padding(0, 0, 0, 2); + this.pnlConflictWarning.Name = "pnlConflictWarning"; + this.pnlConflictWarning.Size = new System.Drawing.Size(448, 30); + this.pnlConflictWarning.TabIndex = 20; + this.pnlConflictWarning.Visible = false; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 2; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.picWarning, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.lblShortcutWarning, 1, 0); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(446, 28); + this.tableLayoutPanel2.TabIndex = 0; + // + // picWarning + // + this.picWarning.Anchor = System.Windows.Forms.AnchorStyles.None; + this.picWarning.Image = global::Mesen.GUI.Properties.Resources.Warning; + this.picWarning.Location = new System.Drawing.Point(3, 6); + this.picWarning.Name = "picWarning"; + this.picWarning.Size = new System.Drawing.Size(16, 16); + this.picWarning.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picWarning.TabIndex = 0; + this.picWarning.TabStop = false; + // + // lblShortcutWarning + // + this.lblShortcutWarning.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblShortcutWarning.Location = new System.Drawing.Point(25, 0); + this.lblShortcutWarning.Name = "lblShortcutWarning"; + this.lblShortcutWarning.Size = new System.Drawing.Size(418, 28); + this.lblShortcutWarning.TabIndex = 1; + this.lblShortcutWarning.Text = "Warning: Your current configuration contains conflicting key bindings. If this is" + + " not intentional, please review and correct your key bindings."; + // + // colShortcut + // + this.colShortcut.HeaderText = "Shortcut"; + this.colShortcut.Name = "colShortcut"; + this.colShortcut.ReadOnly = true; + this.colShortcut.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.colShortcut.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.NotSortable; + this.colShortcut.Width = 233; + // + // colKeys + // + this.colKeys.HeaderText = "Keys"; + this.colKeys.Name = "colKeys"; + this.colKeys.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.colKeys.Width = 110; + // + // ctrlDbgShortcuts + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "ctrlDbgShortcuts"; + this.Size = new System.Drawing.Size(448, 248); + ((System.ComponentModel.ISupportInitialize)(this.gridShortcuts)).EndInit(); + this.tableLayoutPanel1.ResumeLayout(false); + this.pnlConflictWarning.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picWarning)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.DataGridView gridShortcuts; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Panel pnlConflictWarning; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.PictureBox picWarning; + private System.Windows.Forms.Label lblShortcutWarning; + private System.Windows.Forms.DataGridViewTextBoxColumn colShortcut; + private System.Windows.Forms.DataGridViewButtonColumn colKeys; + } +} diff --git a/UI/Debugger/Controls/ctrlDbgShortcuts.resx b/UI/Debugger/Controls/ctrlDbgShortcuts.resx new file mode 100644 index 0000000..fe7be02 --- /dev/null +++ b/UI/Debugger/Controls/ctrlDbgShortcuts.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 + + + True + + + True + + \ No newline at end of file diff --git a/UI/Debugger/Controls/ctrlHexViewer.cs b/UI/Debugger/Controls/ctrlHexViewer.cs new file mode 100644 index 0000000..f720eb4 --- /dev/null +++ b/UI/Debugger/Controls/ctrlHexViewer.cs @@ -0,0 +1,457 @@ +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 Mesen.GUI.Config; +using Be.Windows.Forms; +using Mesen.GUI.Controls; +using static Be.Windows.Forms.DynamicByteProvider; +using Mesen.GUI.Forms; + +namespace Mesen.GUI.Debugger.Controls +{ + public partial class ctrlHexViewer : BaseControl + { + private FindOptions _findOptions; + private StaticByteProvider _byteProvider; + private SnesMemoryType _memoryType; + + public ctrlHexViewer() + { + InitializeComponent(); + + this.BaseFont = new Font(BaseControl.MonospaceFontFamily, 10, FontStyle.Regular); + this.ctrlHexBox.ContextMenuStrip = this.ctxMenuStrip; + this.ctrlHexBox.SelectionForeColor = Color.White; + this.ctrlHexBox.SelectionBackColor = Color.FromArgb(31, 123, 205); + this.ctrlHexBox.ShadowSelectionColor = Color.FromArgb(100, 60, 128, 200); + this.ctrlHexBox.InfoBackColor = Color.FromArgb(235, 235, 235); + this.ctrlHexBox.InfoForeColor = Color.Gray; + this.ctrlHexBox.HighlightCurrentRowColumn = true; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + if(!IsDesignMode) { + this.cboNumberColumns.SelectedIndex = ConfigManager.Config.Debug.HexEditor.ColumnCount; + } + } + + public byte[] GetData() + { + return this._byteProvider != null ? this._byteProvider.Bytes.ToArray() : new byte[0]; + } + + public void RefreshData(SnesMemoryType memoryType) + { + if(_memoryType != memoryType) { + _memoryType = memoryType; + _byteProvider = null; + } + + byte[] data = DebugApi.GetMemoryState(this._memoryType); + + if(data != null) { + bool changed = true; + if(this._byteProvider != null && data.Length == this._byteProvider.Bytes.Count) { + changed = false; + for(int i = 0; i < this._byteProvider.Bytes.Count; i++) { + if(this._byteProvider.Bytes[i] != data[i]) { + changed = true; + break; + } + } + } + + if(changed) { + if(_byteProvider == null || _byteProvider.Length != data.Length) { + _byteProvider = new StaticByteProvider(data); + _byteProvider.ByteChanged += (int byteIndex, byte newValue, byte oldValue) => { + DebugApi.SetMemoryValue(_memoryType, (UInt32)byteIndex, newValue); + }; + _byteProvider.BytesChanged += (int byteIndex, byte[] values) => { + DebugApi.SetMemoryValues(_memoryType, (UInt32)byteIndex, values, values.Length); + }; + this.ctrlHexBox.ByteProvider = _byteProvider; + } else { + _byteProvider.SetData(data); + } + this.ctrlHexBox.Refresh(); + } + } + } + + private int ColumnCount + { + get { return Int32.Parse(this.cboNumberColumns.Text); } + } + + public int RequiredWidth + { + get { return this.ctrlHexBox.RequiredWidth; } + } + + private void cboNumberColumns_SelectedIndexChanged(object sender, EventArgs e) + { + this.ctrlHexBox.Focus(); + + this.ctrlHexBox.BytesPerLine = this.ColumnCount; + this.ctrlHexBox.UseFixedBytesPerLine = true; + + ConfigManager.Config.Debug.HexEditor.ColumnCount = this.cboNumberColumns.SelectedIndex; + ConfigManager.ApplyChanges(); + } + + public Font HexFont + { + get { return this.ctrlHexBox.Font; } + } + + private int _textZoom = 100; + public int TextZoom + { + get { return _textZoom; } + set + { + if(value >= 30 && value <= 500) { + _textZoom = value; + this.UpdateFont(); + } + } + } + + private Font _baseFont = new Font(BaseControl.MonospaceFontFamily, BaseControl.DefaultFontSize, FontStyle.Regular); + public Font BaseFont { + get { return _baseFont; } + set + { + if(!value.Equals(_baseFont)) { + _baseFont = value; + this.UpdateFont(); + } + } + } + + public void UpdateFont() + { + this.ctrlHexBox.Font = new Font(BaseFont.FontFamily, BaseFont.Size * _textZoom / 100f, BaseFont.Style); + } + + public void GoToAddress(int address) + { + this.ctrlHexBox.ScrollByteIntoView(GetData().Length - 1); + this.ctrlHexBox.ScrollByteIntoView(address); + this.ctrlHexBox.Select(address, 0); + this.ctrlHexBox.Focus(); + } + + public void GoToAddress() + { + GoToAddress address = new GoToAddress(); + + int currentAddr = (int)(this.ctrlHexBox.CurrentLine - 1) * this.ctrlHexBox.BytesPerLine; + address.Address = (UInt32)currentAddr; + + using(frmGoToLine frm = new frmGoToLine(address, (_byteProvider.Length - 1).ToString("X").Length)) { + frm.StartPosition = FormStartPosition.Manual; + Point topLeft = this.PointToScreen(new Point(0, 0)); + frm.Location = new Point(topLeft.X + (this.Width - frm.Width) / 2, topLeft.Y + (this.Height - frm.Height) / 2); + if(frm.ShowDialog() == DialogResult.OK) { + GoToAddress((int)address.Address); + } + } + } + + public void OpenSearchBox(bool forceFocus = false) + { + this._findOptions = new Be.Windows.Forms.FindOptions(); + this._findOptions.Type = chkTextSearch.Checked ? FindType.Text : FindType.Hex; + this._findOptions.MatchCase = false; + this._findOptions.Text = this.cboSearch.Text; + this._findOptions.WrapSearch = true; + + bool focus = !this.panelSearch.Visible; + this.panelSearch.Visible = true; + + if(Program.IsMono) { + //Mono doesn't resize the TLP properly for some reason when set to autosize + this.tlpMain.RowStyles[2].SizeType = System.Windows.Forms.SizeType.Absolute; + this.tlpMain.RowStyles[2].Height = 30; + } + if(focus || forceFocus) { + this.cboSearch.Focus(); + this.cboSearch.SelectAll(); + } + } + + private void CloseSearchBox() + { + this.panelSearch.Visible = false; + if(Program.IsMono) { + //Mono doesn't resize the TLP properly for some reason when set to autosize + this.tlpMain.RowStyles[2].SizeType = System.Windows.Forms.SizeType.Absolute; + this.tlpMain.RowStyles[2].Height = 0; + } + this.Focus(); + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if(keyData == ConfigManager.Config.Debug.Shortcuts.Find) { + this.OpenSearchBox(true); + return true; + } else if(keyData == ConfigManager.Config.Debug.Shortcuts.IncreaseFontSize) { + this.TextZoom += 10; + return true; + } else if(keyData == ConfigManager.Config.Debug.Shortcuts.DecreaseFontSize) { + this.TextZoom -= 10; + return true; + } else if(keyData == ConfigManager.Config.Debug.Shortcuts.ResetFontSize) { + this.TextZoom = 100; + return true; + } + + if(this.cboSearch.Focused) { + if(keyData == Keys.Escape) { + this.CloseSearchBox(); + return true; + } + } + + return base.ProcessCmdKey(ref msg, keyData); + } + + public void FindNext() + { + this.OpenSearchBox(); + if(this.UpdateSearchOptions()) { + if(this.ctrlHexBox.Find(this._findOptions, HexBox.eSearchDirection.Next) == -1) { + this.lblSearchWarning.Text = "No matches found!"; + } + } + } + + public void FindPrevious() + { + this.OpenSearchBox(); + if(this.UpdateSearchOptions()) { + if(this.ctrlHexBox.Find(this._findOptions, HexBox.eSearchDirection.Previous) == -1) { + this.lblSearchWarning.Text = "No matches found!"; + } + } + } + + private void picCloseSearch_Click(object sender, EventArgs e) + { + this.CloseSearchBox(); + } + + private void picSearchPrevious_MouseUp(object sender, MouseEventArgs e) + { + this.FindPrevious(); + } + + private void picSearchNext_MouseUp(object sender, MouseEventArgs e) + { + this.FindNext(); + } + + private byte[] GetByteArray(string hexText, ref bool hasWildcard) + { + hexText = hexText.Replace(" ", ""); + + try { + List bytes = new List(hexText.Length/2); + for(int i = 0; i < hexText.Length; i+=2) { + if(i == hexText.Length - 1) { + bytes.Add((byte)(Convert.ToByte(hexText.Substring(i, 1), 16) << 4)); + hasWildcard = true; + } else { + bytes.Add(Convert.ToByte(hexText.Substring(i, 2), 16)); + } + } + return bytes.ToArray(); + } catch { + return new byte[0]; + } + } + + private bool UpdateSearchOptions() + { + bool invalidSearchString = false; + + this._findOptions.MatchCase = this.chkMatchCase.Checked; + + if(this.chkTextSearch.Checked) { + this._findOptions.Type = FindType.Text; + this._findOptions.Text = this.cboSearch.Text; + this._findOptions.HasWildcard = false; + } else { + this._findOptions.Type = FindType.Hex; + bool hasWildcard = false; + this._findOptions.Hex = this.GetByteArray(this.cboSearch.Text, ref hasWildcard); + this._findOptions.HasWildcard = hasWildcard; + invalidSearchString = this._findOptions.Hex.Length == 0 && this.cboSearch.Text.Trim().Length > 0; + } + + this.lblSearchWarning.Text = ""; + + bool emptySearch = this._findOptions.Text.Length == 0 || (!this.chkTextSearch.Checked && (this._findOptions.Hex == null || this._findOptions.Hex.Length == 0)); + if(invalidSearchString) { + this.lblSearchWarning.Text = "Invalid search string"; + } else if(!emptySearch) { + return true; + } + return false; + } + + private void cboSearch_TextUpdate(object sender, EventArgs e) + { + if(this.UpdateSearchOptions()) { + if(this.ctrlHexBox.Find(this._findOptions, HexBox.eSearchDirection.Incremental) == -1) { + this.lblSearchWarning.Text = "No matches found!"; + } + } + } + + private void cboSearch_KeyDown(object sender, KeyEventArgs e) + { + if(e.KeyCode == Keys.Enter) { + this.FindNext(); + if(this.cboSearch.Items.Contains(this.cboSearch.Text)) { + this.cboSearch.Items.Remove(this.cboSearch.Text); + } + this.cboSearch.Items.Insert(0, this.cboSearch.Text); + + e.Handled = true; + e.SuppressKeyPress = true; + } + } + + private void chkTextSearch_CheckedChanged(object sender, EventArgs e) + { + this.UpdateSearchOptions(); + } + + public event EventHandler RequiredWidthChanged + { + add { this.ctrlHexBox.RequiredWidthChanged += value; } + remove { this.ctrlHexBox.RequiredWidthChanged -= value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IByteCharConverter ByteCharConverter + { + get { return this.ctrlHexBox.ByteCharConverter; } + set { this.ctrlHexBox.ByteCharConverter = value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IByteColorProvider ByteColorProvider + { + get { return this.ctrlHexBox.ByteColorProvider; } + set { this.ctrlHexBox.ByteColorProvider = value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool StringViewVisible + { + get { return this.ctrlHexBox.StringViewVisible; } + set { this.ctrlHexBox.StringViewVisible = value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool ReadOnly + { + get { return this.ctrlHexBox.ReadOnly; } + set { this.ctrlHexBox.ReadOnly = value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool HighDensityMode + { + get { return this.ctrlHexBox.HighDensityMode; } + set { this.ctrlHexBox.HighDensityMode = value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool EnablePerByteNavigation + { + get { return this.ctrlHexBox.EnablePerByteNavigation; } + set { this.ctrlHexBox.EnablePerByteNavigation = value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool ByteEditingMode + { + get { return this.ctrlHexBox.ByteEditingMode; } + set { this.ctrlHexBox.ByteEditingMode = value; } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool HighlightCurrentRowColumn + { + get { return this.ctrlHexBox.HighlightCurrentRowColumn; } + set { this.ctrlHexBox.HighlightCurrentRowColumn = value; } + } + + public delegate void ByteMouseHoverHandler(int address, Point position); + public event ByteMouseHoverHandler ByteMouseHover; + private void ctrlHexBox_MouseMove(object sender, MouseEventArgs e) + { + BytePositionInfo? bpi = ctrlHexBox.GetRestrictedHexBytePositionInfo(e.Location); + if(bpi.HasValue) { + Point position = ctrlHexBox.GetBytePosition(bpi.Value.Index); + ByteMouseHover?.Invoke((int)bpi.Value.Index, new Point(position.X + (int)(ctrlHexBox.CharSize.Width * 2.5), position.Y + (int)(ctrlHexBox.CharSize.Height * 1.1))); + } else { + ByteMouseHover?.Invoke(-1, Point.Empty); + } + } + + private void ctrlHexBox_MouseLeave(object sender, EventArgs e) + { + ByteMouseHover?.Invoke(-1, Point.Empty); + } + + private void UpdateLocationLabel() + { + if(ctrlHexBox.SelectionLength > 0) { + this.lblLocation.Text = $"Selection: ${ctrlHexBox.SelectionStart.ToString("X4")} - ${(ctrlHexBox.SelectionStart + ctrlHexBox.SelectionLength - 1).ToString("X4")} ({ctrlHexBox.SelectionLength} bytes)"; + } else { + this.lblLocation.Text = $"Location: ${ctrlHexBox.SelectionStart.ToString("X4")}"; + } + } + + private void ctrlHexBox_SelectionStartChanged(object sender, EventArgs e) + { + UpdateLocationLabel(); + } + + private void ctrlHexBox_SelectionLengthChanged(object sender, EventArgs e) + { + UpdateLocationLabel(); + } + + private void mnuCopy_Click(object sender, EventArgs e) + { + ctrlHexBox.CopyHex(); + } + + private void mnuPaste_Click(object sender, EventArgs e) + { + ctrlHexBox.Paste(); + } + + private void mnuSelectAll_Click(object sender, EventArgs e) + { + ctrlHexBox.SelectAll(); + } + } +} diff --git a/UI/Debugger/Controls/ctrlHexViewer.designer.cs b/UI/Debugger/Controls/ctrlHexViewer.designer.cs new file mode 100644 index 0000000..7035531 --- /dev/null +++ b/UI/Debugger/Controls/ctrlHexViewer.designer.cs @@ -0,0 +1,389 @@ +namespace Mesen.GUI.Debugger.Controls +{ + partial class ctrlHexViewer + { + /// + /// 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 Component 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.tlpMain = new System.Windows.Forms.TableLayoutPanel(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblNumberOfColumns = new System.Windows.Forms.Label(); + this.cboNumberColumns = new System.Windows.Forms.ComboBox(); + this.panelSearch = new System.Windows.Forms.Panel(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.picCloseSearch = new System.Windows.Forms.PictureBox(); + this.picSearchNext = new System.Windows.Forms.PictureBox(); + this.picSearchPrevious = new System.Windows.Forms.PictureBox(); + this.cboSearch = new System.Windows.Forms.ComboBox(); + this.lblSearchWarning = new System.Windows.Forms.Label(); + this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel(); + this.chkTextSearch = new System.Windows.Forms.CheckBox(); + this.chkMatchCase = new System.Windows.Forms.CheckBox(); + this.ctrlHexBox = new Be.Windows.Forms.HexBox(); + this.statusStrip = new System.Windows.Forms.StatusStrip(); + this.lblLocation = new System.Windows.Forms.ToolStripStatusLabel(); + this.ctxMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuCopy = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuPaste = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuSelectAll = new System.Windows.Forms.ToolStripMenuItem(); + this.tlpMain.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.panelSearch.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picCloseSearch)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchNext)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchPrevious)).BeginInit(); + this.flowLayoutPanel2.SuspendLayout(); + this.statusStrip.SuspendLayout(); + this.ctxMenuStrip.SuspendLayout(); + this.SuspendLayout(); + // + // tlpMain + // + this.tlpMain.ColumnCount = 1; + this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpMain.Controls.Add(this.flowLayoutPanel1, 0, 0); + this.tlpMain.Controls.Add(this.panelSearch, 0, 2); + this.tlpMain.Controls.Add(this.ctrlHexBox, 0, 1); + this.tlpMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpMain.Location = new System.Drawing.Point(0, 0); + this.tlpMain.Margin = new System.Windows.Forms.Padding(0); + this.tlpMain.Name = "tlpMain"; + this.tlpMain.RowCount = 3; + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tlpMain.Size = new System.Drawing.Size(543, 287); + this.tlpMain.TabIndex = 0; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.flowLayoutPanel1.AutoSize = true; + this.flowLayoutPanel1.Controls.Add(this.lblNumberOfColumns); + this.flowLayoutPanel1.Controls.Add(this.cboNumberColumns); + this.flowLayoutPanel1.Location = new System.Drawing.Point(379, 0); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(164, 27); + this.flowLayoutPanel1.TabIndex = 1; + // + // lblNumberOfColumns + // + this.lblNumberOfColumns.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblNumberOfColumns.AutoSize = true; + this.lblNumberOfColumns.Location = new System.Drawing.Point(3, 7); + this.lblNumberOfColumns.Name = "lblNumberOfColumns"; + this.lblNumberOfColumns.Size = new System.Drawing.Size(102, 13); + this.lblNumberOfColumns.TabIndex = 0; + this.lblNumberOfColumns.Text = "Number of Columns:"; + // + // cboNumberColumns + // + this.cboNumberColumns.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboNumberColumns.FormattingEnabled = true; + this.cboNumberColumns.Items.AddRange(new object[] { + "4", + "8", + "16", + "32", + "64"}); + this.cboNumberColumns.Location = new System.Drawing.Point(111, 3); + this.cboNumberColumns.Name = "cboNumberColumns"; + this.cboNumberColumns.Size = new System.Drawing.Size(50, 21); + this.cboNumberColumns.TabIndex = 1; + this.cboNumberColumns.SelectedIndexChanged += new System.EventHandler(this.cboNumberColumns_SelectedIndexChanged); + // + // panelSearch + // + this.panelSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelSearch.Controls.Add(this.tableLayoutPanel2); + this.panelSearch.Dock = System.Windows.Forms.DockStyle.Fill; + this.panelSearch.Location = new System.Drawing.Point(3, 259); + this.panelSearch.Margin = new System.Windows.Forms.Padding(3, 0, 3, 0); + this.panelSearch.Name = "panelSearch"; + this.panelSearch.Size = new System.Drawing.Size(537, 28); + this.panelSearch.TabIndex = 3; + this.panelSearch.Visible = false; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 6; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 250F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Controls.Add(this.picCloseSearch, 3, 0); + this.tableLayoutPanel2.Controls.Add(this.picSearchNext, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.picSearchPrevious, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.cboSearch, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.lblSearchWarning, 4, 0); + this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel2, 5, 0); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 2; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.Size = new System.Drawing.Size(535, 26); + this.tableLayoutPanel2.TabIndex = 0; + // + // picCloseSearch + // + this.picCloseSearch.Anchor = System.Windows.Forms.AnchorStyles.None; + this.picCloseSearch.Cursor = System.Windows.Forms.Cursors.Hand; + this.picCloseSearch.Image = global::Mesen.GUI.Properties.Resources.Close; + this.picCloseSearch.Location = new System.Drawing.Point(297, 5); + this.picCloseSearch.Name = "picCloseSearch"; + this.picCloseSearch.Size = new System.Drawing.Size(16, 16); + this.picCloseSearch.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picCloseSearch.TabIndex = 3; + this.picCloseSearch.TabStop = false; + this.picCloseSearch.Click += new System.EventHandler(this.picCloseSearch_Click); + // + // picSearchNext + // + this.picSearchNext.Anchor = System.Windows.Forms.AnchorStyles.None; + this.picSearchNext.Cursor = System.Windows.Forms.Cursors.Hand; + this.picSearchNext.Image = global::Mesen.GUI.Properties.Resources.NextArrow; + this.picSearchNext.Location = new System.Drawing.Point(275, 5); + this.picSearchNext.Name = "picSearchNext"; + this.picSearchNext.Size = new System.Drawing.Size(16, 16); + this.picSearchNext.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picSearchNext.TabIndex = 2; + this.picSearchNext.TabStop = false; + this.picSearchNext.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picSearchNext_MouseUp); + // + // picSearchPrevious + // + this.picSearchPrevious.Anchor = System.Windows.Forms.AnchorStyles.None; + this.picSearchPrevious.Cursor = System.Windows.Forms.Cursors.Hand; + this.picSearchPrevious.Image = global::Mesen.GUI.Properties.Resources.PreviousArrow; + this.picSearchPrevious.Location = new System.Drawing.Point(253, 5); + this.picSearchPrevious.Name = "picSearchPrevious"; + this.picSearchPrevious.Size = new System.Drawing.Size(16, 16); + this.picSearchPrevious.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.picSearchPrevious.TabIndex = 1; + this.picSearchPrevious.TabStop = false; + this.picSearchPrevious.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picSearchPrevious_MouseUp); + // + // cboSearch + // + this.cboSearch.Dock = System.Windows.Forms.DockStyle.Fill; + this.cboSearch.FormattingEnabled = true; + this.cboSearch.Location = new System.Drawing.Point(3, 3); + this.cboSearch.Name = "cboSearch"; + this.cboSearch.Size = new System.Drawing.Size(244, 21); + this.cboSearch.TabIndex = 4; + this.cboSearch.TextUpdate += new System.EventHandler(this.cboSearch_TextUpdate); + this.cboSearch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.cboSearch_KeyDown); + // + // lblSearchWarning + // + this.lblSearchWarning.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblSearchWarning.AutoSize = true; + this.lblSearchWarning.ForeColor = System.Drawing.Color.Red; + this.lblSearchWarning.Location = new System.Drawing.Point(319, 7); + this.lblSearchWarning.Name = "lblSearchWarning"; + this.lblSearchWarning.Size = new System.Drawing.Size(0, 13); + this.lblSearchWarning.TabIndex = 6; + // + // flowLayoutPanel2 + // + this.flowLayoutPanel2.Controls.Add(this.chkTextSearch); + this.flowLayoutPanel2.Controls.Add(this.chkMatchCase); + this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutPanel2.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.flowLayoutPanel2.Location = new System.Drawing.Point(322, 2); + this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0, 2, 0, 0); + this.flowLayoutPanel2.Name = "flowLayoutPanel2"; + this.flowLayoutPanel2.Size = new System.Drawing.Size(213, 25); + this.flowLayoutPanel2.TabIndex = 7; + // + // chkTextSearch + // + this.chkTextSearch.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.chkTextSearch.AutoSize = true; + this.chkTextSearch.Location = new System.Drawing.Point(128, 3); + this.chkTextSearch.Name = "chkTextSearch"; + this.chkTextSearch.Size = new System.Drawing.Size(82, 17); + this.chkTextSearch.TabIndex = 5; + this.chkTextSearch.Text = "Text search"; + this.chkTextSearch.UseVisualStyleBackColor = true; + this.chkTextSearch.CheckedChanged += new System.EventHandler(this.chkTextSearch_CheckedChanged); + // + // chkMatchCase + // + this.chkMatchCase.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.chkMatchCase.AutoSize = true; + this.chkMatchCase.Location = new System.Drawing.Point(39, 3); + this.chkMatchCase.Name = "chkMatchCase"; + this.chkMatchCase.Size = new System.Drawing.Size(83, 17); + this.chkMatchCase.TabIndex = 6; + this.chkMatchCase.Text = "Match Case"; + this.chkMatchCase.UseVisualStyleBackColor = true; + // + // ctrlHexBox + // + this.ctrlHexBox.ByteColorProvider = null; + this.ctrlHexBox.ByteEditingMode = false; + this.ctrlHexBox.ColumnInfoVisible = true; + this.ctrlHexBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlHexBox.EnablePerByteNavigation = false; + this.ctrlHexBox.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ctrlHexBox.HighDensityMode = false; + this.ctrlHexBox.InfoBackColor = System.Drawing.Color.DarkGray; + this.ctrlHexBox.LineInfoVisible = true; + this.ctrlHexBox.Location = new System.Drawing.Point(0, 27); + this.ctrlHexBox.Margin = new System.Windows.Forms.Padding(0); + this.ctrlHexBox.Name = "ctrlHexBox"; + this.ctrlHexBox.SelectionBackColor = System.Drawing.Color.RoyalBlue; + this.ctrlHexBox.ShadowSelectionColor = System.Drawing.Color.Orange; + this.ctrlHexBox.Size = new System.Drawing.Size(543, 232); + this.ctrlHexBox.StringViewVisible = true; + this.ctrlHexBox.TabIndex = 2; + this.ctrlHexBox.UseFixedBytesPerLine = true; + this.ctrlHexBox.VScrollBarVisible = true; + this.ctrlHexBox.SelectionStartChanged += new System.EventHandler(this.ctrlHexBox_SelectionStartChanged); + this.ctrlHexBox.SelectionLengthChanged += new System.EventHandler(this.ctrlHexBox_SelectionLengthChanged); + this.ctrlHexBox.MouseLeave += new System.EventHandler(this.ctrlHexBox_MouseLeave); + this.ctrlHexBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlHexBox_MouseMove); + // + // statusStrip + // + this.statusStrip.BackColor = System.Drawing.SystemColors.ControlLightLight; + this.statusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.lblLocation}); + this.statusStrip.Location = new System.Drawing.Point(0, 287); + this.statusStrip.Name = "statusStrip"; + this.statusStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional; + this.statusStrip.Size = new System.Drawing.Size(543, 22); + this.statusStrip.SizingGrip = false; + this.statusStrip.TabIndex = 1; + // + // lblLocation + // + this.lblLocation.Name = "lblLocation"; + this.lblLocation.Size = new System.Drawing.Size(0, 17); + // + // ctxMenuStrip + // + this.ctxMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuCopy, + this.mnuPaste, + this.toolStripMenuItem5, + this.mnuSelectAll}); + this.ctxMenuStrip.Name = "ctxMenuStrip"; + this.ctxMenuStrip.Size = new System.Drawing.Size(153, 98); + // + // mnuCopy + // + this.mnuCopy.Image = global::Mesen.GUI.Properties.Resources.Copy; + this.mnuCopy.Name = "mnuCopy"; + this.mnuCopy.Size = new System.Drawing.Size(152, 22); + this.mnuCopy.Text = "Copy"; + this.mnuCopy.Click += new System.EventHandler(this.mnuCopy_Click); + // + // mnuPaste + // + this.mnuPaste.Image = global::Mesen.GUI.Properties.Resources.Paste; + this.mnuPaste.Name = "mnuPaste"; + this.mnuPaste.Size = new System.Drawing.Size(152, 22); + this.mnuPaste.Text = "Paste"; + this.mnuPaste.Click += new System.EventHandler(this.mnuPaste_Click); + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(149, 6); + // + // mnuSelectAll + // + this.mnuSelectAll.Image = global::Mesen.GUI.Properties.Resources.SelectAll; + this.mnuSelectAll.Name = "mnuSelectAll"; + this.mnuSelectAll.Size = new System.Drawing.Size(152, 22); + this.mnuSelectAll.Text = "Select All"; + this.mnuSelectAll.Click += new System.EventHandler(this.mnuSelectAll_Click); + // + // ctrlHexViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tlpMain); + this.Controls.Add(this.statusStrip); + this.Margin = new System.Windows.Forms.Padding(0); + this.Name = "ctrlHexViewer"; + this.Size = new System.Drawing.Size(543, 309); + this.tlpMain.ResumeLayout(false); + this.tlpMain.PerformLayout(); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.PerformLayout(); + this.panelSearch.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picCloseSearch)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchNext)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchPrevious)).EndInit(); + this.flowLayoutPanel2.ResumeLayout(false); + this.flowLayoutPanel2.PerformLayout(); + this.statusStrip.ResumeLayout(false); + this.statusStrip.PerformLayout(); + this.ctxMenuStrip.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tlpMain; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label lblNumberOfColumns; + private System.Windows.Forms.ComboBox cboNumberColumns; + private Be.Windows.Forms.HexBox ctrlHexBox; + private System.Windows.Forms.Panel panelSearch; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.PictureBox picCloseSearch; + private System.Windows.Forms.PictureBox picSearchNext; + private System.Windows.Forms.PictureBox picSearchPrevious; + private System.Windows.Forms.ComboBox cboSearch; + private System.Windows.Forms.CheckBox chkTextSearch; + private System.Windows.Forms.Label lblSearchWarning; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2; + private System.Windows.Forms.CheckBox chkMatchCase; + private System.Windows.Forms.StatusStrip statusStrip; + private System.Windows.Forms.ToolStripStatusLabel lblLocation; + private System.Windows.Forms.ContextMenuStrip ctxMenuStrip; + private System.Windows.Forms.ToolStripMenuItem mnuCopy; + private System.Windows.Forms.ToolStripMenuItem mnuPaste; + private System.Windows.Forms.ToolStripMenuItem mnuSelectAll; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5; + } +} diff --git a/UI/Debugger/Controls/ctrlHexViewer.resx b/UI/Debugger/Controls/ctrlHexViewer.resx new file mode 100644 index 0000000..26cc44c --- /dev/null +++ b/UI/Debugger/Controls/ctrlHexViewer.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 + + + 107, 17 + + + 216, 17 + + \ No newline at end of file diff --git a/UI/Debugger/Controls/ctrlTextbox.cs b/UI/Debugger/Controls/ctrlTextbox.cs index 8a99f47..b214ba6 100644 --- a/UI/Debugger/Controls/ctrlTextbox.cs +++ b/UI/Debugger/Controls/ctrlTextbox.cs @@ -1050,8 +1050,8 @@ namespace Mesen.GUI.Debugger.Controls string indirect = match.Groups[14].Value; string paren3 = match.Groups[15].Value; string rest = match.Groups[16].Value; - Color operandColor = operand.Length > 0 ? (operand[0] == '#' ? (Color)info.AssemblerImmediateColor : (operand[0] == '$' ? (Color)info.AssemblerAddressColor : (Color)info.AssemblerLabelDefinitionColor)) : Color.Black; - List colors = new List() { defaultColor, info.AssemblerOpcodeColor, defaultColor, defaultColor, defaultColor, operandColor, defaultColor, defaultColor, defaultColor }; + Color operandColor = operand.Length > 0 ? (operand[0] == '#' ? (Color)info.CodeImmediateColor : (operand[0] == '$' ? (Color)info.CodeAddressColor : (Color)info.CodeLabelDefinitionColor)) : Color.Black; + List colors = new List() { defaultColor, info.CodeOpcodeColor, defaultColor, defaultColor, defaultColor, operandColor, defaultColor, defaultColor, defaultColor }; int codePartCount = colors.Count; List parts = new List() { padding, opcode, invalidStar, " ", paren1, operand, paren2, indirect, paren3 }; @@ -1110,7 +1110,7 @@ namespace Mesen.GUI.Debugger.Controls } codeStringLength = xOffset; } else { - using(Brush fgBrush = new SolidBrush(codeString.EndsWith(":") ? (Color)info.AssemblerLabelDefinitionColor : (textColor ?? defaultColor))) { + using(Brush fgBrush = new SolidBrush(codeString.EndsWith(":") ? (Color)info.CodeLabelDefinitionColor : (textColor ?? defaultColor))) { g.DrawString(codeString, this.Font, fgBrush, marginLeft, positionY, StringFormat.GenericTypographic); } characterCount = codeString.Trim().Length; @@ -1118,7 +1118,7 @@ namespace Mesen.GUI.Debugger.Controls } if(!string.IsNullOrWhiteSpace(commentString)) { - using(Brush commentBrush = new SolidBrush(info.AssemblerCommentColor)) { + using(Brush commentBrush = new SolidBrush(info.CodeCommentColor)) { int padding = Math.Max(CommentSpacingCharCount, characterCount + 1); if(characterCount == 0) { //Draw comment left-aligned, next to the margin when there is no code on the line diff --git a/UI/Debugger/HexBox/BuiltInContextMenu.cs b/UI/Debugger/HexBox/BuiltInContextMenu.cs new file mode 100644 index 0000000..429b3e2 --- /dev/null +++ b/UI/Debugger/HexBox/BuiltInContextMenu.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; +using System.ComponentModel; +using System.Windows.Forms; + +namespace Be.Windows.Forms +{ + /// + /// Defines a build-in ContextMenuStrip manager for HexBox control to show Copy, Cut, Paste menu in contextmenu of the control. + /// + [TypeConverterAttribute(typeof(ExpandableObjectConverter))] + public sealed class BuiltInContextMenu : Component + { + /// + /// Contains the HexBox control. + /// + HexBox _hexBox; + /// + /// Contains the ContextMenuStrip control. + /// + ContextMenuStrip _contextMenuStrip; + /// + /// Contains the "Cut"-ToolStripMenuItem object. + /// + ToolStripMenuItem _cutToolStripMenuItem; + /// + /// Contains the "Copy"-ToolStripMenuItem object. + /// + ToolStripMenuItem _copyToolStripMenuItem; + /// + /// Contains the "Paste"-ToolStripMenuItem object. + /// + ToolStripMenuItem _pasteToolStripMenuItem; + /// + /// Contains the "Select All"-ToolStripMenuItem object. + /// + ToolStripMenuItem _selectAllToolStripMenuItem; + /// + /// Initializes a new instance of BuildInContextMenu class. + /// + /// the HexBox control + internal BuiltInContextMenu(HexBox hexBox) + { + _hexBox = hexBox; + _hexBox.ByteProviderChanged += new EventHandler(HexBox_ByteProviderChanged); + } + /// + /// If ByteProvider + /// + /// the sender object + /// the event data + void HexBox_ByteProviderChanged(object sender, EventArgs e) + { + CheckBuiltInContextMenu(); + } + /// + /// Assigns the ContextMenuStrip control to the HexBox control. + /// + void CheckBuiltInContextMenu() + { + if (Util.DesignMode) + return; + + if (this._contextMenuStrip == null) + { + ContextMenuStrip cms = new ContextMenuStrip(); + _cutToolStripMenuItem = new ToolStripMenuItem(CutMenuItemTextInternal, CutMenuItemImage, new EventHandler(CutMenuItem_Click)); + cms.Items.Add(_cutToolStripMenuItem); + _copyToolStripMenuItem = new ToolStripMenuItem(CopyMenuItemTextInternal, CopyMenuItemImage, new EventHandler(CopyMenuItem_Click)); + cms.Items.Add(_copyToolStripMenuItem); + _pasteToolStripMenuItem = new ToolStripMenuItem(PasteMenuItemTextInternal, PasteMenuItemImage, new EventHandler(PasteMenuItem_Click)); + cms.Items.Add(_pasteToolStripMenuItem); + + cms.Items.Add(new ToolStripSeparator()); + + _selectAllToolStripMenuItem = new ToolStripMenuItem(SelectAllMenuItemTextInternal, SelectAllMenuItemImage, new EventHandler(SelectAllMenuItem_Click)); + cms.Items.Add(_selectAllToolStripMenuItem); + cms.Opening += new CancelEventHandler(BuildInContextMenuStrip_Opening); + + _contextMenuStrip = cms; + } + + if (this._hexBox.ByteProvider == null && this._hexBox.ContextMenuStrip == this._contextMenuStrip) + this._hexBox.ContextMenuStrip = null; + else if (this._hexBox.ByteProvider != null && this._hexBox.ContextMenuStrip == null) + this._hexBox.ContextMenuStrip = _contextMenuStrip; + } + /// + /// Before opening the ContextMenuStrip, we manage the availability of the items. + /// + /// the sender object + /// the event data + void BuildInContextMenuStrip_Opening(object sender, CancelEventArgs e) + { + _cutToolStripMenuItem.Enabled = this._hexBox.CanCut(); + _copyToolStripMenuItem.Enabled = this._hexBox.CanCopy(); + _pasteToolStripMenuItem.Enabled = this._hexBox.CanPaste(); + _selectAllToolStripMenuItem.Enabled = this._hexBox.CanSelectAll(); + } + /// + /// The handler for the "Cut"-Click event + /// + /// the sender object + /// the event data + void CutMenuItem_Click(object sender, EventArgs e) { this._hexBox.Cut(); } + /// + /// The handler for the "Copy"-Click event + /// + /// the sender object + /// the event data + void CopyMenuItem_Click(object sender, EventArgs e) { this._hexBox.Copy(); } + /// + /// The handler for the "Paste"-Click event + /// + /// the sender object + /// the event data + void PasteMenuItem_Click(object sender, EventArgs e) { this._hexBox.Paste(); } + /// + /// The handler for the "Select All"-Click event + /// + /// the sender object + /// the event data + void SelectAllMenuItem_Click(object sender, EventArgs e) { this._hexBox.SelectAll(); } + /// + /// Gets or sets the custom text of the "Copy" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)] + public string CopyMenuItemText + { + get { return _copyMenuItemText; } + set { _copyMenuItemText = value; } + } string _copyMenuItemText; + + /// + /// Gets or sets the custom text of the "Cut" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)] + public string CutMenuItemText + { + get { return _cutMenuItemText; } + set { _cutMenuItemText = value; } + } string _cutMenuItemText; + + /// + /// Gets or sets the custom text of the "Paste" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)] + public string PasteMenuItemText + { + get { return _pasteMenuItemText; } + set { _pasteMenuItemText = value; } + } string _pasteMenuItemText; + + /// + /// Gets or sets the custom text of the "Select All" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)] + public string SelectAllMenuItemText + { + get { return _selectAllMenuItemText; } + set { _selectAllMenuItemText = value; } + } string _selectAllMenuItemText = null; + + /// + /// Gets the text of the "Cut" ContextMenuStrip item. + /// + internal string CutMenuItemTextInternal { get { return !string.IsNullOrEmpty(CutMenuItemText) ? CutMenuItemText : "Cut"; } } + /// + /// Gets the text of the "Copy" ContextMenuStrip item. + /// + internal string CopyMenuItemTextInternal { get { return !string.IsNullOrEmpty(CopyMenuItemText) ? CopyMenuItemText : "Copy"; } } + /// + /// Gets the text of the "Paste" ContextMenuStrip item. + /// + internal string PasteMenuItemTextInternal { get { return !string.IsNullOrEmpty(PasteMenuItemText) ? PasteMenuItemText : "Paste"; } } + /// + /// Gets the text of the "Select All" ContextMenuStrip item. + /// + internal string SelectAllMenuItemTextInternal { get { return !string.IsNullOrEmpty(SelectAllMenuItemText) ? SelectAllMenuItemText : "Select All"; } } + + /// + /// Gets or sets the image of the "Cut" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null)] + public Image CutMenuItemImage + { + get { return _cutMenuItemImage; } + set { _cutMenuItemImage = value; } + } Image _cutMenuItemImage = null; + /// + /// Gets or sets the image of the "Copy" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null)] + public Image CopyMenuItemImage + { + get { return _copyMenuItemImage; } + set { _copyMenuItemImage = value; } + } Image _copyMenuItemImage = null; + /// + /// Gets or sets the image of the "Paste" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null)] + public Image PasteMenuItemImage + { + get { return _pasteMenuItemImage; } + set { _pasteMenuItemImage = value; } + } Image _pasteMenuItemImage = null; + /// + /// Gets or sets the image of the "Select All" ContextMenuStrip item. + /// + [Category("BuiltIn-ContextMenu"), DefaultValue(null)] + public Image SelectAllMenuItemImage + { + get { return _selectAllMenuItemImage; } + set { _selectAllMenuItemImage = value; } + } Image _selectAllMenuItemImage = null; + } +} diff --git a/UI/Debugger/HexBox/ByteCharConverters.cs b/UI/Debugger/HexBox/ByteCharConverters.cs new file mode 100644 index 0000000..aacf035 --- /dev/null +++ b/UI/Debugger/HexBox/ByteCharConverters.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Be.Windows.Forms +{ + /// + /// The interface for objects that can translate between characters and bytes. + /// + public interface IByteCharConverter + { + /// + /// Returns the character to display for the byte passed across. + /// + /// + /// + string ToChar(UInt64 value, out int keyLength); + + /// + /// Returns the byte to use when the character passed across is entered during editing. + /// + /// + /// + byte ToByte(char c); + + byte[] GetBytes(string str); + } + + /// + /// The default implementation. + /// + public class DefaultByteCharConverter : IByteCharConverter + { + /// + /// Returns the character to display for the byte passed across. + /// + /// + /// + public virtual string ToChar(UInt64 value, out int keyLength) + { + keyLength = 1; + byte b = (byte)value; + return new string(b > 0x1F && !(b > 0x7E && b < 0xA0) ? (char)b : '.', 1); + } + + /// + /// Returns the byte to use for the character passed across. + /// + /// + /// + public virtual byte ToByte(char c) + { + return (byte)c; + } + + public virtual byte[] GetBytes(string str) + { + List bytes = new List(str.Length); + foreach(char c in str) { + bytes.Add((byte)c); + } + return bytes.ToArray(); + } + + /// + /// Returns a description of the byte char provider. + /// + /// + public override string ToString() + { + return "ANSI (Default)"; + } + } + + /// + /// A byte char provider that can translate bytes encoded in codepage 500 EBCDIC + /// + public class EbcdicByteCharProvider : IByteCharConverter + { + /// + /// The IBM EBCDIC code page 500 encoding. Note that this is not always supported by .NET, + /// the underlying platform has to provide support for it. + /// + private Encoding _ebcdicEncoding = Encoding.GetEncoding(500); + + /// + /// Returns the EBCDIC character corresponding to the byte passed across. + /// + /// + /// + public virtual string ToChar(UInt64 value, out int keyLength) + { + keyLength = 1; + byte b = (byte)value; + string encoded = _ebcdicEncoding.GetString(new byte[] { b }); + return new string(encoded.Length > 0 ? encoded[0] : '.', 1); + } + + /// + /// Returns the byte corresponding to the EBCDIC character passed across. + /// + /// + /// + public virtual byte ToByte(char c) + { + byte[] decoded = _ebcdicEncoding.GetBytes(new char[] { c }); + return decoded.Length > 0 ? decoded[0] : (byte)0; + } + + public virtual byte[] GetBytes(string str) + { + List bytes = new List(str.Length); + foreach(char c in str) { + bytes.Add((byte)c); + } + return bytes.ToArray(); + } + + /// + /// Returns a description of the byte char provider. + /// + /// + public override string ToString() + { + return "EBCDIC (Code Page 500)"; + } + } +} \ No newline at end of file diff --git a/UI/Debugger/HexBox/ByteCollection.cs b/UI/Debugger/HexBox/ByteCollection.cs new file mode 100644 index 0000000..c7d2d37 --- /dev/null +++ b/UI/Debugger/HexBox/ByteCollection.cs @@ -0,0 +1,127 @@ +using System; + +using System.Collections; + +namespace Be.Windows.Forms +{ + /// + /// Represents a collection of bytes. + /// + public class ByteCollection : CollectionBase + { + /// + /// Initializes a new instance of ByteCollection class. + /// + public ByteCollection() { } + + /// + /// Initializes a new instance of ByteCollection class. + /// + /// an array of bytes to add to collection + public ByteCollection(byte[] bs) + { AddRange(bs); } + + /// + /// Gets or sets the value of a byte + /// + public byte this[int index] + { + get { return (byte)List[index]; } + set { List[index] = value; } + } + + /// + /// Adds a byte into the collection. + /// + /// the byte to add + public void Add(byte b) + { List.Add(b); } + + /// + /// Adds a range of bytes to the collection. + /// + /// the bytes to add + public void AddRange(byte[] bs) + { InnerList.AddRange(bs); } + + /// + /// Removes a byte from the collection. + /// + /// the byte to remove + public void Remove(byte b) + { List.Remove(b); } + + /// + /// Removes a range of bytes from the collection. + /// + /// the index of the start byte + /// the count of the bytes to remove + public void RemoveRange(int index, int count) + { InnerList.RemoveRange(index, count); } + + /// + /// Inserts a range of bytes to the collection. + /// + /// the index of start byte + /// an array of bytes to insert + public void InsertRange(int index, byte[] bs) + { InnerList.InsertRange(index, bs); } + + /// + /// Gets all bytes in the array + /// + /// an array of bytes. + public byte[] GetBytes() + { + byte[] bytes = new byte[Count]; + InnerList.CopyTo(0, bytes, 0, bytes.Length); + return bytes; + } + + /// + /// Inserts a byte to the collection. + /// + /// the index + /// a byte to insert + public void Insert(int index, byte b) + { + InnerList.Insert(index, b); + } + + /// + /// Returns the index of the given byte. + /// + public int IndexOf(byte b) + { + return InnerList.IndexOf(b); + } + + /// + /// Returns true, if the byte exists in the collection. + /// + public bool Contains(byte b) + { + return InnerList.Contains(b); + } + + /// + /// Copies the content of the collection into the given array. + /// + public void CopyTo(byte[] bs, int index) + { + InnerList.CopyTo(bs, index); + } + + /// + /// Copies the content of the collection into an array. + /// + /// the array containing all bytes. + public byte[] ToArray() + { + byte[] data = new byte[this.Count]; + this.CopyTo(data, 0); + return data; + } + + } +} diff --git a/UI/Debugger/HexBox/BytePositionInfo.cs b/UI/Debugger/HexBox/BytePositionInfo.cs new file mode 100644 index 0000000..1f02975 --- /dev/null +++ b/UI/Debugger/HexBox/BytePositionInfo.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Be.Windows.Forms +{ + /// + /// Represents a position in the HexBox control + /// + internal struct BytePositionInfo + { + public BytePositionInfo(long index, int characterPosition) + { + _index = index; + _characterPosition = characterPosition; + } + + public int CharacterPosition + { + get { return _characterPosition; } + } int _characterPosition; + + public long Index + { + get { return _index; } + } long _index; + } +} diff --git a/UI/Debugger/HexBox/DataBlock.cs b/UI/Debugger/HexBox/DataBlock.cs new file mode 100644 index 0000000..e5ea58f --- /dev/null +++ b/UI/Debugger/HexBox/DataBlock.cs @@ -0,0 +1,42 @@ +using System; + +namespace Be.Windows.Forms +{ + internal abstract class DataBlock + { + internal DataMap _map; + internal DataBlock _nextBlock; + internal DataBlock _previousBlock; + + public abstract long Length + { + get; + } + + public DataMap Map + { + get + { + return _map; + } + } + + public DataBlock NextBlock + { + get + { + return _nextBlock; + } + } + + public DataBlock PreviousBlock + { + get + { + return _previousBlock; + } + } + + public abstract void RemoveBytes(long position, long count); + } +} diff --git a/UI/Debugger/HexBox/DataMap.cs b/UI/Debugger/HexBox/DataMap.cs new file mode 100644 index 0000000..2876b08 --- /dev/null +++ b/UI/Debugger/HexBox/DataMap.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections; +using System.Text; + +namespace Be.Windows.Forms +{ + internal class DataMap : ICollection, IEnumerable + { + readonly object _syncRoot = new object(); + internal int _count; + internal DataBlock _firstBlock; + internal int _version; + + public DataMap() + { + } + + public DataMap(IEnumerable collection) + { + if (collection == null) + { + throw new ArgumentNullException("collection"); + } + + foreach (DataBlock item in collection) + { + AddLast(item); + } + } + + public DataBlock FirstBlock + { + get + { + return _firstBlock; + } + } + + public void AddAfter(DataBlock block, DataBlock newBlock) + { + AddAfterInternal(block, newBlock); + } + + public void AddBefore(DataBlock block, DataBlock newBlock) + { + AddBeforeInternal(block, newBlock); + } + + public void AddFirst(DataBlock block) + { + if (_firstBlock == null) + { + AddBlockToEmptyMap(block); + } + else + { + AddBeforeInternal(_firstBlock, block); + } + } + + public void AddLast(DataBlock block) + { + if (_firstBlock == null) + { + AddBlockToEmptyMap(block); + } + else + { + AddAfterInternal(GetLastBlock(), block); + } + } + + public void Remove(DataBlock block) + { + RemoveInternal(block); + } + + public void RemoveFirst() + { + if (_firstBlock == null) + { + throw new InvalidOperationException("The collection is empty."); + } + RemoveInternal(_firstBlock); + } + + public void RemoveLast() + { + if (_firstBlock == null) + { + throw new InvalidOperationException("The collection is empty."); + } + RemoveInternal(GetLastBlock()); + } + + public DataBlock Replace(DataBlock block, DataBlock newBlock) + { + AddAfterInternal(block, newBlock); + RemoveInternal(block); + return newBlock; + } + + public void Clear() + { + DataBlock block = FirstBlock; + while (block != null) + { + DataBlock nextBlock = block.NextBlock; + InvalidateBlock(block); + block = nextBlock; + } + _firstBlock = null; + _count = 0; + _version++; + } + + void AddAfterInternal(DataBlock block, DataBlock newBlock) + { + newBlock._previousBlock = block; + newBlock._nextBlock = block._nextBlock; + newBlock._map = this; + + if (block._nextBlock != null) + { + block._nextBlock._previousBlock = newBlock; + } + block._nextBlock = newBlock; + + this._version++; + this._count++; + } + + void AddBeforeInternal(DataBlock block, DataBlock newBlock) + { + newBlock._nextBlock = block; + newBlock._previousBlock = block._previousBlock; + newBlock._map = this; + + if (block._previousBlock != null) + { + block._previousBlock._nextBlock = newBlock; + } + block._previousBlock = newBlock; + + if (_firstBlock == block) + { + _firstBlock = newBlock; + } + this._version++; + this._count++; + } + + void RemoveInternal(DataBlock block) + { + DataBlock previousBlock = block._previousBlock; + DataBlock nextBlock = block._nextBlock; + + if (previousBlock != null) + { + previousBlock._nextBlock = nextBlock; + } + + if (nextBlock != null) + { + nextBlock._previousBlock = previousBlock; + } + + if (_firstBlock == block) + { + _firstBlock = nextBlock; + } + + InvalidateBlock(block); + + _count--; + _version++; + } + + DataBlock GetLastBlock() + { + DataBlock lastBlock = null; + for (DataBlock block = FirstBlock; block != null; block = block.NextBlock) + { + lastBlock = block; + } + return lastBlock; + } + + void InvalidateBlock(DataBlock block) + { + block._map = null; + block._nextBlock = null; + block._previousBlock = null; + } + + void AddBlockToEmptyMap(DataBlock block) + { + block._map = this; + block._nextBlock = null; + block._previousBlock = null; + + _firstBlock = block; + _version++; + _count++; + } + + #region ICollection Members + public void CopyTo(Array array, int index) + { + DataBlock[] blockArray = array as DataBlock[]; + for (DataBlock block = FirstBlock; block != null; block = block.NextBlock) + { + blockArray[index++] = block; + } + } + + public int Count + { + get + { + return _count; + } + } + + public bool IsSynchronized + { + get + { + return false; + } + } + + public object SyncRoot + { + get + { + return _syncRoot; + } + } + #endregion + + #region IEnumerable Members + public IEnumerator GetEnumerator() + { + return new Enumerator(this); + } + #endregion + + #region Enumerator Nested Type + internal class Enumerator : IEnumerator, IDisposable + { + DataMap _map; + DataBlock _current; + int _index; + int _version; + + internal Enumerator(DataMap map) + { + _map = map; + _version = map._version; + _current = null; + _index = -1; + } + + object IEnumerator.Current + { + get + { + if (_index < 0 || _index > _map.Count) + { + throw new InvalidOperationException("Enumerator is positioned before the first element or after the last element of the collection."); + } + return _current; + } + } + + public bool MoveNext() + { + if (this._version != _map._version) + { + throw new InvalidOperationException("Collection was modified after the enumerator was instantiated."); + } + + if (_index >= _map.Count) + { + return false; + } + + if (++_index == 0) + { + _current = _map.FirstBlock; + } + else + { + _current = _current.NextBlock; + } + + return (_index < _map.Count); + } + + void IEnumerator.Reset() + { + if (this._version != this._map._version) + { + throw new InvalidOperationException("Collection was modified after the enumerator was instantiated."); + } + + this._index = -1; + this._current = null; + } + + public void Dispose() + { + } + } + #endregion + } +} diff --git a/UI/Debugger/HexBox/DynamicByteProvider.cs b/UI/Debugger/HexBox/DynamicByteProvider.cs new file mode 100644 index 0000000..ff023e7 --- /dev/null +++ b/UI/Debugger/HexBox/DynamicByteProvider.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; + +namespace Be.Windows.Forms +{ + /// + /// Byte provider for a small amount of data. + /// + public class DynamicByteProvider : IByteProvider + { + /// + /// Contains information about changes. + /// + bool _hasChanges; + /// + /// Contains a byte collection. + /// + List _bytes; + + /// + /// Initializes a new instance of the DynamicByteProvider class. + /// + /// + public DynamicByteProvider(byte[] data) : this(new List(data)) + { + } + + /// + /// Initializes a new instance of the DynamicByteProvider class. + /// + /// + public DynamicByteProvider(List bytes) + { + _bytes = bytes; + } + + public void SetData(byte[] data) + { + _bytes = new List(data); + } + + /// + /// Raises the Changed event. + /// + void OnChanged(EventArgs e) + { + _hasChanges = true; + + if(Changed != null) + Changed(this, e); + } + + /// + /// Raises the LengthChanged event. + /// + void OnLengthChanged(EventArgs e) + { + if(LengthChanged != null) + LengthChanged(this, e); + } + + /// + /// Gets the byte collection. + /// + public List Bytes + { + get { return _bytes; } + } + + #region IByteProvider Members + /// + /// True, when changes are done. + /// + public bool HasChanges() + { + return _hasChanges; + } + + /// + /// Applies changes. + /// + public void ApplyChanges() + { + _hasChanges = false; + } + + /// + /// Occurs, when the write buffer contains new changes. + /// + public event EventHandler Changed; + + /// + /// Occurs, when InsertBytes or DeleteBytes method is called. + /// + public event EventHandler LengthChanged; + + public delegate void ByteChangedHandler(int byteIndex, byte newValue, byte oldValue); + public event ByteChangedHandler ByteChanged; + + public delegate void BytesChangedHandler(int byteIndex, byte[] values); + public event BytesChangedHandler BytesChanged; + + /// + /// Reads a byte from the byte collection. + /// + /// the index of the byte to read + /// the byte + public byte ReadByte(long index) + { + if(_partialPos == index) { + return _partialValue; + } else { + return _bytes[(int)index]; + } + } + + /// + /// Write a byte into the byte collection. + /// + /// the index of the byte to write. + /// the byte + public void WriteByte(long index, byte value) + { + if(index == _partialPos) { + _partialPos = -1; + } + + if(_bytes[(int)index] != value) { + ByteChanged?.Invoke((int)index, value, _bytes[(int)index]); + _bytes[(int)index] = value; + OnChanged(EventArgs.Empty); + } + } + + public void WriteBytes(long index, byte[] values) + { + _partialPos = -1; + BytesChanged?.Invoke((int)index, values); + for(int i = 0; i < values.Length && index + i < _bytes.Count; i++) { + _bytes[(int)index+i] = values[i]; + } + } + + long _partialPos = -1; + byte _partialValue = 0; + public void PartialWriteByte(long index, byte value) + { + //Wait for a full byte to be written + _partialPos = index; + _partialValue = value; + } + + public void CommitWriteByte() + { + if(_partialPos >= 0) { + WriteByte(_partialPos, _partialValue); + _partialPos = -1; + } + } + + /// + /// Deletes bytes from the byte collection. + /// + /// the start index of the bytes to delete. + /// the length of bytes to delete. + public void DeleteBytes(long index, long length) + { + int internal_index = (int)Math.Max(0, index); + int internal_length = (int)Math.Min((int)Length, length); + _bytes.RemoveRange(internal_index, internal_length); + + OnLengthChanged(EventArgs.Empty); + OnChanged(EventArgs.Empty); + } + + /// + /// Inserts byte into the byte collection. + /// + /// the start index of the bytes in the byte collection + /// the byte array to insert + public void InsertBytes(long index, byte[] bs) + { + _bytes.InsertRange((int)index, bs); + + OnLengthChanged(EventArgs.Empty); + OnChanged(EventArgs.Empty); + } + + /// + /// Gets the length of the bytes in the byte collection. + /// + public long Length + { + get + { + return _bytes.Count; + } + } + + /// + /// Returns true + /// + public virtual bool SupportsWriteByte() + { + return true; + } + + /// + /// Returns true + /// + public virtual bool SupportsInsertBytes() + { + return true; + } + + /// + /// Returns true + /// + public virtual bool SupportsDeleteBytes() + { + return true; + } + #endregion + + } +} diff --git a/UI/Debugger/HexBox/FileDataBlock.cs b/UI/Debugger/HexBox/FileDataBlock.cs new file mode 100644 index 0000000..40e0b0e --- /dev/null +++ b/UI/Debugger/HexBox/FileDataBlock.cs @@ -0,0 +1,96 @@ +using System; + +namespace Be.Windows.Forms +{ + internal sealed class FileDataBlock : DataBlock + { + long _length; + long _fileOffset; + + public FileDataBlock(long fileOffset, long length) + { + _fileOffset = fileOffset; + _length = length; + } + + public long FileOffset + { + get + { + return _fileOffset; + } + } + + public override long Length + { + get + { + return _length; + } + } + + public void SetFileOffset(long value) + { + _fileOffset = value; + } + + public void RemoveBytesFromEnd(long count) + { + if (count > _length) + { + throw new ArgumentOutOfRangeException("count"); + } + + _length -= count; + } + + public void RemoveBytesFromStart(long count) + { + if (count > _length) + { + throw new ArgumentOutOfRangeException("count"); + } + + _fileOffset += count; + _length -= count; + } + + public override void RemoveBytes(long position, long count) + { + if (position > _length) + { + throw new ArgumentOutOfRangeException("position"); + } + + if (position + count > _length) + { + throw new ArgumentOutOfRangeException("count"); + } + + long prefixLength = position; + long prefixFileOffset = _fileOffset; + + long suffixLength = _length - count - prefixLength; + long suffixFileOffset = _fileOffset + position + count; + + if (prefixLength > 0 && suffixLength > 0) + { + _fileOffset = prefixFileOffset; + _length = prefixLength; + _map.AddAfter(this, new FileDataBlock(suffixFileOffset, suffixLength)); + return; + } + + if (prefixLength > 0) + { + _fileOffset = prefixFileOffset; + _length = prefixLength; + } + else + { + _fileOffset = suffixFileOffset; + _length = suffixLength; + } + } + } +} diff --git a/UI/Debugger/HexBox/FindOptions.cs b/UI/Debugger/HexBox/FindOptions.cs new file mode 100644 index 0000000..acfd8dc --- /dev/null +++ b/UI/Debugger/HexBox/FindOptions.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Be.Windows.Forms +{ + /// + /// Defines the type of the Find operation. + /// + public enum FindType + { + /// + /// Used for Text Find operations + /// + Text, + /// + /// Used for Hex Find operations + /// + Hex + } + + /// + /// Defines all state information nee + /// + public class FindOptions + { + public bool WrapSearch { get; set; } + public bool HasWildcard { get; set; } + + /// + /// Gets or sets whether the Find options are valid + /// + public bool IsValid { get; set; } + /// + /// Gets the Find buffer used for case insensitive Find operations. This is the binary representation of Text. + /// + internal byte[] FindBuffer { get; private set; } + /// + /// Gets the Find buffer used for case sensitive Find operations. This is the binary representation of Text in lower case format. + /// + internal byte[] FindBufferLowerCase { get; private set; } + /// + /// Gets the Find buffer used for case sensitive Find operations. This is the binary representation of Text in upper case format. + /// + internal byte[] FindBufferUpperCase { get; private set; } + /// + /// Contains the MatchCase value + /// + bool _matchCase; + /// + /// Gets or sets the value, whether the Find operation is case sensitive or not. + /// + public bool MatchCase + { + get { return _matchCase; } + set + { + _matchCase = value; + } + } + /// + /// Contains the text that should be found. + /// + string _text; + /// + /// Gets or sets the text that should be found. Only used, when Type is FindType.Hex. + /// + public string Text + { + get { return _text; } + set + { + _text = value; + } + } + /// + /// Gets or sets the hex buffer that should be found. Only used, when Type is FindType.Hex. + /// + public byte[] Hex { get; set; } + /// + /// Gets or sets the type what should be searched. + /// + public FindType Type { get; set; } + /// + /// Updates the find buffer. + /// + internal void UpdateFindBuffer(IByteCharConverter byteCharConverter) + { + string text = this.Text != null ? this.Text : string.Empty; + FindBuffer = byteCharConverter.GetBytes(text); + FindBufferLowerCase = byteCharConverter.GetBytes(text.ToLower()); + FindBufferUpperCase = byteCharConverter.GetBytes(text.ToUpper()); + } + } +} diff --git a/UI/Debugger/HexBox/HexBox.cs b/UI/Debugger/HexBox/HexBox.cs new file mode 100644 index 0000000..af2d1f7 --- /dev/null +++ b/UI/Debugger/HexBox/HexBox.cs @@ -0,0 +1,4244 @@ +using System; +using System.Drawing; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Security.Permissions; +using System.Windows.Forms.VisualStyles; +using System.Text; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace Be.Windows.Forms +{ + /// + /// Represents a hex box control. + /// + [ToolboxBitmap(typeof(HexBox), "HexBox.bmp")] + public class HexBox : Control + { + #region IKeyInterpreter interface + /// + /// Defines a user input handler such as for mouse and keyboard input + /// + interface IKeyInterpreter + { + /// + /// Activates mouse events + /// + void Activate(); + /// + /// Deactivate mouse events + /// + void Deactivate(); + /// + /// Preprocesses WM_KEYUP window message. + /// + /// the Message object to process. + /// True, if the message was processed. + bool PreProcessWmKeyUp(ref Message m); + /// + /// Preprocesses WM_CHAR window message. + /// + /// the Message object to process. + /// True, if the message was processed. + bool PreProcessWmChar(ref Message m); + /// + /// Preprocesses WM_KEYDOWN window message. + /// + /// the Message object to process. + /// True, if the message was processed. + bool PreProcessWmKeyDown(ref Message m); + /// + /// Gives some information about where to place the caret. + /// + /// the index of the byte + /// the position where the caret is to place. + PointF GetCaretPointF(long byteIndex); + } + #endregion + + #region EmptyKeyInterpreter class + /// + /// Represents an empty input handler without any functionality. + /// If is set ByteProvider to null, then this interpreter is used. + /// + class EmptyKeyInterpreter : IKeyInterpreter + { + HexBox _hexBox; + + public EmptyKeyInterpreter(HexBox hexBox) + { + _hexBox = hexBox; + } + + #region IKeyInterpreter Members + public void Activate() { } + public void Deactivate() { } + + public bool PreProcessWmKeyUp(ref Message m) + { return _hexBox.BasePreProcessMessage(ref m); } + + public bool PreProcessWmChar(ref Message m) + { return _hexBox.BasePreProcessMessage(ref m); } + + public bool PreProcessWmKeyDown(ref Message m) + { return _hexBox.BasePreProcessMessage(ref m); } + + public PointF GetCaretPointF(long byteIndex) + { return new PointF(); } + + #endregion + } + #endregion + + #region KeyInterpreter class + /// + /// Handles user input such as mouse and keyboard input during hex view edit + /// + class KeyInterpreter : IKeyInterpreter + { + /// + /// Delegate for key-down processing. + /// + /// the message object contains key data information + /// True, if the message was processed + delegate bool MessageDelegate(ref Message m); + + #region Fields + /// + /// Contains the parent HexBox control + /// + protected HexBox _hexBox; + + /// + /// Contains True, if shift key is down + /// + protected bool _shiftDown; + /// + /// Contains True, if mouse is down + /// + bool _mouseDown; + /// + /// Contains the selection start position info + /// + BytePositionInfo _bpiStart; + /// + /// Contains the current mouse selection position info + /// + BytePositionInfo _bpi; + /// + /// Contains all message handlers of key interpreter key down message + /// + Dictionary _messageHandlers; + #endregion + + #region Ctors + public KeyInterpreter(HexBox hexBox) + { + _hexBox = hexBox; + } + #endregion + + #region Activate, Deactive methods + public virtual void Activate() + { + _hexBox.MouseDown += new MouseEventHandler(BeginMouseSelection); + _hexBox.MouseMove += new MouseEventHandler(UpdateMouseSelection); + _hexBox.MouseUp += new MouseEventHandler(EndMouseSelection); + } + + public virtual void Deactivate() + { + _hexBox.MouseDown -= new MouseEventHandler(BeginMouseSelection); + _hexBox.MouseMove -= new MouseEventHandler(UpdateMouseSelection); + _hexBox.MouseUp -= new MouseEventHandler(EndMouseSelection); + } + #endregion + + #region Mouse selection methods + void BeginMouseSelection(object sender, MouseEventArgs e) + { + System.Diagnostics.Debug.WriteLine("BeginMouseSelection()", "KeyInterpreter"); + + if (e.Button != MouseButtons.Left) + return; + + _mouseDown = true; + + if (!_shiftDown) + { + _bpiStart = new BytePositionInfo(_hexBox._bytePos, _hexBox._byteCharacterPos); + _hexBox.ReleaseSelection(); + } + else + { + UpdateMouseSelection(this, e); + } + } + + void UpdateMouseSelection(object sender, MouseEventArgs e) + { + if (!_mouseDown) + return; + + _bpi = GetBytePositionInfo(new Point(e.X, e.Y)); + long selEnd = _bpi.Index; + long realselStart; + long realselLength; + + if (selEnd < _bpiStart.Index) + { + realselStart = selEnd; + realselLength = _bpiStart.Index - selEnd; + } + else if (selEnd > _bpiStart.Index) + { + realselStart = _bpiStart.Index; + realselLength = selEnd - realselStart; + } + else + { + realselStart = _hexBox._bytePos; + realselLength = 0; + } + + if (realselStart != _hexBox._bytePos || realselLength != _hexBox._selectionLength) + { + _hexBox.InternalSelect(realselStart, realselLength); + _hexBox.ScrollByteIntoView(_bpi.Index); + } + } + + void EndMouseSelection(object sender, MouseEventArgs e) + { + _mouseDown = false; + } + #endregion + + #region PrePrcessWmKeyDown methods + public virtual bool PreProcessWmKeyDown(ref Message m) + { + System.Diagnostics.Debug.WriteLine("PreProcessWmKeyDown(ref Message m)", "KeyInterpreter"); + + Keys vc = (Keys)m.WParam.ToInt32(); + + Keys keyData = vc | Control.ModifierKeys; + + // detect whether key down event should be raised + var hasMessageHandler = this.MessageHandlers.ContainsKey(keyData); + if (hasMessageHandler && RaiseKeyDown(keyData)) + return true; + + MessageDelegate messageHandler = hasMessageHandler + ? this.MessageHandlers[keyData] + : messageHandler = new MessageDelegate(PreProcessWmKeyDown_Default); + + return messageHandler(ref m); + } + + protected bool PreProcessWmKeyDown_Default(ref Message m) + { + _hexBox.ScrollByteIntoView(); + return _hexBox.BasePreProcessMessage(ref m); + } + + protected bool RaiseKeyDown(Keys keyData) + { + KeyEventArgs e = new KeyEventArgs(keyData); + _hexBox.OnKeyDown(e); + return e.Handled; + } + + protected virtual bool PreProcessWmKeyDown_Left(ref Message m) + { + return PerformPosMoveLeft(); + } + + protected virtual bool PreProcessWmKeyDown_Up(ref Message m) + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (!(pos == 0 && cp == 0)) + { + pos = Math.Max(-1, pos - _hexBox._iHexMaxHBytes); + if (pos == -1) + return true; + + _hexBox.SetPosition(pos); + + if (pos < _hexBox._startByte) + { + _hexBox.PerformScrollLineUp(); + } + + _hexBox.UpdateCaret(); + _hexBox.Invalidate(); + } + + _hexBox.ScrollByteIntoView(); + _hexBox.ReleaseSelection(); + + return true; + } + + protected virtual bool PreProcessWmKeyDown_Right(ref Message m) + { + return PerformPosMoveRight(true); + } + + protected virtual bool PreProcessWmKeyDown_Down(ref Message m) + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (pos == _hexBox._byteProvider.Length && cp == 0) + return true; + + pos = Math.Min(_hexBox._byteProvider.Length, pos + _hexBox._iHexMaxHBytes); + + if (pos == _hexBox._byteProvider.Length) + cp = 0; + + _hexBox.SetPosition(pos, cp); + + if (pos > _hexBox._endByte - 1) + { + _hexBox.PerformScrollLineDown(); + } + + _hexBox.UpdateCaret(); + _hexBox.ScrollByteIntoView(); + _hexBox.ReleaseSelection(); + _hexBox.Invalidate(); + + return true; + } + + protected virtual bool PreProcessWmKeyDown_PageUp(ref Message m) + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (pos == 0 && cp == 0) + return true; + + pos = Math.Max(0, pos - _hexBox._iHexMaxBytes); + if (pos == 0) + return true; + + _hexBox.SetPosition(pos); + + if (pos < _hexBox._startByte) + { + _hexBox.PerformScrollPageUp(); + } + + _hexBox.ReleaseSelection(); + _hexBox.UpdateCaret(); + _hexBox.Invalidate(); + return true; + } + + protected virtual bool PreProcessWmKeyDown_PageDown(ref Message m) + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (pos == _hexBox._byteProvider.Length && cp == 0) + return true; + + pos = Math.Min(_hexBox._byteProvider.Length, pos + _hexBox._iHexMaxBytes); + + if (pos == _hexBox._byteProvider.Length) + cp = 0; + + _hexBox.SetPosition(pos, cp); + + if (pos > _hexBox._endByte - 1) + { + _hexBox.PerformScrollPageDown(); + } + + _hexBox.ReleaseSelection(); + _hexBox.UpdateCaret(); + _hexBox.Invalidate(); + + return true; + } + + protected virtual bool PreProcessWmKeyDown_ShiftLeft(ref Message m) + { + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + + if (pos + sel < 1) + return true; + + if (pos + sel <= _bpiStart.Index) + { + if (pos == 0) + return true; + + pos--; + sel++; + } + else + { + sel = Math.Max(0, sel - 1); + } + + _hexBox.ScrollByteIntoView(); + _hexBox.InternalSelect(pos, sel); + + return true; + } + + protected virtual bool PreProcessWmKeyDown_ShiftUp(ref Message m) + { + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + + if (pos - _hexBox._iHexMaxHBytes < 0 && pos <= _bpiStart.Index) + return true; + + if (_bpiStart.Index >= pos + sel) + { + pos = pos - _hexBox._iHexMaxHBytes; + sel += _hexBox._iHexMaxHBytes; + _hexBox.InternalSelect(pos, sel); + _hexBox.ScrollByteIntoView(); + } + else + { + sel -= _hexBox._iHexMaxHBytes; + if (sel < 0) + { + pos = _bpiStart.Index + sel; + sel = -sel; + _hexBox.InternalSelect(pos, sel); + _hexBox.ScrollByteIntoView(); + } + else + { + sel -= _hexBox._iHexMaxHBytes; + _hexBox.InternalSelect(pos, sel); + _hexBox.ScrollByteIntoView(pos + sel); + } + } + + return true; + } + + protected virtual bool PreProcessWmKeyDown_ShiftRight(ref Message m) + { + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + + if (pos + sel >= _hexBox._byteProvider.Length) + return true; + + if (_bpiStart.Index <= pos) + { + sel++; + _hexBox.InternalSelect(pos, sel); + _hexBox.ScrollByteIntoView(pos + sel); + } + else + { + pos++; + sel = Math.Max(0, sel - 1); + _hexBox.InternalSelect(pos, sel); + _hexBox.ScrollByteIntoView(); + } + + return true; + } + + protected virtual bool PreProcessWmKeyDown_ShiftDown(ref Message m) + { + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + + long max = _hexBox._byteProvider.Length; + + if (pos + sel + _hexBox._iHexMaxHBytes > max) + return true; + + if (_bpiStart.Index <= pos) + { + sel += _hexBox._iHexMaxHBytes; + _hexBox.InternalSelect(pos, sel); + _hexBox.ScrollByteIntoView(pos + sel); + } + else + { + sel -= _hexBox._iHexMaxHBytes; + if (sel < 0) + { + pos = _bpiStart.Index; + sel = -sel; + } + else + { + pos += _hexBox._iHexMaxHBytes; + //sel -= _hexBox._iHexMaxHBytes; + } + + _hexBox.InternalSelect(pos, sel); + _hexBox.ScrollByteIntoView(); + } + + return true; + } + + protected virtual bool PreProcessWmKeyDown_Tab(ref Message m) + { + if (_hexBox._stringViewVisible && _hexBox._keyInterpreter.GetType() == typeof(KeyInterpreter)) + { + _hexBox.ActivateStringKeyInterpreter(); + _hexBox.ScrollByteIntoView(); + _hexBox.ReleaseSelection(); + _hexBox.UpdateCaret(); + _hexBox.Invalidate(); + return true; + } + + if (_hexBox.Parent == null) return true; + _hexBox.Parent.SelectNextControl(_hexBox, true, true, true, true); + return true; + } + + protected virtual bool PreProcessWmKeyDown_ShiftTab(ref Message m) + { + if (_hexBox._keyInterpreter is StringKeyInterpreter) + { + _shiftDown = false; + _hexBox.ActivateKeyInterpreter(); + _hexBox.ScrollByteIntoView(); + _hexBox.ReleaseSelection(); + _hexBox.UpdateCaret(); + _hexBox.Invalidate(); + return true; + } + + if (_hexBox.Parent == null) return true; + _hexBox.Parent.SelectNextControl(_hexBox, false, true, true, true); + return true; + } + + protected virtual bool PreProcessWmKeyDown_Back(ref Message m) + { + if (!_hexBox._byteProvider.SupportsDeleteBytes()) + return true; + + if (_hexBox.ReadOnly) + return true; + + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + int cp = _hexBox._byteCharacterPos; + + long startDelete = (cp == 0 && sel == 0) ? pos - 1 : pos; + if (startDelete < 0 && sel < 1) + return true; + + long bytesToDelete = (sel > 0) ? sel : 1; + _hexBox._byteProvider.DeleteBytes(Math.Max(0, startDelete), bytesToDelete); + _hexBox.UpdateScrollSize(); + + if (sel == 0) + PerformPosMoveLeftByte(); + + _hexBox.ReleaseSelection(); + _hexBox.Invalidate(); + + return true; + } + + protected virtual bool PreProcessWmKeyDown_Delete(ref Message m) + { + if (!_hexBox._byteProvider.SupportsDeleteBytes()) + return true; + + if (_hexBox.ReadOnly) + return true; + + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + + if (pos >= _hexBox._byteProvider.Length) + return true; + + long bytesToDelete = (sel > 0) ? sel : 1; + _hexBox._byteProvider.DeleteBytes(pos, bytesToDelete); + + _hexBox.UpdateScrollSize(); + _hexBox.ReleaseSelection(); + _hexBox.Invalidate(); + + return true; + } + + protected virtual bool PreProcessWmKeyDown_Home(ref Message m) + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (pos < 1) + return true; + + pos = 0; + cp = 0; + _hexBox.SetPosition(pos, cp); + + _hexBox.ScrollByteIntoView(); + _hexBox.UpdateCaret(); + _hexBox.ReleaseSelection(); + _hexBox.Invalidate(); + return true; + } + + protected virtual bool PreProcessWmKeyDown_End(ref Message m) + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (pos >= _hexBox._byteProvider.Length - 1) + return true; + + pos = _hexBox._byteProvider.Length; + cp = 0; + _hexBox.SetPosition(pos, cp); + + _hexBox.ScrollByteIntoView(); + _hexBox.UpdateCaret(); + _hexBox.ReleaseSelection(); + _hexBox.Invalidate(); + return true; + } + + protected virtual bool PreProcessWmKeyDown_ShiftShiftKey(ref Message m) + { + if (_mouseDown) + return true; + if (_shiftDown) + return true; + + _shiftDown = true; + + if (_hexBox._selectionLength > 0) + return true; + + _bpiStart = new BytePositionInfo(_hexBox._bytePos, _hexBox._byteCharacterPos); + + return true; + } + + protected virtual bool PreProcessWmKeyDown_ControlC(ref Message m) + { + _hexBox.Copy(); + return true; + } + + protected virtual bool PreProcessWmKeyDown_ControlX(ref Message m) + { + _hexBox.Cut(); + return true; + } + + protected virtual bool PreProcessWmKeyDown_ControlV(ref Message m) + { + _hexBox.Paste(); + return true; + } + + #endregion + + #region PreProcessWmChar methods + public virtual bool PreProcessWmChar(ref Message m) + { + if (Control.ModifierKeys == Keys.Control) + { + return _hexBox.BasePreProcessMessage(ref m); + } + + bool sw = _hexBox._byteProvider.SupportsWriteByte(); + bool si = _hexBox._byteProvider.SupportsInsertBytes(); + bool sd = _hexBox._byteProvider.SupportsDeleteBytes(); + + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + int cp = _hexBox._byteCharacterPos; + + if ( + (!sw && pos != _hexBox._byteProvider.Length) || + (!si && pos == _hexBox._byteProvider.Length)) + { + return _hexBox.BasePreProcessMessage(ref m); + } + + char c = (char)m.WParam.ToInt32(); + + if (Uri.IsHexDigit(c)) + { + if (RaiseKeyPress(c)) + return true; + + if (_hexBox.ReadOnly) + return true; + + bool isInsertMode = (pos == _hexBox._byteProvider.Length); + + // do insert when insertActive = true + if (!isInsertMode && si && _hexBox.InsertActive && cp == 0) + isInsertMode = true; + + if (sd && si && sel > 0) + { + _hexBox._byteProvider.DeleteBytes(pos, sel); + isInsertMode = true; + cp = 0; + _hexBox.SetPosition(pos, cp); + } + + _hexBox.ReleaseSelection(); + + byte currentByte; + if (isInsertMode) + currentByte = 0; + else + currentByte = _hexBox._byteProvider.ReadByte(pos); + + string sCb = currentByte.ToString("X", System.Threading.Thread.CurrentThread.CurrentCulture); + if (sCb.Length == 1) + sCb = "0" + sCb; + + string sNewCb = c.ToString(); + if (cp == 0) + sNewCb += sCb.Substring(1, 1); + else + sNewCb = sCb.Substring(0, 1) + sNewCb; + byte newcb = byte.Parse(sNewCb, System.Globalization.NumberStyles.AllowHexSpecifier, System.Threading.Thread.CurrentThread.CurrentCulture); + + if(isInsertMode) { + _hexBox._byteProvider.InsertBytes(pos, new byte[] { newcb }); + } else { + if(cp == 1 || !_hexBox.ByteEditingMode) { + _hexBox._byteProvider.WriteByte(pos, newcb); + } else { + //First char in byte, do not update the changes right away + _hexBox._byteProvider.PartialWriteByte(pos, newcb); + } + } + + PerformPosMoveRight(false); + + _hexBox.Invalidate(); + return true; + } + else + { + return _hexBox.BasePreProcessMessage(ref m); + } + } + + protected bool RaiseKeyPress(char keyChar) + { + KeyPressEventArgs e = new KeyPressEventArgs(keyChar); + _hexBox.OnKeyPress(e); + return e.Handled; + } + #endregion + + #region PreProcessWmKeyUp methods + public virtual bool PreProcessWmKeyUp(ref Message m) + { + System.Diagnostics.Debug.WriteLine("PreProcessWmKeyUp(ref Message m)", "KeyInterpreter"); + + Keys vc = (Keys)m.WParam.ToInt32(); + + Keys keyData = vc | Control.ModifierKeys; + + switch (keyData) + { + case Keys.ShiftKey: + case Keys.Insert: + if (RaiseKeyUp(keyData)) + return true; + break; + } + + switch (keyData) + { + case Keys.ShiftKey: + _shiftDown = false; + return true; + case Keys.Insert: + return PreProcessWmKeyUp_Insert(ref m); + default: + return _hexBox.BasePreProcessMessage(ref m); + } + } + + protected virtual bool PreProcessWmKeyUp_Insert(ref Message m) + { + _hexBox.InsertActive = !_hexBox.InsertActive; + return true; + } + + protected bool RaiseKeyUp(Keys keyData) + { + KeyEventArgs e = new KeyEventArgs(keyData); + _hexBox.OnKeyUp(e); + return e.Handled; + } + #endregion + + #region Misc + Dictionary MessageHandlers + { + get + { + if (_messageHandlers == null) + { + _messageHandlers = new Dictionary(); + _messageHandlers.Add(Keys.Left, new MessageDelegate(PreProcessWmKeyDown_Left)); // move left + _messageHandlers.Add(Keys.Up, new MessageDelegate(PreProcessWmKeyDown_Up)); // move up + _messageHandlers.Add(Keys.Right, new MessageDelegate(PreProcessWmKeyDown_Right)); // move right + _messageHandlers.Add(Keys.Down, new MessageDelegate(PreProcessWmKeyDown_Down)); // move down + _messageHandlers.Add(Keys.PageUp, new MessageDelegate(PreProcessWmKeyDown_PageUp)); // move pageup + _messageHandlers.Add(Keys.PageDown, new MessageDelegate(PreProcessWmKeyDown_PageDown)); // move page down + _messageHandlers.Add(Keys.Left | Keys.Shift, new MessageDelegate(PreProcessWmKeyDown_ShiftLeft)); // move left with selection + _messageHandlers.Add(Keys.Up | Keys.Shift, new MessageDelegate(PreProcessWmKeyDown_ShiftUp)); // move up with selection + _messageHandlers.Add(Keys.Right | Keys.Shift, new MessageDelegate(PreProcessWmKeyDown_ShiftRight)); // move right with selection + _messageHandlers.Add(Keys.Down | Keys.Shift, new MessageDelegate(PreProcessWmKeyDown_ShiftDown)); // move down with selection + _messageHandlers.Add(Keys.Tab, new MessageDelegate(PreProcessWmKeyDown_Tab)); // switch to string view + _messageHandlers.Add(Keys.Back, new MessageDelegate(PreProcessWmKeyDown_Back)); // back + _messageHandlers.Add(Keys.Delete, new MessageDelegate(PreProcessWmKeyDown_Delete)); // delete + _messageHandlers.Add(Keys.Home, new MessageDelegate(PreProcessWmKeyDown_Home)); // move to home + _messageHandlers.Add(Keys.End, new MessageDelegate(PreProcessWmKeyDown_End)); // move to end + _messageHandlers.Add(Keys.ShiftKey | Keys.Shift, new MessageDelegate(PreProcessWmKeyDown_ShiftShiftKey)); // begin selection process + _messageHandlers.Add(Keys.C | Keys.Control, new MessageDelegate(PreProcessWmKeyDown_ControlC)); // copy + _messageHandlers.Add(Keys.X | Keys.Control, new MessageDelegate(PreProcessWmKeyDown_ControlX)); // cut + _messageHandlers.Add(Keys.V | Keys.Control, new MessageDelegate(PreProcessWmKeyDown_ControlV)); // paste + } + return _messageHandlers; + } + } + + protected virtual bool PerformPosMoveLeft() + { + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + int cp = _hexBox._byteCharacterPos; + + if (sel != 0) + { + cp = 0; + _hexBox.SetPosition(pos, cp); + _hexBox.ReleaseSelection(); + } + else + { + if (pos == 0 && cp == 0) + return true; + + if(_hexBox.EnablePerByteNavigation) { + pos = Math.Max(0, pos - 1); + cp = 0; + } else { + if (cp > 0) { + cp--; + } else { + pos = Math.Max(0, pos - 1); + cp++; + } + } + + _hexBox.SetPosition(pos, cp); + + if (pos < _hexBox._startByte) + { + _hexBox.PerformScrollLineUp(); + } + _hexBox.UpdateCaret(); + _hexBox.Invalidate(); + } + + _hexBox.ScrollByteIntoView(); + return true; + } + protected virtual bool PerformPosMoveRight(bool allowNibbleSkip) + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + long sel = _hexBox._selectionLength; + + if (sel != 0) + { + pos += sel; + cp = 0; + _hexBox.SetPosition(pos, cp); + _hexBox.ReleaseSelection(); + } + else + { + if (!(pos == _hexBox._byteProvider.Length && cp == 0)) + { + + if(cp > 0 || (allowNibbleSkip && _hexBox.EnablePerByteNavigation)) { + pos = Math.Min(_hexBox._byteProvider.Length, pos + 1); + cp = 0; + } else { + cp++; + } + + _hexBox.SetPosition(pos, cp); + + if (pos > _hexBox._endByte - 1) + { + _hexBox.PerformScrollLineDown(); + } + _hexBox.UpdateCaret(); + _hexBox.Invalidate(); + } + } + + _hexBox.ScrollByteIntoView(); + return true; + } + protected virtual bool PerformPosMoveLeftByte() + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (pos == 0) + return true; + + pos = Math.Max(0, pos - 1); + cp = 0; + + _hexBox.SetPosition(pos, cp); + + if (pos < _hexBox._startByte) + { + _hexBox.PerformScrollLineUp(); + } + _hexBox.UpdateCaret(); + _hexBox.ScrollByteIntoView(); + _hexBox.Invalidate(); + + return true; + } + + protected virtual bool PerformPosMoveRightByte() + { + long pos = _hexBox._bytePos; + int cp = _hexBox._byteCharacterPos; + + if (pos == _hexBox._byteProvider.Length) + return true; + + pos = Math.Min(_hexBox._byteProvider.Length, pos + 1); + cp = 0; + + _hexBox.SetPosition(pos, cp); + + if (pos > _hexBox._endByte - 1) + { + _hexBox.PerformScrollLineDown(); + } + _hexBox.UpdateCaret(); + _hexBox.ScrollByteIntoView(); + _hexBox.Invalidate(); + + return true; + } + + + public virtual PointF GetCaretPointF(long byteIndex) + { + System.Diagnostics.Debug.WriteLine("GetCaretPointF()", "KeyInterpreter"); + + return _hexBox.GetBytePointF(byteIndex); + } + + protected virtual BytePositionInfo GetBytePositionInfo(Point p) + { + return _hexBox.GetHexBytePositionInfo(p); + } + #endregion + } + #endregion + + #region StringKeyInterpreter class + /// + /// Handles user input such as mouse and keyboard input during string view edit + /// + class StringKeyInterpreter : KeyInterpreter + { + #region Ctors + public StringKeyInterpreter(HexBox hexBox) + : base(hexBox) + { + _hexBox._byteCharacterPos = 0; + } + #endregion + + #region PreProcessWmKeyDown methods + public override bool PreProcessWmKeyDown(ref Message m) + { + Keys vc = (Keys)m.WParam.ToInt32(); + + Keys keyData = vc | Control.ModifierKeys; + + switch (keyData) + { + case Keys.Tab | Keys.Shift: + case Keys.Tab: + if (RaiseKeyDown(keyData)) + return true; + break; + } + + switch (keyData) + { + case Keys.Tab | Keys.Shift: + return PreProcessWmKeyDown_ShiftTab(ref m); + case Keys.Tab: + return PreProcessWmKeyDown_Tab(ref m); + default: + return base.PreProcessWmKeyDown(ref m); + } + } + + protected override bool PreProcessWmKeyDown_Left(ref Message m) + { + return PerformPosMoveLeftByte(); + } + + protected override bool PreProcessWmKeyDown_Right(ref Message m) + { + return PerformPosMoveRightByte(); + } + + #endregion + + #region PreProcessWmChar methods + public override bool PreProcessWmChar(ref Message m) + { + if (Control.ModifierKeys == Keys.Control) + { + return _hexBox.BasePreProcessMessage(ref m); + } + + bool sw = _hexBox._byteProvider.SupportsWriteByte(); + bool si = _hexBox._byteProvider.SupportsInsertBytes(); + bool sd = _hexBox._byteProvider.SupportsDeleteBytes(); + + long pos = _hexBox._bytePos; + long sel = _hexBox._selectionLength; + int cp = _hexBox._byteCharacterPos; + + if ( + (!sw && pos != _hexBox._byteProvider.Length) || + (!si && pos == _hexBox._byteProvider.Length)) + { + return _hexBox.BasePreProcessMessage(ref m); + } + + char c = (char)m.WParam.ToInt32(); + + if (RaiseKeyPress(c)) + return true; + + if (_hexBox.ReadOnly) + return true; + + bool isInsertMode = (pos == _hexBox._byteProvider.Length); + + // do insert when insertActive = true + if (!isInsertMode && si && _hexBox.InsertActive) + isInsertMode = true; + + if (sd && si && sel > 0) + { + _hexBox._byteProvider.DeleteBytes(pos, sel); + isInsertMode = true; + cp = 0; + _hexBox.SetPosition(pos, cp); + } + + _hexBox.ReleaseSelection(); + + byte b = _hexBox.ByteCharConverter.ToByte(c); + if (isInsertMode) + _hexBox._byteProvider.InsertBytes(pos, new byte[] { b }); + else + _hexBox._byteProvider.WriteByte(pos, b); + + PerformPosMoveRightByte(); + _hexBox.Invalidate(); + + return true; + } + #endregion + + #region Misc + public override PointF GetCaretPointF(long byteIndex) + { + System.Diagnostics.Debug.WriteLine("GetCaretPointF()", "StringKeyInterpreter"); + + Point gp = _hexBox.GetGridBytePoint(byteIndex); + return _hexBox.GetByteStringPointF(gp); + } + + protected override BytePositionInfo GetBytePositionInfo(Point p) + { + return _hexBox.GetStringBytePositionInfo(p); + } + #endregion + } + #endregion + + #region Fields + /// + /// Contains the hole content bounds of all text + /// + Rectangle _recContent; + /// + /// Contains the line info bounds + /// + Rectangle _recLineInfo; + /// + /// Contains the column info header rectangle bounds + /// + Rectangle _recColumnInfo; + /// + /// Contains the hex data bounds + /// + Rectangle _recHex; + /// + /// Contains the string view bounds + /// + Rectangle _recStringView; + + /// + /// Contains string format information for text drawing + /// + StringFormat _stringFormat; + /// + /// Contains the maximum of visible horizontal bytes + /// + int _iHexMaxHBytes; + /// + /// Contains the maximum of visible vertical bytes + /// + int _iHexMaxVBytes; + /// + /// Contains the maximum of visible bytes. + /// + int _iHexMaxBytes; + + /// + /// Contains the scroll bars minimum value + /// + long _scrollVmin; + /// + /// Contains the scroll bars maximum value + /// + long _scrollVmax; + /// + /// Contains the scroll bars current position + /// + long _scrollVpos; + /// + /// Contains a vertical scroll + /// + VScrollBar _vScrollBar; + /// + /// Contains a timer for thumbtrack scrolling + /// + Timer _thumbTrackTimer = new Timer(); + /// + /// Contains the thumbtrack scrolling position + /// + long _thumbTrackPosition; + /// + /// Contains the thumptrack delay for scrolling in milliseconds. + /// + const int THUMPTRACKDELAY = 50; + /// + /// Contains the Enviroment.TickCount of the last refresh + /// + int _lastThumbtrack; + /// + /// Contains the border´s left shift + /// + int _recBorderLeft = SystemInformation.Border3DSize.Width; + /// + /// Contains the border´s right shift + /// + int _recBorderRight = SystemInformation.Border3DSize.Width; + /// + /// Contains the border´s top shift + /// + int _recBorderTop = SystemInformation.Border3DSize.Height; + /// + /// Contains the border bottom shift + /// + int _recBorderBottom = SystemInformation.Border3DSize.Height; + + /// + /// Contains the index of the first visible byte + /// + long _startByte; + /// + /// Contains the index of the last visible byte + /// + long _endByte; + + /// + /// Contains the current byte position + /// + long _bytePos = -1; + /// + /// Contains the current char position in one byte + /// + /// + /// "1A" + /// "1" = char position of 0 + /// "A" = char position of 1 + /// + int _byteCharacterPos; + + /// + /// Contains string format information for hex values + /// + string _hexStringFormat = "X"; + + + /// + /// Contains the current key interpreter + /// + IKeyInterpreter _keyInterpreter; + /// + /// Contains an empty key interpreter without functionality + /// + EmptyKeyInterpreter _eki; + /// + /// Contains the default key interpreter + /// + KeyInterpreter _ki; + /// + /// Contains the string key interpreter + /// + StringKeyInterpreter _ski; + + /// + /// Contains True if caret is visible + /// + bool _caretVisible; + + /// + /// Contains true, if the find (Find method) should be aborted. + /// + bool _abortFind; + /// + /// Contains a value of the current finding position. + /// + long _findingPos; + + /// + /// Contains a state value about Insert or Write mode. When this value is true and the ByteProvider SupportsInsert is true bytes are inserted instead of overridden. + /// + bool _insertActive; + + PointF _caretPos; + + #endregion + + #region Events + /// + /// Occurs, when the value of InsertActive property has changed. + /// + [Description("Occurs, when the value of InsertActive property has changed.")] + public event EventHandler InsertActiveChanged; + /// + /// Occurs, when the value of ReadOnly property has changed. + /// + [Description("Occurs, when the value of ReadOnly property has changed.")] + public event EventHandler ReadOnlyChanged; + /// + /// Occurs, when the value of ByteProvider property has changed. + /// + [Description("Occurs, when the value of ByteProvider property has changed.")] + public event EventHandler ByteProviderChanged; + /// + /// Occurs, when the value of SelectionStart property has changed. + /// + [Description("Occurs, when the value of SelectionStart property has changed.")] + public event EventHandler SelectionStartChanged; + /// + /// Occurs, when the value of SelectionLength property has changed. + /// + [Description("Occurs, when the value of SelectionLength property has changed.")] + public event EventHandler SelectionLengthChanged; + /// + /// Occurs, when the value of LineInfoVisible property has changed. + /// + [Description("Occurs, when the value of LineInfoVisible property has changed.")] + public event EventHandler LineInfoVisibleChanged; + /// + /// Occurs, when the value of ColumnInfoVisibleChanged property has changed. + /// + [Description("Occurs, when the value of ColumnInfoVisibleChanged property has changed.")] + public event EventHandler ColumnInfoVisibleChanged; + /// + /// Occurs, when the value of GroupSeparatorVisibleChanged property has changed. + /// + [Description("Occurs, when the value of GroupSeparatorVisibleChanged property has changed.")] + public event EventHandler GroupSeparatorVisibleChanged; + /// + /// Occurs, when the value of StringViewVisible property has changed. + /// + [Description("Occurs, when the value of StringViewVisible property has changed.")] + public event EventHandler StringViewVisibleChanged; + /// + /// Occurs, when the value of BorderStyle property has changed. + /// + [Description("Occurs, when the value of BorderStyle property has changed.")] + public event EventHandler BorderStyleChanged; + /// + /// Occurs, when the value of ColumnWidth property has changed. + /// + [Description("Occurs, when the value of GroupSize property has changed.")] + public event EventHandler GroupSizeChanged; + /// + /// Occurs, when the value of BytesPerLine property has changed. + /// + [Description("Occurs, when the value of BytesPerLine property has changed.")] + public event EventHandler BytesPerLineChanged; + /// + /// Occurs, when the value of UseFixedBytesPerLine property has changed. + /// + [Description("Occurs, when the value of UseFixedBytesPerLine property has changed.")] + public event EventHandler UseFixedBytesPerLineChanged; + /// + /// Occurs, when the value of VScrollBarVisible property has changed. + /// + [Description("Occurs, when the value of VScrollBarVisible property has changed.")] + public event EventHandler VScrollBarVisibleChanged; + /// + /// Occurs, when the value of HexCasing property has changed. + /// + [Description("Occurs, when the value of HexCasing property has changed.")] + public event EventHandler HexCasingChanged; + /// + /// Occurs, when the value of HorizontalByteCount property has changed. + /// + [Description("Occurs, when the value of HorizontalByteCount property has changed.")] + public event EventHandler HorizontalByteCountChanged; + /// + /// Occurs, when the value of VerticalByteCount property has changed. + /// + [Description("Occurs, when the value of VerticalByteCount property has changed.")] + public event EventHandler VerticalByteCountChanged; + /// + /// Occurs, when the value of CurrentLine property has changed. + /// + [Description("Occurs, when the value of CurrentLine property has changed.")] + public event EventHandler CurrentLineChanged; + /// + /// Occurs, when the value of CurrentPositionInLine property has changed. + /// + [Description("Occurs, when the value of CurrentPositionInLine property has changed.")] + public event EventHandler CurrentPositionInLineChanged; + /// + /// Occurs, when Copy method was invoked and ClipBoardData changed. + /// + [Description("Occurs, when Copy method was invoked and ClipBoardData changed.")] + public event EventHandler Copied; + /// + /// Occurs, when CopyHex method was invoked and ClipBoardData changed. + /// + [Description("Occurs, when CopyHex method was invoked and ClipBoardData changed.")] + public event EventHandler CopiedHex; + /// + /// Occurs, when the CharSize property has changed + /// + [Description("Occurs, when the CharSize property has changed")] + public event EventHandler CharSizeChanged; + /// + /// Occurs, when the RequiredWidth property changes + /// + [Description("Occurs, when the RequiredWidth property changes")] + public event EventHandler RequiredWidthChanged; + #endregion + + #region Ctors + + /// + /// Initializes a new instance of a HexBox class. + /// + public HexBox() + { + this._vScrollBar = new VScrollBar(); + this._vScrollBar.Scroll += new ScrollEventHandler(_vScrollBar_Scroll); + + this._builtInContextMenu = new BuiltInContextMenu(this); + + BackColor = Color.White; + Font = SystemFonts.MessageBoxFont; + _stringFormat = new StringFormat(StringFormat.GenericTypographic); + _stringFormat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces; + + ActivateEmptyKeyInterpreter(); + + SetStyle(ControlStyles.UserPaint, true); + SetStyle(ControlStyles.DoubleBuffer, true); + SetStyle(ControlStyles.AllPaintingInWmPaint, true); + SetStyle(ControlStyles.ResizeRedraw, true); + + _thumbTrackTimer.Interval = 50; + _thumbTrackTimer.Tick += new EventHandler(PerformScrollThumbTrack); + } + + #endregion + + #region Scroll methods + void _vScrollBar_Scroll(object sender, ScrollEventArgs e) + { + switch (e.Type) + { + case ScrollEventType.Last: + break; + case ScrollEventType.EndScroll: + break; + case ScrollEventType.SmallIncrement: + PerformScrollLineDown(); + break; + case ScrollEventType.SmallDecrement: + PerformScrollLineUp(); + break; + case ScrollEventType.LargeIncrement: + PerformScrollPageDown(); + break; + case ScrollEventType.LargeDecrement: + PerformScrollPageUp(); + break; + case ScrollEventType.ThumbPosition: + long lPos = FromScrollPos(e.NewValue); + PerformScrollThumpPosition(lPos); + break; + case ScrollEventType.ThumbTrack: + // to avoid performance problems use a refresh delay implemented with a timer + if (_thumbTrackTimer.Enabled) // stop old timer + _thumbTrackTimer.Enabled = false; + + // perform scroll immediately only if last refresh is very old + int currentThumbTrack = System.Environment.TickCount; + if (currentThumbTrack - _lastThumbtrack > THUMPTRACKDELAY) + { + PerformScrollThumbTrack(null, null); + _lastThumbtrack = currentThumbTrack; + break; + } + + // start thumbtrack timer + _thumbTrackPosition = FromScrollPos(e.NewValue); + _thumbTrackTimer.Enabled = true; + break; + case ScrollEventType.First: + break; + default: + break; + } + + e.NewValue = ToScrollPos(_scrollVpos); + } + + /// + /// Performs the thumbtrack scrolling after an delay. + /// + void PerformScrollThumbTrack(object sender, EventArgs e) + { + _thumbTrackTimer.Enabled = false; + PerformScrollThumpPosition(_thumbTrackPosition); + _lastThumbtrack = Environment.TickCount; + } + + void UpdateScrollSize() + { + System.Diagnostics.Debug.WriteLine("UpdateScrollSize()", "HexBox"); + + // calc scroll bar info + if (VScrollBarVisible && _byteProvider != null && _byteProvider.Length > 0 && _iHexMaxHBytes != 0) + { + long scrollmax = (long)Math.Ceiling((double)(_byteProvider.Length + 1) / (double)_iHexMaxHBytes - (double)_iHexMaxVBytes); + scrollmax = Math.Max(0, scrollmax); + + long scrollpos = _startByte / _iHexMaxHBytes; + + if (scrollmax < _scrollVmax) + { + /* Data size has been decreased. */ + if (_scrollVpos == _scrollVmax) + /* Scroll one line up if we at bottom. */ + PerformScrollLineUp(); + } + + if (scrollmax == _scrollVmax && scrollpos == _scrollVpos) + return; + + _scrollVmin = 0; + _scrollVmax = scrollmax; + _scrollVpos = Math.Min(scrollpos, scrollmax); + UpdateVScroll(); + } + else if (VScrollBarVisible) + { + // disable scroll bar + _scrollVmin = 0; + _scrollVmax = 0; + _scrollVpos = 0; + UpdateVScroll(); + } + } + + void UpdateVScroll() + { + System.Diagnostics.Debug.WriteLine("UpdateVScroll()", "HexBox"); + + int max = ToScrollMax(_scrollVmax); + + if (max > 0) + { + _vScrollBar.Minimum = 0; + _vScrollBar.Maximum = max; + _vScrollBar.Value = ToScrollPos(_scrollVpos); + _vScrollBar.Visible = true; + } + else + { + _vScrollBar.Visible = false; + } + } + + int ToScrollPos(long value) + { + int max = 65535; + + if (_scrollVmax < max) + return (int)value; + else + { + double valperc = (double)value / (double)_scrollVmax * (double)100; + int res = (int)Math.Floor((double)max / (double)100 * valperc); + res = (int)Math.Max(_scrollVmin, res); + res = (int)Math.Min(_scrollVmax, res); + return res; + } + } + + long FromScrollPos(int value) + { + int max = 65535; + if (_scrollVmax < max) + { + return (long)value; + } + else + { + double valperc = (double)value / (double)max * (double)100; + long res = (int)Math.Floor((double)_scrollVmax / (double)100 * valperc); + return res; + } + } + + int ToScrollMax(long value) + { + long max = 65535; + if (value > max) + return (int)max; + else + return (int)value; + } + + void PerformScrollToLine(long pos) + { + if (pos < _scrollVmin || pos > _scrollVmax || pos == _scrollVpos) + return; + + _scrollVpos = pos; + + UpdateVScroll(); + UpdateVisibilityBytes(); + UpdateCaret(); + Invalidate(); + } + + void PerformScrollLines(int lines) + { + long pos; + if (lines > 0) + { + pos = Math.Min(_scrollVmax, _scrollVpos + lines); + } + else if (lines < 0) + { + pos = Math.Max(_scrollVmin, _scrollVpos + lines); + } + else + { + return; + } + + PerformScrollToLine(pos); + } + + void PerformScrollLineDown() + { + this.PerformScrollLines(1); + } + + void PerformScrollLineUp() + { + this.PerformScrollLines(-1); + } + + void PerformScrollPageDown() + { + this.PerformScrollLines(_iHexMaxVBytes); + } + + void PerformScrollPageUp() + { + this.PerformScrollLines(-_iHexMaxVBytes); + } + + void PerformScrollThumpPosition(long pos) + { + // Bug fix: Scroll to end, do not scroll to end + int difference = (_scrollVmax > 65535) ? 10 : 9; + + if (ToScrollPos(pos) == ToScrollMax(_scrollVmax) - difference) + pos = _scrollVmax; + // End Bug fix + + + PerformScrollToLine(pos); + } + + /// + /// Scrolls the selection start byte into view + /// + public void ScrollByteIntoView() + { + System.Diagnostics.Debug.WriteLine("ScrollByteIntoView()", "HexBox"); + + ScrollByteIntoView(_bytePos); + } + + /// + /// Scrolls the specific byte into view + /// + /// the index of the byte + public void ScrollByteIntoView(long index) + { + System.Diagnostics.Debug.WriteLine("ScrollByteIntoView(long index)", "HexBox"); + + if (_byteProvider == null || _keyInterpreter == null) + return; + + if (index < _startByte) + { + long line = (long)Math.Floor((double)index / (double)_iHexMaxHBytes); + PerformScrollThumpPosition(line); + } + else if (index > _endByte) + { + long line = (long)Math.Floor((double)index / (double)_iHexMaxHBytes); + line -= _iHexMaxVBytes - 1; + PerformScrollThumpPosition(line); + } + } + #endregion + + #region Selection methods + void ReleaseSelection() + { + System.Diagnostics.Debug.WriteLine("ReleaseSelection()", "HexBox"); + + if (_selectionLength == 0) + return; + _selectionLength = 0; + OnSelectionLengthChanged(EventArgs.Empty); + + if (!_caretVisible) + CreateCaret(); + else + UpdateCaret(); + + Invalidate(); + } + + /// + /// Returns true if Select method could be invoked. + /// + public bool CanSelectAll() + { + if (!this.Enabled) + return false; + if (_byteProvider == null) + return false; + + return true; + } + + /// + /// Selects all bytes. + /// + public void SelectAll() + { + if (this.ByteProvider == null) + return; + this.Select(0, this.ByteProvider.Length); + } + + /// + /// Selects the hex box. + /// + /// the start index of the selection + /// the length of the selection + public void Select(long start, long length) + { + if (this.ByteProvider == null) + return; + if (!this.Enabled) + return; + + InternalSelect(start, length); + ScrollByteIntoView(); + + ActivateKeyInterpreter(); + } + + void InternalSelect(long start, long length) + { + long pos = start; + long sel = length; + int cp = 0; + + if (sel > 0 && _caretVisible) + DestroyCaret(); + else if (sel == 0 && !_caretVisible) + CreateCaret(); + + SetPosition(pos, cp); + SetSelectionLength(sel); + + UpdateCaret(); + Invalidate(); + } + #endregion + + #region Key interpreter methods + void ActivateEmptyKeyInterpreter() + { + if (_eki == null) + _eki = new EmptyKeyInterpreter(this); + + if (_eki == _keyInterpreter) + return; + + if (_keyInterpreter != null) + _keyInterpreter.Deactivate(); + + _keyInterpreter = _eki; + _keyInterpreter.Activate(); + } + + void ActivateKeyInterpreter() + { + if (_ki == null) + _ki = new KeyInterpreter(this); + + if (_ki == _keyInterpreter) + return; + + if (_keyInterpreter != null) + _keyInterpreter.Deactivate(); + + _keyInterpreter = _ki; + _keyInterpreter.Activate(); + } + + void ActivateStringKeyInterpreter() + { + if (_ski == null) + _ski = new StringKeyInterpreter(this); + + if (_ski == _keyInterpreter) + return; + + if (_keyInterpreter != null) + _keyInterpreter.Deactivate(); + + _keyInterpreter = _ski; + _keyInterpreter.Activate(); + } + #endregion + + #region Caret methods + void CreateCaret() + { + if (_byteProvider == null || _keyInterpreter == null || _caretVisible) + return; + + System.Diagnostics.Debug.WriteLine("CreateCaret()", "HexBox"); + + UpdateCaret(); + + _caretVisible = true; + } + + void UpdateCaret() + { + if (_byteProvider == null || _keyInterpreter == null) + return; + + System.Diagnostics.Debug.WriteLine("UpdateCaret()", "HexBox"); + + long byteIndex = _bytePos - _startByte; + + PointF p = _keyInterpreter.GetCaretPointF(byteIndex); + p.X += _byteCharacterPos * _charSize.Width; + if(HighDensityMode) { + p.Y += _highDensityModeOffset; + } + + _caretPos = p; + } + + void DestroyCaret() + { + //Never hide caret + return; + } + + internal BytePositionInfo? GetRestrictedHexBytePositionInfo(Point p) + { + if(_recHex.Contains(p) && p.X < _recHex.Left + (HorizontalByteCount * 3 - 1) * _charSize.Width) { + //Only return the position if the mouse is over a hex digit + return GetHexBytePositionInfo(p); + } + return null; + } + + BytePositionInfo? GetBytePositionInfo(Point p) + { + if(_recHex.Contains(p)) { + return GetHexBytePositionInfo(p); + } else if(_recStringView.Contains(p) || p.X > _recStringView.Right) { + return GetStringBytePositionInfo(p); + } + return null; + } + + void SetCaretPosition(Point p) + { + System.Diagnostics.Debug.WriteLine("SetCaretPosition()", "HexBox"); + + if (_byteProvider == null || _keyInterpreter == null) + return; + + long pos = _bytePos; + int cp = _byteCharacterPos; + + CreateCaret(); + + if (_recHex.Contains(p)) + { + BytePositionInfo bpi = GetHexBytePositionInfo(p); + pos = bpi.Index; + cp = ByteEditingMode ? 0 : bpi.CharacterPosition; + + SetPosition(pos, cp); + + ActivateKeyInterpreter(); + UpdateCaret(); + Invalidate(); + } + else if (_recStringView.Contains(p) || p.X > _recStringView.Right) + { + BytePositionInfo bpi = GetStringBytePositionInfo(p); + pos = bpi.Index; + cp = bpi.CharacterPosition; + + SetPosition(pos, cp); + + ActivateStringKeyInterpreter(); + UpdateCaret(); + Invalidate(); + } + } + + internal BytePositionInfo GetHexBytePositionInfo(Point p) + { + System.Diagnostics.Debug.WriteLine("GetHexBytePositionInfo()", "HexBox"); + + long bytePos; + int byteCharaterPos; + + float x = ((float)(p.X - _recHex.X) / _charSize.Width); + float y = ((float)(p.Y - _recHex.Y) / _charSize.Height); + int iY = (int)y; + + //Offset by half a character to make the selection more intuitive (e.g half the white space belongs to the left byte, the other to the right) + float pos = (x + 0.5f) / 3 + 1; + int hPos = (int)pos; + + bytePos = Math.Min(_byteProvider.Length, + _startByte + (_iHexMaxHBytes * (iY + 1) - _iHexMaxHBytes) + hPos - 1); + + if(pos - Math.Floor(pos) > 0.5f) { + byteCharaterPos = 1; + } else { + byteCharaterPos = 0; + } + + if (bytePos == _byteProvider.Length) + byteCharaterPos = 0; + + if (bytePos < 0) + return new BytePositionInfo(0, 0); + return new BytePositionInfo(bytePos, byteCharaterPos); + } + + BytePositionInfo GetStringBytePositionInfo(Point p) + { + System.Diagnostics.Debug.WriteLine("GetStringBytePositionInfo()", "HexBox"); + + long bytePos; + int byteCharacterPos; + + float y = ((float)(p.Y - _recStringView.Y) / _charSize.Height); + int iY = (int)y; + int hPos = 0; + + if(_xPosList.ContainsKey(iY)) { + List posList = _xPosList[iY]; + for(int i = 0; i < posList.Count - 1; i++) { + if(posList[i] <= p.X && posList[i + 1] >= p.X) { + hPos = i + 1; + break; + } + } + if(hPos == 0) { + hPos = posList.Count; + } + } + + bytePos = Math.Min(_byteProvider.Length, + _startByte + (_iHexMaxHBytes * (iY + 1) - _iHexMaxHBytes) + hPos - 1); + byteCharacterPos = 0; + + if (bytePos < 0) + return new BytePositionInfo(0, 0); + return new BytePositionInfo(bytePos, byteCharacterPos); + } + #endregion + + #region PreProcessMessage methods + /// + /// Preprocesses windows messages. + /// + /// the message to process. + /// true, if the message was processed + [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true), SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)] + public override bool PreProcessMessage(ref Message m) + { + switch (m.Msg) + { + case NativeMethods.WM_KEYDOWN: + return _keyInterpreter.PreProcessWmKeyDown(ref m); + case NativeMethods.WM_CHAR: + return _keyInterpreter.PreProcessWmChar(ref m); + case NativeMethods.WM_KEYUP: + return _keyInterpreter.PreProcessWmKeyUp(ref m); + default: + return base.PreProcessMessage(ref m); + } + } + + bool BasePreProcessMessage(ref Message m) + { + return base.PreProcessMessage(ref m); + } + #endregion + + public enum eSearchDirection + { + Incremental, + Next, + Previous + } + + #region Find methods + /// + /// Searches the current ByteProvider + /// + /// contains all find options + /// the SelectionStart property value if find was successfull or + /// -1 if there is no match + /// -2 if Find was aborted. + public long Find(FindOptions options, eSearchDirection direction) + { + long startIndex = 0; + switch(direction) { + case eSearchDirection.Previous: startIndex = this.SelectionStart - 1; break; + default: + case eSearchDirection.Incremental: startIndex = this.SelectionStart; break; + case eSearchDirection.Next: startIndex = this.SelectionStart + 1; break; + } + + options.UpdateFindBuffer(this.ByteCharConverter); + + byte[] buffer1 = null; + byte[] buffer2 = null; + if(options.Type == FindType.Text && options.MatchCase) { + if(options.FindBuffer == null || options.FindBuffer.Length == 0) + throw new ArgumentException("FindBuffer can not be null when Type: Text and MatchCase: false"); + buffer1 = options.FindBuffer; + } else if(options.Type == FindType.Text && !options.MatchCase) { + if(options.FindBufferLowerCase == null || options.FindBufferLowerCase.Length == 0) + throw new ArgumentException("FindBufferLowerCase can not be null when Type is Text and MatchCase is true"); + if(options.FindBufferUpperCase == null || options.FindBufferUpperCase.Length == 0) + throw new ArgumentException("FindBufferUpperCase can not be null when Type is Text and MatchCase is true"); + if(options.FindBufferLowerCase.Length != options.FindBufferUpperCase.Length) + throw new ArgumentException("FindBufferUpperCase and FindBufferUpperCase must have the same size when Type is Text and MatchCase is true"); + buffer1 = options.FindBufferLowerCase; + buffer2 = options.FindBufferUpperCase; + + } else if(options.Type == FindType.Hex) { + if(options.Hex == null || options.Hex.Length == 0) + throw new ArgumentException("Hex can not be null when Type is Hex"); + buffer1 = options.Hex; + } + + + _abortFind = false; + + long result = -1; + if(direction == eSearchDirection.Previous) { + result = FindLoop(buffer1, buffer2, startIndex, 0, options.HasWildcard); + if(result == -1 && options.WrapSearch) { + result = FindLoop(buffer1, buffer2, _byteProvider.Length - 1, Math.Max(0, startIndex - buffer1.Length), options.HasWildcard); + } + } else { + result = FindLoop(buffer1, buffer2, startIndex, _byteProvider.Length, options.HasWildcard); + if(result == -1 && options.WrapSearch) { + result = FindLoop(buffer1, buffer2, 0, Math.Min(_byteProvider.Length, startIndex + buffer1.Length), options.HasWildcard); + } + } + return result; + } + + private long FindLoop(byte[] buffer1, byte[] buffer2, long start, long end, bool hasWildcard) + { + int matchStartIndex = start > end ? buffer1.Length - 1 : 0; + int match = matchStartIndex; + int buffer1Length = buffer1.Length; + + for(long pos = start; pos != end; pos = start > end ? pos-1 : pos+1) { + if(_abortFind) + return -2; + + //if (pos % 1000 == 0) // for performance reasons: DoEvents only 1 times per 1000 loops + //Application.DoEvents(); + + byte compareByte = _byteProvider.ReadByte(pos); + + bool wildcardMatch = false; + if(match == buffer1Length - 1 && hasWildcard) { + if((buffer1[match] >> 4) == (compareByte >> 4)) { + wildcardMatch = true; + } + } + + bool buffer1Match = compareByte == buffer1[match]; + bool hasBuffer2 = buffer2 != null; + bool buffer2Match = hasBuffer2 ? compareByte == buffer2[match] : false; + bool isMatch = buffer1Match || buffer2Match || wildcardMatch; + if(!isMatch) { + pos = pos + (matchStartIndex - match); + match = matchStartIndex; + _findingPos = pos; + continue; + } + + match = (start > end ? match-1: match+1); + + if(match == buffer1Length || match == -1) { + long bytePos = start > end ? pos : (pos - buffer1Length + 1); + Select(bytePos, buffer1Length); + ScrollByteIntoView(_bytePos + _selectionLength); + ScrollByteIntoView(_bytePos); + + return bytePos; + } + } + + return -1; + } + + /// + /// Aborts a working Find method. + /// + public void AbortFind() + { + _abortFind = true; + } + + /// + /// Gets a value that indicates the current position during Find method execution. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public long CurrentFindingPosition + { + get + { + return _findingPos; + } + } + #endregion + + #region Copy, Cut and Paste methods + byte[] GetCopyData() + { + if (!CanCopy()) return new byte[0]; + + // put bytes into buffer + byte[] buffer = new byte[_selectionLength]; + int id = -1; + for (long i = _bytePos; i < _bytePos + _selectionLength; i++) + { + id++; + + buffer[id] = _byteProvider.ReadByte(i); + } + return buffer; + } + /// + /// Copies the current selection in the hex box to the Clipboard. + /// + public void Copy() + { + if (!CanCopy()) return; + + // put bytes into buffer + byte[] buffer = GetCopyData(); + + DataObject da = new DataObject(); + + // set string buffer clipbard data + string hex = BitConverter.ToString(buffer).Replace("-", ""); + StringBuilder sb = new StringBuilder(hex.Length * 2); + for(int i = 0; i < hex.Length; i+=BytesPerLine*2) { + sb.AppendLine(hex.Substring(i, i + BytesPerLine*2 > hex.Length ? hex.Length - i : BytesPerLine*2)); + } + + da.SetData(typeof(string), sb.ToString()); + + //string sBuffer = System.Text.Encoding.ASCII.GetString(buffer, 0, buffer.Length); + //da.SetData(typeof(string), sBuffer); + + //set memorystream (BinaryData) clipboard data + System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer, 0, buffer.Length, false, true); + da.SetData("BinaryData", ms); + + Clipboard.SetDataObject(da, true); + UpdateCaret(); + ScrollByteIntoView(); + Invalidate(); + + OnCopied(EventArgs.Empty); + } + + /// + /// Return true if Copy method could be invoked. + /// + public bool CanCopy() + { + if (_selectionLength < 1 || _byteProvider == null) + return false; + + return true; + } + + /// + /// Moves the current selection in the hex box to the Clipboard. + /// + public void Cut() + { + if (!CanCut()) return; + + Copy(); + + _byteProvider.DeleteBytes(_bytePos, _selectionLength); + _byteCharacterPos = 0; + UpdateCaret(); + ScrollByteIntoView(); + ReleaseSelection(); + Invalidate(); + Refresh(); + } + + /// + /// Return true if Cut method could be invoked. + /// + public bool CanCut() + { + if (ReadOnly || !this.Enabled) + return false; + if (_byteProvider == null) + return false; + if (_selectionLength < 1 || !_byteProvider.SupportsDeleteBytes()) + return false; + + return true; + } + + private Regex _nonHexRegex = new Regex("[^A-Fa-f0-9]", RegexOptions.Compiled); + + /// + /// Replaces the current selection in the hex box with the contents of the Clipboard. + /// + public void Paste() + { + if (!CanPaste()) return; + + byte[] buffer = null; + IDataObject da = Clipboard.GetDataObject(); + if(da != null && da.GetDataPresent("BinaryData")) { + System.IO.MemoryStream ms = (System.IO.MemoryStream)da.GetData("BinaryData"); + buffer = new byte[ms.Length]; + ms.Read(buffer, 0, buffer.Length); + } else if(da != null && da.GetDataPresent(typeof(string))) { + string sBuffer = (string)da.GetData(typeof(string)); + if(_keyInterpreter.GetType() == typeof(StringKeyInterpreter)) { + //When pasting in text view, assume any string is text + buffer = System.Text.Encoding.ASCII.GetBytes(sBuffer); + } else { + //When pasting in the hex view, assume any string is hex + string hexString = _nonHexRegex.Replace(sBuffer, ""); + if(hexString.Length % 2 != 0) { + hexString = "0" + hexString; + } + buffer = new byte[hexString.Length / 2]; + for(int i = 0; i < hexString.Length; i += 2) { + buffer[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16); + } + } + } else { + return; + } + + _byteProvider.WriteBytes(_bytePos, buffer); + SetPosition(_bytePos + buffer.Length, 0); + + ReleaseSelection(); + ScrollByteIntoView(); + UpdateCaret(); + Invalidate(); + } + + /// + /// Return true if Paste method could be invoked. + /// + public bool CanPaste() + { + if (ReadOnly || !this.Enabled) return false; + + IDataObject da = Clipboard.GetDataObject(); + if(da == null) { + return false; + } + if (da.GetDataPresent("BinaryData")) + return true; + else if (da.GetDataPresent(typeof(string))) + return true; + else + return false; + } + /// + /// Return true if PasteHex method could be invoked. + /// + public bool CanPasteHex() + { + if (!CanPaste()) return false; + + byte[] buffer = null; + IDataObject da = Clipboard.GetDataObject(); + if(da == null) { + return false; + } + if (da.GetDataPresent(typeof(string))) + { + string hexString = (string)da.GetData(typeof(string)); + buffer = ConvertHexToBytes(hexString); + return (buffer != null); + } + return false; + } + + /// + /// Replaces the current selection in the hex box with the hex string data of the Clipboard. + /// + public void PasteHex() + { + if (!CanPaste()) return; + + byte[] buffer = null; + IDataObject da = Clipboard.GetDataObject(); + if (da != null && da.GetDataPresent(typeof(string))) + { + string hexString = (string)da.GetData(typeof(string)); + buffer = ConvertHexToBytes(hexString); + if (buffer == null) + return; + } + else + { + return; + } + + if (_selectionLength > 0) + _byteProvider.DeleteBytes(_bytePos, _selectionLength); + + _byteProvider.InsertBytes(_bytePos, buffer); + + SetPosition(_bytePos + buffer.Length, 0); + + ReleaseSelection(); + ScrollByteIntoView(); + UpdateCaret(); + Invalidate(); + } + + /// + /// Copies the current selection in the hex box to the Clipboard in hex format. + /// + public void CopyHex() + { + if (!CanCopy()) return; + + // put bytes into buffer + byte[] buffer = GetCopyData(); + + DataObject da = new DataObject(); + + // set string buffer clipbard data + string hexString = ConvertBytesToHex(buffer); ; + da.SetData(typeof(string), hexString); + + //set memorystream (BinaryData) clipboard data + System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer, 0, buffer.Length, false, true); + da.SetData("BinaryData", ms); + + Clipboard.SetDataObject(da, true); + UpdateCaret(); + ScrollByteIntoView(); + Invalidate(); + + OnCopiedHex(EventArgs.Empty); + } + + + #endregion + + #region Paint methods + /// + /// Paints the background. + /// + /// A PaintEventArgs that contains the event data. + protected override void OnPaintBackground(PaintEventArgs e) + { + switch (_borderStyle) + { + case BorderStyle.Fixed3D: + { + if (TextBoxRenderer.IsSupported) + { + VisualStyleElement state = VisualStyleElement.TextBox.TextEdit.Normal; + Color backColor = this.BackColor; + + if (this.Enabled) + { + if (this.ReadOnly) + state = VisualStyleElement.TextBox.TextEdit.ReadOnly; + else if (this.Focused) + state = VisualStyleElement.TextBox.TextEdit.Focused; + } + else + { + state = VisualStyleElement.TextBox.TextEdit.Disabled; + backColor = this.BackColorDisabled; + } + + VisualStyleRenderer vsr = new VisualStyleRenderer(state); + vsr.DrawBackground(e.Graphics, this.ClientRectangle); + + Rectangle rectContent = vsr.GetBackgroundContentRectangle(e.Graphics, this.ClientRectangle); + e.Graphics.FillRectangle(new SolidBrush(backColor), rectContent); + } + else + { + // draw background + e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle); + + // draw default border + ControlPaint.DrawBorder3D(e.Graphics, ClientRectangle, Border3DStyle.Sunken); + } + + break; + } + case BorderStyle.FixedSingle: + { + // draw background + e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle); + + // draw fixed single border + ControlPaint.DrawBorder(e.Graphics, ClientRectangle, Color.Black, ButtonBorderStyle.Solid); + break; + } + default: + { + // draw background + e.Graphics.FillRectangle(new SolidBrush(BackColor), ClientRectangle); + break; + } + } + } + + /// + /// Paints the hex box. + /// + /// A PaintEventArgs that contains the event data. + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + if (_byteProvider == null) + return; + + System.Diagnostics.Debug.WriteLine("OnPaint " + DateTime.Now.ToString(), "HexBox"); + + // draw only in the content rectangle, so exclude the border and the scrollbar. + Region r = new Region(ClientRectangle); + r.Exclude(_recContent); + e.Graphics.ExcludeClip(r); + + e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.Default; + + UpdateVisibilityBytes(); + + if(_caretVisible && _keyInterpreter.GetType() != typeof(StringKeyInterpreter)) { + int caretWidth = (this.InsertActive) ? 1 : (int)_charSize.Width; + int caretHeight = (int)_charSize.Height; + e.Graphics.FillRectangle(Brushes.Yellow, _caretPos.X - 1, _caretPos.Y, caretWidth, caretHeight); + } + + PaintHexAndStringView(e.Graphics, _startByte, _endByte); + if (_shadowSelectionVisible && _stringViewVisible) + PaintCurrentBytesSign(e.Graphics); + + if(_lineInfoVisible) + PaintLineInfo(e.Graphics, _startByte, _endByte); + if(_columnInfoVisible) + PaintHeaderRow(e.Graphics); + + if(_groupSeparatorVisible) + PaintColumnSeparator(e.Graphics); + + if(_caretVisible && _keyInterpreter.GetType() != typeof(StringKeyInterpreter)) { + float caretWidth = (this.InsertActive) ? 1 : _charSize.Width; + float caretHeight = _charSize.Height; + e.Graphics.DrawRectangle(Pens.Gray, _caretPos.X - 1, _caretPos.Y, caretWidth, caretHeight); + } + } + + + void PaintLineInfo(Graphics g, long startByte, long endByte) + { + // Ensure endByte isn't > length of array. + endByte = Math.Min(_byteProvider.Length - 1, endByte); + + Color lineInfoColor = (this.InfoForeColor != Color.Empty) ? this.InfoForeColor : this.ForeColor; + + using(Brush backBrush = new SolidBrush(this.InfoBackColor)) { + using(Brush brush = new SolidBrush(lineInfoColor)) { + int maxLine = GetGridBytePoint(endByte - startByte).Y + 1; + + g.FillRectangle(backBrush, _recLineInfo.X-4, _recLineInfo.Y, _recLineInfo.Width, _recLineInfo.Height); + + Point gp = GetGridBytePoint(_bytePos - _startByte); + for(int i = 0; i < maxLine; i++) { + long firstLineByte = (startByte + (_iHexMaxHBytes) * i) + _lineInfoOffset; + + PointF bytePointF = GetBytePointF(new Point(0, 0 + i)); + string info = firstLineByte.ToString(_hexStringFormat, System.Threading.Thread.CurrentThread.CurrentCulture); + int nulls = 8 - info.Length; + string formattedInfo; + if(nulls > -1) { + formattedInfo = new string('0', LineInfoCharCount - info.Length) + info; + } else { + formattedInfo = new string('~', LineInfoCharCount); + } + + if(gp.Y == i && _highlightCurrentRowColumn && !(_keyInterpreter is StringKeyInterpreter)) { + using(SolidBrush highlightBrush = new SolidBrush(Color.FromArgb(15, 0, 0, 0))) { + g.FillRectangle(highlightBrush, _recHex.X, bytePointF.Y, _recHex.Width - (int)(_charSize.Width*2.5), _charSize.Height); + } + g.FillRectangle(Brushes.White, _recLineInfo.X - 4, bytePointF.Y, _recLineInfo.Width, _charSize.Height); + } + + g.DrawString(formattedInfo, Font, brush, new PointF(_recLineInfo.X, bytePointF.Y), _stringFormat); + } + } + } + } + + void PaintHeaderRow(Graphics g) + { + using(Brush brush = new SolidBrush(this.InfoForeColor)) { + using(Brush backBrush = new SolidBrush(this.InfoBackColor)) { + g.FillRectangle(backBrush, 0, 0, this.ClientRectangle.Width, _recLineInfo.Y); + + if(_highlightCurrentRowColumn && !(_keyInterpreter is StringKeyInterpreter)) { + Point gp = GetGridBytePoint(_bytePos - _startByte); + PointF bytePointF = GetBytePointF(gp); + float columnLeft = _recColumnInfo.Left + _charSize.Width * gp.X * 3 - _charSize.Width / 2; + using(SolidBrush highlightBrush = new SolidBrush(Color.FromArgb(15, 0, 0, 0))) { + g.FillRectangle(highlightBrush, columnLeft, _recHex.Y, _charSize.Width * 3, bytePointF.Y - _recHex.Y); + g.FillRectangle(highlightBrush, columnLeft, bytePointF.Y + _charSize.Height, _charSize.Width * 3, _recHex.Height - (bytePointF.Y - _recHex.Y) - _charSize.Height); + } + g.FillRectangle(Brushes.White, columnLeft, 0, _charSize.Width * 3, _recLineInfo.Y); + } + + for(int col = 0; col < _iHexMaxHBytes; col++) { + PaintColumnInfo(g, (byte)col, brush, col); + } + } + } + } + + void PaintColumnSeparator(Graphics g) + { + for (int col = GroupSize; col < _iHexMaxHBytes; col += GroupSize) + { + var pen = new Pen(new SolidBrush(this.InfoForeColor), 1); + PointF headerPointF = GetColumnInfoPointF(col); + headerPointF.X -= _charSize.Width / 2; + g.DrawLine(pen, headerPointF, new PointF(headerPointF.X, headerPointF.Y + _recColumnInfo.Height + _recHex.Height)); + if (StringViewVisible) + { + PointF byteStringPointF = GetByteStringPointF(new Point(col, 0)); + headerPointF.X -= 2; + g.DrawLine(pen, new PointF(byteStringPointF.X, byteStringPointF.Y), new PointF(byteStringPointF.X, byteStringPointF.Y + _recHex.Height)); + } + } + } + + void PaintHexString(Graphics g, string hexString, Brush brush, Point gridPoint) + { + PointF bytePointF = GetBytePointF(gridPoint); + g.DrawString(hexString, Font, brush, bytePointF, _stringFormat); + } + + void PaintColumnInfo(Graphics g, byte b, Brush brush, int col) + { + PointF headerPointF = GetColumnInfoPointF(col); + + string sB = ConvertByteToHex(b); + + g.DrawString(sB.Substring(0, 1), Font, brush, headerPointF, _stringFormat); + headerPointF.X += _charSize.Width; + g.DrawString(sB.Substring(1, 1), Font, brush, headerPointF, _stringFormat); + } + + void PaintHexStringSelected(Graphics g, string hexString, Brush brush, Brush brushBack, Point gridPoint, Color borderColor) + { + PointF bytePointF = GetBytePointF(gridPoint); + + float width = hexString.Length * _charSize.Width; + float xPos = bytePointF.X - _charSize.Width / 2; + + float offset = HighDensityMode ? _highDensityModeOffset : 0; + g.FillRectangle(brushBack, xPos, bytePointF.Y + offset, width, _charSize.Height); + + if(!borderColor.IsEmpty) { + using(Pen p = new Pen(borderColor, 2)) { + g.DrawRectangle(p, xPos, bytePointF.Y + offset, width - 1, _charSize.Height - 1); + } + } + + if(_selectionLength == 0 && _caretPos.Y == bytePointF.Y && _caretPos.X >= bytePointF.X && _caretPos.X <= bytePointF.X + width) { + if(_caretVisible && _keyInterpreter.GetType() != typeof(StringKeyInterpreter)) { + //Redraw caret over background color + float caretWidth = (this.InsertActive) ? 1 : _charSize.Width; + float caretHeight = (int)_charSize.Height; + g.FillRectangle(Brushes.Yellow, _caretPos.X - 1, _caretPos.Y, caretWidth, caretHeight); + } + } + g.DrawString(hexString, Font, brush, bytePointF, _stringFormat); + } + + Dictionary _lineWidthCache = new Dictionary(); + void PaintHexAndStringView(Graphics g, long startByte, long endByte) + { + int counter = -1; + long intern_endByte = Math.Min(_byteProvider.Length - 1, endByte + _iHexMaxHBytes); + + int yPrevious = -1; + float xOffset = 0; + Color defaultForeColor = this.ForeColor; + _lineWidthCache.Clear(); + for(int i = 0; i < _xPosCache.Length; i++) { + _xPosCache[i] = -1; + } + _xPosList.Clear(); + float xPrevious = 0; + + Point? caretPoint = null; + if(_caretVisible && _keyInterpreter.GetType() == typeof(StringKeyInterpreter)) { + long byteIndex = _bytePos - _startByte; + caretPoint = GetGridBytePoint(byteIndex); + } + + int skipCount = 0; + if(this.ByteColorProvider != null) { + this.ByteColorProvider.Prepare(_startByte, intern_endByte); + } + + bool forceDraw = false; + float prevXOffset = 0; + bool prevSelected = false; + + ByteColors defaultColors = new ByteColors() { + ForeColor = defaultForeColor, + BackColor = Color.Transparent, + BorderColor = Color.Transparent + }; + + ByteColors prevColors = new ByteColors() { + ForeColor = defaultForeColor, + BackColor = Color.Transparent, + BorderColor = Color.Transparent + }; + + Point gridPoint = GetGridBytePoint(0); + List bytesToDisplay = new List(); + string stringToDisplay = string.Empty; + float caretWidth = 0; + Action outputHex = (Color byteColor, Color bgColor, Color borderColor) => { + StringBuilder sb = new StringBuilder(); + foreach(byte b in bytesToDisplay) { + sb.Append(b.ToString("X2")); + sb.Append(" "); + } + + PointF bytePointF = GetBytePointF(gridPoint); + PointF byteStringPointF = GetByteStringPointF(gridPoint, false); + float xPos = byteStringPointF.X + prevXOffset; + + //String view caret + Action drawCaret = () => { + float yPos = bytePointF.Y + (HighDensityMode ? _highDensityModeOffset : 0); + if(_selectionLength == 0 && _caretPos.Y == yPos && _caretPos.X >= xPos) { + if(_caretVisible && _keyInterpreter.GetType() == typeof(StringKeyInterpreter)) { + g.FillRectangle(Brushes.Yellow, _caretPos.X, _caretPos.Y, this.InsertActive ? 1 : caretWidth, _charSize.Height); + g.DrawRectangle(Pens.Gray, _caretPos.X, _caretPos.Y, this.InsertActive ? 1 : caretWidth, _charSize.Height); + } + } + }; + + using(Brush fgBrush = new SolidBrush(byteColor)) { + if(bgColor != Color.Transparent || !borderColor.IsEmpty) { + using(Brush bgBrush = new SolidBrush(bgColor)) { + PaintHexStringSelected(g, sb.ToString(), fgBrush, bgBrush, gridPoint, borderColor); + + //Draw string view + SizeF stringSize = g.MeasureString(stringToDisplay, Font, 1000, _stringFormat); + float yPos = bytePointF.Y + (HighDensityMode ? _highDensityModeOffset : 0); + g.FillRectangle(bgBrush, xPos, yPos, stringSize.Width, _charSize.Height); + + using(Pen p = new Pen(borderColor, 2)) { + g.DrawRectangle(p, xPos, yPos, stringSize.Width - 1, _charSize.Height - 1); + } + + drawCaret(); + g.DrawString(stringToDisplay, Font, fgBrush, new PointF(xPos, bytePointF.Y), _stringFormat); + } + } else { + PaintHexString(g, sb.ToString(), fgBrush, gridPoint); + + //Draw string view + drawCaret(); + g.DrawString(stringToDisplay, Font, fgBrush, new PointF(xPos, bytePointF.Y), _stringFormat); + } + } + + bytesToDisplay = new List(); + stringToDisplay = string.Empty; + }; + + for(long i = startByte; i < intern_endByte + 1; i++) { + ByteColors colors; + bool isSelectedByte = i >= _bytePos && i <= (_bytePos + _selectionLength - 1) && _selectionLength != 0; + if(this.ByteColorProvider != null) { + colors = this.ByteColorProvider.GetByteColor(_startByte, i); + } else { + colors = defaultColors; + } + + counter++; + + Point currentPoint = GetGridBytePoint(counter); + + bool lineChanged = false; + if(yPrevious != currentPoint.Y) { + if(_xPosList.ContainsKey(yPrevious)) { + _xPosList[yPrevious].Add(xPrevious); + } + _xPosList[currentPoint.Y] = new List(); + _lineWidthCache[yPrevious] = _recStringView.Width + xOffset; + xOffset = 0; + yPrevious = currentPoint.Y; + lineChanged = true; + } + + if(forceDraw || lineChanged || colors.ForeColor != prevColors.ForeColor || colors.BackColor != prevColors.BackColor || colors.BorderColor != prevColors.BorderColor || prevSelected != isSelectedByte) { + outputHex(this.ByteColorProvider != null ? prevColors.ForeColor : (prevSelected ? _selectionForeColor : defaultForeColor), prevSelected ? _selectionBackColor : prevColors.BackColor, prevColors.BorderColor); + gridPoint = GetGridBytePoint(counter); + prevXOffset = xOffset; + forceDraw = false; + } + + byte b = _byteProvider.ReadByte(i); + bytesToDisplay.Add(b); + + prevSelected = isSelectedByte; + prevColors.ForeColor = colors.ForeColor; + prevColors.BackColor = colors.BackColor; + prevColors.BorderColor = colors.BorderColor; + + if(!_stringViewVisible) { + continue; + } + + string s; + if(skipCount > 0) { + skipCount--; + s = ""; + } else { + long len = _byteProvider.Length; + UInt64 tblValue = (UInt64)b; + for(int j = 1; j < 8; j++) { + if(len > i + j) { + tblValue += (UInt64)_byteProvider.ReadByte(i + j) << (8 * j); + } + } + + int keyLength; + s = ByteCharConverter.ToChar(tblValue, out keyLength); + skipCount = keyLength - 1; + } + + float width; + if(!_measureCache.TryGetValue(s, out width)) { + width = g.MeasureString(s, Font, 1000, _stringFormat).Width; + _measureCache[s] = width; + } + + PointF byteStringPointF = GetByteStringPointF(currentPoint, false); + float xPos = byteStringPointF.X + xOffset; + if(currentPoint.Y < 150) { + _xPosCache[currentPoint.Y * 64 + currentPoint.X] = xPos; + } + _xPosList[currentPoint.Y].Add(xPos); + + if(currentPoint == caretPoint) { + caretWidth = width; + } + + stringToDisplay += s; + + if(s.Length > 1 || (s.Length > 0 && (s[0] > 127 || s[0] < 32))) { + //Force draw if we hit a non-ascii character (to avoid minor positioning issues) + forceDraw = true; + } + + xOffset += width - _charSize.Width; + xPrevious = xPos + width; + } + outputHex(this.ByteColorProvider != null ? prevColors.ForeColor : (prevSelected ? _selectionForeColor : defaultForeColor), prevSelected ? _selectionBackColor : prevColors.BackColor, prevColors.BorderColor); + } + + float GetLineWidth(int y) + { + float width; + if(_lineWidthCache.TryGetValue(y, out width)) { + return width; + } else { + return _recStringView.Width; + } + } + + void PaintCurrentBytesSign(Graphics g) + { + if(_selectionLength == 0 && _keyInterpreter != null && _bytePos != -1 && Enabled) { + if(_keyInterpreter.GetType() == typeof(KeyInterpreter)) { + Point gp = GetGridBytePoint(_bytePos - _startByte); + PointF pf = GetByteStringPointF(gp); + Point gp2 = GetGridBytePoint(_bytePos - _startByte + 1); + PointF pf2 = GetByteStringPointF(gp2); + + Size s; + if(gp.X > gp2.X) { + s = new Size((int)(GetLineWidth(gp.Y) - (pf.X - _recStringView.X)), (int)_charSize.Height); + } else { + s = new Size((int)(pf2.X - pf.X), (int)_charSize.Height); + } + Rectangle r = new Rectangle((int)pf.X, (int)pf.Y, s.Width, s.Height); + PaintCurrentByteSign(g, r); + } else { + Point gp = GetGridBytePoint(_bytePos - _startByte); + PointF pf = GetBytePointF(gp); + Size s = new Size((int)(_charSize.Width * 2), (int)_charSize.Height); + Rectangle r = new Rectangle((int)pf.X, (int)pf.Y, s.Width, s.Height); + PaintCurrentByteSign(g, r); + } + } + } + + Dictionary _shadowSelectionCache = new Dictionary(); + void PaintCurrentByteSign(Graphics g, Rectangle rec) + { + // stack overflowexception on big files - workaround + if(rec.Top < 0 || rec.Left < 0 || rec.Width <= 0 || rec.Height <= 0) { + return; + } + + + Point dimensions = new Point(rec.Width, rec.Height); + Bitmap bitmap; + if(!_shadowSelectionCache.TryGetValue(dimensions, out bitmap)) { + bitmap = new Bitmap(rec.Width, rec.Height); + using(Graphics bitmapGraphics = Graphics.FromImage(bitmap)) { + using(SolidBrush shadowSelectionBrush = new SolidBrush(_shadowSelectionColor)) { + bitmapGraphics.FillRectangle(shadowSelectionBrush, 0, 0, rec.Width, rec.Height); + _shadowSelectionCache[dimensions] = bitmap; + } + } + } + + g.DrawImage(bitmap, rec.Left, rec.Top); + } + + Color GetDefaultForeColor() + { + if (Enabled) + return ForeColor; + else + return Color.Gray; + } + void UpdateVisibilityBytes() + { + if (_byteProvider == null || _byteProvider.Length == 0) + return; + + _startByte = (_scrollVpos + 1) * _iHexMaxHBytes - _iHexMaxHBytes; + _endByte = (long)Math.Min(_byteProvider.Length - 1, _startByte + _iHexMaxBytes); + } + #endregion + + public int LineInfoCharCount + { + get + { + if(this.ByteProvider == null || this.ByteProvider.Length > 0x10000000) { + return 8; + } else if(this.ByteProvider.Length > 0x1000000) { + return 7; + } else if(this.ByteProvider.Length > 0x100000) { + return 6; + } else if(this.ByteProvider.Length > 0x10000) { + return 5; + } else { + return 4; + } + } + } + + #region Positioning methods + void UpdateRectanglePositioning() + { + // calc char size + SizeF charSize; + using (var graphics = this.CreateGraphics()) + { + charSize = this.CreateGraphics().MeasureString("A", Font, 100, _stringFormat); + } + CharSize = new SizeF((float)charSize.Width, (float)Math.Ceiling(charSize.Height * 100 / (this.HighDensityMode ? 133 : 100))); + _highDensityModeOffset = ((float)Math.Ceiling(charSize.Height) - CharSize.Height) / 2; + + int requiredWidth = 0; + + // calc content bounds + _recContent = ClientRectangle; + _recContent.X += _recBorderLeft; + _recContent.Y += _recBorderTop; + _recContent.Width -= _recBorderRight + _recBorderLeft; + _recContent.Height -= _recBorderBottom + _recBorderTop; + + if (_vScrollBarVisible) + { + _recContent.Width -= _vScrollBar.Width; + _vScrollBar.Left = _recContent.X + _recContent.Width; + _vScrollBar.Top = _recContent.Y; + _vScrollBar.Height = _recContent.Height; + requiredWidth += _vScrollBar.Width; + } + + int marginLeft = 4; + + // calc line info bounds + if (_lineInfoVisible) + { + _recLineInfo = new Rectangle(_recContent.X + marginLeft, + _recContent.Y, + (int)(_charSize.Width * (LineInfoCharCount + 1)), + _recContent.Height); + requiredWidth += _recLineInfo.Width + marginLeft; + } + else + { + _recLineInfo = Rectangle.Empty; + _recLineInfo.X = marginLeft; + requiredWidth += marginLeft; + } + + // calc line info bounds + _recColumnInfo = new Rectangle(_recLineInfo.X + _recLineInfo.Width, _recContent.Y, _recContent.Width - _recLineInfo.Width, (int)charSize.Height + 4); + if (_columnInfoVisible) + { + _recLineInfo.Y += (int)charSize.Height + 4; + _recLineInfo.Height -= (int)charSize.Height + 4; + } + else + { + _recColumnInfo.Height = 0; + } + + // calc hex bounds and grid + _recHex = new Rectangle(_recLineInfo.X + _recLineInfo.Width, + _recLineInfo.Y, + _recContent.Width - _recLineInfo.Width, + _recContent.Height - _recColumnInfo.Height); + + if (UseFixedBytesPerLine) + { + SetHorizontalByteCount(_bytesPerLine); + _recHex.Width = (int)Math.Floor(((double)_iHexMaxHBytes) * _charSize.Width * 3 + (2 * _charSize.Width)); + requiredWidth += _recHex.Width; + } + else + { + int hmax = (int)Math.Floor((double)_recHex.Width / (double)_charSize.Width); + if (_stringViewVisible) + { + hmax -= 2; + if (hmax > 1) + SetHorizontalByteCount((int)Math.Floor((double)hmax / 4)); + else + SetHorizontalByteCount(1); + } + else + { + if (hmax > 1) + SetHorizontalByteCount((int)Math.Floor((double)hmax / 3)); + else + SetHorizontalByteCount(1); + } + _recHex.Width = (int)Math.Floor(((double)_iHexMaxHBytes) * _charSize.Width * 3 + (2 * _charSize.Width)); + requiredWidth += _recHex.Width; + } + + if (_stringViewVisible) + { + _recStringView = new Rectangle(_recHex.X + _recHex.Width, + _recHex.Y, + (int)(_charSize.Width * _iHexMaxHBytes), + _recHex.Height); + requiredWidth += _recStringView.Width + 40; + } + else + { + _recStringView = Rectangle.Empty; + } + + RequiredWidth = requiredWidth; + + int vmax = (int)Math.Floor((double)_recHex.Height / (double)_charSize.Height); + SetVerticalByteCount(vmax > 0 ? vmax : 1); + + _iHexMaxBytes = _iHexMaxHBytes * _iHexMaxVBytes; + + UpdateScrollSize(); + } + + public Point GetBytePosition(long byteIndex) + { + if(byteIndex < _startByte) { + return Point.Empty; + } + + Point gp = GetGridBytePoint(byteIndex - _startByte); + PointF pos = GetBytePointF(gp); + return this.PointToScreen(new Point((int)pos.X, (int)pos.Y)); + } + + PointF GetBytePointF(long byteIndex) + { + Point gp = GetGridBytePoint(byteIndex); + + return GetBytePointF(gp); + } + + PointF GetBytePointF(Point gp) + { + float x = (3 * _charSize.Width) * gp.X + _recHex.X; + float y = (gp.Y + 1) * _charSize.Height - _charSize.Height + _recHex.Y; + + return new PointF(x, y); + } + PointF GetColumnInfoPointF(int col) + { + Point gp = GetGridBytePoint(col); + float x = (3 * _charSize.Width) * gp.X + _recColumnInfo.X; + float y = _recColumnInfo.Y; + + return new PointF(x, y); + } + + float[] _xPosCache = new float[64 * 150]; + Dictionary> _xPosList = new Dictionary>(); + PointF GetByteStringPointF(Point gp, bool useCache = true) + { + float x = (_charSize.Width) * gp.X + _recStringView.X; + float y = (gp.Y + 1) * _charSize.Height - _charSize.Height + _recStringView.Y; + + float cachedXPos = gp.Y > 0 && gp.Y < 150 ? _xPosCache[gp.Y * 64 + gp.X] : -1; + + if(useCache && cachedXPos >= 0) { + return new PointF(cachedXPos, y); + } else { + return new PointF(x, y); + } + } + + Point GetGridBytePoint(long byteIndex) + { + int row = (int)Math.Floor((double)byteIndex / (double)_iHexMaxHBytes); + int column = (int)(byteIndex + _iHexMaxHBytes - _iHexMaxHBytes * (row + 1)); + + Point res = new Point(column, row); + return res; + } + #endregion + + #region Overridden properties + /// + /// Gets or sets the background color for the control. + /// + [DefaultValue(typeof(Color), "White")] + public override Color BackColor + { + get + { + return base.BackColor; + } + set + { + base.BackColor = value; + } + } + + /// + /// The font used to display text in the hexbox. + /// + public override Font Font + { + get + { + return base.Font; + } + set + { + if(value == null) + return; + + base.Font = value; + this.UpdateRectanglePositioning(); + this.UpdateCaret(); + this.Invalidate(); + + _measureCache = new Dictionary(); + } + } + Dictionary _measureCache = new Dictionary(); + + /// + /// Not used. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override string Text + { + get + { + return base.Text; + } + set + { + base.Text = value; + } + } + + /// + /// Not used. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never), Bindable(false)] + public override RightToLeft RightToLeft + { + get + { + return base.RightToLeft; + } + set + { + base.RightToLeft = value; + } + } + #endregion + + #region Properties + + public bool ByteEditingMode { get; set; } = false; + + /// + /// Gets or sets the background color for the disabled control. + /// + [Category("Appearance"), DefaultValue(typeof(Color), "WhiteSmoke")] + public Color BackColorDisabled + { + get + { + return _backColorDisabled; + } + set + { + _backColorDisabled = value; + } + } Color _backColorDisabled = Color.FromName("WhiteSmoke"); + + /// + /// Gets or sets if the count of bytes in one line is fix. + /// + /// + /// When set to True, BytesPerLine property determine the maximum count of bytes in one line. + /// + [DefaultValue(false), Category("Hex"), Description("Gets or sets if the count of bytes in one line is fix.")] + public bool ReadOnly + { + get { return _readOnly; } + set + { + if (_readOnly == value) + return; + + _readOnly = value; + OnReadOnlyChanged(EventArgs.Empty); + Invalidate(); + } + } bool _readOnly; + + /// + /// Gets or sets the maximum count of bytes in one line. + /// + /// + /// UseFixedBytesPerLine property no longer has to be set to true for this to work + /// + [DefaultValue(16), Category("Hex"), Description("Gets or sets the maximum count of bytes in one line.")] + public int BytesPerLine + { + get { return _bytesPerLine; } + set + { + if (_bytesPerLine == value) + return; + + _bytesPerLine = value; + OnBytesPerLineChanged(EventArgs.Empty); + + UpdateRectanglePositioning(); + Invalidate(); + } + } int _bytesPerLine = 16; + + /// + /// Gets or sets the number of bytes in a group. Used to show the group separator line (if GroupSeparatorVisible is true) + /// + /// + /// GroupSeparatorVisible property must set to true + /// + [DefaultValue(4), Category("Hex"), Description("Gets or sets the byte-count between group separators (if visible).")] + public int GroupSize + { + get { return _groupSize; } + set + { + if (_groupSize == value) + return; + + _groupSize = value; + OnGroupSizeChanged(EventArgs.Empty); + + UpdateRectanglePositioning(); + Invalidate(); + } + } int _groupSize = 4; + /// + /// Gets or sets if the count of bytes in one line is fix. + /// + /// + /// When set to True, BytesPerLine property determine the maximum count of bytes in one line. + /// + [DefaultValue(false), Category("Hex"), Description("Gets or sets if the count of bytes in one line is fix.")] + public bool UseFixedBytesPerLine + { + get { return _useFixedBytesPerLine; } + set + { + if (_useFixedBytesPerLine == value) + return; + + _useFixedBytesPerLine = value; + OnUseFixedBytesPerLineChanged(EventArgs.Empty); + + UpdateRectanglePositioning(); + Invalidate(); + } + } bool _useFixedBytesPerLine; + + /// + /// Gets or sets the visibility of a vertical scroll bar. + /// + [DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of a vertical scroll bar.")] + public bool VScrollBarVisible + { + get { return this._vScrollBarVisible; } + set + { + if (_vScrollBarVisible == value) + return; + + _vScrollBarVisible = value; + + if (_vScrollBarVisible) + Controls.Add(_vScrollBar); + else + Controls.Remove(_vScrollBar); + + UpdateRectanglePositioning(); + UpdateScrollSize(); + + OnVScrollBarVisibleChanged(EventArgs.Empty); + } + } bool _vScrollBarVisible; + + /// + /// Gets or sets the ByteProvider. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IByteProvider ByteProvider + { + get { return _byteProvider; } + set + { + bool sameLength = value != null && _byteProvider != null && _byteProvider.Length == value.Length; + if (_byteProvider == value) + return; + + ActivateKeyInterpreter(); + + if (_byteProvider != null) + _byteProvider.LengthChanged -= new EventHandler(_byteProvider_LengthChanged); + + _byteProvider = value; + if (_byteProvider != null) + _byteProvider.LengthChanged += new EventHandler(_byteProvider_LengthChanged); + + OnByteProviderChanged(EventArgs.Empty); + + if (value == null) // do not raise events if value is null + { + _bytePos = -1; + _byteCharacterPos = 0; + _selectionLength = 0; + + DestroyCaret(); + } + else + { + if(!sameLength) { + SetPosition(0, 0); + SetSelectionLength(0); + } + } + + CheckCurrentLineChanged(); + CheckCurrentPositionInLineChanged(); + + if(!sameLength) { + _scrollVpos = 0; + } + + UpdateVisibilityBytes(); + UpdateRectanglePositioning(); + + if(_caretVisible) + UpdateCaret(); + else + CreateCaret(); + + Invalidate(); + } + } + + IByteProvider _byteProvider; + IByteColorProvider _byteColorProvider; + + public IByteColorProvider ByteColorProvider + { + get { return _byteColorProvider; } + set { _byteColorProvider = value; Invalidate(); } + } + + /// + /// Gets or sets the visibility of the group separator. + /// + [DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of a separator vertical line.")] + public bool GroupSeparatorVisible + { + get { return _groupSeparatorVisible; } + set + { + if (_groupSeparatorVisible == value) + return; + + _groupSeparatorVisible = value; + OnGroupSeparatorVisibleChanged(EventArgs.Empty); + + UpdateRectanglePositioning(); + Invalidate(); + } + } bool _groupSeparatorVisible = false; + + /// + /// Gets or sets the visibility of the column info + /// + [DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of header row.")] + public bool ColumnInfoVisible + { + get { return _columnInfoVisible; } + set + { + if (_columnInfoVisible == value) + return; + + _columnInfoVisible = value; + OnColumnInfoVisibleChanged(EventArgs.Empty); + + UpdateRectanglePositioning(); + Invalidate(); + } + } bool _columnInfoVisible = false; + + /// + /// Gets or sets the visibility of a line info. + /// + [DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of a line info.")] + public bool LineInfoVisible + { + get { return _lineInfoVisible; } + set + { + if (_lineInfoVisible == value) + return; + + _lineInfoVisible = value; + OnLineInfoVisibleChanged(EventArgs.Empty); + + UpdateRectanglePositioning(); + Invalidate(); + } + } bool _lineInfoVisible = false; + + /// + /// Gets or sets the offset of a line info. + /// + [DefaultValue((long)0), Category("Hex"), Description("Gets or sets the offset of the line info.")] + public long LineInfoOffset + { + get { return _lineInfoOffset; } + set + { + if (_lineInfoOffset == value) + return; + + _lineInfoOffset = value; + + Invalidate(); + } + } long _lineInfoOffset = 0; + + /// + /// Gets or sets the hex box´s border style. + /// + [DefaultValue(typeof(BorderStyle), "Fixed3D"), Category("Hex"), Description("Gets or sets the hex box´s border style.")] + public BorderStyle BorderStyle + { + get { return _borderStyle; } + set + { + if (_borderStyle == value) + return; + + _borderStyle = value; + switch (_borderStyle) + { + case BorderStyle.None: + _recBorderLeft = _recBorderTop = _recBorderRight = _recBorderBottom = 0; + break; + case BorderStyle.Fixed3D: + _recBorderLeft = _recBorderRight = SystemInformation.Border3DSize.Width; + _recBorderTop = _recBorderBottom = SystemInformation.Border3DSize.Height; + break; + case BorderStyle.FixedSingle: + _recBorderLeft = _recBorderTop = _recBorderRight = _recBorderBottom = 1; + break; + } + + UpdateRectanglePositioning(); + + OnBorderStyleChanged(EventArgs.Empty); + + } + } BorderStyle _borderStyle = BorderStyle.Fixed3D; + + /// + /// Gets or sets the visibility of the string view. + /// + [DefaultValue(false), Category("Hex"), Description("Gets or sets the visibility of the string view.")] + public bool StringViewVisible + { + get { return _stringViewVisible; } + set + { + if (_stringViewVisible == value) + return; + + _stringViewVisible = value; + OnStringViewVisibleChanged(EventArgs.Empty); + + UpdateRectanglePositioning(); + Invalidate(); + } + } bool _stringViewVisible; + + /// + /// Gets or sets whether the HexBox control displays the hex characters in upper or lower case. + /// + [DefaultValue(typeof(HexCasing), "Upper"), Category("Hex"), Description("Gets or sets whether the HexBox control displays the hex characters in upper or lower case.")] + public HexCasing HexCasing + { + get + { + if (_hexStringFormat == "X") + return HexCasing.Upper; + else + return HexCasing.Lower; + } + set + { + string format; + if (value == HexCasing.Upper) + format = "X"; + else + format = "x"; + + if (_hexStringFormat == format) + return; + + _hexStringFormat = format; + OnHexCasingChanged(EventArgs.Empty); + + Invalidate(); + } + } + + /// + /// Gets and sets the starting point of the bytes selected in the hex box. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public long SelectionStart + { + get { return _bytePos; } + set + { + SetPosition(value, 0); + ScrollByteIntoView(); + Invalidate(); + } + } + + /// + /// Gets and sets the number of bytes selected in the hex box. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public long SelectionLength + { + get { return _selectionLength; } + set + { + SetSelectionLength(value); + ScrollByteIntoView(); + Invalidate(); + } + } long _selectionLength; + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool HighlightCurrentRowColumn + { + get { return _highlightCurrentRowColumn; } + set + { + _highlightCurrentRowColumn = value; + Invalidate(); + } + } + bool _highlightCurrentRowColumn; + + /// + /// Gets or sets the info color used for column info and line info. When this property is null, then ForeColor property is used. + /// + [DefaultValue(typeof(Color), "Gray"), Category("Hex"), Description("Gets or sets the line info color. When this property is null, then ForeColor property is used.")] + public Color InfoForeColor + { + get { return _infoForeColor; } + set { _infoForeColor = value; Invalidate(); } + } Color _infoForeColor = Color.Gray; + + /// + /// Gets or sets the info color used for column info and line info. When this property is null, then ForeColor property is used. + /// + [DefaultValue(typeof(Color), "Gray"), Category("Hex"), Description("Gets or sets the line info color. When this property is null, then ForeColor property is used.")] + public Color InfoBackColor + { + get { return _infoBackColor; } + set { _infoBackColor = value; Invalidate(); } + } + Color _infoBackColor = Color.DarkGray; + + /// + /// Gets or sets the background color for the selected bytes. + /// + [DefaultValue(typeof(Color), "Blue"), Category("Hex"), Description("Gets or sets the background color for the selected bytes.")] + public Color SelectionBackColor + { + get { return _selectionBackColor; } + set { _selectionBackColor = value; Invalidate(); } + } Color _selectionBackColor = Color.Blue; + + /// + /// Gets or sets the foreground color for the selected bytes. + /// + [DefaultValue(typeof(Color), "White"), Category("Hex"), Description("Gets or sets the foreground color for the selected bytes.")] + public Color SelectionForeColor + { + get { return _selectionForeColor; } + set { _selectionForeColor = value; Invalidate(); } + } Color _selectionForeColor = Color.White; + + /// + /// Gets or sets the visibility of a shadow selection. + /// + [DefaultValue(true), Category("Hex"), Description("Gets or sets the visibility of a shadow selection.")] + public bool ShadowSelectionVisible + { + get { return _shadowSelectionVisible; } + set + { + if (_shadowSelectionVisible == value) + return; + _shadowSelectionVisible = value; + Invalidate(); + } + } bool _shadowSelectionVisible = true; + + /// + /// Gets or sets the color of the shadow selection. + /// + /// + /// A alpha component must be given! + /// Default alpha = 100 + /// + [Category("Hex"), Description("Gets or sets the color of the shadow selection.")] + public Color ShadowSelectionColor + { + get { return _shadowSelectionColor; } + set { _shadowSelectionColor = value; Invalidate(); } + } Color _shadowSelectionColor = Color.FromArgb(100, 60, 188, 255); + + /// + /// Contains the size of a single character in pixel + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public SizeF CharSize + { + get { return _charSize; } + private set + { + if (_charSize == value) + return; + _charSize = value; + if (CharSizeChanged != null) + CharSizeChanged(this, EventArgs.Empty); + } + } SizeF _charSize; + + public bool HighDensityMode + { + get { return _highDensityMode; } + set + { + if (_highDensityMode == value) + return; + _highDensityMode = value; + this.UpdateCaret(); + this.Invalidate(); + this.UpdateRectanglePositioning(); + } + } bool _highDensityMode; + float _highDensityModeOffset = 0f; + + public bool EnablePerByteNavigation { get; set; } + + /// + /// Gets the width required for the content + /// + [DefaultValue(0), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int RequiredWidth + { + get { return _requiredWidth; } + private set + { + if (_requiredWidth == value) + return; + _requiredWidth = value; + if (RequiredWidthChanged != null) + RequiredWidthChanged(this, EventArgs.Empty); + } + } int _requiredWidth; + + /// + /// Gets the number bytes drawn horizontally. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int HorizontalByteCount + { + get { return _iHexMaxHBytes; } + } + + /// + /// Gets the number bytes drawn vertically. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int VerticalByteCount + { + get { return _iHexMaxVBytes; } + } + + /// + /// Gets the current line + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public long CurrentLine + { + get { return _currentLine; } + } long _currentLine; + + /// + /// Gets the current position in the current line + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public long CurrentPositionInLine + { + get { return _currentPositionInLine; } + } int _currentPositionInLine; + + /// + /// Gets the a value if insertion mode is active or not. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool InsertActive + { + get { return _insertActive; } + set + { + if (_insertActive == value) + return; + + _insertActive = value; + + // recreate caret + DestroyCaret(); + CreateCaret(); + + // raise change event + OnInsertActiveChanged(EventArgs.Empty); + } + } + + /// + /// Gets or sets the built-in context menu. + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public BuiltInContextMenu BuiltInContextMenu + { + get { return _builtInContextMenu; } + } BuiltInContextMenu _builtInContextMenu; + + + /// + /// Gets or sets the converter that will translate between byte and character values. + /// + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IByteCharConverter ByteCharConverter + { + get + { + if (_byteCharConverter == null) + _byteCharConverter = new DefaultByteCharConverter(); + return _byteCharConverter; + } + set + { + if (value != _byteCharConverter) + { + _byteCharConverter = value; + Invalidate(); + } + } + } IByteCharConverter _byteCharConverter; + + #endregion + + #region Misc + /// + /// Converts a byte array to a hex string. For example: {10,11} = "0A 0B" + /// + /// the byte array + /// the hex string + string ConvertBytesToHex(byte[] data) + { + StringBuilder sb = new StringBuilder(); + foreach (byte b in data) + { + string hex = ConvertByteToHex(b); + sb.Append(hex); + sb.Append(" "); + } + if (sb.Length > 0) + sb.Remove(sb.Length - 1, 1); + string result = sb.ToString(); + return result; + } + /// + /// Converts the byte to a hex string. For example: "10" = "0A"; + /// + /// the byte to format + /// the hex string + string ConvertByteToHex(byte b) + { + string sB = b.ToString(_hexStringFormat, System.Threading.Thread.CurrentThread.CurrentCulture); + if (sB.Length == 1) + sB = "0" + sB; + return sB; + } + /// + /// Converts the hex string to an byte array. The hex string must be separated by a space char ' '. If there is any invalid hex information in the string the result will be null. + /// + /// the hex string separated by ' '. For example: "0A 0B 0C" + /// the byte array. null if hex is invalid or empty + byte[] ConvertHexToBytes(string hex) + { + if (string.IsNullOrEmpty(hex)) + return null; + hex = hex.Trim(); + var hexArray = hex.Split(' '); + var byteArray = new byte[hexArray.Length]; + + for (int i = 0; i < hexArray.Length; i++) + { + var hexValue = hexArray[i]; + + byte b; + var isByte = ConvertHexToByte(hexValue, out b); + if (!isByte) + return null; + byteArray[i] = b; + } + + return byteArray; + } + + bool ConvertHexToByte(string hex, out byte b) + { + bool isByte = byte.TryParse(hex, System.Globalization.NumberStyles.HexNumber, System.Threading.Thread.CurrentThread.CurrentCulture, out b); + return isByte; + } + + void SetPosition(long bytePos) + { + SetPosition(bytePos, _byteCharacterPos); + } + + void SetPosition(long bytePos, int byteCharacterPos) + { + if(bytePos >= this._byteProvider.Length) { + bytePos = this._byteProvider.Length - 1; + } + + if (_byteCharacterPos != byteCharacterPos) + { + _byteCharacterPos = byteCharacterPos; + } + + if (bytePos != _bytePos) + { + _byteProvider.CommitWriteByte(); + _bytePos = bytePos; + CheckCurrentLineChanged(); + CheckCurrentPositionInLineChanged(); + + OnSelectionStartChanged(EventArgs.Empty); + } + } + + void SetSelectionLength(long selectionLength) + { + if (selectionLength != _selectionLength) + { + _selectionLength = selectionLength; + OnSelectionLengthChanged(EventArgs.Empty); + } + } + + void SetHorizontalByteCount(int value) + { + if (_iHexMaxHBytes == value) + return; + + _iHexMaxHBytes = value; + OnHorizontalByteCountChanged(EventArgs.Empty); + } + + void SetVerticalByteCount(int value) + { + if (_iHexMaxVBytes == value) + return; + + _iHexMaxVBytes = value; + OnVerticalByteCountChanged(EventArgs.Empty); + } + + void CheckCurrentLineChanged() + { + long currentLine = (long)Math.Floor((double)_bytePos / (double)_iHexMaxHBytes) + 1; + + if (_byteProvider == null && _currentLine != 0) + { + _currentLine = 0; + OnCurrentLineChanged(EventArgs.Empty); + } + else if (currentLine != _currentLine) + { + _currentLine = currentLine; + OnCurrentLineChanged(EventArgs.Empty); + } + } + + void CheckCurrentPositionInLineChanged() + { + Point gb = GetGridBytePoint(_bytePos); + int currentPositionInLine = gb.X + 1; + + if (_byteProvider == null && _currentPositionInLine != 0) + { + _currentPositionInLine = 0; + OnCurrentPositionInLineChanged(EventArgs.Empty); + } + else if (currentPositionInLine != _currentPositionInLine) + { + _currentPositionInLine = currentPositionInLine; + OnCurrentPositionInLineChanged(EventArgs.Empty); + } + } + + /// + /// Raises the InsertActiveChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnInsertActiveChanged(EventArgs e) + { + if (InsertActiveChanged != null) + InsertActiveChanged(this, e); + } + + /// + /// Raises the ReadOnlyChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnReadOnlyChanged(EventArgs e) + { + if (ReadOnlyChanged != null) + ReadOnlyChanged(this, e); + } + + /// + /// Raises the ByteProviderChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnByteProviderChanged(EventArgs e) + { + if (ByteProviderChanged != null) + ByteProviderChanged(this, e); + } + + /// + /// Raises the SelectionStartChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnSelectionStartChanged(EventArgs e) + { + if (SelectionStartChanged != null) + SelectionStartChanged(this, e); + } + + /// + /// Raises the SelectionLengthChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnSelectionLengthChanged(EventArgs e) + { + if (SelectionLengthChanged != null) + SelectionLengthChanged(this, e); + } + + /// + /// Raises the LineInfoVisibleChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnLineInfoVisibleChanged(EventArgs e) + { + if (LineInfoVisibleChanged != null) + LineInfoVisibleChanged(this, e); + } + + /// + /// Raises the OnColumnInfoVisibleChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnColumnInfoVisibleChanged(EventArgs e) + { + if (ColumnInfoVisibleChanged != null) + ColumnInfoVisibleChanged(this, e); + } + + /// + /// Raises the ColumnSeparatorVisibleChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnGroupSeparatorVisibleChanged(EventArgs e) + { + if (GroupSeparatorVisibleChanged != null) + GroupSeparatorVisibleChanged(this, e); + } + + /// + /// Raises the StringViewVisibleChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnStringViewVisibleChanged(EventArgs e) + { + if (StringViewVisibleChanged != null) + StringViewVisibleChanged(this, e); + } + + /// + /// Raises the BorderStyleChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnBorderStyleChanged(EventArgs e) + { + if (BorderStyleChanged != null) + BorderStyleChanged(this, e); + } + + /// + /// Raises the UseFixedBytesPerLineChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnUseFixedBytesPerLineChanged(EventArgs e) + { + if (UseFixedBytesPerLineChanged != null) + UseFixedBytesPerLineChanged(this, e); + } + + /// + /// Raises the GroupSizeChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnGroupSizeChanged(EventArgs e) + { + if (GroupSizeChanged != null) + GroupSizeChanged(this, e); + } + + /// + /// Raises the BytesPerLineChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnBytesPerLineChanged(EventArgs e) + { + if (BytesPerLineChanged != null) + BytesPerLineChanged(this, e); + } + + /// + /// Raises the VScrollBarVisibleChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnVScrollBarVisibleChanged(EventArgs e) + { + if (VScrollBarVisibleChanged != null) + VScrollBarVisibleChanged(this, e); + } + + /// + /// Raises the HexCasingChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnHexCasingChanged(EventArgs e) + { + if (HexCasingChanged != null) + HexCasingChanged(this, e); + } + + /// + /// Raises the HorizontalByteCountChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnHorizontalByteCountChanged(EventArgs e) + { + if (HorizontalByteCountChanged != null) + HorizontalByteCountChanged(this, e); + } + + /// + /// Raises the VerticalByteCountChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnVerticalByteCountChanged(EventArgs e) + { + if (VerticalByteCountChanged != null) + VerticalByteCountChanged(this, e); + } + + /// + /// Raises the CurrentLineChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnCurrentLineChanged(EventArgs e) + { + if (CurrentLineChanged != null) + CurrentLineChanged(this, e); + } + + /// + /// Raises the CurrentPositionInLineChanged event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnCurrentPositionInLineChanged(EventArgs e) + { + if (CurrentPositionInLineChanged != null) + CurrentPositionInLineChanged(this, e); + } + + + /// + /// Raises the Copied event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnCopied(EventArgs e) + { + if (Copied != null) + Copied(this, e); + } + + /// + /// Raises the CopiedHex event. + /// + /// An EventArgs that contains the event data. + protected virtual void OnCopiedHex(EventArgs e) + { + if (CopiedHex != null) + CopiedHex(this, e); + } + + /// + /// Raises the MouseDown event. + /// + /// An EventArgs that contains the event data. + protected override void OnMouseDown(MouseEventArgs e) + { + System.Diagnostics.Debug.WriteLine("OnMouseDown()", "HexBox"); + + if (!Focused) + Focus(); + + BytePositionInfo? bpi = GetBytePositionInfo(new Point(e.X, e.Y)); + bool insideSelection = false; + if(bpi.HasValue) { + if(_bytePos <= bpi.Value.Index && _bytePos + _selectionLength >= bpi.Value.Index) { + //Clicked inside selection + insideSelection = true; + } + } + + if(!insideSelection || e.Button == MouseButtons.Left) { + if(e.Button != MouseButtons.Left) { + _selectionLength = 0; + } + SetCaretPosition(new Point(e.X, e.Y)); + } + + base.OnMouseDown(e); + } + + /// + /// Raises the MouseWhell event + /// + /// An EventArgs that contains the event data. + protected override void OnMouseWheel(MouseEventArgs e) + { + if(_byteProvider != null && !this.ReadOnly && Control.ModifierKeys == Keys.Shift) { + //Allow the user to change the value of the byte under the cursor by hold Alt & using the mouse wheel + BytePositionInfo? byteUnderCursor = GetBytePositionInfo(e.Location); + if(byteUnderCursor.HasValue) { + _byteProvider.WriteByte(byteUnderCursor.Value.Index, (byte)(_byteProvider.ReadByte(byteUnderCursor.Value.Index) + (e.Delta > 0 ? 1 : -1))); + } + } else { + int linesToScroll = -(e.Delta * SystemInformation.MouseWheelScrollLines / 120); + this.PerformScrollLines(linesToScroll); + } + + base.OnMouseWheel(e); + } + + + /// + /// Raises the Resize event. + /// + /// An EventArgs that contains the event data. + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + UpdateRectanglePositioning(); + } + + /// + /// Raises the GotFocus event. + /// + /// An EventArgs that contains the event data. + protected override void OnGotFocus(EventArgs e) + { + System.Diagnostics.Debug.WriteLine("OnGotFocus()", "HexBox"); + + base.OnGotFocus(e); + + CreateCaret(); + } + + /// + /// Raises the LostFocus event. + /// + /// An EventArgs that contains the event data. + protected override void OnLostFocus(EventArgs e) + { + System.Diagnostics.Debug.WriteLine("OnLostFocus()", "HexBox"); + + base.OnLostFocus(e); + + if(_byteProvider != null) { + _byteProvider.CommitWriteByte(); + } + + DestroyCaret(); + } + + void _byteProvider_LengthChanged(object sender, EventArgs e) + { + UpdateScrollSize(); + } + #endregion + + #region Scaling Support for High DPI resolution screens + /// + /// For high resolution screen support + /// + /// the factor + /// bounds + protected override void ScaleControl(SizeF factor, BoundsSpecified specified) + { + base.ScaleControl(factor, specified); + + this.BeginInvoke(new MethodInvoker(() => + { + this.UpdateRectanglePositioning(); + if (_caretVisible) + { + DestroyCaret(); + CreateCaret(); + } + this.Invalidate(); + })); + } + #endregion + } +} diff --git a/UI/Debugger/HexBox/HexBox.resx b/UI/Debugger/HexBox/HexBox.resx new file mode 100644 index 0000000..90f8d2d --- /dev/null +++ b/UI/Debugger/HexBox/HexBox.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + False + + \ No newline at end of file diff --git a/UI/Debugger/HexBox/HexCasing.cs b/UI/Debugger/HexBox/HexCasing.cs new file mode 100644 index 0000000..bf007ee --- /dev/null +++ b/UI/Debugger/HexBox/HexCasing.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Be.Windows.Forms +{ + /// + /// Specifies the case of hex characters in the HexBox control + /// + public enum HexCasing + { + /// + /// Converts all characters to uppercase. + /// + Upper = 0, + /// + /// Converts all characters to lowercase. + /// + Lower = 1 + } +} diff --git a/UI/Debugger/HexBox/IByteColorProvider.cs b/UI/Debugger/HexBox/IByteColorProvider.cs new file mode 100644 index 0000000..85543fe --- /dev/null +++ b/UI/Debugger/HexBox/IByteColorProvider.cs @@ -0,0 +1,18 @@ +using System; +using System.Drawing; + +namespace Be.Windows.Forms +{ + public interface IByteColorProvider + { + void Prepare(long firstByteIndex, long lastByteIndex); + ByteColors GetByteColor(long firstByteIndex, long byteIndex); + } + + public class ByteColors + { + public Color ForeColor { get; set; } + public Color BackColor { get; set; } + public Color BorderColor { get; set; } + } +} diff --git a/UI/Debugger/HexBox/IByteProvider.cs b/UI/Debugger/HexBox/IByteProvider.cs new file mode 100644 index 0000000..c8b02d7 --- /dev/null +++ b/UI/Debugger/HexBox/IByteProvider.cs @@ -0,0 +1,81 @@ +using System; + +namespace Be.Windows.Forms +{ + /// + /// Defines a byte provider for HexBox control + /// + public interface IByteProvider + { + /// + /// Reads a byte from the provider + /// + /// the index of the byte to read + /// the byte to read + byte ReadByte(long index); + /// + /// Writes a byte into the provider + /// + /// the index of the byte to write + /// the byte to write + void WriteByte(long index, byte value); + + void WriteBytes(long index, byte[] values); + + void PartialWriteByte(long index, byte value); + void CommitWriteByte(); + + /// + /// Inserts bytes into the provider + /// + /// + /// + /// This method must raise the LengthChanged event. + void InsertBytes(long index, byte[] bs); + /// + /// Deletes bytes from the provider + /// + /// the start index of the bytes to delete + /// the length of the bytes to delete + /// This method must raise the LengthChanged event. + void DeleteBytes(long index, long length); + + /// + /// Returns the total length of bytes the byte provider is providing. + /// + long Length { get; } + /// + /// Occurs, when the Length property changed. + /// + event EventHandler LengthChanged; + + /// + /// True, when changes are done. + /// + bool HasChanges(); + /// + /// Applies changes. + /// + void ApplyChanges(); + /// + /// Occurs, when bytes are changed. + /// + event EventHandler Changed; + + /// + /// Returns a value if the WriteByte methods is supported by the provider. + /// + /// True, when it´s supported. + bool SupportsWriteByte(); + /// + /// Returns a value if the InsertBytes methods is supported by the provider. + /// + /// True, when it´s supported. + bool SupportsInsertBytes(); + /// + /// Returns a value if the DeleteBytes methods is supported by the provider. + /// + /// True, when it´s supported. + bool SupportsDeleteBytes(); + } +} diff --git a/UI/Debugger/HexBox/MemoryDataBlock.cs b/UI/Debugger/HexBox/MemoryDataBlock.cs new file mode 100644 index 0000000..9729267 --- /dev/null +++ b/UI/Debugger/HexBox/MemoryDataBlock.cs @@ -0,0 +1,87 @@ +using System; + +namespace Be.Windows.Forms +{ + internal sealed class MemoryDataBlock : DataBlock + { + byte[] _data; + + public MemoryDataBlock(byte data) + { + _data = new byte[] { data }; + } + + public MemoryDataBlock(byte[] data) + { + if (data == null) + { + throw new ArgumentNullException("data"); + } + + _data = (byte[])data.Clone(); + } + + public override long Length + { + get + { + return _data.LongLength; + } + } + + public byte[] Data + { + get + { + return _data; + } + } + + public void AddByteToEnd(byte value) + { + byte[] newData = new byte[_data.LongLength + 1]; + _data.CopyTo(newData, 0); + newData[newData.LongLength - 1] = value; + _data = newData; + } + + public void AddByteToStart(byte value) + { + byte[] newData = new byte[_data.LongLength + 1]; + newData[0] = value; + _data.CopyTo(newData, 1); + _data = newData; + } + + public void InsertBytes(long position, byte[] data) + { + byte[] newData = new byte[_data.LongLength + data.LongLength]; + if (position > 0) + { + Array.Copy(_data, 0, newData, 0, position); + } + Array.Copy(data, 0, newData, position, data.LongLength); + if (position < _data.LongLength) + { + Array.Copy(_data, position, newData, position + data.LongLength, _data.LongLength - position); + } + _data = newData; + } + + public override void RemoveBytes(long position, long count) + { + byte[] newData = new byte[_data.LongLength - count]; + + if (position > 0) + { + Array.Copy(_data, 0, newData, 0, position); + } + if (position + count < _data.LongLength) + { + Array.Copy(_data, position + count, newData, position, newData.LongLength - position); + } + + _data = newData; + } + } +} diff --git a/UI/Debugger/HexBox/NativeMethods.cs b/UI/Debugger/HexBox/NativeMethods.cs new file mode 100644 index 0000000..b73f82c --- /dev/null +++ b/UI/Debugger/HexBox/NativeMethods.cs @@ -0,0 +1,14 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Be.Windows.Forms +{ + internal static class NativeMethods + { + // Key definitions + public const int WM_KEYDOWN = 0x100; + public const int WM_KEYUP = 0x101; + public const int WM_CHAR = 0x102; + } +} diff --git a/UI/Debugger/HexBox/StaticByteProvider.cs b/UI/Debugger/HexBox/StaticByteProvider.cs new file mode 100644 index 0000000..3184304 --- /dev/null +++ b/UI/Debugger/HexBox/StaticByteProvider.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Be.Windows.Forms +{ + public class StaticByteProvider : DynamicByteProvider + { + public StaticByteProvider(byte[] data) : base(data) + { + } + + public override bool SupportsInsertBytes() + { + return false; + } + + public override bool SupportsDeleteBytes() + { + return false; + } + } +} diff --git a/UI/Debugger/HexBox/TblByteCharConverter.cs b/UI/Debugger/HexBox/TblByteCharConverter.cs new file mode 100644 index 0000000..7dc0aed --- /dev/null +++ b/UI/Debugger/HexBox/TblByteCharConverter.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static Mesen.GUI.Debugger.TblLoader; + +namespace Be.Windows.Forms +{ + /// + /// The default implementation. + /// + class TblByteCharConverter : IByteCharConverter + { + Dictionary _tblRules; + Dictionary _reverseTblRules; + List _stringList; + bool[] _hasPotentialRule = new bool[256]; + + public TblByteCharConverter(Dictionary tblRules) + { + this._tblRules = tblRules; + foreach(KeyValuePair kvp in tblRules) { + _hasPotentialRule[kvp.Key.Key & 0xFF] = true; + } + + this._stringList = new List(); + this._reverseTblRules = new Dictionary(); + foreach(KeyValuePair kvp in tblRules) { + this._stringList.Add(kvp.Value); + this._reverseTblRules[kvp.Value] = kvp.Key; + } + this._stringList.Sort(new Comparison((a, b) => { + if(a.Length > b.Length) { + return 1; + } else if(a.Length < b.Length) { + return -1; + } else { + return string.Compare(a, b); + } + })); + } + + /// + /// Returns the character to display for the byte passed across. + /// + /// + /// + public virtual string ToChar(UInt64 value, out int keyLength) + { + if(!_hasPotentialRule[value & 0xFF]) { + keyLength = 1; + return "."; + } + + int byteCount = 8; + string result; + while(!_tblRules.TryGetValue(new TblKey() { Key = value, Length = byteCount }, out result) && byteCount > 0) { + value &= ~(((UInt64)0xFF) << (8 * (byteCount - 1))); + byteCount--; + } + + if(result != null) { + keyLength = byteCount; + return result; + } else { + keyLength = 1; + return "."; + } + } + + public virtual byte[] GetBytes(string text) + { + List bytes = new List(); + + bool match = false; + while(text.Length > 0) { + do { + match = false; + foreach(string key in _stringList) { + //Without an ordinal comparison, it's possible for an empty string to "StartsWith" a non-empty string (e.g replacement char U+FFFD) + //This causes a crash here (because key.Length > text.Length) + if(text.StartsWith(key, StringComparison.Ordinal)) { + bytes.AddRange(_reverseTblRules[key].GetBytes()); + text = text.Substring(key.Length); + match = true; + break; + } + } + } while(match); + + if(!match && text.Length > 0) { + bytes.Add(0); + text = text.Substring(1); + } + } + + return bytes.ToArray(); + } + + /// + /// Returns the byte to use for the character passed across. + /// + /// + /// + public virtual byte ToByte(char c) + { + return (byte)c; + } + + /// + /// Returns a description of the byte char provider. + /// + /// + public override string ToString() + { + return "TBL"; + } + } +} diff --git a/UI/Debugger/HexBox/Util.cs b/UI/Debugger/HexBox/Util.cs new file mode 100644 index 0000000..3e6207f --- /dev/null +++ b/UI/Debugger/HexBox/Util.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Be.Windows.Forms +{ + static class Util + { + /// + /// Contains true, if we are in design mode of Visual Studio + /// + private static bool _designMode; + + /// + /// Initializes an instance of Util class + /// + static Util() + { + // design mode is true if host process is: Visual Studio, Visual Studio Express Versions (C#, VB, C++) or SharpDevelop + var designerHosts = new List() { "devenv", "vcsexpress", "vbexpress", "vcexpress", "sharpdevelop" }; + using (var process = System.Diagnostics.Process.GetCurrentProcess()) + { + var processName = process.ProcessName.ToLower(); + _designMode = designerHosts.Contains(processName); + } + } + + /// + /// Gets true, if we are in design mode of Visual Studio + /// + /// + /// In Visual Studio 2008 SP1 the designer is crashing sometimes on windows forms. + /// The DesignMode property of Control class is buggy and cannot be used, so use our own implementation instead. + /// + public static bool DesignMode + { + get + { + return _designMode; + } + } + + } +} diff --git a/UI/Debugger/TblLoader.cs b/UI/Debugger/TblLoader.cs new file mode 100644 index 0000000..c592023 --- /dev/null +++ b/UI/Debugger/TblLoader.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Mesen.GUI.Debugger +{ + public class TblLoader + { + public struct TblKey + { + public UInt64 Key; + public int Length; + + public byte[] GetBytes() + { + byte[] bytes = new byte[this.Length]; + UInt64 value = this.Key; + for(int i = 0; i < this.Length; i++) { + bytes[i] = (byte)value; + value >>= 8; + } + return bytes; + } + } + + public static Dictionary ToDictionary(string[] fileContents) + { + try { + Dictionary dict = new Dictionary(); + + for(int i = 0; i < fileContents.Length; i++) { + if(!string.IsNullOrWhiteSpace(fileContents[i])) { + string[] data = fileContents[i].Split('='); + if(data.Length == 2) { + data[0] = data[0].Replace(" ", ""); + if(data[0].Length % 2 == 0 && Regex.IsMatch(data[0], "[0-9A-Fa-f]+")) { + TblKey key = new TblKey(); + + for(int j = 0; j < data[0].Length; j+=2) { + byte result = byte.Parse(data[0].Substring(j, 2), System.Globalization.NumberStyles.HexNumber); + key.Key |= (UInt64)result << (8 * j / 2); + key.Length++; + } + + dict[key] = string.IsNullOrEmpty(data[1]) ? " " : data[1]; + } + } + } + } + + return dict; + } catch { + return null; + } + } + } +} diff --git a/UI/Debugger/frmDbgPreferences.cs b/UI/Debugger/frmDbgPreferences.cs new file mode 100644 index 0000000..c7d76b0 --- /dev/null +++ b/UI/Debugger/frmDbgPreferences.cs @@ -0,0 +1,161 @@ +using Mesen.GUI.Config; +using Mesen.GUI.Forms; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmDbgPreferences : BaseConfigForm + { + public frmDbgPreferences() + { + InitializeComponent(); + + ctrlDbgShortcutsShared.Shortcuts = new FieldInfo[] { + GetMember(nameof(DebuggerShortcutsConfig.IncreaseFontSize)), + GetMember(nameof(DebuggerShortcutsConfig.DecreaseFontSize)), + GetMember(nameof(DebuggerShortcutsConfig.ResetFontSize)), + GetMember(nameof(DebuggerShortcutsConfig.GoTo)), + GetMember(nameof(DebuggerShortcutsConfig.Find)), + GetMember(nameof(DebuggerShortcutsConfig.FindNext)), + GetMember(nameof(DebuggerShortcutsConfig.FindPrev)), + GetMember(nameof(DebuggerShortcutsConfig.Undo)), + GetMember(nameof(DebuggerShortcutsConfig.Cut)), + GetMember(nameof(DebuggerShortcutsConfig.Copy)), + GetMember(nameof(DebuggerShortcutsConfig.Paste)), + GetMember(nameof(DebuggerShortcutsConfig.SelectAll)), + GetMember(nameof(DebuggerShortcutsConfig.Refresh)), + GetMember(nameof(DebuggerShortcutsConfig.MarkAsCode)), + GetMember(nameof(DebuggerShortcutsConfig.MarkAsData)), + GetMember(nameof(DebuggerShortcutsConfig.MarkAsUnidentified)), + GetMember(nameof(DebuggerShortcutsConfig.GoToAll)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_EditInMemoryViewer)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_ViewInDisassembly)), + GetMember(nameof(DebuggerShortcutsConfig.PpuViewer_ToggleView)), + GetMember(nameof(DebuggerShortcutsConfig.PpuViewer_ToggleZoom)), + + GetMember(nameof(DebuggerShortcutsConfig.OpenApuViewer)), + GetMember(nameof(DebuggerShortcutsConfig.OpenAssembler)), + GetMember(nameof(DebuggerShortcutsConfig.OpenDebugger)), + GetMember(nameof(DebuggerShortcutsConfig.OpenEventViewer)), + GetMember(nameof(DebuggerShortcutsConfig.OpenMemoryTools)), + GetMember(nameof(DebuggerShortcutsConfig.OpenProfiler)), + GetMember(nameof(DebuggerShortcutsConfig.OpenPpuViewer)), + GetMember(nameof(DebuggerShortcutsConfig.OpenScriptWindow)), + GetMember(nameof(DebuggerShortcutsConfig.OpenTextHooker)), + GetMember(nameof(DebuggerShortcutsConfig.OpenTraceLogger)), + GetMember(nameof(DebuggerShortcutsConfig.OpenWatchWindow)), + + GetMember(nameof(DebuggerShortcutsConfig.OpenNametableViewer)), + GetMember(nameof(DebuggerShortcutsConfig.OpenChrViewer)), + GetMember(nameof(DebuggerShortcutsConfig.OpenSpriteViewer)), + GetMember(nameof(DebuggerShortcutsConfig.OpenPaletteViewer)), + }; + + ctrlDbgShortcutsMemoryViewer.Shortcuts = new FieldInfo[] { + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_Freeze)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_Unfreeze)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_AddToWatch)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_EditBreakpoint)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_EditLabel)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_Import)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_Export)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_ViewInCpuMemory)), + GetMember(nameof(DebuggerShortcutsConfig.MemoryViewer_ViewInMemoryType)) + }; + + ctrlDbgShortcutsScriptWindow.Shortcuts = new FieldInfo[] { + GetMember(nameof(DebuggerShortcutsConfig.ScriptWindow_OpenScript)), + GetMember(nameof(DebuggerShortcutsConfig.ScriptWindow_SaveScript)), + GetMember(nameof(DebuggerShortcutsConfig.ScriptWindow_RunScript)), + GetMember(nameof(DebuggerShortcutsConfig.ScriptWindow_StopScript)) + }; + + ctrlDbgShortcutsDebugger.Shortcuts = new FieldInfo[] { + GetMember(nameof(DebuggerShortcutsConfig.Reset)), + GetMember(nameof(DebuggerShortcutsConfig.PowerCycle)), + GetMember(nameof(DebuggerShortcutsConfig.Continue)), + GetMember(nameof(DebuggerShortcutsConfig.Break)), + GetMember(nameof(DebuggerShortcutsConfig.ToggleBreakContinue)), + GetMember(nameof(DebuggerShortcutsConfig.StepInto)), + GetMember(nameof(DebuggerShortcutsConfig.StepOver)), + GetMember(nameof(DebuggerShortcutsConfig.StepOut)), + GetMember(nameof(DebuggerShortcutsConfig.StepBack)), + GetMember(nameof(DebuggerShortcutsConfig.RunPpuCycle)), + GetMember(nameof(DebuggerShortcutsConfig.RunPpuScanline)), + GetMember(nameof(DebuggerShortcutsConfig.RunPpuFrame)), + GetMember(nameof(DebuggerShortcutsConfig.BreakIn)), + GetMember(nameof(DebuggerShortcutsConfig.BreakOn)), + GetMember(nameof(DebuggerShortcutsConfig.FindOccurrences)), + GetMember(nameof(DebuggerShortcutsConfig.GoToProgramCounter)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_SetNextStatement)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_EditSubroutine)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_EditSelectedCode)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_EditSourceFile)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_EditLabel)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_NavigateBack)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_NavigateForward)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_ToggleBreakpoint)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_DisableEnableBreakpoint)), + GetMember(nameof(DebuggerShortcutsConfig.CodeWindow_SwitchView)), + GetMember(nameof(DebuggerShortcutsConfig.FunctionList_EditLabel)), + GetMember(nameof(DebuggerShortcutsConfig.FunctionList_AddBreakpoint)), + GetMember(nameof(DebuggerShortcutsConfig.FunctionList_FindOccurrences)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_Add)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_Edit)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_Delete)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_AddBreakpoint)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_AddToWatch)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_FindOccurrences)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_ViewInCpuMemory)), + GetMember(nameof(DebuggerShortcutsConfig.LabelList_ViewInMemoryType)), + GetMember(nameof(DebuggerShortcutsConfig.BreakpointList_Add)), + GetMember(nameof(DebuggerShortcutsConfig.BreakpointList_Edit)), + GetMember(nameof(DebuggerShortcutsConfig.BreakpointList_GoToLocation)), + GetMember(nameof(DebuggerShortcutsConfig.BreakpointList_Delete)), + GetMember(nameof(DebuggerShortcutsConfig.WatchList_Delete)), + GetMember(nameof(DebuggerShortcutsConfig.WatchList_MoveUp)), + GetMember(nameof(DebuggerShortcutsConfig.WatchList_MoveDown)), + GetMember(nameof(DebuggerShortcutsConfig.SaveRom)), + GetMember(nameof(DebuggerShortcutsConfig.SaveRomAs)), + GetMember(nameof(DebuggerShortcutsConfig.SaveEditAsIps)), + GetMember(nameof(DebuggerShortcutsConfig.RevertPrgChrChanges)), + GetMember(nameof(DebuggerShortcutsConfig.ToggleVerifiedData)), + GetMember(nameof(DebuggerShortcutsConfig.ToggleUnidentifiedCodeData)) + }; + } + + private FieldInfo GetMember(string name) + { + return typeof(DebuggerShortcutsConfig).GetField(name); + } + + protected override void OnFormClosed(FormClosedEventArgs e) + { + base.OnFormClosed(e); + if(DialogResult == DialogResult.OK) { + DebuggerShortcutsConfig.UpdateMenus(); + } + } + + private void btnReset_Click(object sender, EventArgs e) + { + DebuggerShortcutsConfig defaults = new DebuggerShortcutsConfig(); + foreach(FieldInfo field in typeof(DebuggerShortcutsConfig).GetFields()) { + field.SetValue(ConfigManager.Config.Debug.Shortcuts, field.GetValue(defaults)); + } + ctrlDbgShortcutsDebugger.InitializeGrid(); + ctrlDbgShortcutsMemoryViewer.InitializeGrid(); + ctrlDbgShortcutsScriptWindow.InitializeGrid(); + ctrlDbgShortcutsShared.InitializeGrid(); + } + } +} diff --git a/UI/Debugger/frmDbgPreferences.designer.cs b/UI/Debugger/frmDbgPreferences.designer.cs new file mode 100644 index 0000000..cb15123 --- /dev/null +++ b/UI/Debugger/frmDbgPreferences.designer.cs @@ -0,0 +1,188 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmDbgPreferences + { + /// + /// 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.ctrlDbgShortcutsShared = new Mesen.GUI.Debugger.ctrlDbgShortcuts(); + this.tabMain = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.ctrlDbgShortcutsDebugger = new Mesen.GUI.Debugger.ctrlDbgShortcuts(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.ctrlDbgShortcutsMemoryViewer = new Mesen.GUI.Debugger.ctrlDbgShortcuts(); + this.tabPage4 = new System.Windows.Forms.TabPage(); + this.ctrlDbgShortcutsScriptWindow = new Mesen.GUI.Debugger.ctrlDbgShortcuts(); + this.btnReset = new System.Windows.Forms.Button(); + this.baseConfigPanel.SuspendLayout(); + this.tabMain.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.tabPage4.SuspendLayout(); + this.SuspendLayout(); + // + // baseConfigPanel + // + this.baseConfigPanel.Controls.Add(this.btnReset); + this.baseConfigPanel.Location = new System.Drawing.Point(0, 460); + this.baseConfigPanel.Size = new System.Drawing.Size(605, 29); + this.baseConfigPanel.Controls.SetChildIndex(this.btnReset, 0); + // + // ctrlDbgShortcutsShared + // + this.ctrlDbgShortcutsShared.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlDbgShortcutsShared.Location = new System.Drawing.Point(3, 3); + this.ctrlDbgShortcutsShared.Name = "ctrlDbgShortcutsShared"; + this.ctrlDbgShortcutsShared.Size = new System.Drawing.Size(591, 428); + this.ctrlDbgShortcutsShared.TabIndex = 2; + // + // tabMain + // + this.tabMain.Controls.Add(this.tabPage1); + this.tabMain.Controls.Add(this.tabPage2); + this.tabMain.Controls.Add(this.tabPage3); + this.tabMain.Controls.Add(this.tabPage4); + this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabMain.Location = new System.Drawing.Point(0, 0); + this.tabMain.Name = "tabMain"; + this.tabMain.SelectedIndex = 0; + this.tabMain.Size = new System.Drawing.Size(605, 460); + this.tabMain.TabIndex = 3; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.ctrlDbgShortcutsShared); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(597, 434); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Shared"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.ctrlDbgShortcutsDebugger); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(597, 434); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Debugger"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // ctrlDbgShortcutsDebugger + // + this.ctrlDbgShortcutsDebugger.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlDbgShortcutsDebugger.Location = new System.Drawing.Point(3, 3); + this.ctrlDbgShortcutsDebugger.Name = "ctrlDbgShortcutsDebugger"; + this.ctrlDbgShortcutsDebugger.Size = new System.Drawing.Size(591, 428); + this.ctrlDbgShortcutsDebugger.TabIndex = 3; + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.ctrlDbgShortcutsMemoryViewer); + this.tabPage3.Location = new System.Drawing.Point(4, 22); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Padding = new System.Windows.Forms.Padding(3); + this.tabPage3.Size = new System.Drawing.Size(597, 434); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "Memory Viewer"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // ctrlDbgShortcutsMemoryViewer + // + this.ctrlDbgShortcutsMemoryViewer.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlDbgShortcutsMemoryViewer.Location = new System.Drawing.Point(3, 3); + this.ctrlDbgShortcutsMemoryViewer.Name = "ctrlDbgShortcutsMemoryViewer"; + this.ctrlDbgShortcutsMemoryViewer.Size = new System.Drawing.Size(591, 428); + this.ctrlDbgShortcutsMemoryViewer.TabIndex = 3; + // + // tabPage4 + // + this.tabPage4.Controls.Add(this.ctrlDbgShortcutsScriptWindow); + this.tabPage4.Location = new System.Drawing.Point(4, 22); + this.tabPage4.Name = "tabPage4"; + this.tabPage4.Padding = new System.Windows.Forms.Padding(3); + this.tabPage4.Size = new System.Drawing.Size(597, 434); + this.tabPage4.TabIndex = 3; + this.tabPage4.Text = "Script Window"; + this.tabPage4.UseVisualStyleBackColor = true; + // + // ctrlDbgShortcutsScriptWindow + // + this.ctrlDbgShortcutsScriptWindow.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlDbgShortcutsScriptWindow.Location = new System.Drawing.Point(3, 3); + this.ctrlDbgShortcutsScriptWindow.Name = "ctrlDbgShortcutsScriptWindow"; + this.ctrlDbgShortcutsScriptWindow.Size = new System.Drawing.Size(591, 428); + this.ctrlDbgShortcutsScriptWindow.TabIndex = 3; + // + // btnReset + // + this.btnReset.Location = new System.Drawing.Point(4, 3); + this.btnReset.Name = "btnReset"; + this.btnReset.Size = new System.Drawing.Size(111, 23); + this.btnReset.TabIndex = 3; + this.btnReset.Text = "Reset to Defaults"; + this.btnReset.UseVisualStyleBackColor = true; + this.btnReset.Click += new System.EventHandler(this.btnReset_Click); + // + // frmDbgPreferences + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(605, 489); + this.Controls.Add(this.tabMain); + this.Name = "frmDbgPreferences"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Configure Shortcut Keys"; + this.Controls.SetChildIndex(this.baseConfigPanel, 0); + this.Controls.SetChildIndex(this.tabMain, 0); + this.baseConfigPanel.ResumeLayout(false); + this.tabMain.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + this.tabPage3.ResumeLayout(false); + this.tabPage4.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private ctrlDbgShortcuts ctrlDbgShortcutsShared; + private System.Windows.Forms.TabControl tabMain; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private ctrlDbgShortcuts ctrlDbgShortcutsDebugger; + private System.Windows.Forms.TabPage tabPage3; + private ctrlDbgShortcuts ctrlDbgShortcutsMemoryViewer; + private System.Windows.Forms.TabPage tabPage4; + private ctrlDbgShortcuts ctrlDbgShortcutsScriptWindow; + private System.Windows.Forms.Button btnReset; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmDbgPreferences.resx b/UI/Debugger/frmDbgPreferences.resx new file mode 100644 index 0000000..8766f29 --- /dev/null +++ b/UI/Debugger/frmDbgPreferences.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/UI/Debugger/frmDbgShortcutGetKey.cs b/UI/Debugger/frmDbgShortcutGetKey.cs new file mode 100644 index 0000000..6f75ec2 --- /dev/null +++ b/UI/Debugger/frmDbgShortcutGetKey.cs @@ -0,0 +1,62 @@ +using Mesen.GUI.Config; +using Mesen.GUI.Forms; +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.Debugger +{ + public partial class frmDbgShortcutGetKey : BaseForm + { + private Keys[] _ignoredKeys = new Keys[9] { + Keys.LMenu, Keys.RMenu, Keys.Menu, Keys.LShiftKey, Keys.RShiftKey, Keys.ShiftKey, Keys.LControlKey, Keys.RControlKey, Keys.ControlKey + }; + + private Keys _shortcutKeys = Keys.None; + public Keys ShortcutKeys + { + get { return this._shortcutKeys; } + set { this._shortcutKeys = value; } + } + + public frmDbgShortcutGetKey() + { + InitializeComponent(); + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + foreach(Keys ignoredKey in _ignoredKeys) { + if((keyData & (Keys)0xFF) == ignoredKey) { + keyData ^= ignoredKey; + } + } + + _shortcutKeys = keyData; + lblCurrentKeys.Text = DebuggerShortcutsConfig.GetShortcutDisplay(keyData); + + return true; + } + + protected override void OnKeyDown(KeyEventArgs e) + { + } + + protected override void OnKeyUp(KeyEventArgs e) + { + base.OnKeyUp(e); + this.Close(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + } + } +} diff --git a/UI/Debugger/frmDbgShortcutGetKey.designer.cs b/UI/Debugger/frmDbgShortcutGetKey.designer.cs new file mode 100644 index 0000000..3a9f609 --- /dev/null +++ b/UI/Debugger/frmDbgShortcutGetKey.designer.cs @@ -0,0 +1,114 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmDbgShortcutGetKey + { + /// + /// 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.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.lblSetKeyMessage = new System.Windows.Forms.Label(); + this.lblCurrentKeys = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.AutoSize = true; + this.groupBox1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.groupBox1.Controls.Add(this.tableLayoutPanel1); + this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox1.Location = new System.Drawing.Point(3, 0); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(391, 105); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.AutoSize = true; + this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.tableLayoutPanel1.ColumnCount = 1; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.lblSetKeyMessage, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.lblCurrentKeys, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 16); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(385, 86); + this.tableLayoutPanel1.TabIndex = 2; + // + // lblSetKeyMessage + // + this.lblSetKeyMessage.AutoSize = true; + this.lblSetKeyMessage.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblSetKeyMessage.Location = new System.Drawing.Point(3, 0); + this.lblSetKeyMessage.Name = "lblSetKeyMessage"; + this.lblSetKeyMessage.Size = new System.Drawing.Size(379, 43); + this.lblSetKeyMessage.TabIndex = 0; + this.lblSetKeyMessage.Text = "Press any key on your keyboard to set a new binding."; + this.lblSetKeyMessage.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // lblCurrentKeys + // + this.lblCurrentKeys.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblCurrentKeys.Location = new System.Drawing.Point(3, 43); + this.lblCurrentKeys.Name = "lblCurrentKeys"; + this.lblCurrentKeys.Size = new System.Drawing.Size(379, 43); + this.lblCurrentKeys.TabIndex = 1; + this.lblCurrentKeys.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // frmDbgShortcutGetKey + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(397, 108); + this.Controls.Add(this.groupBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "frmDbgShortcutGetKey"; + this.Padding = new System.Windows.Forms.Padding(3, 0, 3, 3); + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Set key binding..."; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label lblSetKeyMessage; + private System.Windows.Forms.Label lblCurrentKeys; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmDbgShortcutGetKey.resx b/UI/Debugger/frmDbgShortcutGetKey.resx new file mode 100644 index 0000000..8766f29 --- /dev/null +++ b/UI/Debugger/frmDbgShortcutGetKey.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/UI/Debugger/frmFadeSpeed.cs b/UI/Debugger/frmFadeSpeed.cs new file mode 100644 index 0000000..0f6f104 --- /dev/null +++ b/UI/Debugger/frmFadeSpeed.cs @@ -0,0 +1,33 @@ +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; +using Mesen.GUI.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmFadeSpeed : BaseConfigForm + { + public frmFadeSpeed(int initialValue) + { + InitializeComponent(); + nudFrameCount.Value = initialValue; + } + + public int FadeSpeed + { + get { return (int)nudFrameCount.Value; } + } + + protected override void OnShown(EventArgs e) + { + base.OnShown(e); + nudFrameCount.Focus(); + } + } +} diff --git a/UI/Debugger/frmFadeSpeed.designer.cs b/UI/Debugger/frmFadeSpeed.designer.cs new file mode 100644 index 0000000..60f5a9e --- /dev/null +++ b/UI/Debugger/frmFadeSpeed.designer.cs @@ -0,0 +1,125 @@ +using Mesen.GUI.Controls; + +namespace Mesen.GUI.Debugger +{ + partial class frmFadeSpeed + { + /// + /// 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.lblAddress = new System.Windows.Forms.Label(); + this.lblFrames = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.nudFrameCount = new MesenNumericUpDown(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // baseConfigPanel + // + this.baseConfigPanel.Location = new System.Drawing.Point(0, 28); + this.baseConfigPanel.Size = new System.Drawing.Size(226, 29); + // + // lblAddress + // + this.lblAddress.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblAddress.AutoSize = true; + this.lblAddress.Location = new System.Drawing.Point(3, 6); + this.lblAddress.Name = "lblAddress"; + this.lblAddress.Size = new System.Drawing.Size(55, 13); + this.lblAddress.TabIndex = 0; + this.lblAddress.Text = "Fade after"; + // + // lblFrames + // + this.lblFrames.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblFrames.AutoSize = true; + this.lblFrames.Location = new System.Drawing.Point(149, 6); + this.lblFrames.Name = "lblFrames"; + this.lblFrames.Size = new System.Drawing.Size(38, 13); + this.lblFrames.TabIndex = 2; + this.lblFrames.Text = "frames"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80F)); + this.tableLayoutPanel1.Controls.Add(this.lblFrames, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.lblAddress, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.nudFrameCount, 1, 0); + 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()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(226, 57); + this.tableLayoutPanel1.TabIndex = 0; + // + // nudFrameCount + // + this.nudFrameCount.Dock = System.Windows.Forms.DockStyle.Fill; + this.nudFrameCount.Location = new System.Drawing.Point(64, 3); + this.nudFrameCount.Maximum = new decimal(new int[] { + 99999, + 0, + 0, + 0}); + this.nudFrameCount.Name = "nudFrameCount"; + this.nudFrameCount.Size = new System.Drawing.Size(79, 20); + this.nudFrameCount.TabIndex = 3; + this.nudFrameCount.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + // + // frmFadeSpeed + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(226, 57); + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "frmFadeSpeed"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Fade speed..."; + this.Controls.SetChildIndex(this.tableLayoutPanel1, 0); + this.Controls.SetChildIndex(this.baseConfigPanel, 0); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label lblAddress; + private System.Windows.Forms.Label lblFrames; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private MesenNumericUpDown nudFrameCount; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmFadeSpeed.resx b/UI/Debugger/frmFadeSpeed.resx new file mode 100644 index 0000000..8766f29 --- /dev/null +++ b/UI/Debugger/frmFadeSpeed.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/UI/Debugger/frmGoToLine.cs b/UI/Debugger/frmGoToLine.cs new file mode 100644 index 0000000..e00aa4a --- /dev/null +++ b/UI/Debugger/frmGoToLine.cs @@ -0,0 +1,37 @@ +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; +using Mesen.GUI.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmGoToLine : BaseConfigForm + { + public frmGoToLine(GoToAddress address, int charLimit) + { + InitializeComponent(); + + Entity = address; + AddBinding("Address", txtAddress); + + txtAddress.MaxLength = charLimit; + } + + protected override void OnShown(EventArgs e) + { + base.OnShown(e); + txtAddress.Focus(); + } + } + + public class GoToAddress + { + public UInt32 Address; + } +} diff --git a/UI/Debugger/frmGoToLine.designer.cs b/UI/Debugger/frmGoToLine.designer.cs new file mode 100644 index 0000000..a71d868 --- /dev/null +++ b/UI/Debugger/frmGoToLine.designer.cs @@ -0,0 +1,101 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmGoToLine + { + /// + /// 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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.lblAddress = new System.Windows.Forms.Label(); + this.txtAddress = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // baseConfigPanel + // + this.baseConfigPanel.Location = new System.Drawing.Point(0, 28); + this.baseConfigPanel.Size = new System.Drawing.Size(219, 29); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Controls.Add(this.lblAddress, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.txtAddress, 1, 0); + 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()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(219, 57); + this.tableLayoutPanel1.TabIndex = 0; + // + // lblAddress + // + this.lblAddress.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblAddress.AutoSize = true; + this.lblAddress.Location = new System.Drawing.Point(3, 6); + this.lblAddress.Name = "lblAddress"; + this.lblAddress.Size = new System.Drawing.Size(48, 13); + this.lblAddress.TabIndex = 0; + this.lblAddress.Text = "Address:"; + // + // txtAddress + // + this.txtAddress.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtAddress.Location = new System.Drawing.Point(57, 3); + this.txtAddress.MaxLength = 4; + this.txtAddress.Name = "txtAddress"; + this.txtAddress.Size = new System.Drawing.Size(159, 20); + this.txtAddress.TabIndex = 1; + // + // frmGoToLine + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(219, 57); + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "frmGoToLine"; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Go To..."; + this.Controls.SetChildIndex(this.tableLayoutPanel1, 0); + this.Controls.SetChildIndex(this.baseConfigPanel, 0); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label lblAddress; + private System.Windows.Forms.TextBox txtAddress; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmGoToLine.resx b/UI/Debugger/frmGoToLine.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Debugger/frmGoToLine.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + \ No newline at end of file diff --git a/UI/Debugger/frmMemoryTools.cs b/UI/Debugger/frmMemoryTools.cs new file mode 100644 index 0000000..5a5d531 --- /dev/null +++ b/UI/Debugger/frmMemoryTools.cs @@ -0,0 +1,693 @@ +using System; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using Mesen.GUI.Config; +using Mesen.GUI.Forms; +using Mesen.GUI.Debugger.Controls; + +namespace Mesen.GUI.Debugger +{ + public partial class frmMemoryTools : BaseForm + { + private EntityBinder _entityBinder = new EntityBinder(); + private NotificationListener _notifListener; + private SnesMemoryType _memoryType = SnesMemoryType.CpuMemory; + private bool _updating = false; + private DateTime _lastUpdate = DateTime.MinValue; + private TabPage _selectedTab; + private bool _formClosed; + + public frmMemoryTools() + { + InitializeComponent(); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + this._selectedTab = this.tabMain.SelectedTab; + + HexEditorInfo config = ConfigManager.Config.Debug.HexEditor; + _entityBinder.Entity = config; + _entityBinder.AddBinding(nameof(config.AutoRefresh), mnuAutoRefresh); + _entityBinder.AddBinding(nameof(config.HighDensityTextMode), mnuHighDensityMode); + _entityBinder.AddBinding(nameof(config.ByteEditingMode), mnuByteEditingMode); + _entityBinder.AddBinding(nameof(config.EnablePerByteNavigation), mnuEnablePerByteNavigation); + _entityBinder.AddBinding(nameof(config.IgnoreRedundantWrites), mnuIgnoreRedundantWrites); + _entityBinder.AddBinding(nameof(config.HighlightCurrentRowColumn), mnuHighlightCurrentRowColumn); + _entityBinder.AddBinding(nameof(config.ShowCharacters), mnuShowCharacters); + _entityBinder.AddBinding(nameof(config.ShowLabelInfo), mnuShowLabelInfoOnMouseOver); + + _entityBinder.AddBinding(nameof(config.HighlightExecution), mnuHighlightExecution); + _entityBinder.AddBinding(nameof(config.HighlightReads), mnuHightlightReads); + _entityBinder.AddBinding(nameof(config.HighlightWrites), mnuHighlightWrites); + _entityBinder.AddBinding(nameof(config.HideUnusedBytes), mnuHideUnusedBytes); + _entityBinder.AddBinding(nameof(config.HideReadBytes), mnuHideReadBytes); + _entityBinder.AddBinding(nameof(config.HideWrittenBytes), mnuHideWrittenBytes); + _entityBinder.AddBinding(nameof(config.HideExecutedBytes), mnuHideExecutedBytes); + + _entityBinder.AddBinding(nameof(config.HighlightLabelledBytes), mnuHighlightLabelledBytes); + _entityBinder.AddBinding(nameof(config.HighlightBreakpoints), mnuHighlightBreakpoints); + _entityBinder.AddBinding(nameof(config.HighlightCodeBytes), mnuHighlightCodeBytes); + _entityBinder.AddBinding(nameof(config.HighlightDataBytes), mnuHighlightDataBytes); + + _entityBinder.UpdateUI(); + + UpdateRefreshSpeedMenu(); + UpdateFlags(); + + this.ctrlHexViewer.HighlightCurrentRowColumn = config.HighlightCurrentRowColumn; + this.ctrlHexViewer.TextZoom = config.TextZoom; + this.ctrlHexViewer.BaseFont = new Font(config.FontFamily, config.FontSize, config.FontStyle); + + //TODO this.ctrlMemoryAccessCounters.BaseFont = new Font(config.FontFamily, config.FontSize, config.FontStyle); + //TODO this.ctrlMemoryAccessCounters.TextZoom = config.TextZoom; + + this.UpdateFadeOptions(); + + //TODO + //this.InitTblMappings(); + + this.ctrlHexViewer.StringViewVisible = mnuShowCharacters.Checked; + //TODO + //this.ctrlHexViewer.MemoryViewer = this; + + UpdateImportButton(); + InitMemoryTypeDropdown(true); + + _notifListener = new NotificationListener(); + _notifListener.OnNotification += _notifListener_OnNotification; + + this.mnuShowCharacters.CheckedChanged += this.mnuShowCharacters_CheckedChanged; + this.mnuIgnoreRedundantWrites.CheckedChanged += mnuIgnoreRedundantWrites_CheckedChanged; + + if(!config.WindowSize.IsEmpty) { + this.StartPosition = FormStartPosition.Manual; + this.Size = config.WindowSize; + this.Location = config.WindowLocation; + } + + this.InitShortcuts(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + base.OnFormClosing(e); + + HexEditorInfo config = ConfigManager.Config.Debug.HexEditor; + config.TextZoom = this.ctrlHexViewer.TextZoom; + config.FontFamily = ctrlHexViewer.BaseFont.FontFamily.Name; + config.FontStyle = ctrlHexViewer.BaseFont.Style; + config.FontSize = ctrlHexViewer.BaseFont.Size; + config.WindowSize = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Size : this.Size; + config.WindowLocation = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Location : this.Location; + config.MemoryType = cboMemoryType.GetEnumValue(); + _entityBinder.UpdateObject(); + ConfigManager.ApplyChanges(); + + //TODO? + //DebugWorkspaceManager.SaveWorkspace(); + + if(this._notifListener != null) { + this._notifListener.Dispose(); + this._notifListener = null; + } + + _formClosed = true; + } + + private void InitShortcuts() + { + mnuIncreaseFontSize.InitShortcut(this, nameof(DebuggerShortcutsConfig.IncreaseFontSize)); + mnuDecreaseFontSize.InitShortcut(this, nameof(DebuggerShortcutsConfig.DecreaseFontSize)); + mnuResetFontSize.InitShortcut(this, nameof(DebuggerShortcutsConfig.ResetFontSize)); + + mnuImport.InitShortcut(this, nameof(DebuggerShortcutsConfig.MemoryViewer_Import)); + mnuExport.InitShortcut(this, nameof(DebuggerShortcutsConfig.MemoryViewer_Export)); + + mnuRefresh.InitShortcut(this, nameof(DebuggerShortcutsConfig.Refresh)); + + mnuGoToAll.InitShortcut(this, nameof(DebuggerShortcutsConfig.GoToAll)); + mnuGoTo.InitShortcut(this, nameof(DebuggerShortcutsConfig.GoTo)); + mnuFind.InitShortcut(this, nameof(DebuggerShortcutsConfig.Find)); + mnuFindNext.InitShortcut(this, nameof(DebuggerShortcutsConfig.FindNext)); + mnuFindPrev.InitShortcut(this, nameof(DebuggerShortcutsConfig.FindPrev)); + } + + private void InitMemoryTypeDropdown(bool forStartup) + { + cboMemoryType.SelectedIndexChanged -= this.cboMemoryType_SelectedIndexChanged; + + SnesMemoryType originalValue = forStartup ? ConfigManager.Config.Debug.HexEditor.MemoryType : cboMemoryType.GetEnumValue(); + + cboMemoryType.BeginUpdate(); + cboMemoryType.Items.Clear(); + + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.CpuMemory)); + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.PrgRom)); + cboMemoryType.Items.Add("-"); + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.WorkRam)); + if(DebugApi.GetMemorySize(SnesMemoryType.SaveRam) > 0) { + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.SaveRam)); + } + cboMemoryType.Items.Add("-"); + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.VideoRam)); + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.CGRam)); + cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.SpriteRam)); + + + cboMemoryType.SelectedIndex = 0; + cboMemoryType.SetEnumValue(originalValue); + cboMemoryType.SelectedIndexChanged += this.cboMemoryType_SelectedIndexChanged; + + cboMemoryType.EndUpdate(); + UpdateMemoryType(); + } + + + private void UpdateFlags() + { + //TODO + /* + if(mnuIgnoreRedundantWrites.Checked) { + DebugWorkspaceManager.SetFlags(DebuggerFlags.IgnoreRedundantWrites); + } else { + DebugWorkspaceManager.ClearFlags(DebuggerFlags.IgnoreRedundantWrites); + } + */ + } + + public void ShowAddress(int address, SnesMemoryType memoryType) + { + tabMain.SelectedTab = tpgMemoryViewer; + cboMemoryType.SetEnumValue(memoryType); + ctrlHexViewer.GoToAddress(address); + } + + //TODO + /* + public void GoToDestination(GoToDestination dest) + { + if(_memoryType == DebugMemoryType.CpuMemory && dest.CpuAddress >= 0) { + this.ShowAddress(dest.CpuAddress, DebugMemoryType.CpuMemory); + } else if(dest.AddressInfo != null) { + this.ShowAddress(dest.AddressInfo.Address, dest.AddressInfo.Type.ToMemoryType()); + } else if(dest.Label != null) { + int relAddress = dest.Label.GetRelativeAddress(); + if(_memoryType == DebugMemoryType.CpuMemory && relAddress >= 0) { + this.ShowAddress(relAddress, DebugMemoryType.CpuMemory); + } else { + this.ShowAddress((int)dest.Label.Address, dest.Label.AddressType.ToMemoryType()); + } + } else if(dest.CpuAddress >= 0) { + this.ShowAddress(dest.CpuAddress, DebugMemoryType.CpuMemory); + } + + this.BringToFront(); + } + + public void GoToAll() + { + using(frmGoToAll frm = new frmGoToAll(true, false)) { + if(frm.ShowDialog() == DialogResult.OK) { + GoToDestination(frm.Destination); + } + } + } + + private void InitTblMappings() + { + DebugWorkspace workspace = DebugWorkspaceManager.GetWorkspace(); + if(workspace.TblMappings != null && workspace.TblMappings.Count > 0) { + var tblDict = TblLoader.ToDictionary(workspace.TblMappings.ToArray()); + if(tblDict != null) { + this.ctrlHexViewer.ByteCharConverter = new TblByteCharConverter(tblDict); + } + } else { + this.ctrlHexViewer.ByteCharConverter = null; + } + }*/ + + private void _notifListener_OnNotification(NotificationEventArgs e) + { + switch(e.NotificationType) { + case ConsoleNotificationType.CodeBreak: + this.BeginInvoke((MethodInvoker)(() => this.RefreshData())); + break; + + case ConsoleNotificationType.GameReset: + case ConsoleNotificationType.GameLoaded: + this.BeginInvoke((Action)(() => { + if(_formClosed) { + return; + } + this.InitMemoryTypeDropdown(false); + //TODO ctrlMemoryAccessCounters.InitMemoryTypeDropdown(); + })); + this.UpdateFlags(); + break; + + case ConsoleNotificationType.PpuFrameDone: + int refreshDelay = 90; + switch(ConfigManager.Config.Debug.HexEditor.AutoRefreshSpeed) { + case RefreshSpeed.Low: refreshDelay = 90; break; + case RefreshSpeed.Normal: refreshDelay = 32; break; + case RefreshSpeed.High: refreshDelay = 16; break; + } + + DateTime now = DateTime.Now; + if(!_updating && ConfigManager.Config.Debug.HexEditor.AutoRefresh && (now - _lastUpdate).Milliseconds >= refreshDelay) { + _lastUpdate = now; + _updating = true; + this.BeginInvoke((Action)(() => { + this.RefreshData(); + _updating = false; + })); + } + break; + } + } + + private void cboMemoryType_SelectedIndexChanged(object sender, EventArgs e) + { + UpdateMemoryType(); + } + + private void UpdateMemoryType() + { + this._memoryType = this.cboMemoryType.GetEnumValue(); + this.UpdateImportButton(); + this.RefreshData(); + } + + private void UpdateByteColorProvider() + { + //TODO + /*switch(this._memoryType) { + case SnesMemoryType.CpuMemory: + case SnesMemoryType.PrgRom: + case SnesMemoryType.WorkRam: + case SnesMemoryType.SaveRam: + this.ctrlHexViewer.ByteColorProvider = new ByteColorProvider( + this._memoryType, + mnuHighlightExecution.Checked, + mnuHighlightWrites.Checked, + mnuHightlightReads.Checked, + ConfigManager.Config.Debug.RamFadeSpeed, + mnuHideUnusedBytes.Checked, + mnuHideReadBytes.Checked, + mnuHideWrittenBytes.Checked, + mnuHideExecutedBytes.Checked, + mnuHighlightDataBytes.Checked, + mnuHighlightCodeBytes.Checked, + mnuHighlightLabelledBytes.Checked, + mnuHighlightBreakpoints.Checked + ); + break; + + case SnesMemoryType.VideoRam: + case DebugMemoryType.ChrRom: + case DebugMemoryType.ChrRam: + case DebugMemoryType.PaletteMemory: + case DebugMemoryType.NametableRam: + this.ctrlHexViewer.ByteColorProvider = new ChrByteColorProvider( + this._memoryType, + mnuHighlightWrites.Checked, + mnuHightlightReads.Checked, + ConfigManager.Config.Debug.RamFadeSpeed, + mnuHideUnusedBytes.Checked, + mnuHideReadBytes.Checked, + mnuHideWrittenBytes.Checked, + mnuHighlightChrDrawnBytes.Checked, + mnuHighlightChrReadBytes.Checked, + mnuHighlightBreakpoints.Checked + ); + break; + + default: + this.ctrlHexViewer.ByteColorProvider = null; + break; + }*/ + } + + private void mnuRefresh_Click(object sender, EventArgs e) + { + this.RefreshData(); + } + + private void RefreshData() + { + if(_formClosed) { + return; + } + + //TODO + /* + if(DebugWorkspaceManager.GetWorkspace() != this._previousWorkspace) { + this.InitTblMappings(); + _previousWorkspace = DebugWorkspaceManager.GetWorkspace(); + } + */ + + if(this.tabMain.SelectedTab == this.tpgAccessCounters) { + //TODO this.ctrlMemoryAccessCounters.RefreshData(); + } else if(this.tabMain.SelectedTab == this.tpgMemoryViewer) { + this.UpdateByteColorProvider(); + this.ctrlHexViewer.RefreshData(_memoryType); + } + } + + private void mnuFind_Click(object sender, EventArgs e) + { + this.ctrlHexViewer.OpenSearchBox(); + } + + private void mnuFindNext_Click(object sender, EventArgs e) + { + this.ctrlHexViewer.FindNext(); + } + + private void mnuFindPrev_Click(object sender, EventArgs e) + { + this.ctrlHexViewer.FindPrevious(); + } + + private void mnuGoTo_Click(object sender, EventArgs e) + { + if(_selectedTab == tpgMemoryViewer) { + this.ctrlHexViewer.GoToAddress(); + } else if(_selectedTab == tpgAccessCounters) { + //TODO this.ctrlMemoryAccessCounters.GoToAddress(); + } + } + + private void mnuGoToAll_Click(object sender, EventArgs e) + { + //TODO + //this.GoToAll(); + } + + private void mnuIncreaseFontSize_Click(object sender, EventArgs e) + { + this.ctrlHexViewer.TextZoom += 10; + //TODO this.ctrlMemoryAccessCounters.TextZoom += 10; + } + + private void mnuDecreaseFontSize_Click(object sender, EventArgs e) + { + this.ctrlHexViewer.TextZoom -= 10; + //TODO this.ctrlMemoryAccessCounters.TextZoom -= 10; + } + + private void mnuResetFontSize_Click(object sender, EventArgs e) + { + this.ctrlHexViewer.TextZoom = 100; + //TODO this.ctrlMemoryAccessCounters.TextZoom = 100; + } + + private void mnuClose_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void UpdateImportButton() + { + switch(_memoryType) { + case SnesMemoryType.VideoRam: + case SnesMemoryType.CGRam: + case SnesMemoryType.SpriteRam: + case SnesMemoryType.WorkRam: + case SnesMemoryType.SaveRam: + mnuImport.Enabled = true; + break; + + default: + mnuImport.Enabled = false; + break; + } + } + + private void mnuImport_Click(object sender, EventArgs e) + { + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.SetFilter("Memory dump files (*.dmp)|*.dmp|All files (*.*)|*.*"); + ofd.InitialDirectory = ConfigManager.DebuggerFolder; + if(ofd.ShowDialog() == DialogResult.OK) { + byte[] fileContent = File.ReadAllBytes(ofd.FileName); + DebugApi.SetMemoryState(_memoryType, fileContent, fileContent.Length); + RefreshData(); + } + } + } + + private void mnuExport_Click(object sender, EventArgs e) + { + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.SetFilter("Memory dump files (*.dmp)|*.dmp|All files (*.*)|*.*"); + sfd.InitialDirectory = ConfigManager.DebuggerFolder; + //TODO sfd.FileName = InteropEmu.GetRomInfo().GetRomName() + " - " + cboMemoryType.SelectedItem.ToString() + ".dmp"; + sfd.FileName = cboMemoryType.SelectedItem.ToString() + ".dmp"; + if(sfd.ShowDialog() == DialogResult.OK) { + File.WriteAllBytes(sfd.FileName, this.ctrlHexViewer.GetData()); + } + } + } + + private void tabMain_SelectedIndexChanged(object sender, EventArgs e) + { + _selectedTab = this.tabMain.SelectedTab; + this.RefreshData(); + } + + private void ctrlHexViewer_RequiredWidthChanged(object sender, EventArgs e) + { + this.Size = new Size(this.ctrlHexViewer.RequiredWidth + 20, this.Height); + } + + private void mnuLoadTblFile_Click(object sender, EventArgs e) + { + //TODO + /*OpenFileDialog ofd = new OpenFileDialog(); + ofd.SetFilter("TBL files (*.tbl)|*.tbl"); + if(ofd.ShowDialog() == DialogResult.OK) { + string[] fileContents = File.ReadAllLines(ofd.FileName); + var tblDict = TblLoader.ToDictionary(fileContents); + if(tblDict == null) { + MessageBox.Show("Could not load TBL file. The file selected file appears to be invalid.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } else { + DebugWorkspaceManager.GetWorkspace().TblMappings = new List(fileContents); + this.ctrlHexViewer.ByteCharConverter = new TblByteCharConverter(tblDict); + this.mnuShowCharacters.Checked = true; + } + }*/ + } + + private void mnuResetTblMappings_Click(object sender, EventArgs e) + { + //TODO + //DebugWorkspaceManager.GetWorkspace().TblMappings = null; + //this.ctrlHexViewer.ByteCharConverter = null; + } + + private void mnuShowCharacters_CheckedChanged(object sender, EventArgs e) + { + this.ctrlHexViewer.StringViewVisible = mnuShowCharacters.Checked; + } + + private void mnuIgnoreRedundantWrites_CheckedChanged(object sender, EventArgs e) + { + this.UpdateFlags(); + } + + private void UpdateFadeOptions() + { + int fadeSpeed = ConfigManager.Config.Debug.HexEditor.FadeSpeed; + mnuFadeSlow.Checked = fadeSpeed == 600; + mnuFadeNormal.Checked = fadeSpeed == 300; + mnuFadeFast.Checked = fadeSpeed == 120; + mnuFadeNever.Checked = fadeSpeed == 0; + mnuCustomFadeSpeed.Checked = !mnuFadeSlow.Checked && !mnuFadeNormal.Checked && !mnuFadeFast.Checked && !mnuFadeSlow.Checked; + } + + private void mnuFadeSpeed_Click(object sender, EventArgs e) + { + HexEditorInfo config = ConfigManager.Config.Debug.HexEditor; + if(sender == mnuFadeSlow) { + config.FadeSpeed = 600; + } else if(sender == mnuFadeNormal) { + config.FadeSpeed = 300; + } else if(sender == mnuFadeFast) { + config.FadeSpeed = 120; + } else if(sender == mnuFadeNever) { + config.FadeSpeed = 0; + } + ConfigManager.ApplyChanges(); + UpdateFadeOptions(); + } + + private void mnuCustomFadeSpeed_Click(object sender, EventArgs e) + { + using(frmFadeSpeed frm = new frmFadeSpeed(ConfigManager.Config.Debug.HexEditor.FadeSpeed)) { + if(frm.ShowDialog() == DialogResult.OK) { + ConfigManager.Config.Debug.HexEditor.FadeSpeed = frm.FadeSpeed; + ConfigManager.ApplyChanges(); + UpdateFadeOptions(); + } + } + } + + private void mnuColorProviderOptions_Click(object sender, EventArgs e) + { + this.UpdateByteColorProvider(); + } + + + private void mnuConfigureColors_Click(object sender, EventArgs e) + { + //TODO + //using(frmMemoryViewerColors frm = new frmMemoryViewerColors()) { + // if(frm.ShowDialog(this, this) == DialogResult.OK) { + // this.RefreshData(); + // } + //} + } + + /*private frmCodeTooltip _tooltip = null; + private CodeLabel _lastLabelTooltip = null; + private int _lastLabelArrayOffset = -1; + private int _lastTooltipAddress = -1;*/ + private void ctrlHexViewer_ByteMouseHover(int address, Point position) + { + //TODO + /*if(address < 0 || !mnuShowLabelInfoOnMouseOver.Checked) { + if(_tooltip != null) { + _tooltip.Close(); + _lastLabelTooltip = null; + _lastLabelArrayOffset = -1; + _lastTooltipAddress = -1; + } + return; + } + + if(_lastTooltipAddress == address) { + return; + } + + _lastTooltipAddress = address; + + CodeLabel label = null; + int arrayOffset = 0; + switch(_memoryType) { + case DebugMemoryType.CpuMemory: + AddressTypeInfo info = new AddressTypeInfo(); + InteropEmu.DebugGetAbsoluteAddressAndType((UInt32)address, info); + if(info.Address >= 0) { + label = LabelManager.GetLabel((UInt32)info.Address, info.Type); + if(label != null) { + arrayOffset = info.Address - (int)label.Address; + } + } + if(label == null) { + label = LabelManager.GetLabel((UInt32)address, AddressType.Register); + } + break; + + case DebugMemoryType.InternalRam: + case DebugMemoryType.WorkRam: + case DebugMemoryType.SaveRam: + case DebugMemoryType.PrgRom: + label = LabelManager.GetLabel((UInt32)address, _memoryType.ToAddressType()); + if(label != null) { + arrayOffset = address - (int)label.Address; + } + break; + } + + if(label != null) { + if(_lastLabelTooltip != label || _lastLabelArrayOffset != arrayOffset) { + if(_tooltip != null) { + _tooltip.Close(); + } + + Dictionary values = new Dictionary(); + if(!string.IsNullOrWhiteSpace(label.Label)) { + values["Label"] = label.Label; + if(label.Length > 1) { + values["Label"] += "+" + arrayOffset.ToString(); + } + } + values["Address"] = "$" + (label.Address + arrayOffset).ToString("X4"); + values["Address Type"] = label.AddressType.ToString(); + if(!string.IsNullOrWhiteSpace(label.Comment)) { + values["Comment"] = label.Comment; + } + + _tooltip = new frmCodeTooltip(this, values); + _tooltip.FormClosed += (s, evt) => { _tooltip = null; }; + _tooltip.SetFormLocation(new Point(position.X, position.Y), ctrlHexViewer); + _lastLabelTooltip = label; + _lastLabelArrayOffset = arrayOffset; + } + } else { + if(_tooltip != null) { + _tooltip.Close(); + _lastLabelTooltip = null; + _lastLabelArrayOffset = -1; + _lastTooltipAddress = -1; + } + }*/ + } + + private void mnuAutoRefreshSpeed_Click(object sender, EventArgs e) + { + HexEditorInfo config = ConfigManager.Config.Debug.HexEditor; + + if(sender == mnuAutoRefreshLow) { + config.AutoRefreshSpeed = RefreshSpeed.Low; + } else if(sender == mnuAutoRefreshNormal) { + config.AutoRefreshSpeed = RefreshSpeed.Normal; + } else if(sender == mnuAutoRefreshHigh) { + config.AutoRefreshSpeed = RefreshSpeed.High; + } + ConfigManager.ApplyChanges(); + + UpdateRefreshSpeedMenu(); + } + + private void UpdateRefreshSpeedMenu() + { + HexEditorInfo config = ConfigManager.Config.Debug.HexEditor; + mnuAutoRefreshLow.Checked = config.AutoRefreshSpeed == RefreshSpeed.Low; + mnuAutoRefreshNormal.Checked = config.AutoRefreshSpeed == RefreshSpeed.Normal; + mnuAutoRefreshHigh.Checked = config.AutoRefreshSpeed == RefreshSpeed.High; + } + + private void mnuHighDensityMode_CheckedChanged(object sender, EventArgs e) + { + ctrlHexViewer.HighDensityMode = mnuHighDensityMode.Checked; + } + + private void mnuEnablePerByteNavigation_CheckedChanged(object sender, EventArgs e) + { + ctrlHexViewer.EnablePerByteNavigation = mnuEnablePerByteNavigation.Checked; + } + + private void mnuSelectFont_Click(object sender, EventArgs e) + { + ctrlHexViewer.BaseFont = FontDialogHelper.SelectFont(ctrlHexViewer.BaseFont); + //TODO ctrlMemoryAccessCounters.BaseFont = ctrlHexViewer.BaseFont; + } + + private void mnuByteEditingMode_CheckedChanged(object sender, EventArgs e) + { + ctrlHexViewer.ByteEditingMode = mnuByteEditingMode.Checked; + } + + private void mnuHighlightCurrentRowColumn_CheckedChanged(object sender, EventArgs e) + { + ctrlHexViewer.HighlightCurrentRowColumn = mnuHighlightCurrentRowColumn.Checked; + } + } +} diff --git a/UI/Debugger/frmMemoryTools.designer.cs b/UI/Debugger/frmMemoryTools.designer.cs new file mode 100644 index 0000000..9bdd35a --- /dev/null +++ b/UI/Debugger/frmMemoryTools.designer.cs @@ -0,0 +1,876 @@ +namespace Mesen.GUI.Debugger +{ + partial class frmMemoryTools + { + /// + /// 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(); + } + if(this._notifListener != null) { + this._notifListener.Dispose(); + this._notifListener = null; + } + 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.ctrlHexViewer = new Mesen.GUI.Debugger.Controls.ctrlHexViewer(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.lblViewMemoryType = new System.Windows.Forms.Label(); + this.cboMemoryType = new Mesen.GUI.Debugger.Controls.ComboBoxWithSeparator(); + this.menuStrip1 = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuImport = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuExport = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuLoadTblFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuResetTblMappings = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuClose = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuView = new System.Windows.Forms.ToolStripMenuItem(); + this.highlightToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightExecution = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightWrites = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHightlightReads = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator(); + this.fadeSpeedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFadeSlow = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFadeNormal = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFadeFast = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFadeNever = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem7 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuCustomFadeSpeed = new System.Windows.Forms.ToolStripMenuItem(); + this.dataTypeHighlightingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightLabelledBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightBreakpoints = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem8 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuHighlightCodeBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightDataBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightDmcDataBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem11 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuHighlightChrDrawnBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightChrReadBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.fadeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHideUnusedBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHideReadBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHideWrittenBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHideExecutedBytes = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem5 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuConfigureColors = new System.Windows.Forms.ToolStripMenuItem(); + this.fontSizeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuIncreaseFontSize = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDecreaseFontSize = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuResetFontSize = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem12 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuSelectFont = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem13 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuHighDensityMode = new System.Windows.Forms.ToolStripMenuItem(); + this.autorefreshSpeedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAutoRefreshLow = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAutoRefreshNormal = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAutoRefreshHigh = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuRefresh = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem9 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuAutoRefresh = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuShowCharacters = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuShowLabelInfoOnMouseOver = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuIgnoreRedundantWrites = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuEnablePerByteNavigation = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuByteEditingMode = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuHighlightCurrentRowColumn = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuGoToAll = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuGoTo = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem14 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuFind = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFindNext = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuFindPrev = new System.Windows.Forms.ToolStripMenuItem(); + this.tabMain = new System.Windows.Forms.TabControl(); + this.tpgMemoryViewer = new System.Windows.Forms.TabPage(); + this.panel1 = new System.Windows.Forms.Panel(); + this.tpgAccessCounters = new System.Windows.Forms.TabPage(); + this.flowLayoutPanel1.SuspendLayout(); + this.menuStrip1.SuspendLayout(); + this.tabMain.SuspendLayout(); + this.tpgMemoryViewer.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // ctrlHexViewer + // + this.ctrlHexViewer.BaseFont = new System.Drawing.Font("Consolas", 10F); + this.ctrlHexViewer.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlHexViewer.Location = new System.Drawing.Point(0, 0); + this.ctrlHexViewer.Margin = new System.Windows.Forms.Padding(0); + this.ctrlHexViewer.Name = "ctrlHexViewer"; + this.ctrlHexViewer.Size = new System.Drawing.Size(606, 343); + this.ctrlHexViewer.TabIndex = 0; + this.ctrlHexViewer.TextZoom = 100; + this.ctrlHexViewer.RequiredWidthChanged += new System.EventHandler(this.ctrlHexViewer_RequiredWidthChanged); + this.ctrlHexViewer.ByteMouseHover += new Mesen.GUI.Debugger.Controls.ctrlHexViewer.ByteMouseHoverHandler(this.ctrlHexViewer_ByteMouseHover); + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.AutoSize = true; + this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.flowLayoutPanel1.Controls.Add(this.lblViewMemoryType); + this.flowLayoutPanel1.Controls.Add(this.cboMemoryType); + this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(207, 27); + this.flowLayoutPanel1.TabIndex = 1; + this.flowLayoutPanel1.WrapContents = false; + // + // lblViewMemoryType + // + this.lblViewMemoryType.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblViewMemoryType.AutoSize = true; + this.lblViewMemoryType.Location = new System.Drawing.Point(3, 7); + this.lblViewMemoryType.Name = "lblViewMemoryType"; + this.lblViewMemoryType.Size = new System.Drawing.Size(33, 13); + this.lblViewMemoryType.TabIndex = 0; + this.lblViewMemoryType.Text = "View:"; + // + // cboMemoryType + // + this.cboMemoryType.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed; + this.cboMemoryType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboMemoryType.FormattingEnabled = true; + this.cboMemoryType.Location = new System.Drawing.Point(42, 3); + this.cboMemoryType.Name = "cboMemoryType"; + this.cboMemoryType.Size = new System.Drawing.Size(162, 21); + this.cboMemoryType.TabIndex = 1; + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem, + this.mnuView, + this.toolStripMenuItem1}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(614, 24); + this.menuStrip1.TabIndex = 2; + this.menuStrip1.Text = "menuStrip1"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuImport, + this.mnuExport, + this.toolStripMenuItem3, + this.mnuLoadTblFile, + this.mnuResetTblMappings, + this.toolStripMenuItem4, + this.mnuClose}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Text = "File"; + // + // mnuImport + // + this.mnuImport.Image = global::Mesen.GUI.Properties.Resources.Import; + this.mnuImport.Name = "mnuImport"; + this.mnuImport.Size = new System.Drawing.Size(181, 22); + this.mnuImport.Text = "Import"; + this.mnuImport.Click += new System.EventHandler(this.mnuImport_Click); + // + // mnuExport + // + this.mnuExport.Image = global::Mesen.GUI.Properties.Resources.Export; + this.mnuExport.Name = "mnuExport"; + this.mnuExport.Size = new System.Drawing.Size(181, 22); + this.mnuExport.Text = "Export"; + this.mnuExport.Click += new System.EventHandler(this.mnuExport_Click); + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + this.toolStripMenuItem3.Size = new System.Drawing.Size(178, 6); + // + // mnuLoadTblFile + // + this.mnuLoadTblFile.Name = "mnuLoadTblFile"; + this.mnuLoadTblFile.Size = new System.Drawing.Size(181, 22); + this.mnuLoadTblFile.Text = "Load TBL file"; + this.mnuLoadTblFile.Click += new System.EventHandler(this.mnuLoadTblFile_Click); + // + // mnuResetTblMappings + // + this.mnuResetTblMappings.Name = "mnuResetTblMappings"; + this.mnuResetTblMappings.Size = new System.Drawing.Size(181, 22); + this.mnuResetTblMappings.Text = "Reset TBL mappings"; + this.mnuResetTblMappings.Click += new System.EventHandler(this.mnuResetTblMappings_Click); + // + // toolStripMenuItem4 + // + this.toolStripMenuItem4.Name = "toolStripMenuItem4"; + this.toolStripMenuItem4.Size = new System.Drawing.Size(178, 6); + // + // mnuClose + // + this.mnuClose.Image = global::Mesen.GUI.Properties.Resources.Exit; + this.mnuClose.Name = "mnuClose"; + this.mnuClose.Size = new System.Drawing.Size(181, 22); + this.mnuClose.Text = "Close"; + this.mnuClose.Click += new System.EventHandler(this.mnuClose_Click); + // + // mnuView + // + this.mnuView.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.highlightToolStripMenuItem, + this.dataTypeHighlightingToolStripMenuItem, + this.fadeToolStripMenuItem, + this.toolStripMenuItem5, + this.mnuConfigureColors, + this.fontSizeToolStripMenuItem, + this.autorefreshSpeedToolStripMenuItem, + this.toolStripMenuItem2, + this.mnuRefresh, + this.toolStripMenuItem9, + this.mnuAutoRefresh, + this.mnuShowCharacters, + this.mnuShowLabelInfoOnMouseOver, + this.toolStripMenuItem10, + this.mnuIgnoreRedundantWrites, + this.mnuEnablePerByteNavigation, + this.mnuByteEditingMode, + this.mnuHighlightCurrentRowColumn}); + this.mnuView.Name = "mnuView"; + this.mnuView.Size = new System.Drawing.Size(44, 20); + this.mnuView.Text = "View"; + // + // highlightToolStripMenuItem + // + this.highlightToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuHighlightExecution, + this.mnuHighlightWrites, + this.mnuHightlightReads, + this.toolStripMenuItem6, + this.fadeSpeedToolStripMenuItem}); + this.highlightToolStripMenuItem.Name = "highlightToolStripMenuItem"; + this.highlightToolStripMenuItem.Size = new System.Drawing.Size(256, 22); + this.highlightToolStripMenuItem.Text = "Memory Access Highlighting"; + // + // mnuHighlightExecution + // + this.mnuHighlightExecution.CheckOnClick = true; + this.mnuHighlightExecution.Name = "mnuHighlightExecution"; + this.mnuHighlightExecution.Size = new System.Drawing.Size(133, 22); + this.mnuHighlightExecution.Text = "Execution"; + this.mnuHighlightExecution.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHighlightWrites + // + this.mnuHighlightWrites.CheckOnClick = true; + this.mnuHighlightWrites.Name = "mnuHighlightWrites"; + this.mnuHighlightWrites.Size = new System.Drawing.Size(133, 22); + this.mnuHighlightWrites.Text = "Writes"; + this.mnuHighlightWrites.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHightlightReads + // + this.mnuHightlightReads.CheckOnClick = true; + this.mnuHightlightReads.Name = "mnuHightlightReads"; + this.mnuHightlightReads.Size = new System.Drawing.Size(133, 22); + this.mnuHightlightReads.Text = "Reads"; + this.mnuHightlightReads.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // toolStripMenuItem6 + // + this.toolStripMenuItem6.Name = "toolStripMenuItem6"; + this.toolStripMenuItem6.Size = new System.Drawing.Size(130, 6); + // + // fadeSpeedToolStripMenuItem + // + this.fadeSpeedToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuFadeSlow, + this.mnuFadeNormal, + this.mnuFadeFast, + this.mnuFadeNever, + this.toolStripMenuItem7, + this.mnuCustomFadeSpeed}); + this.fadeSpeedToolStripMenuItem.Name = "fadeSpeedToolStripMenuItem"; + this.fadeSpeedToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.fadeSpeedToolStripMenuItem.Text = "Fade speed"; + // + // mnuFadeSlow + // + this.mnuFadeSlow.Name = "mnuFadeSlow"; + this.mnuFadeSlow.Size = new System.Drawing.Size(136, 22); + this.mnuFadeSlow.Text = "Slow"; + this.mnuFadeSlow.Click += new System.EventHandler(this.mnuFadeSpeed_Click); + // + // mnuFadeNormal + // + this.mnuFadeNormal.Checked = true; + this.mnuFadeNormal.CheckState = System.Windows.Forms.CheckState.Checked; + this.mnuFadeNormal.Name = "mnuFadeNormal"; + this.mnuFadeNormal.Size = new System.Drawing.Size(136, 22); + this.mnuFadeNormal.Text = "Normal"; + this.mnuFadeNormal.Click += new System.EventHandler(this.mnuFadeSpeed_Click); + // + // mnuFadeFast + // + this.mnuFadeFast.Name = "mnuFadeFast"; + this.mnuFadeFast.Size = new System.Drawing.Size(136, 22); + this.mnuFadeFast.Text = "Fast"; + this.mnuFadeFast.Click += new System.EventHandler(this.mnuFadeSpeed_Click); + // + // mnuFadeNever + // + this.mnuFadeNever.Name = "mnuFadeNever"; + this.mnuFadeNever.Size = new System.Drawing.Size(136, 22); + this.mnuFadeNever.Text = "Do not fade"; + this.mnuFadeNever.Click += new System.EventHandler(this.mnuFadeSpeed_Click); + // + // toolStripMenuItem7 + // + this.toolStripMenuItem7.Name = "toolStripMenuItem7"; + this.toolStripMenuItem7.Size = new System.Drawing.Size(133, 6); + // + // mnuCustomFadeSpeed + // + this.mnuCustomFadeSpeed.Name = "mnuCustomFadeSpeed"; + this.mnuCustomFadeSpeed.Size = new System.Drawing.Size(136, 22); + this.mnuCustomFadeSpeed.Text = "Custom..."; + this.mnuCustomFadeSpeed.Click += new System.EventHandler(this.mnuCustomFadeSpeed_Click); + // + // dataTypeHighlightingToolStripMenuItem + // + this.dataTypeHighlightingToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuHighlightLabelledBytes, + this.mnuHighlightBreakpoints, + this.toolStripMenuItem8, + this.mnuHighlightCodeBytes, + this.mnuHighlightDataBytes, + this.mnuHighlightDmcDataBytes, + this.toolStripMenuItem11, + this.mnuHighlightChrDrawnBytes, + this.mnuHighlightChrReadBytes}); + this.dataTypeHighlightingToolStripMenuItem.Name = "dataTypeHighlightingToolStripMenuItem"; + this.dataTypeHighlightingToolStripMenuItem.Size = new System.Drawing.Size(256, 22); + this.dataTypeHighlightingToolStripMenuItem.Text = "Data Type Highlighting"; + // + // mnuHighlightLabelledBytes + // + this.mnuHighlightLabelledBytes.CheckOnClick = true; + this.mnuHighlightLabelledBytes.Name = "mnuHighlightLabelledBytes"; + this.mnuHighlightLabelledBytes.Size = new System.Drawing.Size(236, 22); + this.mnuHighlightLabelledBytes.Text = "Labeled bytes"; + this.mnuHighlightLabelledBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHighlightBreakpoints + // + this.mnuHighlightBreakpoints.CheckOnClick = true; + this.mnuHighlightBreakpoints.Name = "mnuHighlightBreakpoints"; + this.mnuHighlightBreakpoints.Size = new System.Drawing.Size(236, 22); + this.mnuHighlightBreakpoints.Text = "Breakpoints"; + this.mnuHighlightBreakpoints.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // toolStripMenuItem8 + // + this.toolStripMenuItem8.Name = "toolStripMenuItem8"; + this.toolStripMenuItem8.Size = new System.Drawing.Size(233, 6); + // + // mnuHighlightCodeBytes + // + this.mnuHighlightCodeBytes.CheckOnClick = true; + this.mnuHighlightCodeBytes.Name = "mnuHighlightCodeBytes"; + this.mnuHighlightCodeBytes.Size = new System.Drawing.Size(236, 22); + this.mnuHighlightCodeBytes.Text = "Code bytes (PRG ROM)"; + this.mnuHighlightCodeBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHighlightDataBytes + // + this.mnuHighlightDataBytes.CheckOnClick = true; + this.mnuHighlightDataBytes.Name = "mnuHighlightDataBytes"; + this.mnuHighlightDataBytes.Size = new System.Drawing.Size(236, 22); + this.mnuHighlightDataBytes.Text = "Data bytes (PRG ROM)"; + this.mnuHighlightDataBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHighlightDmcDataBytes + // + this.mnuHighlightDmcDataBytes.CheckOnClick = true; + this.mnuHighlightDmcDataBytes.Name = "mnuHighlightDmcDataBytes"; + this.mnuHighlightDmcDataBytes.Size = new System.Drawing.Size(236, 22); + this.mnuHighlightDmcDataBytes.Text = "DMC sample bytes (PRG ROM)"; + this.mnuHighlightDmcDataBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // toolStripMenuItem11 + // + this.toolStripMenuItem11.Name = "toolStripMenuItem11"; + this.toolStripMenuItem11.Size = new System.Drawing.Size(233, 6); + // + // mnuHighlightChrDrawnBytes + // + this.mnuHighlightChrDrawnBytes.CheckOnClick = true; + this.mnuHighlightChrDrawnBytes.Name = "mnuHighlightChrDrawnBytes"; + this.mnuHighlightChrDrawnBytes.Size = new System.Drawing.Size(236, 22); + this.mnuHighlightChrDrawnBytes.Text = "Drawn bytes (CHR ROM)"; + this.mnuHighlightChrDrawnBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHighlightChrReadBytes + // + this.mnuHighlightChrReadBytes.CheckOnClick = true; + this.mnuHighlightChrReadBytes.Name = "mnuHighlightChrReadBytes"; + this.mnuHighlightChrReadBytes.Size = new System.Drawing.Size(236, 22); + this.mnuHighlightChrReadBytes.Text = "Read bytes (CHR ROM)"; + this.mnuHighlightChrReadBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // fadeToolStripMenuItem + // + this.fadeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuHideUnusedBytes, + this.mnuHideReadBytes, + this.mnuHideWrittenBytes, + this.mnuHideExecutedBytes}); + this.fadeToolStripMenuItem.Name = "fadeToolStripMenuItem"; + this.fadeToolStripMenuItem.Size = new System.Drawing.Size(256, 22); + this.fadeToolStripMenuItem.Text = "De-emphasize"; + // + // mnuHideUnusedBytes + // + this.mnuHideUnusedBytes.CheckOnClick = true; + this.mnuHideUnusedBytes.Name = "mnuHideUnusedBytes"; + this.mnuHideUnusedBytes.Size = new System.Drawing.Size(152, 22); + this.mnuHideUnusedBytes.Text = "Unused bytes"; + this.mnuHideUnusedBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHideReadBytes + // + this.mnuHideReadBytes.CheckOnClick = true; + this.mnuHideReadBytes.Name = "mnuHideReadBytes"; + this.mnuHideReadBytes.Size = new System.Drawing.Size(152, 22); + this.mnuHideReadBytes.Text = "Read bytes"; + this.mnuHideReadBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHideWrittenBytes + // + this.mnuHideWrittenBytes.CheckOnClick = true; + this.mnuHideWrittenBytes.Name = "mnuHideWrittenBytes"; + this.mnuHideWrittenBytes.Size = new System.Drawing.Size(152, 22); + this.mnuHideWrittenBytes.Text = "Written bytes"; + this.mnuHideWrittenBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // mnuHideExecutedBytes + // + this.mnuHideExecutedBytes.CheckOnClick = true; + this.mnuHideExecutedBytes.Name = "mnuHideExecutedBytes"; + this.mnuHideExecutedBytes.Size = new System.Drawing.Size(152, 22); + this.mnuHideExecutedBytes.Text = "Executed bytes"; + this.mnuHideExecutedBytes.Click += new System.EventHandler(this.mnuColorProviderOptions_Click); + // + // toolStripMenuItem5 + // + this.toolStripMenuItem5.Name = "toolStripMenuItem5"; + this.toolStripMenuItem5.Size = new System.Drawing.Size(253, 6); + // + // mnuConfigureColors + // + this.mnuConfigureColors.Image = global::Mesen.GUI.Properties.Resources.PipetteSmall; + this.mnuConfigureColors.Name = "mnuConfigureColors"; + this.mnuConfigureColors.Size = new System.Drawing.Size(256, 22); + this.mnuConfigureColors.Text = "Configure Colors"; + this.mnuConfigureColors.Click += new System.EventHandler(this.mnuConfigureColors_Click); + // + // fontSizeToolStripMenuItem + // + this.fontSizeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuIncreaseFontSize, + this.mnuDecreaseFontSize, + this.mnuResetFontSize, + this.toolStripMenuItem12, + this.mnuSelectFont, + this.toolStripMenuItem13, + this.mnuHighDensityMode}); + this.fontSizeToolStripMenuItem.Image = global::Mesen.GUI.Properties.Resources.Font; + this.fontSizeToolStripMenuItem.Name = "fontSizeToolStripMenuItem"; + this.fontSizeToolStripMenuItem.Size = new System.Drawing.Size(256, 22); + this.fontSizeToolStripMenuItem.Text = "Font Options"; + // + // mnuIncreaseFontSize + // + this.mnuIncreaseFontSize.Name = "mnuIncreaseFontSize"; + this.mnuIncreaseFontSize.ShortcutKeyDisplayString = ""; + this.mnuIncreaseFontSize.Size = new System.Drawing.Size(217, 22); + this.mnuIncreaseFontSize.Text = "Increase"; + this.mnuIncreaseFontSize.Click += new System.EventHandler(this.mnuIncreaseFontSize_Click); + // + // mnuDecreaseFontSize + // + this.mnuDecreaseFontSize.Name = "mnuDecreaseFontSize"; + this.mnuDecreaseFontSize.ShortcutKeyDisplayString = ""; + this.mnuDecreaseFontSize.Size = new System.Drawing.Size(217, 22); + this.mnuDecreaseFontSize.Text = "Decrease"; + this.mnuDecreaseFontSize.Click += new System.EventHandler(this.mnuDecreaseFontSize_Click); + // + // mnuResetFontSize + // + this.mnuResetFontSize.Name = "mnuResetFontSize"; + this.mnuResetFontSize.ShortcutKeyDisplayString = ""; + this.mnuResetFontSize.Size = new System.Drawing.Size(217, 22); + this.mnuResetFontSize.Text = "Reset to Default"; + this.mnuResetFontSize.Click += new System.EventHandler(this.mnuResetFontSize_Click); + // + // toolStripMenuItem12 + // + this.toolStripMenuItem12.Name = "toolStripMenuItem12"; + this.toolStripMenuItem12.Size = new System.Drawing.Size(214, 6); + // + // mnuSelectFont + // + this.mnuSelectFont.Name = "mnuSelectFont"; + this.mnuSelectFont.Size = new System.Drawing.Size(217, 22); + this.mnuSelectFont.Text = "Select Font..."; + this.mnuSelectFont.Click += new System.EventHandler(this.mnuSelectFont_Click); + // + // toolStripMenuItem13 + // + this.toolStripMenuItem13.Name = "toolStripMenuItem13"; + this.toolStripMenuItem13.Size = new System.Drawing.Size(214, 6); + // + // mnuHighDensityMode + // + this.mnuHighDensityMode.CheckOnClick = true; + this.mnuHighDensityMode.Name = "mnuHighDensityMode"; + this.mnuHighDensityMode.Size = new System.Drawing.Size(217, 22); + this.mnuHighDensityMode.Text = "Use high text density mode"; + this.mnuHighDensityMode.CheckedChanged += new System.EventHandler(this.mnuHighDensityMode_CheckedChanged); + // + // autorefreshSpeedToolStripMenuItem + // + this.autorefreshSpeedToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuAutoRefreshLow, + this.mnuAutoRefreshNormal, + this.mnuAutoRefreshHigh}); + this.autorefreshSpeedToolStripMenuItem.Image = global::Mesen.GUI.Properties.Resources.Speed; + this.autorefreshSpeedToolStripMenuItem.Name = "autorefreshSpeedToolStripMenuItem"; + this.autorefreshSpeedToolStripMenuItem.Size = new System.Drawing.Size(256, 22); + this.autorefreshSpeedToolStripMenuItem.Text = "Auto-refresh Speed"; + // + // mnuAutoRefreshLow + // + this.mnuAutoRefreshLow.Name = "mnuAutoRefreshLow"; + this.mnuAutoRefreshLow.Size = new System.Drawing.Size(159, 22); + this.mnuAutoRefreshLow.Text = "Low (10 FPS)"; + this.mnuAutoRefreshLow.Click += new System.EventHandler(this.mnuAutoRefreshSpeed_Click); + // + // mnuAutoRefreshNormal + // + this.mnuAutoRefreshNormal.Name = "mnuAutoRefreshNormal"; + this.mnuAutoRefreshNormal.Size = new System.Drawing.Size(159, 22); + this.mnuAutoRefreshNormal.Text = "Normal (30 FPS)"; + this.mnuAutoRefreshNormal.Click += new System.EventHandler(this.mnuAutoRefreshSpeed_Click); + // + // mnuAutoRefreshHigh + // + this.mnuAutoRefreshHigh.Name = "mnuAutoRefreshHigh"; + this.mnuAutoRefreshHigh.Size = new System.Drawing.Size(159, 22); + this.mnuAutoRefreshHigh.Text = "High (60 FPS)"; + this.mnuAutoRefreshHigh.Click += new System.EventHandler(this.mnuAutoRefreshSpeed_Click); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(253, 6); + // + // mnuRefresh + // + this.mnuRefresh.Image = global::Mesen.GUI.Properties.Resources.Refresh; + this.mnuRefresh.Name = "mnuRefresh"; + this.mnuRefresh.Size = new System.Drawing.Size(256, 22); + this.mnuRefresh.Text = "Refresh"; + this.mnuRefresh.Click += new System.EventHandler(this.mnuRefresh_Click); + // + // toolStripMenuItem9 + // + this.toolStripMenuItem9.Name = "toolStripMenuItem9"; + this.toolStripMenuItem9.Size = new System.Drawing.Size(253, 6); + // + // mnuAutoRefresh + // + this.mnuAutoRefresh.Checked = true; + this.mnuAutoRefresh.CheckOnClick = true; + this.mnuAutoRefresh.CheckState = System.Windows.Forms.CheckState.Checked; + this.mnuAutoRefresh.Name = "mnuAutoRefresh"; + this.mnuAutoRefresh.Size = new System.Drawing.Size(256, 22); + this.mnuAutoRefresh.Text = "Auto-refresh"; + // + // mnuShowCharacters + // + this.mnuShowCharacters.Checked = true; + this.mnuShowCharacters.CheckOnClick = true; + this.mnuShowCharacters.CheckState = System.Windows.Forms.CheckState.Checked; + this.mnuShowCharacters.Name = "mnuShowCharacters"; + this.mnuShowCharacters.Size = new System.Drawing.Size(256, 22); + this.mnuShowCharacters.Text = "Show characters"; + // + // mnuShowLabelInfoOnMouseOver + // + this.mnuShowLabelInfoOnMouseOver.CheckOnClick = true; + this.mnuShowLabelInfoOnMouseOver.Name = "mnuShowLabelInfoOnMouseOver"; + this.mnuShowLabelInfoOnMouseOver.Size = new System.Drawing.Size(256, 22); + this.mnuShowLabelInfoOnMouseOver.Text = "Show label tooltip on mouseover"; + // + // toolStripMenuItem10 + // + this.toolStripMenuItem10.Name = "toolStripMenuItem10"; + this.toolStripMenuItem10.Size = new System.Drawing.Size(253, 6); + // + // mnuIgnoreRedundantWrites + // + this.mnuIgnoreRedundantWrites.CheckOnClick = true; + this.mnuIgnoreRedundantWrites.Name = "mnuIgnoreRedundantWrites"; + this.mnuIgnoreRedundantWrites.Size = new System.Drawing.Size(256, 22); + this.mnuIgnoreRedundantWrites.Text = "Ignore writes that do not alter data"; + // + // mnuEnablePerByteNavigation + // + this.mnuEnablePerByteNavigation.CheckOnClick = true; + this.mnuEnablePerByteNavigation.Name = "mnuEnablePerByteNavigation"; + this.mnuEnablePerByteNavigation.Size = new System.Drawing.Size(256, 22); + this.mnuEnablePerByteNavigation.Text = "Use per-byte left/right navigation"; + this.mnuEnablePerByteNavigation.CheckedChanged += new System.EventHandler(this.mnuEnablePerByteNavigation_CheckedChanged); + // + // mnuByteEditingMode + // + this.mnuByteEditingMode.CheckOnClick = true; + this.mnuByteEditingMode.Name = "mnuByteEditingMode"; + this.mnuByteEditingMode.Size = new System.Drawing.Size(256, 22); + this.mnuByteEditingMode.Text = "Use per-byte editing mode"; + this.mnuByteEditingMode.CheckedChanged += new System.EventHandler(this.mnuByteEditingMode_CheckedChanged); + // + // mnuHighlightCurrentRowColumn + // + this.mnuHighlightCurrentRowColumn.CheckOnClick = true; + this.mnuHighlightCurrentRowColumn.Name = "mnuHighlightCurrentRowColumn"; + this.mnuHighlightCurrentRowColumn.Size = new System.Drawing.Size(256, 22); + this.mnuHighlightCurrentRowColumn.Text = "Highlight current row/column"; + this.mnuHighlightCurrentRowColumn.CheckedChanged += new System.EventHandler(this.mnuHighlightCurrentRowColumn_CheckedChanged); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuGoToAll, + this.mnuGoTo, + this.toolStripMenuItem14, + this.mnuFind, + this.mnuFindNext, + this.mnuFindPrev}); + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(54, 20); + this.toolStripMenuItem1.Text = "Search"; + // + // mnuGoToAll + // + this.mnuGoToAll.Name = "mnuGoToAll"; + this.mnuGoToAll.Size = new System.Drawing.Size(145, 22); + this.mnuGoToAll.Text = "Go to All"; + this.mnuGoToAll.Click += new System.EventHandler(this.mnuGoToAll_Click); + // + // mnuGoTo + // + this.mnuGoTo.Name = "mnuGoTo"; + this.mnuGoTo.Size = new System.Drawing.Size(145, 22); + this.mnuGoTo.Text = "Go To..."; + this.mnuGoTo.Click += new System.EventHandler(this.mnuGoTo_Click); + // + // toolStripMenuItem14 + // + this.toolStripMenuItem14.Name = "toolStripMenuItem14"; + this.toolStripMenuItem14.Size = new System.Drawing.Size(142, 6); + // + // mnuFind + // + this.mnuFind.Image = global::Mesen.GUI.Properties.Resources.Find; + this.mnuFind.Name = "mnuFind"; + this.mnuFind.Size = new System.Drawing.Size(145, 22); + this.mnuFind.Text = "Find..."; + this.mnuFind.Click += new System.EventHandler(this.mnuFind_Click); + // + // mnuFindNext + // + this.mnuFindNext.Image = global::Mesen.GUI.Properties.Resources.NextArrow; + this.mnuFindNext.Name = "mnuFindNext"; + this.mnuFindNext.Size = new System.Drawing.Size(145, 22); + this.mnuFindNext.Text = "Find Next"; + this.mnuFindNext.Click += new System.EventHandler(this.mnuFindNext_Click); + // + // mnuFindPrev + // + this.mnuFindPrev.Image = global::Mesen.GUI.Properties.Resources.PreviousArrow; + this.mnuFindPrev.Name = "mnuFindPrev"; + this.mnuFindPrev.Size = new System.Drawing.Size(145, 22); + this.mnuFindPrev.Text = "Find Previous"; + this.mnuFindPrev.Click += new System.EventHandler(this.mnuFindPrev_Click); + // + // tabMain + // + this.tabMain.Controls.Add(this.tpgMemoryViewer); + this.tabMain.Controls.Add(this.tpgAccessCounters); + this.tabMain.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabMain.Location = new System.Drawing.Point(0, 24); + this.tabMain.Name = "tabMain"; + this.tabMain.SelectedIndex = 0; + this.tabMain.Size = new System.Drawing.Size(614, 369); + this.tabMain.TabIndex = 4; + this.tabMain.SelectedIndexChanged += new System.EventHandler(this.tabMain_SelectedIndexChanged); + // + // tpgMemoryViewer + // + this.tpgMemoryViewer.Controls.Add(this.panel1); + this.tpgMemoryViewer.Location = new System.Drawing.Point(4, 22); + this.tpgMemoryViewer.Margin = new System.Windows.Forms.Padding(0); + this.tpgMemoryViewer.Name = "tpgMemoryViewer"; + this.tpgMemoryViewer.Size = new System.Drawing.Size(606, 343); + this.tpgMemoryViewer.TabIndex = 0; + this.tpgMemoryViewer.Text = "Memory Viewer"; + this.tpgMemoryViewer.UseVisualStyleBackColor = true; + // + // panel1 + // + this.panel1.Controls.Add(this.flowLayoutPanel1); + this.panel1.Controls.Add(this.ctrlHexViewer); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Margin = new System.Windows.Forms.Padding(0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(606, 343); + this.panel1.TabIndex = 4; + // + // tpgAccessCounters + // + this.tpgAccessCounters.Location = new System.Drawing.Point(4, 22); + this.tpgAccessCounters.Margin = new System.Windows.Forms.Padding(0); + this.tpgAccessCounters.Name = "tpgAccessCounters"; + this.tpgAccessCounters.Size = new System.Drawing.Size(606, 343); + this.tpgAccessCounters.TabIndex = 1; + this.tpgAccessCounters.Text = "Access Counters"; + this.tpgAccessCounters.UseVisualStyleBackColor = true; + // + // frmMemoryViewer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(614, 393); + this.Controls.Add(this.tabMain); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.MinimumSize = new System.Drawing.Size(429, 337); + this.Name = "frmMemoryViewer"; + this.Text = "Memory Tools"; + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.PerformLayout(); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.tabMain.ResumeLayout(false); + this.tpgMemoryViewer.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.ctrlHexViewer ctrlHexViewer; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.Label lblViewMemoryType; + private Mesen.GUI.Debugger.Controls.ComboBoxWithSeparator cboMemoryType; + private Mesen.GUI.Controls.ctrlMesenMenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuFind; + private System.Windows.Forms.ToolStripMenuItem mnuFindNext; + private System.Windows.Forms.ToolStripMenuItem mnuFindPrev; + private System.Windows.Forms.ToolStripMenuItem mnuGoTo; + private System.Windows.Forms.ToolStripMenuItem mnuView; + private System.Windows.Forms.ToolStripMenuItem mnuRefresh; + private System.Windows.Forms.ToolStripMenuItem fontSizeToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuIncreaseFontSize; + private System.Windows.Forms.ToolStripMenuItem mnuDecreaseFontSize; + private System.Windows.Forms.ToolStripMenuItem mnuResetFontSize; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuClose; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem mnuAutoRefresh; + private System.Windows.Forms.ToolStripMenuItem mnuImport; + private System.Windows.Forms.ToolStripMenuItem mnuExport; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; + private System.Windows.Forms.TabControl tabMain; + private System.Windows.Forms.TabPage tpgMemoryViewer; + private System.Windows.Forms.TabPage tpgAccessCounters; + private System.Windows.Forms.ToolStripMenuItem mnuLoadTblFile; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem4; + private System.Windows.Forms.ToolStripMenuItem mnuShowCharacters; + private System.Windows.Forms.ToolStripMenuItem mnuResetTblMappings; + private System.Windows.Forms.ToolStripMenuItem highlightToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuHightlightReads; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightWrites; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightExecution; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem6; + private System.Windows.Forms.ToolStripMenuItem fadeSpeedToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem5; + private System.Windows.Forms.ToolStripMenuItem mnuFadeSlow; + private System.Windows.Forms.ToolStripMenuItem mnuFadeNormal; + private System.Windows.Forms.ToolStripMenuItem mnuFadeFast; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7; + private System.Windows.Forms.ToolStripMenuItem mnuCustomFadeSpeed; + private System.Windows.Forms.ToolStripMenuItem fadeToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuHideUnusedBytes; + private System.Windows.Forms.ToolStripMenuItem mnuHideReadBytes; + private System.Windows.Forms.ToolStripMenuItem mnuHideWrittenBytes; + private System.Windows.Forms.ToolStripMenuItem mnuHideExecutedBytes; + private System.Windows.Forms.ToolStripMenuItem mnuFadeNever; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.ToolStripMenuItem dataTypeHighlightingToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightCodeBytes; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightDataBytes; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem11; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightChrDrawnBytes; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightChrReadBytes; + private System.Windows.Forms.ToolStripMenuItem mnuConfigureColors; + private System.Windows.Forms.ToolStripMenuItem mnuShowLabelInfoOnMouseOver; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightLabelledBytes; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightDmcDataBytes; + private System.Windows.Forms.ToolStripMenuItem autorefreshSpeedToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuAutoRefreshLow; + private System.Windows.Forms.ToolStripMenuItem mnuAutoRefreshNormal; + private System.Windows.Forms.ToolStripMenuItem mnuAutoRefreshHigh; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem9; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem10; + private System.Windows.Forms.ToolStripMenuItem mnuIgnoreRedundantWrites; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem12; + private System.Windows.Forms.ToolStripMenuItem mnuHighDensityMode; + private System.Windows.Forms.ToolStripMenuItem mnuEnablePerByteNavigation; + private System.Windows.Forms.ToolStripMenuItem mnuSelectFont; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem13; + private System.Windows.Forms.ToolStripMenuItem mnuByteEditingMode; + private System.Windows.Forms.ToolStripMenuItem mnuGoToAll; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem14; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightBreakpoints; + private System.Windows.Forms.ToolStripMenuItem mnuHighlightCurrentRowColumn; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmMemoryTools.resx b/UI/Debugger/frmMemoryTools.resx new file mode 100644 index 0000000..e83821e --- /dev/null +++ b/UI/Debugger/frmMemoryTools.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/UI/Debugger/frmTraceLogger.cs b/UI/Debugger/frmTraceLogger.cs index d6e05ff..f22ea9a 100644 --- a/UI/Debugger/frmTraceLogger.cs +++ b/UI/Debugger/frmTraceLogger.cs @@ -29,20 +29,20 @@ namespace Mesen.GUI.Debugger { InitializeComponent(); - DebugInfo debugInfo = ConfigManager.Config.Debug; - if(!debugInfo.TraceLoggerSize.IsEmpty) { + TraceLoggerInfo config = ConfigManager.Config.Debug.TraceLogger; + if(!config.WindowSize.IsEmpty) { this.StartPosition = FormStartPosition.Manual; - this.Size = debugInfo.TraceLoggerSize; - this.Location = debugInfo.TraceLoggerLocation; + this.Size = config.WindowSize; + this.Location = config.WindowLocation; } - txtTraceLog.BaseFont = new Font(debugInfo.TraceFontFamily, debugInfo.TraceFontSize, debugInfo.TraceFontStyle); - txtTraceLog.TextZoom = debugInfo.TraceTextZoom; + txtTraceLog.BaseFont = new Font(config.FontFamily, config.FontSize, config.FontStyle); + txtTraceLog.TextZoom = config.TextZoom; - mnuAutoRefresh.Checked = debugInfo.TraceAutoRefresh; - _lineCount = debugInfo.TraceLineCount; + mnuAutoRefresh.Checked = config.AutoRefresh; + _lineCount = config.LineCount; - _entityBinder.Entity = debugInfo.TraceLoggerOptions; + _entityBinder.Entity = config.LogOptions; _entityBinder.AddBinding("ShowByteCode", chkShowByteCode); _entityBinder.AddBinding("ShowCpuCycles", chkShowCpuCycles); _entityBinder.AddBinding("ShowEffectiveAddresses", chkShowEffectiveAddresses); @@ -90,16 +90,16 @@ namespace Mesen.GUI.Debugger private void InitShortcuts() { - //TODO - /*mnuIncreaseFontSize.InitShortcut(this, nameof(DebuggerShortcutsConfig.IncreaseFontSize)); + mnuIncreaseFontSize.InitShortcut(this, nameof(DebuggerShortcutsConfig.IncreaseFontSize)); mnuDecreaseFontSize.InitShortcut(this, nameof(DebuggerShortcutsConfig.DecreaseFontSize)); mnuResetFontSize.InitShortcut(this, nameof(DebuggerShortcutsConfig.ResetFontSize)); mnuRefresh.InitShortcut(this, nameof(DebuggerShortcutsConfig.Refresh)); mnuCopy.InitShortcut(this, nameof(DebuggerShortcutsConfig.Copy)); mnuSelectAll.InitShortcut(this, nameof(DebuggerShortcutsConfig.SelectAll)); - mnuEditInMemoryViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.CodeWindow_EditInMemoryViewer)); - mnuViewInDisassembly.InitShortcut(this, nameof(DebuggerShortcutsConfig.MemoryViewer_ViewInDisassembly));*/ + //TODO + //mnuEditInMemoryViewer.InitShortcut(this, nameof(DebuggerShortcutsConfig.CodeWindow_EditInMemoryViewer)); + //mnuViewInDisassembly.InitShortcut(this, nameof(DebuggerShortcutsConfig.MemoryViewer_ViewInDisassembly)); } protected override void OnLoad(EventArgs e) @@ -122,16 +122,16 @@ namespace Mesen.GUI.Debugger base.OnFormClosing(e); - DebugInfo debugInfo = ConfigManager.Config.Debug; - debugInfo.TraceAutoRefresh = mnuAutoRefresh.Checked; - debugInfo.TraceLineCount = _lineCount; - debugInfo.TraceLoggerSize = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Size : this.Size; - debugInfo.TraceLoggerLocation = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Location : this.Location; + TraceLoggerInfo config = ConfigManager.Config.Debug.TraceLogger; + config.AutoRefresh = mnuAutoRefresh.Checked; + config.LineCount = _lineCount; + config.WindowSize = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Size : this.Size; + config.WindowLocation = this.WindowState != FormWindowState.Normal ? this.RestoreBounds.Location : this.Location; - debugInfo.TraceFontFamily = txtTraceLog.BaseFont.FontFamily.Name; - debugInfo.TraceFontSize = txtTraceLog.BaseFont.Size; - debugInfo.TraceFontStyle = txtTraceLog.BaseFont.Style; - debugInfo.TraceTextZoom = txtTraceLog.TextZoom; + config.FontFamily = txtTraceLog.BaseFont.FontFamily.Name; + config.FontSize = txtTraceLog.BaseFont.Size; + config.FontStyle = txtTraceLog.BaseFont.Style; + config.TextZoom = txtTraceLog.TextZoom; _entityBinder.UpdateObject(); @@ -270,7 +270,6 @@ namespace Mesen.GUI.Debugger SetOptions(); Task.Run(() => { //Update trace log in another thread for performance - //DebugState state = new DebugState(); DebugState state = DebugApi.GetState(); if(_previousCycleCount != state.Cpu.CycleCount || forceUpdate) { string newTrace = DebugApi.GetExecutionTrace((UInt32)_lineCount); diff --git a/UI/Forms/EntityBinder.cs b/UI/Forms/EntityBinder.cs index 7fd3e5c..79d951f 100644 --- a/UI/Forms/EntityBinder.cs +++ b/UI/Forms/EntityBinder.cs @@ -14,7 +14,7 @@ namespace Mesen.GUI.Forms { public class EntityBinder { - private Dictionary _bindings = new Dictionary(); + private Dictionary _bindings = new Dictionary(); private Dictionary _fieldFormat = new Dictionary(); private Dictionary _fieldInfo = null; @@ -27,7 +27,7 @@ namespace Mesen.GUI.Forms public bool Updating { get; private set; } - public void AddBinding(string fieldName, Control bindedField, eNumberFormat format = eNumberFormat.Default) + public void AddBinding(string fieldName, object bindedField, eNumberFormat format = eNumberFormat.Default) { if(BindedType == null) { throw new Exception("Need to override BindedType to use bindings"); @@ -62,7 +62,7 @@ namespace Mesen.GUI.Forms { this.Updating = true; - foreach(KeyValuePair kvp in _bindings) { + foreach(KeyValuePair kvp in _bindings) { if(!_fieldInfo.ContainsKey(kvp.Key)) { throw new Exception("Invalid binding key"); } else { @@ -71,20 +71,22 @@ namespace Mesen.GUI.Forms object value = field.GetValue(this.Entity); if(kvp.Value is TextBox) { if(value is IFormattable) { - kvp.Value.Text = ((IFormattable)value).ToString(format == eNumberFormat.Decimal ? "" : "X", System.Globalization.CultureInfo.InvariantCulture); + ((TextBox)kvp.Value).Text = ((IFormattable)value).ToString(format == eNumberFormat.Decimal ? "" : "X", System.Globalization.CultureInfo.InvariantCulture); } else { - kvp.Value.Text = value == null ? "" : ((string)value).Replace(Environment.NewLine, "\n").Replace("\n", Environment.NewLine); + ((TextBox)kvp.Value).Text = value == null ? "" : ((string)value).Replace(Environment.NewLine, "\n").Replace("\n", Environment.NewLine); } } else if(kvp.Value is ctrlPathSelection) { - kvp.Value.Text = (string)value; + ((ctrlPathSelection)kvp.Value).Text = (string)value; } else if(kvp.Value is CheckBox) { ((CheckBox)kvp.Value).Checked = Convert.ToBoolean(value); + } else if(kvp.Value is ToolStripMenuItem) { + ((ToolStripMenuItem)kvp.Value).Checked = Convert.ToBoolean(value); } else if(kvp.Value is ctrlRiskyOption) { ((ctrlRiskyOption)kvp.Value).Checked = Convert.ToBoolean(value); } else if(kvp.Value is RadioButton) { ((RadioButton)kvp.Value).Checked = (bool)value; } else if(kvp.Value is Panel) { - RadioButton radio = kvp.Value.Controls.OfType().FirstOrDefault(r => r.Tag.Equals(value)); + RadioButton radio = ((Panel)kvp.Value).Controls.OfType().FirstOrDefault(r => r.Tag.Equals(value)); if(radio != null) { radio.Checked = true; } else { @@ -146,7 +148,7 @@ namespace Mesen.GUI.Forms public void UpdateObject() { - foreach(KeyValuePair kvp in _bindings) { + foreach(KeyValuePair kvp in _bindings) { if(!_fieldInfo.ContainsKey(kvp.Key)) { throw new Exception("Invalid binding key"); } else { @@ -154,7 +156,7 @@ namespace Mesen.GUI.Forms FieldInfoWrapper field = _fieldInfo[kvp.Key]; eNumberFormat format = _fieldFormat[kvp.Key]; if(kvp.Value is TextBox) { - object value = kvp.Value.Text; + object value = ((TextBox)kvp.Value).Text; NumberStyles numberStyle = format == eNumberFormat.Decimal ? NumberStyles.Integer : NumberStyles.HexNumber; if(field.FieldType != typeof(string)) { value = ((string)value).Trim().Replace("$", "").Replace("0x", ""); @@ -208,6 +210,10 @@ namespace Mesen.GUI.Forms } else if(field.FieldType == typeof(byte)) { field.SetValue(Entity, ((CheckBox)kvp.Value).Checked ? (byte)1 : (byte)0); } + } else if(kvp.Value is ToolStripMenuItem) { + if(field.FieldType == typeof(bool)) { + field.SetValue(Entity, ((ToolStripMenuItem)kvp.Value).Checked); + } } else if(kvp.Value is ctrlRiskyOption) { if(field.FieldType == typeof(bool)) { field.SetValue(Entity, ((ctrlRiskyOption)kvp.Value).Checked); @@ -217,7 +223,7 @@ namespace Mesen.GUI.Forms } else if(kvp.Value is RadioButton) { field.SetValue(Entity, ((RadioButton)kvp.Value).Checked); } else if(kvp.Value is Panel) { - field.SetValue(Entity, kvp.Value.Controls.OfType().FirstOrDefault(r => r.Checked).Tag); + field.SetValue(Entity, ((Panel)kvp.Value).Controls.OfType().FirstOrDefault(r => r.Checked).Tag); } else if(kvp.Value is ctrlTrackbar) { if(field.FieldType == typeof(Int32)) { field.SetValue(Entity, (Int32)((ctrlTrackbar)kvp.Value).Value); diff --git a/UI/Forms/frmMain.Designer.cs b/UI/Forms/frmMain.Designer.cs index 36aa617..a928f23 100644 --- a/UI/Forms/frmMain.Designer.cs +++ b/UI/Forms/frmMain.Designer.cs @@ -38,6 +38,7 @@ this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuMemoryTools = new System.Windows.Forms.ToolStripMenuItem(); this.mnuMain.SuspendLayout(); this.SuspendLayout(); // @@ -84,6 +85,7 @@ this.mnuRun100Instructions, this.toolStripMenuItem1, this.mnuDebugger, + this.mnuMemoryTools, this.mnuTraceLogger}); this.debugToolStripMenuItem.Name = "debugToolStripMenuItem"; this.debugToolStripMenuItem.Size = new System.Drawing.Size(54, 20); @@ -120,19 +122,31 @@ // // mnuDebugger // + this.mnuDebugger.Enabled = false; + this.mnuDebugger.Image = global::Mesen.GUI.Properties.Resources.Debugger; this.mnuDebugger.Name = "mnuDebugger"; this.mnuDebugger.Size = new System.Drawing.Size(163, 22); this.mnuDebugger.Text = "Debugger"; // // mnuTraceLogger // + this.mnuTraceLogger.Image = global::Mesen.GUI.Properties.Resources.LogWindow; this.mnuTraceLogger.Name = "mnuTraceLogger"; this.mnuTraceLogger.Size = new System.Drawing.Size(163, 22); this.mnuTraceLogger.Text = "Trace Logger"; this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); // + // mnuMemoryTools + // + this.mnuMemoryTools.Image = global::Mesen.GUI.Properties.Resources.CheatCode; + this.mnuMemoryTools.Name = "mnuMemoryTools"; + this.mnuMemoryTools.Size = new System.Drawing.Size(163, 22); + this.mnuMemoryTools.Text = "Memory Tools"; + this.mnuMemoryTools.Click += new System.EventHandler(this.mnuMemoryTools_Click); + // // frmMain // + this.AllowDrop = true; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(512, 475); @@ -161,5 +175,6 @@ private System.Windows.Forms.ToolStripMenuItem mnuRun; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem mnuRun100Instructions; + private System.Windows.Forms.ToolStripMenuItem mnuMemoryTools; } } \ No newline at end of file diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index ee6caee..73e928d 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -34,6 +35,12 @@ namespace Mesen.GUI.Forms frm.Show(); } + private void mnuMemoryTools_Click(object sender, EventArgs e) + { + frmMemoryTools frm = new frmMemoryTools(); + frm.Show(); + } + private void mnuStep_Click(object sender, EventArgs e) { DebugApi.Step(1); @@ -44,10 +51,7 @@ namespace Mesen.GUI.Forms using(OpenFileDialog ofd = new OpenFileDialog()) { ofd.Filter = ResourceHelper.GetMessage("FilterRom"); if(ofd.ShowDialog() == DialogResult.OK) { - EmuApi.LoadRom(ofd.FileName); - Task.Run(() => { - EmuApi.Run(); - }); + LoadFile(ofd.FileName); } } } @@ -61,5 +65,45 @@ namespace Mesen.GUI.Forms { DebugApi.Step(1000); } + + private void LoadFile(string filepath) + { + EmuApi.LoadRom(filepath); + Task.Run(() => { + EmuApi.Run(); + }); + } + + protected override void OnDragDrop(DragEventArgs e) + { + base.OnDragDrop(e); + + try { + string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); + if(File.Exists(files[0])) { + LoadFile(files[0]); + this.Activate(); + } else { + //InteropEmu.DisplayMessage("Error", "File not found: " + files[0]); + } + } catch(Exception ex) { + MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, ex.ToString()); + } + } + + protected override void OnDragEnter(DragEventArgs e) + { + base.OnDragEnter(e); + + try { + if(e.Data != null && e.Data.GetDataPresent(DataFormats.FileDrop)) { + e.Effect = DragDropEffects.Copy; + } else { + //InteropEmu.DisplayMessage("Error", "Unsupported operation."); + } + } catch(Exception ex) { + MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, ex.ToString()); + } + } } } diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index 6926793..d7ae6ed 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -35,6 +35,31 @@ namespace Mesen.GUI DebugApi.GetStateWrapper(ref state); return state; } + + [DllImport(DllPath)] public static extern Int32 GetMemorySize(SnesMemoryType type); + [DllImport(DllPath)] public static extern Byte GetMemoryValue(SnesMemoryType type, UInt32 address); + [DllImport(DllPath)] public static extern void SetMemoryValue(SnesMemoryType type, UInt32 address, byte value); + [DllImport(DllPath)] public static extern void SetMemoryValues(SnesMemoryType type, UInt32 address, [In] byte[] data, Int32 length); + [DllImport(DllPath)] public static extern void SetMemoryState(SnesMemoryType type, [In] byte[] buffer, Int32 length); + + [DllImport(DllPath, EntryPoint = "GetMemoryState")] private static extern void GetMemoryStateWrapper(SnesMemoryType type, [In, Out] byte[] buffer); + public static byte[] GetMemoryState(SnesMemoryType type) + { + byte[] buffer = new byte[DebugApi.GetMemorySize(type)]; + DebugApi.GetMemoryStateWrapper(type, buffer); + return buffer; + } + } + + public enum SnesMemoryType + { + CpuMemory, + PrgRom, + WorkRam, + SaveRam, + VideoRam, + SpriteRam, + CGRam, } public struct CpuState diff --git a/UI/Interop/EmuApi.cs b/UI/Interop/EmuApi.cs index 978f8ca..cde8024 100644 --- a/UI/Interop/EmuApi.cs +++ b/UI/Interop/EmuApi.cs @@ -20,6 +20,9 @@ namespace Mesen.GUI [DllImport(DllPath, EntryPoint = "GetMesenVersion")] private static extern UInt32 GetMesenVersionWrapper(); + [DllImport(DllPath)] public static extern IntPtr RegisterNotificationCallback(NotificationListener.NotificationCallback callback); + [DllImport(DllPath)] public static extern void UnregisterNotificationCallback(IntPtr notificationListener); + [DllImport(DllPath)] public static extern void InitializeEmu([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string homeFolder, IntPtr windowHandle, IntPtr dxViewerHandle, [MarshalAs(UnmanagedType.I1)]bool noAudio, [MarshalAs(UnmanagedType.I1)]bool noVideo, [MarshalAs(UnmanagedType.I1)]bool noInput); [DllImport(DllPath)] public static extern void Release(); diff --git a/UI/Interop/NotificationListener.cs b/UI/Interop/NotificationListener.cs new file mode 100644 index 0000000..29e64e6 --- /dev/null +++ b/UI/Interop/NotificationListener.cs @@ -0,0 +1,61 @@ +using System; + +namespace Mesen.GUI +{ + public class NotificationListener : IDisposable + { + public delegate void NotificationCallback(int type, IntPtr parameter); + public delegate void NotificationEventHandler(NotificationEventArgs e); + public event NotificationEventHandler OnNotification; + + //Need to keep a reference to this callback, or it will get garbage collected (since the only reference to it is on the native side) + NotificationCallback _callback; + IntPtr _notificationListener; + + public NotificationListener() + { + _callback = (int type, IntPtr parameter) => { + this.ProcessNotification(type, parameter); + }; + _notificationListener = EmuApi.RegisterNotificationCallback(_callback); + } + + public void Dispose() + { + EmuApi.UnregisterNotificationCallback(_notificationListener); + } + + public void ProcessNotification(int type, IntPtr parameter) + { + if(this.OnNotification != null) { + this.OnNotification(new NotificationEventArgs() { + NotificationType = (ConsoleNotificationType)type, + Parameter = parameter + }); + } + } + } + + public class NotificationEventArgs + { + public ConsoleNotificationType NotificationType; + public IntPtr Parameter; + } + + public enum ConsoleNotificationType + { + GameLoaded = 0, + StateLoaded = 1, + GameReset = 2, + GamePaused = 3, + GameResumed = 4, + GameStopped = 5, + CodeBreak = 6, + PpuFrameDone = 7, + ResolutionChanged = 8, + ConfigChanged = 9, + ExecuteShortcut = 10, + EmulationStopped = 11, + BeforeEmulationStop = 12, + } +} diff --git a/UI/UI.csproj b/UI/UI.csproj index 23eef7c..afa8a39 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -213,7 +213,10 @@ - + + + + UserControl @@ -277,6 +280,33 @@ Component + + Component + + + UserControl + + + ctrlDbgShortcuts.cs + + + UserControl + + + ctrlHexViewer.cs + + + Form + + + frmDbgPreferences.cs + + + Form + + + frmDbgShortcutGetKey.cs + Component @@ -293,6 +323,24 @@ ctrlScrollableTextbox.cs + + Form + + + frmFadeSpeed.cs + + + Form + + + frmGoToLine.cs + + + Form + + + frmMemoryTools.cs + Form @@ -300,6 +348,29 @@ frmTraceLogger.cs + + Component + + + + + + + + + + + Component + + + + + + + + + + BaseConfigForm.cs @@ -325,6 +396,7 @@ + @@ -355,12 +427,36 @@ ctrlRenderer.cs + + ctrlDbgShortcuts.cs + + + ctrlHexViewer.cs + + + frmDbgPreferences.cs + + + frmDbgShortcutGetKey.cs + ctrlScrollableTextbox.cs + + frmFadeSpeed.cs + + + frmGoToLine.cs + + + frmMemoryTools.cs + frmTraceLogger.cs + + HexBox.cs + BaseConfigForm.cs