diff --git a/Core/APU.cpp b/Core/APU.cpp index 48641aa6..3a296fa1 100644 --- a/Core/APU.cpp +++ b/Core/APU.cpp @@ -3,6 +3,7 @@ #include "stdafx.h" #include "APU.h" #include "CPU.h" +#include "Nes_Apu\apu_snapshot.h" APU* APU::Instance = nullptr; IAudioDevice* APU::AudioDevice = nullptr; @@ -79,3 +80,59 @@ bool APU::Exec(uint32_t executedCycles) } return false; } + +void APU::StreamState(bool saving) +{ + apu_snapshot_t snapshot; + if(saving) { + _apu.save_snapshot(&snapshot); + } + + StreamArray(snapshot.w40xx, 0x14); + Stream(snapshot.w4015); + Stream(snapshot.w4017); + Stream(snapshot.delay); + Stream(snapshot.step); + Stream(snapshot.irq_flag); + + Stream(snapshot.square1.delay); + StreamArray(snapshot.square1.env, 3); + Stream(snapshot.square1.length); + Stream(snapshot.square1.phase); + Stream(snapshot.square1.swp_delay); + Stream(snapshot.square1.swp_reset); + StreamArray(snapshot.square1.unused, 1); + + Stream(snapshot.square2.delay); + StreamArray(snapshot.square2.env, 3); + Stream(snapshot.square2.length); + Stream(snapshot.square2.phase); + Stream(snapshot.square2.swp_delay); + Stream(snapshot.square2.swp_reset); + StreamArray(snapshot.square2.unused, 1); + + Stream(snapshot.triangle.delay); + Stream(snapshot.triangle.length); + Stream(snapshot.triangle.phase); + Stream(snapshot.triangle.linear_counter); + Stream(snapshot.triangle.linear_mode); + + Stream(snapshot.noise.delay); + StreamArray(snapshot.noise.env, 3); + Stream(snapshot.noise.length); + Stream(snapshot.noise.shift_reg); + + Stream(snapshot.dmc.delay); + Stream(snapshot.dmc.remain); + Stream(snapshot.dmc.addr); + Stream(snapshot.dmc.buf); + Stream(snapshot.dmc.bits_remain); + Stream(snapshot.dmc.bits); + Stream(snapshot.dmc.buf_empty); + Stream(snapshot.dmc.silence); + Stream(snapshot.dmc.irq_flag); + + if(!saving) { + _apu.load_snapshot(snapshot); + } +} \ No newline at end of file diff --git a/Core/APU.h b/Core/APU.h index 21fcba34..d1b3af27 100644 --- a/Core/APU.h +++ b/Core/APU.h @@ -4,9 +4,10 @@ #include "MemoryManager.h" #include "IMemoryHandler.h" #include "IAudioDevice.h" +#include "Snapshotable.h" #include "Nes_Apu/Nes_Apu.h" -class APU : public IMemoryHandler +class APU : public IMemoryHandler, public Snapshotable { private: static IAudioDevice* AudioDevice; @@ -23,6 +24,9 @@ class APU : public IMemoryHandler static int DMCRead(void*, cpu_addr_t addr); static void IRQChanged(void* data); + protected: + void StreamState(bool saving); + public: static const uint32_t SampleRate = 44100; static const uint32_t SamplesPerFrame = 44100 / 60; diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h index 9291992b..e8bfc246 100644 --- a/Core/BaseMapper.h +++ b/Core/BaseMapper.h @@ -1,10 +1,11 @@ #pragma once #include "stdafx.h" +#include "Snapshotable.h" #include "IMemoryHandler.h" #include "ROMLoader.h" -class BaseMapper : public IMemoryHandler +class BaseMapper : public IMemoryHandler, public Snapshotable { protected: uint8_t* _prgRAM; @@ -12,6 +13,7 @@ class BaseMapper : public IMemoryHandler uint32_t _prgSize; uint32_t _chrSize; + bool _hasCHRRAM; bool _hasBattery; wstring _romFilename; @@ -20,6 +22,9 @@ class BaseMapper : public IMemoryHandler vector _prgPages; vector _chrPages; + uint32_t* _prgSlotPages; + uint32_t* _chrSlotPages; + uint32_t _chrShift = -1; uint32_t _prgShift = -1; @@ -38,12 +43,14 @@ class BaseMapper : public IMemoryHandler { //std::cout << std::dec << "PRG Slot " << (short)slot << ": " << (short)page << std::endl; _prgPages[slot] = &_prgRAM[(page & (GetPRGPageCount() - 1)) * GetPRGPageSize()]; + _prgSlotPages[slot] = page; } void SelectCHRPage(uint32_t slot, uint32_t page) { //std::cout << std::dec << "CHR Slot " << (short)slot << ": " << (short)page << std::endl; _chrPages[slot] = &_chrRAM[(page & (GetCHRPageCount() - 1)) * GetCHRPageSize()]; + _chrSlotPages[slot] = page; } uint32_t GetPRGSlotCount() @@ -107,6 +114,28 @@ class BaseMapper : public IMemoryHandler return filename; } + protected: + virtual void StreamState(bool saving) + { + StreamArray(_prgSlotPages, GetPRGSlotCount()); + StreamArray(_chrSlotPages, GetCHRSlotCount()); + + Stream(_hasCHRRAM); + if(_hasCHRRAM) { + StreamArray(_chrRAM, BaseMapper::CHRSize); + } + + if(!saving) { + for(int i = GetPRGSlotCount() - 1; i >= 0; i--) { + SelectPRGPage(i, _prgSlotPages[i]); + } + + for(int i = GetCHRSlotCount() - 1; i >= 0; i--) { + SelectCHRPage(i, _chrSlotPages[i]); + } + } + } + public: void Initialize(ROMLoader &romLoader) { @@ -119,6 +148,7 @@ class BaseMapper : public IMemoryHandler _romFilename = romLoader.GetFilename(); if(_chrSize == 0) { + _hasCHRRAM = true; _chrRAM = new uint8_t[BaseMapper::CHRSize]; _chrSize = BaseMapper::CHRSize; } @@ -131,6 +161,9 @@ class BaseMapper : public IMemoryHandler _chrPages.push_back(nullptr); } + _prgSlotPages = new uint32_t[GetPRGSlotCount()]; + _chrSlotPages = new uint32_t[GetCHRSlotCount()]; + InitMapper(); } diff --git a/Core/CPU.cpp b/Core/CPU.cpp index 3ba80969..ead4d97e 100644 --- a/Core/CPU.cpp +++ b/Core/CPU.cpp @@ -135,3 +135,19 @@ uint32_t CPU::Exec() CPU::CycleCount += executedCycles; return executedCycles + GetCyclePenalty(); } + +void CPU::StreamState(bool saving) +{ + Stream(_state.PC); + Stream(_state.SP); + Stream(_state.A); + Stream(_state.X); + Stream(_state.Y); + + Stream(CPU::CycleCount); + Stream(CPU::NMIFlag); + Stream(CPU::IRQFlag); + + Stream(_runNMI); + Stream(_runIRQ); +} \ No newline at end of file diff --git a/Core/CPU.h b/Core/CPU.h index 611dbf58..59aeb45e 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -2,6 +2,7 @@ #include "stdafx.h" #include "MemoryManager.h" +#include "Snapshotable.h" namespace PSFlags { @@ -35,7 +36,7 @@ struct State uint8_t PS; }; -class CPU +class CPU : public Snapshotable { private: const uint16_t NMIVector = 0xFFFA; @@ -638,6 +639,9 @@ private: } #pragma endregion +protected: + void StreamState(bool saving); + public: static const uint32_t ClockRate = 1789773; diff --git a/Core/Console.cpp b/Core/Console.cpp index f0b92e4d..28e76ea8 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -94,10 +94,15 @@ void Console::Run() uint32_t frameCount = _ppu->GetFrameCount(); Console::CurrentFPS = (int)((frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000)); lastFrameCount = frameCount; - //std::cout << Console::CurrentFPS << std::endl; fpsTimer.Reset(); } + if(!_saveStateFilename.empty()) { + SaveState(); + } else if(!_loadStateFilename.empty()) { + LoadState(); + } + if(_stop) { _stop = false; break; @@ -114,6 +119,48 @@ void Console::Run() } } +void Console::SaveState(wstring filename) +{ + _saveStateFilename = filename; +} + +void Console::SaveState() +{ + ofstream file(_saveStateFilename, ios::out | ios::binary); + + if(file) { + _cpu->SaveSnapshot(&file); + _ppu->SaveSnapshot(&file); + _memoryManager->SaveSnapshot(&file); + _mapper->SaveSnapshot(&file); + _apu->SaveSnapshot(&file); + file.close(); + } + + _saveStateFilename.clear(); +} + +void Console::LoadState(wstring filename) +{ + _loadStateFilename = filename; +} + +void Console::LoadState() +{ + ifstream file(_loadStateFilename, ios::out | ios::binary); + + if(file) { + _cpu->LoadSnapshot(&file); + _ppu->LoadSnapshot(&file); + _memoryManager->LoadSnapshot(&file); + _mapper->LoadSnapshot(&file); + _apu->LoadSnapshot(&file); + file.close(); + } + + _loadStateFilename.clear(); +} + bool Console::RunTest(uint8_t *expectedResult) { Timer timer; diff --git a/Core/Console.h b/Core/Console.h index c0feae65..7b5f0b98 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -31,6 +31,12 @@ class Console bool _stop = false; bool _reset = false; + wstring _loadStateFilename; + wstring _saveStateFilename; + + void SaveState(); + void LoadState(); + void ResetComponents(bool softReset); public: @@ -43,6 +49,9 @@ class Console bool RunTest(uint8_t* expectedResult); void SaveTestResult(); + void SaveState(wstring filename); + void LoadState(wstring filename); + static bool CheckFlag(int flag); static void SetFlags(int flags); static void ClearFlags(int flags); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index fd1345d9..e68e9e99 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -95,6 +95,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index eec300a2..79b97663 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -117,6 +117,9 @@ Header Files\Mappers + + Header Files\Interfaces + diff --git a/Core/MMC1.h b/Core/MMC1.h index 959402a3..f14dadcf 100644 --- a/Core/MMC1.h +++ b/Core/MMC1.h @@ -129,6 +129,28 @@ class MMC1 : public BaseMapper } protected: + void StreamState(bool saving) + { + Stream(_state.Reg8000); + Stream(_state.RegA000); + Stream(_state.RegC000); + Stream(_state.RegE000); + + Stream(_writeBuffer); + Stream(_shiftCount); + + Stream(_wramDisable); + Stream(_chrMode); + Stream(_prgMode); + Stream(_slotSelect); + + Stream(_chrReg0); + Stream(_chrReg1); + Stream(_prgReg); + + BaseMapper::StreamState(saving); + } + virtual uint32_t GetPRGPageSize() { return 0x4000; } virtual uint32_t GetCHRPageSize() { return 0x1000; } diff --git a/Core/MMC3.h b/Core/MMC3.h index 3360173a..24c4d7a9 100644 --- a/Core/MMC3.h +++ b/Core/MMC3.h @@ -26,7 +26,8 @@ class MMC3 : public BaseMapper bool _irqReload; bool _irqEnabled; - int32_t _lastPPUCycle; + uint32_t _lastCycle; + uint32_t _cyclesDown; bool _wramEnabled; bool _wramWriteProtected; @@ -51,7 +52,8 @@ class MMC3 : public BaseMapper _irqReloadValue = 0; _irqReload = false; _irqEnabled = false; - _lastPPUCycle = 0xFFFF; + _lastCycle = 0xFFFF; + _cyclesDown = 0; _wramEnabled = false; _wramWriteProtected = false; @@ -107,6 +109,32 @@ class MMC3 : public BaseMapper } protected: + void StreamState(bool saving) + { + Stream(_state.Reg8000); + Stream(_state.RegA000); + Stream(_state.RegA001); + + Stream(_currentRegister); + StreamArray(_registers, 8); + Stream(_chrMode); + Stream(_prgMode); + + Stream(_irqReloadValue); + Stream(_irqCounter); + Stream(_irqReload); + + Stream(_irqEnabled); + Stream(_lastCycle); + Stream(_cyclesDown); + + Stream(_wramEnabled); + Stream(_wramWriteProtected); + + BaseMapper::StreamState(saving); + } + + virtual uint32_t GetPRGPageSize() { return 0x2000; } virtual uint32_t GetCHRPageSize() { return 0x0400; } @@ -168,8 +196,6 @@ class MMC3 : public BaseMapper } } - uint32_t _lastCycle = 0xFFFF; - uint32_t _cyclesDown = 0; virtual void NotifyVRAMAddressChange(uint16_t addr) { uint16_t cycle = PPU::GetCurrentCycle(); diff --git a/Core/MemoryManager.cpp b/Core/MemoryManager.cpp index 526889e3..20a25626 100644 --- a/Core/MemoryManager.cpp +++ b/Core/MemoryManager.cpp @@ -8,7 +8,7 @@ MemoryManager::MemoryManager(shared_ptr mapper) _internalRAM = new uint8_t[InternalRAMSize]; _SRAM = new uint8_t[SRAMSize]; _videoRAM = new uint8_t[VRAMSize]; - _expansionRAM = new uint8_t[0x2000]; + _expansionRAM = new uint8_t[ExpansionRAMSize]; _ramReadHandlers = new IMemoryHandler*[RAMSize]; _ramWriteHandlers = new IMemoryHandler*[RAMSize]; @@ -18,7 +18,7 @@ MemoryManager::MemoryManager(shared_ptr mapper) memset(_internalRAM, 0, InternalRAMSize); memset(_SRAM, 0, SRAMSize); memset(_videoRAM, 0, VRAMSize); - memset(_expansionRAM, 0, 0x2000); + memset(_expansionRAM, 0, ExpansionRAMSize); memset(_ramReadHandlers, 0, RAMSize * sizeof(IMemoryHandler*)); memset(_ramWriteHandlers, 0, RAMSize * sizeof(IMemoryHandler*)); @@ -213,4 +213,12 @@ void MemoryManager::WriteVRAM(uint16_t addr, uint8_t value) throw exception("Not implemented yet"); } } +} + +void MemoryManager::StreamState(bool saving) +{ + StreamArray(_internalRAM, MemoryManager::InternalRAMSize); + StreamArray(_expansionRAM, MemoryManager::ExpansionRAMSize); + StreamArray(_SRAM, MemoryManager::SRAMSize); + StreamArray(_videoRAM, MemoryManager::VRAMSize); } \ No newline at end of file diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index 88aa6f18..c8f77e6a 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -4,12 +4,14 @@ #include "IMemoryHandler.h" #include "ROMLoader.h" #include "BaseMapper.h" +#include "Snapshotable.h" -class MemoryManager +class MemoryManager: public Snapshotable { private: const int RAMSize = 0x10000; const int InternalRAMSize = 0x800; + const int ExpansionRAMSize = 0x2000; const int SRAMSize = 0x2000; const int VRAMSize = 0x4000; @@ -31,6 +33,9 @@ class MemoryManager uint8_t ReadMappedVRAM(uint16_t addr); void WriteMappedVRAM(uint16_t addr, uint8_t value); + protected: + void StreamState(bool saving); + public: MemoryManager(shared_ptr mapper); ~MemoryManager(); diff --git a/Core/PPU.cpp b/Core/PPU.cpp index 25eaec52..2a00f25c 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -644,4 +644,57 @@ void PPU::Exec() gap--; } +} + +void PPU::StreamState(bool saving) +{ + Stream(_state.Control); + Stream(_state.Mask); + Stream(_state.Status); + Stream(_state.SpriteRamAddr); + Stream(_state.VideoRamAddr); + Stream(_state.XScroll); + Stream(_state.TmpVideoRamAddr); + Stream(_state.WriteToggle); + Stream(_state.HighBitShift); + Stream(_state.LowBitShift); + + Stream(_scanline); + Stream(_cycle); + Stream(_frameCount); + Stream(_cycleCount); + Stream(_memoryReadBuffer); + + StreamArray(_paletteRAM, 0x100); + StreamArray(_spriteRAM, 0x100); + StreamArray(_secondarySpriteRAM, 0x20); + + Stream(_currentTile.LowByte); + Stream(_currentTile.HighByte); + Stream(_currentTile.PaletteOffset); + + Stream(_nextTile.LowByte); + Stream(_nextTile.HighByte); + Stream(_nextTile.PaletteOffset); + + Stream(_previousTile.LowByte); + Stream(_previousTile.HighByte); + Stream(_previousTile.PaletteOffset); + + StreamArray(_spriteX, 0x8); + for(int i = 0; i < 8; i++) { + Stream(_spriteTiles[i].LowByte); + Stream(_spriteTiles[i].HighByte); + Stream(_spriteTiles[i].PaletteOffset); + Stream(_spriteTiles[i].HorizontalMirror); + Stream(_spriteTiles[i].BackgroundPriority); + } + Stream(_spriteCount); + Stream(_secondaryOAMAddr); + Stream(_sprite0Visible); + + if(!saving) { + SetControlRegister(_state.Control); + SetMaskRegister(_state.Mask); + } } \ No newline at end of file diff --git a/Core/PPU.h b/Core/PPU.h index 35d195ec..2649ab26 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -1,6 +1,7 @@ #pragma once #include "stdafx.h" +#include "Snapshotable.h" #include "MemoryManager.h" #include "IVideoDevice.h" @@ -73,7 +74,7 @@ struct SpriteInfo bool BackgroundPriority; }; -class PPU : public IMemoryHandler +class PPU : public IMemoryHandler, public Snapshotable { private: static PPU* Instance; @@ -157,6 +158,9 @@ class PPU : public IMemoryHandler } } + protected: + void StreamState(bool saving); + public: PPU(MemoryManager *memoryManager); ~PPU(); diff --git a/Core/Snapshotable.h b/Core/Snapshotable.h new file mode 100644 index 00000000..e6acb734 --- /dev/null +++ b/Core/Snapshotable.h @@ -0,0 +1,71 @@ +#pragma once + +#include "stdafx.h" + +class Snapshotable +{ + uint8_t* _stream; + uint32_t _position; + bool _saving; + + protected: + virtual void StreamState(bool saving) = 0; + + template + void Stream(T &value) + { + if(_saving) { + uint8_t* bytes = (uint8_t*)&value; + int typeSize = sizeof(T); + for(int i = 0; i < typeSize; i++) { + _stream[_position++] = bytes[i]; + } + } else { + value = *((T*)(_stream + _position)); + _position += sizeof(T); + } + } + + template + void StreamArray(T* value, uint32_t length) + { + uint32_t typeSize = sizeof(*value); + if(_saving) { + uint8_t* bytes = (uint8_t*)value; + for(uint32_t i = 0, len = length*typeSize; i < len; i++) { + _stream[_position++] = bytes[i]; + } + } else { + for(uint32_t i = 0; i < length*typeSize; i++) { + ((uint8_t*)value)[i] = _stream[_position]; + _position++; + } + } + } + + public: + void SaveSnapshot(ofstream* file) + { + _stream = new uint8_t[0xFFFF]; + memset((char*)_stream, 0, 0xFFFF); + _position = 0; + _saving = true; + + StreamState(_saving); + file->write((char*)_stream, 0xFFFF); + + delete[] _stream; + } + + void LoadSnapshot(ifstream* file) + { + _stream = new uint8_t[0xFFFF]; + _position = 0; + _saving = false; + + file->read((char*)_stream, 0xFFFF); + StreamState(_saving); + + delete[] _stream; + } +}; \ No newline at end of file diff --git a/GUI/GUI.rc b/GUI/GUI.rc index 8bf2fbee..23b32428 100644 Binary files a/GUI/GUI.rc and b/GUI/GUI.rc differ diff --git a/GUI/MainWindow.cpp b/GUI/MainWindow.cpp index aa1e21a4..4bf4f8d8 100644 --- a/GUI/MainWindow.cpp +++ b/GUI/MainWindow.cpp @@ -195,6 +195,10 @@ namespace NES { SetMenuEnabled(ID_NES_STOP, true); SetMenuEnabled(ID_NES_RESUME, false); + SetMenuEnabled(ID_FILE_QUICKLOAD, true); + + SetMenuEnabled(ID_FILE_QUICKSAVE, true); + _renderer->ClearFlags(UIFlags::ShowPauseScreen); if(IsMenuChecked(ID_OPTIONS_SHOWFPS)) { _renderer->SetFlags(UIFlags::ShowFPS); @@ -223,6 +227,8 @@ namespace NES { SetMenuEnabled(ID_NES_PAUSE, false); SetMenuEnabled(ID_NES_RESET, !powerOff); SetMenuEnabled(ID_NES_STOP, !powerOff); + SetMenuEnabled(ID_FILE_QUICKLOAD, !powerOff); + SetMenuEnabled(ID_FILE_QUICKSAVE, !powerOff); SetMenuEnabled(ID_NES_RESUME, true); } @@ -360,6 +366,14 @@ namespace NES { mainWindow->Start(filename); } break; + case ID_FILE_QUICKLOAD: + mainWindow->_console->LoadState(mainWindow->_currentROM + L".svs"); + mainWindow->_renderer->DisplayMessage(L"State loaded.", 3000); + break; + case ID_FILE_QUICKSAVE: + mainWindow->_console->SaveState(mainWindow->_currentROM + L".svs"); + mainWindow->_renderer->DisplayMessage(L"State saved.", 3000); + break; case ID_FILE_EXIT: DestroyWindow(hWnd); break; diff --git a/GUI/Renderer.cpp b/GUI/Renderer.cpp index 756508e6..69f838f7 100644 --- a/GUI/Renderer.cpp +++ b/GUI/Renderer.cpp @@ -234,6 +234,12 @@ namespace NES return shaderResourceView; } + void Renderer::DisplayMessage(wstring text, uint32_t duration) + { + _displayMessage = text; + _displayTimestamp = timeGetTime() + duration; + } + void Renderer::DrawNESScreen() { RECT sourceRect; @@ -309,7 +315,12 @@ namespace NES //Draw FPS counter if(CheckFlag(UIFlags::ShowFPS)) { _font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 149, 13), Colors::Black, 0.0f, XMFLOAT2(0, 0), 1.0f); - _font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 150, 11), Colors::Yellow, 0.0f, XMFLOAT2(0, 0), 1.0f); + _font->DrawString(_spriteBatch.get(), (wstring(L"FPS: ") + std::to_wstring(Console::GetFPS())).c_str(), XMFLOAT2(256 * 4 - 150, 11), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 1.0f); + } + + if(!_displayMessage.empty() && _displayTimestamp > timeGetTime()) { + _font->DrawString(_spriteBatch.get(), _displayMessage.c_str(), XMFLOAT2(12, 13), Colors::Black, 0.0f, XMFLOAT2(0, 0), 1.0f); + _font->DrawString(_spriteBatch.get(), _displayMessage.c_str(), XMFLOAT2(11, 11), Colors::AntiqueWhite, 0.0f, XMFLOAT2(0, 0), 1.0f); } if(CheckFlag(UIFlags::ShowPauseScreen)) { diff --git a/GUI/Renderer.h b/GUI/Renderer.h index bfd78ed1..d33ff8bc 100644 --- a/GUI/Renderer.h +++ b/GUI/Renderer.h @@ -39,6 +39,9 @@ namespace NES { uint32_t _flags = 0; + wstring _displayMessage = L""; + uint32_t _displayTimestamp = 0; + HRESULT InitDevice(); void CleanupDevice(); @@ -51,6 +54,8 @@ namespace NES { ~Renderer(); void Render(); + + void Renderer::DisplayMessage(wstring text, uint32_t duration); void SetFlags(uint32_t flags) { diff --git a/GUI/resource.h b/GUI/resource.h index 5bb7ba70..5bc0a17e 100644 Binary files a/GUI/resource.h and b/GUI/resource.h differ