diff --git a/Core/BaseRenderer.cpp b/Core/BaseRenderer.cpp new file mode 100644 index 0000000..4d96285 --- /dev/null +++ b/Core/BaseRenderer.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" +#include +#include "BaseRenderer.h" +#include "Console.h" +#include "MessageManager.h" + +BaseRenderer::BaseRenderer(shared_ptr console, bool registerAsMessageManager) +{ + _console = console; + + if(registerAsMessageManager) { + //Only display messages on the master CPU's screen + MessageManager::RegisterMessageManager(this); + } +} + +BaseRenderer::~BaseRenderer() +{ + MessageManager::UnregisterMessageManager(this); +} + +void BaseRenderer::DisplayMessage(string title, string message) +{ + shared_ptr toast(new ToastInfo(title, message, 4000)); + _toasts.push_front(toast); +} + +void BaseRenderer::RemoveOldToasts() +{ + _toasts.remove_if([](shared_ptr toast) { return toast->IsToastExpired(); }); +} + +void BaseRenderer::DrawToasts() +{ + RemoveOldToasts(); + + int counter = 0; + int lastHeight = 5; + for(shared_ptr toast : _toasts) { + if(counter < 6) { + DrawToast(toast, lastHeight); + } else { + break; + } + counter++; + } +} + +std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount) +{ + using std::wstring; + wstring text = utf8::utf8::decode(utf8Text); + wstring wrappedText; + list words; + wstring currentWord; + for(size_t i = 0, len = text.length(); i < len; i++) { + if(text[i] == L' ' || text[i] == L'\n') { + if(currentWord.length() > 0) { + words.push_back(currentWord); + currentWord.clear(); + } + } else { + currentWord += text[i]; + } + } + if(currentWord.length() > 0) { + words.push_back(currentWord); + } + + lineCount = 1; + float spaceWidth = MeasureString(L" "); + + float lineWidth = 0.0f; + for(wstring word : words) { + for(unsigned int i = 0; i < word.size(); i++) { + if(!ContainsCharacter(word[i])) { + word[i] = L'?'; + } + } + + float wordWidth = MeasureString(word.c_str()); + + if(lineWidth + wordWidth < maxLineWidth) { + wrappedText += word + L" "; + lineWidth += wordWidth + spaceWidth; + } else { + wrappedText += L"\n" + word + L" "; + lineWidth = wordWidth + spaceWidth; + lineCount++; + } + } + + return wrappedText; +} + +void BaseRenderer::DrawToast(shared_ptr toast, int &lastHeight) +{ + //Get opacity for fade in/out effect + uint8_t opacity = (uint8_t)(toast->GetOpacity()*255); + int textLeftMargin = 4; + + int lineHeight = 25; + string text = "[" + toast->GetToastTitle() + "] " + toast->GetToastMessage(); + uint32_t lineCount = 0; + std::wstring wrappedText = WrapText(text, (float)(_screenWidth - textLeftMargin * 2 - 20), lineCount); + lastHeight += lineCount * lineHeight; + DrawString(wrappedText, textLeftMargin, _screenHeight - lastHeight, opacity, opacity, opacity, opacity); +} + +void BaseRenderer::DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity) +{ + std::wstring textStr = utf8::utf8::decode(message); + DrawString(textStr, x, y, r, g, b, opacity); +} + +void BaseRenderer::ShowFpsCounter(int lineNumber) +{ + int yPos = 13 + 24 * lineNumber; + if(_fpsTimer.GetElapsedMS() > 1000) { + //Update fps every sec + //TODO + uint32_t frameCount = 0; //_console->GetFrameCount(); + if(_lastFrameCount > frameCount) { + _currentFPS = 0; + } else { + _currentFPS = (int)(std::round((double)(frameCount - _lastFrameCount) / (_fpsTimer.GetElapsedMS() / 1000))); + _currentRenderedFPS = (int)(std::round((double)(_renderedFrameCount - _lastRenderedFrameCount) / (_fpsTimer.GetElapsedMS() / 1000))); + } + _lastFrameCount = frameCount; + _lastRenderedFrameCount = _renderedFrameCount; + _fpsTimer.Reset(); + } + + if(_currentFPS > 5000) { + _currentFPS = 0; + } + if(_currentRenderedFPS > 5000) { + _currentRenderedFPS = 0; + } + + string fpsString = string("FPS: ") + std::to_string(_currentFPS) + " / " + std::to_string(_currentRenderedFPS); + DrawString(fpsString, _screenWidth - 125, yPos, 250, 235, 215); +} + +void BaseRenderer::ShowGameTimer(int lineNumber) +{ + //TODO + /*int yPos = 13 + 24 * lineNumber; + double frameCount = _console->GetFrameCount(); + double frameRate = _console->GetModel() == NesModel::NTSC ? 60.098811862348404716732985230828 : 50.006977968268290848936010226333; + //uint32_t milliseconds = (uint32_t)(frameCount / 60.1 * 1000) % 1000; + uint32_t seconds = (uint32_t)(frameCount / frameRate) % 60; + uint32_t minutes = (uint32_t)(frameCount / frameRate / 60) % 60; + uint32_t hours = (uint32_t)(frameCount / frameRate / 3600); + + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << hours << ":"; + ss << std::setw(2) << std::setfill('0') << minutes << ":"; + ss << std::setw(2) << std::setfill('0') << seconds; + //ss << "." << std::setw(3) << std::setfill('0') << milliseconds; + DrawString(ss.str(), _screenWidth - 95, yPos, 250, 235, 215);*/ +} + +void BaseRenderer::ShowLagCounter(int lineNumber) +{ + //TODO + /*int yPos = 13 + 24 * lineNumber; + string lagCounter = MessageManager::Localize("Lag") + ": " + std::to_string(_console->GetLagCounter()); + DrawString(lagCounter, _screenWidth - 123, yPos, 250, 235, 215);*/ +} + +void BaseRenderer::ShowFrameCounter(int lineNumber) +{ + //TODO + /*int yPos = 13 + 24 * lineNumber; + string lagCounter = MessageManager::Localize("Frame") + ": " + std::to_string(_console->GetFrameCount()); + DrawString(lagCounter, _screenWidth - 146, yPos, 250, 235, 215);*/ +} + +void BaseRenderer::DrawCounters() +{ + //TODO + /*int lineNumber = 0; + EmulationSettings* settings = _console->GetSettings(); + if(settings->CheckFlag(EmulationFlags::ShowGameTimer)) { + ShowGameTimer(lineNumber++); + } + if(settings->CheckFlag(EmulationFlags::ShowFPS)) { + ShowFpsCounter(lineNumber++); + } + if(settings->CheckFlag(EmulationFlags::ShowLagCounter)) { + ShowLagCounter(lineNumber++); + } + if(settings->CheckFlag(EmulationFlags::ShowFrameCounter)) { + ShowFrameCounter(lineNumber++); + }*/ +} + +bool BaseRenderer::IsMessageShown() +{ + return !_toasts.empty(); +} diff --git a/Core/BaseRenderer.h b/Core/BaseRenderer.h new file mode 100644 index 0000000..142c5b0 --- /dev/null +++ b/Core/BaseRenderer.h @@ -0,0 +1,54 @@ +#pragma once +#include "../Core/IMessageManager.h" +#include "../Utilities/Timer.h" + +class Console; + +//TODO +enum class VideoResizeFilter +{ + NearestNeighbor = 0, + Bilinear = 1 +}; + +class BaseRenderer : public IMessageManager +{ +private: + list> _toasts; + + Timer _fpsTimer; + uint32_t _lastFrameCount = 0; + uint32_t _lastRenderedFrameCount = 0; + uint32_t _currentFPS = 0; + uint32_t _currentRenderedFPS = 0; + + void RemoveOldToasts(); + std::wstring WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount); + virtual float MeasureString(std::wstring text) = 0; + virtual bool ContainsCharacter(wchar_t character) = 0; + +protected: + shared_ptr _console; + + uint32_t _screenWidth = 0; + uint32_t _screenHeight = 0; + uint32_t _renderedFrameCount = 0; + + BaseRenderer(shared_ptr console, bool registerAsMessageManager); + virtual ~BaseRenderer(); + + bool IsMessageShown(); + + void DisplayMessage(string title, string message); + void DrawToasts(); + + void DrawToast(shared_ptr toast, int &lastHeight); + void DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity = 255); + virtual void DrawString(std::wstring message, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255, uint8_t opacity = 255) = 0; + + void ShowFpsCounter(int lineNumber); + void ShowLagCounter(int lineNumber); + void ShowFrameCounter(int lineNumber); + void ShowGameTimer(int lineNumber); + void DrawCounters(); +}; diff --git a/Core/BaseSoundManager.cpp b/Core/BaseSoundManager.cpp new file mode 100644 index 0000000..dc95920 --- /dev/null +++ b/Core/BaseSoundManager.cpp @@ -0,0 +1,50 @@ +#include "stdafx.h" +#include "BaseSoundManager.h" + +void BaseSoundManager::ProcessLatency(uint32_t readPosition, uint32_t writePosition) +{ + //Record latency between read & write cursors once per frame + int32_t cursorGap; + if(writePosition < readPosition) { + cursorGap = writePosition - readPosition + _bufferSize; + } else { + cursorGap = writePosition - readPosition; + } + + _cursorGaps[_cursorGapIndex] = cursorGap; + _cursorGapIndex = (_cursorGapIndex + 1) % 60; + if(_cursorGapIndex == 0) { + _cursorGapFilled = true; + } + + if(_cursorGapFilled) { + //Once we have 60+ frames worth of data to work with, adjust playback frequency by +/- 0.5% + //To speed up or slow down playback in order to reach our latency goal. + uint32_t bytesPerSample = _isStereo ? 4 : 2; + + int32_t gapSum = 0; + for(int i = 0; i < 60; i++) { + gapSum += _cursorGaps[i]; + } + int32_t gapAverage = gapSum / 60; + + _averageLatency = (gapAverage / bytesPerSample) / (double)_sampleRate * 1000; + } +} + +AudioStatistics BaseSoundManager::GetStatistics() +{ + AudioStatistics stats; + stats.AverageLatency = _averageLatency; + stats.BufferUnderrunEventCount = _bufferUnderrunEventCount; + stats.BufferSize = _bufferSize; + return stats; +} + +void BaseSoundManager::ResetStats() +{ + _cursorGapIndex = 0; + _cursorGapFilled = false; + _bufferUnderrunEventCount = 0; + _averageLatency = 0; +} diff --git a/Core/BaseSoundManager.h b/Core/BaseSoundManager.h new file mode 100644 index 0000000..f02d289 --- /dev/null +++ b/Core/BaseSoundManager.h @@ -0,0 +1,23 @@ +#pragma once +#include "../Core/IAudioDevice.h" + +class BaseSoundManager : public IAudioDevice +{ +public: + void ProcessLatency(uint32_t readPosition, uint32_t writePosition); + AudioStatistics GetStatistics(); + +protected: + bool _isStereo; + uint32_t _sampleRate = 0; + + double _averageLatency = 0; + uint32_t _bufferSize = 0x10000; + uint32_t _bufferUnderrunEventCount = 0; + + int32_t _cursorGaps[60]; + int32_t _cursorGapIndex = 0; + bool _cursorGapFilled = false; + + void ResetStats(); +}; diff --git a/Core/Console.cpp b/Core/Console.cpp new file mode 100644 index 0000000..11a74ba --- /dev/null +++ b/Core/Console.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "Console.h" +#include "Cpu.h" +#include "MemoryManager.h" +#include "Debugger.h" +#include "../Utilities/Timer.h" +#include "../Utilities/VirtualFile.h" + +void Console::Run() +{ + if(!_cpu) { + return; + } + + _stopFlag = false; + + auto lock = _runLock.AcquireSafe(); + while(!_stopFlag) { + _cpu->Exec(); + } + + //Timer timer; + /*std::cout << "Time: " << std::to_string(timer.GetElapsedMS()) << std::endl; + std::cout << "OP Count: " << std::to_string(_cpu->opCount) << std::endl; + std::cout << "OP/sec: " << std::to_string(_cpu->opCount * 1000 / timer.GetElapsedMS()) << std::endl; + while(true);*/ +} + +void Console::Stop() +{ + _stopFlag = true; + _runLock.WaitForRelease(); + + _cpu.reset(); + _memoryManager.reset(); + _debugger.reset(); +} + +void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile) +{ + Stop(); + + shared_ptr cart = BaseCartridge::CreateCartridge(romFile, patchFile); + if(cart) { + _memoryManager.reset(new MemoryManager(cart, shared_from_this())); + _cpu.reset(new Cpu(_memoryManager)); + _debugger.reset(new Debugger(_cpu, _memoryManager)); + } +} + +shared_ptr Console::GetDebugger(bool allowStart) +{ + return _debugger; +} + +void Console::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type) +{ + if(_debugger) { + _debugger->ProcessCpuRead(addr, value, type); + } +} + +void Console::ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type) +{ + if(_debugger) { + _debugger->ProcessCpuWrite(addr, value, type); + } +} diff --git a/Core/Console.h b/Core/Console.h new file mode 100644 index 0000000..297b65b --- /dev/null +++ b/Core/Console.h @@ -0,0 +1,31 @@ +#pragma once +#include "stdafx.h" +#include "../Utilities/VirtualFile.h" +#include "../Utilities/SimpleLock.h" + +class Cpu; +class MemoryManager; +class Debugger; +enum class MemoryOperationType; + +class Console : public std::enable_shared_from_this +{ +private: + shared_ptr _cpu; + shared_ptr _memoryManager; + shared_ptr _debugger; + + SimpleLock _runLock; + atomic _stopFlag; + +public: + void Run(); + void Stop(); + + void LoadRom(VirtualFile romFile, VirtualFile patchFile); + + shared_ptr GetDebugger(bool allowStart = true); + + void ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type); +}; \ No newline at end of file diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 73252f2..e78f311 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -43,14 +43,35 @@ + + + + + + + + + + + + + + + + + + - + + + + Create Create @@ -63,6 +84,7 @@ Create Create + {78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0} @@ -73,68 +95,68 @@ - Application + StaticLibrary true v141 Unicode - Application + StaticLibrary true v141 Unicode - Application + StaticLibrary false v141 true Unicode - Application + StaticLibrary false v141 true Unicode - Application + StaticLibrary false v141 true Unicode - Application + StaticLibrary false v141 true Unicode - Application + StaticLibrary false v141 true Unicode - Application + StaticLibrary false v141 true Unicode - Application + StaticLibrary false v141 true Unicode - Application + StaticLibrary false v141 true diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index c20746d..deb6e31 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1,14 +1,104 @@  - - - + + SNES + + + SNES + + + SNES + + + Debugger + + + Debugger + + + Debugger + + + SNES + + + Interfaces + + + Interfaces + + + Interfaces + + + Interfaces + + + Interfaces + + + Interfaces + + + Interfaces + + + Misc + + + Misc + + + Misc + + + Misc + - - + + SNES + + + Debugger + + + Debugger + + + Debugger + + + SNES + + + Misc + + + Misc + + + Misc + + + Misc + + + + + {9cd95372-4592-4f8d-a3fc-db38f474c5b2} + + + {eb8294f1-983e-4e76-8b52-0be06fb798d6} + + + {3dcfdfd1-4339-4513-b08b-70dc3bb735b1} + + + {0da6a615-3092-40f7-936c-253eb03a8784} + \ No newline at end of file diff --git a/Core/Cpu.cpp b/Core/Cpu.cpp index 6d5837f..a7e3d18 100644 --- a/Core/Cpu.cpp +++ b/Core/Cpu.cpp @@ -2,34 +2,9 @@ #include "CpuTypes.h" #include "Cpu.h" #include "MemoryManager.h" -#include "../Utilities/HexUtilities.h" //TODO: PER doesn't load the right number of bytes? -uint8_t lastTest = -1; -bool _enableLogging = false; -string opNames[256] = { - //0 1 2 3 4 5 6 7 8 9 A B C D E F - "BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", // 0 - "BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", // 1 - "JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", // 2 - "BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", // 3 - "RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", // 4 - "BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", "CLI", "EOR", "PHY", "TCD", "JMP", "EOR", "LSR", "EOR", // 5 - "RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", // 6 - "BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", // 7 - "BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", // 8 - "BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", // 9 - "LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", // A - "BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", // B - "CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", // C - "BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", // D - "CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", // E - "BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" // F -}; - - - /************************ Add/substract operations *************************/ @@ -997,7 +972,6 @@ Cpu::Cpu(shared_ptr memoryManager) memcpy(_addrMode, addrMode, sizeof(addrMode)); _memoryManager = memoryManager; - _enableLogging = false; _state = {}; _state.PC = ReadDataWord(Cpu::ResetVector); _state.SP = 0x1FF; @@ -1016,19 +990,6 @@ void Cpu::Reset() void Cpu::Exec() { - /*std::cout << - "$" << HexUtilities::ToHex(_state.K) << ":" << HexUtilities::ToHex(_state.PC) << - " $" << HexUtilities::ToHex(ReadCode(_state.PC)) << - " (" << opNames[ReadCode(_state.PC)] << ")" << - " A:$" << HexUtilities::ToHex(_state.A) << - " X:$" << HexUtilities::ToHex(_state.X) << - " Y:$" << HexUtilities::ToHex(_state.Y) << - " S:$" << HexUtilities::ToHex(_state.SP) << - " D:$" << HexUtilities::ToHex(_state.D) << - " DB:$" << HexUtilities::ToHex(_state.DBR) << - " P:$" << HexUtilities::ToHex(_state.PS) << - std::endl;*/ - uint8_t opCode = GetOpCode(); _instAddrMode = _addrMode[opCode]; _operand = FetchEffectiveAddress(); @@ -1086,12 +1047,12 @@ uint32_t Cpu::ReadOperandLong() uint8_t Cpu::ReadCode(uint16_t addr, MemoryOperationType type) { - return _memoryManager->Read((_state.K << 16) | addr); + return _memoryManager->Read((_state.K << 16) | addr, type); } uint8_t Cpu::ReadData(uint32_t addr, MemoryOperationType type) { - return _memoryManager->Read(addr); + return _memoryManager->Read(addr, type); } uint16_t Cpu::ReadDataWord(uint32_t addr, MemoryOperationType type) @@ -1111,10 +1072,7 @@ uint32_t Cpu::ReadDataLong(uint32_t addr, MemoryOperationType type) void Cpu::Write(uint32_t addr, uint8_t value, MemoryOperationType type) { - _memoryManager->Write(addr, value); - if(_enableLogging) { - std::cout << "W: $" << HexUtilities::ToHex(addr) << " = $" << HexUtilities::ToHex(value) << std::endl; - } + _memoryManager->Write(addr, value, type); } void Cpu::WriteWord(uint32_t addr, uint16_t value, MemoryOperationType type) diff --git a/Core/Cpu.h b/Core/Cpu.h index 884233d..5ccf4e7 100644 --- a/Core/Cpu.h +++ b/Core/Cpu.h @@ -9,6 +9,7 @@ class Cpu public: uint64_t opCount = 0; uint16_t GetPc() { return _state.PC; } + CpuState GetState() { return _state; } private: static constexpr uint32_t NmiVector = 0x00FFFA; diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp new file mode 100644 index 0000000..5a2de77 --- /dev/null +++ b/Core/Debugger.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "Debugger.h" +#include "Cpu.h" +#include "CpuTypes.h" +#include "DisassemblyInfo.h" +#include "TraceLogger.h" +#include "../Utilities/HexUtilities.h" + +Debugger::Debugger(shared_ptr cpu, shared_ptr memoryManager) +{ + _cpu = cpu; + _memoryManager = memoryManager; + _traceLogger.reset(new TraceLogger(this, memoryManager)); + + _cpuStepCount = 1; +} + +Debugger::~Debugger() +{ +} + +void Debugger::ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type) +{ + if(type == MemoryOperationType::ExecOpCode) { + CpuState state = _cpu->GetState(); + DisassemblyInfo disassemblyInfo(state, _memoryManager.get()); + DebugState debugState = { state }; + _traceLogger->Log(debugState, disassemblyInfo); + + string out; + out += HexUtilities::ToHex(state.K) + HexUtilities::ToHex(state.PC); + out += " "; + disassemblyInfo.GetDisassembly(out, _memoryManager.get()); + + out += string(20 - out.size(), ' '); + + std::cout << out << + " A:$" << HexUtilities::ToHex(state.A) << + " X:$" << HexUtilities::ToHex(state.X) << + " Y:$" << HexUtilities::ToHex(state.Y) << + " S:$" << HexUtilities::ToHex(state.SP) << + " D:$" << HexUtilities::ToHex(state.D) << + " DB:$" << HexUtilities::ToHex(state.DBR) << + " P:$" << HexUtilities::ToHex(state.PS) << + std::endl; + //_traceLogger->Trace + + if(_cpuStepCount > 0) { + _cpuStepCount--; + while(_cpuStepCount == 0) { + std::this_thread::sleep_for(std::chrono::duration(10)); + } + } + } +} + +void Debugger::ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type) +{ + if(type == MemoryOperationType::ExecOpCode) { + //_traceLogger->Trace + } +} + +void Debugger::Run() +{ + _cpuStepCount = -1; +} + +void Debugger::Step(int32_t stepCount) +{ + _cpuStepCount = stepCount; +} + +bool Debugger::IsExecutionStopped() +{ + //TODO + return false; +} + +shared_ptr Debugger::GetTraceLogger() +{ + return _traceLogger; +} \ No newline at end of file diff --git a/Core/Debugger.h b/Core/Debugger.h new file mode 100644 index 0000000..3d06055 --- /dev/null +++ b/Core/Debugger.h @@ -0,0 +1,42 @@ +#pragma once +#include "stdafx.h" +#include "CpuTypes.h" + +class Cpu; +class MemoryManager; + +enum class MemoryOperationType; +class TraceLogger; +//class Disassembler; + +struct DebugState +{ + CpuState Cpu; + //PpuState ppuState; + //ApuState apuState; +}; + +class Debugger +{ +private: + shared_ptr _cpu; + shared_ptr _memoryManager; + + shared_ptr _traceLogger; + //unique_ptr _disassembler; + + atomic _cpuStepCount; + +public: + Debugger(shared_ptr cpu, shared_ptr memoryManager); + ~Debugger(); + + void ProcessCpuRead(uint32_t addr, uint8_t value, MemoryOperationType type); + void ProcessCpuWrite(uint32_t addr, uint8_t value, MemoryOperationType type); + + void Run(); + void Step(int32_t stepCount); + bool IsExecutionStopped(); + + shared_ptr GetTraceLogger(); +}; \ No newline at end of file diff --git a/Core/DisassemblyInfo.cpp b/Core/DisassemblyInfo.cpp new file mode 100644 index 0000000..bda75f5 --- /dev/null +++ b/Core/DisassemblyInfo.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "DisassemblyInfo.h" +#include "CpuTypes.h" +#include "MemoryManager.h" +#include "../Utilities/HexUtilities.h" + +DisassemblyInfo::DisassemblyInfo() +{ +} + +DisassemblyInfo::DisassemblyInfo(CpuState &state, MemoryManager *memoryManager) +{ + uint32_t addr = (state.K << 16) | state.PC; + _byteCode[0] = memoryManager->Peek(addr); + _addrMode = DisassemblyInfo::OpMode[_byteCode[0]]; + _opSize = GetOperandSize() + 1; + + for(int i = 1; i < _opSize; i++) { + _byteCode[i] = memoryManager->Peek(addr+i); + } + + _flags = state.PS; + _emulationMode = state.EmulationMode; +} + +void DisassemblyInfo::GetDisassembly(string &out, MemoryManager *memoryManager) +{ + out += DisassemblyInfo::OpName[_byteCode[0]]; + out += _opSize > 1 ? " $" : " "; + for(int i = _opSize - 1; i > 0; i--) { + out += HexUtilities::ToHex(_byteCode[i]); + } +} + +uint8_t DisassemblyInfo::GetOperandSize() +{ + switch(_addrMode) { + case AddrMode::Abs: return 2; + case AddrMode::AbsIdxXInd: return 2; + case AddrMode::AbsIdxX: return 2; + case AddrMode::AbsIdxY: return 2; + case AddrMode::AbsInd: return 2; + case AddrMode::AbsIndLng: return 3; + case AddrMode::AbsLngIdxX: return 3; + case AddrMode::AbsLng: return 3; + case AddrMode::Acc: return 0; + case AddrMode::BlkMov: return 2; + case AddrMode::DirIdxIndX: return 1; + case AddrMode::DirIdxX: return 1; + case AddrMode::DirIdxY: return 1; + case AddrMode::DirIndIdxY: return 1; + case AddrMode::DirIndLngIdxY: return 1; + case AddrMode::DirIndLng: return 1; + case AddrMode::DirInd: return 1; + case AddrMode::Dir: return 1; + case AddrMode::Imm8: return 1; + case AddrMode::ImmX: return _flags & ProcFlags::IndexMode8 ? 1 : 2; + case AddrMode::ImmM: return _flags & ProcFlags::MemoryMode8 ? 1 : 2; + case AddrMode::Imp: return 0; + case AddrMode::RelLng: return 2; + case AddrMode::Rel: return 1; + case AddrMode::Stk: return 0; //TODO + case AddrMode::StkRel: return 1; + case AddrMode::StkRelIndIdxY: return 1; + } + + throw new std::runtime_error("Invalid mode"); +} + +void DisassemblyInfo::GetByteCode(string &out) +{ + for(int i = 0; i < _opSize; i++) { + out += "$" + HexUtilities::ToHex(_byteCode[i]) + ((i < _opSize - 1) ? " " : ""); + } +} + +string DisassemblyInfo::OpName[256] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + "BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA", // 0 + "BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA", // 1 + "JSR", "AND", "JSL", "AND", "BIT", "AND", "ROL", "AND", "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND", // 2 + "BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND", // 3 + "RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR", // 4 + "BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", "CLI", "EOR", "PHY", "TCD", "JMP", "EOR", "LSR", "EOR", // 5 + "RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC", // 6 + "BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC", // 7 + "BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA", // 8 + "BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA", // 9 + "LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA", // A + "BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA", // B + "CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP", // C + "BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP", // D + "CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC", // E + "BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC" // F +}; + +typedef AddrMode M; +AddrMode DisassemblyInfo::OpMode[256] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + M::Stk, M::DirIdxIndX, M::Stk, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 0 + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Dir, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 1 + M::Abs, M::DirIdxIndX, M::AbsLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 2 + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Acc, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 3 + M::Stk, M::DirIdxIndX, M::Imm8, M::StkRel, M::BlkMov, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 4 + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::BlkMov, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 5 + M::Stk, M::DirIdxIndX, M::Stk, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Stk, M::ImmM, M::Acc, M::Stk, M::AbsInd, M::Abs, M::Abs, M::AbsLng, // 6 + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 7 + M::Rel, M::DirIdxIndX, M::RelLng, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // 8 + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::Abs, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // 9 + M::ImmX, M::DirIdxIndX, M::ImmX, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Stk, M::Abs, M::Abs, M::Abs, M::AbsLng, // A + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::DirIdxX, M::DirIdxX, M::DirIdxY, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Imp, M::Imp, M::AbsIdxX, M::AbsIdxX, M::AbsIdxY, M::AbsLngIdxX, // B + M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // C + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Stk, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIndLng, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX, // D + M::ImmX, M::DirIdxIndX, M::Imm8, M::StkRel, M::Dir, M::Dir, M::Dir, M::DirIndLng, M::Imp, M::ImmM, M::Imp, M::Imp, M::Abs, M::Abs, M::Abs, M::AbsLng, // E + M::Rel, M::DirIndIdxY, M::DirInd, M::StkRelIndIdxY, M::Stk, M::DirIdxX, M::DirIdxX, M::DirIndLngIdxY, M::Imp, M::AbsIdxY, M::Stk, M::Imp, M::AbsIdxXInd, M::AbsIdxX, M::AbsIdxX, M::AbsLngIdxX // F +}; \ No newline at end of file diff --git a/Core/DisassemblyInfo.h b/Core/DisassemblyInfo.h new file mode 100644 index 0000000..9b8e1d1 --- /dev/null +++ b/Core/DisassemblyInfo.h @@ -0,0 +1,43 @@ +#pragma once +#include "stdafx.h" +#include + +class MemoryManager; + +struct CpuState; +enum class AddrMode : uint8_t; + +class DisassemblyInfo +{ +public: + static string OpName[256]; + static AddrMode OpMode[256]; + static uint8_t OpSize[256]; + +private: + uint8_t _byteCode[4]; + uint8_t _opSize; + AddrMode _addrMode; + uint8_t _flags; + bool _emulationMode; + +public: + DisassemblyInfo(); + DisassemblyInfo(CpuState &state, MemoryManager *memoryManager); + + void GetDisassembly(string &out, MemoryManager* memoryManager); + uint8_t GetOperandSize(); + void GetByteCode(string &out); + /* + int32_t GetEffectiveAddress(CpuState& cpuState, MemoryManager* memoryManager); + void GetEffectiveAddressString(string &out, CpuState& cpuState, MemoryManager* memoryManager); + + /*int32_t GetMemoryValue(CpuState& cpuState, MemoryManager* memoryManager); + uint16_t GetJumpDestination(uint16_t pc, MemoryManager* memoryManager); + uint16_t GetIndirectJumpDestination(MemoryManager* memoryManager); + void ToString(string &out, uint32_t memoryAddr, MemoryManager* memoryManager, bool extendZeroPage); + void GetByteCode(string &out); + uint32_t GetSize(); + uint16_t GetOpAddr(uint16_t memoryAddr);*/ +}; + diff --git a/Core/IAudioDevice.h b/Core/IAudioDevice.h new file mode 100644 index 0000000..105a511 --- /dev/null +++ b/Core/IAudioDevice.h @@ -0,0 +1,25 @@ +#pragma once + +#include "stdafx.h" + +struct AudioStatistics +{ + double AverageLatency = 0; + uint32_t BufferUnderrunEventCount = 0; + uint32_t BufferSize = 0; +}; + +class IAudioDevice +{ + public: + virtual ~IAudioDevice() {} + virtual void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize, uint32_t sampleRate, bool isStereo) = 0; + virtual void Stop() = 0; + virtual void Pause() = 0; + virtual void ProcessEndOfFrame() = 0; + + virtual string GetAvailableDevices() = 0; + virtual void SetAudioDevice(string deviceName) = 0; + + virtual AudioStatistics GetStatistics() = 0; +}; \ No newline at end of file diff --git a/Core/IInputProvider.h b/Core/IInputProvider.h new file mode 100644 index 0000000..d4dbab8 --- /dev/null +++ b/Core/IInputProvider.h @@ -0,0 +1,9 @@ +#pragma once + +class BaseControlDevice; + +class IInputProvider +{ +public: + virtual bool SetInput(BaseControlDevice* device) = 0; +}; \ No newline at end of file diff --git a/Core/IInputRecorder.h b/Core/IInputRecorder.h new file mode 100644 index 0000000..3e7195d --- /dev/null +++ b/Core/IInputRecorder.h @@ -0,0 +1,10 @@ +#pragma once +#include "stdafx.h" + +class BaseControlDevice; + +class IInputRecorder +{ +public: + virtual void RecordInput(vector> devices) = 0; +}; \ No newline at end of file diff --git a/Core/IKeyManager.h b/Core/IKeyManager.h new file mode 100644 index 0000000..c9eed5b --- /dev/null +++ b/Core/IKeyManager.h @@ -0,0 +1,39 @@ +#pragma once +#include "stdafx.h" + +enum class MouseButton +{ + LeftButton = 0, + RightButton = 1, + MiddleButton = 2, +}; + +struct MousePosition +{ + int16_t X; + int16_t Y; +}; + +struct MouseMovement +{ + int16_t dx; + int16_t dy; +}; + +class IKeyManager +{ +public: + virtual ~IKeyManager() {} + + virtual void RefreshState() = 0; + virtual void UpdateDevices() = 0; + virtual bool IsMouseButtonPressed(MouseButton button) = 0; + virtual bool IsKeyPressed(uint32_t keyCode) = 0; + virtual vector GetPressedKeys() = 0; + virtual string GetKeyName(uint32_t keyCode) = 0; + virtual uint32_t GetKeyCode(string keyName) = 0; + + virtual void SetKeyState(uint16_t scanCode, bool state) = 0; + virtual void ResetKeyState() = 0; + virtual void SetDisabled(bool disabled) = 0; +}; \ No newline at end of file diff --git a/Core/IMessageManager.h b/Core/IMessageManager.h new file mode 100644 index 0000000..1069a17 --- /dev/null +++ b/Core/IMessageManager.h @@ -0,0 +1,64 @@ +#pragma once + +#include "stdafx.h" +#include + +class ToastInfo; + +class IMessageManager +{ +public: + virtual void DisplayMessage(string title, string message) = 0; +}; + +class ToastInfo +{ +private: + string _title; + string _message; + uint64_t _endTime; + uint64_t _startTime; + + uint64_t GetCurrentTime() + { + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + } + +public: + ToastInfo(string title, string message, int displayDuration) + { + _title = title; + _message = message; + _startTime = GetCurrentTime(); + _endTime = _startTime + displayDuration; + } + + string GetToastTitle() + { + return _title; + } + + string GetToastMessage() + { + return _message; + } + + float GetOpacity() + { + uint64_t currentTime = GetCurrentTime(); + if(currentTime - _startTime < 200) { + return (currentTime - _startTime) * 5.0f / 1000.0f; + } else if(_endTime - currentTime < 200) { + return (_endTime - currentTime) * 5.0f / 1000.0f; + } else if(currentTime >= _endTime) { + return 0.0f; + } else { + return 1.0f; + } + } + + bool IsToastExpired() + { + return _endTime < GetCurrentTime(); + } +}; \ No newline at end of file diff --git a/Core/INotificationListener.h b/Core/INotificationListener.h new file mode 100644 index 0000000..dbac61c --- /dev/null +++ b/Core/INotificationListener.h @@ -0,0 +1,25 @@ +#pragma once +#include "stdafx.h" + +enum class ConsoleNotificationType +{ + GameLoaded = 0, + StateLoaded = 1, + GameReset = 2, + GamePaused = 3, + GameResumed = 4, + GameStopped = 5, + CodeBreak = 6, + PpuFrameDone = 9, + ResolutionChanged = 11, + ConfigChanged = 13, + ExecuteShortcut = 16, + EmulationStopped = 17, + BeforeEmulationStop = 19, +}; + +class INotificationListener +{ +public: + virtual void ProcessNotification(ConsoleNotificationType type, void* parameter) = 0; +}; \ No newline at end of file diff --git a/Core/IRenderingDevice.h b/Core/IRenderingDevice.h new file mode 100644 index 0000000..a8612ea --- /dev/null +++ b/Core/IRenderingDevice.h @@ -0,0 +1,13 @@ +#pragma once + +#include "stdafx.h" + +class IRenderingDevice +{ + public: + virtual ~IRenderingDevice() {} + virtual void UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height) = 0; + virtual void Render() = 0; + virtual void Reset() = 0; + virtual void SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) = 0; +}; \ No newline at end of file diff --git a/Core/KeyManager.cpp b/Core/KeyManager.cpp new file mode 100644 index 0000000..0d02ec8 --- /dev/null +++ b/Core/KeyManager.cpp @@ -0,0 +1,108 @@ +#include "stdafx.h" +#include "KeyManager.h" +#include "IKeyManager.h" + +IKeyManager* KeyManager::_keyManager = nullptr; +MousePosition KeyManager::_mousePosition = { 0, 0 }; +atomic KeyManager::_xMouseMovement; +atomic KeyManager::_yMouseMovement; + +void KeyManager::RegisterKeyManager(IKeyManager* keyManager) +{ + _xMouseMovement = 0; + _yMouseMovement = 0; + _keyManager = keyManager; +} + +void KeyManager::RefreshKeyState() +{ + if(_keyManager != nullptr) { + return _keyManager->RefreshState(); + } +} + +bool KeyManager::IsKeyPressed(uint32_t keyCode) +{ + //TODO + /*if(_keyManager != nullptr) { + return _settings->InputEnabled() && _keyManager->IsKeyPressed(keyCode); + }*/ + return false; +} + +bool KeyManager::IsMouseButtonPressed(MouseButton button) +{ + //TODO + /*if(_keyManager != nullptr) { + return _settings->InputEnabled() && _keyManager->IsMouseButtonPressed(button); + }*/ + return false; +} + +vector KeyManager::GetPressedKeys() +{ + if(_keyManager != nullptr) { + return _keyManager->GetPressedKeys(); + } + return vector(); +} + +string KeyManager::GetKeyName(uint32_t keyCode) +{ + if(_keyManager != nullptr) { + return _keyManager->GetKeyName(keyCode); + } + return ""; +} + +uint32_t KeyManager::GetKeyCode(string keyName) +{ + if(_keyManager != nullptr) { + return _keyManager->GetKeyCode(keyName); + } + return 0; +} + +void KeyManager::UpdateDevices() +{ + if(_keyManager != nullptr) { + _keyManager->UpdateDevices(); + } +} + +void KeyManager::SetMouseMovement(int16_t x, int16_t y) +{ + _xMouseMovement += x; + _yMouseMovement += y; +} + +MouseMovement KeyManager::GetMouseMovement(double mouseSensitivity) +{ + //TODO + //double factor = _settings->GetVideoScale() / mouseSensitivity; + MouseMovement mov = {}; + /*mov.dx = (int16_t)(_xMouseMovement / factor); + mov.dy = (int16_t)(_yMouseMovement / factor); + _xMouseMovement -= (int16_t)(mov.dx * factor); + _yMouseMovement -= (int16_t)(mov.dy * factor);*/ + + return mov; +} + +void KeyManager::SetMousePosition(double x, double y) +{ + //TODO + /*if(x < 0 || y < 0) { + _mousePosition.X = -1; + _mousePosition.Y = -1; + } else { + OverscanDimensions overscan = _settings->GetOverscanDimensions(); + _mousePosition.X = (int32_t)(x * (PPU::ScreenWidth - overscan.Left - overscan.Right) + overscan.Left); + _mousePosition.Y = (int32_t)(y * (PPU::ScreenHeight - overscan.Top - overscan.Bottom) + overscan.Top); + }*/ +} + +MousePosition KeyManager::GetMousePosition() +{ + return _mousePosition; +} \ No newline at end of file diff --git a/Core/KeyManager.h b/Core/KeyManager.h new file mode 100644 index 0000000..c062bac --- /dev/null +++ b/Core/KeyManager.h @@ -0,0 +1,30 @@ +#pragma once +#include "stdafx.h" +#include "IKeyManager.h" + +class KeyManager +{ +private: + static IKeyManager* _keyManager; + static MousePosition _mousePosition; + static atomic _xMouseMovement; + static atomic _yMouseMovement; + +public: + static void RegisterKeyManager(IKeyManager* keyManager); + + static void RefreshKeyState(); + static bool IsKeyPressed(uint32_t keyCode); + static bool IsMouseButtonPressed(MouseButton button); + static vector GetPressedKeys(); + static string GetKeyName(uint32_t keyCode); + static uint32_t GetKeyCode(string keyName); + + static void UpdateDevices(); + + static void SetMouseMovement(int16_t x, int16_t y); + static MouseMovement GetMouseMovement(double mouseSensitivity); + + static void SetMousePosition(double x, double y); + static MousePosition GetMousePosition(); +}; \ No newline at end of file diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index 39442b0..ada9b76 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -1,5 +1,8 @@ #pragma once #include "stdafx.h" +#include "Console.h" +#include "../Utilities/HexUtilities.h" +#include "../Utilities/VirtualFile.h" class IMemoryHandler { @@ -16,17 +19,25 @@ public: class BaseCartridge : public IMemoryHandler { private: - uint32_t _prgRomSize; + size_t _prgRomSize; uint8_t* _prgRom; public: - BaseCartridge() + static shared_ptr CreateCartridge(VirtualFile romFile, VirtualFile patchFile) { - _prgRomSize = 1024 * 1024; - _prgRom = new uint8_t[_prgRomSize]; + if(romFile.IsValid()) { + vector romData; + romFile.ReadFile(romData); - ifstream rom("..\\bin\\x64\\Debug\\game.sfc", ios::binary); - rom.read((char*)_prgRom, _prgRomSize); + 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 @@ -65,16 +76,20 @@ public: class MemoryManager { private: + shared_ptr _console; + uint8_t * _workRam; IMemoryHandler* _handlers[0x100 * 0x10]; vector> _workRamHandlers; - unique_ptr _cart; + shared_ptr _cart; public: - MemoryManager() + MemoryManager(shared_ptr cart, shared_ptr console) { - _cart.reset(new BaseCartridge()); + _console = console; + _cart = cart; + memset(_handlers, 0, sizeof(_handlers)); _workRam = new uint8_t[128 * 1024]; for(uint32_t i = 0; i < 128 * 1024; i += 0x1000) { _workRamHandlers.push_back(unique_ptr(new WorkRamHandler(_workRam + i))); @@ -105,19 +120,37 @@ public: } } - uint8_t Read(uint32_t addr) + uint8_t Read(uint32_t addr, MemoryOperationType type) { + uint8_t value = 0; if(_handlers[addr >> 12]) { - return _handlers[addr >> 12]->Read(addr); + value = _handlers[addr >> 12]->Read(addr); } else { - return 0; + //std::cout << "Read - missing handler: $" << HexUtilities::ToHex(addr) << std::endl; } + _console->ProcessCpuRead(addr, value, type); + return value; } - void Write(uint32_t addr, uint8_t value) + uint8_t Peek(uint32_t addr) { + //Read, without triggering side-effects + uint8_t value = 0; + if(_handlers[addr >> 12]) { + value = _handlers[addr >> 12]->Read(addr); + } else { + //std::cout << "Read - missing handler: $" << HexUtilities::ToHex(addr) << std::endl; + } + return value; + } + + void Write(uint32_t addr, uint8_t value, MemoryOperationType type) + { + _console->ProcessCpuWrite(addr, value, type); if(_handlers[addr >> 12]) { return _handlers[addr >> 12]->Write(addr, value); + } else { + //std::cout << "Write - missing handler: $" << HexUtilities::ToHex(addr) << " = " << HexUtilities::ToHex(value) << std::endl; } } }; \ No newline at end of file diff --git a/Core/MessageManager.cpp b/Core/MessageManager.cpp new file mode 100644 index 0000000..616a076 --- /dev/null +++ b/Core/MessageManager.cpp @@ -0,0 +1,183 @@ +#include "stdafx.h" +#include "MessageManager.h" + +std::unordered_map MessageManager::_enResources = { + { "Cheats", u8"Cheats" }, + { "Debug", u8"Debug" }, + { "EmulationSpeed", u8"Emulation Speed" }, + { "ClockRate", u8"Clock Rate" }, + { "Error", u8"Error" }, + { "GameInfo", u8"Game Info" }, + { "GameLoaded", u8"Game loaded" }, + { "Input", u8"Input" }, + { "Patch", u8"Patch" }, + { "Movies", u8"Movies" }, + { "NetPlay", u8"Net Play" }, + { "Region", u8"Region" }, + { "SaveStates", u8"Save States" }, + { "ScreenshotSaved", u8"Screenshot Saved" }, + { "SoundRecorder", u8"Sound Recorder" }, + { "Test", u8"Test" }, + { "VideoRecorder", u8"Video Recorder" }, + + { "ApplyingPatch", u8"Applying patch: %1" }, + { "CheatApplied", u8"1 cheat applied." }, + { "CheatsApplied", u8"%1 cheats applied." }, + { "CheatsDisabled", u8"All cheats disabled." }, + { "CoinInsertedSlot", u8"Coin inserted (slot %1)" }, + { "ConnectedToServer", u8"Connected to server." }, + { "ConnectedAsPlayer", u8"Connected as player %1" }, + { "ConnectedAsSpectator", u8"Connected as spectator." }, + { "ConnectionLost", u8"Connection to server lost." }, + { "CouldNotConnect", u8"Could not connect to the server." }, + { "CouldNotInitializeAudioSystem", u8"Could not initialize audio system" }, + { "CouldNotFindRom", u8"Could not find matching game ROM." }, + { "CouldNotLoadFile", u8"Could not load file: %1" }, + { "EmulationMaximumSpeed", u8"Maximum speed" }, + { "EmulationSpeedPercent", u8"%1%" }, + { "FdsDiskInserted", u8"Disk %1 Side %2 inserted." }, + { "Frame", u8"Frame" }, + { "GameCrash", u8"Game has crashed (%1)" }, + { "KeyboardModeDisabled", u8"Keyboard mode disabled." }, + { "KeyboardModeEnabled", u8"Keyboard mode enabled." }, + { "Lag", u8"Lag" }, + { "Mapper", u8"Mapper: %1, SubMapper: %2" }, + { "MovieEnded", u8"Movie ended." }, + { "MovieInvalid", u8"Invalid movie file." }, + { "MovieMissingRom", u8"Missing ROM required (%1) to play movie." }, + { "MovieNewerVersion", u8"Cannot load movies created by a more recent version of Mesen. Please download the latest version." }, + { "MovieIncompatibleVersion", u8"This movie is incompatible with this version of Mesen." }, + { "MoviePlaying", u8"Playing movie: %1" }, + { "MovieRecordingTo", u8"Recording to: %1" }, + { "MovieSaved", u8"Movie saved to file: %1" }, + { "NetplayVersionMismatch", u8"%1 is not running the same version of Mesen and has been disconnected." }, + { "PrgSizeWarning", u8"PRG size is smaller than 32kb" }, + { "SaveStateEmpty", u8"Slot is empty." }, + { "SaveStateIncompatibleVersion", u8"Save state is incompatible with this version of Mesen." }, + { "SaveStateInvalidFile", u8"Invalid save state file." }, + { "SaveStateLoaded", u8"State #%1 loaded." }, + { "SaveStateMissingRom", u8"Missing ROM required (%1) to load save state." }, + { "SaveStateNewerVersion", u8"Cannot load save states created by a more recent version of Mesen. Please download the latest version." }, + { "SaveStateSaved", u8"State #%1 saved." }, + { "SaveStateSlotSelected", u8"Slot #%1 selected." }, + { "ScanlineTimingWarning", u8"PPU timing has been changed." }, + { "ServerStarted", u8"Server started (Port: %1)" }, + { "ServerStopped", u8"Server stopped" }, + { "SoundRecorderStarted", u8"Recording to: %1" }, + { "SoundRecorderStopped", u8"Recording saved to: %1" }, + { "TestFileSavedTo", u8"Test file saved to: %1" }, + { "UnsupportedMapper", u8"Unsupported mapper (%1), cannot load game." }, + { "VideoRecorderStarted", u8"Recording to: %1" }, + { "VideoRecorderStopped", u8"Recording saved to: %1" }, +}; + +std::list MessageManager::_log; +SimpleLock MessageManager::_logLock; +SimpleLock MessageManager::_messageLock; +bool MessageManager::_osdEnabled = false; +IMessageManager* MessageManager::_messageManager = nullptr; + +void MessageManager::RegisterMessageManager(IMessageManager* messageManager) +{ + auto lock = _messageLock.AcquireSafe(); + MessageManager::_messageManager = messageManager; +} + +void MessageManager::UnregisterMessageManager(IMessageManager* messageManager) +{ + auto lock = _messageLock.AcquireSafe(); + if(MessageManager::_messageManager == messageManager) { + MessageManager::_messageManager = nullptr; + } +} + +void MessageManager::SetOsdState(bool enabled) +{ + _osdEnabled = enabled; +} + +string MessageManager::Localize(string key) +{ + std::unordered_map *resources = &_enResources; + /*switch(EmulationSettings::GetDisplayLanguage()) { + case Language::English: resources = &_enResources; break; + case Language::French: resources = &_frResources; break; + case Language::Japanese: resources = &_jaResources; break; + case Language::Russian: resources = &_ruResources; break; + case Language::Spanish: resources = &_esResources; break; + case Language::Ukrainian: resources = &_ukResources; break; + case Language::Portuguese: resources = &_ptResources; break; + case Language::Catalan: resources = &_caResources; break; + case Language::Chinese: resources = &_zhResources; break; + }*/ + if(resources) { + if(resources->find(key) != resources->end()) { + return (*resources)[key]; + }/* else if(EmulationSettings::GetDisplayLanguage() != Language::English) { + //Fallback on English if resource key is missing another language + resources = &_enResources; + if(resources->find(key) != resources->end()) { + return (*resources)[key]; + } + }*/ + } + + return key; +} + +void MessageManager::DisplayMessage(string title, string message, string param1, string param2) +{ + if(MessageManager::_messageManager) { + auto lock = _messageLock.AcquireSafe(); + if(!MessageManager::_messageManager) { + return; + } + + title = Localize(title); + message = Localize(message); + + size_t startPos = message.find(u8"%1"); + if(startPos != std::string::npos) { + message.replace(startPos, 2, param1); + } + + startPos = message.find(u8"%2"); + if(startPos != std::string::npos) { + message.replace(startPos, 2, param2); + } + + if(_osdEnabled) { + MessageManager::_messageManager->DisplayMessage(title, message); + } else { + MessageManager::Log("[" + title + "] " + message); + } + } +} + +void MessageManager::Log(string message) +{ +#ifndef LIBRETRO + auto lock = _logLock.AcquireSafe(); + if(message.empty()) { + message = "------------------------------------------------------"; + } + if(_log.size() >= 1000) { + _log.pop_front(); + } + _log.push_back(message); +#else + if(MessageManager::_messageManager) { + MessageManager::_messageManager->DisplayMessage("", message + "\n"); + } +#endif +} + +string MessageManager::GetLog() +{ + auto lock = _logLock.AcquireSafe(); + stringstream ss; + for(string &msg : _log) { + ss << msg << "\n"; + } + return ss.str(); +} diff --git a/Core/MessageManager.h b/Core/MessageManager.h new file mode 100644 index 0000000..af2d11b --- /dev/null +++ b/Core/MessageManager.h @@ -0,0 +1,31 @@ +#pragma once + +#include "stdafx.h" + +#include "IMessageManager.h" +#include +#include "../Utilities/SimpleLock.h" + +class MessageManager +{ +private: + static IMessageManager* _messageManager; + static std::unordered_map _enResources; + + static bool _osdEnabled; + static SimpleLock _logLock; + static SimpleLock _messageLock; + static std::list _log; + +public: + static void SetOsdState(bool enabled); + + static string Localize(string key); + + static void RegisterMessageManager(IMessageManager* messageManager); + static void UnregisterMessageManager(IMessageManager* messageManager); + static void DisplayMessage(string title, string message, string param1 = "", string param2 = ""); + + static void Log(string message = ""); + static string GetLog(); +}; diff --git a/Core/TraceLogger.cpp b/Core/TraceLogger.cpp new file mode 100644 index 0000000..ffffb63 --- /dev/null +++ b/Core/TraceLogger.cpp @@ -0,0 +1,332 @@ +#include "stdafx.h" +#include +#include "TraceLogger.h" +#include "DisassemblyInfo.h" +#include "Console.h" +#include "Debugger.h" +#include "MemoryManager.h" +#include "../Utilities/HexUtilities.h" + +string TraceLogger::_executionTrace = ""; + +TraceLogger::TraceLogger(Debugger* debugger, shared_ptr memoryManager) +{ + _memoryManager = memoryManager; + _currentPos = 0; + _logCount = 0; + _logToFile = false; + _pendingLog = false; +} + +TraceLogger::~TraceLogger() +{ + StopLogging(); +} + +template +void TraceLogger::WriteValue(string &output, T value, RowPart& rowPart) +{ + string str = rowPart.DisplayInHex ? HexUtilities::ToHex((uint32_t)value) : std::to_string(value); + output += str; + if(rowPart.MinWidth > (int)str.size()) { + output += std::string(rowPart.MinWidth - str.size(), ' '); + } +} + +template<> +void TraceLogger::WriteValue(string &output, string value, RowPart& rowPart) +{ + output += value; + if(rowPart.MinWidth > (int)value.size()) { + output += std::string(rowPart.MinWidth - value.size(), ' '); + } +} + +void TraceLogger::SetOptions(TraceLoggerOptions options) +{ + _options = options; + string condition = _options.Condition; + string format = _options.Format; + + auto lock = _lock.AcquireSafe(); + /*_conditionData = ExpressionData(); + if(!condition.empty()) { + bool success = false; + ExpressionData rpnList = _expEvaluator->GetRpnList(condition, success); + if(success) { + _conditionData = rpnList; + } + }*/ + + _rowParts.clear(); + + std::regex formatRegex = std::regex("(\\[\\s*([^[]*?)\\s*(,\\s*([\\d]*)\\s*(h){0,1}){0,1}\\s*\\])|([^[]*)", std::regex_constants::icase); + std::sregex_iterator start = std::sregex_iterator(format.cbegin(), format.cend(), formatRegex); + std::sregex_iterator end = std::sregex_iterator(); + + for(std::sregex_iterator it = start; it != end; it++) { + const std::smatch& match = *it; + + if(match.str(1) == "") { + RowPart part = {}; + part.DataType = RowDataType::Text; + part.Text = match.str(6); + _rowParts.push_back(part); + } else { + RowPart part = {}; + + string dataType = match.str(2); + if(dataType == "ByteCode") { + part.DataType = RowDataType::ByteCode; + } else if(dataType == "Disassembly") { + part.DataType = RowDataType::Disassembly; + } else if(dataType == "EffectiveAddress") { + part.DataType = RowDataType::EffectiveAddress; + } else if(dataType == "MemoryValue") { + part.DataType = RowDataType::MemoryValue; + } else if(dataType == "Align") { + part.DataType = RowDataType::Align; + } else if(dataType == "PC") { + part.DataType = RowDataType::PC; + } else if(dataType == "A") { + part.DataType = RowDataType::A; + } else if(dataType == "X") { + part.DataType = RowDataType::X; + } else if(dataType == "Y") { + part.DataType = RowDataType::Y; + } else if(dataType == "P") { + part.DataType = RowDataType::PS; + } else if(dataType == "SP") { + part.DataType = RowDataType::SP; + } else if(dataType == "Cycle") { + part.DataType = RowDataType::Cycle; + } else if(dataType == "Scanline") { + part.DataType = RowDataType::Scanline; + } else if(dataType == "FrameCount") { + part.DataType = RowDataType::FrameCount; + } else if(dataType == "CycleCount") { + part.DataType = RowDataType::CycleCount; + } else { + part.DataType = RowDataType::Text; + part.Text = "[Invalid tag]"; + } + + if(!match.str(4).empty()) { + try { + part.MinWidth = std::stoi(match.str(4)); + } catch(std::exception) { + } + } + part.DisplayInHex = match.str(5) == "h"; + + _rowParts.push_back(part); + } + } +} + +void TraceLogger::StartLogging(string filename) +{ + _outputBuffer.clear(); + _outputFile.open(filename, ios::out | ios::binary); + _logToFile = true; +} + +void TraceLogger::StopLogging() +{ + if(_logToFile) { + _logToFile = false; + if(_outputFile) { + if(!_outputBuffer.empty()) { + _outputFile << _outputBuffer; + } + _outputFile.close(); + } + } +} + +void TraceLogger::LogExtraInfo(const char *log, uint32_t cycleCount) +{ + if(_logToFile && _options.ShowExtraInfo) { + //Flush current buffer + _outputFile << _outputBuffer; + _outputBuffer.clear(); + _outputFile << "[" << log << " - Cycle: " << std::to_string(cycleCount) << "]" << (_options.UseWindowsEol ? "\r\n" : "\n"); + } +} + +void TraceLogger::GetStatusFlag(string &output, uint8_t ps, RowPart& part) +{ + if(part.DisplayInHex) { + WriteValue(output, ps, part); + } else { + constexpr char activeStatusLetters[8] = { 'N', 'V', 'X', 'M', 'D', 'I', 'Z', 'C' }; + constexpr char inactiveStatusLetters[8] = { 'n', 'v', 'x', 'm', 'd', 'i', 'z', 'c' }; + string flags; + for(int i = 0; i < 8; i++) { + if(ps & 0x80) { + flags += activeStatusLetters[i]; + } else if(part.MinWidth >= 8) { + flags += inactiveStatusLetters[i]; + } + ps <<= 1; + } + WriteValue(output, flags, part); + } +} + +void TraceLogger::GetTraceRow(string &output, CpuState &cpuState, DisassemblyInfo &disassemblyInfo) +{ + int originalSize = (int)output.size(); + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::Text: output += rowPart.Text; break; + + case RowDataType::ByteCode: + { + string byteCode; + disassemblyInfo.GetByteCode(byteCode); + if(!rowPart.DisplayInHex) { + //Remove $ marks if not in "hex" mode (but still display the bytes as hex) + byteCode.erase(std::remove(byteCode.begin(), byteCode.end(), '$'), byteCode.end()); + } + WriteValue(output, byteCode, rowPart); + break; + } + + case RowDataType::Disassembly: + { + int indentLevel = 0; + string code; + + if(_options.IndentCode) { + indentLevel = 0xFF - (cpuState.SP & 0xFF); + code = std::string(indentLevel, ' '); + } + + //LabelManager* labelManager = _options.UseLabels ? _labelManager.get() : nullptr; + disassemblyInfo.GetDisassembly(code, _memoryManager.get()); + WriteValue(output, code, rowPart); + break; + } + + case RowDataType::EffectiveAddress: + { + /*string effectiveAddress; + disassemblyInfo.GetEffectiveAddressString(effectiveAddress, cpuState, _memoryManager.get(), _options.UseLabels ? _labelManager.get() : nullptr); + WriteValue(output, effectiveAddress, rowPart);*/ + break; + } + + case RowDataType::MemoryValue: + { + /*int32_t value = disassemblyInfo.GetMemoryValue(cpuState, _memoryManager.get()); + if(value >= 0) { + output += rowPart.DisplayInHex ? "= $" : "= "; + WriteValue(output, (uint8_t)value, rowPart); + }*/ + break; + } + + case RowDataType::Align: + if((int)output.size() - originalSize < rowPart.MinWidth) { + output += std::string(rowPart.MinWidth - (output.size() - originalSize), ' '); + } + break; + + case RowDataType::PC: WriteValue(output, (cpuState.K << 16) | cpuState.PC, rowPart); break; + case RowDataType::A: WriteValue(output, cpuState.A, rowPart); break; + case RowDataType::X: WriteValue(output, cpuState.X, rowPart); break; + case RowDataType::Y: WriteValue(output, cpuState.Y, rowPart); break; + case RowDataType::SP: WriteValue(output, cpuState.SP, rowPart); break; + case RowDataType::PS: GetStatusFlag(output, cpuState.PS, rowPart); break; + //case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break; + //case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break; + //case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break; + case RowDataType::CycleCount: WriteValue(output, cpuState.CycleCount, rowPart); break; + } + } + output += _options.UseWindowsEol ? "\r\n" : "\n"; +} +/* +bool TraceLogger::ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo) +{ + if(!_conditionData.RpnQueue.empty()) { + EvalResultType type; + if(!_expEvaluator->Evaluate(_conditionData, state, type, operationInfo)) { + if(operationInfo.OperationType == MemoryOperationType::ExecOpCode) { + //Condition did not match, keep state/disassembly info for instruction's subsequent cycles + _lastState = state; + _lastDisassemblyInfo = disassemblyInfo; + _pendingLog = true; + } + return false; + } + } + return true; +} +*/ +void TraceLogger::AddRow(DisassemblyInfo &disassemblyInfo, DebugState &state) +{ + _disassemblyCache[_currentPos] = disassemblyInfo; + _cpuStateCache[_currentPos] = state.Cpu; + //_ppuStateCache[_currentPos] = state.PPU; + _pendingLog = false; + + if(_logCount < ExecutionLogSize) { + _logCount++; + } + + if(_logToFile) { + GetTraceRow(_outputBuffer, state.Cpu, disassemblyInfo); + if(_outputBuffer.size() > 32768) { + _outputFile << _outputBuffer; + _outputBuffer.clear(); + } + } + + _currentPos = (_currentPos + 1) % ExecutionLogSize; +} +/* +void TraceLogger::LogNonExec(OperationInfo& operationInfo) +{ + if(_pendingLog) { + auto lock = _lock.AcquireSafe(); + if(ConditionMatches(_lastState, _lastDisassemblyInfo, operationInfo)) { + AddRow(_lastDisassemblyInfo, _lastState); + } + } +}*/ + +void TraceLogger::Log(DebugState &state, DisassemblyInfo &disassemblyInfo) +{ + auto lock = _lock.AcquireSafe(); + //if(ConditionMatches(state, disassemblyInfo, operationInfo)) { + AddRow(disassemblyInfo, state); + //} +} + +const char* TraceLogger::GetExecutionTrace(uint32_t lineCount) +{ + int startPos; + + _executionTrace.clear(); + { + auto lock = _lock.AcquireSafe(); + lineCount = std::min(lineCount, _logCount); + memcpy(_cpuStateCacheCopy, _cpuStateCache, sizeof(_cpuStateCache)); + //memcpy(_ppuStateCacheCopy, _ppuStateCache, sizeof(_ppuStateCache)); + memcpy(_disassemblyCacheCopy, _disassemblyCache, sizeof(_disassemblyCache)); + startPos = _currentPos + ExecutionLogSize - lineCount; + } + + for(int i = 0; i < (int)lineCount; i++) { + int index = (startPos + i) % ExecutionLogSize; + _executionTrace += HexUtilities::ToHex((_cpuStateCacheCopy[index].K << 16) | _cpuStateCacheCopy[index].PC) + "\x1"; + string byteCode; + _disassemblyCacheCopy[index].GetByteCode(byteCode); + _executionTrace += byteCode + "\x1"; + GetTraceRow(_executionTrace, _cpuStateCacheCopy[index], _disassemblyCacheCopy[index]); + } + + return _executionTrace.c_str(); +} \ No newline at end of file diff --git a/Core/TraceLogger.h b/Core/TraceLogger.h new file mode 100644 index 0000000..39d476e --- /dev/null +++ b/Core/TraceLogger.h @@ -0,0 +1,106 @@ +#pragma once +#include "stdafx.h" +#include "CpuTypes.h" +#include "DisassemblyInfo.h" +#include "../Utilities/SimpleLock.h" + +class MemoryManager; +class Debugger; +struct DebugState; + +struct TraceLoggerOptions +{ + bool ShowExtraInfo; + bool IndentCode; + bool UseLabels; + bool UseWindowsEol; + bool ExtendZeroPage; + + char Condition[1000]; + char Format[1000]; +}; + +enum class RowDataType +{ + Text = 0, + ByteCode, + Disassembly, + EffectiveAddress, + MemoryValue, + Align, + PC, + A, + X, + Y, + SP, + PS, + Cycle, + Scanline, + FrameCount, + CycleCount +}; + +struct RowPart +{ + RowDataType DataType; + string Text; + bool DisplayInHex; + int MinWidth; +}; + +class TraceLogger +{ +private: + static constexpr int ExecutionLogSize = 30000; + + //Must be static to be thread-safe when switching game + static string _executionTrace; + + TraceLoggerOptions _options; + string _outputFilepath; + string _outputBuffer; + ofstream _outputFile; + shared_ptr _memoryManager; + //shared_ptr _labelManager; + + vector _rowParts; + + bool _pendingLog; + //CpuState _lastState; + //DisassemblyInfo _lastDisassemblyInfo; + + bool _logToFile; + uint16_t _currentPos; + uint32_t _logCount; + CpuState _cpuStateCache[ExecutionLogSize] = {}; + //PPUDebugState _ppuStateCache[ExecutionLogSize] = {}; + DisassemblyInfo _disassemblyCache[ExecutionLogSize]; + + CpuState _cpuStateCacheCopy[ExecutionLogSize] = {}; + //PPUDebugState _ppuStateCacheCopy[ExecutionLogSize] = {}; + DisassemblyInfo _disassemblyCacheCopy[ExecutionLogSize]; + + SimpleLock _lock; + + void GetStatusFlag(string &output, uint8_t ps, RowPart& part); + void AddRow(DisassemblyInfo &disassemblyInfo, DebugState &state); + //bool ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo); + + void GetTraceRow(string &output, CpuState &cpuState, DisassemblyInfo &disassemblyInfo); + + template void WriteValue(string &output, T value, RowPart& rowPart); + +public: + TraceLogger(Debugger* debugger, shared_ptr memoryManager); + ~TraceLogger(); + + void Log(DebugState &state, DisassemblyInfo &disassemblyInfo); + //void LogNonExec(OperationInfo& operationInfo); + void SetOptions(TraceLoggerOptions options); + void StartLogging(string filename); + void StopLogging(); + + void LogExtraInfo(const char *log, uint32_t cycleCount); + + const char* GetExecutionTrace(uint32_t lineCount); +}; diff --git a/Core/main.cpp b/Core/main.cpp deleted file mode 100644 index 7b5b607..0000000 --- a/Core/main.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "stdafx.h" -#include "Cpu.h" -#include "MemoryManager.h" -#include "../Utilities/Timer.h" - -int main() -{ - shared_ptr memoryManager(new MemoryManager()); - shared_ptr cpu(new Cpu(memoryManager)); - Timer timer; - while(cpu->opCount < 10000000) { - cpu->Exec(); - } - - std::cout << "Time: " << std::to_string(timer.GetElapsedMS()) << std::endl; - std::cout << "OP Count: " << std::to_string(cpu->opCount) << std::endl; - std::cout << "OP/sec: " << std::to_string(cpu->opCount * 1000 / timer.GetElapsedMS()) << std::endl; - while(true); -} \ No newline at end of file diff --git a/Mesen-S.sln b/Mesen-S.sln index f2285c7..dd457f1 100644 --- a/Mesen-S.sln +++ b/Mesen-S.sln @@ -14,7 +14,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Utilities", "Utilities\Util {52C4BA3A-E699-4305-B23F-C9083FD07AB6} = {52C4BA3A-E699-4305-B23F-C9083FD07AB6} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI.NET", "GUI.NET\GUI.NET.csproj", "{08D83A7E-52A9-451E-A53A-1A7946F8BB77}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UI", "UI\UI.csproj", "{08D83A7E-52A9-451E-A53A-1A7946F8BB77}" ProjectSection(ProjectDependencies) = postProject {36ABBF1C-66E1-4577-828A-619A2EF0DAE9} = {36ABBF1C-66E1-4577-828A-619A2EF0DAE9} {AABB5225-3A49-47FF-8A48-031673CADCE9} = {AABB5225-3A49-47FF-8A48-031673CADCE9} diff --git a/UI/CommandLineHelper.cs b/UI/CommandLineHelper.cs new file mode 100644 index 0000000..ed294c8 --- /dev/null +++ b/UI/CommandLineHelper.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI +{ + class CommandLineHelper + { + public static List PreprocessCommandLineArguments(string[] args, bool toLower) + { + var switches = new List(); + for(int i = 0; i < args.Length; i++) { + if(args[i] != null) { + string arg = args[i].Trim(); + if(arg.StartsWith("--")) { + arg = "/" + arg.Substring(2); + } else if(arg.StartsWith("-")) { + arg = "/" + arg.Substring(1); + } + + if(toLower) { + arg = arg.ToLowerInvariant(); + } + switches.Add(arg); + } + } + return switches; + } + + public static void GetRomPathFromCommandLine(List switches, out string romPath, out List luaScriptsToLoad) + { + Func getValidPath = (string path, bool forLua) => { + path = path.Trim(); + if(path.ToLower().EndsWith(".lua") == forLua) { + try { + if(!File.Exists(path)) { + //Try loading file as a relative path to the folder Mesen was started from + path = Path.Combine(Program.OriginalFolder, path); + } + if(File.Exists(path)) { + return path; + } + } catch { } + } + return null; + }; + + //Check if any Lua scripts were specified + luaScriptsToLoad = new List(); + foreach(string arg in switches) { + string path = getValidPath(arg, true); + if(path != null) { + luaScriptsToLoad.Add(path); + } + } + + romPath = null; + foreach(string arg in switches) { + string path = getValidPath(arg, false); + if(path != null) { + romPath = path; + break; + } + } + } + } +} diff --git a/UI/Config/ConfigAttributes.cs b/UI/Config/ConfigAttributes.cs new file mode 100644 index 0000000..230e04c --- /dev/null +++ b/UI/Config/ConfigAttributes.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Config +{ + public class MinMaxAttribute : Attribute + { + public object Min { get; set; } + public object Max { get; set; } + + public MinMaxAttribute(object min, object max) + { + this.Min = min; + this.Max = max; + } + } + + public class ValidValuesAttribute : Attribute + { + public uint[] ValidValues { get; set; } + + public ValidValuesAttribute(params uint[] validValues) + { + this.ValidValues = validValues; + } + } +} diff --git a/UI/Config/ConfigManager.cs b/UI/Config/ConfigManager.cs new file mode 100644 index 0000000..8a24e7f --- /dev/null +++ b/UI/Config/ConfigManager.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Xml.Serialization; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; +using System.Text.RegularExpressions; +using System.Reflection; +using System.Windows.Forms; + +namespace Mesen.GUI.Config +{ + class ConfigManager + { + private static Configuration _config; + private static Configuration _dirtyConfig; + public static bool DoNotSaveSettings { get; set; } + + public static string DefaultPortableFolder { get { return Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); } } + public static string DefaultDocumentsFolder + { + get + { + if(Program.IsMono) { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), ".config", "mesen-s"); + } else { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Mesen-S"); + } + } + } + + public static string DefaultAviFolder { get { return Path.Combine(HomeFolder, "Avi"); } } + public static string DefaultMovieFolder { get { return Path.Combine(HomeFolder, "Movies"); } } + public static string DefaultSaveDataFolder { get { return Path.Combine(HomeFolder, "Saves"); } } + public static string DefaultSaveStateFolder { get { return Path.Combine(HomeFolder, "SaveStates"); } } + public static string DefaultScreenshotFolder { get { return Path.Combine(HomeFolder, "Screenshots"); } } + public static string DefaultWaveFolder { get { return Path.Combine(HomeFolder, "Wave"); } } + + public static void InitHomeFolder() + { + string portableFolder = DefaultPortableFolder; + string documentsFolder = DefaultDocumentsFolder; + + //Linux only + string portableConfig = Path.Combine(portableFolder, "settings.xml"); + string documentsConfig = Path.Combine(documentsFolder, "settings.xml"); + + HomeFolder = null; + if(File.Exists(portableConfig)) { + HomeFolder = portableFolder; + } else if(File.Exists(documentsConfig)) { + HomeFolder = documentsFolder; + } + } + + public static string GetConfigFile() + { + InitHomeFolder(); + + if(!string.IsNullOrWhiteSpace(HomeFolder)) { + return Path.Combine(HomeFolder, "settings.xml"); + } else { + return null; + } + } + + public static void CreateConfig(bool portable) + { + if(portable) { + HomeFolder = DefaultPortableFolder; + } else { + HomeFolder = DefaultDocumentsFolder; + } + + LoadConfig(); + } + + private static object _initLock = new object(); + private static void LoadConfig() + { + if(_config == null) { + lock(_initLock) { + if(_config == null) { + if(File.Exists(ConfigFile)) { + _config = Configuration.Deserialize(ConfigFile); + _dirtyConfig = Configuration.Deserialize(ConfigFile); + } else { + //Create new config file and save it to disk + _config = new Configuration(); + _dirtyConfig = new Configuration(); + _config.Save(); + } + } + } + } + } + + private static void ApplySetting(Type type, object instance, string name, string value) + { + FieldInfo[] fields = type.GetFields(); + foreach(FieldInfo info in fields) { + if(string.Compare(info.Name, name, true) == 0) { + try { + if(info.FieldType == typeof(int) || info.FieldType == typeof(uint) || info.FieldType == typeof(double)) { + MinMaxAttribute minMaxAttribute = info.GetCustomAttribute(typeof(MinMaxAttribute)) as MinMaxAttribute; + if(minMaxAttribute != null) { + if(info.FieldType == typeof(int)) { + int result; + if(int.TryParse(value, out result)) { + if(result >= (int)minMaxAttribute.Min && result <= (int)minMaxAttribute.Max) { + info.SetValue(instance, result); + } + } + } else if(info.FieldType == typeof(uint)) { + uint result; + if(uint.TryParse(value, out result)) { + if(result >= (uint)(int)minMaxAttribute.Min && result <= (uint)(int)minMaxAttribute.Max) { + info.SetValue(instance, result); + } + } + } else if(info.FieldType == typeof(double)) { + double result; + if(double.TryParse(value, out result)) { + if(result >= (double)minMaxAttribute.Min && result <= (double)minMaxAttribute.Max) { + info.SetValue(instance, result); + } + } + } + } else { + ValidValuesAttribute validValuesAttribute = info.GetCustomAttribute(typeof(ValidValuesAttribute)) as ValidValuesAttribute; + if(validValuesAttribute != null) { + uint result; + if(uint.TryParse(value, out result)) { + if(validValuesAttribute.ValidValues.Contains(result)) { + info.SetValue(instance, result); + } + } + } + } + } else if(info.FieldType == typeof(bool)) { + if(string.Compare(value, "false", true) == 0) { + info.SetValue(instance, false); + } else if(string.Compare(value, "true", true) == 0) { + info.SetValue(instance, true); + } + } else if(info.FieldType.IsEnum) { + int indexOf = Enum.GetNames(info.FieldType).Select((enumValue) => enumValue.ToLower()).ToList().IndexOf(value.ToLower()); + if(indexOf >= 0) { + info.SetValue(instance, indexOf); + } + } + } catch { + } + break; + } + } + } + + public static void ProcessSwitches(List switches) + { + Regex regex = new Regex("/([a-z0-9_A-Z.]+)=([a-z0-9_A-Z.\\-]+)"); + foreach(string param in switches) { + Match match = regex.Match(param); + if(match.Success) { + string switchName = match.Groups[1].Value; + string switchValue = match.Groups[2].Value; + + /*ApplySetting(typeof(VideoInfo), Config.VideoInfo, switchName, switchValue); + ApplySetting(typeof(AudioInfo), Config.AudioInfo, switchName, switchValue); + ApplySetting(typeof(EmulationInfo), Config.EmulationInfo, switchName, switchValue);*/ + ApplySetting(typeof(Configuration), Config, switchName, switchValue); + } + } + } + + public static void SaveConfig() + { + _config.Save(); + } + + public static string HomeFolder { get; private set; } + + public static string GetFolder(string defaultFolderName, string overrideFolder, bool useOverride) + { + string folder; + if(useOverride) { + folder = overrideFolder; + } else { + folder = defaultFolderName; + } + + try { + if(!Directory.Exists(folder)) { + Directory.CreateDirectory(folder); + } + } catch { + //If the folder doesn't exist and we couldn't create it, use the default folder + //EmuApi.WriteLogEntry("[UI] Folder could not be created: " + folder); + folder = defaultFolderName; + } + return folder; + } + +/* public static string AviFolder { get { return GetFolder(DefaultAviFolder, Config.PreferenceInfo.AviFolder, Config.PreferenceInfo.OverrideAviFolder); } } + public static string MovieFolder { get { return GetFolder(DefaultMovieFolder, Config.PreferenceInfo.MovieFolder, Config.PreferenceInfo.OverrideMovieFolder); } } + public static string SaveFolder { get { return GetFolder(DefaultSaveDataFolder, Config.PreferenceInfo.SaveDataFolder, Config.PreferenceInfo.OverrideSaveDataFolder); } } + public static string SaveStateFolder { get { return GetFolder(DefaultSaveStateFolder, Config.PreferenceInfo.SaveStateFolder, Config.PreferenceInfo.OverrideSaveStateFolder); } } + public static string ScreenshotFolder { get { return GetFolder(DefaultScreenshotFolder, Config.PreferenceInfo.ScreenshotFolder, Config.PreferenceInfo.OverrideScreenshotFolder); } } + public static string WaveFolder { get { return GetFolder(DefaultWaveFolder, Config.PreferenceInfo.WaveFolder, Config.PreferenceInfo.OverrideWaveFolder); } } +*/ + public static string DebuggerFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Debugger"), null, false); } } + public static string DownloadFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Downloads"), null, false); } } + public static string BackupFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Backups"), null, false); } } + public static string TestFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "Tests"), null, false); } } + public static string HdPackFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "HdPacks"), null, false); } } + public static string RecentGamesFolder { get { return GetFolder(Path.Combine(ConfigManager.HomeFolder, "RecentGames"), null, false); } } + public static string FontFolder { get { return GetFolder(Environment.GetFolderPath(Environment.SpecialFolder.Fonts, Environment.SpecialFolderOption.Create), null, false); } } + + public static string ConfigFile + { + get + { + if(HomeFolder == null) { + //Initializes the HomeFolder property + InitHomeFolder(); + } + + if(!Directory.Exists(HomeFolder)) { + Directory.CreateDirectory(HomeFolder); + } + + return Path.Combine(HomeFolder, "settings.xml"); + } + } + + public static Configuration Config + { + get + { + LoadConfig(); + return _dirtyConfig; + } + } + + private static DateTime _lastSaveTime = DateTime.MinValue; + public static void ApplyChanges() + { + _config.NeedToSave = false; + _config = _dirtyConfig.Clone(); + _config.NeedToSave = true; + + if((DateTime.Now - _lastSaveTime).Seconds > 1) { + ConfigManager.SaveConfig(); + _lastSaveTime = DateTime.Now; + } + } + + public static void RejectChanges() + { + _dirtyConfig = _config.Clone(); + } + + public static void RevertToBackup(Configuration config) + { + _config = config; + _dirtyConfig = _config.Clone(); + } + + public static void RevertDirtyToBackup(Configuration config) + { + _dirtyConfig = config.Clone(); + } + + public static void ResetSettings() + { + //DefaultKeyMappingType defaultMappings = Config.InputInfo.DefaultMapping; + _dirtyConfig = new Configuration(); + //Config.InputInfo.DefaultMapping = defaultMappings; + Config.InitializeDefaults(); + ApplyChanges(); + Config.ApplyConfig(); + } + + public static void RestartMesen(bool preventSave = false) + { + if(preventSave) { + DoNotSaveSettings = true; + } + + if(Program.IsMono) { + System.Diagnostics.Process.Start("mono", "\"" + Assembly.GetEntryAssembly().Location + "\" /delayrestart"); + } else { + System.Diagnostics.Process.Start(Assembly.GetEntryAssembly().Location, "/delayrestart"); + } + } + } +} diff --git a/UI/Config/Configuration.cs b/UI/Config/Configuration.cs new file mode 100644 index 0000000..ec4936f --- /dev/null +++ b/UI/Config/Configuration.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; +using Mesen.GUI.Forms; + +namespace Mesen.GUI.Config +{ + public class Configuration + { + private const int MaxRecentFiles = 10; + private bool _needToSave = false; + + public string Version = "0.1.0"; + public List RecentFiles; + public DebugInfo Debug; + public Point? WindowLocation; + public Size? WindowSize; + + public Configuration() + { + RecentFiles = new List(); + Debug = new DebugInfo(); + } + + ~Configuration() + { + //Try to save before destruction if we were unable to save at a previous point in time + Save(); + } + + public void Save() + { + if(_needToSave) { + Serialize(ConfigManager.ConfigFile); + } + } + + public bool NeedToSave + { + set + { + _needToSave = value; + } + } + + public void ApplyConfig() + { + } + + public void InitializeDefaults() + { + } + + public void AddRecentFile(ResourcePath romFile, ResourcePath? patchFile) + { + RecentItem existingItem = RecentFiles.Where((item) => item.RomFile == romFile && item.PatchFile == patchFile).FirstOrDefault(); + if(existingItem != null) { + RecentFiles.Remove(existingItem); + } + RecentItem recentItem = new RecentItem { RomFile = romFile, PatchFile = patchFile }; + + RecentFiles.Insert(0, recentItem); + if(RecentFiles.Count > Configuration.MaxRecentFiles) { + RecentFiles.RemoveAt(Configuration.MaxRecentFiles); + } + ConfigManager.ApplyChanges(); + } + + public static Configuration Deserialize(string configFile) + { + Configuration config; + + try { + XmlSerializer xmlSerializer = new XmlSerializer(typeof(Configuration)); + using(TextReader textReader = new StreamReader(configFile)) { + config = (Configuration)xmlSerializer.Deserialize(textReader); + } + } catch { + config = new Configuration(); + } + + return config; + } + + public void Serialize(string configFile) + { + try { + if(!ConfigManager.DoNotSaveSettings) { + XmlSerializer xmlSerializer = new XmlSerializer(typeof(Configuration)); + using(TextWriter textWriter = new StreamWriter(configFile)) { + xmlSerializer.Serialize(textWriter, this); + } + } + _needToSave = false; + } catch { + //This can sometime fail due to the file being used by another Mesen instance, etc. + //In this case, the _needToSave flag will still be set, and the config will be saved when the emulator is closed + } + } + + public Configuration Clone() + { + XmlSerializer xmlSerializer = new XmlSerializer(typeof(Configuration)); + StringWriter stringWriter = new StringWriter(); + xmlSerializer.Serialize(stringWriter, this); + + StringReader stringReader = new StringReader(stringWriter.ToString()); + Configuration config = (Configuration)xmlSerializer.Deserialize(stringReader); + config.NeedToSave = false; + return config; + } + } + + public class RecentItem + { + public ResourcePath RomFile; + public ResourcePath? PatchFile; + + public override string ToString() + { + string text; + /*if(ConfigManager.Config.PreferenceInfo.ShowFullPathInRecents) { + text = RomFile.ReadablePath.Replace("&", "&&"); + } else {*/ + text = Path.GetFileName(RomFile.FileName).Replace("&", "&&"); + //} + + if(PatchFile.HasValue) { + text += " [" + Path.GetFileName(PatchFile.Value) + "]"; + } + return text; + } + } + + [Flags] + public enum DefaultKeyMappingType + { + None = 0, + Xbox = 1, + Ps4 = 2, + WasdKeys = 3, + ArrowKeys = 4 + } +} diff --git a/UI/Config/DebugInfo.cs b/UI/Config/DebugInfo.cs new file mode 100644 index 0000000..e1d3699 --- /dev/null +++ b/UI/Config/DebugInfo.cs @@ -0,0 +1,85 @@ +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 TraceLoggerOptions + { + public bool ShowByteCode; + public bool ShowRegisters; + public bool ShowCpuCycles; + public bool ShowPpuCycles; + public bool ShowPpuScanline; + public bool ShowPpuFrames; + public bool ShowExtraInfo; + public bool IndentCode; + public bool ShowEffectiveAddresses; + public bool ShowMemoryValues; + 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, + Text = 1, + CompactText = 2 + } +} diff --git a/UI/Controls/BaseControl.cs b/UI/Controls/BaseControl.cs new file mode 100644 index 0000000..fe99d08 --- /dev/null +++ b/UI/Controls/BaseControl.cs @@ -0,0 +1,74 @@ +using System.Windows.Forms; +using System.Drawing; +using Mesen.GUI; +using System; +using System.ComponentModel; + +namespace Mesen.GUI.Controls +{ + public class BaseControl : UserControl + { + public static float DefaultFontSize = Program.IsMono ? 10 : 12; + + public static string MonospaceFontFamily + { + get + { + if(Program.IsMono) { + return "DroidSansMono"; + } else { + return "Consolas"; + } + } + } + + private static bool? _isDesignMode = null; + public bool IsDesignMode + { + get + { + try { + if(!_isDesignMode.HasValue) { + _isDesignMode = System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv"; + } + return _isDesignMode.Value || LicenseManager.UsageMode == LicenseUsageMode.Designtime; + } catch { + return false; + } + } + } + + public static Bitmap DownArrow + { + get + { + if(!Program.IsMono && Environment.OSVersion.Version >= new Version(6, 2)) { + return Properties.Resources.DownArrowWin10; + } else { + return Properties.Resources.DownArrow; + } + } + } + + public new AutoScaleMode AutoScaleMode + { + set { + if(Program.IsMono) { + base.AutoScaleMode = AutoScaleMode.None; + } else { + base.AutoScaleMode = value; + } + } + } + + public new SizeF AutoScaleDimensions + { + set + { + if(!Program.IsMono) { + base.AutoScaleDimensions = value; + } + } + } + } +} \ No newline at end of file diff --git a/UI/Controls/MesenNumericUpDown.cs b/UI/Controls/MesenNumericUpDown.cs new file mode 100644 index 0000000..267e699 --- /dev/null +++ b/UI/Controls/MesenNumericUpDown.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + class MesenNumericUpDown : BaseControl + { + private TextBox txtValue; + private Button btnUp; + private Button btnDown; + private Panel pnlButtons; + private Timer tmrRepeat; + + private int _repeatCount = 0; + private bool _repeatIncrease = false; + private bool _preventClick = false; + + public event EventHandler ValueChanged; + + public MesenNumericUpDown() + { + InitializeComponent(); + + if(!IsDesignMode) { + this.BackColor = SystemColors.ControlLightLight; + this.MaximumSize = new Size(10000, 21); + this.MinimumSize = new Size(0, 21); + this.Size = new Size(62, 21); + + this.btnDown.Image = Properties.Resources.NudDownArrow; + this.btnUp.Image = Properties.Resources.NudUpArrow; + + if(Program.IsMono) { + this.BorderStyle = BorderStyle.None; + this.txtValue.Dock = DockStyle.Fill; + this.txtValue.Multiline = true; + this.btnUp.Location = new Point(-1, 0); + this.btnDown.Location = new Point(-1, 10); + this.MinimumSize = new Size(0, 22); + } else { + this.BorderStyle = BorderStyle.FixedSingle; + } + } + } + + private void tmrRepeat_Tick(object sender, EventArgs e) + { + tmrRepeat.Interval = _repeatCount > 5 ? 75 : 200; + + if(_repeatIncrease) { + this.Value += this.Increment; + } else { + this.Value -= this.Increment; + } + _repeatCount++; + _preventClick = true; + } + + private void btn_MouseDown(object sender, MouseEventArgs e) + { + _repeatCount = 0; + _repeatIncrease = (sender == btnUp); + tmrRepeat.Start(); + } + + private void btn_MouseUp(object sender, MouseEventArgs e) + { + tmrRepeat.Stop(); + } + + private void btnDown_Click(object sender, EventArgs e) + { + if(!_preventClick) { + this.Value -= this.Increment; + } + _preventClick = false; + } + + private void btnUp_Click(object sender, EventArgs e) + { + if(!_preventClick) { + this.Value += this.Increment; + } + _preventClick = false; + } + + private void txtValue_TextChanged(object sender, EventArgs e) + { + decimal val; + if(string.IsNullOrWhiteSpace(txtValue.Text)) { + SetValue(0, false); + } else if(decimal.TryParse(txtValue.Text, out val)) { + SetValue(val, false); + } + } + + private void txtValue_Validated(object sender, EventArgs e) + { + decimal val; + if(string.IsNullOrWhiteSpace(txtValue.Text)) { + SetValue(0, true); + } else if(decimal.TryParse(txtValue.Text, out val)) { + SetValue(val, true); + } else { + SetValue(this.Value, true); + } + } + + private void SetValue(decimal value, bool updateText) + { + value = decimal.Round(value, this.DecimalPlaces); + + if(value > Maximum) { + value = Maximum; + } + if(value < Minimum) { + value = Minimum; + } + + if(value != _value) { + _value = value; + ValueChanged?.Invoke(this, EventArgs.Empty); + } + if(updateText) { + txtValue.Text = value.ToString("0" + (this.DecimalPlaces > 0 ? "." : "") + new string('0', this.DecimalPlaces)); + } + } + + private decimal _value = 0; + public decimal Value + { + get { return _value; } + set + { + SetValue(value, true); + } + } + + public new string Text + { + get { return txtValue.Text; } + set { txtValue.Text = value; } + } + + private decimal _maximum = 100; + public decimal Maximum + { + get { return _maximum; } + set { _maximum = value; SetValue(this.Value, true); } + } + + private decimal _minimum = 0; + public decimal Minimum + { + get { return _minimum; } + set { _minimum = value; SetValue(this.Value, true); } + } + + public decimal Increment { get; set; } = 1; + + private int _decimalPlaces = 0; + public int DecimalPlaces + { + get { return _decimalPlaces; } + set { _decimalPlaces = value; } + } + + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); + if(this.Height < 21) { + this.Height = 21; + } + } + + private void txtValue_KeyDown(object sender, KeyEventArgs e) + { + if(e.KeyCode == Keys.Up) { + this.Value += this.Increment; + e.SuppressKeyPress = true; + } else if(e.KeyCode == Keys.Down) { + this.Value -= this.Increment; + e.SuppressKeyPress = true; + } else if( + !((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) || + (e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.NumPad9) || + e.KeyCode == Keys.OemPeriod || e.KeyCode == Keys.Oemcomma || + e.KeyCode == Keys.Delete || e.KeyCode == Keys.Left || + e.KeyCode == Keys.Right || e.KeyCode == Keys.Home || + e.KeyCode == Keys.End || e.KeyCode == Keys.Back) + ) { + e.SuppressKeyPress = true; + } + } + + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.txtValue = new System.Windows.Forms.TextBox(); + this.btnUp = new System.Windows.Forms.Button(); + this.btnDown = new System.Windows.Forms.Button(); + this.pnlButtons = new System.Windows.Forms.Panel(); + this.pnlButtons.SuspendLayout(); + this.SuspendLayout(); + // + // txtValue + // + this.txtValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.txtValue.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.txtValue.Location = new System.Drawing.Point(1, 3); + this.txtValue.Margin = new System.Windows.Forms.Padding(0); + this.txtValue.Name = "txtValue"; + this.txtValue.Size = new System.Drawing.Size(44, 13); + this.txtValue.TabIndex = 0; + this.txtValue.TextChanged += new System.EventHandler(this.txtValue_TextChanged); + this.txtValue.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txtValue_KeyDown); + this.txtValue.Validated += new System.EventHandler(this.txtValue_Validated); + // + // btnUp + // + this.btnUp.Location = new System.Drawing.Point(1, -1); + this.btnUp.Margin = new System.Windows.Forms.Padding(0); + this.btnUp.Name = "btnUp"; + this.btnUp.Size = new System.Drawing.Size(15, 11); + this.btnUp.TabIndex = 2; + this.btnUp.TabStop = false; + this.btnUp.UseVisualStyleBackColor = true; + this.btnUp.Click += new System.EventHandler(this.btnUp_Click); + this.btnUp.MouseDown += new System.Windows.Forms.MouseEventHandler(this.btn_MouseDown); + this.btnUp.MouseUp += new System.Windows.Forms.MouseEventHandler(this.btn_MouseUp); + // + // btnDown + // + this.btnDown.Location = new System.Drawing.Point(1, 9); + this.btnDown.Margin = new System.Windows.Forms.Padding(0); + this.btnDown.Name = "btnDown"; + this.btnDown.Size = new System.Drawing.Size(15, 11); + this.btnDown.TabIndex = 3; + this.btnDown.TabStop = false; + this.btnDown.UseVisualStyleBackColor = true; + this.btnDown.Click += new System.EventHandler(this.btnDown_Click); + this.btnDown.MouseDown += new System.Windows.Forms.MouseEventHandler(this.btn_MouseDown); + this.btnDown.MouseUp += new System.Windows.Forms.MouseEventHandler(this.btn_MouseUp); + // + // pnlButtons + // + this.pnlButtons.Controls.Add(this.btnDown); + this.pnlButtons.Controls.Add(this.btnUp); + this.pnlButtons.Dock = System.Windows.Forms.DockStyle.Right; + this.pnlButtons.Location = new System.Drawing.Point(47, 0); + this.pnlButtons.Margin = new System.Windows.Forms.Padding(0); + this.pnlButtons.Name = "pnlButtons"; + this.pnlButtons.Size = new System.Drawing.Size(15, 21); + this.pnlButtons.TabIndex = 1; + // + // tmrRepeat + // + this.tmrRepeat = new Timer(components); + this.tmrRepeat.Tick += tmrRepeat_Tick; + // + // MesenNumericUpDown + // + this.Controls.Add(this.txtValue); + this.Controls.Add(this.pnlButtons); + this.MaximumSize = new System.Drawing.Size(10000, 21); + this.MinimumSize = new System.Drawing.Size(0, 21); + this.Name = "MesenNumericUpDown"; + this.Size = new System.Drawing.Size(62, 21); + this.pnlButtons.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + } + + /// + /// 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); + } + } +} diff --git a/UI/Controls/MesenNumericUpDown.resx b/UI/Controls/MesenNumericUpDown.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Controls/MesenNumericUpDown.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/Controls/MyListView.cs b/UI/Controls/MyListView.cs new file mode 100644 index 0000000..c0ebfff --- /dev/null +++ b/UI/Controls/MyListView.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + class MyListView : ListView + { + private bool _preventCheck = false; + + public MyListView() + { + this.DoubleBuffered = true; + } + + protected override void OnItemCheck(ItemCheckEventArgs e) + { + if(this._preventCheck || Control.ModifierKeys.HasFlag(Keys.Control) || Control.ModifierKeys.HasFlag(Keys.Shift)) { + e.NewValue = e.CurrentValue; + this._preventCheck = false; + } else { + base.OnItemCheck(e); + } + } + + protected override void OnMouseDown(MouseEventArgs e) + { + if(e.Button == MouseButtons.Left && e.Clicks > 1) { + this._preventCheck = true; + } else { + this._preventCheck = false; + } + base.OnMouseDown(e); + } + + protected override void OnKeyDown(KeyEventArgs e) + { + this._preventCheck = false; + base.OnKeyDown(e); + } + } + + public class WatchListView : DoubleBufferedListView + { + public delegate void MoveUpDownHandler(Keys keyData, ref bool processed); + public event MoveUpDownHandler OnMoveUpDown; + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if(keyData.HasFlag(Keys.Up) || keyData.HasFlag(Keys.Down)) { + bool processed = false; + OnMoveUpDown?.Invoke(keyData, ref processed); + if(processed) { + return true; + } + } + return base.ProcessCmdKey(ref msg, keyData); + } + } + + public class DoubleBufferedListView : ListView + { + public DoubleBufferedListView() + { + this.DoubleBuffered = true; + } + + protected override void OnVirtualItemsSelectionRangeChanged(ListViewVirtualItemsSelectionRangeChangedEventArgs e) + { + base.OnVirtualItemsSelectionRangeChanged(e); + base.OnSelectedIndexChanged(e); + } + } +} diff --git a/UI/Controls/ctrlAutoGrowLabel.cs b/UI/Controls/ctrlAutoGrowLabel.cs new file mode 100644 index 0000000..2f7cf6a --- /dev/null +++ b/UI/Controls/ctrlAutoGrowLabel.cs @@ -0,0 +1,50 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + public class ctrlAutoGrowLabel : Label + { + private bool _growing; + + public ctrlAutoGrowLabel() + { + this.AutoSize = false; + } + + private void resizeLabel() + { + if(_growing) { + return; + } + + try { + _growing = true; + Size textSize = new Size(this.ClientSize.Width - this.Padding.Left - this.Padding.Right, Int32.MaxValue); + textSize = TextRenderer.MeasureText(this.Text, this.Font, textSize, TextFormatFlags.WordBreak); + this.ClientSize = new Size(textSize.Width + this.Margin.Size.Width + this.Padding.Size.Width, textSize.Height); + } finally { + _growing = false; + } + } + + protected override void OnTextChanged(EventArgs e) + { + base.OnTextChanged(e); + resizeLabel(); + } + + protected override void OnFontChanged(EventArgs e) + { + base.OnFontChanged(e); + resizeLabel(); + } + + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); + resizeLabel(); + } + } +} diff --git a/UI/Controls/ctrlHorizontalTrackbar.cs b/UI/Controls/ctrlHorizontalTrackbar.cs new file mode 100644 index 0000000..2166739 --- /dev/null +++ b/UI/Controls/ctrlHorizontalTrackbar.cs @@ -0,0 +1,67 @@ +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; + +namespace Mesen.GUI.Controls +{ + public partial class ctrlHorizontalTrackbar : BaseControl + { + public event EventHandler ValueChanged + { + add { trackBar.ValueChanged += value; } + remove { trackBar.ValueChanged -= value; } + } + + public ctrlHorizontalTrackbar() + { + InitializeComponent(); + + if(!Program.IsMono) { + this.trackBar.BackColor = System.Drawing.SystemColors.ControlLightLight; + } + } + + public int Maximum + { + get { return trackBar.Maximum; } + set { trackBar.Maximum = value; } + } + + public int Minimum + { + get { return trackBar.Minimum; } + set { trackBar.Minimum = value; } + } + + [Bindable(true)] + [Browsable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + [EditorBrowsable(EditorBrowsableState.Always)] + public override string Text + { + get { return lblText.Text; } + set { lblText.Text = value; } + } + + public int Value + { + get { return trackBar.Value; } + set + { + trackBar.Value = Math.Max(trackBar.Minimum, Math.Min(value, trackBar.Maximum)); + lblValue.Text = trackBar.Value.ToString(); + } + } + + private void trackBar_ValueChanged(object sender, EventArgs e) + { + lblValue.Text = trackBar.Value.ToString(); + } + } +} diff --git a/UI/Controls/ctrlHorizontalTrackbar.designer.cs b/UI/Controls/ctrlHorizontalTrackbar.designer.cs new file mode 100644 index 0000000..2b23136 --- /dev/null +++ b/UI/Controls/ctrlHorizontalTrackbar.designer.cs @@ -0,0 +1,118 @@ +namespace Mesen.GUI.Controls +{ + partial class ctrlHorizontalTrackbar + { + /// + /// 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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.trackBar = new System.Windows.Forms.TrackBar(); + this.lblValue = new System.Windows.Forms.Label(); + this.lblText = new System.Windows.Forms.Label(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trackBar)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 35F)); + this.tableLayoutPanel1.Controls.Add(this.trackBar, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.lblValue, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.lblText, 0, 0); + 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 = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + 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(206, 50); + this.tableLayoutPanel1.TabIndex = 0; + // + // trackBar + // + this.trackBar.AutoSize = false; + this.trackBar.Dock = System.Windows.Forms.DockStyle.Fill; + this.trackBar.Location = new System.Drawing.Point(0, 13); + this.trackBar.Margin = new System.Windows.Forms.Padding(0); + this.trackBar.Maximum = 100; + this.trackBar.Minimum = -100; + this.trackBar.Name = "trackBar"; + this.trackBar.Size = new System.Drawing.Size(171, 35); + this.trackBar.TabIndex = 13; + this.trackBar.TickFrequency = 10; + this.trackBar.Value = 50; + this.trackBar.ValueChanged += new System.EventHandler(this.trackBar_ValueChanged); + // + // lblValue + // + this.lblValue.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.lblValue.BackColor = System.Drawing.Color.White; + this.lblValue.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblValue.Location = new System.Drawing.Point(174, 13); + this.lblValue.MinimumSize = new System.Drawing.Size(30, 17); + this.lblValue.Name = "lblValue"; + this.lblValue.Size = new System.Drawing.Size(30, 17); + this.lblValue.TabIndex = 17; + this.lblValue.Text = "100"; + this.lblValue.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // lblText + // + this.lblText.AutoSize = true; + this.lblText.Dock = System.Windows.Forms.DockStyle.Fill; + this.lblText.Location = new System.Drawing.Point(3, 0); + this.lblText.Name = "lblText"; + this.lblText.Size = new System.Drawing.Size(165, 13); + this.lblText.TabIndex = 18; + this.lblText.Text = "Text"; + this.lblText.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // ctrlHorizontalTrackbar + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(0); + this.Name = "ctrlHorizontalTrackbar"; + this.Size = new System.Drawing.Size(206, 50); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trackBar)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TrackBar trackBar; + private System.Windows.Forms.Label lblValue; + private System.Windows.Forms.Label lblText; + } +} diff --git a/UI/Controls/ctrlHorizontalTrackbar.resx b/UI/Controls/ctrlHorizontalTrackbar.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Controls/ctrlHorizontalTrackbar.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/Controls/ctrlLoadingRom.cs b/UI/Controls/ctrlLoadingRom.cs new file mode 100644 index 0000000..4f1ee9f --- /dev/null +++ b/UI/Controls/ctrlLoadingRom.cs @@ -0,0 +1,20 @@ +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; + +namespace Mesen.GUI.Controls +{ + public partial class ctrlLoadingRom : BaseControl + { + public ctrlLoadingRom() + { + InitializeComponent(); + } + } +} diff --git a/UI/Controls/ctrlLoadingRom.designer.cs b/UI/Controls/ctrlLoadingRom.designer.cs new file mode 100644 index 0000000..f3178b5 --- /dev/null +++ b/UI/Controls/ctrlLoadingRom.designer.cs @@ -0,0 +1,96 @@ +namespace Mesen.GUI.Controls +{ + partial class ctrlLoadingRom + { + /// + /// 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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.pbLoading = new System.Windows.Forms.ProgressBar(); + this.lblExtractingFile = new System.Windows.Forms.Label(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 200F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Controls.Add(this.pbLoading, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.lblExtractingFile, 0, 2); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 4; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(261, 107); + this.tableLayoutPanel1.TabIndex = 0; + // + // pbLoading + // + this.pbLoading.Dock = System.Windows.Forms.DockStyle.Fill; + this.pbLoading.Location = new System.Drawing.Point(33, 35); + this.pbLoading.Name = "pbLoading"; + this.pbLoading.Size = new System.Drawing.Size(194, 23); + this.pbLoading.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.pbLoading.TabIndex = 0; + // + // lblExtractingFile + // + this.lblExtractingFile.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.lblExtractingFile.AutoSize = true; + this.tableLayoutPanel1.SetColumnSpan(this.lblExtractingFile, 3); + this.lblExtractingFile.ForeColor = System.Drawing.Color.White; + this.lblExtractingFile.Location = new System.Drawing.Point(61, 61); + this.lblExtractingFile.Name = "lblExtractingFile"; + this.lblExtractingFile.Size = new System.Drawing.Size(138, 13); + this.lblExtractingFile.TabIndex = 1; + this.lblExtractingFile.Text = "Extracting file, please wait..."; + // + // ctrlLoadingRom + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.Black; + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "ctrlLoadingRom"; + this.Size = new System.Drawing.Size(261, 107); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.ProgressBar pbLoading; + private System.Windows.Forms.Label lblExtractingFile; + } +} diff --git a/UI/Controls/ctrlLoadingRom.resx b/UI/Controls/ctrlLoadingRom.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Controls/ctrlLoadingRom.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/Controls/ctrlMesenContextMenuStrip.cs b/UI/Controls/ctrlMesenContextMenuStrip.cs new file mode 100644 index 0000000..38424d2 --- /dev/null +++ b/UI/Controls/ctrlMesenContextMenuStrip.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + public class ctrlMesenContextMenuStrip : ContextMenuStrip + { + public ctrlMesenContextMenuStrip() : base() + { + } + + public ctrlMesenContextMenuStrip(IContainer container) : base(container) + { + } + + internal bool ProcessCommandKey(ref Message msg, Keys keyData) + { + return this.ProcessCmdKey(ref msg, keyData); + } + } +} diff --git a/UI/Controls/ctrlMesenMenuStrip.cs b/UI/Controls/ctrlMesenMenuStrip.cs new file mode 100644 index 0000000..7883602 --- /dev/null +++ b/UI/Controls/ctrlMesenMenuStrip.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + public class ctrlMesenMenuStrip : MenuStrip + { + private const int WM_MOUSEACTIVATE = 0x21; + protected override void WndProc(ref Message m) + { + if(m.Msg == WM_MOUSEACTIVATE && this.CanFocus && !this.Focused) { + this.FindForm()?.Focus(); + } + base.WndProc(ref m); + } + } +} diff --git a/UI/Controls/ctrlMesenPictureBox.cs b/UI/Controls/ctrlMesenPictureBox.cs new file mode 100644 index 0000000..1e3ec2e --- /dev/null +++ b/UI/Controls/ctrlMesenPictureBox.cs @@ -0,0 +1,16 @@ +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + public class ctrlMesenPictureBox : PictureBox + { + public InterpolationMode InterpolationMode { get; set; } + + protected override void OnPaint(PaintEventArgs paintEventArgs) + { + paintEventArgs.Graphics.InterpolationMode = InterpolationMode; + base.OnPaint(paintEventArgs); + } + } +} diff --git a/UI/Controls/ctrlMesenToolStrip.cs b/UI/Controls/ctrlMesenToolStrip.cs new file mode 100644 index 0000000..cd686eb --- /dev/null +++ b/UI/Controls/ctrlMesenToolStrip.cs @@ -0,0 +1,60 @@ +using Mesen.GUI.Config; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Controls +{ + public class ctrlMesenToolStrip : ToolStrip + { + private const int WM_MOUSEACTIVATE = 0x21; + protected override void WndProc(ref Message m) + { + if(m.Msg == WM_MOUSEACTIVATE && this.CanFocus && !this.Focused) { + this.FindForm()?.Focus(); + } + base.WndProc(ref m); + } + + public void AddItemToToolbar(ToolStripMenuItem item, string caption = null) + { + if(item == null) { + this.Items.Add("-"); + } else { + ToolStripItem newItem = item.HasDropDownItems ? (ToolStripItem)new ToolStripDropDownButton(item.Text, item.Image) : (ToolStripItem)new ToolStripButton(item.Image); + if(item.Image == null) { + newItem.Text = item.Text; + } + //newItem.ToolTipText = (caption ?? item.Text) + (item.ShortcutKeys != Keys.None ? $" ({DebuggerShortcutsConfig.GetShortcutDisplay(item.ShortcutKeys)})" : ""); + newItem.Click += (s, e) => item.PerformClick(); + if(newItem is ToolStripButton) { + ((ToolStripButton)newItem).Checked = item.Checked; + item.CheckedChanged += (s, e) => ((ToolStripButton)newItem).Checked = item.Checked; + } + newItem.Enabled = item.Enabled; + //newItem.MouseEnter += (s, e) => newItem.ToolTipText = (caption ?? item.Text) + (item.ShortcutKeys != Keys.None ? $" ({DebuggerShortcutsConfig.GetShortcutDisplay(item.ShortcutKeys)})" : ""); + item.EnabledChanged += (s, e) => newItem.Enabled = item.Enabled; + item.VisibleChanged += (s, e) => newItem.Visible = item.Visible; + + if(item.HasDropDownItems) { + foreach(ToolStripItem ddItem in item.DropDownItems) { + ToolStripItem newDdItem = ((ToolStripDropDownButton)newItem).DropDownItems.Add(ddItem.Text, ddItem.Image); + newDdItem.Click += (s, e) => ddItem.PerformClick(); + } + } + + this.Items.Add(newItem); + } + } + + public void AddItemsToToolbar(params ToolStripMenuItem[] items) + { + foreach(ToolStripMenuItem item in items) { + AddItemToToolbar(item); + } + } + } +} diff --git a/UI/Controls/ctrlPathSelection.cs b/UI/Controls/ctrlPathSelection.cs new file mode 100644 index 0000000..4e9c1a3 --- /dev/null +++ b/UI/Controls/ctrlPathSelection.cs @@ -0,0 +1,59 @@ +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.Controls; + +namespace Mesen.GUI.Forms.Config +{ + public partial class ctrlPathSelection : BaseControl + { + public ctrlPathSelection() + { + InitializeComponent(); + + txtDisabledPath.ReadOnly = true; + txtDisabledPath.Visible = false; + } + + public string DisabledText + { + get { return txtDisabledPath.Text; } + set { txtDisabledPath.Text = value; } + } + + public override string Text + { + get { return txtPath.Text; } + set { txtPath.Text = value; } + } + + public new bool Enabled + { + get { return !txtPath.ReadOnly; } + set + { + txtPath.Visible = value; + txtDisabledPath.Visible = !value; + tlpPath.ColumnStyles[0].SizeType = value ? SizeType.Percent : SizeType.Absolute; + tlpPath.ColumnStyles[1].SizeType = value ? SizeType.Absolute : SizeType.Percent; + tlpPath.ColumnStyles[0].Width = value ? 100F : 0F; + tlpPath.ColumnStyles[1].Width = value ? 0F : 100F; + btnBrowse.Visible = value; + } + } + + private void btnBrowse_Click(object sender, EventArgs e) + { + FolderBrowserDialog fbd = new FolderBrowserDialog(); + if(fbd.ShowDialog() == DialogResult.OK) { + txtPath.Text = fbd.SelectedPath; + } + } + } +} diff --git a/UI/Controls/ctrlPathSelection.designer.cs b/UI/Controls/ctrlPathSelection.designer.cs new file mode 100644 index 0000000..8c0af67 --- /dev/null +++ b/UI/Controls/ctrlPathSelection.designer.cs @@ -0,0 +1,126 @@ +namespace Mesen.GUI.Forms.Config +{ + partial class ctrlPathSelection + { + /// + /// 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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.tlpPath = new System.Windows.Forms.TableLayoutPanel(); + this.txtDisabledPath = new System.Windows.Forms.TextBox(); + this.txtPath = new System.Windows.Forms.TextBox(); + this.btnBrowse = new System.Windows.Forms.Button(); + this.tableLayoutPanel1.SuspendLayout(); + this.tlpPath.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Controls.Add(this.tlpPath, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.btnBrowse, 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 = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(235, 21); + this.tableLayoutPanel1.TabIndex = 0; + // + // tlpPath + // + this.tlpPath.ColumnCount = 2; + this.tlpPath.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpPath.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 0F)); + this.tlpPath.Controls.Add(this.txtDisabledPath, 1, 0); + this.tlpPath.Controls.Add(this.txtPath, 0, 0); + this.tlpPath.Dock = System.Windows.Forms.DockStyle.Fill; + this.tlpPath.Location = new System.Drawing.Point(0, 0); + this.tlpPath.Margin = new System.Windows.Forms.Padding(0); + this.tlpPath.Name = "tlpPath"; + this.tlpPath.RowCount = 1; + this.tlpPath.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tlpPath.Size = new System.Drawing.Size(209, 21); + this.tlpPath.TabIndex = 1; + // + // txtDisabledPath + // + this.txtDisabledPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.txtDisabledPath.Location = new System.Drawing.Point(209, 0); + this.txtDisabledPath.Margin = new System.Windows.Forms.Padding(0); + this.txtDisabledPath.Name = "txtDisabledPath"; + this.txtDisabledPath.Size = new System.Drawing.Size(1, 20); + this.txtDisabledPath.TabIndex = 1; + // + // txtPath + // + this.txtPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.txtPath.Location = new System.Drawing.Point(0, 0); + this.txtPath.Margin = new System.Windows.Forms.Padding(0); + this.txtPath.Name = "txtPath"; + this.txtPath.Size = new System.Drawing.Size(209, 20); + this.txtPath.TabIndex = 0; + // + // btnBrowse + // + this.btnBrowse.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.btnBrowse.Location = new System.Drawing.Point(209, 0); + this.btnBrowse.Margin = new System.Windows.Forms.Padding(0); + this.btnBrowse.Name = "btnBrowse"; + this.btnBrowse.Size = new System.Drawing.Size(26, 20); + this.btnBrowse.TabIndex = 1; + this.btnBrowse.Text = "..."; + this.btnBrowse.UseVisualStyleBackColor = true; + this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click); + // + // ctrlPathSelection + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(0); + this.MaximumSize = new System.Drawing.Size(1000, 21); + this.MinimumSize = new System.Drawing.Size(0, 21); + this.Name = "ctrlPathSelection"; + this.Size = new System.Drawing.Size(235, 21); + this.tableLayoutPanel1.ResumeLayout(false); + this.tlpPath.ResumeLayout(false); + this.tlpPath.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.TextBox txtPath; + private System.Windows.Forms.Button btnBrowse; + private System.Windows.Forms.TableLayoutPanel tlpPath; + private System.Windows.Forms.TextBox txtDisabledPath; + } +} diff --git a/UI/Controls/ctrlPathSelection.resx b/UI/Controls/ctrlPathSelection.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Controls/ctrlPathSelection.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/Controls/ctrlRenderer.cs b/UI/Controls/ctrlRenderer.cs new file mode 100644 index 0000000..7037088 --- /dev/null +++ b/UI/Controls/ctrlRenderer.cs @@ -0,0 +1,67 @@ +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; + +namespace Mesen.GUI.Controls +{ + public partial class ctrlRenderer : BaseControl + { + private const int LeftMouseButtonKeyCode = 0x200; + private const int RightMouseButtonKeyCode = 0x201; + private const int MiddleMouseButtonKeyCode = 0x202; + + public ctrlRenderer() + { + InitializeComponent(); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + SetMouseButtonState(Control.MouseButtons); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + SetMouseButtonState(Control.MouseButtons); + } + + private void SetMouseButtonState(MouseButtons pressedButtons) + { + EmuApi.SetKeyState(LeftMouseButtonKeyCode, pressedButtons.HasFlag(MouseButtons.Left)); + EmuApi.SetKeyState(RightMouseButtonKeyCode, pressedButtons.HasFlag(MouseButtons.Right)); + EmuApi.SetKeyState(MiddleMouseButtonKeyCode, pressedButtons.HasFlag(MouseButtons.Middle)); + } + + private void ctrlRenderer_MouseMove(object sender, MouseEventArgs e) + { + /*CursorManager.OnMouseMove(this); + + if(CursorManager.NeedMouseIcon) { + this.Cursor = Cursors.Cross; + } + */ + double xPos = (double)e.X / this.Width; + double yPos = (double)e.Y / this.Height; + + xPos = Math.Max(0.0, Math.Min(1.0, xPos)); + yPos = Math.Max(0.0, Math.Min(1.0, yPos)); + + EmuApi.SetMousePosition(xPos, yPos); + } + + private void ctrlRenderer_MouseLeave(object sender, EventArgs e) + { + //CursorManager.OnMouseLeave(); + EmuApi.SetMousePosition(-1, -1); + } + } +} diff --git a/UI/Controls/ctrlRenderer.designer.cs b/UI/Controls/ctrlRenderer.designer.cs new file mode 100644 index 0000000..e3b5be8 --- /dev/null +++ b/UI/Controls/ctrlRenderer.designer.cs @@ -0,0 +1,45 @@ +namespace Mesen.GUI.Controls +{ + partial class ctrlRenderer + { + /// + /// 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.SuspendLayout(); + // + // ctrlRenderer + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Name = "ctrlRenderer"; + this.MouseLeave += new System.EventHandler(this.ctrlRenderer_MouseLeave); + this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ctrlRenderer_MouseMove); + this.ResumeLayout(false); + + } + + #endregion + } +} diff --git a/UI/Controls/ctrlRenderer.resx b/UI/Controls/ctrlRenderer.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Controls/ctrlRenderer.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/Controls/ctrlRiskyOption.cs b/UI/Controls/ctrlRiskyOption.cs new file mode 100644 index 0000000..a94b485 --- /dev/null +++ b/UI/Controls/ctrlRiskyOption.cs @@ -0,0 +1,48 @@ +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.Forms; +using System.IO; +using Mesen.GUI.Controls; + +namespace Mesen.GUI.Controls +{ + public partial class ctrlRiskyOption : BaseControl + { + public ctrlRiskyOption() + { + InitializeComponent(); + this.lblNotRecommended.Text = ResourceHelper.GetMessage("RiskyOptionHint"); + this.lblNotRecommended.ForeColor = SystemColors.ControlDark; + } + + private void chkOption_CheckedChanged(object sender, EventArgs e) + { + this.lblNotRecommended.ForeColor = this.chkOption.Checked ? Color.Red : SystemColors.ControlDark; + } + + [Bindable(true)] + [Browsable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + [EditorBrowsable(EditorBrowsableState.Always)] + public override string Text + { + get { return chkOption.Text; } + set { chkOption.Text = value; } + } + + public bool Checked + { + get { return chkOption.Checked; } + set { chkOption.Checked = value; } + } + + protected override Padding DefaultMargin { get { return new Padding(0); } } + } +} diff --git a/UI/Controls/ctrlRiskyOption.designer.cs b/UI/Controls/ctrlRiskyOption.designer.cs new file mode 100644 index 0000000..e913bcf --- /dev/null +++ b/UI/Controls/ctrlRiskyOption.designer.cs @@ -0,0 +1,96 @@ +namespace Mesen.GUI.Controls +{ + partial class ctrlRiskyOption + { + /// + /// 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.chkOption = new System.Windows.Forms.CheckBox(); + this.lblNotRecommended = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // chkOption + // + this.chkOption.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkOption.AutoSize = true; + this.chkOption.Location = new System.Drawing.Point(3, 3); + this.chkOption.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); + this.chkOption.Name = "chkOption"; + this.chkOption.Size = new System.Drawing.Size(86, 15); + this.chkOption.TabIndex = 0; + this.chkOption.Text = "Option name"; + this.chkOption.UseVisualStyleBackColor = true; + this.chkOption.CheckedChanged += new System.EventHandler(this.chkOption_CheckedChanged); + // + // lblNotRecommended + // + this.lblNotRecommended.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblNotRecommended.AutoSize = true; + this.lblNotRecommended.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblNotRecommended.Location = new System.Drawing.Point(89, 3); + this.lblNotRecommended.Margin = new System.Windows.Forms.Padding(0, 0, 0, 2); + this.lblNotRecommended.Name = "lblNotRecommended"; + this.lblNotRecommended.Size = new System.Drawing.Size(98, 13); + this.lblNotRecommended.TabIndex = 1; + this.lblNotRecommended.Text = "(not recommended)"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + 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.lblNotRecommended, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.chkOption, 0, 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 = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 226F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(193, 21); + this.tableLayoutPanel1.TabIndex = 1; + // + // ctrlRiskyOption + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel1); + this.Name = "ctrlRiskyOption"; + this.Size = new System.Drawing.Size(193, 21); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.CheckBox chkOption; + private System.Windows.Forms.Label lblNotRecommended; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + } +} diff --git a/UI/Controls/ctrlRiskyOption.resx b/UI/Controls/ctrlRiskyOption.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Controls/ctrlRiskyOption.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/Controls/ctrlSplitContainer.cs b/UI/Controls/ctrlSplitContainer.cs new file mode 100644 index 0000000..40b260f --- /dev/null +++ b/UI/Controls/ctrlSplitContainer.cs @@ -0,0 +1,194 @@ +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.Controls +{ + class ctrlSplitContainer : SplitContainer + { + public event EventHandler PanelCollapsed; + public event EventHandler PanelExpanded; + private int _originalDistance = 0; + private int _originalMinSize = 0; + private bool _collapsed = false; + private bool _preventExpand = false; + private bool _hidePanel2 = false; + + public ctrlSplitContainer() + { + this.DoubleBuffered = true; + this.SplitterMoving += ctrlSplitContainer_SplitterMoving; + } + + public bool HidePanel2 + { + get { return _hidePanel2; } + set { _hidePanel2 = value; this.Invalidate(); } + } + + private void ctrlSplitContainer_SplitterMoving(object sender, SplitterCancelEventArgs e) + { + if(_originalMinSize > 0) { + e.Cancel = true; + this.BeginInvoke((MethodInvoker)(() => { + this.ExpandPanel(); + })); + } + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + if(_hidePanel2) { + return; + } + + if(this.Orientation == Orientation.Horizontal) { + int top = this.SplitterDistance + 1; + int center = this.Width / 2; + e.Graphics.DrawLine(Pens.DarkGray, center - 100, top, center + 100, top); + top+=2; + e.Graphics.DrawLine(Pens.DarkGray, center - 100, top, center + 100, top); + } else { + int center = this.Height / 2; + int left = this.SplitterDistance + 1; + e.Graphics.DrawLine(Pens.DarkGray, left, center - 100, left, center + 100); + left+=2; + e.Graphics.DrawLine(Pens.DarkGray, left, center - 100, left, center + 100); + } + } + + protected override void OnDoubleClick(EventArgs e) + { + base.OnDoubleClick(e); + + if(_hidePanel2) { + return; + } + + if(_originalMinSize == 0) { + this.CollapsePanel(); + _preventExpand = true; + } else { + this.ExpandPanel(); + } + } + + public void ExpandPanel() + { + if(_hidePanel2 || !_collapsed) { + return; + } + + if(this.FixedPanel == FixedPanel.Panel1) { + throw new Exception("Not implemented"); + } else if(this.FixedPanel == FixedPanel.Panel2) { + if(_originalMinSize > 0) { + this.Panel2MinSize = _originalMinSize; + this.SplitterDistance = _originalDistance; + _originalMinSize = 0; + } + this.PanelExpanded?.Invoke(this, EventArgs.Empty); + _collapsed = false; + } + } + + public void CollapsePanel() + { + if(_collapsed) { + return; + } + + if(this.FixedPanel == FixedPanel.Panel1) { + throw new Exception("Not implemented"); + } else if(this.FixedPanel == FixedPanel.Panel2) { + _collapsed = true; + _originalDistance = this.SplitterDistance; + _originalMinSize = this.Panel2MinSize; + this.Panel2MinSize = 2; + this.SplitterDistance = this.Orientation == Orientation.Horizontal ? this.Height : this.Width; + + this.PanelCollapsed?.Invoke(this, EventArgs.Empty); + } + } + + public int GetSplitterDistance() + { + return _originalMinSize > 0 ? _originalDistance : this.SplitterDistance; + } + + DateTime _lastResize = DateTime.MinValue; + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + + if(_hidePanel2) { + return; + } + + //Horrible patch to fix what looks like a resize bug in SplitContainers + //Resizing the window sometimes doesn't resize the inner panels properly + //causing their content to go partially offscreen. + //This code alters SplitterDistance approx 100ms after the last resize event + //to fix the display bug + if(this.IsHandleCreated) { + bool firstResize; + lock(this) { + firstResize = _lastResize == DateTime.MinValue; + _lastResize = DateTime.Now; + } + if(firstResize) { + Task.Run(() => { + while((DateTime.Now - _lastResize).Milliseconds < 100) { + System.Threading.Thread.Sleep(100); + } + + this.BeginInvoke((MethodInvoker)(() => { + if(_originalMinSize == 0) { + this.SuspendLayout(); + this.Panel1.SuspendLayout(); + this.Panel2.SuspendLayout(); + try { + if(this.Width > 0 && this.Height > 0) { + this.SplitterDistance++; + this.SplitterDistance--; + } + } catch { + } finally { + this.ResumeLayout(); + this.Panel1.ResumeLayout(); + this.Panel2.ResumeLayout(); + } + } + this.Invalidate(); + lock(this) { + _lastResize = DateTime.MinValue; + } + })); + }); + } + } + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + this.Panel1.Focus(); + + if(_hidePanel2) { + return; + } + + if(!_preventExpand && _originalMinSize > 0) { + this.ExpandPanel(); + } + + _preventExpand = false; + } + } +} diff --git a/UI/Controls/ctrlTrackbar.cs b/UI/Controls/ctrlTrackbar.cs new file mode 100644 index 0000000..a856494 --- /dev/null +++ b/UI/Controls/ctrlTrackbar.cs @@ -0,0 +1,77 @@ +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; + +namespace Mesen.GUI.Controls +{ + public partial class ctrlTrackbar : BaseControl + { + public event EventHandler ValueChanged + { + add { trackBar.ValueChanged += value; } + remove { trackBar.ValueChanged -= value; } + } + + public ctrlTrackbar() + { + InitializeComponent(); + + if(!Program.IsMono) { + this.trackBar.BackColor = System.Drawing.SystemColors.ControlLightLight; + } + } + + public int Minimum + { + get { return trackBar.Minimum; } + set { trackBar.Minimum = value; } + } + + public int Maximum + { + get { return trackBar.Maximum; } + set { trackBar.Maximum = value; } + } + + [Bindable(true)] + [Browsable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] + [EditorBrowsable(EditorBrowsableState.Always)] + public override string Text + { + get { return lblText.Text; } + set { lblText.Text = value; } + } + + public int Value + { + get { return trackBar.Value; } + set + { + trackBar.Value = value; + UpdateText(); + } + } + + private void UpdateText() + { + if(this.Minimum == 0) { + lblValue.Text = trackBar.Value.ToString() + "%"; + } else { + lblValue.Text = (trackBar.Value / 10.0).ToString() + "dB"; + lblValue.Font = new Font("Microsoft Sans Serif", 6.75F); + } + } + + private void trackBar_ValueChanged(object sender, EventArgs e) + { + UpdateText(); + } + } +} diff --git a/UI/Controls/ctrlTrackbar.designer.cs b/UI/Controls/ctrlTrackbar.designer.cs new file mode 100644 index 0000000..c734358 --- /dev/null +++ b/UI/Controls/ctrlTrackbar.designer.cs @@ -0,0 +1,122 @@ +namespace Mesen.GUI.Controls +{ + partial class ctrlTrackbar + { + /// + /// 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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.lblText = new System.Windows.Forms.Label(); + this.trackBar = new System.Windows.Forms.TrackBar(); + this.lblValue = new System.Windows.Forms.Label(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trackBar)).BeginInit(); + this.SuspendLayout(); + // + // 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.lblText, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.trackBar, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.lblValue, 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 = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(34, 160); + this.tableLayoutPanel1.TabIndex = 0; + // + // lblText + // + this.lblText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.lblText.AutoSize = true; + this.lblText.Font = new System.Drawing.Font("Microsoft Sans Serif", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblText.Location = new System.Drawing.Point(0, 148); + this.lblText.Margin = new System.Windows.Forms.Padding(0); + this.lblText.Name = "lblText"; + this.lblText.Size = new System.Drawing.Size(34, 12); + this.lblText.TabIndex = 14; + this.lblText.Text = "Text"; + this.lblText.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // trackBar + // + this.trackBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.trackBar.Location = new System.Drawing.Point(0, 0); + this.trackBar.Margin = new System.Windows.Forms.Padding(0); + this.trackBar.Maximum = 100; + this.trackBar.Name = "trackBar"; + this.trackBar.Orientation = System.Windows.Forms.Orientation.Vertical; + this.trackBar.Size = new System.Drawing.Size(34, 133); + this.trackBar.TabIndex = 13; + this.trackBar.TickFrequency = 10; + this.trackBar.Value = 50; + this.trackBar.ValueChanged += new System.EventHandler(this.trackBar_ValueChanged); + // + // lblValue + // + this.lblValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); + this.lblValue.AutoSize = true; + this.lblValue.BackColor = System.Drawing.Color.White; + this.lblValue.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.lblValue.Location = new System.Drawing.Point(3, 133); + this.lblValue.MinimumSize = new System.Drawing.Size(28, 15); + this.lblValue.Name = "lblValue"; + this.lblValue.Size = new System.Drawing.Size(28, 15); + this.lblValue.TabIndex = 15; + this.lblValue.Text = "100"; + this.lblValue.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // ctrlTrackbar + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.tableLayoutPanel1); + this.Margin = new System.Windows.Forms.Padding(0); + this.MaximumSize = new System.Drawing.Size(63, 160); + this.MinimumSize = new System.Drawing.Size(34, 160); + this.Name = "ctrlTrackbar"; + this.Size = new System.Drawing.Size(34, 160); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.trackBar)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label lblText; + private System.Windows.Forms.TrackBar trackBar; + private System.Windows.Forms.Label lblValue; + } +} diff --git a/UI/Controls/ctrlTrackbar.resx b/UI/Controls/ctrlTrackbar.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Controls/ctrlTrackbar.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/Controls/TextboxHistory.cs b/UI/Debugger/Controls/TextboxHistory.cs new file mode 100644 index 0000000..141f6a0 --- /dev/null +++ b/UI/Debugger/Controls/TextboxHistory.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Debugger +{ + class TextboxHistory + { + private List _historyList = new List() { 0 }; + private int _historyPosition = 0; + + private void ClearForwardHistory() + { + _historyList.RemoveRange(_historyPosition + 1, _historyList.Count - _historyPosition - 1); + } + + public void AddHistory(int lineIndex) + { + if(_historyList.Count - 1 > _historyPosition) { + ClearForwardHistory(); + } + + if(_historyList[_historyList.Count-1] != lineIndex) { + _historyList.Add(lineIndex); + _historyPosition++; + } + } + + public int GoBack() + { + if(_historyPosition > 0) { + _historyPosition--; + } + return _historyList[_historyPosition]; + } + + public int GoForward() + { + if(_historyPosition < _historyList.Count - 1) { + _historyPosition++; + } + return _historyList[_historyPosition]; + } + } +} diff --git a/UI/Debugger/Controls/ctrlCodeScrollbar.cs b/UI/Debugger/Controls/ctrlCodeScrollbar.cs new file mode 100644 index 0000000..62e9d30 --- /dev/null +++ b/UI/Debugger/Controls/ctrlCodeScrollbar.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger.Controls +{ + public class ctrlCodeScrollbar : Control + { + public event EventHandler ValueChanged; + + private Timer _tmrScroll; + private bool _scrollUp = false; + + private const int _buttonSize = 15; + + private IScrollbarColorProvider _colorProvider = null; + public IScrollbarColorProvider ColorProvider + { + get { return _colorProvider; } + set { _colorProvider = value; this.Invalidate(); } + } + + public ctrlCodeScrollbar() + { + this.DoubleBuffered = true; + this.ResizeRedraw = true; + this._tmrScroll = new Timer(); + this._tmrScroll.Tick += tmrScroll_Tick; + } + + private void tmrScroll_Tick(object sender, EventArgs e) + { + _tmrScroll.Interval = 50; + if(this._scrollUp) { + if(this.Value > 0) { + this.Value--; + } + } else { + if(this.Value < this.Maximum) { + this.Value++; + } + } + } + + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + + Rectangle rect = this.ClientRectangle; + + int left = rect.Left; + int width = rect.Width; + e.Graphics.FillRectangle(Brushes.DimGray, rect); + + int barTop = rect.Top + _buttonSize; + int barHeight = this.Height - _buttonSize * 2; + + float startPos = (float)this.Value / this.Maximum; + if(this.ColorProvider != null) { + Color prevBgColor = Color.White; + int drawHeight = 0; + for(int i = 0; i < barHeight; i++) { + float top = barTop + i; + float position = (float)i / barHeight; + Color bgColor = this.ColorProvider.GetBackgroundColor(position); + if(bgColor != prevBgColor && i > 0) { + using(Brush brush = new SolidBrush(prevBgColor)) { + e.Graphics.FillRectangle(brush, left + 1, top - drawHeight, width - 1, drawHeight); + drawHeight = 0; + } + } + drawHeight++; + + if(bgColor != Color.Transparent) { + prevBgColor = bgColor; + } + } + using(Brush brush = new SolidBrush(prevBgColor)) { + e.Graphics.FillRectangle(brush, left + 1, barTop + barHeight- drawHeight, width - 1, drawHeight); + } + } else { + e.Graphics.FillRectangle(Brushes.White, left + 1, barTop, width - 1, barHeight); + } + + float highlightTop = barTop + barHeight * startPos - HighlightOffset; + using(SolidBrush brush = new SolidBrush(Color.FromArgb(120, 220, 220, 255))) { + e.Graphics.FillRectangle(brush, left + 1, highlightTop, width, HighlightHeight - 2); + e.Graphics.DrawRectangle(Pens.DarkSlateGray, left + 1, highlightTop, width - 2, HighlightHeight - 2); + e.Graphics.DrawRectangle(Pens.Gray, left + 2, highlightTop + 1, width - 4, HighlightHeight - 4); + } + + if(this.ColorProvider != null) { + int selectedIndex = this.ColorProvider.GetSelectedLine(); + if(selectedIndex >= 0) { + int selectedTop = selectedIndex * barHeight / this.Maximum + barTop; + e.Graphics.FillRectangle(Brushes.LightBlue, left + 1, selectedTop - 1, width - 2, 3); + e.Graphics.DrawRectangle(Pens.DimGray, left + 1, selectedTop - 1, width - 2, 3); + } + + int activeIndex = this.ColorProvider.GetActiveLine(); + if(activeIndex >= 0) { + int activeTop = activeIndex * barHeight / this.Maximum + barTop; + e.Graphics.FillRectangle(Brushes.Yellow, left + 1, activeTop - 1, width - 2, 3); + e.Graphics.DrawRectangle(Pens.DimGray, left + 1, activeTop - 1, width - 2, 3); + } + + int linesPerPixel = (int)Math.Ceiling((float)this.Maximum / barHeight); + for(int i = 0; i < barHeight; i++) { + float top = barTop + i; + float position = (float)i / barHeight; + Color markerColor = this.ColorProvider.GetMarkerColor(position, linesPerPixel); + if(markerColor != Color.Transparent) { + using(Brush brush = new SolidBrush(markerColor)) { + e.Graphics.FillRectangle(brush, left + 3, top - 2, 5, 5); + } + } + } + } + + int arrowWidth = 10; + int arrowHeight = 5; + int bottom = barTop + barHeight + _buttonSize; + e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + e.Graphics.FillRectangle(Brushes.Gainsboro, rect.Left + 1, rect.Top, this.Width - 1, _buttonSize); + e.Graphics.FillRectangle(Brushes.Gainsboro, rect.Left + 1, bottom - _buttonSize, this.Width - 1, _buttonSize); + e.Graphics.DrawLine(Pens.DimGray, rect.Left + 1, rect.Top + _buttonSize, rect.Left + width, rect.Top + _buttonSize); + e.Graphics.DrawLine(Pens.DimGray, rect.Left + 1, bottom - _buttonSize, rect.Left + width, bottom - _buttonSize); + e.Graphics.DrawLine(Pens.DimGray, rect.Left + 1, bottom, rect.Left + width, bottom); + e.Graphics.TranslateTransform(5, (_buttonSize - arrowHeight) / 2); + e.Graphics.FillPolygon(Brushes.DimGray, new Point[] { new Point(left, rect.Top + arrowHeight), new Point(left + arrowWidth, rect.Top + arrowHeight), new Point(left + arrowWidth / 2, rect.Top) }, FillMode.Winding); + e.Graphics.TranslateTransform(0, -(_buttonSize - arrowHeight)); + e.Graphics.FillPolygon(Brushes.DimGray, new Point[] { new Point(left, bottom - arrowHeight), new Point(left + arrowWidth, bottom - arrowHeight), new Point(left + arrowWidth / 2, bottom) }, FillMode.Winding); + } + + //private bool _mouseDown = false; + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + + if(e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) { + this.UpdatePosition(e.Y); + //this._mouseDown = true; + } else { + if(e.Y <= _buttonSize) { + if(this.Value > 0) { + this._scrollUp = true; + this.Value--; + this._tmrScroll.Interval = 350; + this._tmrScroll.Start(); + } + } else { + if(this.Value < this.Maximum) { + this._scrollUp = false; + this.Value++; + this._tmrScroll.Interval = 350; + this._tmrScroll.Start(); + } + } + } + + //TODO + /*if(_codeTooltip != null) { + _codeTooltip.Close(); + _codeTooltip = null; + } + + _lastPreviewScrollPosition = -1;*/ + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + //this._mouseDown = false; + this._tmrScroll.Stop(); + } + + //TODO + /* + frmCodePreviewTooltip _codeTooltip = null; + int _lastPreviewScrollPosition = -1; + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + if(this._mouseDown) { + this.UpdatePosition(e.Y); + } else { + if(this.ColorProvider != null && e.Y > _buttonSize && e.Y < (this.Height - _buttonSize)) { + int scrollPosition = Math.Max(0, (e.Y - this.Top - _buttonSize) * this.Maximum / (this.Height - _buttonSize * 2)); + if(_lastPreviewScrollPosition != scrollPosition) { + Point p = this.PointToScreen(new Point(this.ClientRectangle.Right, e.Y)); + if(_codeTooltip == null) { + _codeTooltip = this.ColorProvider.GetPreview(scrollPosition); + if(_codeTooltip != null) { + _codeTooltip.FormClosed += (s, evt) => { _codeTooltip = null; }; + } + } else { + _codeTooltip.ScrollToLineIndex(scrollPosition); + } + if(_codeTooltip != null) { + _codeTooltip.SetFormLocation(new Point(p.X + 5, p.Y), this.Parent); + } + _lastPreviewScrollPosition = scrollPosition; + } + } + } + } + + + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + if(_codeTooltip != null) { + bool restoreFocus = _codeTooltip.NeedRestoreFocus; + _codeTooltip.Close(); + if(restoreFocus) { + this.Parent.Focus(); + } + _codeTooltip = null; + } + _lastPreviewScrollPosition = -1; + _tmrScroll.Stop(); + }*/ + + private int HighlightHeight { get { return Math.Max(8, (int)(((float)this.VisibleLineCount / this.Maximum) * this.Height)); } } + private int HighlightOffset + { + get + { + int highlightHeight = (int)(((float)this.VisibleLineCount / this.Maximum) * this.Height); + if(HighlightHeight - highlightHeight > 0) { + return (HighlightHeight - highlightHeight) / 2; + } else { + return 0; + } + } + } + + private void UpdatePosition(int y) + { + this.Value = Math.Max(0 , y - HighlightHeight / 2 + HighlightOffset - this.Top - _buttonSize) * this.Maximum / (this.Height - _buttonSize * 2); + } + + private int _value = 0; + public int Value + { + get + { + return this._value; + } + set + { + if(this._value != value) { + this._value = value; + this.ValueChanged?.Invoke(this, EventArgs.Empty); + + this.Invalidate(); + } + } + } + + private int _maximum = 1; + public int Maximum + { + get { return Math.Max(1, this._maximum); } + set { this._maximum = value; this.Invalidate(); } + } + + public int VisibleLineCount { get; set; } = 1; + public int LargeChange { get; set; } = 1; + } + + public interface IScrollbarColorProvider + { + Color GetBackgroundColor(float position); + Color GetMarkerColor(float position, int linesPerPixel); + int GetActiveLine(); + int GetSelectedLine(); + + //TODO + //frmCodePreviewTooltip GetPreview(int lineIndex); + } +} diff --git a/UI/Debugger/Controls/ctrlScrollableTextbox.cs b/UI/Debugger/Controls/ctrlScrollableTextbox.cs new file mode 100644 index 0000000..67d4f03 --- /dev/null +++ b/UI/Debugger/Controls/ctrlScrollableTextbox.cs @@ -0,0 +1,571 @@ +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.Controls; +using Mesen.GUI.Config; +using System.Globalization; + +namespace Mesen.GUI.Debugger.Controls +{ + public partial class ctrlScrollableTextbox : BaseControl + { + public event EventHandler ScrollPositionChanged; + + private bool _showScrollbars = true; + + public new event MouseEventHandler MouseUp + { + add { this.ctrlTextbox.MouseUp += value; } + remove { this.ctrlTextbox.MouseUp -= value; } + } + + public new event MouseEventHandler MouseMove + { + add { this.ctrlTextbox.MouseMove += value; } + remove { this.ctrlTextbox.MouseMove -= value; } + } + + public new event MouseEventHandler MouseDown + { + add { this.ctrlTextbox.MouseDown += value; } + remove { this.ctrlTextbox.MouseDown -= value; } + } + + public new event MouseEventHandler MouseDoubleClick + { + add { this.ctrlTextbox.MouseDoubleClick += value; } + remove { this.ctrlTextbox.MouseDoubleClick -= value; } + } + + public new event EventHandler MouseLeave + { + add { this.ctrlTextbox.MouseLeave += value; } + remove { this.ctrlTextbox.MouseLeave -= value; } + } + + public event EventHandler TextZoomChanged; + + public ctrlScrollableTextbox() + { + InitializeComponent(); + + bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime); + if(!designMode) { + this.panelSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left))); + this.panelSearch.Location = new System.Drawing.Point(this.Width - this.panelSearch.Width - 20, -1); + + this.ctrlTextbox.ShowLineNumbers = true; + this.ctrlTextbox.ShowLineInHex = true; + + this.hScrollBar.ValueChanged += hScrollBar_ValueChanged; + this.vScrollBar.ValueChanged += vScrollBar_ValueChanged; + this.ctrlTextbox.ScrollPositionChanged += ctrlTextbox_ScrollPositionChanged; + this.ctrlTextbox.SelectedLineChanged += ctrlTextbox_SelectedLineChanged; + + new ToolTip().SetToolTip(picCloseSearch, "Close"); + new ToolTip().SetToolTip(picSearchNext, "Find Next (F3)"); + new ToolTip().SetToolTip(picSearchPrevious, "Find Previous (Shift-F3)"); + } + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + this.panelSearch.Location = new System.Drawing.Point(this.Width - this.panelSearch.Width - 20, -1); + } + + public bool ShowScrollbars + { + get + { + return this._showScrollbars; + } + set + { + this._showScrollbars = value; + this.hScrollBar.Visible = value; + this.vScrollBar.Visible = value; + } + } + + public IScrollbarColorProvider ScrollbarColorProvider + { + set { this.vScrollBar.ColorProvider = value; } + } + + private void ctrlTextbox_SelectedLineChanged(object sender, EventArgs e) + { + this.vScrollBar.Invalidate(); + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Font BaseFont + { + get { return this.ctrlTextbox.BaseFont; } + set + { + this.ctrlTextbox.BaseFont = value; + UpdateHorizontalScrollbar(); + this.ctrlTextbox.Invalidate(); + } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int TextZoom + { + get { return this.ctrlTextbox.TextZoom; } + set { + if(this.ctrlTextbox.TextZoom != value) { + this.ctrlTextbox.TextZoom = value; + UpdateHorizontalScrollbar(); + if(this.TextZoomChanged != null) { + this.TextZoomChanged(this, null); + } + } + } + } + + public string GetWordUnderLocation(Point position) + { + return this.ctrlTextbox.GetWordUnderLocation(position); + } + + private void ctrlTextbox_ScrollPositionChanged(object sender, EventArgs e) + { + this.vScrollBar.Value = this.ctrlTextbox.ScrollPosition; + this.hScrollBar.Value = this.ctrlTextbox.HorizontalScrollPosition; + UpdateHorizontalScrollbar(); + UpdateVerticalScrollbar(); + + ScrollPositionChanged?.Invoke(null, null); + } + + private void UpdateVerticalScrollbar() + { + this.vScrollBar.Maximum = this.ctrlTextbox.LineCount; + this.vScrollBar.VisibleLineCount = this.ctrlTextbox.GetNumberVisibleLines(); + } + + private void UpdateHorizontalScrollbar() + { + this.hScrollBar.Visible = this.ctrlTextbox.HorizontalScrollWidth > 0 && _showScrollbars; + int newMax = this.ctrlTextbox.HorizontalScrollWidth + this.hScrollBar.LargeChange - 1; + if(this.hScrollBar.Value > this.ctrlTextbox.HorizontalScrollWidth) { + this.hScrollBar.Value = this.ctrlTextbox.HorizontalScrollWidth; + } + this.hScrollBar.Maximum = newMax; + } + + public ctrlTextbox.ILineStyleProvider StyleProvider { set { this.ctrlTextbox.StyleProvider = value; } } + + public int GetLineIndex(int lineNumber) + { + return this.ctrlTextbox.GetLineIndex(lineNumber); + } + + public int GetLineIndexAtPosition(int yPos) + { + return this.ctrlTextbox.GetLineIndexAtPosition(yPos); + } + + public string GetLineNoteAtLineIndex(int lineIndex) + { + if(lineIndex >= 0 && lineIndex < this.ctrlTextbox.LineNumberNotes.Length) { + return this.ctrlTextbox.LineNumberNotes[lineIndex]; + } else { + return ""; + } + } + + public int GetLineNumber(int lineIndex) + { + return this.ctrlTextbox.GetLineNumber(lineIndex); + } + + public int GetLineNumberAtPosition(int yPos) + { + return this.GetLineNumber(this.GetLineIndexAtPosition(yPos)); + } + + public int GetNumberVisibleLines() + { + return this.ctrlTextbox.GetNumberVisibleLines(); + } + + public void ScrollToLineIndex(int lineIndex, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) + { + this.ctrlTextbox.ScrollToLineIndex(lineIndex, historyType, scrollToTop, forceScroll); + } + + public void ScrollToLineNumber(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) + { + this.ctrlTextbox.ScrollToLineNumber(lineNumber, historyType, scrollToTop, forceScroll); + } + + public void CopySelection(bool copyLineNumbers, bool copyContentNotes, bool copyComments) + { + this.ctrlTextbox.CopySelection(copyLineNumbers, copyContentNotes, copyComments); + } + + public void SetMessage(TextboxMessageInfo message) + { + this.ctrlTextbox.SetMessage(message); + } + + public int CurrentLine + { + get { return this.ctrlTextbox.CurrentLine; } + } + + public int SelectedLine + { + get { return this.ctrlTextbox.SelectedLine; } + } + + public int LastSelectedLine + { + get { return this.ctrlTextbox.LastSelectedLine; } + } + + public int CodeMargin + { + get { return this.ctrlTextbox.CodeMargin; } + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + this.vScrollBar.Value = Math.Min(this.vScrollBar.Maximum, Math.Max(0, this.vScrollBar.Value - e.Delta / 40)); + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if(!this.cboSearch.Focused) { + //TODO + /*if(keyData == ConfigManager.Config.DebugInfo.Shortcuts.SelectAll) { + this.SelectAll(); + return true; + }*/ + + switch(keyData) { + case Keys.Right | Keys.Shift: + case Keys.Down | Keys.Shift: + this.ctrlTextbox.MoveSelectionDown(); + return true; + + case Keys.Down: + case Keys.Right: + this.ctrlTextbox.SelectionStart = this.ctrlTextbox.SelectedLine + 1; + this.ctrlTextbox.SelectionLength = 0; + return true; + + case Keys.Up | Keys.Shift: + case Keys.Left | Keys.Shift: + this.ctrlTextbox.MoveSelectionUp(); + return true; + + case Keys.Up: + case Keys.Left: + this.ctrlTextbox.SelectionStart = this.ctrlTextbox.SelectedLine - 1; + this.ctrlTextbox.SelectionLength = 0; + return true; + + case Keys.Home | Keys.Shift: + this.ctrlTextbox.MoveSelectionUp(this.ctrlTextbox.LineCount); + break; + + case Keys.End | Keys.Shift: + this.ctrlTextbox.MoveSelectionDown(this.ctrlTextbox.LineCount); + break; + + case Keys.Home: + this.ctrlTextbox.SelectionStart = 0; + this.ctrlTextbox.SelectionLength = 0; + return true; + + case Keys.End: + this.ctrlTextbox.SelectionStart = this.ctrlTextbox.LineCount - 1; + this.ctrlTextbox.SelectionLength = 0; + return true; + } + } + + //TODO + /* + if(keyData == ConfigManager.Config.DebugInfo.Shortcuts.IncreaseFontSize) { + this.TextZoom += 10; + return true; + } else if(keyData == ConfigManager.Config.DebugInfo.Shortcuts.DecreaseFontSize) { + this.TextZoom -= 10; + return true; + } else if(keyData == ConfigManager.Config.DebugInfo.Shortcuts.ResetFontSize) { + this.TextZoom = 100; + return true; + } else if(keyData == ConfigManager.Config.DebugInfo.Shortcuts.Find) { + this.OpenSearchBox(true); + return true; + }*/ + + switch(keyData) { + case Keys.PageUp | Keys.Shift: + this.ctrlTextbox.MoveSelectionUp(20); + return true; + + case Keys.PageUp: + this.ctrlTextbox.SelectionStart-=20; + this.ctrlTextbox.SelectionLength = 0; + return true; + + case Keys.PageDown | Keys.Shift: + this.ctrlTextbox.MoveSelectionDown(20); + return true; + + case Keys.PageDown: + this.ctrlTextbox.SelectionStart+=20; + this.ctrlTextbox.SelectionLength = 0; + return true; + + case Keys.Escape: + if(this.cboSearch.Focused) { + this.CloseSearchBox(); + return true; + } + break; + } + + return base.ProcessCmdKey(ref msg, keyData); + } + + public void SelectAll() + { + this.ctrlTextbox.SelectionStart = 0; + this.ctrlTextbox.SelectionLength = this.ctrlTextbox.LineCount; + } + + private void vScrollBar_ValueChanged(object sender, EventArgs e) + { + this.ctrlTextbox.ScrollPosition = this.vScrollBar.Value; + } + + private void hScrollBar_ValueChanged(object sender, EventArgs e) + { + this.ctrlTextbox.HorizontalScrollPosition = this.hScrollBar.Value; + } + + public string[] Addressing { set { this.ctrlTextbox.Addressing = value; } } + public string[] Comments { set { this.ctrlTextbox.Comments = value; } } + public int[] LineIndentations{ set { this.ctrlTextbox.LineIndentations = value; } } + + public string[] TextLines + { + set + { + this.ctrlTextbox.TextLines = value; + UpdateVerticalScrollbar(); + UpdateHorizontalScrollbar(); + } + } + + public string[] TextLineNotes + { + set + { + this.ctrlTextbox.TextLineNotes = value; + } + } + + public string[] CompareLines + { + set + { + this.ctrlTextbox.CompareLines = value; + } + } + + public int[] LineNumbers + { + set + { + this.ctrlTextbox.LineNumbers = value; + } + } + + public string[] LineNumberNotes + { + set + { + this.ctrlTextbox.LineNumberNotes = value; + } + } + + public bool ShowSingleContentLineNotes + { + get { return this.ctrlTextbox.ShowSingleContentLineNotes; } + set { this.ctrlTextbox.ShowSingleContentLineNotes = value; } + } + + public bool ShowContentNotes + { + get { return this.ctrlTextbox.ShowContentNotes; } + set { this.ctrlTextbox.ShowContentNotes = value; } + } + + public bool ShowCompactPrgAddresses { get { return this.ctrlTextbox.ShowCompactPrgAddresses; } set { this.ctrlTextbox.ShowCompactPrgAddresses = value; } } + + public bool ShowLineNumberNotes + { + get { return this.ctrlTextbox.ShowLineNumberNotes; } + set { this.ctrlTextbox.ShowLineNumberNotes = value; } + } + + public bool ShowSingleLineLineNumberNotes + { + get { return this.ctrlTextbox.ShowSingleLineLineNumberNotes; } + set { this.ctrlTextbox.ShowSingleLineLineNumberNotes = value; } + } + + public bool ShowMemoryValues + { + get { return this.ctrlTextbox.ShowMemoryValues; } + set { this.ctrlTextbox.ShowMemoryValues = value; } + } + + public bool HideSelection + { + get { return this.ctrlTextbox.HideSelection; } + set { this.ctrlTextbox.HideSelection = value; } + } + + public bool CodeHighlightingEnabled + { + get { return this.ctrlTextbox.CodeHighlightingEnabled; } + set { this.ctrlTextbox.CodeHighlightingEnabled = value; } + } + + public int LineCount { get { return this.ctrlTextbox.LineCount; } } + public int SelectionStart { get { return this.ctrlTextbox.SelectionStart; } } + public int SelectionLength { get { return this.ctrlTextbox.SelectionLength; } } + + public string Header + { + set + { + this.ctrlTextbox.Header = value; + } + } + + public int MarginWidth { set { this.ctrlTextbox.MarginWidth = value; } } + + public void OpenSearchBox(bool forceFocus = false) + { + bool focus = !this.panelSearch.Visible; + this.panelSearch.Visible = true; + if(focus || forceFocus) { + this.cboSearch.Focus(); + this.cboSearch.SelectAll(); + } + } + + private void CloseSearchBox() + { + this.ctrlTextbox.Search(null, false, false); + this.panelSearch.Visible = false; + this.Focus(); + } + + public void FindNext() + { + this.OpenSearchBox(); + this.ctrlTextbox.Search(this.cboSearch.Text, false, false); + } + + public void FindPrevious() + { + this.OpenSearchBox(); + this.ctrlTextbox.Search(this.cboSearch.Text, true, false); + } + + 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 void cboSearch_TextUpdate(object sender, EventArgs e) + { + if(!this.ctrlTextbox.Search(this.cboSearch.Text, false, true)) { + this.cboSearch.BackColor = Color.Coral; + } else { + this.cboSearch.BackColor = Color.Empty; + } + } + + 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; + } + } + + public string GetLineContent(int lineIndex) + { + return this.ctrlTextbox.GetFullWidthString(lineIndex); + } + + public void NavigateForward() + { + this.ctrlTextbox.NavigateForward(); + } + + public void NavigateBackward() + { + this.ctrlTextbox.NavigateBackward(); + } + + public bool GetNoteRangeAtLocation(int yPos, out int rangeStart, out int rangeEnd) + { + rangeStart = -1; + rangeEnd = -1; + int lineIndex = GetLineIndexAtPosition(yPos); + + while(lineIndex < LineCount - 2 && string.IsNullOrWhiteSpace(GetLineNoteAtLineIndex(lineIndex))) { + //Find the address of the next line with an address + lineIndex++; + } + + if(Int32.TryParse(GetLineNoteAtLineIndex(lineIndex), NumberStyles.AllowHexSpecifier, null, out rangeStart)) { + while(lineIndex < LineCount - 2 && string.IsNullOrWhiteSpace(GetLineNoteAtLineIndex(lineIndex + 1))) { + //Find the next line with an address + lineIndex++; + } + if(Int32.TryParse(GetLineNoteAtLineIndex(lineIndex + 1), NumberStyles.AllowHexSpecifier, null, out rangeEnd)) { + rangeEnd--; + return true; + } + } + return false; + } + } +} diff --git a/UI/Debugger/Controls/ctrlScrollableTextbox.designer.cs b/UI/Debugger/Controls/ctrlScrollableTextbox.designer.cs new file mode 100644 index 0000000..d3d2f78 --- /dev/null +++ b/UI/Debugger/Controls/ctrlScrollableTextbox.designer.cs @@ -0,0 +1,202 @@ +namespace Mesen.GUI.Debugger.Controls +{ + partial class ctrlScrollableTextbox + { + /// + /// 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.vScrollBar = new Mesen.GUI.Debugger.Controls.ctrlCodeScrollbar(); + this.panelSearch = new System.Windows.Forms.Panel(); + this.tableLayoutPanel1 = 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.hScrollBar = new System.Windows.Forms.HScrollBar(); + this.ctrlTextbox = new Mesen.GUI.Debugger.Controls.ctrlTextbox(); + this.panelSearch.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picCloseSearch)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchNext)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchPrevious)).BeginInit(); + this.SuspendLayout(); + // + // vScrollBar + // + this.vScrollBar.ColorProvider = null; + this.vScrollBar.Dock = System.Windows.Forms.DockStyle.Right; + this.vScrollBar.LargeChange = 20; + this.vScrollBar.Location = new System.Drawing.Point(490, 0); + this.vScrollBar.Maximum = 1; + this.vScrollBar.MaximumSize = new System.Drawing.Size(18, 0); + this.vScrollBar.MinimumSize = new System.Drawing.Size(18, 0); + this.vScrollBar.Name = "vScrollBar"; + this.vScrollBar.Size = new System.Drawing.Size(18, 141); + this.vScrollBar.TabIndex = 0; + this.vScrollBar.Value = 0; + this.vScrollBar.VisibleLineCount = 1; + // + // panelSearch + // + this.panelSearch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.panelSearch.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelSearch.Controls.Add(this.tableLayoutPanel1); + this.panelSearch.Location = new System.Drawing.Point(266, -1); + this.panelSearch.Name = "panelSearch"; + this.panelSearch.Size = new System.Drawing.Size(222, 28); + this.panelSearch.TabIndex = 2; + this.panelSearch.Visible = false; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 4; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.picCloseSearch, 3, 0); + this.tableLayoutPanel1.Controls.Add(this.picSearchNext, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.picSearchPrevious, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.cboSearch, 0, 0); + 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 = 1; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(220, 26); + this.tableLayoutPanel1.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(201, 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(179, 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(157, 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(148, 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); + // + // hScrollBar + // + this.hScrollBar.Dock = System.Windows.Forms.DockStyle.Bottom; + this.hScrollBar.Location = new System.Drawing.Point(0, 141); + this.hScrollBar.Name = "hScrollBar"; + this.hScrollBar.Size = new System.Drawing.Size(508, 18); + this.hScrollBar.TabIndex = 3; + // + // ctrlTextbox + // + this.ctrlTextbox.Dock = System.Windows.Forms.DockStyle.Fill; + this.ctrlTextbox.HideSelection = false; + this.ctrlTextbox.HorizontalScrollWidth = 0; + this.ctrlTextbox.Location = new System.Drawing.Point(0, 0); + this.ctrlTextbox.Name = "ctrlTextbox"; + this.ctrlTextbox.ShowCompactPrgAddresses = false; + this.ctrlTextbox.ShowContentNotes = false; + this.ctrlTextbox.ShowLineInHex = false; + this.ctrlTextbox.ShowLineNumberNotes = false; + this.ctrlTextbox.ShowLineNumbers = true; + this.ctrlTextbox.ShowMemoryValues = false; + this.ctrlTextbox.ShowSingleContentLineNotes = true; + this.ctrlTextbox.ShowSingleLineLineNumberNotes = false; + this.ctrlTextbox.Size = new System.Drawing.Size(490, 141); + this.ctrlTextbox.StyleProvider = null; + this.ctrlTextbox.TabIndex = 1; + // + // ctrlScrollableTextbox + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.Controls.Add(this.panelSearch); + this.Controls.Add(this.ctrlTextbox); + this.Controls.Add(this.vScrollBar); + this.Controls.Add(this.hScrollBar); + this.Name = "ctrlScrollableTextbox"; + this.Size = new System.Drawing.Size(508, 159); + this.panelSearch.ResumeLayout(false); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picCloseSearch)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchNext)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picSearchPrevious)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private ctrlCodeScrollbar vScrollBar; + private ctrlTextbox ctrlTextbox; + private System.Windows.Forms.Panel panelSearch; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.PictureBox picSearchPrevious; + private System.Windows.Forms.PictureBox picSearchNext; + private System.Windows.Forms.PictureBox picCloseSearch; + private System.Windows.Forms.ComboBox cboSearch; + private System.Windows.Forms.HScrollBar hScrollBar; + } +} diff --git a/UI/Debugger/Controls/ctrlScrollableTextbox.resx b/UI/Debugger/Controls/ctrlScrollableTextbox.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Debugger/Controls/ctrlScrollableTextbox.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/Controls/ctrlTextbox.Designer.cs b/UI/Debugger/Controls/ctrlTextbox.Designer.cs new file mode 100644 index 0000000..a337810 --- /dev/null +++ b/UI/Debugger/Controls/ctrlTextbox.Designer.cs @@ -0,0 +1,35 @@ +namespace Mesen.GUI.Debugger.Controls +{ + partial class ctrlTextbox + { + /// + /// 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() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/UI/Debugger/Controls/ctrlTextbox.cs b/UI/Debugger/Controls/ctrlTextbox.cs new file mode 100644 index 0000000..8a99f47 --- /dev/null +++ b/UI/Debugger/Controls/ctrlTextbox.cs @@ -0,0 +1,1459 @@ +using Mesen.GUI.Config; +using Mesen.GUI.Controls; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Debugger.Controls +{ + public partial class ctrlTextbox : Control + { + private Regex _codeRegex = new Regex("^(\\s*)([a-z]{3})([*]{0,1})($|[ ]){1}([(]{0,1})(([$][0-9a-f]*)|(#[@$:_0-9a-z]*)|([@_a-z]([@_a-z0-9])*){0,1}(\\+(\\d+)){0,1}){0,1}([)]{0,1})(,X|,Y){0,1}([)]{0,1})(.*)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + public event EventHandler ScrollPositionChanged; + public event EventHandler SelectedLineChanged; + private bool _disableScrollPositionChangedEvent; + + private const float HorizontalScrollFactor = 8; + private const int CommentSpacingCharCount = 25; + + private TextboxHistory _history = new TextboxHistory(); + + private string[] _contents = new string[0]; + private string[] _contentNotes = new string[0]; + private string[] _compareContents = null; + private int[] _lineNumbers = new int[0]; + private string[] _lineNumberNotes = new string[0]; + private Dictionary _lineNumberIndex = new Dictionary(); + private bool _showLineNumbers = false; + private bool _showLineInHex = false; + private bool _showLineNumberNotes = false; + private bool _showSingleLineLineNumberNotes = false; + private bool _showContentNotes = false; + private bool _showSingleLineContentNotes = true; + private bool _showCompactPrgAddresses = false; + private int _selectionStart = 0; + private int _selectionLength = 0; + private int _scrollPosition = 0; + private int _horizontalScrollPosition = 0; + private string _searchString = null; + private string _header = null; + private Font _noteFont = null; + private int _marginWidth = 9; + private int _extendedMarginWidth = 16; + private float _maxLineWidth = 0; + private int _maxLineWidthIndex = 0; + private TextboxMessageInfo _message; + + public ctrlTextbox() + { + InitializeComponent(); + this.ResizeRedraw = true; + this.DoubleBuffered = true; + this.UpdateFont(); + } + + public string[] Comments; + public string[] Addressing; + public int[] LineIndentations; + + public string[] TextLines + { + set + { + int maxLength = 0; + + _maxLineWidthIndex = 0; + + _contents = value; + for(int i = 0, len = value.Length; i < len; i++) { + int length = _contents[i].Length + (Addressing != null ? Addressing[i].Length : 0); + if(Comments?[i].Length > 0) { + length = Math.Max(length, length > 0 ? CommentSpacingCharCount : 0) + Comments[i].Length; + } + if(length > maxLength) { + maxLength = length; + _maxLineWidthIndex = i; + } + } + + UpdateHorizontalScrollWidth(); + + if(_lineNumbers.Length != _contents.Length) { + _lineNumbers = new int[_contents.Length]; + _lineNumberIndex.Clear(); + for(int i = _contents.Length - 1; i >=0; i--) { + _lineNumbers[i] = i; + _lineNumberIndex[i] = i; + } + } + this.Invalidate(); + } + } + + //Cache Font.Height value because accessing it is slow + private new int FontHeight { get; set; } + + private Font _baseFont = new Font(BaseControl.MonospaceFontFamily, BaseControl.DefaultFontSize); + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Font BaseFont + { + get { return _baseFont; } + set + { + _baseFont = value; + this.UpdateFont(); + this.Invalidate(); + } + } + + private Font _font = new Font(BaseControl.MonospaceFontFamily, BaseControl.DefaultFontSize); + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public override Font Font + { + get { return this._font; } + set { throw new Exception("Do not use"); } + } + + private int _textZoom = 100; + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int TextZoom + { + get { return this._textZoom; } + set + { + if(value != _textZoom && value >= 30 && value <= 500) { + this._textZoom = value; + UpdateFont(); + this.Invalidate(); + } + } + } + + private void UpdateFont() + { + _font = new Font(this.BaseFont.FontFamily, this.BaseFont.Size * this.TextZoom / 100f, this.BaseFont.Style); + _noteFont = new Font(this.BaseFont.FontFamily, this.BaseFont.Size * this.TextZoom * 0.75f / 100f); + FontHeight = this._font.Height; + UpdateHorizontalScrollWidth(); + } + + public bool ShowSingleContentLineNotes + { + get { return _showSingleLineContentNotes; } + set + { + _showSingleLineContentNotes = value; + this.Invalidate(); + } + } + + public bool ShowCompactPrgAddresses + { + get { return _showCompactPrgAddresses; } + set + { + _showCompactPrgAddresses = value; + this.Invalidate(); + } + } + + public bool ShowContentNotes + { + get { return _showContentNotes; } + set + { + _showContentNotes = value; + this.Invalidate(); + } + } + + public bool ShowSingleLineLineNumberNotes + { + get { return _showSingleLineLineNumberNotes; } + set + { + _showSingleLineLineNumberNotes = value; + this.Invalidate(); + } + } + + private bool _showMemoryValues = false; + public bool ShowMemoryValues + { + get { return _showMemoryValues; } + set + { + _showMemoryValues = value; + this.Invalidate(); + } + } + + private bool _hideSelection = false; + public bool HideSelection + { + get { return _hideSelection; } + set + { + _hideSelection = value; + this.Invalidate(); + } + } + + public bool ShowLineNumberNotes + { + get { return this._showLineNumberNotes; } + set + { + this._showLineNumberNotes = value; + this.Invalidate(); + } + } + + public string[] TextLineNotes + { + set + { + this._contentNotes = value; + this.Invalidate(); + } + } + + public string[] CompareLines + { + set + { + _compareContents = value; + } + } + + public int LineCount + { + get + { + return _contents.Length; + } + } + + public int[] LineNumbers + { + set + { + _lineNumbers = value; + _lineNumberIndex.Clear(); + int i = 0; + foreach(int line in _lineNumbers) { + _lineNumberIndex[line] = i; + i++; + } + this.Invalidate(); + } + } + + public string[] LineNumberNotes + { + set + { + _lineNumberNotes = value; + this.Invalidate(); + } + get + { + return _lineNumberNotes; + } + } + + public string Header + { + set + { + this._header = value; + this.Invalidate(); + } + } + + public int MarginWidth + { + set + { + this._marginWidth = value; + this.Invalidate(); + } + } + + public bool CodeHighlightingEnabled { get; set; } = true; + + public bool Search(string searchString, bool searchBackwards, bool isNewSearch) + { + if(string.IsNullOrWhiteSpace(searchString)) { + this._searchString = null; + this.Invalidate(); + return true; + } else { + int startPosition; + int endPosition; + + this._searchString = searchString.ToLowerInvariant(); + int searchOffset = (searchBackwards ? -1 : 1); + if(isNewSearch) { + startPosition = this.SelectionStart; + endPosition = this.SelectionStart - searchOffset; + if(endPosition < 0) { + endPosition = _contents.Length - 1; + } else if(endPosition >= _contents.Length) { + endPosition = 0; + } + + } else { + startPosition = this.SelectionStart + searchOffset; + endPosition = this.SelectionStart; + if(startPosition < 0) { + startPosition = _contents.Length - 1; + } else if(startPosition >= _contents.Length) { + startPosition = 0; + } + } + + for(int i = startPosition; i != endPosition; i += searchOffset) { + string line = _contents[i].ToLowerInvariant(); + if(line.Contains(this._searchString)) { + this.ScrollToLineIndex(i); + this.Invalidate(); + return true; + } + + //Continue search from start/end of document + if(!searchBackwards && i == this._contents.Length - 1) { + i = 0; + } else if(searchBackwards && i == 0) { + i = this._contents.Length - 1; + } + } + this.Invalidate(); + return _contents[_selectionStart].ToLowerInvariant().Contains(this._searchString); + } + } + + public interface ILineStyleProvider + { + LineProperties GetLineStyle(int cpuAddress, int lineIndex); + string GetLineComment(int lineIndex); + } + + private ILineStyleProvider _styleProvider; + public ILineStyleProvider StyleProvider + { + get { return _styleProvider; } + set + { + _styleProvider = value; + this.Invalidate(); + } + } + + public LineProperties GetLineStyle(int lineIndex) + { + if(StyleProvider != null) { + return StyleProvider.GetLineStyle(_lineNumbers[lineIndex], lineIndex); + } else { + return null; + } + } + + public int GetLineIndex(int lineNumber) + { + if(_lineNumberIndex.ContainsKey(lineNumber)) { + return _lineNumberIndex[lineNumber]; + } else { + foreach(int line in _lineNumbers) { + if(line > lineNumber) { + return Math.Max(0, GetLineIndex(line) - 1); + } + } + } + return -1; + } + + public int GetLineNumber(int lineIndex) + { + if(_lineNumbers.Length <= lineIndex) { + return 0; + } else { + return _lineNumbers[lineIndex]; + } + } + + public void ScrollToLineIndex(int lineIndex, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) + { + bool scrolled = false; + + //TODO + /*if(!scrollToTop && ConfigManager.Config.DebugInfo.AlwaysScrollToCenter) { + forceScroll = true; + }*/ + + if(forceScroll || lineIndex < this.ScrollPosition || lineIndex > this.GetLastVisibleLineIndex()) { + //Line isn't currently visible, scroll it to the middle of the viewport + if(scrollToTop) { + int scrollPos = lineIndex; + while(scrollPos > 0 && _lineNumbers[scrollPos - 1] < 0 && string.IsNullOrWhiteSpace(_lineNumberNotes[scrollPos - 1])) { + //Make sure any comment for the line is in scroll view + bool emptyLine = string.IsNullOrWhiteSpace(_contents[scrollPos]) && string.IsNullOrWhiteSpace(this.Comments[scrollPos]); + if(emptyLine) { + //If there's a blank line, stop scrolling up + scrollPos++; + break; + } + + scrollPos--; + if(emptyLine || _contents[scrollPos].StartsWith("--") || _contents[scrollPos].StartsWith("__")) { + //Reached the start of a block, stop going back up + break; + } + } + this.ScrollPosition = scrollPos; + } else { + this.ScrollPosition = lineIndex - this.GetNumberVisibleLines() / 2; + } + scrolled = true; + } + + if(this.SelectionStart != lineIndex) { + if(historyType == eHistoryType.Always || scrolled && historyType == eHistoryType.OnScroll) { + _history.AddHistory(this.SelectionStart); + } + this.SelectionStart = lineIndex; + this.SelectionLength = 0; + if(historyType == eHistoryType.Always || scrolled && historyType == eHistoryType.OnScroll) { + _history.AddHistory(this.SelectionStart); + } + } + } + + public void ScrollToLineNumber(int lineNumber, eHistoryType historyType = eHistoryType.Always, bool scrollToTop = false, bool forceScroll = false) + { + int lineIndex = this.GetLineIndex(lineNumber); + if(lineIndex >= 0) { + ScrollToLineIndex(lineIndex, historyType, scrollToTop, forceScroll); + } + } + + public int CodeMargin + { + get + { + using(Graphics g = Graphics.FromHwnd(this.Handle)) { + return this.GetMargin(g, false); + } + } + } + + private int GetMargin(Graphics g, bool getExtendedMargin) + { + int marginWidth = getExtendedMargin && this.ShowContentNotes && this.ShowSingleContentLineNotes ? _marginWidth + _extendedMarginWidth : _marginWidth; + if(ShowCompactPrgAddresses) { + marginWidth += 4; + } + return (this.ShowLineNumbers ? (int)(g.MeasureString("".PadLeft(marginWidth, 'W'), this.Font, int.MaxValue, StringFormat.GenericTypographic).Width) : 0) - 1; + } + + public int GetLineIndexAtPosition(int yPos) + { + int lineIndex = this.ScrollPosition + this.GetLineAtPosition(yPos); + if(lineIndex > _contents.Length && _contents.Length != 0) { + lineIndex = _contents.Length - 1; + } + return lineIndex; + } + + public string GetFullWidthString(int lineIndex) + { + string text = _contents[lineIndex] + Addressing?[lineIndex]; + if(Comments?[lineIndex].Length > 0) { + return text.PadRight(text.Length > 0 ? CommentSpacingCharCount : 0) + Comments[lineIndex]; + } + return text; + } + + private bool GetCharIndex(Point position, out int charIndex, out int lineIndex) + { + charIndex = int.MaxValue; + using(Graphics g = Graphics.FromHwnd(this.Handle)) { + int marginLeft = this.GetMargin(g, true); + lineIndex = this.GetLineIndexAtPosition(position.Y); + if(lineIndex >= _contents.Length) { + return false; + } + + int positionX = position.X - marginLeft; + positionX -= (LineIndentations != null ? LineIndentations[lineIndex] : 0); + if(positionX >= 0) { + float charWidth = g.MeasureString("W", this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; + charIndex = (int)(positionX / charWidth); + return true; + } + } + return false; + } + + char[] _wordDelimiters = new char[] { ' ', ',', '|', ';', '(', ')', '.', '-', ':', '<', '>', '#', '*', '/', '&', '[', ']', '~', '%' }; + public string GetWordUnderLocation(Point position) + { + int charIndex; + int lineIndex; + if(this.GetCharIndex(position, out charIndex, out lineIndex)) { + string text = this.GetFullWidthString(lineIndex); + if(charIndex < text.Length) { + if(_wordDelimiters.Contains(text[charIndex])) { + return string.Empty; + } else { + int endIndex = text.IndexOfAny(_wordDelimiters, charIndex); + if(endIndex == -1) { + endIndex = text.Length; + } + int startIndex = text.LastIndexOfAny(_wordDelimiters, charIndex); + + if(startIndex >= 0 && text[startIndex] == '#' && text.Length > startIndex && text[startIndex + 1] == '$') { + //Special case for immediate values. e.g: we want to show a tooltip for #MyLabel, but not for #$EF + return text.Substring(startIndex, endIndex - startIndex); + } else { + return text.Substring(startIndex + 1, endIndex - startIndex - 1); + } + } + } + } + return string.Empty; + } + + private int GetLineAtPosition(int yPos) + { + if(!string.IsNullOrWhiteSpace(this._header)) { + yPos -= this.LineHeight; + } + return Math.Max(0, yPos / this.LineHeight); + } + + private int GetLastVisibleLineIndex() + { + return this.ScrollPosition + this.GetNumberVisibleLines() - 1; + } + + public int GetNumberVisibleLines() + { + Rectangle rect = this.ClientRectangle; + return this.GetLineAtPosition(rect.Height); + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int SelectionStart + { + get { return Math.Min(this._contents.Length - 1, Math.Max(0, _selectionStart)); } + set + { + int selectionStart = Math.Max(0, Math.Min(this._contents.Length - 1, Math.Max(0, value))); + + _selectionStart = selectionStart; + + if(this.SelectionLength == 0) { + this.SelectedLine = this.SelectionStart; + } + + this.Invalidate(); + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int SelectionLength + { + get { return (this.SelectionStart + _selectionLength) > this._contents.Length - 1 ? this._contents.Length - this.SelectionStart - 1 : _selectionLength; } + set + { + _selectionLength = value; + + if(this.SelectionStart + _selectionLength > this._contents.Length - 1) { + _selectionLength = this._contents.Length - this.SelectionStart - 1; + } + + if(value == 0) { + this.SelectedLine = this.SelectionStart; + } + + this.Invalidate(); + } + } + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int SelectedLine + { + get { return this._selectedLine; } + set + { + this._selectedLine = value; + if(_selectedLine < this.ScrollPosition) { + this.ScrollPosition = _selectedLine; + } else if(_selectedLine > this.GetLastVisibleLineIndex()) { + this.ScrollPosition = _selectedLine - this.GetNumberVisibleLines() + 1; + } + this.SelectedLineChanged?.Invoke(this, EventArgs.Empty); + this.Invalidate(); + } + } + private int _selectedLine = 0; + + public void MoveSelectionDown(int lines = 1) + { + _disableScrollPositionChangedEvent = true; + while(lines > 0) { + bool singleLineSelection = this.SelectionLength == 0; + + if(singleLineSelection) { + if(this.SelectionStart + this.SelectionLength >= this._contents.Length - 1) { + //End of document reached + break; + } + this.SelectedLine = this.SelectionStart + 1; + this.SelectionLength++; + } else if(this.SelectionStart + this.SelectionLength == this.SelectedLine) { + if(this.SelectionStart + this.SelectionLength >= this._contents.Length - 1) { + //End of document reached + break; + } + this.SelectedLine++; + this.SelectionLength++; + } else { + this.SelectionStart++; + this.SelectedLine++; + this.SelectionLength--; + } + lines--; + } + _disableScrollPositionChangedEvent = false; + ScrollPositionChanged?.Invoke(this, null); + } + + public void MoveSelectionUp(int lines = 1) + { + _disableScrollPositionChangedEvent = true; + while(lines > 0) { + bool singleLineSelection = this.SelectionLength == 0; + + if(singleLineSelection) { + if(this.SelectionStart == 0) { + //Top of document reached + break; + } + this.SelectionStart--; + this.SelectedLine = this.SelectionStart; + this.SelectionLength++; + } else if(this.SelectionStart == this.SelectedLine) { + if(this.SelectionStart == 0) { + //Top of document reached + break; + } + this.SelectionStart--; + this.SelectedLine--; + this.SelectionLength++; + } else { + this.SelectedLine--; + this.SelectionLength--; + } + lines--; + } + _disableScrollPositionChangedEvent = false; + ScrollPositionChanged?.Invoke(this, null); + } + + public int CurrentLine + { + get { return _lineNumbers.Length > _selectionStart ? _lineNumbers[_selectionStart] : 0; } + } + + public int LastSelectedLine + { + get { return _lineNumbers.Length > _selectionStart + this.SelectionLength ? _lineNumbers[_selectionStart + this.SelectionLength] : 0; } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int ScrollPosition + { + get { return _scrollPosition; } + set + { + value = Math.Max(0, Math.Min(value, this._contents.Length-this.GetNumberVisibleLines())); + _scrollPosition = value; + if(!_disableScrollPositionChangedEvent && this.ScrollPositionChanged != null) { + ScrollPositionChanged(this, null); + } + + //Clear message if scroll position changes + this.SetMessage(null); + + this.Invalidate(); + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int HorizontalScrollPosition + { + get { return _horizontalScrollPosition; } + set + { + _horizontalScrollPosition = value; + if(!_disableScrollPositionChangedEvent && this.ScrollPositionChanged != null) { + ScrollPositionChanged(this, null); + } + this.Invalidate(); + } + } + + public int HorizontalScrollWidth { get; set; } = 0; + + private void UpdateHorizontalScrollWidth() + { + if(LineIndentations != null && LineIndentations.Length > _maxLineWidthIndex) { + using(Graphics g = this.CreateGraphics()) { + _maxLineWidth = (LineIndentations != null ? LineIndentations[_maxLineWidthIndex] : 0) + g.MeasureString(GetFullWidthString(_maxLineWidthIndex), this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; + HorizontalScrollWidth = (int)(Math.Max(0, HorizontalScrollFactor + _maxLineWidth - (this.Width - GetMargin(g, true))) / HorizontalScrollFactor); + } + } else { + _maxLineWidth = 0; + HorizontalScrollPosition = 0; + HorizontalScrollWidth = 0; + } + } + + public void SetMessage(TextboxMessageInfo message) + { + if(_message != message) { + _message = message; + Invalidate(); + } + } + + protected override void OnResize(EventArgs e) + { + base.OnResize(e); + this.ScrollPosition = this.ScrollPosition; + UpdateHorizontalScrollWidth(); + ScrollPositionChanged?.Invoke(this, e); + } + + public bool ShowLineNumbers + { + get { return _showLineNumbers; } + set { _showLineNumbers = value; } + } + + public bool ShowLineInHex + { + get { return _showLineInHex; } + set { _showLineInHex = value; } + } + + private int LineHeight + { + get + { + if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes || this.ShowContentNotes && !this.ShowSingleContentLineNotes) { + return (int)(this.FontHeight * 1.60); + } else { + return this.FontHeight - 1; + } + } + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + //Clear message if a key is pressed + this.SetMessage(null); + + return base.ProcessCmdKey(ref msg, keyData); + } + + int _clickedLine; + bool _mouseDragging; + protected override void OnMouseDown(MouseEventArgs e) + { + this.Focus(); + + //Clear message if a mouse button is clicked + this.SetMessage(null); + + if(e.Button == MouseButtons.XButton1) { + this.NavigateBackward(); + } else if(e.Button == MouseButtons.XButton2) { + this.NavigateForward(); + } else { + _clickedLine = this.ScrollPosition + this.GetLineAtPosition(e.Y); + + if(e.Button == MouseButtons.Right) { + if(_clickedLine >= this.SelectionStart && _clickedLine <= this.SelectionStart + this.SelectionLength) { + //Right-clicking on selection should not change it + return; + } + } + + if(Control.ModifierKeys.HasFlag(Keys.Shift)) { + if(_clickedLine > this.SelectedLine) { + MoveSelectionDown(_clickedLine - this.SelectedLine); + } else { + MoveSelectionUp(this.SelectedLine - _clickedLine); + } + } else { + _mouseDragging = true; + this.SelectedLine = _clickedLine; + this.SelectionStart = _clickedLine; + this.SelectionLength = 0; + } + } + base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + _mouseDragging = false; + base.OnMouseUp(e); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + if(_mouseDragging) { + int lineUnderMouse = this.ScrollPosition + this.GetLineAtPosition(e.Y); + this.SelectedLine = lineUnderMouse; + this.SelectedLine = lineUnderMouse; + if(lineUnderMouse > _clickedLine) { + this.SelectionLength = lineUnderMouse - _clickedLine; + } else { + this.SelectedLine = lineUnderMouse; + this.SelectionStart = lineUnderMouse; + this.SelectionLength = _clickedLine - lineUnderMouse; + } + } + base.OnMouseMove(e); + } + + public void CopySelection(bool copyLineNumbers, bool copyContentNotes, bool copyComments) + { + StringBuilder sb = new StringBuilder(); + for(int i = this.SelectionStart, end = this.SelectionStart + this.SelectionLength; i <= end; i++) { + string indent = ""; + if(LineIndentations != null) { + indent = "".PadLeft(LineIndentations[i] / 10); + } + + string codeString = _contents[i].Trim(); + if(codeString.StartsWith("__") || codeString.StartsWith("--")) { + codeString = "--------" + codeString.Substring(2, codeString.Length - 4) + "--------"; + } + + int padding = Math.Max(CommentSpacingCharCount, codeString.Length); + if(codeString.Length == 0) { + padding = 0; + } + + codeString = codeString.PadRight(padding); + + string line = indent + codeString; + if(copyContentNotes && _contentNotes[i].Length > 0) { + line = _contentNotes[i].PadRight(13) + line; + } + if(copyLineNumbers && _lineNumbers[i] >= 0) { + line = _lineNumbers[i].ToString("X4") + " " + line; + } + if(copyComments && !string.IsNullOrWhiteSpace(Comments[i])) { + line = line + Comments[i]; + } + sb.AppendLine(line); + } + Clipboard.SetText(sb.ToString()); + } + + public void NavigateForward() + { + this.ScrollToLineIndex(_history.GoForward(), eHistoryType.None); + } + + public void NavigateBackward() + { + this.ScrollToLineIndex(_history.GoBack(), eHistoryType.None); + } + + private void DrawLine(Graphics g, int currentLine, int marginLeft, int positionY, int lineHeight) + { + string codeString = _contents[currentLine]; + string addressString = this.Addressing?[currentLine]; + string commentString = this.Comments?[currentLine]; + + float codeStringLength = g.MeasureString(codeString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; + float addressStringLength = g.MeasureString(addressString, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; + + //Adjust background color highlights based on number of spaces in front of content + int originalMargin = marginLeft; + marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0); + + bool isBlockStart = codeString.StartsWith("__") && codeString.EndsWith("__"); + bool isBlockEnd = codeString.StartsWith("--") && codeString.EndsWith("--"); + + Color? textColor = null; + LineProperties lineProperties = GetLineStyle(currentLine); + + //Setup text and bg color (only if the line is not the start/end of a block) + if(lineProperties != null) { + //Process background, foreground, outline color and line symbol + textColor = lineProperties.FgColor; + + if(lineProperties.LineBgColor.HasValue) { + using(Brush bgBrush = new SolidBrush(lineProperties.LineBgColor.Value)) { + int yOffset = Program.IsMono ? 2 : 1; + if(isBlockStart) { + g.FillRectangle(bgBrush, originalMargin, positionY + yOffset + lineHeight / 2, Math.Max(_maxLineWidth + 10, this.ClientRectangle.Width - originalMargin), lineHeight / 2 + 1); + } else if(isBlockEnd) { + g.FillRectangle(bgBrush, originalMargin, positionY + yOffset, Math.Max(_maxLineWidth + 10, this.ClientRectangle.Width - originalMargin), lineHeight / 2 - 3); + } else { + g.FillRectangle(bgBrush, originalMargin, positionY + yOffset, Math.Max(_maxLineWidth + 10, this.ClientRectangle.Width - originalMargin), lineHeight); + } + } + } + } + + if(!this.HideSelection && currentLine >= this.SelectionStart && currentLine <= this.SelectionStart + this.SelectionLength) { + //Highlight current line + using(Brush brush = new SolidBrush(Color.FromArgb(150, 185, 210, 255))) { + int offset = currentLine - 1 == this.SelectedLine ? 1 : 0; + g.FillRectangle(brush, originalMargin, positionY + offset, Math.Max(_maxLineWidth, this.ClientRectangle.Width), lineHeight - offset); + } + if(currentLine == this.SelectedLine) { + g.DrawRectangle(Pens.Blue, originalMargin + 1, positionY + 1, Math.Max(_maxLineWidth, this.ClientRectangle.Width - originalMargin) - 1, lineHeight); + } + } + + if(lineProperties != null) { + if(!isBlockStart && !isBlockEnd && lineProperties.TextBgColor.HasValue) { + using(Brush bgBrush = new SolidBrush(lineProperties.TextBgColor.Value)) { + int yOffset = Program.IsMono ? 2 : 1; + g.FillRectangle(bgBrush, marginLeft, positionY + yOffset, codeStringLength, lineHeight - 1); + } + } + + if(!isBlockStart && !isBlockEnd && lineProperties.OutlineColor.HasValue) { + using(Pen outlinePen = new Pen(lineProperties.OutlineColor.Value, 1)) { + g.DrawRectangle(outlinePen, marginLeft, positionY + 1, codeStringLength, lineHeight - 1); + } + } + } + + this.DrawLineText(g, currentLine, marginLeft, positionY, codeString, addressString, commentString, codeStringLength, addressStringLength, textColor, lineHeight); + } + + private void DrawLineNumber(Graphics g, int currentLine, int marginLeft, int positionY, Color addressColor) + { + using(Brush numberBrush = new SolidBrush(addressColor)) { + if(this.ShowLineNumberNotes && this.ShowSingleLineLineNumberNotes) { + //Display line note instead of line number + string lineNumber; + if(string.IsNullOrEmpty(_lineNumberNotes[currentLine])) { + lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : ".."; + } else { + lineNumber = _lineNumberNotes[currentLine]; + } + float width = g.MeasureString(lineNumber, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; + g.DrawString(lineNumber, this.Font, numberBrush, marginLeft - width, positionY, StringFormat.GenericTypographic); + } else { + //Display line number + string lineNumber = _lineNumbers[currentLine] >= 0 ? _lineNumbers[currentLine].ToString(_showLineInHex ? "X4" : "") : ".."; + + if(ShowCompactPrgAddresses) { + string lineNumberNote = _lineNumberNotes[currentLine]; + if(lineNumberNote.Length > 3) { + string compactView = lineNumberNote.Substring(0, lineNumberNote.Length - 3).TrimStart('0'); + if(compactView.Length == 0) { + compactView = "0"; + } + lineNumber += " [" + compactView + "]"; + } + } + + float width = g.MeasureString(lineNumber, this.Font, int.MaxValue, StringFormat.GenericTypographic).Width; + g.DrawString(lineNumber, this.Font, numberBrush, marginLeft - width, positionY, StringFormat.GenericTypographic); + + if(this.ShowLineNumberNotes && !this.ShowSingleLineLineNumberNotes) { + //Display line note below line number + width = g.MeasureString(_lineNumberNotes[currentLine], _noteFont, int.MaxValue, StringFormat.GenericTypographic).Width; + g.DrawString(_lineNumberNotes[currentLine], _noteFont, numberBrush, marginLeft - width, positionY+this.Font.Size+3, StringFormat.GenericTypographic); + } + } + } + } + + private void DrawLineText(Graphics g, int currentLine, int marginLeft, int positionY, string codeString, string addressString, string commentString, float codeStringLength, float addressStringLength, Color? textColor, int lineHeight) + { + DebugInfo info = ConfigManager.Config.Debug; + + if(codeString.StartsWith("__") && codeString.EndsWith("__") || codeString.StartsWith("--") && codeString.EndsWith("--")) { + //Draw block start/end + g.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0); + string text = codeString.Substring(2, codeString.Length - 4); + float yOffset = codeString.StartsWith("__") ? 2 : -2; + if(text.Length > 0) { + SizeF size = g.MeasureString(text, this._noteFont, int.MaxValue, StringFormat.GenericTypographic); + float textLength = size.Width; + float textHeight = size.Height; + float positionX = (marginLeft + this.Width - textLength) / 2; + g.DrawLine(Pens.Black, marginLeft, yOffset + positionY + lineHeight / 2, marginLeft + this.Width, yOffset + positionY + lineHeight / 2); + yOffset = codeString.StartsWith("__") ? 3 : 2; + g.FillRectangle(Brushes.White, positionX - 4, yOffset + positionY, textLength + 8, textHeight); + g.DrawRectangle(Pens.Black, positionX - 4, yOffset + positionY, textLength + 8, textHeight); + g.DrawString(text, this._noteFont, Brushes.Black, positionX, yOffset + positionY, StringFormat.GenericTypographic); + } else { + g.DrawLine(Pens.Black, marginLeft, yOffset + positionY + lineHeight / 2, marginLeft + this.Width, yOffset + positionY + lineHeight / 2); + } + g.TranslateTransform(-HorizontalScrollPosition * HorizontalScrollFactor, 0); + } else { + if(StyleProvider != null) { + string symbolComment = StyleProvider.GetLineComment(currentLine); + if(symbolComment != null) { + symbolComment = symbolComment.Replace("\t", " "); + } + + if(symbolComment != _lastSymbolComment) { + commentString = symbolComment ?? commentString; + if(symbolComment != null) { + _lastSymbolComment = symbolComment; + } + } + } + + //Draw line content + int characterCount = 0; + Color defaultColor = Color.FromArgb(60, 60, 60); + if(codeString.Length > 0) { + Match match = CodeHighlightingEnabled ? _codeRegex.Match(codeString) : null; + if(match != null && match.Success && !codeString.EndsWith(":")) { + string padding = match.Groups[1].Value; + string opcode = match.Groups[2].Value; + string invalidStar = match.Groups[3].Value; + string paren1 = match.Groups[5].Value; + string operand = match.Groups[6].Value; + string arrayPosition = match.Groups[12].Value; + string paren2 = match.Groups[13].Value; + 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 }; + int codePartCount = colors.Count; + + List parts = new List() { padding, opcode, invalidStar, " ", paren1, operand, paren2, indirect, paren3 }; + string memoryAddress = ""; + if(!string.IsNullOrWhiteSpace(addressString)) { + colors.Add(info.CodeEffectiveAddressColor); + parts.Add(addressString); + memoryAddress = addressString.Substring(3).Trim(); + + //Update the contents of "arrayPosition" based on the address string, rather than use the one from the original address + int plusIndex = memoryAddress.IndexOf("+"); + arrayPosition = plusIndex >= 0 ? memoryAddress.Substring(plusIndex+1) : ""; + } else if(operand.Length > 0 && operand[0] != '#') { + memoryAddress = operand.Trim(); + } + + if(this.ShowMemoryValues && memoryAddress.Length > 0) { + int address = -1; + if(memoryAddress[0] == '$') { + Int32.TryParse(memoryAddress.Substring(1), System.Globalization.NumberStyles.AllowHexSpecifier, null, out address); + } else { + //Label + UInt32 arrayOffset = 0; + if(!string.IsNullOrWhiteSpace(arrayPosition)) { + memoryAddress = memoryAddress.Substring(0, memoryAddress.Length - arrayPosition.Length - 1); + arrayOffset = UInt32.Parse(arrayPosition); + } + + //TODO + /*CodeLabel label = LabelManager.GetLabel(memoryAddress); + if(label != null) { + address = InteropEmu.DebugGetRelativeAddress(label.Address + arrayOffset, label.AddressType); + }*/ + } + + //TODO + /* + if(address >= 0) { + colors.Add(defaultColor); + parts.Add(" = $" + InteropEmu.DebugGetMemoryValue(DebugMemoryType.CpuMemory, (UInt32)address).ToString("X2")); + } + */ + } + + //Display the rest of the line (used by trace logger) + colors.Add(defaultColor); + parts.Add(rest); + + float xOffset = 0; + for(int i = 0; i < parts.Count; i++) { + using(Brush b = new SolidBrush(textColor.HasValue && (i < codePartCount || i == parts.Count - 1) ? textColor.Value : colors[i])) { + g.DrawString(parts[i], this.Font, b, marginLeft + xOffset, positionY, StringFormat.GenericTypographic); + xOffset += g.MeasureString("".PadLeft(parts[i].Length, 'w'), this.Font, Point.Empty, StringFormat.GenericTypographic).Width; + characterCount += parts[i].Length; + } + } + codeStringLength = xOffset; + } else { + using(Brush fgBrush = new SolidBrush(codeString.EndsWith(":") ? (Color)info.AssemblerLabelDefinitionColor : (textColor ?? defaultColor))) { + g.DrawString(codeString, this.Font, fgBrush, marginLeft, positionY, StringFormat.GenericTypographic); + } + characterCount = codeString.Trim().Length; + } + } + + if(!string.IsNullOrWhiteSpace(commentString)) { + using(Brush commentBrush = new SolidBrush(info.AssemblerCommentColor)) { + 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 + padding = 0; + } + g.DrawString(commentString.PadLeft(padding+commentString.Length), this.Font, commentBrush, marginLeft, positionY, StringFormat.GenericTypographic); + } + } + + if(this.ShowContentNotes && !this.ShowSingleContentLineNotes) { + g.DrawString(_contentNotes[currentLine], _noteFont, Brushes.Gray, marginLeft, positionY + this.Font.Size+3, StringFormat.GenericTypographic); + } + this.DrawHighlightedSearchString(g, codeString, marginLeft, positionY); + this.DrawHighlightedCompareString(g, codeString, currentLine, marginLeft, positionY); + } + } + string _lastSymbolComment = null; + + private void DrawLineSymbols(Graphics g, int positionY, LineProperties lineProperties, int lineHeight) + { + int circleSize = lineHeight - 3; + if((circleSize % 2) == 1) { + circleSize++; + } + int circleOffsetY = positionY + 4; + int circleOffsetX = 3; + + Action drawPlus = (Brush b) => { + float barWidth = 2; + float centerPoint = circleSize / 2.0f - barWidth / 2.0f; + float barLength = circleSize - 6; + if((barLength % 2) == 1) { + barLength++; + } + float startOffset = (circleSize - barLength) / 2.0f; + + g.FillRectangle(b, circleOffsetX + startOffset, circleOffsetY + centerPoint, barLength, barWidth); + g.FillRectangle(b, circleOffsetX + centerPoint, circleOffsetY + startOffset, barWidth, barLength); + }; + + if(lineProperties.Symbol.HasFlag(LineSymbol.Circle)) { + using(Brush brush = new SolidBrush(lineProperties.OutlineColor.Value)) { + g.FillEllipse(brush, 3, circleOffsetY, circleSize, circleSize); + } + if(lineProperties.Symbol.HasFlag(LineSymbol.Plus)) { + drawPlus(Brushes.White); + } + } else if(lineProperties.Symbol.HasFlag(LineSymbol.CircleOutline) && lineProperties.OutlineColor.HasValue) { + using(Pen pen = new Pen(lineProperties.OutlineColor.Value, 1)) { + g.DrawEllipse(pen, 3, circleOffsetY, circleSize, circleSize); + if(lineProperties.Symbol.HasFlag(LineSymbol.Plus)) { + using(Brush b = new SolidBrush(lineProperties.OutlineColor.Value)) { + drawPlus(b); + } + } + } + } + + if(lineProperties.Symbol.HasFlag(LineSymbol.Mark)) { + using(Brush b = new SolidBrush(ConfigManager.Config.Debug.EventViewerBreakpointColor)) { + g.FillEllipse(b, circleOffsetX + circleSize * 3 / 4, positionY + 1, lineHeight / 2.0f, lineHeight / 2.0f); + } + g.DrawEllipse(Pens.Black, circleOffsetX + circleSize * 3 / 4, positionY + 1, lineHeight / 2.0f, lineHeight / 2.0f); + } + + if(lineProperties.Symbol.HasFlag(LineSymbol.Arrow)) { + if(Program.IsMono) { + int arrowY = positionY + lineHeight / 2 + 1; + using(Brush brush = new SolidBrush(lineProperties.TextBgColor.Value)) { + g.FillRectangle(brush, 1, arrowY - lineHeight * 0.25f / 2, lineHeight - 1, lineHeight * 0.35f); + } + g.DrawRectangle(Pens.Black, 1, arrowY - lineHeight * 0.25f / 2, lineHeight - 1, lineHeight * 0.35f); + } else { + float arrowY = circleOffsetY + circleSize / 2.0f; + g.TranslateTransform(2, 0); + using(Pen pen = new Pen(Color.Black, lineHeight * 0.33f)) { + //Outline + g.DrawLine(pen, 3, arrowY, 3 + lineHeight * 0.25f, arrowY); + pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; + g.DrawLine(pen, 3 + lineHeight * 0.25f, arrowY, 3 + lineHeight * 0.75f, arrowY); + + //Fill + pen.Width-=2f; + pen.Color = lineProperties.TextBgColor.Value; + pen.EndCap = System.Drawing.Drawing2D.LineCap.Square; + g.DrawLine(pen, 4, arrowY, 3 + lineHeight * 0.25f - 1, arrowY); + pen.EndCap = System.Drawing.Drawing2D.LineCap.ArrowAnchor; + g.DrawLine(pen, 3 + lineHeight * 0.25f, arrowY, lineHeight * 0.75f + 1, arrowY); + } + g.TranslateTransform(-2, 0); + } + } + } + + private void DrawLineProgress(Graphics g, int positionY, LineProgress progress, int lineHeight) + { + if(progress != null) { + int currentStep = progress.Current + 1; + int stepCount = Math.Max(progress.Maxixum, currentStep); + + string display = currentStep.ToString() + "/" + stepCount.ToString() + " " + progress.Text; + SizeF size = g.MeasureString(display, this._noteFont, int.MaxValue, StringFormat.GenericTypographic); + + float width = size.Width + 16; + float left = this.ClientSize.Width - 5 - width; + float height = size.Height; + float top = positionY + (lineHeight - height) / 2; + + g.FillRectangle(Brushes.White, left, top, width, height); + using(SolidBrush brush = new SolidBrush(progress.Color)) { + g.FillRectangle(brush, left, top, width * currentStep / stepCount, height); + } + g.DrawRectangle(Pens.Black, left, top, width, height); + + g.DrawString(display, this._noteFont, Brushes.Black, left + (width - size.Width) / 2, top, StringFormat.GenericTypographic); + } + } + + private void DrawHighlightedCompareString(Graphics g, string lineText, int currentLine, int marginLeft, int positionY) + { + if(_compareContents != null && _compareContents.Length > currentLine) { + string compareText = _compareContents[currentLine]; + + if(compareText != lineText) { + StringBuilder sb = new StringBuilder(); + for(int i = 0, len = lineText.Length; i < len; i++) { + if(lineText[i] == compareText[i]) { + sb.Append(" "); + } else { + sb.Append(lineText[i]); + } + } + + g.DrawString(sb.ToString(), new Font(this.Font, FontStyle.Bold), Brushes.Red, marginLeft, positionY, StringFormat.GenericTypographic); + } + } + } + + private void DrawHighlightedSearchString(Graphics g, string lineText, int marginLeft, int positionY) + { + int searchIndex; + if(!string.IsNullOrWhiteSpace(this._searchString) && (searchIndex = lineText.ToLowerInvariant().IndexOf(this._searchString)) >= 0) { + //Draw colored search string + string lowerCaseText = lineText.ToLowerInvariant(); + + Action draw = (bool forBackground) => { + int index = searchIndex; + do { + string padding = string.Empty.PadLeft(index, 'A'); + string highlightedText = lineText.Substring(index, this._searchString.Length); + index = lowerCaseText.IndexOf(this._searchString, index + this._searchString.Length); + + SizeF size = g.MeasureString(highlightedText, this.Font, int.MaxValue, StringFormat.GenericTypographic); + SizeF offsetSize = g.MeasureString(padding, this.Font, int.MaxValue, StringFormat.GenericTypographic); + if(forBackground) { + g.FillRectangle(Brushes.CornflowerBlue, marginLeft + offsetSize.Width, positionY + 1, size.Width + 1, size.Height - 2); + } else { + g.DrawString(highlightedText, this.Font, Brushes.White, marginLeft + offsetSize.Width + 1, positionY, StringFormat.GenericTypographic); + } + } while(index >= 0); + }; + + draw(true); + draw(false); + } + } + + private void DrawMargin(Graphics g, int currentLine, int marginLeft, int regularMargin, int positionY, int lineHeight) + { + LineProperties lineProperties = GetLineStyle(currentLine); + + //Draw instruction progress here to avoid it being scrolled horizontally when window is small (or comments/etc exist) + if(lineProperties?.Progress != null) { + this.DrawLineProgress(g, positionY, lineProperties?.Progress, lineHeight); + } else { + this.DrawSelectionLength(g, currentLine, positionY, lineHeight); + } + + if(this.ShowLineNumbers) { + //Show line number + Color lineNumberColor = lineProperties != null && lineProperties.AddressColor.HasValue ? lineProperties.AddressColor.Value : Color.Gray; + this.DrawLineNumber(g, currentLine, regularMargin, positionY, lineNumberColor); + } + if(this.ShowContentNotes && this.ShowSingleContentLineNotes) { + g.DrawString(_contentNotes[currentLine], this.Font, Brushes.Gray, regularMargin + 6, positionY, StringFormat.GenericTypographic); + } + + //Adjust background color highlights based on number of spaces in front of content + marginLeft += (LineIndentations != null ? LineIndentations[currentLine] : 0); + + if(lineProperties != null) { + this.DrawLineSymbols(g, positionY, lineProperties, lineHeight); + } + } + + private void DrawSelectionLength(Graphics g, int currentLine, int positionY, int lineHeight) + { + if(ConfigManager.Config.Debug.ShowSelectionLength && currentLine == this.SelectedLine && this.SelectionLength > 0) { + int startAddress = -1; + int endAddress = -1; + + int end = this.SelectionStart + this.SelectionLength + 1; + while(endAddress < 0 && end < _lineNumbers.Length) { + endAddress = _lineNumbers[end]; + end++; + } + + int start = this.SelectionStart; + while(startAddress < 0 && start < _lineNumbers.Length && start < end) { + startAddress = _lineNumbers[start]; + start++; + } + + if(startAddress >= 0 && endAddress > startAddress) { + string text = (endAddress - startAddress).ToString() + " bytes"; + SizeF textSize = g.MeasureString(text, this._noteFont); + PointF position = new PointF(this.ClientSize.Width - textSize.Width - 5, positionY + 3); + g.FillRectangle(Brushes.White, position.X, position.Y, textSize.Width, lineHeight - 4); + g.DrawRectangle(Pens.Black, position.X, position.Y, textSize.Width, lineHeight - 4); + g.DrawString(text, this._noteFont, Brushes.Black, position.X, position.Y - 1); + } + } + } + + private void DrawMessage(Graphics g) + { + if(this._message != null && !string.IsNullOrWhiteSpace(this._message.Message)) { + //Display message if one is active + SizeF textSize = g.MeasureString(this._message.Message, this.Font, int.MaxValue, StringFormat.GenericTypographic); + + using(SolidBrush brush = new SolidBrush(Color.FromArgb(255, 246, 168))) { + g.FillRectangle(brush, ClientRectangle.Width - textSize.Width - 10, ClientRectangle.Bottom - textSize.Height - 10, textSize.Width + 4, textSize.Height + 1); + } + g.DrawRectangle(Pens.Black, ClientRectangle.Width - textSize.Width - 10, ClientRectangle.Bottom - textSize.Height - 10, textSize.Width + 4, textSize.Height + 1); + g.DrawString(this._message.Message, this.Font, Brushes.Black, ClientRectangle.Width - textSize.Width - 8, ClientRectangle.Bottom - textSize.Height - 10, StringFormat.GenericTypographic); + } + } + + protected override void OnPaint(PaintEventArgs pe) + { + _lastSymbolComment = null; + int lineHeight = this.LineHeight; + pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + Rectangle rect = this.ClientRectangle; + pe.Graphics.FillRectangle(Brushes.White, rect); + + pe.Graphics.TranslateTransform(-HorizontalScrollPosition * HorizontalScrollFactor, 0); + + int marginLeft = this.GetMargin(pe.Graphics, true); + int regularMargin = this.GetMargin(pe.Graphics, false); + int currentLine = this.ScrollPosition; + int positionY = 0; + + if(!string.IsNullOrWhiteSpace(this._header)) { + using(Brush lightGrayBrush = new SolidBrush(Color.FromArgb(240, 240, 240))) { + pe.Graphics.FillRectangle(lightGrayBrush, marginLeft, 0, Math.Max(_maxLineWidth, rect.Right), lineHeight); + } + pe.Graphics.DrawString(_header, this.Font, Brushes.Gray, marginLeft, positionY, StringFormat.GenericTypographic); + positionY += lineHeight; + } + + while(positionY < rect.Bottom && currentLine < _contents.Length) { + this.DrawLine(pe.Graphics, currentLine, marginLeft, positionY, lineHeight); + positionY += lineHeight; + currentLine++; + } + + pe.Graphics.TranslateTransform(HorizontalScrollPosition * HorizontalScrollFactor, 0); + + if(this.ShowLineNumbers) { + using(Brush brush = new SolidBrush(Color.FromArgb(235, 235, 235))) { + pe.Graphics.FillRectangle(brush, 0, 0, regularMargin, rect.Bottom); + } + using(Brush brush = new SolidBrush(Color.FromArgb(251, 251, 251))) { + pe.Graphics.FillRectangle(brush, regularMargin, 0, marginLeft - regularMargin, rect.Bottom); + } + + using(Pen pen = new Pen(Color.LightGray)) { + pe.Graphics.DrawLine(pen, regularMargin, rect.Top, regularMargin, rect.Bottom); + pe.Graphics.DrawLine(pen, marginLeft, rect.Top, marginLeft, rect.Bottom); + } + } + + currentLine = this.ScrollPosition; + positionY = string.IsNullOrWhiteSpace(this._header) ? 0 : lineHeight; + while(positionY < rect.Bottom && currentLine < _contents.Length) { + this.DrawMargin(pe.Graphics, currentLine, marginLeft, regularMargin, positionY, lineHeight); + positionY += lineHeight; + currentLine++; + } + + this.DrawMessage(pe.Graphics); + } + } + + [Flags] + public enum LineSymbol + { + None = 0, + Circle = 1, + CircleOutline = 2, + Arrow = 4, + Mark = 8, + Plus = 16 + } + + public enum eHistoryType + { + Always, + OnScroll, + None + } + + public class LineProperties + { + public Color? LineBgColor; + public Color? TextBgColor; + public Color? FgColor; + public Color? OutlineColor; + public Color? AddressColor; + public LineSymbol Symbol; + + public LineProgress Progress; + } + + public class LineProgress + { + public int Current; + public int Maxixum; + public string Text; + public Color Color; + } + + public class TextboxMessageInfo + { + public string Message; + } +} diff --git a/UI/Debugger/FontDialogHelper.cs b/UI/Debugger/FontDialogHelper.cs new file mode 100644 index 0000000..eac564c --- /dev/null +++ b/UI/Debugger/FontDialogHelper.cs @@ -0,0 +1,42 @@ +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 +{ + class FontDialogHelper + { + public static Font SelectFont(Font currentFont) + { + using(FontDialog fd = new FontDialog()) { + fd.Font = currentFont; + fd.ShowApply = false; + fd.ShowColor = false; + fd.ShowEffects = false; + fd.ShowHelp = false; + if(fd.ShowDialog() == DialogResult.OK) { + Font font = fd.Font; + + Size sizeM = TextRenderer.MeasureText("M", font); + Size sizeDot = TextRenderer.MeasureText(".", font); + + if(sizeM != sizeDot) { + if(MessageBox.Show("The font you've selected (" + fd.Font.FontFamily.Name.ToString() + ") doesn't appear to be a monospace font. Using anything other than a monospace font will cause display issues in the UI. Are you sure you want to use this font?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) { + return fd.Font; + } else { + return currentFont; + } + } else { + return fd.Font; + } + } else { + return currentFont; + } + } + } + } +} diff --git a/UI/Debugger/frmTraceLogger.cs b/UI/Debugger/frmTraceLogger.cs new file mode 100644 index 0000000..5112312 --- /dev/null +++ b/UI/Debugger/frmTraceLogger.cs @@ -0,0 +1,483 @@ +using System; +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; +using System.Windows.Forms; +using Mesen.GUI.Config; +using Mesen.GUI.Controls; +using Mesen.GUI.Forms; + +namespace Mesen.GUI.Debugger +{ + public partial class frmTraceLogger : BaseForm + { + private int _lineCount; + private bool _loggingEnabled = false; + private string _lastFilename; + private EntityBinder _entityBinder = new EntityBinder(); + private string _previousTrace; + private volatile bool _refreshRunning; + private bool _initialized; + + public frmTraceLogger() + { + InitializeComponent(); + + DebugInfo debugInfo = ConfigManager.Config.Debug; + if(!debugInfo.TraceLoggerSize.IsEmpty) { + this.StartPosition = FormStartPosition.Manual; + this.Size = debugInfo.TraceLoggerSize; + this.Location = debugInfo.TraceLoggerLocation; + } + + txtTraceLog.BaseFont = new Font(debugInfo.TraceFontFamily, debugInfo.TraceFontSize, debugInfo.TraceFontStyle); + txtTraceLog.TextZoom = debugInfo.TraceTextZoom; + + mnuAutoRefresh.Checked = debugInfo.TraceAutoRefresh; + _lineCount = debugInfo.TraceLineCount; + + _entityBinder.Entity = debugInfo.TraceLoggerOptions; + _entityBinder.AddBinding("ShowByteCode", chkShowByteCode); + _entityBinder.AddBinding("ShowCpuCycles", chkShowCpuCycles); + _entityBinder.AddBinding("ShowEffectiveAddresses", chkShowEffectiveAddresses); + _entityBinder.AddBinding("ShowMemoryValues", chkShowMemoryValues); + _entityBinder.AddBinding("ShowExtraInfo", chkShowExtraInfo); + _entityBinder.AddBinding("ShowPpuFrames", chkShowFrameCount); + _entityBinder.AddBinding("ShowPpuCycles", chkShowPpuCycles); + _entityBinder.AddBinding("ShowPpuScanline", chkShowPpuScanline); + _entityBinder.AddBinding("ShowRegisters", chkShowRegisters); + _entityBinder.AddBinding("IndentCode", chkIndentCode); + _entityBinder.AddBinding("UseLabels", chkUseLabels); + _entityBinder.AddBinding("ExtendZeroPage", chkExtendZeroPage); + _entityBinder.AddBinding("UseWindowsEol", chkUseWindowsEol); + _entityBinder.AddBinding("StatusFormat", cboStatusFlagFormat); + _entityBinder.AddBinding("OverrideFormat", chkOverrideFormat); + _entityBinder.UpdateUI(); + + this.toolTip.SetToolTip(this.picExpressionWarning, "Condition contains invalid syntax or symbols."); + //this.toolTip.SetToolTip(this.picHelp, "When a condition is given, instructions will only be logged by the trace logger if the condition returns a value not equal to 0 or false." + Environment.NewLine + Environment.NewLine + frmBreakpoint.GetConditionTooltip(false)); + this.toolTip.SetToolTip(this.picFormatHelp, + "You can customize the trace logger's output by enabling the 'Override' option and altering the format." + Environment.NewLine + Environment.NewLine + + "The following tags are available: " + Environment.NewLine + + "[ByteCode]: The byte code for the instruction (1 to 3 bytes)." + Environment.NewLine + + "[Disassembly]: The disassembly for the current instruction." + Environment.NewLine + + "[EffectiveAddress]: The effective address used for indirect addressing modes." + Environment.NewLine + + "[MemoryValue]: The value stored at the memory location referred to by the instruction." + Environment.NewLine + + "[PC]: Program Counter" + Environment.NewLine + + "[A]: A register" + Environment.NewLine + + "[X]: X register" + Environment.NewLine + + "[Y]: Y register" + Environment.NewLine + + "[SP]: Stack Pointer" + Environment.NewLine + + "[P]: Processor Flags" + Environment.NewLine + + "[Cycle]: The current PPU cycle." + Environment.NewLine + + "[Scanline]: The current PPU scanline." + Environment.NewLine + + "[FrameCount]: The current PPU frame." + Environment.NewLine + + "[CycleCount]: The current CPU cycle (32-bit signed value, resets to 0 at power on)" + Environment.NewLine + Environment.NewLine + + "You can also specify some options by using a comma. e.g:" + Environment.NewLine + + "[Cycle,3] will display the cycle and pad out the output to always be 3 characters wide." + Environment.NewLine + + "[Scanline,h] will display the scanline in hexadecimal." + Environment.NewLine + + "[Align,50]: Align is a special tag that is useful when trying to align some content. [Align,50] will make the next tag start on column 50." + ); + + this._initialized = true; + } + + private void InitShortcuts() + { + //TODO + /*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));*/ + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + UpdateMenu(); + tmrUpdateLog.Start(); + RefreshLog(true, true); + + InitShortcuts(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + tmrUpdateLog.Stop(); + while(_refreshRunning) { + System.Threading.Thread.Sleep(50); + } + + 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; + + debugInfo.TraceFontFamily = txtTraceLog.BaseFont.FontFamily.Name; + debugInfo.TraceFontSize = txtTraceLog.BaseFont.Size; + debugInfo.TraceFontStyle = txtTraceLog.BaseFont.Style; + debugInfo.TraceTextZoom = txtTraceLog.TextZoom; + + _entityBinder.UpdateObject(); + + ConfigManager.ApplyChanges(); + + if(_loggingEnabled) { + DebugApi.StopTraceLogger(); + } + } + + protected void UpdateFormatOptions() + { + if(!chkOverrideFormat.Checked) { + string format = "[PC,6h] "; + if(chkShowByteCode.Checked) { + format += "[ByteCode,15h] "; + } + format += "[Disassembly]"; + int alignValue = 40; + if(chkShowEffectiveAddresses.Checked) { + format += "[EffectiveAddress]"; + alignValue += 8; + } + if(chkShowMemoryValues.Checked) { + format += " [MemoryValue,h]"; + alignValue += 6; + } + format += "[Align," + alignValue.ToString() + "] "; + + if(chkShowRegisters.Checked) { + format += "A:[A,h] X:[X,h] Y:[Y,h] "; + switch(cboStatusFlagFormat.GetEnumValue()) { + case StatusFlagFormat.Hexadecimal: format += "P:[P,h]"; break; + case StatusFlagFormat.CompactText: format += "P:[P]"; break; + case StatusFlagFormat.Text: format += "P:[P,8]"; break; + } + format += " SP:[SP,h] "; + } + if(chkShowPpuCycles.Checked) { + format += "CYC:[Cycle,3] "; + } + if(chkShowPpuScanline.Checked) { + format += "SL:[Scanline,3] "; + } + if(chkShowFrameCount.Checked) { + format += "FC:[FrameCount] "; + } + if(chkShowCpuCycles.Checked) { + format += "CPU Cycle:[CycleCount]"; + } + txtFormat.Text = format.Trim(); + } + + txtFormat.ReadOnly = !chkOverrideFormat.Checked; + chkShowByteCode.Enabled = !chkOverrideFormat.Checked; + chkShowRegisters.Enabled = !chkOverrideFormat.Checked; + chkShowPpuCycles.Enabled = !chkOverrideFormat.Checked; + chkShowPpuScanline.Enabled = !chkOverrideFormat.Checked; + chkShowFrameCount.Enabled = !chkOverrideFormat.Checked; + chkShowCpuCycles.Enabled = !chkOverrideFormat.Checked; + chkShowEffectiveAddresses.Enabled = !chkOverrideFormat.Checked; + chkShowMemoryValues.Enabled = !chkOverrideFormat.Checked; + cboStatusFlagFormat.Enabled = !chkOverrideFormat.Checked; + + if(_initialized) { + RefreshLog(false, true); + } + } + + private void SetOptions() + { + _entityBinder.UpdateObject(); + TraceLoggerOptions options = (TraceLoggerOptions)_entityBinder.Entity; + + InteropTraceLoggerOptions interopOptions = new InteropTraceLoggerOptions(); + interopOptions.IndentCode = options.IndentCode; + interopOptions.ShowExtraInfo = options.ShowExtraInfo; + interopOptions.UseLabels = options.UseLabels; + interopOptions.UseWindowsEol = options.UseWindowsEol; + interopOptions.ExtendZeroPage = options.ExtendZeroPage; + + interopOptions.Condition = Encoding.UTF8.GetBytes(txtCondition.Text); + Array.Resize(ref interopOptions.Condition, 1000); + + interopOptions.Format = Encoding.UTF8.GetBytes(txtFormat.Text.Replace("\t", "\\t")); + Array.Resize(ref interopOptions.Format, 1000); + + DebugApi.SetTraceOptions(interopOptions); + } + + private void btnStartLogging_Click(object sender, EventArgs e) + { + using(SaveFileDialog sfd = new SaveFileDialog()) { + sfd.SetFilter("Trace logs (*.txt)|*.txt"); + sfd.FileName = "Trace.txt"; + sfd.InitialDirectory = ConfigManager.DebuggerFolder; + if(sfd.ShowDialog() == DialogResult.OK) { + _lastFilename = sfd.FileName; + SetOptions(); + DebugApi.StartTraceLogger(sfd.FileName); + + btnStartLogging.Enabled = false; + btnStopLogging.Enabled = true; + btnOpenTrace.Enabled = false; + + _loggingEnabled = true; + } + } + } + + private void btnStopLogging_Click(object sender, EventArgs e) + { + DebugApi.StopTraceLogger(); + btnStartLogging.Enabled = true; + btnStopLogging.Enabled = false; + btnOpenTrace.Enabled = true; + } + + private void btnOpenTrace_Click(object sender, EventArgs e) + { + try { + System.Diagnostics.Process.Start(_lastFilename); + } catch { } + } + + private void RefreshLog(bool scrollToBottom, bool forceUpdate) + { + if(_refreshRunning) { + return; + } + + //TODO + //Make sure labels are up to date + //DebugWorkspaceManager.GetWorkspace(); + + _refreshRunning = true; + SetOptions(); + Task.Run(() => { + //Update trace log in another thread for performance + //DebugState state = new DebugState(); + //InteropEmu.DebugGetState(ref state); + //if(_previousCycleCount != state.CPU.CycleCount || forceUpdate) { + //TODO + if(true) { + string newTrace = DebugApi.GetExecutionTrace((UInt32)_lineCount); + //_previousCycleCount = state.CPU.CycleCount; + _previousTrace = newTrace; + + int index = 0; + string line = null; + Func readLine = () => { + if(index < newTrace.Length) { + int endOfLineIndex = newTrace.IndexOf('\n', index); + line = newTrace.Substring(index, endOfLineIndex - index); + index = endOfLineIndex + 1; + return true; + } else { + return false; + } + }; + + List programCounter = new List(30000); + List byteCode = new List(30000); + List lineContent = new List(30000); + List indent = new List(30000); + + bool showByteCode = false; + while(readLine()) { + string[] parts = line.Split('\x1'); + programCounter.Add(Int32.Parse(parts[0], System.Globalization.NumberStyles.HexNumber)); + byteCode.Add(parts[1]); + + string content = parts[2]; + while(true) { + string str = content.TrimStart(); + if(str.StartsWith(parts[0])) { + content = str.Substring(parts[0].Length); + } else if(str.StartsWith(parts[1])) { + content = str.Substring(parts[1].Length); + showByteCode = true; + } else if(str.StartsWith(parts[1].Replace("$", ""))) { + content = str.Substring(8); + showByteCode = true; + } else { + break; + } + } + lineContent.Add(content.TrimStart()); + indent.Add(10); + } + this.BeginInvoke((Action)(() => { + txtTraceLog.ShowContentNotes = showByteCode; + txtTraceLog.ShowSingleContentLineNotes = showByteCode; + + txtTraceLog.LineIndentations = indent.ToArray(); + txtTraceLog.LineNumbers = programCounter.ToArray(); + txtTraceLog.TextLineNotes = byteCode.ToArray(); + txtTraceLog.TextLines = lineContent.ToArray(); + + if(scrollToBottom) { + txtTraceLog.ScrollToLineIndex(txtTraceLog.LineCount - 1); + } + })); + } + _refreshRunning = false; + }); + } + + private void UpdateMenu() + { + mnu30000Lines.Checked = _lineCount == 30000; + mnu15000Lines.Checked = _lineCount == 15000; + mnu10000Lines.Checked = _lineCount == 10000; + mnu5000Lines.Checked = _lineCount == 5000; + mnu1000Lines.Checked = _lineCount == 1000; + mnu100Lines.Checked = _lineCount == 100; + + if(_lineCount >= 1000) { + tmrUpdateLog.Interval = 250; + } else { + tmrUpdateLog.Interval = 150; + } + } + + private void tmrUpdateLog_Tick(object sender, EventArgs e) + { + if(txtCondition.Text.Length > 0) { + //TODO + /*EvalResultType resultType; + InteropEmu.DebugEvaluateExpression(txtCondition.Text, out resultType, false); + picExpressionWarning.Visible = (resultType == EvalResultType.Invalid);*/ + } else { + picExpressionWarning.Visible = false; + } + + if(mnuAutoRefresh.Checked) { + RefreshLog(false, false); + } + } + + private void SetLineCount(int count) + { + _lineCount = count; + UpdateMenu(); + RefreshLog(false, true); + } + + private void mnu30000Lines_Click(object sender, EventArgs e) + { + SetLineCount(30000); + } + + private void mnu15000Lines_Click(object sender, EventArgs e) + { + SetLineCount(15000); + } + + private void mnu10000Lines_Click(object sender, EventArgs e) + { + SetLineCount(10000); + } + + private void mnu5000Lines_Click(object sender, EventArgs e) + { + SetLineCount(5000); + } + + private void mnu1000Lines_Click(object sender, EventArgs e) + { + SetLineCount(1000); + } + + private void mnu100Lines_Click(object sender, EventArgs e) + { + SetLineCount(100); + } + + private void mnuRefresh_Click(object sender, EventArgs e) + { + RefreshLog(false, true); + } + + private void mnuIncreaseFontSize_Click(object sender, EventArgs e) + { + txtTraceLog.TextZoom += 10; + } + + private void mnuDecreaseFontSize_Click(object sender, EventArgs e) + { + txtTraceLog.TextZoom -= 10; + } + + private void mnuResetFontSize_Click(object sender, EventArgs e) + { + txtTraceLog.TextZoom = 100; + } + + private void mnuSelectFont_Click(object sender, EventArgs e) + { + txtTraceLog.BaseFont = FontDialogHelper.SelectFont(txtTraceLog.BaseFont); + } + + private void chkOptions_CheckedChanged(object sender, EventArgs e) + { + UpdateFormatOptions(); + } + + private void cboStatusFlagFormat_SelectedIndexChanged(object sender, EventArgs e) + { + UpdateFormatOptions(); + } + + private void txtFormat_TextChanged(object sender, EventArgs e) + { + if(chkOverrideFormat.Checked) { + //Only save format string when override flag is set + ((TraceLoggerOptions)_entityBinder.Entity).Format = txtFormat.Text; + } + } + + private void chkOverrideFormat_CheckedChanged(object sender, EventArgs e) + { + if(chkOverrideFormat.Checked) { + string format = ((TraceLoggerOptions)_entityBinder.Entity).Format; + if(string.IsNullOrWhiteSpace(format)) { + format = txtFormat.Text; + } + txtFormat.Text = format; + } + UpdateFormatOptions(); + } + + private void mnuCopy_Click(object sender, EventArgs e) + { + string[] lines = _previousTrace.Split('\n'); + StringBuilder sb = new StringBuilder(); + for(int i = txtTraceLog.SelectionStart, end = txtTraceLog.SelectionStart + txtTraceLog.SelectionLength; i <= end && i < lines.Length; i++) { + sb.Append(lines[i]); + } + Clipboard.SetText(sb.ToString()); + + txtTraceLog.CopySelection(true, true, false); + } + + private void mnuSelectAll_Click(object sender, EventArgs e) + { + txtTraceLog.SelectAll(); + } + } +} diff --git a/UI/Debugger/frmTraceLogger.designer.cs b/UI/Debugger/frmTraceLogger.designer.cs new file mode 100644 index 0000000..6c35290 --- /dev/null +++ b/UI/Debugger/frmTraceLogger.designer.cs @@ -0,0 +1,842 @@ +using Mesen.GUI.Debugger.Controls; + +namespace Mesen.GUI.Debugger +{ + partial class frmTraceLogger + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.btnOpenTrace = new System.Windows.Forms.Button(); + this.btnStartLogging = new System.Windows.Forms.Button(); + this.btnStopLogging = new System.Windows.Forms.Button(); + this.grpLogOptions = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); + this.chkOverrideFormat = new System.Windows.Forms.CheckBox(); + this.picFormatHelp = new System.Windows.Forms.PictureBox(); + this.lblFormat = new System.Windows.Forms.Label(); + this.txtFormat = new System.Windows.Forms.TextBox(); + this.chkShowMemoryValues = new System.Windows.Forms.CheckBox(); + this.chkShowRegisters = new System.Windows.Forms.CheckBox(); + this.chkShowByteCode = new System.Windows.Forms.CheckBox(); + this.chkShowCpuCycles = new System.Windows.Forms.CheckBox(); + this.chkShowPpuCycles = new System.Windows.Forms.CheckBox(); + this.chkShowPpuScanline = new System.Windows.Forms.CheckBox(); + this.chkShowFrameCount = new System.Windows.Forms.CheckBox(); + this.chkShowEffectiveAddresses = new System.Windows.Forms.CheckBox(); + this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); + this.picHelp = new System.Windows.Forms.PictureBox(); + this.picExpressionWarning = new System.Windows.Forms.PictureBox(); + this.lblCondition = new System.Windows.Forms.Label(); + this.txtCondition = new System.Windows.Forms.TextBox(); + this.chkShowExtraInfo = new System.Windows.Forms.CheckBox(); + this.chkIndentCode = new System.Windows.Forms.CheckBox(); + this.chkUseLabels = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.cboStatusFlagFormat = new System.Windows.Forms.ComboBox(); + this.chkUseWindowsEol = new System.Windows.Forms.CheckBox(); + this.chkExtendZeroPage = new System.Windows.Forms.CheckBox(); + this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); + this.grpExecutionLog = new System.Windows.Forms.GroupBox(); + this.txtTraceLog = new Mesen.GUI.Debugger.Controls.ctrlScrollableTextbox(); + this.ctxMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.mnuCopy = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuSelectAll = new System.Windows.Forms.ToolStripMenuItem(); + this.tmrUpdateLog = new System.Windows.Forms.Timer(this.components); + this.menuStrip1 = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); + this.showToolStripMenuItem = 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.logLinesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu100Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu1000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu5000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu10000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu15000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnu30000Lines = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuAutoRefresh = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuRefresh = new System.Windows.Forms.ToolStripMenuItem(); + this.tableLayoutPanel1.SuspendLayout(); + this.grpLogOptions.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.tableLayoutPanel5.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picFormatHelp)).BeginInit(); + this.tableLayoutPanel4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picHelp)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.picExpressionWarning)).BeginInit(); + this.tableLayoutPanel3.SuspendLayout(); + this.grpExecutionLog.SuspendLayout(); + this.ctxMenu.SuspendLayout(); + this.menuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + 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.btnOpenTrace, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.btnStartLogging, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.btnStopLogging, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.grpLogOptions, 0, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 200); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(778, 182); + this.tableLayoutPanel1.TabIndex = 0; + // + // btnOpenTrace + // + this.btnOpenTrace.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnOpenTrace.Enabled = false; + this.btnOpenTrace.Location = new System.Drawing.Point(680, 3); + this.btnOpenTrace.Name = "btnOpenTrace"; + this.btnOpenTrace.Size = new System.Drawing.Size(95, 23); + this.btnOpenTrace.TabIndex = 2; + this.btnOpenTrace.Text = "Open Trace File"; + this.btnOpenTrace.UseVisualStyleBackColor = true; + this.btnOpenTrace.Click += new System.EventHandler(this.btnOpenTrace_Click); + // + // btnStartLogging + // + this.btnStartLogging.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.btnStartLogging.Location = new System.Drawing.Point(3, 3); + this.btnStartLogging.Name = "btnStartLogging"; + this.btnStartLogging.Size = new System.Drawing.Size(95, 23); + this.btnStartLogging.TabIndex = 0; + this.btnStartLogging.Text = "Start Logging"; + this.btnStartLogging.UseVisualStyleBackColor = true; + this.btnStartLogging.Click += new System.EventHandler(this.btnStartLogging_Click); + // + // btnStopLogging + // + this.btnStopLogging.Enabled = false; + this.btnStopLogging.Location = new System.Drawing.Point(104, 3); + this.btnStopLogging.Name = "btnStopLogging"; + this.btnStopLogging.Size = new System.Drawing.Size(95, 23); + this.btnStopLogging.TabIndex = 1; + this.btnStopLogging.Text = "Stop Logging"; + this.btnStopLogging.UseVisualStyleBackColor = true; + this.btnStopLogging.Click += new System.EventHandler(this.btnStopLogging_Click); + // + // grpLogOptions + // + this.tableLayoutPanel1.SetColumnSpan(this.grpLogOptions, 3); + this.grpLogOptions.Controls.Add(this.tableLayoutPanel2); + this.grpLogOptions.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpLogOptions.Location = new System.Drawing.Point(3, 32); + this.grpLogOptions.Name = "grpLogOptions"; + this.grpLogOptions.Size = new System.Drawing.Size(772, 150); + this.grpLogOptions.TabIndex = 3; + this.grpLogOptions.TabStop = false; + this.grpLogOptions.Text = "Log Options"; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.ColumnCount = 7; + 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()); + 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.tableLayoutPanel5, 0, 4); + this.tableLayoutPanel2.Controls.Add(this.chkShowMemoryValues, 3, 1); + this.tableLayoutPanel2.Controls.Add(this.chkShowRegisters, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.chkShowByteCode, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.chkShowCpuCycles, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.chkShowPpuCycles, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.chkShowPpuScanline, 2, 1); + this.tableLayoutPanel2.Controls.Add(this.chkShowFrameCount, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.chkShowEffectiveAddresses, 3, 0); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel4, 0, 5); + this.tableLayoutPanel2.Controls.Add(this.chkShowExtraInfo, 4, 0); + this.tableLayoutPanel2.Controls.Add(this.chkIndentCode, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.chkUseLabels, 2, 2); + this.tableLayoutPanel2.Controls.Add(this.label1, 4, 2); + this.tableLayoutPanel2.Controls.Add(this.cboStatusFlagFormat, 5, 2); + this.tableLayoutPanel2.Controls.Add(this.chkUseWindowsEol, 3, 2); + this.tableLayoutPanel2.Controls.Add(this.chkExtendZeroPage, 4, 1); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 8; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5F)); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + 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.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.Size = new System.Drawing.Size(766, 131); + this.tableLayoutPanel2.TabIndex = 0; + // + // tableLayoutPanel5 + // + this.tableLayoutPanel5.ColumnCount = 5; + this.tableLayoutPanel2.SetColumnSpan(this.tableLayoutPanel5, 7); + this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel5.Controls.Add(this.chkOverrideFormat, 0, 0); + this.tableLayoutPanel5.Controls.Add(this.picFormatHelp, 4, 0); + this.tableLayoutPanel5.Controls.Add(this.lblFormat, 0, 0); + this.tableLayoutPanel5.Controls.Add(this.txtFormat, 2, 0); + this.tableLayoutPanel5.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel5.Location = new System.Drawing.Point(0, 78); + this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel5.Name = "tableLayoutPanel5"; + this.tableLayoutPanel5.RowCount = 1; + this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel5.Size = new System.Drawing.Size(766, 25); + this.tableLayoutPanel5.TabIndex = 18; + // + // chkOverrideFormat + // + this.chkOverrideFormat.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkOverrideFormat.AutoSize = true; + this.chkOverrideFormat.Location = new System.Drawing.Point(51, 5); + this.chkOverrideFormat.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); + this.chkOverrideFormat.Name = "chkOverrideFormat"; + this.chkOverrideFormat.Size = new System.Drawing.Size(66, 17); + this.chkOverrideFormat.TabIndex = 18; + this.chkOverrideFormat.Text = "Override"; + this.chkOverrideFormat.UseVisualStyleBackColor = true; + this.chkOverrideFormat.CheckedChanged += new System.EventHandler(this.chkOverrideFormat_CheckedChanged); + // + // picFormatHelp + // + this.picFormatHelp.Image = global::Mesen.GUI.Properties.Resources.Help; + this.picFormatHelp.Location = new System.Drawing.Point(745, 5); + this.picFormatHelp.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); + this.picFormatHelp.Name = "picFormatHelp"; + this.picFormatHelp.Size = new System.Drawing.Size(18, 17); + this.picFormatHelp.TabIndex = 17; + this.picFormatHelp.TabStop = false; + // + // lblFormat + // + this.lblFormat.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblFormat.AutoSize = true; + this.lblFormat.Location = new System.Drawing.Point(3, 6); + this.lblFormat.Name = "lblFormat"; + this.lblFormat.Size = new System.Drawing.Size(42, 13); + this.lblFormat.TabIndex = 14; + this.lblFormat.Text = "Format:"; + // + // txtFormat + // + this.txtFormat.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtFormat.Location = new System.Drawing.Point(123, 3); + this.txtFormat.Name = "txtFormat"; + this.txtFormat.Size = new System.Drawing.Size(616, 20); + this.txtFormat.TabIndex = 15; + this.txtFormat.TextChanged += new System.EventHandler(this.txtFormat_TextChanged); + // + // chkShowMemoryValues + // + this.chkShowMemoryValues.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkShowMemoryValues.AutoSize = true; + this.chkShowMemoryValues.Checked = true; + this.chkShowMemoryValues.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowMemoryValues.Location = new System.Drawing.Point(301, 26); + this.chkShowMemoryValues.Name = "chkShowMemoryValues"; + this.chkShowMemoryValues.Size = new System.Drawing.Size(128, 17); + this.chkShowMemoryValues.TabIndex = 17; + this.chkShowMemoryValues.Text = "Show Memory Values"; + this.chkShowMemoryValues.UseVisualStyleBackColor = true; + this.chkShowMemoryValues.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkShowRegisters + // + this.chkShowRegisters.AutoSize = true; + this.chkShowRegisters.Checked = true; + this.chkShowRegisters.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowRegisters.Location = new System.Drawing.Point(3, 3); + this.chkShowRegisters.Name = "chkShowRegisters"; + this.chkShowRegisters.Size = new System.Drawing.Size(70, 17); + this.chkShowRegisters.TabIndex = 2; + this.chkShowRegisters.Text = "Registers"; + this.chkShowRegisters.UseVisualStyleBackColor = true; + this.chkShowRegisters.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkShowByteCode + // + this.chkShowByteCode.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkShowByteCode.AutoSize = true; + this.chkShowByteCode.Checked = true; + this.chkShowByteCode.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowByteCode.Location = new System.Drawing.Point(3, 26); + this.chkShowByteCode.Name = "chkShowByteCode"; + this.chkShowByteCode.Size = new System.Drawing.Size(75, 17); + this.chkShowByteCode.TabIndex = 4; + this.chkShowByteCode.Text = "Byte Code"; + this.chkShowByteCode.UseVisualStyleBackColor = true; + this.chkShowByteCode.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkShowCpuCycles + // + this.chkShowCpuCycles.AutoSize = true; + this.chkShowCpuCycles.Checked = true; + this.chkShowCpuCycles.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowCpuCycles.Location = new System.Drawing.Point(84, 3); + this.chkShowCpuCycles.Name = "chkShowCpuCycles"; + this.chkShowCpuCycles.Size = new System.Drawing.Size(82, 17); + this.chkShowCpuCycles.TabIndex = 3; + this.chkShowCpuCycles.Text = "CPU Cycles"; + this.chkShowCpuCycles.UseVisualStyleBackColor = true; + this.chkShowCpuCycles.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkShowPpuCycles + // + this.chkShowPpuCycles.AutoSize = true; + this.chkShowPpuCycles.Checked = true; + this.chkShowPpuCycles.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowPpuCycles.Location = new System.Drawing.Point(203, 3); + this.chkShowPpuCycles.Name = "chkShowPpuCycles"; + this.chkShowPpuCycles.Size = new System.Drawing.Size(77, 17); + this.chkShowPpuCycles.TabIndex = 5; + this.chkShowPpuCycles.Text = "PPU Cycle"; + this.chkShowPpuCycles.UseVisualStyleBackColor = true; + this.chkShowPpuCycles.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkShowPpuScanline + // + this.chkShowPpuScanline.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkShowPpuScanline.AutoSize = true; + this.chkShowPpuScanline.Checked = true; + this.chkShowPpuScanline.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowPpuScanline.Location = new System.Drawing.Point(203, 26); + this.chkShowPpuScanline.Name = "chkShowPpuScanline"; + this.chkShowPpuScanline.Size = new System.Drawing.Size(92, 17); + this.chkShowPpuScanline.TabIndex = 6; + this.chkShowPpuScanline.Text = "PPU Scanline"; + this.chkShowPpuScanline.UseVisualStyleBackColor = true; + this.chkShowPpuScanline.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkShowFrameCount + // + this.chkShowFrameCount.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkShowFrameCount.AutoSize = true; + this.chkShowFrameCount.Location = new System.Drawing.Point(84, 26); + this.chkShowFrameCount.Name = "chkShowFrameCount"; + this.chkShowFrameCount.Size = new System.Drawing.Size(86, 17); + this.chkShowFrameCount.TabIndex = 7; + this.chkShowFrameCount.Text = "Frame Count"; + this.chkShowFrameCount.UseVisualStyleBackColor = true; + this.chkShowFrameCount.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkShowEffectiveAddresses + // + this.chkShowEffectiveAddresses.AutoSize = true; + this.chkShowEffectiveAddresses.Checked = true; + this.chkShowEffectiveAddresses.CheckState = System.Windows.Forms.CheckState.Checked; + this.chkShowEffectiveAddresses.Location = new System.Drawing.Point(301, 3); + this.chkShowEffectiveAddresses.Name = "chkShowEffectiveAddresses"; + this.chkShowEffectiveAddresses.Size = new System.Drawing.Size(150, 17); + this.chkShowEffectiveAddresses.TabIndex = 10; + this.chkShowEffectiveAddresses.Text = "Show Effective Addresses"; + this.chkShowEffectiveAddresses.UseVisualStyleBackColor = true; + this.chkShowEffectiveAddresses.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // tableLayoutPanel4 + // + this.tableLayoutPanel4.ColumnCount = 4; + this.tableLayoutPanel2.SetColumnSpan(this.tableLayoutPanel4, 7); + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel4.Controls.Add(this.picHelp, 3, 0); + this.tableLayoutPanel4.Controls.Add(this.picExpressionWarning, 2, 0); + this.tableLayoutPanel4.Controls.Add(this.lblCondition, 0, 0); + this.tableLayoutPanel4.Controls.Add(this.txtCondition, 1, 0); + this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 103); + this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel4.Name = "tableLayoutPanel4"; + this.tableLayoutPanel4.RowCount = 1; + this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel4.Size = new System.Drawing.Size(766, 25); + this.tableLayoutPanel4.TabIndex = 16; + // + // picHelp + // + this.picHelp.Image = global::Mesen.GUI.Properties.Resources.Help; + this.picHelp.Location = new System.Drawing.Point(745, 5); + this.picHelp.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); + this.picHelp.Name = "picHelp"; + this.picHelp.Size = new System.Drawing.Size(18, 17); + this.picHelp.TabIndex = 17; + this.picHelp.TabStop = false; + // + // picExpressionWarning + // + this.picExpressionWarning.Image = global::Mesen.GUI.Properties.Resources.Warning; + this.picExpressionWarning.Location = new System.Drawing.Point(721, 5); + this.picExpressionWarning.Margin = new System.Windows.Forms.Padding(3, 5, 3, 3); + this.picExpressionWarning.Name = "picExpressionWarning"; + this.picExpressionWarning.Size = new System.Drawing.Size(18, 17); + this.picExpressionWarning.TabIndex = 16; + this.picExpressionWarning.TabStop = false; + this.picExpressionWarning.Visible = false; + // + // lblCondition + // + this.lblCondition.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.lblCondition.AutoSize = true; + this.lblCondition.Location = new System.Drawing.Point(3, 6); + this.lblCondition.Name = "lblCondition"; + this.lblCondition.Size = new System.Drawing.Size(54, 13); + this.lblCondition.TabIndex = 14; + this.lblCondition.Text = "Condition:"; + // + // txtCondition + // + this.txtCondition.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtCondition.Location = new System.Drawing.Point(63, 3); + this.txtCondition.Name = "txtCondition"; + this.txtCondition.Size = new System.Drawing.Size(652, 20); + this.txtCondition.TabIndex = 15; + // + // chkShowExtraInfo + // + this.chkShowExtraInfo.AutoSize = true; + this.chkShowExtraInfo.Checked = true; + this.chkShowExtraInfo.CheckState = System.Windows.Forms.CheckState.Checked; + this.tableLayoutPanel2.SetColumnSpan(this.chkShowExtraInfo, 2); + this.chkShowExtraInfo.Location = new System.Drawing.Point(462, 3); + this.chkShowExtraInfo.Name = "chkShowExtraInfo"; + this.chkShowExtraInfo.Size = new System.Drawing.Size(204, 17); + this.chkShowExtraInfo.TabIndex = 9; + this.chkShowExtraInfo.Text = "Additional information (IRQ, NMI, etc.)"; + this.chkShowExtraInfo.UseVisualStyleBackColor = true; + this.chkShowExtraInfo.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkIndentCode + // + this.chkIndentCode.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkIndentCode.AutoSize = true; + this.tableLayoutPanel2.SetColumnSpan(this.chkIndentCode, 2); + this.chkIndentCode.Location = new System.Drawing.Point(3, 51); + this.chkIndentCode.Name = "chkIndentCode"; + this.chkIndentCode.Size = new System.Drawing.Size(194, 17); + this.chkIndentCode.TabIndex = 8; + this.chkIndentCode.Text = "Indent code based on stack pointer"; + this.chkIndentCode.UseVisualStyleBackColor = true; + this.chkIndentCode.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // chkUseLabels + // + this.chkUseLabels.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkUseLabels.AutoSize = true; + this.chkUseLabels.Location = new System.Drawing.Point(203, 51); + this.chkUseLabels.Name = "chkUseLabels"; + this.chkUseLabels.Size = new System.Drawing.Size(79, 17); + this.chkUseLabels.TabIndex = 11; + this.chkUseLabels.Text = "Use Labels"; + this.chkUseLabels.UseVisualStyleBackColor = true; + this.chkUseLabels.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // label1 + // + this.label1.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(462, 53); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(98, 13); + this.label1.TabIndex = 12; + this.label1.Text = "Status Flag Format:"; + // + // cboStatusFlagFormat + // + this.tableLayoutPanel2.SetColumnSpan(this.cboStatusFlagFormat, 2); + this.cboStatusFlagFormat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cboStatusFlagFormat.FormattingEnabled = true; + this.cboStatusFlagFormat.Location = new System.Drawing.Point(566, 49); + this.cboStatusFlagFormat.Name = "cboStatusFlagFormat"; + this.cboStatusFlagFormat.Size = new System.Drawing.Size(121, 21); + this.cboStatusFlagFormat.TabIndex = 13; + this.cboStatusFlagFormat.SelectedIndexChanged += new System.EventHandler(this.cboStatusFlagFormat_SelectedIndexChanged); + // + // chkUseWindowsEol + // + this.chkUseWindowsEol.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkUseWindowsEol.AutoSize = true; + this.chkUseWindowsEol.Location = new System.Drawing.Point(301, 51); + this.chkUseWindowsEol.Name = "chkUseWindowsEol"; + this.chkUseWindowsEol.Size = new System.Drawing.Size(155, 17); + this.chkUseWindowsEol.TabIndex = 19; + this.chkUseWindowsEol.Text = "Use Windows EOL (CR LF)"; + this.chkUseWindowsEol.UseVisualStyleBackColor = true; + // + // chkExtendZeroPage + // + this.chkExtendZeroPage.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.chkExtendZeroPage.AutoSize = true; + this.tableLayoutPanel2.SetColumnSpan(this.chkExtendZeroPage, 2); + this.chkExtendZeroPage.Location = new System.Drawing.Point(462, 26); + this.chkExtendZeroPage.Name = "chkExtendZeroPage"; + this.chkExtendZeroPage.Size = new System.Drawing.Size(205, 17); + this.chkExtendZeroPage.TabIndex = 20; + this.chkExtendZeroPage.Text = "Show zero page addresses as 2 bytes"; + this.chkExtendZeroPage.UseVisualStyleBackColor = true; + this.chkExtendZeroPage.CheckedChanged += new System.EventHandler(this.chkOptions_CheckedChanged); + // + // tableLayoutPanel3 + // + this.tableLayoutPanel3.ColumnCount = 1; + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.tableLayoutPanel3.Controls.Add(this.tableLayoutPanel1, 0, 1); + this.tableLayoutPanel3.Controls.Add(this.grpExecutionLog, 0, 0); + this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel3.Location = new System.Drawing.Point(0, 24); + this.tableLayoutPanel3.Name = "tableLayoutPanel3"; + this.tableLayoutPanel3.RowCount = 2; + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel3.Size = new System.Drawing.Size(784, 385); + this.tableLayoutPanel3.TabIndex = 1; + // + // grpExecutionLog + // + this.grpExecutionLog.Controls.Add(this.txtTraceLog); + this.grpExecutionLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.grpExecutionLog.Location = new System.Drawing.Point(3, 3); + this.grpExecutionLog.Name = "grpExecutionLog"; + this.grpExecutionLog.Size = new System.Drawing.Size(778, 191); + this.grpExecutionLog.TabIndex = 2; + this.grpExecutionLog.TabStop = false; + this.grpExecutionLog.Text = "Execution Log"; + // + // txtTraceLog + // + this.txtTraceLog.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtTraceLog.CodeHighlightingEnabled = true; + this.txtTraceLog.ContextMenuStrip = this.ctxMenu; + this.txtTraceLog.Dock = System.Windows.Forms.DockStyle.Fill; + this.txtTraceLog.HideSelection = false; + this.txtTraceLog.Location = new System.Drawing.Point(3, 16); + this.txtTraceLog.Name = "txtTraceLog"; + this.txtTraceLog.ShowCompactPrgAddresses = false; + this.txtTraceLog.ShowContentNotes = false; + this.txtTraceLog.ShowLineNumberNotes = false; + this.txtTraceLog.ShowMemoryValues = false; + this.txtTraceLog.ShowScrollbars = true; + this.txtTraceLog.ShowSingleContentLineNotes = true; + this.txtTraceLog.ShowSingleLineLineNumberNotes = false; + this.txtTraceLog.Size = new System.Drawing.Size(772, 172); + this.txtTraceLog.TabIndex = 0; + // + // ctxMenu + // + this.ctxMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuCopy, + this.mnuSelectAll}); + this.ctxMenu.Name = "ctxMenu"; + this.ctxMenu.Size = new System.Drawing.Size(123, 48); + // + // mnuCopy + // + this.mnuCopy.Image = global::Mesen.GUI.Properties.Resources.Copy; + this.mnuCopy.Name = "mnuCopy"; + this.mnuCopy.Size = new System.Drawing.Size(122, 22); + this.mnuCopy.Text = "Copy"; + this.mnuCopy.Click += new System.EventHandler(this.mnuCopy_Click); + // + // mnuSelectAll + // + this.mnuSelectAll.Image = global::Mesen.GUI.Properties.Resources.SelectAll; + this.mnuSelectAll.Name = "mnuSelectAll"; + this.mnuSelectAll.Size = new System.Drawing.Size(122, 22); + this.mnuSelectAll.Text = "Select All"; + this.mnuSelectAll.Click += new System.EventHandler(this.mnuSelectAll_Click); + // + // tmrUpdateLog + // + this.tmrUpdateLog.Interval = 150; + this.tmrUpdateLog.Tick += new System.EventHandler(this.tmrUpdateLog_Tick); + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.showToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(784, 24); + this.menuStrip1.TabIndex = 2; + this.menuStrip1.Text = "menuStrip1"; + // + // showToolStripMenuItem + // + this.showToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fontSizeToolStripMenuItem, + this.logLinesToolStripMenuItem, + this.mnuAutoRefresh, + this.toolStripMenuItem1, + this.mnuRefresh}); + this.showToolStripMenuItem.Name = "showToolStripMenuItem"; + this.showToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.showToolStripMenuItem.Text = "View"; + // + // fontSizeToolStripMenuItem + // + this.fontSizeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuIncreaseFontSize, + this.mnuDecreaseFontSize, + this.mnuResetFontSize, + this.toolStripMenuItem12, + this.mnuSelectFont}); + this.fontSizeToolStripMenuItem.Image = global::Mesen.GUI.Properties.Resources.Font; + this.fontSizeToolStripMenuItem.Name = "fontSizeToolStripMenuItem"; + this.fontSizeToolStripMenuItem.Size = new System.Drawing.Size(143, 22); + this.fontSizeToolStripMenuItem.Text = "Font Options"; + // + // mnuIncreaseFontSize + // + this.mnuIncreaseFontSize.Name = "mnuIncreaseFontSize"; + this.mnuIncreaseFontSize.ShortcutKeyDisplayString = ""; + this.mnuIncreaseFontSize.Size = new System.Drawing.Size(157, 22); + this.mnuIncreaseFontSize.Text = "Increase Size"; + this.mnuIncreaseFontSize.Click += new System.EventHandler(this.mnuIncreaseFontSize_Click); + // + // mnuDecreaseFontSize + // + this.mnuDecreaseFontSize.Name = "mnuDecreaseFontSize"; + this.mnuDecreaseFontSize.ShortcutKeyDisplayString = ""; + this.mnuDecreaseFontSize.Size = new System.Drawing.Size(157, 22); + this.mnuDecreaseFontSize.Text = "Decrease Size"; + this.mnuDecreaseFontSize.Click += new System.EventHandler(this.mnuDecreaseFontSize_Click); + // + // mnuResetFontSize + // + this.mnuResetFontSize.Name = "mnuResetFontSize"; + this.mnuResetFontSize.ShortcutKeyDisplayString = ""; + this.mnuResetFontSize.Size = new System.Drawing.Size(157, 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(154, 6); + // + // mnuSelectFont + // + this.mnuSelectFont.Name = "mnuSelectFont"; + this.mnuSelectFont.Size = new System.Drawing.Size(157, 22); + this.mnuSelectFont.Text = "Select Font..."; + this.mnuSelectFont.Click += new System.EventHandler(this.mnuSelectFont_Click); + // + // logLinesToolStripMenuItem + // + this.logLinesToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnu100Lines, + this.mnu1000Lines, + this.mnu5000Lines, + this.mnu10000Lines, + this.mnu15000Lines, + this.mnu30000Lines}); + this.logLinesToolStripMenuItem.Name = "logLinesToolStripMenuItem"; + this.logLinesToolStripMenuItem.Size = new System.Drawing.Size(143, 22); + this.logLinesToolStripMenuItem.Text = "Line Count"; + // + // mnu100Lines + // + this.mnu100Lines.Name = "mnu100Lines"; + this.mnu100Lines.Size = new System.Drawing.Size(104, 22); + this.mnu100Lines.Text = "100"; + this.mnu100Lines.Click += new System.EventHandler(this.mnu100Lines_Click); + // + // mnu1000Lines + // + this.mnu1000Lines.Checked = true; + this.mnu1000Lines.CheckState = System.Windows.Forms.CheckState.Checked; + this.mnu1000Lines.Name = "mnu1000Lines"; + this.mnu1000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu1000Lines.Text = "1000"; + this.mnu1000Lines.Click += new System.EventHandler(this.mnu1000Lines_Click); + // + // mnu5000Lines + // + this.mnu5000Lines.Name = "mnu5000Lines"; + this.mnu5000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu5000Lines.Text = "5000"; + this.mnu5000Lines.Click += new System.EventHandler(this.mnu5000Lines_Click); + // + // mnu10000Lines + // + this.mnu10000Lines.Name = "mnu10000Lines"; + this.mnu10000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu10000Lines.Text = "10000"; + this.mnu10000Lines.Click += new System.EventHandler(this.mnu10000Lines_Click); + // + // mnu15000Lines + // + this.mnu15000Lines.Name = "mnu15000Lines"; + this.mnu15000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu15000Lines.Text = "15000"; + this.mnu15000Lines.Click += new System.EventHandler(this.mnu15000Lines_Click); + // + // mnu30000Lines + // + this.mnu30000Lines.Name = "mnu30000Lines"; + this.mnu30000Lines.Size = new System.Drawing.Size(104, 22); + this.mnu30000Lines.Text = "30000"; + this.mnu30000Lines.Click += new System.EventHandler(this.mnu30000Lines_Click); + // + // 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(143, 22); + this.mnuAutoRefresh.Text = "Auto-refresh"; + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(140, 6); + // + // mnuRefresh + // + this.mnuRefresh.Image = global::Mesen.GUI.Properties.Resources.Refresh; + this.mnuRefresh.Name = "mnuRefresh"; + this.mnuRefresh.Size = new System.Drawing.Size(143, 22); + this.mnuRefresh.Text = "Refresh"; + this.mnuRefresh.Click += new System.EventHandler(this.mnuRefresh_Click); + // + // frmTraceLogger + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(784, 409); + this.Controls.Add(this.tableLayoutPanel3); + this.Controls.Add(this.menuStrip1); + this.MinimumSize = new System.Drawing.Size(800, 448); + this.Name = "frmTraceLogger"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Trace Logger"; + this.tableLayoutPanel1.ResumeLayout(false); + this.grpLogOptions.ResumeLayout(false); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.tableLayoutPanel5.ResumeLayout(false); + this.tableLayoutPanel5.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picFormatHelp)).EndInit(); + this.tableLayoutPanel4.ResumeLayout(false); + this.tableLayoutPanel4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picHelp)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.picExpressionWarning)).EndInit(); + this.tableLayoutPanel3.ResumeLayout(false); + this.grpExecutionLog.ResumeLayout(false); + this.ctxMenu.ResumeLayout(false); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Button btnStopLogging; + private System.Windows.Forms.Button btnStartLogging; + private System.Windows.Forms.GroupBox grpLogOptions; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.CheckBox chkShowCpuCycles; + private System.Windows.Forms.CheckBox chkShowRegisters; + private System.Windows.Forms.CheckBox chkShowFrameCount; + private System.Windows.Forms.CheckBox chkShowPpuScanline; + private System.Windows.Forms.CheckBox chkShowPpuCycles; + private System.Windows.Forms.CheckBox chkShowByteCode; + private System.Windows.Forms.CheckBox chkShowExtraInfo; + private System.Windows.Forms.CheckBox chkIndentCode; + private System.Windows.Forms.Button btnOpenTrace; + private System.Windows.Forms.CheckBox chkShowEffectiveAddresses; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel3; + private System.Windows.Forms.Timer tmrUpdateLog; + private System.Windows.Forms.GroupBox grpExecutionLog; + private Mesen.GUI.Controls.ctrlMesenMenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem showToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem logLinesToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuAutoRefresh; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem mnuRefresh; + private System.Windows.Forms.ToolStripMenuItem mnu100Lines; + private System.Windows.Forms.ToolStripMenuItem mnu1000Lines; + private System.Windows.Forms.ToolStripMenuItem mnu10000Lines; + private System.Windows.Forms.ToolStripMenuItem mnu30000Lines; + private System.Windows.Forms.CheckBox chkUseLabels; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox cboStatusFlagFormat; + private System.Windows.Forms.Label lblCondition; + private System.Windows.Forms.TextBox txtCondition; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4; + private System.Windows.Forms.PictureBox picExpressionWarning; + private System.Windows.Forms.PictureBox picHelp; + private ctrlScrollableTextbox txtTraceLog; + private System.Windows.Forms.CheckBox chkShowMemoryValues; + 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.ToolStripSeparator toolStripMenuItem12; + private System.Windows.Forms.ToolStripMenuItem mnuSelectFont; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5; + private System.Windows.Forms.PictureBox picFormatHelp; + private System.Windows.Forms.Label lblFormat; + private System.Windows.Forms.TextBox txtFormat; + private System.Windows.Forms.CheckBox chkOverrideFormat; + private System.Windows.Forms.CheckBox chkUseWindowsEol; + private System.Windows.Forms.CheckBox chkExtendZeroPage; + private System.Windows.Forms.ToolStripMenuItem mnu5000Lines; + private System.Windows.Forms.ToolStripMenuItem mnu15000Lines; + private System.Windows.Forms.ContextMenuStrip ctxMenu; + private System.Windows.Forms.ToolStripMenuItem mnuCopy; + private System.Windows.Forms.ToolStripMenuItem mnuSelectAll; + } +} \ No newline at end of file diff --git a/UI/Debugger/frmTraceLogger.resx b/UI/Debugger/frmTraceLogger.resx new file mode 100644 index 0000000..4068e7b --- /dev/null +++ b/UI/Debugger/frmTraceLogger.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + 353, 17 + + + 107, 17 + + + 238, 17 + + \ No newline at end of file diff --git a/UI/Dependencies/Font.24.spritefont b/UI/Dependencies/Font.24.spritefont new file mode 100644 index 0000000..38e35d6 Binary files /dev/null and b/UI/Dependencies/Font.24.spritefont differ diff --git a/UI/Dependencies/Font.64.spritefont b/UI/Dependencies/Font.64.spritefont new file mode 100644 index 0000000..dcb9e89 Binary files /dev/null and b/UI/Dependencies/Font.64.spritefont differ diff --git a/UI/Dependencies/LICENSE.txt b/UI/Dependencies/LICENSE.txt new file mode 100644 index 0000000..2e95ff5 --- /dev/null +++ b/UI/Dependencies/LICENSE.txt @@ -0,0 +1,99 @@ +----------------------------------------------------------------------- +IMPORTANT NOTE: +THIS LICENSE APPLIES TO Font.24.spritefont AND Font.64.spritefont ONLY. +Those files are derived works of SIL fonts and as such are distributed +under the same license. +----------------------------------------------------------------------- + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to +provide a free and open framework in which fonts may be shared and +improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software +components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, +deleting, or substituting -- in part or in whole -- any of the +components of the Original Version, by changing formats or by porting +the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, +modify, redistribute, and sell modified and unmodified copies of the +Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in +Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the +corresponding Copyright Holder. This restriction only applies to the +primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created using +the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/UI/Dependencies/PixelFont.ttf b/UI/Dependencies/PixelFont.ttf new file mode 100644 index 0000000..f5cbfc0 Binary files /dev/null and b/UI/Dependencies/PixelFont.ttf differ diff --git a/UI/Dependencies/resources.en.xml b/UI/Dependencies/resources.en.xml new file mode 100644 index 0000000..8c78877 --- /dev/null +++ b/UI/Dependencies/resources.en.xml @@ -0,0 +1,1322 @@ + + + +
+ File + Open + Save State + Load State + Load Last Session + Recent Files + Exit + Game + Pause + Reset + Power Cycle + Power Off + Switch Disk Side + Select Disk + Eject Disk + Game Configuration + Insert Coin (1) + Insert Coin (2) + Insert Coin (3 - DualSystem) + Insert Coin (4 - DualSystem) + Input Barcode... + Tape Recorder + Load from file... + Record to file... + Stop recording + Options + Speed + Normal (100%) + Increase Speed + Decrease Speed + Maximum Speed + Triple (300%) + Double (200%) + Half (50%) + Quarter (25%) + Show FPS + Video Size + 1x + 2x + 3x + 4x + 5x + 6x + Fullscreen + Video Filter + None + NTSC 2x (blargg) + Use Bilinear Interpolation + Audio + Input + Region + Auto + NTSC + PAL + Dendy + Video + Emulation + Preferences + Tools + History Viewer + Net Play + Start Server + Connect to Server + Select Controller + Player 1 + Player 2 + Player 3 + Player 4 + Expansion Device + Spectator + Find Public Server... + Configure Profile + Movies + Play... + Record... + Stop + Sound Recorder + Record... + Stop Recording + Video Recorder + Record... + Stop Recording + Cheats + Tests + Run... + Record from... + Start + Now + Movie + Test + Stop recording + Run all tests + Debugger + Log Window + Install HD Pack + HD Pack Builder + Load Random Game + Take Screenshot + Help + Online Help + Check for updates + Report a bug + Command-line options + About + + + Extracting file, please wait... + + + Title: + Artist: + Copyright: + Sound Chips: + REC + Slow Motion + Rewinding + Fast Forward +
+
+ Close +
+
+ General + Advanced + Disable dynamic sample rate + Swap square channels duty cycles (Mimics old clones) + Mute ultrasonic frequencies on triangle channel (reduces popping) + Reduce popping sounds on the DMC channel + Disable noise channel mode flag + Effects + Stereo + Disabled + Delay + Panning + ms + (Angle in degrees) + Comb Filter + ms + Delay: + Strength: + Enable Crossfeed: + Reverb + Enable Reverb + Delay: + Strength: + Mute sound when in background + Reduce volume when in background + Reduce volume during fast forward/rewind + Volume Reduction (%) + Volume Reduction Settings + Enable Audio + Sample Rate: + ms + Low values may cause sound problems + Latency: + Device: + Volume + Volume + Reset to Defaults + OK + Cancel + DMC + Noise + Triangle + Square 2 + Square 1 + Master + FDS + MMC5 + VRC6 + VRC7 + Namco + Sunsoft + + Panning + DMC + Noise + Triangle + Square 2 + Square 1 + FDS + MMC5 + VRC6 + VRC7 + Namco + Sunsoft + + Equalizer + Enable Equalizer +
+
+ OK + Cancel + Controllers + Setup + Setup + Setup + Setup + Cartridge: + Player 1: + Player 2: + Player 4: + Player 3: + Setup + Setup + Console Type: + Expansion Port: + Use Four Score accessory + Automatically configure controllers when loading a game + + Advanced + Display Controller Input + Port 1 + Port 2 + Port 3 + Port 4 + Display Position: + Display horizontally + + Small + Large + Controller axis deadzone size: + + Hide mouse pointer when using zapper + + Warning: Your current configuration contains conflicting key bindings - some physical buttons on your keyboard or gamepad are mapped to multiple buttons on the NES controller. If this is not intentional, please review and correct your key bindings. +
+
+ General + Scale: + Filter: + Enable integer FPS mode (e.g: run at 60 fps instead of 60.1) + Enable vertical sync + Aspect Ratio: + Custom Ratio: + Use bilinear interpolation when scaling + Use integer scale values when entering fullscreen mode + Show FPS + Use HDNes HD packs + Use exclusive fullscreen mode + Requested Refresh Rate: + Overscan + Global + Game-Specific + Enable game-specific overscan settings + Left + Top + Bottom + Right + Palette + Show color indexes + Default (NTSC) + Load Preset Palette... + Export Palette + Load Palette File + Use this palette for VS System games + Picture + Select Preset... + Common Settings + Brightness + Contrast + Hue + Saturation + NTSC Filter + Artifacts + Bleed + Fringing + Gamma + Resolution + Sharpness + Merge Fields + Apply Vertical Blending + + Y Filter (Horizontal Blur) + I Filter (Horizontal Bleed) + Q Filter (Horizontal Bleed) + + Scanlines + Scanlines + Reset + Composite + S-Video + RGB + Monochrome + + Advanced + Screen Rotation: + Remove sprite limit (Reduces flashing) + Automatically re-enable sprite limit as needed to prevent graphical glitches when possible + Disable background + Disable sprites + Force background display in first column + Force sprite display in first column + + OK + Cancel +
+
+ General + % (0 = Maximum speed) + Emulation Speed: + % (0 = Maximum speed) + Fast Forward Speed: + % (0 = Maximum speed) + Rewind Speed: + + Advanced + Recommended settings for developers (homebrew / ROM hacking) + Miscellaneous Settings + Use alternative MMC3 IRQ behavior + Allow invalid input (e.g Down + Up or Left + Right at the same time) + Enable OAM RAM decay + Disable PPU $2004 reads (Famicom behavior) + Disable PPU OAMADDR bug emulation + Disable PPU palette reads + Do not reset PPU when resetting console (Famicom behavior) + Use NES/HVC-101 (Top-loader / AV Famicom) behavior + Randomize power-on state for mappers + + Default power on state for RAM: + + Overclocking + Overclocking + Overclocking can help reduce or remove slowdowns in games (but it can also cause issues). The most compatible way to overclock is by increasing the "Scanline before NMI" value by a few hundred lines (e.g 400+) + Clock Rate Multiplier: + % (Default: 100%) + + PPU Vertical Blank Configuration + Additional scanlines before NMI: + Additional scanlines after NMI: + Effective Clock Rate (NTSC): + Effective Clock Rate (PAL): + Effective Clock Rate (Dendy): + + Do not overclock APU (prevents sound pitch changes caused by overclocking) + + Show Lag Counter + Reset Counter + + OK + Cancel +
+
+ General + Display Language: + Only allow one instance of Mesen at a time + Automatically check for updates + + Enable developer mode + + Pause/Background Settings + Miscellaneous Settings + Automatically hide menu bar + Display additional information in title bar + + Display confirmation dialog before reset/power cycle/exit + Pause when a movie finishes playing + Allow input when in background + + Pause when in: + Background + Menu and config dialogs + Debugging tools + + Background + Automatically load IPS/BPS patches + Hide the pause screen + Display play/record icon when playing or recording a movie + Open Mesen Folder + Reset All Settings + + Folders/Files + File Associations + .NES + .FDS (Famicom Disk System) + .MMO (Mesen Movies) + .MST (Mesen Save State) + + Data Storage Location + Store Mesen's data in my user profile + Store Mesen's data in the same folder as the application + Folder: + + Folder Overrides + Games: + Videos: + Screenshots: + Save Data: + Audio: + Save States: + Movies: + + Advanced + Automatically insert disk 1 side A when starting FDS games + Automatically fast forward FDS games when disk or BIOS is loading + Automatically switch disks for FDS games + + Show full file path in recent file list + Show frame counter + Show game timer + Show game configuration dialog when loading VS System games + + Keep rewind data for the last + minutes (Memory Usage ≈1MB/min) + + Shortcut Keys + Warning: Your current configuration contains conflicting key bindings. If this is not intentional, please review and correct your key bindings. + Action + Binding #1 + Binding #2 + + Save Data + Save States + Automatically create a save state every + minutes + Notify when an automatic save state is saved + Allow save states to be loaded on modified ROMs (e.g IPS patches) + + Cloud Saves + Mesen can integrate with Google Drive to keep your save data in the cloud. When Google Drive integration is enabled, your save data is easily accessible from any computer and is synced between computers. Additionally, save data stored on Google Drive can be restored in the event it is erased from your computer. + Google Drive integration is active. + Disable Google Drive Integration + Enable Google Drive Integration + Last Sync: + Resync + + VS. DualSystem Settings + Play audio for: + Show video for: + + Famicom Disk System Settings + UI Display Settings + Window Settings + Do not allow the main window to be resized using the mouse + Game Selection Screen Settings + Start game from power-on instead of resuming the previous gameplay session + Disable game selection screen + + Disable built-in game database + Disable high resolution timer + Disable on-screen display (OSD) + + Always display on top of other windows + + FDS / VS / NSF + NSF Settings + Move to next track after + milliseconds of silence + Limit track run time to + seconds + Enable APU IRQs for NSF files + + OK + Cancel +
+
+ Port: + Public server + Server name: + Allow spectators + Max. number of players: + Password: + OK + Cancel +
+
+ Host: + Port: + Password: + Join as spectator + OK + Cancel +
+
+ Import Options + Cheat File: + Game: + Browse... + Browse... + Import + Cancel +
+
+ Import Options + Cheat File: + Game: + Browse... + Browse... + Import + Cancel +
+
+ Search: + Import + Cancel +
+
+ Cheats + Add Cheat + Delete + Delete selected cheats + Delete all cheats for selected game + Add cheat... + Delete + Delete + Game + Cheat Name + Code + Import + From Cheat Database + From File (XML, CHT) + Export + All Cheats + Selected Game + Export + Selected Cheats + Export + Disable all cheats + + Cheat Finder + Filters + Reset + Undo + Current value is + Previous value was + Add Filter + Add Filter + Create Cheat + Create Cheat + at + Automatically pause game when this window is active + + OK + Cancel +
+
+ Game: + Cheat Name: + Code + Custom: + Game Genie: + Pro Action Rocky: + Address: + New Value: + Memory + Game Code + Compare Value + Browse... + Cheat Enabled + OK + Cancel +
+
+ Mesen + © 2019 M. Bibaud (aka Sour) + Website: + www.mesen.ca + Version: + Build Date: + If you want to support Mesen, please consider donating. Thank you for your support! + &OK +
+
+ DIP Switches +
+
+ Latest Version: + Current Version: + Changelog: + If you want to support Mesen, please consider donating. Thank you! + Update + Cancel +
+
+ Search: + OK + Cancel +
+
+ Press any key on your keyboard or controller to set a new binding. +
+
+ Player name: + OK + Cancel +
+
+ Save to: + Video Codec: + Compression Level: + low (fast) + high (slow) + Browse... + OK + Cancel +
+
+ CHR Bank Preview + CHR Bank: + Recording Options + CHR Bank Size: + Scale/Filter: + Sort pages by usage frequency + Use 8x16 sprite display mode + Group blank tiles + Ignore tiles at the edges of the screen (overscan) + Save Folder: + Browse... + Start Recording + Stop Recording + Open Save Folder +
+
+ Volume + File + Import Movie + Export Movie + Create Save State + Resume Gameplay + Close + + Options + Video Size + 1x + 2x + 3x + 4x + 5x + 6x +
+
+ Start time: + End time: + OK + Cancel +
+
+ Usage Example + General + Video Options + Audio Options + Emulation Options + This will start Mesen in fullscreen mode with the "MyGame.nes" rom loaded. It will also use the NTSC filter, set at a 2x scale and configure the Overscan settings. The "DoNotSaveSettings" flag is used to prevent the command line switches from pernanently altering Mesen's settings. +
+
+ Copying: +
+
+ Mesen - NES Emulator + Configuration Wizard + Please take a moment to perform Mesen's initial setup. + Data Storage Location + Select where you want to store Mesen's data: + Store the data in my user profile + Store the data in the same folder as the application + Folder: + Input Mappings + Select your preferred input methods: + Other Options + Create a shortcut on my desktop + CONFIRM + Cancel +
+
+ Save to: + Record from: + Movie Information (Optional) + Author: + Description: + Browse... + OK + Cancel +
+
+ Barcode: + OK + Cancel +
+
+ Tabs with an icon contain key bindings for this player. Each button can be mapped to up to 4 different keyboard keys or gamepad buttons. + Key Set #1 + Key Set #2 + Key Set #3 + Key Set #4 + Set Default Bindings + Clear Key Bindings + OK + Cancel +
+
+ Turbo Speed: + Fast + Slow + + Select Preset... + Keyboard + WASD Layout + Arrow Keys Layout + FCEUX Default + Nestopia Default + Xbox Controller + Controller #1 + Controller #2 + PS4 Controller + Controller #1 + Controller #2 + SNES30 Controller + Controller #1 + Controller #2 +
+
+ Small + Large + Light Detection Radius: + OK + Cancel +
+
+ Low + High + Sensitivity: + OK + Cancel +
+
+
+
+
+
+
+
+
+
+ Use side A +
+
+
+
+
+
+
+
+ + + Left Hook + Right Hook + Left Jab + Right Jab + Straight + Body + Move Left + Move Right + + + Press + Release + + + Microphone + + + + All Files (*.*)|*.* + Zip files (*.zip)|*.zip + Movie files (*.mmo)|*.mmo|All Files (*.*)|*.* + Wave files (*.wav)|*.wav|All Files (*.*)|*.* + Avi files (*.avi)|*.avi|All Files (*.*)|*.* + Palette Files (*.pal)|*.pal|All Files (*.*)|*.* + All supported formats (*.sfc, *.zip, *.7z)|*.SFC;*.ZIP;*.7Z|SNES Roms (*.sfc)|*.SFC|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|All (*.*)|*.* + All supported formats (*.nes, *.zip, *.7z, *.fds, *.nsf, *.nsfe, *.unf, *.ips, *.bps, *.ups)|*.NES;*.ZIP;*.7z;*.IPS;*.BPS;*.UPS;*.FDS;*.NSF;*.NSFE;*.UNF|NES Roms (*.nes, *.unf)|*.NES;*.UNF|Famicom Disk System Roms (*.fds)|*.FDS|NSF files (*.nsf, *.nsfe)|*.nsf;*.nsfe|ZIP Archives (*.zip)|*.ZIP|7-Zip Archives (*.7z)|*.7z|IPS/UPS/BPS Patches (*.ips, *.bps, *.ups)|*.IPS;*.BPS;*.UPS|All (*.*)|*.* + Test files (*.mtp)|*.mtp|All (*.*)|*.* + All supported formats (*.cht, *.xml)|*.cht;*.xml + Mesen Savestates (*.mst)|*.mst|All files (*.*)|*.* + Family Basic Tape files (*.fbt)|*.fbt|All Files (*.*)|*.* + + Load from file... + Save to file... + + Segment #{0} + Export entire segment + Export specific range... + An error occurred while trying to save the movie file. + An error occurred while trying to save the file. + + Clear History + Last Folder Used + + Mouse mode enabled - press ESC or pause to release the cursor. + Bandai Microphone + Datach Barcode Reader + + /fullscreen - Start Mesen in fullscreen mode + /DoNotSaveSettings - Prevent settings from being saved to the disk (useful to prevent command line options from becoming the default settings) + /RecordMovie="filename.mmo" - Start recording a movie after the specified game is loaded. + /LoadLastSession - Resumes the game in the state it was left in when it was last played. + + Resume + Pause + Start Server + Stop Server + Connect to Server + Disconnect + Player {0} + Expansion Device + + Press {0} to exit fullscreen + + {0} roms found + + (not recommended) + + Warning: This will reset ALL of settings and cannot be undone! Continue? + + Mesen could not find any games to load. + + The selected folder cannot be written to - please select another folder and try again. Details: {0} + The following path overrides are invalid: {0} Please use valid and writable folders and try again. + All files will be copied from: {0} to {1} Once the copy is completed, Mesen will restart. Continue? + + {0} games and {1} cheats in database + {0} cheats imported for {1}. + + Next Track (Hold to fast forward) + <no name> + <unknown> + + The Visual Studio Runtime could not be installed properly. + <empty> + An error has occurred while trying to check for updates. Check your internet connection and try again. Error details: {0} + Automatic updates are not enabled on this build - please download the latest version of the code and recompile Mesen to get the latest updates. + FDS bios not found. The bios is required to run FDS games. Select bios file now? + Disk {0} Side {1} + File not found: {0} + The selected bios file is invalid. + + This option allows Mesen to load HDNes-format HD packs if they are found. HD Packs should be placed in the "HdPacks" folder in a subfolder matching the name of the ROM. e.g: MyRom.nes should have their HD Pack in "HdPacks\MyRom\hires.txt". + Selects the scale and video filter to use when generating the PNG files for the HD Pack. Use the "Prescale" filters to generate the tiles at a larger scale without applying any transformation to the pixels. + This option is only available for CHR RAM games. CHR RAM games have no fixed "banks" - they are dynamically created by the game's code. This option alters the HD Pack Builder's behavior when grouping the tiles into the PNG files - a smaller bank size will usually result in less PNG files (but depending on the game's code, larger values may produce better results). + When this option is enabled, the tiles in PNG files are sorted by the frequency at which they are shown on the screen while recording (more common palettes will be grouped together in the first PNG for a specific bank number. If this option is unchecked, the PNGs will be sorted by palette - each PNG will only contain up to 4 different colors in this case. + This option groups all the blank tiles sequentially into the same PNG files - this helps reduce the number of PNG files produced by removing almost-empty PNG files containing only blank tiles. + When enabled, this option will alter the display order of CHR banks that contain only sprites to make the sprites easier to edit in the PNG file. + When enabled, this will make the builder ignore any pixels in the overscan area. This is useful in games that contain glitches on the outer edge of the screen. Incorrect palette combinations due to these glitches will be ignored and won't be shown in the PNG files. + + The selected HD Pack is not compatible with the currently running game and cannot be installed. + An error occurred while trying to install the HD Pack: {0} + The selected file is not a valid HD Pack. + The selected file is not a valid Zip file. + The destination folder ({0}) already exists - are you sure you want to overwrite it? + The HD Pack has been installed successfully. Do you want to reset the game and load the HD Pack now? + + You are running the latest version of Mesen + Patch and reset the current game? + Please select a ROM matching the IPS/UPS/BPS patch file. + Unable to download file. Check your internet connection and try again. Details: {0} + Mesen could not launch because it was unable to load MesenCore.dll due to missing dependencies. + Mesen was unable to start due to missing files. Error: MesenCore.dll is missing. + An unexpected error has occurred. Error details: {0} + Download failed - the file appears to be corrupted. Please visit the Mesen website to download the latest version manually. + Upgrade completed successfully. + The update process could not be started due to missing files. + The Microsoft .NET Framework 4.5 could not be found. Please download and install the latest version of the .NET Framework from Microsoft's website and try again. + + Mesen could not connect to your Google Drive account - please try again. + + The selected file ({0}) is not a valid cheat file. + The selected file ({0}) is not a valid XML file. + The selected cheat file ({0}) contains no cheats that match the selected game. + + Are you sure you want to reset? + Are you sure you want to power cycle? + Are you sure you want to exit? + + Fast Forward (Hold button) + Toggle Fast Forward + Rewind (Hold button) + Toggle Rewind + Rewind 10 seconds + Rewind 1 minute + Increase Speed + Decrease Speed + Pause + Reset + Power Cycle + Power Off + Exit + Take Screenshot + FDS - Switch Side + FDS - Insert Next Disk + VS - Insert Coin 1 + VS - Insert Coin 2 + VS - Insert Coin 3 (DualSystem) + VS - Insert Coin 4 (DualSystem) + Input Barcode + VS - Service Button + VS - Service Button 2 (DualSystem) + Select Next Save Slot + Select Previous Save Slot + Save State + Load State + Enable/Disable Cheat Codes + Enable/Disable Audio + Run Single Frame + + FDS - Eject Disk + Set Scale 1x + Set Scale 2x + Set Scale 3x + Set Scale 4x + Set Scale 5x + Set Scale 6x + Toggle Fullscreen Mode + Toggle FPS Counter + Toggle Debug Information + Toggle Game Timer + Toggle Frame Counter + Toggle Lag Counter + Toggle OSD (On-Screen Display) + Toggle Display on Top + Toggle Background Layer + Toggle Sprite Layer + Toggle Keyboard Mode + + Toggle Maximum Speed + Load Random Game + Save State - Slot 1 + Save State - Slot 2 + Save State - Slot 3 + Save State - Slot 4 + Save State - Slot 5 + Save State - Slot 6 + Save State - Slot 7 + Save State - Slot 8 + Save State - Slot 9 + Save State - Slot 10 + Save State to File + Load State - Slot 1 + Load State - Slot 2 + Load State - Slot 3 + Load State - Slot 4 + Load State - Slot 5 + Load State - Slot 6 + Load State - Slot 7 + Load State - Slot 8 + Load State - Slot 9 + Load State - Slot 10 + Load State - Auto Save Slot + Load State from File + Load Last Session + Open File + Open Debugger + Open Assembler + Open PPU Viewer + Open Memory Tools + Open Script Window + Open Trace Logger + Open APU Viewer + Open Event Viewer + + + + None + Standard Controller + Zapper + Arkanoid Controller + SNES Controller + Power Pad + SNES Mouse + Subor Mouse + + + None + Zapper + Four Player Adapter + Arkanoid Controller + Oeka Kids Tablet + Family Trainer + Konami Hyper Shot + Family Basic Keyboard + Partytap + Pachinko Controller + Exciting Boxing Punching Bag + Jissen Mahjong Controller + Subor Keyboard + Barcode Battler + Hori Track + Bandai Hyper Shot + Turbo File + Battle Box + + + Default (No Stretching) + Auto (8:7 or 11:8 based on game) + NTSC (8:7) + PAL (11:8) + Standard (4:3) + Widescreen (16:9) + Custom + + + 50 Hz + 60 Hz + 100 Hz + 120 Hz + 200 Hz + 240 Hz + + + None + 90° + 180° + 270° + + + None + NTSC 2x (blargg) + NTSC 2x (Bisqwit) + NTSC 4x (Bisqwit) + NTSC 8x (Bisqwit) + xBRZ 2x + xBRZ 3x + xBRZ 4x + xBRZ 5x + xBRZ 6x + HQ 2x + HQ 3x + HQ 4x + Scale 2x + Scale 3x + Scale 4x + 2xSai + Super2xSai + SuperEagle + Prescale 2x + Prescale 3x + Prescale 4x + Prescale 6x + Prescale 8x + Prescale 10x + + + Both + Master Only + Slave Only + + + NES + Famicom + + + Default + Swap controllers + Swap A/B buttons + + + RP2C03 + RP2C04-0001 + RP2C04-0002 + RP2C04-0003 + RP2C04-0004 + RP2C05-01 + RP2C05-02 + RP2C05-03 + RP2C05-04 + RP2C05-05 + + + Top Left + Top Right + Bottom Left + Bottom Right + + + User Account Default + English + Français + 日本語 + Português + Русский + Español + Українська + Català + 中文 + + + All 0s (Default) + All 1s + Random Values + + + Smaller than + Equal to + Not equal to + Greater than + + + Smaller + Equal + Not equal + Greater + + + None (Uncompressed) + Zip Motion Block Video (ZMBV) + Camstudio (CSCD) + + + Power on + Power on, with save data + Current state + + + + + NES RAM (2 KB) + PRG ROM + Work RAM + Save RAM + Register + + + CHR ROM + CHR RAM + Nametable RAM + Palette RAM + + + CPU Memory + PPU Memory + Palette RAM + Sprite / OAM RAM + Secondary OAM RAM + PRG ROM + CHR ROM + CHR RAM + Work RAM + Save RAM + NES RAM (2 KB) + Nametable RAM + + + None + Accumulator + Implicit + Immediate + Relative + Zero Page + Absolute + Zero Page, X + Zero Page, Y + Indirect + Indexed Indirect + Indirect Indexed + Indirect Indexed + Absolute, X + Absolute, X + Absolute, Y + Absolute, Y + + + Hexadecimal + Text + Text (Active only) + + + None + 128 bytes + 256 bytes + 512 bytes + 1 KB + 2 KB + 4 KB + 8 KB + 16 KB + 32 KB + 64 KB + 128 KB + 256 KB + 512 KB + 1024 KB + Reserved + + + NTSC + PAL + NTSC and PAL + Dendy + + + NES / Famicom / Dendy + VS System + Playchoice-10 + Bit Corporation Creator + VT01 Monochrome + VT01 Red/Cyan + VT02 + VT03 + VT09 + VT3x + VT36x + + + Horizontal + Vertical + Four Screens + + + Horizontal + Vertical + Screen A + Screen B + Four Screens + + + PPU Register Write + PPU Register Read + Mapper Register Write + Mapper Register Read + NMI + IRQ + Sprite 0 Hit + + + Global + Execute + CPU Read + CPU Write + PPU Read + PPU Write + (Dummy) CPU Read + (Dummy) CPU Write + + + Breakpoint + CPU Step + PPU Step + BRK + Unofficial OP code + Reset event + Debugger focused + Uninitialized memory read + Decayed OAM read + CPU crashed + + + RP2C03B + RP2C03G + RP2C04-0001 + RP2C04-0002 + RP2C04-0003 + RP2C04-0004 + RC2C03B + RC2C03C + RC2C05-01 + RC2C05-02 + RC2C05-03 + RC2C05-04 + RC2C05-05 + Undefined + Undefined (2) + Undefined (3) + + + Default + RBI Baseball + TKO Boxing + Super Xevious + Ice Climber + VS. Dual System + Raid on Bungeling Bay + + + Unspecified + Standard Controllers + Four Score (NES) + Four Player Adapter (Famicom) + VS System + VS System - Swap P1/P2 + VS System - Swap A/B buttons + VS Zapper + Zapper + 2 zappers + Bandai Hypershot + Power Pad Side A + Power Pad Side B + Family Trainer Side A + Family Trainer Side B + Arkanoid Controller (NES) + Arkanoid Controller (Famicom) + 2x Arkanoid Controllers (NES) + Konami Hyper Shot + Pachinko Controller + Exciting Boxing + Jissen Mahjong + Party Tap + Oeka Kids Tablet + Barcode Battler + Miracle Piano + Pokkun Moguraa + Top Rider + Double Fisted + Famicom 3D System + Doremikko Keyboard + ROB + Family Data Recorder + Turbo File + Battle Box + Family Basic Keyboard + PEC-586 Keyboard + Bit-79 Keyboard + Subor Keyboard + Subor Keyboard+Mouse (Type A) + Subor Keyboard+Mouse (Type B) + SNES Mouse + Generic Multicart + SNES Controllers + + +
\ No newline at end of file diff --git a/UI/Forms/BaseConfigForm.cs b/UI/Forms/BaseConfigForm.cs new file mode 100644 index 0000000..d22330d --- /dev/null +++ b/UI/Forms/BaseConfigForm.cs @@ -0,0 +1,202 @@ +using System; +using System.Windows.Forms; +using Mesen.GUI.Config; +using System.ComponentModel; + +namespace Mesen.GUI.Forms +{ + public partial class BaseConfigForm : BaseForm + { + private EntityBinder _binder; + private object _entity; + private Timer _validateTimer; + + public BaseConfigForm() + { + InitializeComponent(); + + _binder = new EntityBinder(); + this.ShowInTaskbar = false; + + bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime); + if(!designMode) { + _validateTimer = new Timer(); + _validateTimer.Interval = 50; + _validateTimer.Tick += OnValidateInput; + _validateTimer.Start(); + } + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + UpdateUI(); + } + + protected override bool IsConfigForm { get { return true; } } + + protected void UpdateUI() + { + _binder.UpdateUI(); + this.AfterUpdateUI(); + } + + protected void UpdateObject() + { + _binder.UpdateObject(); + UpdateConfig(); + } + + private void OnValidateInput(object sender, EventArgs e) + { + btnOK.Enabled = ValidateInput(); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + if(DialogResult == System.Windows.Forms.DialogResult.OK) { + if(!ValidateInput()) { + e.Cancel = true; + } + } + + if(!e.Cancel) { + _validateTimer.Tick -= OnValidateInput; + _validateTimer.Stop(); + } + + base.OnFormClosing(e); + } + + protected override void OnFormClosed(FormClosedEventArgs e) + { + if(this.DialogResult == System.Windows.Forms.DialogResult.OK) { + UpdateObject(); + if(ApplyChangesOnOK) { + ConfigManager.ApplyChanges(); + } + } else { + if(ApplyChangesOnOK) { + ConfigManager.RejectChanges(); + } + } + base.OnFormClosed(e); + } + + protected virtual bool ApplyChangesOnOK + { + get { return true; } + } + + + protected virtual void UpdateConfig() + { + } + + protected bool Updating + { + get { return _binder.Updating; } + } + + protected object Entity + { + get { return _entity; } + set + { + _binder.Entity = value; + _entity = value; + } + } + + protected virtual bool ValidateInput() + { + return true; + } + + protected void AddBinding(string fieldName, RadioButton trueRadio, RadioButton falseRadio) + { + falseRadio.Checked = true; + _binder.AddBinding(fieldName, trueRadio); + } + + protected void AddBinding(string fieldName, Control bindedField, eNumberFormat format = eNumberFormat.Default) + { + _binder.AddBinding(fieldName, bindedField, format); + } + + public static void InitializeComboBox(ComboBox combo, Type enumType, Enum[] hiddenValues = null) + { + Enum selectedValue = combo.GetEnumValue(enumType); + + combo.DropDownStyle = ComboBoxStyle.DropDownList; + combo.Items.Clear(); + foreach(Enum value in Enum.GetValues(enumType)) { + if(hiddenValues == null || Array.IndexOf(hiddenValues, value) < 0) { + combo.Items.Add(ResourceHelper.GetEnumText(value)); + } + } + + if(selectedValue != null) { + combo.SetEnumValue(selectedValue); + } + } + + virtual protected void AfterUpdateUI() + { + } + + private void btnOK_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + this.Close(); + } + } + + public static class ComboBoxExtensions + { + public static Enum GetEnumValue(this ComboBox cbo, Type enumType) + { + if(cbo.SelectedItem == null) { + return null; + } + + foreach(Enum value in Enum.GetValues(enumType)) { + if(ResourceHelper.GetEnumText(value) == cbo.SelectedItem.ToString()) { + return value; + } + } + + return null; + } + + public static T GetEnumValue(this ComboBox cbo) + { + if(cbo.SelectedItem == null) { + return default(T); + } + + foreach(Enum value in Enum.GetValues(typeof(T))) { + if(ResourceHelper.GetEnumText(value) == cbo.SelectedItem.ToString()) { + return (T)(object)value; + } + } + + return default(T); + } + + public static void SetEnumValue(this ComboBox cbo, T value) + { + for(int i = 0; i < cbo.Items.Count; i++) { + if(ResourceHelper.GetEnumText((Enum)(object)value) == cbo.Items[i].ToString()) { + cbo.SelectedIndex = i; + break; + } + } + } + } +} diff --git a/UI/Forms/BaseConfigForm.designer.cs b/UI/Forms/BaseConfigForm.designer.cs new file mode 100644 index 0000000..e937dae --- /dev/null +++ b/UI/Forms/BaseConfigForm.designer.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + partial class BaseConfigForm + { + /// + /// 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.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.baseConfigPanel = new System.Windows.Forms.Panel(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.baseConfigPanel.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(84, 3); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 0; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // btnOK + // + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(3, 3); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(75, 23); + this.btnOK.TabIndex = 1; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // baseConfigPanel + // + this.baseConfigPanel.Controls.Add(this.flowLayoutPanel1); + this.baseConfigPanel.Dock = System.Windows.Forms.DockStyle.Bottom; + this.baseConfigPanel.Location = new System.Drawing.Point(0, 233); + this.baseConfigPanel.Name = "baseConfigPanel"; + this.baseConfigPanel.Size = new System.Drawing.Size(327, 29); + this.baseConfigPanel.TabIndex = 1; + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.Controls.Add(this.btnCancel); + this.flowLayoutPanel1.Controls.Add(this.btnOK); + this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Right; + this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.flowLayoutPanel1.Location = new System.Drawing.Point(121, 0); + this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(206, 29); + this.flowLayoutPanel1.TabIndex = 2; + // + // BaseConfigForm + // + this.AcceptButton = this.btnOK; + this.CancelButton = this.btnCancel; + this.Controls.Add(this.baseConfigPanel); + this.Name = "BaseConfigForm"; + this.baseConfigPanel.ResumeLayout(false); + this.flowLayoutPanel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + protected Button btnCancel; + protected Button btnOK; + protected Panel baseConfigPanel; + private FlowLayoutPanel flowLayoutPanel1; + } +} diff --git a/UI/Forms/BaseConfigForm.resx b/UI/Forms/BaseConfigForm.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/UI/Forms/BaseConfigForm.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/Forms/BaseForm.cs b/UI/Forms/BaseForm.cs new file mode 100644 index 0000000..3a35adf --- /dev/null +++ b/UI/Forms/BaseForm.cs @@ -0,0 +1,221 @@ +using Mesen.GUI.Config; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public class BaseForm : Form + { + public delegate void ProcessCmdKeyHandler(Keys keyData, ref bool processed); + public event ProcessCmdKeyHandler OnProcessCmdKey; + + protected ToolTip toolTip; + private System.ComponentModel.IContainer components; + private bool _iconSet = false; + protected int _inMenu = 0; + private static Timer _tmrUpdateBackground; + //private static bool _needResume = false; + + public BaseForm() + { + InitializeComponent(); + } + + protected virtual bool IsConfigForm { get { return false; } } + + public static void StartBackgroundTimer() + { + _tmrUpdateBackground = new Timer(); + _tmrUpdateBackground.Start(); + _tmrUpdateBackground.Tick += tmrUpdateBackground_Tick; + } + + public static void StopBackgroundTimer() + { + _tmrUpdateBackground?.Stop(); + } + + private static void tmrUpdateBackground_Tick(object sender, EventArgs e) + { + /*Form focusedForm = null; + foreach(Form form in Application.OpenForms) { + if(form.ContainsFocus) { + focusedForm = form; + break; + } + } + + bool needPause = focusedForm == null && ConfigManager.Config.PreferenceInfo.PauseWhenInBackground; + if(focusedForm != null) { + needPause |= ConfigManager.Config.PreferenceInfo.PauseWhenInMenusAndConfig && focusedForm is BaseForm && (((BaseForm)focusedForm)._inMenu > 0 || ((BaseForm)focusedForm).IsConfigForm); + needPause |= ConfigManager.Config.PreferenceInfo.PauseWhenInMenusAndConfig && !(focusedForm is BaseInputForm) && !focusedForm.GetType().FullName.Contains("Debugger"); + needPause |= ConfigManager.Config.PreferenceInfo.PauseWhenInDebuggingTools && focusedForm.GetType().FullName.Contains("Debugger"); + } + + if(needPause) { + if(!EmuApi.IsPaused(EmuApi.ConsoleId.Master)) { + _needResume = true; + EmuApi.Pause(EmuApi.ConsoleId.Master); + } + } else if(_needResume) { + EmuApi.Resume(EmuApi.ConsoleId.Master); + _needResume = false; + } + + EmuApi.SetFlag(EmulationFlags.InBackground, focusedForm == null);*/ + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + bool processed = false; + OnProcessCmdKey?.Invoke(keyData, ref processed); + return processed || base.ProcessCmdKey(ref msg, keyData); + } + + public void Show(object sender, IWin32Window owner = null) + { + if(sender is ToolStripMenuItem) { + ToolStripItem menuItem = (ToolStripMenuItem)sender; + if(menuItem.Image == null) { + menuItem = menuItem.OwnerItem; + } + this.Icon = menuItem.Image; + } + + CenterOnParent(owner); + base.Show(); + } + + private void CenterOnParent(IWin32Window owner) + { + Form parent = (Form)owner; + Point point = parent.PointToScreen(new Point(parent.Width / 2, parent.Height / 2)); + + this.StartPosition = FormStartPosition.Manual; + this.Top = point.Y - this.Height / 2; + this.Left = point.X - this.Width / 2; + } + + public DialogResult ShowDialog(object sender, IWin32Window owner = null) + { + if(sender is ToolStripMenuItem) { + ToolStripItem menuItem = (ToolStripMenuItem)sender; + if(menuItem.Image == null) { + menuItem = menuItem.OwnerItem; + } + this.Icon = menuItem.Image; + } + return base.ShowDialog(owner); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + if(!DesignMode) { + if(!_iconSet) { + base.Icon = Properties.Resources.MesenIcon; + } + } + + int tabIndex = 0; + InitializeTabIndexes(this, ref tabIndex); + ResourceHelper.ApplyResources(this); + } + + private void InitializeTabIndexes(TableLayoutPanel tlp, ref int tabIndex) + { + tlp.TabIndex = tabIndex; + tabIndex++; + + for(int i = 0; i < tlp.RowCount; i++) { + for(int j = 0; j < tlp.ColumnCount; j++) { + Control ctrl = tlp.GetControlFromPosition(j, i); + if(ctrl != null) { + if(ctrl is TableLayoutPanel) { + InitializeTabIndexes(((TableLayoutPanel)ctrl), ref tabIndex); + } else { + InitializeTabIndexes(ctrl, ref tabIndex); + } + } + } + } + } + + private void InitializeTabIndexes(Control container, ref int tabIndex) + { + container.TabIndex = tabIndex; + tabIndex++; + + foreach(Control ctrl in container.Controls) { + if(ctrl is TableLayoutPanel) { + InitializeTabIndexes(((TableLayoutPanel)ctrl), ref tabIndex); + } else { + InitializeTabIndexes(ctrl, ref tabIndex); + } + } + } + + public new Image Icon + { + set + { + if(value != null) { + Bitmap b = new Bitmap(value); + Icon i = System.Drawing.Icon.FromHandle(b.GetHicon()); + base.Icon = i; + i.Dispose(); + + _iconSet = true; + } + } + } + + public new SizeF AutoScaleDimensions + { + set + { + if(!Program.IsMono) { + base.AutoScaleDimensions = value; + } + } + } + + public new AutoScaleMode AutoScaleMode + { + set { + if(Program.IsMono) { + base.AutoScaleMode = AutoScaleMode.None; + } else { + base.AutoScaleMode = value; + } + } + } + + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.toolTip = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // toolTip + // + this.toolTip.AutomaticDelay = 0; + this.toolTip.AutoPopDelay = 32700; + this.toolTip.InitialDelay = 10; + this.toolTip.ReshowDelay = 10; + // + // BaseForm + // + this.Name = "BaseForm"; + this.ResumeLayout(false); + + } + } +} diff --git a/UI/Forms/BaseForm.resx b/UI/Forms/BaseForm.resx new file mode 100644 index 0000000..8766f29 --- /dev/null +++ b/UI/Forms/BaseForm.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/Forms/BaseInputForm.cs b/UI/Forms/BaseInputForm.cs new file mode 100644 index 0000000..68141d1 --- /dev/null +++ b/UI/Forms/BaseInputForm.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public class BaseInputForm : BaseForm, IMessageFilter + { + private const int WM_KEYDOWN = 0x100; + private const int WM_KEYUP = 0x101; + private const int WM_SYSKEYDOWN = 0x104; + private const int WM_SYSKEYUP = 0x105; + + public BaseInputForm() + { + bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime); + if(!designMode) { + Application.AddMessageFilter(this); + } + } + + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if(keyData == Keys.Escape) { + //CursorManager.ReleaseMouse(); + } + + return base.ProcessCmdKey(ref msg, keyData); + } + + bool IMessageFilter.PreFilterMessage(ref Message m) + { + if(this.ContainsFocus) { + if(m.Msg == WM_KEYUP || m.Msg == WM_SYSKEYUP) { + int scanCode = (Int32)(((Int64)m.LParam & 0x1FF0000) >> 16); + EmuApi.SetKeyState(scanCode, false); + } else if(m.Msg == WM_SYSKEYDOWN || m.Msg == WM_KEYDOWN) { + int scanCode = (Int32)(((Int64)m.LParam & 0x1FF0000) >> 16); + EmuApi.SetKeyState(scanCode, true); + } + } + return false; + } + + protected override void OnFormClosed(FormClosedEventArgs e) + { + base.OnFormClosed(e); + Application.RemoveMessageFilter(this); + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + if(Program.IsMono) { + //Mono does not trigger the activate/deactivate events when opening a modal popup, but it does set the form to disabled + //Use this to reset key states + this.EnabledChanged += (object s, EventArgs evt) => { + EmuApi.ResetKeyState(); + }; + } + } + + protected override void OnDeactivate(EventArgs e) + { + base.OnDeactivate(e); + EmuApi.ResetKeyState(); + } + + protected override void OnActivated(EventArgs e) + { + base.OnActivated(e); + EmuApi.ResetKeyState(); + } + } +} diff --git a/UI/Forms/BaseInputForm.resx b/UI/Forms/BaseInputForm.resx new file mode 100644 index 0000000..5f270f9 --- /dev/null +++ b/UI/Forms/BaseInputForm.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/Forms/EntityBinder.cs b/UI/Forms/EntityBinder.cs new file mode 100644 index 0000000..7fd3e5c --- /dev/null +++ b/UI/Forms/EntityBinder.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Controls; +using Mesen.GUI.Forms.Config; + +namespace Mesen.GUI.Forms +{ + public class EntityBinder + { + private Dictionary _bindings = new Dictionary(); + private Dictionary _fieldFormat = new Dictionary(); + private Dictionary _fieldInfo = null; + + public object Entity { get; set; } + + protected virtual Type BindedType + { + get { return Entity.GetType(); } + } + + public bool Updating { get; private set; } + + public void AddBinding(string fieldName, Control bindedField, eNumberFormat format = eNumberFormat.Default) + { + if(BindedType == null) { + throw new Exception("Need to override BindedType to use bindings"); + } + + if(_fieldInfo == null) { + _fieldInfo = new Dictionary(); + PropertyInfo[] properties = BindedType.GetProperties(); + foreach(PropertyInfo info in properties) { + _fieldInfo[info.Name] = new FieldInfoWrapper(info); + } + + FieldInfo[] members = BindedType.GetFields(); + foreach(FieldInfo info in members) { + _fieldInfo[info.Name] = new FieldInfoWrapper(info); + } + } + + if(_fieldInfo.ContainsKey(fieldName)) { + Type fieldType = _fieldInfo[fieldName].FieldType; + if(fieldType.IsSubclassOf(typeof(Enum)) && bindedField is ComboBox) { + BaseConfigForm.InitializeComboBox(((ComboBox)bindedField), fieldType); + } + _bindings[fieldName] = bindedField; + _fieldFormat[fieldName] = format; + } else { + throw new Exception("Invalid field name"); + } + } + + public void UpdateUI() + { + this.Updating = true; + + foreach(KeyValuePair kvp in _bindings) { + if(!_fieldInfo.ContainsKey(kvp.Key)) { + throw new Exception("Invalid binding key"); + } else { + FieldInfoWrapper field = _fieldInfo[kvp.Key]; + eNumberFormat format = _fieldFormat[kvp.Key]; + 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); + } else { + 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; + } else if(kvp.Value is CheckBox) { + ((CheckBox)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)); + if(radio != null) { + radio.Checked = true; + } else { + throw new Exception("No radio button matching value found"); + } + } else if(kvp.Value is ctrlTrackbar) { + if(field.FieldType == typeof(Int32)) { + ((ctrlTrackbar)kvp.Value).Value = (int)value; + } else { + ((ctrlTrackbar)kvp.Value).Value = (int)(uint)value; + } + } else if(kvp.Value is ctrlHorizontalTrackbar) { + ((ctrlHorizontalTrackbar)kvp.Value).Value = (int)value; + } else if(kvp.Value is TrackBar) { + if(field.FieldType == typeof(Int32)) { + ((TrackBar)kvp.Value).Value = (int)value; + } else { + ((TrackBar)kvp.Value).Value = (int)(uint)value; + } + } else if(kvp.Value is MesenNumericUpDown) { + MesenNumericUpDown nud = kvp.Value as MesenNumericUpDown; + decimal val; + if(field.FieldType == typeof(UInt32)) { + val = (UInt32)value; + } else if(field.FieldType == typeof(Int32)) { + val = (Int32)value; + } else { + val = (decimal)(double)value; + } + val = Math.Min(Math.Max(val, nud.Minimum), nud.Maximum); + nud.Value = val; + } else if(kvp.Value is ComboBox) { + ComboBox combo = kvp.Value as ComboBox; + if(value is Enum) { + combo.SelectedItem = ResourceHelper.GetEnumText((Enum)value); + } else if(field.FieldType == typeof(UInt32)) { + for(int i = 0, len = combo.Items.Count; i < len; i++) { + UInt32 numericValue; + string item = Regex.Replace(combo.Items[i].ToString(), "[^0-9]", ""); + if(UInt32.TryParse(item, out numericValue)) { + if(numericValue == (UInt32)value) { + combo.SelectedIndex = i; + break; + } + } + } + } else if(field.FieldType == typeof(string)) { + combo.SelectedItem = value; + if(combo.SelectedIndex < 0 && combo.Items.Count > 0) { + combo.SelectedIndex = 0; + } + } + } + } + } + + this.Updating = false; + } + + public void UpdateObject() + { + foreach(KeyValuePair kvp in _bindings) { + if(!_fieldInfo.ContainsKey(kvp.Key)) { + throw new Exception("Invalid binding key"); + } else { + try { + FieldInfoWrapper field = _fieldInfo[kvp.Key]; + eNumberFormat format = _fieldFormat[kvp.Key]; + if(kvp.Value is TextBox) { + object value = kvp.Value.Text; + NumberStyles numberStyle = format == eNumberFormat.Decimal ? NumberStyles.Integer : NumberStyles.HexNumber; + if(field.FieldType != typeof(string)) { + value = ((string)value).Trim().Replace("$", "").Replace("0x", ""); + if(string.IsNullOrWhiteSpace((string)value)) { + value = "0"; + } + } + if(field.FieldType == typeof(UInt32)) { + UInt32 result; + if(!UInt32.TryParse((string)value, numberStyle, null, out result)) { + continue; //Invalid value, ignore it + } + value = result; + } else if(field.FieldType == typeof(Int32)) { + Int32 result; + if(!Int32.TryParse((string)value, numberStyle, null, out result)) { + continue; //Invalid value, ignore it + } + value = result; + } else if(field.FieldType == typeof(Byte)) { + Byte result; + if(!Byte.TryParse((string)value, numberStyle, null, out result)) { + continue; //Invalid value, ignore it + } + value = result; + } else if(field.FieldType == typeof(UInt16)) { + UInt16 result; + if(!UInt16.TryParse((string)value, numberStyle, null, out result)) { + continue; //Invalid value, ignore it + } + value = result; + } else if(field.FieldType == typeof(UInt64)) { + UInt64 result; + if(!UInt64.TryParse((string)value, numberStyle, null, out result)) { + continue; //Invalid value, ignore it + } + value = result; + } else if(field.FieldType == typeof(Int64)) { + Int64 result; + if(!Int64.TryParse((string)value, numberStyle, null, out result)) { + continue; //Invalid value, ignore it + } + value = result; + } + field.SetValue(Entity, value); + } else if(kvp.Value is ctrlPathSelection) { + field.SetValue(Entity, ((ctrlPathSelection)kvp.Value).Text); + } else if(kvp.Value is CheckBox) { + if(field.FieldType == typeof(bool)) { + field.SetValue(Entity, ((CheckBox)kvp.Value).Checked); + } else if(field.FieldType == typeof(byte)) { + field.SetValue(Entity, ((CheckBox)kvp.Value).Checked ? (byte)1 : (byte)0); + } + } else if(kvp.Value is ctrlRiskyOption) { + if(field.FieldType == typeof(bool)) { + field.SetValue(Entity, ((ctrlRiskyOption)kvp.Value).Checked); + } else if(field.FieldType == typeof(byte)) { + field.SetValue(Entity, ((ctrlRiskyOption)kvp.Value).Checked ? (byte)1 : (byte)0); + } + } 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); + } else if(kvp.Value is ctrlTrackbar) { + if(field.FieldType == typeof(Int32)) { + field.SetValue(Entity, (Int32)((ctrlTrackbar)kvp.Value).Value); + } else { + field.SetValue(Entity, (UInt32)((ctrlTrackbar)kvp.Value).Value); + } + } else if(kvp.Value is ctrlHorizontalTrackbar) { + field.SetValue(Entity, (Int32)((ctrlHorizontalTrackbar)kvp.Value).Value); + } else if(kvp.Value is TrackBar) { + if(field.FieldType == typeof(Int32)) { + field.SetValue(Entity, ((TrackBar)kvp.Value).Value); + } else { + field.SetValue(Entity, (UInt32)((TrackBar)kvp.Value).Value); + } + } else if(kvp.Value is MesenNumericUpDown) { + if(field.FieldType == typeof(UInt32)) { + field.SetValue(Entity, (UInt32)((MesenNumericUpDown)kvp.Value).Value); + } else if(field.FieldType == typeof(Int32)) { + field.SetValue(Entity, (Int32)((MesenNumericUpDown)kvp.Value).Value); + } else { + field.SetValue(Entity, (double)((MesenNumericUpDown)kvp.Value).Value); + } + } else if(kvp.Value is ComboBox) { + if(field.FieldType.IsSubclassOf(typeof(Enum))) { + Enum enumValue = ((ComboBox)kvp.Value).GetEnumValue(field.FieldType); + if(enumValue != null) { + field.SetValue(Entity, enumValue); + } + } else if(field.FieldType == typeof(UInt32)) { + UInt32 numericValue; + string item = Regex.Replace(((ComboBox)kvp.Value).SelectedItem.ToString(), "[^0-9]", ""); + if(UInt32.TryParse(item, out numericValue)) { + field.SetValue(Entity, numericValue); + } + } else if(field.FieldType == typeof(string)) { + field.SetValue(Entity, ((ComboBox)kvp.Value).SelectedItem); + } + } + } catch { + //Ignore exceptions caused by bad user input + } + } + } + } + private class FieldInfoWrapper + { + private FieldInfo _fieldInfo; + private PropertyInfo _propertyInfo; + + public FieldInfoWrapper(PropertyInfo info) + { + _propertyInfo = info; + } + + public FieldInfoWrapper(FieldInfo info) + { + _fieldInfo = info; + } + + public Type FieldType + { + get + { + if(_fieldInfo != null) { + return _fieldInfo.FieldType; + } else { + return _propertyInfo.PropertyType; + } + } + } + + public void SetValue(object obj, object value) + { + if(_fieldInfo != null) { + _fieldInfo.SetValue(obj, value); + } else { + _propertyInfo.SetValue(obj, value); + } + } + + public object GetValue(object obj) + { + if(_fieldInfo != null) { + return _fieldInfo.GetValue(obj); + } else { + return _propertyInfo.GetValue(obj); + } + } + } + } + + public enum eNumberFormat + { + Default, + Hex, + Decimal, + } +} diff --git a/UI/Forms/MesenMsgBox.cs b/UI/Forms/MesenMsgBox.cs new file mode 100644 index 0000000..d76b610 --- /dev/null +++ b/UI/Forms/MesenMsgBox.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + class MesenMsgBox + { + public static DialogResult Show(string text, MessageBoxButtons buttons, MessageBoxIcon icon, params string[] args) + { + string resourceText = ResourceHelper.GetMessage(text, args); + + if(resourceText.StartsWith("[[")) { + if(args != null && args.Length > 0) { + return MessageBox.Show(string.Format("Critical error (" + text + ") {0}", args), "Mesen", buttons, icon); + } else { + return MessageBox.Show(string.Format("Critical error (" + text + ")"), "Mesen", buttons, icon); + } + } else { + Form mainForm = Application.OpenForms.Count > 0 ? Application.OpenForms[0] : null; + if(mainForm?.InvokeRequired == true) { + DialogResult result = DialogResult.Cancel; + mainForm.Invoke((Action)(() => { + result = MessageBox.Show(mainForm, ResourceHelper.GetMessage(text, args), "Mesen", buttons, icon); + })); + return result; + } else { + return MessageBox.Show(ResourceHelper.GetMessage(text, args), "Mesen", buttons, icon); + } + } + } + } +} diff --git a/UI/Forms/OpenSaveFileDialogExtensions.cs b/UI/Forms/OpenSaveFileDialogExtensions.cs new file mode 100644 index 0000000..dede750 --- /dev/null +++ b/UI/Forms/OpenSaveFileDialogExtensions.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public static class OpenFileDialogExtensions + { + private static string ToCaseInsensitiveFilter(string filter) + { + if(Program.IsMono) { + string[] filterData = filter.Split('|'); + for(int i = 0; i < filterData.Length; i+=2) { + List fileTypes = new List(filterData[i+1].Split(';')); + for(int j = 0, len = fileTypes.Count; j < len; j++) { + fileTypes[j] = fileTypes[j].ToUpper(); + fileTypes.Add(fileTypes[j].ToLower()); + } + filterData[i+1] = string.Join(";", fileTypes.ToArray()); + } + return string.Join("|", filterData); + } else { + return filter; + } + } + + public static void SetFilter(this OpenFileDialog ofd, string filter) + { + ofd.Filter = ToCaseInsensitiveFilter(filter); + } + + public static void SetFilter(this SaveFileDialog ofd, string filter) + { + ofd.Filter = ToCaseInsensitiveFilter(filter); + } + } + +} \ No newline at end of file diff --git a/UI/Forms/ResourceHelper.cs b/UI/Forms/ResourceHelper.cs new file mode 100644 index 0000000..a8c6a32 --- /dev/null +++ b/UI/Forms/ResourceHelper.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Windows.Forms.Layout; +using System.Xml; +using Mesen.GUI.Config; +using Mesen.GUI.Controls; + +namespace Mesen.GUI.Forms +{ + public enum Language + { + SystemDefault = 0, + English = 1, + French = 2, + Japanese = 3, + Russian = 4, + Spanish = 5, + Ukrainian = 6, + Portuguese = 7, + Catalan = 8, + Chinese = 9, + } + + class ResourceHelper + { + private static Language _language; + private static XmlDocument _resources = new XmlDocument(); + private static XmlDocument _enResources = new XmlDocument(); + + public static Language GetCurrentLanguage() + { + return _language; + } + + public static string GetLanguageCode() + { + switch(ResourceHelper.GetCurrentLanguage()) { + case Language.English: return "en"; + case Language.French: return "fr"; + case Language.Japanese: return "ja"; + case Language.Russian: return "ru"; + case Language.Spanish: return "es"; + case Language.Ukrainian: return "uk"; + case Language.Portuguese: return "pt"; + case Language.Catalan: return "ca"; + case Language.Chinese: return "zh"; + } + + return ""; + } + + public static void UpdateEmuLanguage() + { + EmuApi.SetDisplayLanguage(_language); + } + + public static void LoadResources(Language language) + { + if(language == Language.SystemDefault) { + switch(System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName) { + default: + case "en": language = Language.English; break; + case "fr": language = Language.French; break; + case "ja": language = Language.Japanese; break; + case "ru": language = Language.Russian; break; + case "es": language = Language.Spanish; break; + case "uk": language = Language.Ukrainian; break; + case "pt": language = Language.Portuguese; break; + case "zh": language = Language.Chinese; break; + } + } + + string filename; + string enFilename = "resources.en.xml"; + switch(language) { + default: + case Language.English: filename = enFilename; break; + case Language.French: filename = "resources.fr.xml"; break; + case Language.Japanese: filename = "resources.ja.xml"; break; + case Language.Russian: filename = "resources.ru.xml"; break; + case Language.Spanish: filename = "resources.es.xml"; break; + case Language.Ukrainian: filename = "resources.uk.xml"; break; + case Language.Portuguese: filename = "resources.pt.xml"; break; + case Language.Catalan: filename = "resources.ca.xml"; break; + case Language.Chinese: filename = "resources.zh.xml"; break; + } + + _language = language; + + using(Stream stream = ResourceExtractor.GetZippedResource(filename)) { + _resources.Load(stream); + } + + using(Stream stream = ResourceExtractor.GetZippedResource(enFilename)) { + _enResources.Load(stream); + } + } + + public static string GetMessage(string id, params object[] args) + { + var baseNode = _resources.SelectSingleNode("/Resources/Messages/Message[@ID='" + id + "']"); + if(baseNode == null) { + baseNode = _enResources.SelectSingleNode("/Resources/Messages/Message[@ID='" + id + "']"); + } + + if(baseNode != null) { + return string.Format(baseNode.InnerText, args); + } else { + return "[[" + id + "]]"; + } + } + + public static string GetEnumText(Enum e) + { + var baseNode = _resources.SelectSingleNode("/Resources/Enums/Enum[@ID='" + e.GetType().Name + "']/Value[@ID='" + e.ToString() + "']"); + if(baseNode == null) { + baseNode = _enResources.SelectSingleNode("/Resources/Enums/Enum[@ID='" + e.GetType().Name + "']/Value[@ID='" + e.ToString() + "']"); + } + + if(baseNode != null) { + return baseNode.InnerText; + } else { + return e.ToString(); + } + } + + public static void ApplyResources(Form form) + { + ApplyResources(form, form.Name); + } + + public static void ApplyResources(Form form, string formName) + { + XmlNode baseNode = _resources.SelectSingleNode("/Resources/Forms/Form[@ID='" + formName + "']"); + + if(baseNode != null) { + if(baseNode.Attributes["Title"] != null) { + form.Text = baseNode.Attributes["Title"].Value; + } + ApplyResources(baseNode, form.Controls); + } + } + + public static void ApplyResources(Form form, ContextMenuStrip contextMenu) + { + XmlNode baseNode = _resources.SelectSingleNode("/Resources/Forms/Form[@ID='" + form.Name + "']"); + + if(baseNode != null) { + ApplyResources(baseNode, contextMenu.Items); + } + } + + private static void ApplyResources(XmlNode baseNode, UserControl control) + { + XmlNode controlNode = _resources.SelectSingleNode("/Resources/UserControls/UserControl[@ID='" + control.GetType().Name + "']"); + + if(controlNode != null) { + ApplyResources(controlNode, control.Controls); + } else { + ApplyResources(baseNode, control.Controls); + } + } + + private static void ApplyResources(XmlNode baseNode, IEnumerable container) + { + foreach(object ctrl in container) { + string name = null; + if(ctrl is Control) { + name = ((Control)ctrl).Name; + } else if(ctrl is ToolStripItem) { + name = ((ToolStripItem)ctrl).Name; + } else if(ctrl is ColumnHeader) { + name = ((ColumnHeader)ctrl).Name; + } else if(ctrl is DataGridViewColumn) { + name = ((DataGridViewColumn)ctrl).Name; + } + + var controlNode = baseNode.SelectSingleNode("Control[@ID='" + name + "']"); + if(controlNode != null) { + if(ctrl is Control) { + ((Control)ctrl).Text = controlNode.InnerText; + } else if(ctrl is ToolStripItem) { + ((ToolStripItem)ctrl).Text = controlNode.InnerText; + if(((ToolStripItem)ctrl).DisplayStyle != ToolStripItemDisplayStyle.Image) { + ((ToolStripItem)ctrl).ToolTipText = ""; + } + } else if(ctrl is ColumnHeader) { + ((ColumnHeader)ctrl).Text = controlNode.InnerText; + } else if(ctrl is DataGridViewColumn) { + ((DataGridViewColumn)ctrl).HeaderText = controlNode.InnerText; + } + } + + if(ctrl is DataGridView) { + ApplyResources(baseNode, ((DataGridView)ctrl).Columns); + } else if(ctrl is MenuStrip) { + ApplyResources(baseNode, ((MenuStrip)ctrl).Items); + } else if(ctrl is ContextMenuStrip) { + ApplyResources(baseNode, ((ContextMenuStrip)ctrl).Items); + } else if(ctrl is ListView) { + ApplyResources(baseNode, ((ListView)ctrl).Columns); + } else if(ctrl is ToolStrip) { + ApplyResources(baseNode, ((ToolStrip)ctrl).Items); + } else if(ctrl is ToolStripSplitButton) { + ApplyResources(baseNode, ((ToolStripSplitButton)ctrl).DropDownItems); + } else if(ctrl is UserControl) { + ApplyResources(baseNode, ctrl as UserControl); + } else if(ctrl is Control) { + ApplyResources(baseNode, ((Control)ctrl).Controls); + } else if(ctrl is ToolStripMenuItem) { + ApplyResources(baseNode, ((ToolStripMenuItem)ctrl).DropDownItems); + } + + if(ctrl is Control) { + if(((Control)ctrl).ContextMenuStrip != null) { + ApplyResources(baseNode, ((Control)ctrl).ContextMenuStrip.Items); + } + } + } + } + + private static XmlDocument BuildResourceFile(Form form) + { + XmlDocument document = new XmlDocument(); + XmlNode resources = document.CreateElement("Resources"); + document.AppendChild(resources); + resources.AppendChild(document.CreateElement("Forms")); + + BuildResourceFile(document, form, form.Controls); + + return document; + } + + private static void BuildResourceFile(XmlDocument xmlDoc, Form form, IEnumerable container) + { + var baseNode = xmlDoc.SelectSingleNode("/Resources/Forms/Form[@ID='" + form.Name + "']"); + if(baseNode == null) { + baseNode = xmlDoc.CreateElement("Form"); + baseNode.Attributes.Append(xmlDoc.CreateAttribute("ID")); + baseNode.Attributes.Append(xmlDoc.CreateAttribute("Title")); + baseNode.Attributes["ID"].Value = form.Name; + baseNode.Attributes["Title"].Value = form.Text; + xmlDoc.SelectSingleNode("/Resources/Forms").AppendChild(baseNode); + } + + foreach(Component ctrl in container) { + string text = null; + string name = null; + if(ctrl is Control) { + text = ((Control)ctrl).Text; + name = ((Control)ctrl).Name; + } else if(ctrl is ToolStripItem) { + text = ((ToolStripItem)ctrl).Text; + name = ((ToolStripItem)ctrl).Name; + } + + if(!string.IsNullOrWhiteSpace(text)) { + var controlNode = baseNode.SelectSingleNode("Control[@ID='" + name + "']"); + if(controlNode == null) { + controlNode = xmlDoc.CreateElement("Control"); + controlNode.Attributes.Append(xmlDoc.CreateAttribute("ID")); + controlNode.Attributes["ID"].Value = name; + baseNode.AppendChild(controlNode); + } + controlNode.InnerText = text; + } + if(ctrl is MenuStrip) { + BuildResourceFile(xmlDoc, form, ((MenuStrip)ctrl).Items); + } else if(ctrl is Control) { + BuildResourceFile(xmlDoc, form, ((Control)ctrl).Controls); + } else if(ctrl is ToolStripMenuItem) { + BuildResourceFile(xmlDoc, form, ((ToolStripMenuItem)ctrl).DropDownItems); + } + } + } + } +} diff --git a/UI/Forms/ResourcePath.cs b/UI/Forms/ResourcePath.cs new file mode 100644 index 0000000..b2cd787 --- /dev/null +++ b/UI/Forms/ResourcePath.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Forms +{ + public struct ResourcePath : IEquatable + { + public string Path { get; set; } + public string InnerFile { get; set; } + public int InnerFileIndex { get; set; } + + public bool Exists { get { return File.Exists(Path); } } + public bool Compressed { get { return !string.IsNullOrWhiteSpace(InnerFile); } } + + public string FileName { get { return Compressed ? InnerFile : System.IO.Path.GetFileName(Path); } } + public string Folder { get { return System.IO.Path.GetDirectoryName(Path); } } + + public string ReadablePath + { + get + { + if(Compressed) { + return $"{Path} ({InnerFile})"; + } else { + return Path; + } + } + } + + public override string ToString() + { + string resPath = Path; + if(Compressed) { + resPath += "\x1" + InnerFile; + if(InnerFileIndex > 0) { + resPath += "\x1" + (InnerFileIndex - 1).ToString(); + } + } + return resPath; + } + + static public implicit operator ResourcePath(string path) + { + string[] tokens = path.Split('\x1'); + return new ResourcePath() { + Path = tokens[0], + InnerFile = tokens.Length > 1 ? tokens[1] : "", + InnerFileIndex = tokens.Length > 2 ? (int.Parse(tokens[2]) + 1) : 0 + }; + } + + static public implicit operator string(ResourcePath resourcePath) + { + return resourcePath.ToString(); + } + + bool IEquatable.Equals(ResourcePath other) + { + return other.ToString() == this.ToString(); + } + } +} diff --git a/UI/Forms/frmMain.Designer.cs b/UI/Forms/frmMain.Designer.cs new file mode 100644 index 0000000..695cda2 --- /dev/null +++ b/UI/Forms/frmMain.Designer.cs @@ -0,0 +1,152 @@ +namespace Mesen.GUI.Forms +{ + partial class frmMain + { + /// + /// 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.ctrlRenderer = new Mesen.GUI.Controls.ctrlRenderer(); + this.mnuMain = new Mesen.GUI.Controls.ctrlMesenMenuStrip(); + this.mnuFile = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuOpen = new System.Windows.Forms.ToolStripMenuItem(); + this.debugToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuStep = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuDebugger = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuTraceLogger = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.mnuRun = new System.Windows.Forms.ToolStripMenuItem(); + this.mnuMain.SuspendLayout(); + this.SuspendLayout(); + // + // ctrlRenderer + // + this.ctrlRenderer.Location = new System.Drawing.Point(0, 27); + this.ctrlRenderer.Name = "ctrlRenderer"; + this.ctrlRenderer.Size = new System.Drawing.Size(512, 448); + this.ctrlRenderer.TabIndex = 0; + // + // mnuMain + // + this.mnuMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuFile, + this.debugToolStripMenuItem}); + this.mnuMain.Location = new System.Drawing.Point(0, 0); + this.mnuMain.Name = "mnuMain"; + this.mnuMain.Size = new System.Drawing.Size(514, 24); + this.mnuMain.TabIndex = 1; + this.mnuMain.Text = "ctrlMesenMenuStrip1"; + // + // mnuFile + // + this.mnuFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuOpen}); + this.mnuFile.Name = "mnuFile"; + this.mnuFile.Size = new System.Drawing.Size(37, 20); + this.mnuFile.Text = "File"; + // + // mnuOpen + // + this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.Folder; + this.mnuOpen.Name = "mnuOpen"; + this.mnuOpen.Size = new System.Drawing.Size(152, 22); + this.mnuOpen.Text = "Open"; + this.mnuOpen.Click += new System.EventHandler(this.mnuOpen_Click); + // + // debugToolStripMenuItem + // + this.debugToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.mnuRun, + this.mnuStep, + this.toolStripMenuItem1, + this.mnuDebugger, + this.mnuTraceLogger}); + this.debugToolStripMenuItem.Name = "debugToolStripMenuItem"; + this.debugToolStripMenuItem.Size = new System.Drawing.Size(54, 20); + this.debugToolStripMenuItem.Text = "Debug"; + // + // mnuStep + // + this.mnuStep.Name = "mnuStep"; + this.mnuStep.ShortcutKeys = System.Windows.Forms.Keys.F11; + this.mnuStep.Size = new System.Drawing.Size(152, 22); + this.mnuStep.Text = "Step"; + this.mnuStep.Click += new System.EventHandler(this.mnuStep_Click); + // + // mnuDebugger + // + this.mnuDebugger.Name = "mnuDebugger"; + this.mnuDebugger.Size = new System.Drawing.Size(152, 22); + this.mnuDebugger.Text = "Debugger"; + // + // mnuTraceLogger + // + this.mnuTraceLogger.Name = "mnuTraceLogger"; + this.mnuTraceLogger.Size = new System.Drawing.Size(152, 22); + this.mnuTraceLogger.Text = "Trace Logger"; + this.mnuTraceLogger.Click += new System.EventHandler(this.mnuTraceLogger_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(149, 6); + // + // mnuRun + // + this.mnuRun.Name = "mnuRun"; + this.mnuRun.Size = new System.Drawing.Size(152, 22); + this.mnuRun.Text = "Run"; + this.mnuRun.Click += new System.EventHandler(this.mnuRun_Click); + // + // frmMain + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(514, 476); + this.Controls.Add(this.ctrlRenderer); + this.Controls.Add(this.mnuMain); + this.MainMenuStrip = this.mnuMain; + this.Name = "frmMain"; + this.Text = "frmMain"; + this.mnuMain.ResumeLayout(false); + this.mnuMain.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Controls.ctrlRenderer ctrlRenderer; + private Controls.ctrlMesenMenuStrip mnuMain; + private System.Windows.Forms.ToolStripMenuItem debugToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem mnuDebugger; + private System.Windows.Forms.ToolStripMenuItem mnuTraceLogger; + private System.Windows.Forms.ToolStripMenuItem mnuStep; + private System.Windows.Forms.ToolStripMenuItem mnuFile; + private System.Windows.Forms.ToolStripMenuItem mnuOpen; + private System.Windows.Forms.ToolStripMenuItem mnuRun; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + } +} \ No newline at end of file diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs new file mode 100644 index 0000000..3fa7da7 --- /dev/null +++ b/UI/Forms/frmMain.cs @@ -0,0 +1,62 @@ +using Mesen.GUI.Config; +using Mesen.GUI.Debugger; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Mesen.GUI.Forms +{ + public partial class frmMain : BaseForm + { + public frmMain(string[] args) + { + InitializeComponent(); + ResourceHelper.LoadResources(Language.English); + } + + protected override void OnShown(EventArgs e) + { + base.OnShown(e); + + EmuApi.InitDll(); + EmuApi.InitializeEmu(ConfigManager.HomeFolder, Handle, ctrlRenderer.Handle, false, false, false); + } + + private void mnuTraceLogger_Click(object sender, EventArgs e) + { + + } + + private void mnuStep_Click(object sender, EventArgs e) + { + DebugApi.Step(1); + } + + private void mnuOpen_Click(object sender, EventArgs e) + { + using(OpenFileDialog ofd = new OpenFileDialog()) { + ofd.Filter = ResourceHelper.GetMessage("FilterRom"); + if(ofd.ShowDialog() == DialogResult.OK) { + EmuApi.LoadRom(ofd.FileName); + Task.Run(() => { + EmuApi.Run(); + }); + + frmTraceLogger frm = new frmTraceLogger(); + frm.Show(); + } + } + } + + private void mnuRun_Click(object sender, EventArgs e) + { + DebugApi.ResumeExecution(); + } + } +} diff --git a/UI/Forms/frmMain.resx b/UI/Forms/frmMain.resx new file mode 100644 index 0000000..d4bbc4c --- /dev/null +++ b/UI/Forms/frmMain.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/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs new file mode 100644 index 0000000..0233db1 --- /dev/null +++ b/UI/Interop/DebugApi.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Config; +using Mesen.GUI.Forms; + +namespace Mesen.GUI +{ + public class DebugApi + { + private const string DllPath = "MesenSCore.dll"; + [DllImport(DllPath)] public static extern void InitializeDebugger(); + [DllImport(DllPath)] public static extern void ReleaseDebugger(); + + [DllImport(DllPath)] public static extern void ResumeExecution(); + [DllImport(DllPath)] public static extern void Step(Int32 scanCode); + + [DllImport(DllPath)] public static extern void StartTraceLogger([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename); + [DllImport(DllPath)] public static extern void StopTraceLogger(); + [DllImport(DllPath)] public static extern void SetTraceOptions(InteropTraceLoggerOptions options); + + [DllImport(DllPath, EntryPoint = "GetExecutionTrace")] private static extern IntPtr GetExecutionTraceWrapper(UInt32 lineCount); + public static string GetExecutionTrace(UInt32 lineCount) { return Utf8Marshaler.PtrToStringUtf8(DebugApi.GetExecutionTraceWrapper(lineCount)); } + } + + [Serializable] + public struct InteropTraceLoggerOptions + { + [MarshalAs(UnmanagedType.I1)] public bool ShowExtraInfo; + [MarshalAs(UnmanagedType.I1)] public bool IndentCode; + [MarshalAs(UnmanagedType.I1)] public bool UseLabels; + [MarshalAs(UnmanagedType.I1)] public bool UseWindowsEol; + [MarshalAs(UnmanagedType.I1)] public bool ExtendZeroPage; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)] + public byte[] Condition; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1000)] + public byte[] Format; + } +} diff --git a/UI/Interop/EmuApi.cs b/UI/Interop/EmuApi.cs new file mode 100644 index 0000000..978f8ca --- /dev/null +++ b/UI/Interop/EmuApi.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Config; +using Mesen.GUI.Forms; + +namespace Mesen.GUI +{ + public class EmuApi + { + private const string DllPath = "MesenSCore.dll"; + [DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool TestDll(); + [DllImport(DllPath)] public static extern void InitDll(); + + [DllImport(DllPath, EntryPoint = "GetMesenVersion")] private static extern UInt32 GetMesenVersionWrapper(); + + [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(); + + [DllImport(DllPath)] public static extern void Run(); + + [DllImport(DllPath)] public static extern void LoadRom( + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filepath, + [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string patchFile = "" + ); + + [DllImport(DllPath)] public static extern void SetKeyState(Int32 scanCode, [MarshalAs(UnmanagedType.I1)]bool pressed); + [DllImport(DllPath)] public static extern void ResetKeyState(); + [DllImport(DllPath)] public static extern void SetMousePosition(double x, double y); + + [DllImport(DllPath)] public static extern void SetDisplayLanguage(Language lang); + } +} diff --git a/UI/Interop/Utf8Marshaler.cs b/UI/Interop/Utf8Marshaler.cs new file mode 100644 index 0000000..7c6aa1a --- /dev/null +++ b/UI/Interop/Utf8Marshaler.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI +{ + public class Utf8Marshaler : ICustomMarshaler + { + static Utf8Marshaler _instance; + + public IntPtr MarshalManagedToNative(object managedObj) + { + if(managedObj == null) { + return IntPtr.Zero; + } + if(!(managedObj is string)) { + throw new MarshalDirectiveException("UTF8Marshaler must be used on a string."); + } + + // not null terminated + byte[] strbuf = Encoding.UTF8.GetBytes((string)managedObj); + IntPtr buffer = Marshal.AllocHGlobal(strbuf.Length + 1); + Marshal.Copy(strbuf, 0, buffer, strbuf.Length); + + // write the terminating null + Marshal.WriteByte(buffer + strbuf.Length, 0); + return buffer; + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + return GetStringFromIntPtr(pNativeData); + } + + public void CleanUpNativeData(IntPtr pNativeData) + { + Marshal.FreeHGlobal(pNativeData); + } + + public void CleanUpManagedData(object managedObj) + { + } + + public int GetNativeDataSize() + { + return -1; + } + + public static ICustomMarshaler GetInstance(string cookie) + { + if(_instance == null) { + return _instance = new Utf8Marshaler(); + } + return _instance; + } + + public static string GetStringFromIntPtr(IntPtr pNativeData) + { + int offset = 0; + byte b = 0; + do { + b = Marshal.ReadByte(pNativeData, offset); + offset++; + } while(b != 0); + + int length = offset - 1; + + // should not be null terminated + byte[] strbuf = new byte[length]; + // skip the trailing null + Marshal.Copy((IntPtr)pNativeData, strbuf, 0, length); + string data = Encoding.UTF8.GetString(strbuf); + return data; + } + + public static string PtrToStringUtf8(IntPtr ptr) + { + if(ptr == IntPtr.Zero) { + return ""; + } + + int len = 0; + while(Marshal.ReadByte(ptr, len) != 0) { + len++; + } + + if(len == 0) { + return ""; + } + + byte[] array = new byte[len]; + Marshal.Copy(ptr, array, 0, len); + return Encoding.UTF8.GetString(array); + } + } +} diff --git a/UI/Program.cs b/UI/Program.cs new file mode 100644 index 0000000..35a9099 --- /dev/null +++ b/UI/Program.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Xml.Serialization; +using Mesen.GUI.Config; +using Mesen.GUI.Forms; + +namespace Mesen.GUI +{ + static class Program + { + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool SetForegroundWindow(IntPtr hWnd); + + public static bool IsMono { get; private set; } + public static string OriginalFolder { get; private set; } + + private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) + { + MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, e.Exception.ToString()); + } + + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, e.ExceptionObject.ToString()); + } + + /// + /// The main entry point for the application. + /// + [STAThread] + [HandleProcessCorruptedStateExceptions] + private static void Main(string[] args) + { + try { + Task.Run(() => { + //Cache deserializers in another thread + new XmlSerializer(typeof(Configuration)); + //new XmlSerializer(typeof(DebugWorkspace)); + }); + + if(Type.GetType("Mono.Runtime") != null) { + Program.IsMono = true; + } + + Program.OriginalFolder = Directory.GetCurrentDirectory(); + + Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); + Application.ThreadException += Application_ThreadException; + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + /*if(ConfigManager.GetConfigFile() == null) { + //Show config wizard + ResourceHelper.LoadResources(Language.SystemDefault); + Application.Run(new frmConfigWizard()); + + if(ConfigManager.GetConfigFile() == null) { + Application.Exit(); + return; + } + }*/ + ConfigManager.GetConfigFile(); + ConfigManager.CreateConfig(false); + Directory.CreateDirectory(ConfigManager.HomeFolder); + Directory.SetCurrentDirectory(ConfigManager.HomeFolder); + try { + if(!ResourceExtractor.ExtractResources()) { + return; + } + } catch(FileNotFoundException e) { + string message = "The Microsoft .NET Framework 4.5 could not be found. Please download and install the latest version of the .NET Framework from Microsoft's website and try again."; + switch(ResourceHelper.GetCurrentLanguage()) { + case Language.French: message = "Le .NET Framework 4.5 de Microsoft n'a pas été trouvé. Veuillez télécharger la plus récente version du .NET Framework à partir du site de Microsoft et essayer à nouveau."; break; + case Language.Japanese: message = "Microsoft .NET Framework 4.5はインストールされていないため、Mesenは起動できません。Microsoft .NET Frameworkの最新版をMicrosoftのサイトからダウンロードして、インストールしてください。"; break; + case Language.Russian: message = "Microsoft .NET Framework 4.5 не найден. Пожалуйста загрузите и установите последнюю версию .NET Framework с сайта Microsoft и попробуйте снова."; break; + case Language.Spanish: message = "Microsoft .NET Framework 4.5 no se ha encontrado. Por favor, descargue la versión más reciente de .NET Framework desde el sitio de Microsoft y vuelva a intentarlo."; break; + case Language.Ukrainian: message = "Microsoft .NET Framework 4.5 не знайдений. Будь ласка завантажте і встановіть останню версію .NET Framework з сайту Microsoft і спробуйте знову."; break; + case Language.Portuguese: message = "Microsoft .NET Framework 4.5 não foi encontrado. Por favor, baixe a versão mais recente de .NET Framework do site da Microsoft e tente novamente."; break; + case Language.Chinese: message = "找不到 Microsoft .NET Framework 4.5,请访问 Microsoft 官网下载安装之后再试。"; break; + } + MessageBox.Show(message + Environment.NewLine + Environment.NewLine + e.ToString(), "Mesen", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } catch(Exception e) { + string message = "An unexpected error has occurred.\n\nError details:\n{0}"; + switch(ResourceHelper.GetCurrentLanguage()) { + case Language.French: message = "Une erreur inattendue s'est produite.\n\nDétails de l'erreur :\n{0}"; break; + case Language.Japanese: message = "予期しないエラーが発生しました。\n\nエラーの詳細:\n{0}"; break; + case Language.Russian: message = "Неизвестная ошибка. Подробно: {0}"; break; + case Language.Spanish: message = "Se ha producido un error inesperado. Detalles del error: {0}"; break; + case Language.Ukrainian: message = "Невідома помилка. Детально: {0}"; break; + case Language.Portuguese: message = "Houve um erro inesperado. Detalhes do erro: {0}"; break; + case Language.Chinese: message = "发生意外错误。\n\n详情:\n{0}"; break; + } + MessageBox.Show(string.Format(message, e.ToString()), "Mesen", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + if(!RuntimeChecker.TestDll()) { + return; + } + + using(SingleInstance singleInstance = new SingleInstance()) { + //if(singleInstance.FirstInstance || !ConfigManager.Config.PreferenceInfo.SingleInstance) { + frmMain frmMain = new frmMain(args); + + singleInstance.ListenForArgumentsFromSuccessiveInstances(); + singleInstance.ArgumentsReceived += (object sender, ArgumentsReceivedEventArgs e) => { + if(frmMain.IsHandleCreated) { + frmMain.BeginInvoke((MethodInvoker)(() => { + //frmMain.ProcessCommandLineArguments(CommandLineHelper.PreprocessCommandLineArguments(e.Args, true), false); + //frmMain.LoadGameFromCommandLine(CommandLineHelper.PreprocessCommandLineArguments(e.Args, false)); + })); + } + }; + + Application.Run(frmMain); + /*} else { + if(singleInstance.PassArgumentsToFirstInstance(args)) { + Process current = Process.GetCurrentProcess(); + foreach(Process process in Process.GetProcessesByName(current.ProcessName)) { + if(process.Id != current.Id) { + Program.SetForegroundWindow(process.MainWindowHandle); + break; + } + } + } else { + Application.Run(new frmMain(args)); + } + }*/ + } + } catch(Exception e) { + MesenMsgBox.Show("UnexpectedError", MessageBoxButtons.OK, MessageBoxIcon.Error, e.ToString()); + } + } + } +} diff --git a/UI/Properties/AssemblyInfo.cs b/UI/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5383a6e --- /dev/null +++ b/UI/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Mesen (Beta)")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Mesen")] +[assembly: AssemblyProduct("Mesen (Beta)")] +[assembly: AssemblyCopyright("Copyright © 2019 M. Bibaud")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c8d4fadf-6247-47bc-8cd5-4c2e29812c3c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.9.*")] +[assembly: AssemblyFileVersion("0.9.7.0")] diff --git a/UI/Properties/Resources.Designer.cs b/UI/Properties/Resources.Designer.cs new file mode 100644 index 0000000..0208499 --- /dev/null +++ b/UI/Properties/Resources.Designer.cs @@ -0,0 +1,1163 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Mesen.GUI.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Mesen.GUI.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Accept { + get { + object obj = ResourceManager.GetObject("Accept", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Add { + get { + object obj = ResourceManager.GetObject("Add", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ArrowKeys { + get { + object obj = ResourceManager.GetObject("ArrowKeys", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Audio { + get { + object obj = ResourceManager.GetObject("Audio", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Barcode { + get { + object obj = ResourceManager.GetObject("Barcode", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Breakpoint { + get { + object obj = ResourceManager.GetObject("Breakpoint", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap BreakpointDisabled { + get { + object obj = ResourceManager.GetObject("BreakpointDisabled", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap BreakpointEnableDisable { + get { + object obj = ResourceManager.GetObject("BreakpointEnableDisable", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Camera { + get { + object obj = ResourceManager.GetObject("Camera", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CheatCode { + get { + object obj = ResourceManager.GetObject("CheatCode", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Chip { + get { + object obj = ResourceManager.GetObject("Chip", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Close { + get { + object obj = ResourceManager.GetObject("Close", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap coins { + get { + object obj = ResourceManager.GetObject("coins", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Collapse { + get { + object obj = ResourceManager.GetObject("Collapse", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap CommandLine { + get { + object obj = ResourceManager.GetObject("CommandLine", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Comment { + get { + object obj = ResourceManager.GetObject("Comment", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Controller { + get { + object obj = ResourceManager.GetObject("Controller", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Copy { + get { + object obj = ResourceManager.GetObject("Copy", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Cut { + get { + object obj = ResourceManager.GetObject("Cut", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Debugger { + get { + object obj = ResourceManager.GetObject("Debugger", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Dice { + get { + object obj = ResourceManager.GetObject("Dice", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DipSwitches { + get { + object obj = ResourceManager.GetObject("DipSwitches", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Donate { + get { + object obj = ResourceManager.GetObject("Donate", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DownArrow { + get { + object obj = ResourceManager.GetObject("DownArrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DownArrow1 { + get { + object obj = ResourceManager.GetObject("DownArrow1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DownArrowDarkTheme { + get { + object obj = ResourceManager.GetObject("DownArrowDarkTheme", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DownArrowWin10 { + get { + object obj = ResourceManager.GetObject("DownArrowWin10", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap DownArrowWin101 { + get { + object obj = ResourceManager.GetObject("DownArrowWin101", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Edit { + get { + object obj = ResourceManager.GetObject("Edit", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap EditLabel { + get { + object obj = ResourceManager.GetObject("EditLabel", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Enum { + get { + object obj = ResourceManager.GetObject("Enum", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Exclamation { + get { + object obj = ResourceManager.GetObject("Exclamation", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Exit { + get { + object obj = ResourceManager.GetObject("Exit", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Expand { + get { + object obj = ResourceManager.GetObject("Expand", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Export { + get { + object obj = ResourceManager.GetObject("Export", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Find { + get { + object obj = ResourceManager.GetObject("Find", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Folder { + get { + object obj = ResourceManager.GetObject("Folder", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Font { + get { + object obj = ResourceManager.GetObject("Font", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Fullscreen { + get { + object obj = ResourceManager.GetObject("Fullscreen", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Function { + get { + object obj = ResourceManager.GetObject("Function", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap HdPack { + get { + object obj = ResourceManager.GetObject("HdPack", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Help { + get { + object obj = ResourceManager.GetObject("Help", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap HistoryViewer { + get { + object obj = ResourceManager.GetObject("HistoryViewer", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon Icon { + get { + object obj = ResourceManager.GetObject("Icon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Import { + get { + object obj = ResourceManager.GetObject("Import", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap JumpTarget { + get { + object obj = ResourceManager.GetObject("JumpTarget", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap LogWindow { + get { + object obj = ResourceManager.GetObject("LogWindow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MediaEject { + get { + object obj = ResourceManager.GetObject("MediaEject", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MediaPause { + get { + object obj = ResourceManager.GetObject("MediaPause", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MediaPlay { + get { + object obj = ResourceManager.GetObject("MediaPlay", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MediaStop { + get { + object obj = ResourceManager.GetObject("MediaStop", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon MesenIcon { + get { + object obj = ResourceManager.GetObject("MesenIcon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MesenSIconSmall { + get { + object obj = ResourceManager.GetObject("MesenSIconSmall", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MesenSIconSmall1 { + get { + object obj = ResourceManager.GetObject("MesenSIconSmall1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Microphone { + get { + object obj = ResourceManager.GetObject("Microphone", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MoveDown { + get { + object obj = ResourceManager.GetObject("MoveDown", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap MoveUp { + get { + object obj = ResourceManager.GetObject("MoveUp", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Movie { + get { + object obj = ResourceManager.GetObject("Movie", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NavigateBack { + get { + object obj = ResourceManager.GetObject("NavigateBack", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NavigateForward { + get { + object obj = ResourceManager.GetObject("NavigateForward", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NesEventViewer { + get { + object obj = ResourceManager.GetObject("NesEventViewer", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Network { + get { + object obj = ResourceManager.GetObject("Network", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NextArrow { + get { + object obj = ResourceManager.GetObject("NextArrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NextTrack { + get { + object obj = ResourceManager.GetObject("NextTrack", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NudDownArrow { + get { + object obj = ResourceManager.GetObject("NudDownArrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NudDownArrow1 { + get { + object obj = ResourceManager.GetObject("NudDownArrow1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NudDownArrowDarkTheme { + get { + object obj = ResourceManager.GetObject("NudDownArrowDarkTheme", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NudUpArrow { + get { + object obj = ResourceManager.GetObject("NudUpArrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NudUpArrow1 { + get { + object obj = ResourceManager.GetObject("NudUpArrow1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap NudUpArrowDarkTheme { + get { + object obj = ResourceManager.GetObject("NudUpArrowDarkTheme", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Paste { + get { + object obj = ResourceManager.GetObject("Paste", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Pencil { + get { + object obj = ResourceManager.GetObject("Pencil", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PerfTracker { + get { + object obj = ResourceManager.GetObject("PerfTracker", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Pipette { + get { + object obj = ResourceManager.GetObject("Pipette", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PipetteSmall { + get { + object obj = ResourceManager.GetObject("PipetteSmall", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PowerCycle { + get { + object obj = ResourceManager.GetObject("PowerCycle", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PreviousArrow { + get { + object obj = ResourceManager.GetObject("PreviousArrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PrevTrack { + get { + object obj = ResourceManager.GetObject("PrevTrack", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap PsIcon { + get { + object obj = ResourceManager.GetObject("PsIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Record { + get { + object obj = ResourceManager.GetObject("Record", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Refresh { + get { + object obj = ResourceManager.GetObject("Refresh", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap RegisterIcon { + get { + object obj = ResourceManager.GetObject("RegisterIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap RunPpuCycle { + get { + object obj = ResourceManager.GetObject("RunPpuCycle", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap RunPpuFrame { + get { + object obj = ResourceManager.GetObject("RunPpuFrame", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap RunPpuScanline { + get { + object obj = ResourceManager.GetObject("RunPpuScanline", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SaveFloppy { + get { + object obj = ResourceManager.GetObject("SaveFloppy", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Script { + get { + object obj = ResourceManager.GetObject("Script", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SelectAll { + get { + object obj = ResourceManager.GetObject("SelectAll", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Settings { + get { + object obj = ResourceManager.GetObject("Settings", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Speed { + get { + object obj = ResourceManager.GetObject("Speed", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SplitView { + get { + object obj = ResourceManager.GetObject("SplitView", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StepBack { + get { + object obj = ResourceManager.GetObject("StepBack", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StepInto { + get { + object obj = ResourceManager.GetObject("StepInto", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StepOut { + get { + object obj = ResourceManager.GetObject("StepOut", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap StepOver { + get { + object obj = ResourceManager.GetObject("StepOver", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap SwitchView { + get { + object obj = ResourceManager.GetObject("SwitchView", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Tape { + get { + object obj = ResourceManager.GetObject("Tape", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Undo { + get { + object obj = ResourceManager.GetObject("Undo", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap UnidentifiedData { + get { + object obj = ResourceManager.GetObject("UnidentifiedData", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Update { + get { + object obj = ResourceManager.GetObject("Update", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap VerifiedData { + get { + object obj = ResourceManager.GetObject("VerifiedData", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap VerticalLayout { + get { + object obj = ResourceManager.GetObject("VerticalLayout", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap VideoFilter { + get { + object obj = ResourceManager.GetObject("VideoFilter", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap VideoOptions { + get { + object obj = ResourceManager.GetObject("VideoOptions", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap VideoRecorder { + get { + object obj = ResourceManager.GetObject("VideoRecorder", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Warning { + get { + object obj = ResourceManager.GetObject("Warning", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap WasdKeys { + get { + object obj = ResourceManager.GetObject("WasdKeys", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap WebBrowser { + get { + object obj = ResourceManager.GetObject("WebBrowser", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap XbIcon { + get { + object obj = ResourceManager.GetObject("XbIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Zoom2x { + get { + object obj = ResourceManager.GetObject("Zoom2x", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/UI/Properties/Resources.resx b/UI/Properties/Resources.resx new file mode 100644 index 0000000..bfddda1 --- /dev/null +++ b/UI/Properties/Resources.resx @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + ..\Resources\Accept.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ArrowKeys.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Audio.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Barcode.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Breakpoint.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\BreakpointDisabled.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\BreakpointEnableDisable.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Camera.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CheatCode.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Chip.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Close.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\coins.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Collapse.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\CommandLine.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Comment.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Controller.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Copy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Cut.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Debugger.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Dice.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\DipSwitches.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Donate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\DownArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\DownArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\DownArrowDarkTheme.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\DownArrowWin10.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\DownArrowWin10.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\EditLabel.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Enum.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Exclamation.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Exit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Expand.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Export.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Find.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Font.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Fullscreen.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Function.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\HdPack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Help.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\HistoryViewer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Import.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\JumpTarget.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\LogWindow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MediaEject.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MediaPause.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MediaPlay.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MediaStop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MesenSIconSmall.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MesenSIconSmall.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Microphone.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MoveDown.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\MoveUp.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Movie.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NavigateBack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NavigateForward.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NesEventViewer.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Network.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NextArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NextTrack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NudDownArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NudDownArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NudDownArrowDarkTheme.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NudUpArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NudUpArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\NudUpArrowDarkTheme.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Paste.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Pencil.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PerfTracker.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Pipette.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PipetteSmall.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PowerCycle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PreviousArrow.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PrevTrack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\PsIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Record.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Refresh.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\RegisterIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\RunPpuCycle.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\RunPpuFrame.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\RunPpuScanline.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SaveFloppy.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Script.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SelectAll.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Settings.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Speed.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SplitView.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\StepBack.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\StepInto.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\StepOut.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\StepOver.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\SwitchView.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Tape.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Undo.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\UnidentifiedData.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Update.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\VerifiedData.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\VerticalLayout.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\VideoFilter.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\VideoOptions.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\VideoRecorder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Warning.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\WasdKeys.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\WebBrowser.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\XbIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Zoom2x.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/UI/Properties/Settings.Designer.cs b/UI/Properties/Settings.Designer.cs new file mode 100644 index 0000000..cd9f978 --- /dev/null +++ b/UI/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18408 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Mesen.GUI.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/UI/Properties/Settings.settings b/UI/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/UI/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/UI/ResourceExtractor.cs b/UI/ResourceExtractor.cs new file mode 100644 index 0000000..d943b1d --- /dev/null +++ b/UI/ResourceExtractor.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using Mesen.GUI.Config; +using System.IO.Compression; +using System.Security.Cryptography; + +namespace Mesen.GUI +{ + class ResourceExtractor + { + public static string GetSha1Hash(byte[] fileData) + { + using(SHA1Managed sha1 = new SHA1Managed()) { + byte[] hash = sha1.ComputeHash(fileData); + + var sb = new StringBuilder(hash.Length * 2); + foreach(byte b in hash) { + sb.Append(b.ToString("x2")); + } + return sb.ToString(); + } + } + + private static void ExtractFile(ZipArchiveEntry entry, string outputFilename) + { + if(File.Exists(outputFilename)) { + byte[] zipFileData = new byte[entry.Length]; + using(Stream fileStream = entry.Open()) { + fileStream.Read(zipFileData, 0, (int)entry.Length); + } + + string diskFileSha1 = GetSha1Hash(File.ReadAllBytes(outputFilename)); + string zipFileSha1 = GetSha1Hash(zipFileData); + + if(diskFileSha1 != zipFileSha1) { + try { + File.Delete(outputFilename); + } catch { } + try { + File.WriteAllBytes(outputFilename, zipFileData); + } catch { } + } + } else { + try { + //On Mono, using overwrite = true for ExtractToFile crashes/kills any currently running instance that uses the file. + //This is probably a Mono bug? + //Better to attempt a delete & then extract, like now (and like it used to be) + entry.ExtractToFile(outputFilename); + } catch { } + } + } + + public static Stream GetZippedResource(string filename) + { + ZipArchive zip = new ZipArchive(Assembly.GetExecutingAssembly().GetManifestResourceStream("Mesen.GUI.Dependencies.Dependencies.zip")); + foreach(ZipArchiveEntry entry in zip.Entries) { + if(entry.Name == filename) { + return entry.Open(); + } + } + return null; + } + + public static string ReadZippedResource(string filename) + { + ZipArchive zip = new ZipArchive(Assembly.GetExecutingAssembly().GetManifestResourceStream("Mesen.GUI.Dependencies.Dependencies.zip")); + foreach(ZipArchiveEntry entry in zip.Entries) { + string entryFileName = Program.IsMono ? entry.Name.Substring(entry.Name.LastIndexOf('\\') + 1) : entry.Name; + if(entryFileName == filename) { + using(Stream stream = entry.Open()) { + using(StreamReader reader = new StreamReader(stream)) { + return reader.ReadToEnd(); + } + } + } + } + return null; + } + + public static bool ExtractResources() + { + Directory.CreateDirectory(Path.Combine(ConfigManager.HomeFolder, "Resources")); + + ZipArchive zip = new ZipArchive(Assembly.GetExecutingAssembly().GetManifestResourceStream("Mesen.GUI.Dependencies.Dependencies.zip")); + + //Extract all needed files + string suffix = IntPtr.Size == 4 ? ".x86" : ".x64"; + foreach(ZipArchiveEntry entry in zip.Entries) { + if(entry.Name.StartsWith("MesenSCore") && !Program.IsMono && entry.Name.Contains(suffix)) { + string outputFilename = Path.Combine(ConfigManager.HomeFolder, entry.Name.Replace(suffix, "")); + ExtractFile(entry, outputFilename); + } else if(entry.Name.StartsWith("libMesenSCore") && Program.IsMono && entry.Name.Contains(suffix)) { + string outputFilename = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), entry.Name.Replace(suffix, "")); + ExtractFile(entry, outputFilename); + } else if(entry.Name == "MesenUpdater.exe") { + string outputFilename = Path.Combine(ConfigManager.HomeFolder, entry.Name); + ExtractFile(entry, outputFilename); + } else if(entry.Name == "Font.24.spritefont" || entry.Name == "Font.64.spritefont" || entry.Name == "LICENSE.txt" || entry.Name == "PixelFont.ttf") { + string outputFilename = Path.Combine(ConfigManager.HomeFolder, "Resources", entry.Name); + ExtractFile(entry, outputFilename); + } else if(entry.Name == "DroidSansMono.ttf" && Program.IsMono) { + string outputFilename = Path.Combine(ConfigManager.FontFolder, entry.Name); + bool needRestart = !File.Exists(outputFilename); + ExtractFile(entry, outputFilename); + if(needRestart) { + //If font is newly installed, restart Mesen (otherwise debugger will not be able to use the font and display incorrectly) + ConfigManager.RestartMesen(); + return false; + } + } + } + + return true; + } + } +} diff --git a/UI/Resources/Accept.png b/UI/Resources/Accept.png new file mode 100644 index 0000000..b9e2389 Binary files /dev/null and b/UI/Resources/Accept.png differ diff --git a/UI/Resources/Add.png b/UI/Resources/Add.png new file mode 100644 index 0000000..48ef800 Binary files /dev/null and b/UI/Resources/Add.png differ diff --git a/UI/Resources/ArrowKeys.png b/UI/Resources/ArrowKeys.png new file mode 100644 index 0000000..bab2cde Binary files /dev/null and b/UI/Resources/ArrowKeys.png differ diff --git a/UI/Resources/Audio.png b/UI/Resources/Audio.png new file mode 100644 index 0000000..2bd5af9 Binary files /dev/null and b/UI/Resources/Audio.png differ diff --git a/UI/Resources/Barcode.png b/UI/Resources/Barcode.png new file mode 100644 index 0000000..064f2ec Binary files /dev/null and b/UI/Resources/Barcode.png differ diff --git a/UI/Resources/Breakpoint.png b/UI/Resources/Breakpoint.png new file mode 100644 index 0000000..04a95e4 Binary files /dev/null and b/UI/Resources/Breakpoint.png differ diff --git a/UI/Resources/BreakpointDisabled.png b/UI/Resources/BreakpointDisabled.png new file mode 100644 index 0000000..5ce1520 Binary files /dev/null and b/UI/Resources/BreakpointDisabled.png differ diff --git a/UI/Resources/BreakpointEnableDisable.png b/UI/Resources/BreakpointEnableDisable.png new file mode 100644 index 0000000..b934b89 Binary files /dev/null and b/UI/Resources/BreakpointEnableDisable.png differ diff --git a/UI/Resources/Camera.png b/UI/Resources/Camera.png new file mode 100644 index 0000000..1e8e886 Binary files /dev/null and b/UI/Resources/Camera.png differ diff --git a/UI/Resources/CheatCode.png b/UI/Resources/CheatCode.png new file mode 100644 index 0000000..4d9e793 Binary files /dev/null and b/UI/Resources/CheatCode.png differ diff --git a/UI/Resources/Chip.png b/UI/Resources/Chip.png new file mode 100644 index 0000000..13c88ca Binary files /dev/null and b/UI/Resources/Chip.png differ diff --git a/UI/Resources/Close.png b/UI/Resources/Close.png new file mode 100644 index 0000000..f9be619 Binary files /dev/null and b/UI/Resources/Close.png differ diff --git a/UI/Resources/Coins.png b/UI/Resources/Coins.png new file mode 100644 index 0000000..84b94dc Binary files /dev/null and b/UI/Resources/Coins.png differ diff --git a/UI/Resources/Collapse.png b/UI/Resources/Collapse.png new file mode 100644 index 0000000..659cd90 Binary files /dev/null and b/UI/Resources/Collapse.png differ diff --git a/UI/Resources/CommandLine.png b/UI/Resources/CommandLine.png new file mode 100644 index 0000000..4efd07a Binary files /dev/null and b/UI/Resources/CommandLine.png differ diff --git a/UI/Resources/Comment.png b/UI/Resources/Comment.png new file mode 100644 index 0000000..296b830 Binary files /dev/null and b/UI/Resources/Comment.png differ diff --git a/UI/Resources/Controller.png b/UI/Resources/Controller.png new file mode 100644 index 0000000..9d040ee Binary files /dev/null and b/UI/Resources/Controller.png differ diff --git a/UI/Resources/Copy.png b/UI/Resources/Copy.png new file mode 100644 index 0000000..2915370 Binary files /dev/null and b/UI/Resources/Copy.png differ diff --git a/UI/Resources/Cut.png b/UI/Resources/Cut.png new file mode 100644 index 0000000..dc9eb9a Binary files /dev/null and b/UI/Resources/Cut.png differ diff --git a/UI/Resources/Debugger.png b/UI/Resources/Debugger.png new file mode 100644 index 0000000..c406559 Binary files /dev/null and b/UI/Resources/Debugger.png differ diff --git a/UI/Resources/Dice.png b/UI/Resources/Dice.png new file mode 100644 index 0000000..c958bd9 Binary files /dev/null and b/UI/Resources/Dice.png differ diff --git a/UI/Resources/DipSwitches.png b/UI/Resources/DipSwitches.png new file mode 100644 index 0000000..e52c7f3 Binary files /dev/null and b/UI/Resources/DipSwitches.png differ diff --git a/UI/Resources/Donate.png b/UI/Resources/Donate.png new file mode 100644 index 0000000..0765db9 Binary files /dev/null and b/UI/Resources/Donate.png differ diff --git a/UI/Resources/DownArrow.png b/UI/Resources/DownArrow.png new file mode 100644 index 0000000..7e8a559 Binary files /dev/null and b/UI/Resources/DownArrow.png differ diff --git a/UI/Resources/DownArrowDarkTheme.png b/UI/Resources/DownArrowDarkTheme.png new file mode 100644 index 0000000..e8bfc59 Binary files /dev/null and b/UI/Resources/DownArrowDarkTheme.png differ diff --git a/UI/Resources/DownArrowWin10.png b/UI/Resources/DownArrowWin10.png new file mode 100644 index 0000000..6782508 Binary files /dev/null and b/UI/Resources/DownArrowWin10.png differ diff --git a/UI/Resources/Edit.png b/UI/Resources/Edit.png new file mode 100644 index 0000000..c86f032 Binary files /dev/null and b/UI/Resources/Edit.png differ diff --git a/UI/Resources/EditLabel.png b/UI/Resources/EditLabel.png new file mode 100644 index 0000000..452acfe Binary files /dev/null and b/UI/Resources/EditLabel.png differ diff --git a/UI/Resources/Enum.png b/UI/Resources/Enum.png new file mode 100644 index 0000000..be2a585 Binary files /dev/null and b/UI/Resources/Enum.png differ diff --git a/UI/Resources/Exclamation.png b/UI/Resources/Exclamation.png new file mode 100644 index 0000000..466be2a Binary files /dev/null and b/UI/Resources/Exclamation.png differ diff --git a/UI/Resources/Exit.png b/UI/Resources/Exit.png new file mode 100644 index 0000000..0010931 Binary files /dev/null and b/UI/Resources/Exit.png differ diff --git a/UI/Resources/Expand.png b/UI/Resources/Expand.png new file mode 100644 index 0000000..6ef8de7 Binary files /dev/null and b/UI/Resources/Expand.png differ diff --git a/UI/Resources/Export.png b/UI/Resources/Export.png new file mode 100644 index 0000000..2ab77b7 Binary files /dev/null and b/UI/Resources/Export.png differ diff --git a/UI/Resources/Find.png b/UI/Resources/Find.png new file mode 100644 index 0000000..baf7667 Binary files /dev/null and b/UI/Resources/Find.png differ diff --git a/UI/Resources/Folder.png b/UI/Resources/Folder.png new file mode 100644 index 0000000..b67403d Binary files /dev/null and b/UI/Resources/Folder.png differ diff --git a/UI/Resources/Font.png b/UI/Resources/Font.png new file mode 100644 index 0000000..9c09f01 Binary files /dev/null and b/UI/Resources/Font.png differ diff --git a/UI/Resources/Fullscreen.png b/UI/Resources/Fullscreen.png new file mode 100644 index 0000000..ffdabd4 Binary files /dev/null and b/UI/Resources/Fullscreen.png differ diff --git a/UI/Resources/Function.png b/UI/Resources/Function.png new file mode 100644 index 0000000..d99e761 Binary files /dev/null and b/UI/Resources/Function.png differ diff --git a/UI/Resources/HdPack.png b/UI/Resources/HdPack.png new file mode 100644 index 0000000..f6caedb Binary files /dev/null and b/UI/Resources/HdPack.png differ diff --git a/UI/Resources/Help.png b/UI/Resources/Help.png new file mode 100644 index 0000000..1d15c7b Binary files /dev/null and b/UI/Resources/Help.png differ diff --git a/UI/Resources/HistoryViewer.png b/UI/Resources/HistoryViewer.png new file mode 100644 index 0000000..0d2cab1 Binary files /dev/null and b/UI/Resources/HistoryViewer.png differ diff --git a/UI/Resources/Icon.ico b/UI/Resources/Icon.ico new file mode 100644 index 0000000..72c5c76 Binary files /dev/null and b/UI/Resources/Icon.ico differ diff --git a/UI/Resources/Import.png b/UI/Resources/Import.png new file mode 100644 index 0000000..38f8423 Binary files /dev/null and b/UI/Resources/Import.png differ diff --git a/UI/Resources/JumpTarget.png b/UI/Resources/JumpTarget.png new file mode 100644 index 0000000..56cf8d0 Binary files /dev/null and b/UI/Resources/JumpTarget.png differ diff --git a/UI/Resources/LogWindow.png b/UI/Resources/LogWindow.png new file mode 100644 index 0000000..663cbad Binary files /dev/null and b/UI/Resources/LogWindow.png differ diff --git a/UI/Resources/MediaEject.png b/UI/Resources/MediaEject.png new file mode 100644 index 0000000..2084067 Binary files /dev/null and b/UI/Resources/MediaEject.png differ diff --git a/UI/Resources/MediaPause.png b/UI/Resources/MediaPause.png new file mode 100644 index 0000000..c8b4fe2 Binary files /dev/null and b/UI/Resources/MediaPause.png differ diff --git a/UI/Resources/MediaPlay.png b/UI/Resources/MediaPlay.png new file mode 100644 index 0000000..a7de0fe Binary files /dev/null and b/UI/Resources/MediaPlay.png differ diff --git a/UI/Resources/MediaStop.png b/UI/Resources/MediaStop.png new file mode 100644 index 0000000..ede2815 Binary files /dev/null and b/UI/Resources/MediaStop.png differ diff --git a/UI/Resources/MesenSIconSmall.png b/UI/Resources/MesenSIconSmall.png new file mode 100644 index 0000000..0c12b19 Binary files /dev/null and b/UI/Resources/MesenSIconSmall.png differ diff --git a/UI/Resources/Microphone.png b/UI/Resources/Microphone.png new file mode 100644 index 0000000..12db59e Binary files /dev/null and b/UI/Resources/Microphone.png differ diff --git a/UI/Resources/MoveDown.png b/UI/Resources/MoveDown.png new file mode 100644 index 0000000..39730ac Binary files /dev/null and b/UI/Resources/MoveDown.png differ diff --git a/UI/Resources/MoveUp.png b/UI/Resources/MoveUp.png new file mode 100644 index 0000000..75f5913 Binary files /dev/null and b/UI/Resources/MoveUp.png differ diff --git a/UI/Resources/Movie.png b/UI/Resources/Movie.png new file mode 100644 index 0000000..3e4ced5 Binary files /dev/null and b/UI/Resources/Movie.png differ diff --git a/UI/Resources/NavigateBack.png b/UI/Resources/NavigateBack.png new file mode 100644 index 0000000..e45516f Binary files /dev/null and b/UI/Resources/NavigateBack.png differ diff --git a/UI/Resources/NavigateForward.png b/UI/Resources/NavigateForward.png new file mode 100644 index 0000000..293a334 Binary files /dev/null and b/UI/Resources/NavigateForward.png differ diff --git a/UI/Resources/NesEventViewer.png b/UI/Resources/NesEventViewer.png new file mode 100644 index 0000000..73c84d2 Binary files /dev/null and b/UI/Resources/NesEventViewer.png differ diff --git a/UI/Resources/Network.png b/UI/Resources/Network.png new file mode 100644 index 0000000..5c140d8 Binary files /dev/null and b/UI/Resources/Network.png differ diff --git a/UI/Resources/NextArrow.png b/UI/Resources/NextArrow.png new file mode 100644 index 0000000..b04a590 Binary files /dev/null and b/UI/Resources/NextArrow.png differ diff --git a/UI/Resources/NextTrack.png b/UI/Resources/NextTrack.png new file mode 100644 index 0000000..2f4f91f Binary files /dev/null and b/UI/Resources/NextTrack.png differ diff --git a/UI/Resources/NudDownArrow.png b/UI/Resources/NudDownArrow.png new file mode 100644 index 0000000..470dadd Binary files /dev/null and b/UI/Resources/NudDownArrow.png differ diff --git a/UI/Resources/NudDownArrowDarkTheme.png b/UI/Resources/NudDownArrowDarkTheme.png new file mode 100644 index 0000000..a46e190 Binary files /dev/null and b/UI/Resources/NudDownArrowDarkTheme.png differ diff --git a/UI/Resources/NudUpArrow.png b/UI/Resources/NudUpArrow.png new file mode 100644 index 0000000..450060f Binary files /dev/null and b/UI/Resources/NudUpArrow.png differ diff --git a/UI/Resources/NudUpArrowDarkTheme.png b/UI/Resources/NudUpArrowDarkTheme.png new file mode 100644 index 0000000..dac27a5 Binary files /dev/null and b/UI/Resources/NudUpArrowDarkTheme.png differ diff --git a/UI/Resources/Paste.png b/UI/Resources/Paste.png new file mode 100644 index 0000000..24588a3 Binary files /dev/null and b/UI/Resources/Paste.png differ diff --git a/UI/Resources/Pencil.png b/UI/Resources/Pencil.png new file mode 100644 index 0000000..9f18610 Binary files /dev/null and b/UI/Resources/Pencil.png differ diff --git a/UI/Resources/PerfTracker.png b/UI/Resources/PerfTracker.png new file mode 100644 index 0000000..8734e77 Binary files /dev/null and b/UI/Resources/PerfTracker.png differ diff --git a/UI/Resources/Pipette.png b/UI/Resources/Pipette.png new file mode 100644 index 0000000..43738f4 Binary files /dev/null and b/UI/Resources/Pipette.png differ diff --git a/UI/Resources/PipetteSmall.png b/UI/Resources/PipetteSmall.png new file mode 100644 index 0000000..f621ff9 Binary files /dev/null and b/UI/Resources/PipetteSmall.png differ diff --git a/UI/Resources/PowerCycle.png b/UI/Resources/PowerCycle.png new file mode 100644 index 0000000..4e5f7b9 Binary files /dev/null and b/UI/Resources/PowerCycle.png differ diff --git a/UI/Resources/PrevTrack.png b/UI/Resources/PrevTrack.png new file mode 100644 index 0000000..833667a Binary files /dev/null and b/UI/Resources/PrevTrack.png differ diff --git a/UI/Resources/PreviousArrow.png b/UI/Resources/PreviousArrow.png new file mode 100644 index 0000000..0b82c08 Binary files /dev/null and b/UI/Resources/PreviousArrow.png differ diff --git a/UI/Resources/PsIcon.png b/UI/Resources/PsIcon.png new file mode 100644 index 0000000..c2f55fb Binary files /dev/null and b/UI/Resources/PsIcon.png differ diff --git a/UI/Resources/Record.png b/UI/Resources/Record.png new file mode 100644 index 0000000..9e15341 Binary files /dev/null and b/UI/Resources/Record.png differ diff --git a/UI/Resources/Refresh.png b/UI/Resources/Refresh.png new file mode 100644 index 0000000..8201255 Binary files /dev/null and b/UI/Resources/Refresh.png differ diff --git a/UI/Resources/RegisterIcon.png b/UI/Resources/RegisterIcon.png new file mode 100644 index 0000000..a1062b7 Binary files /dev/null and b/UI/Resources/RegisterIcon.png differ diff --git a/UI/Resources/RunPpuCycle.png b/UI/Resources/RunPpuCycle.png new file mode 100644 index 0000000..e52b253 Binary files /dev/null and b/UI/Resources/RunPpuCycle.png differ diff --git a/UI/Resources/RunPpuFrame.png b/UI/Resources/RunPpuFrame.png new file mode 100644 index 0000000..330bf94 Binary files /dev/null and b/UI/Resources/RunPpuFrame.png differ diff --git a/UI/Resources/RunPpuScanline.png b/UI/Resources/RunPpuScanline.png new file mode 100644 index 0000000..9314272 Binary files /dev/null and b/UI/Resources/RunPpuScanline.png differ diff --git a/UI/Resources/SaveFloppy.png b/UI/Resources/SaveFloppy.png new file mode 100644 index 0000000..f1d7a19 Binary files /dev/null and b/UI/Resources/SaveFloppy.png differ diff --git a/UI/Resources/Script.png b/UI/Resources/Script.png new file mode 100644 index 0000000..87f94f8 Binary files /dev/null and b/UI/Resources/Script.png differ diff --git a/UI/Resources/SelectAll.png b/UI/Resources/SelectAll.png new file mode 100644 index 0000000..f4b0b19 Binary files /dev/null and b/UI/Resources/SelectAll.png differ diff --git a/UI/Resources/Settings.png b/UI/Resources/Settings.png new file mode 100644 index 0000000..259ed26 Binary files /dev/null and b/UI/Resources/Settings.png differ diff --git a/UI/Resources/Speed.png b/UI/Resources/Speed.png new file mode 100644 index 0000000..18b7c67 Binary files /dev/null and b/UI/Resources/Speed.png differ diff --git a/UI/Resources/SplitView.png b/UI/Resources/SplitView.png new file mode 100644 index 0000000..ce5e1fe Binary files /dev/null and b/UI/Resources/SplitView.png differ diff --git a/UI/Resources/StepBack.png b/UI/Resources/StepBack.png new file mode 100644 index 0000000..01a9b38 Binary files /dev/null and b/UI/Resources/StepBack.png differ diff --git a/UI/Resources/StepInto.png b/UI/Resources/StepInto.png new file mode 100644 index 0000000..0007dec Binary files /dev/null and b/UI/Resources/StepInto.png differ diff --git a/UI/Resources/StepOut.png b/UI/Resources/StepOut.png new file mode 100644 index 0000000..1af183d Binary files /dev/null and b/UI/Resources/StepOut.png differ diff --git a/UI/Resources/StepOver.png b/UI/Resources/StepOver.png new file mode 100644 index 0000000..bdca721 Binary files /dev/null and b/UI/Resources/StepOver.png differ diff --git a/UI/Resources/SwitchView.png b/UI/Resources/SwitchView.png new file mode 100644 index 0000000..5043147 Binary files /dev/null and b/UI/Resources/SwitchView.png differ diff --git a/UI/Resources/Tape.png b/UI/Resources/Tape.png new file mode 100644 index 0000000..89d20f4 Binary files /dev/null and b/UI/Resources/Tape.png differ diff --git a/UI/Resources/Undo.png b/UI/Resources/Undo.png new file mode 100644 index 0000000..8b0fef9 Binary files /dev/null and b/UI/Resources/Undo.png differ diff --git a/UI/Resources/UnidentifiedData.png b/UI/Resources/UnidentifiedData.png new file mode 100644 index 0000000..85b8469 Binary files /dev/null and b/UI/Resources/UnidentifiedData.png differ diff --git a/UI/Resources/Update.png b/UI/Resources/Update.png new file mode 100644 index 0000000..58f19c6 Binary files /dev/null and b/UI/Resources/Update.png differ diff --git a/UI/Resources/VerifiedData.png b/UI/Resources/VerifiedData.png new file mode 100644 index 0000000..5289bd4 Binary files /dev/null and b/UI/Resources/VerifiedData.png differ diff --git a/UI/Resources/VerticalLayout.png b/UI/Resources/VerticalLayout.png new file mode 100644 index 0000000..e2b9846 Binary files /dev/null and b/UI/Resources/VerticalLayout.png differ diff --git a/UI/Resources/VideoFilter.png b/UI/Resources/VideoFilter.png new file mode 100644 index 0000000..9ecb79a Binary files /dev/null and b/UI/Resources/VideoFilter.png differ diff --git a/UI/Resources/VideoOptions.png b/UI/Resources/VideoOptions.png new file mode 100644 index 0000000..68da502 Binary files /dev/null and b/UI/Resources/VideoOptions.png differ diff --git a/UI/Resources/VideoRecorder.png b/UI/Resources/VideoRecorder.png new file mode 100644 index 0000000..36a62b3 Binary files /dev/null and b/UI/Resources/VideoRecorder.png differ diff --git a/UI/Resources/Warning.png b/UI/Resources/Warning.png new file mode 100644 index 0000000..81e9ed2 Binary files /dev/null and b/UI/Resources/Warning.png differ diff --git a/UI/Resources/WasdKeys.png b/UI/Resources/WasdKeys.png new file mode 100644 index 0000000..00c286f Binary files /dev/null and b/UI/Resources/WasdKeys.png differ diff --git a/UI/Resources/WebBrowser.png b/UI/Resources/WebBrowser.png new file mode 100644 index 0000000..ac5957a Binary files /dev/null and b/UI/Resources/WebBrowser.png differ diff --git a/UI/Resources/XbIcon.png b/UI/Resources/XbIcon.png new file mode 100644 index 0000000..2322f24 Binary files /dev/null and b/UI/Resources/XbIcon.png differ diff --git a/UI/Resources/Zoom2x.png b/UI/Resources/Zoom2x.png new file mode 100644 index 0000000..6efb699 Binary files /dev/null and b/UI/Resources/Zoom2x.png differ diff --git a/UI/RuntimeChecker.cs b/UI/RuntimeChecker.cs new file mode 100644 index 0000000..7e16c76 --- /dev/null +++ b/UI/RuntimeChecker.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using Mesen.GUI.Config; +using Mesen.GUI.Forms; + +namespace Mesen.GUI +{ + class RuntimeChecker + { + public static bool TestDll() + { + try { + return EmuApi.TestDll(); + } catch { + } + + bool dllExists; + if(Program.IsMono) { + dllExists = File.Exists(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "libMesenSCore.dll")); + } else { + dllExists = File.Exists("MesenSCore.dll"); + } + + if(!dllExists) { + MesenMsgBox.Show("UnableToStartMissingFiles", MessageBoxButtons.OK, MessageBoxIcon.Error); + } else { + MesenMsgBox.Show("UnableToStartMissingDependencies", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + return false; + } + } +} diff --git a/UI/SingleInstance.cs b/UI/SingleInstance.cs new file mode 100644 index 0000000..dc43a45 --- /dev/null +++ b/UI/SingleInstance.cs @@ -0,0 +1,189 @@ +using System; +using System.IO; +using System.IO.Pipes; +using System.Threading; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Mesen.GUI +{ + public class ArgumentsReceivedEventArgs : EventArgs + { + public String[] Args { get; set; } + } + + public class SingleInstance : IDisposable + { + /// + /// Taken from flawlesscode.com (website no longer available) + /// + private Mutex _mutex = null; + private bool _firstInstance = false; + private Guid _identifier = Guid.Empty; + private FileStream _fileStream = null; + + /// + /// Enforces single instance for an application. + /// + /// An identifier unique to this application. + public SingleInstance() + { + if(Program.IsMono) { + try { + _fileStream = System.IO.File.Open("mesen.lock", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); + _fileStream.Lock(0, 0); + _firstInstance = true; + } catch { + _firstInstance = false; + } + try { + if(File.Exists("mesen.arguments")) { + File.Delete("mesen.arguments"); + } + } catch { } + } else { + this._identifier = new Guid("{A46606B7-2D1C-4CC5-A52F-43BCAF094AED}"); + this._mutex = new Mutex(true, _identifier.ToString(), out _firstInstance); + } + } + + /// + /// Indicates whether this is the first instance of this application. + /// + public bool FirstInstance { get { return _firstInstance; } } + + /// + /// Passes the given arguments to the first running instance of the application. + /// + /// The arguments to pass. + /// Return true if the operation succeded, false otherwise. + public bool PassArgumentsToFirstInstance(string[] arguments) + { + if(Program.IsMono) { + try { + File.WriteAllText("mesen.arguments", string.Join(Environment.NewLine, arguments)); + } catch { } + + return true; + } else { + try { + using(NamedPipeClientStream client = new NamedPipeClientStream(_identifier.ToString())) { + using(StreamWriter writer = new StreamWriter(client)) { + client.Connect(200); + + foreach(String argument in arguments) { + writer.WriteLine(argument); + } + } + } + return true; + } catch { } + + return false; + } + } + + /// + /// Listens for arguments being passed from successive instances of the applicaiton. + /// + public void ListenForArgumentsFromSuccessiveInstances() + { + Task.Run(() => this.ListenForArguments()); + } + + /// + /// Listens for arguments on a named pipe. + /// + private void ListenForArguments() + { + if(Program.IsMono) { + while(true) { + if(File.Exists("mesen.arguments")) { + try { + string[] arguments = File.ReadAllLines("mesen.arguments"); + ThreadPool.QueueUserWorkItem(new WaitCallback(CallOnArgumentsReceived), arguments); + File.Delete("mesen.arguments"); + } catch { } + } + System.Threading.Thread.Sleep(200); + } + } else { + try { + while(true) { + using(NamedPipeServerStream server = new NamedPipeServerStream(_identifier.ToString())) { + using(StreamReader reader = new StreamReader(server)) { + server.WaitForConnection(); + + List arguments = new List(); + while(server.IsConnected) { + arguments.Add(reader.ReadLine()); + } + + ThreadPool.QueueUserWorkItem(new WaitCallback(CallOnArgumentsReceived), arguments.ToArray()); + } + } + } + } catch(IOException) { + //Pipe was broken + } finally { + Thread.Sleep(10000); + Task.Run(() => this.ListenForArguments()); + } + } + } + + /// + /// Calls the OnArgumentsReceived method casting the state Object to String[]. + /// + /// The arguments to pass. + private void CallOnArgumentsReceived(object state) + { + OnArgumentsReceived((string[])state); + } + + /// + /// Event raised when arguments are received from successive instances. + /// + public event EventHandler ArgumentsReceived; + + /// + /// Fires the ArgumentsReceived event. + /// + /// The arguments to pass with the ArgumentsReceivedEventArgs. + private void OnArgumentsReceived(string[] arguments) + { + if(ArgumentsReceived != null) + ArgumentsReceived(this, new ArgumentsReceivedEventArgs() { Args = arguments }); + } + + #region IDisposable + private Boolean _disposed = false; + + protected virtual void Dispose(bool disposing) + { + if(!_disposed) { + if(_fileStream != null) { + _fileStream.Dispose(); + } + + if(_mutex != null && _firstInstance) { + _mutex.ReleaseMutex(); + _mutex = null; + } + _disposed = true; + } + } + + ~SingleInstance() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + } +} \ No newline at end of file diff --git a/UI/UI.csproj b/UI/UI.csproj new file mode 100644 index 0000000..23eef7c --- /dev/null +++ b/UI/UI.csproj @@ -0,0 +1,558 @@ + + + + + Debug + AnyCPU + {08D83A7E-52A9-451E-A53A-1A7946F8BB77} + WinExe + Properties + Mesen.GUI + MesenS + v4.5 + 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + + + true + ..\bin\x64\Debug\ + DEBUG;TRACE + false + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\x64\Release\ + TRACE + false + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + + + true + ..\bin\x86\Debug\ + DEBUG;TRACE + false + full + x86 + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\x86\Release\ + TRACE + false + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + false + + + true + ..\bin\Any CPU\Debug\ + DEBUG;TRACE + false + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\Any CPU\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\x64\PGO Profile\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\x86\PGO Profile\ + TRACE + false + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\Any CPU\PGO Profile\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + false + + + ..\bin\x64\PGO Profile\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\x86\PGO Profile\ + TRACE + false + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + false + + + ..\bin\Any CPU\PGO Profile\ + TRACE;HIDETESTMENU + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + false + + + false + + + + + + app.manifest + + + bin\x64\Libretro\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Libretro\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\Libretro\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + + + + + + + + + + UserControl + + + Component + + + UserControl + + + ctrlHorizontalTrackbar.cs + + + UserControl + + + ctrlLoadingRom.cs + + + Component + + + Component + + + Component + + + Component + + + UserControl + + + ctrlPathSelection.cs + + + UserControl + + + UserControl + + + ctrlRiskyOption.cs + + + Component + + + UserControl + + + ctrlTrackbar.cs + + + UserControl + + + ctrlRenderer.cs + + + Component + + + Component + + + Component + + + ctrlTextbox.cs + + + UserControl + + + ctrlScrollableTextbox.cs + + + + Form + + + frmTraceLogger.cs + + + + BaseConfigForm.cs + + + Form + + + Form + + + Form + + + + Form + + + frmMain.cs + + + + + + + + + + + + + + + + + + ctrlPathSelection.cs + + + MesenNumericUpDown.cs + + + ctrlHorizontalTrackbar.cs + + + ctrlLoadingRom.cs + + + ctrlRiskyOption.cs + + + ctrlTrackbar.cs + + + ctrlRenderer.cs + + + ctrlScrollableTextbox.cs + + + frmTraceLogger.cs + + + BaseConfigForm.cs + + + BaseForm.cs + + + BaseInputForm.cs + + + frmMain.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + Always + + + Always + + + Always + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + Designer + + + Dependencies\Dependencies.zip + + + + + + False + .NET Framework 3.5 SP1 + false + + + + + mkdir "Dependencies" + +if "x86" == "$(PlatformName)" copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\pgort140.dll" "pgort140.dll" + +if "x64" == "$(PlatformName)" copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64\pgort140.dll" "pgort140.dll" + +if "PGO Optimize" == "$(ConfigurationName)" ( +copy "$(SolutionDir)bin\x86\PGO Profile\MesenCore.dll" "Dependencies\MesenCore.x86.dll" + +copy "$(SolutionDir)bin\x64\PGO Profile\MesenCore.dll" "Dependencies\MesenCore.x64.dll" +) +if NOT "PGO Optimize" == "$(ConfigurationName)" ( +copy "$(SolutionDir)bin\x86\$(ConfigurationName)\MesenCore.dll" "Dependencies\MesenCore.x86.dll" + +copy "$(SolutionDir)bin\x64\$(ConfigurationName)\MesenCore.dll" "Dependencies\MesenCore.x64.dll" +) +copy "MesenUpdater.exe" "Dependencies\MesenUpdater.exe" +call DependencyPacker.exe + + + + \ No newline at end of file diff --git a/UI/Utilities/Md5Helper.cs b/UI/Utilities/Md5Helper.cs new file mode 100644 index 0000000..f5e0a87 --- /dev/null +++ b/UI/Utilities/Md5Helper.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Mesen.GUI.Utilities +{ + public class Md5Helper + { + public static string GetMD5Hash(string filename) + { + if(File.Exists(filename)) { + var md5 = System.Security.Cryptography.MD5.Create(); + return BitConverter.ToString(md5.ComputeHash(File.ReadAllBytes(filename))).Replace("-", ""); + } + return null; + } + } +} diff --git a/UI/Utilities/XmlColor.cs b/UI/Utilities/XmlColor.cs new file mode 100644 index 0000000..0eb87b5 --- /dev/null +++ b/UI/Utilities/XmlColor.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace Mesen.GUI.Utilities +{ + public class XmlColor + { + private Color _color = Color.Black; + + public XmlColor() { } + public XmlColor(Color c) { _color = c; } + + [XmlIgnore] + public Color Color + { + get { return _color; } + set { _color = value; } + } + + public static implicit operator Color(XmlColor x) + { + return x.Color; + } + + public static implicit operator XmlColor(Color c) + { + return new XmlColor(c); + } + + [XmlAttribute] + public string ColorString + { + get { return ColorTranslator.ToHtml(_color); } + set + { + try { + _color = ColorTranslator.FromHtml(value); + } catch(Exception) { + _color = Color.Black; + } + } + } + } +} diff --git a/UI/app.manifest b/UI/app.manifest new file mode 100644 index 0000000..bee14f5 --- /dev/null +++ b/UI/app.manifest @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + +