diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cf15c31..84090a07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -152,6 +152,7 @@ target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::c target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED) if (SANITIZERS) + message("UBSAN AND ASAN: ON") target_compile_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address) target_link_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address) endif () diff --git a/external/parallel-rdp/ParallelRDPWrapper.cpp b/external/parallel-rdp/ParallelRDPWrapper.cpp index 32c33097..3b4bff54 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.cpp +++ b/external/parallel-rdp/ParallelRDPWrapper.cpp @@ -6,14 +6,13 @@ #include #include #include -#include using namespace Vulkan; using namespace RDP; bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; } -void ParallelRDP::SetFramerateUnlocked(bool unlocked) const { +void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const { if (unlocked) { wsi->set_present_mode(PresentMode::UnlockedForceTearing); } else { @@ -24,11 +23,11 @@ void ParallelRDP::SetFramerateUnlocked(bool unlocked) const { Program *fullscreen_quad_program; // Copied and modified from WSI::init_context_from_platform -Util::IntrusivePtr InitVulkanContext(WSIPlatform *platform, unsigned num_thread_indices, +Util::IntrusivePtr InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices, const Context::SystemHandles &system_handles) { VK_ASSERT(platform); - auto instance_ext = platform->get_instance_extensions(); - auto device_ext = platform->get_device_extensions(); + const auto instance_ext = platform->get_instance_extensions(); + const auto device_ext = platform->get_device_extensions(); auto new_context = Util::make_handle(); new_context->set_application_info(platform->get_application_info()); @@ -39,9 +38,9 @@ Util::IntrusivePtr InitVulkanContext(WSIPlatform *platform, unsigned nu panic("Failed to create Vulkan instance.\n"); } - VkSurfaceKHR tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE); + const auto tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE); - bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(), + const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(), CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT); if (tmp_surface) { @@ -61,9 +60,9 @@ void ParallelRDP::LoadWSIPlatform(const std::shared_ptr &wsi_platfo wsi->set_backbuffer_srgb(false); wsi->set_platform(wsi_platform.get()); wsi->set_present_mode(PresentMode::SyncToVBlank); - Context::SystemHandles handles; - if (!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) { + if (constexpr Context::SystemHandles handles; + !wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) { panic("Failed to initialize WSI: init_from_existing_context() failed"); } @@ -97,8 +96,8 @@ void ParallelRDP::Init(const std::shared_ptr &wsiPlatform, fragLayout.sets[0].fp_mask = 1; fragLayout.sets[0].array_size[0] = 1; - auto sizeVert = sizeof(vertex_shader); - auto sizeFrag = sizeof(fragment_shader); + constexpr auto sizeVert = sizeof(vertex_shader); + constexpr auto sizeFrag = sizeof(fragment_shader); fullscreen_quad_program = wsi->get_device().request_program( reinterpret_cast(vertex_shader), sizeVert, reinterpret_cast(fragment_shader), @@ -108,8 +107,8 @@ void ParallelRDP::Init(const std::shared_ptr &wsiPlatform, uintptr_t offset = 0; if (wsi->get_device().get_device_features().supports_external_memory_host) { - size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment; - offset = aligned_rdram & (align - 1); + const size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment; + offset = aligned_rdram & align - 1; aligned_rdram -= offset; } @@ -128,7 +127,7 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr image, cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp); cmd->set_program(fullscreen_quad_program); cmd->set_quad_state(); - auto data = static_cast(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float))); + const auto data = static_cast(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float))); data[0] = -1.0f; data[1] = -3.0f; data[2] = -1.0f; @@ -136,15 +135,15 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr image, data[4] = +3.0f; data[5] = +1.0f; - auto windowSize = windowInfo->get_window_size(); + const auto [x, y] = windowInfo->get_window_size(); - float zoom = std::min(windowSize.x / wsi->get_platform().get_surface_width(), - windowSize.y / wsi->get_platform().get_surface_height()); + const float zoom = std::min(x / static_cast(wsi->get_platform().get_surface_width()), + y / static_cast(wsi->get_platform().get_surface_height())); - float width = (wsi->get_platform().get_surface_width() / windowSize.x) * zoom; - float height = (wsi->get_platform().get_surface_height() / windowSize.y) * zoom; + const float width = static_cast(wsi->get_platform().get_surface_width()) / x * zoom; + const float height = static_cast(wsi->get_platform().get_surface_height()) / y * zoom; - float uniform_data[] = {// Size + const float uniform_data[] = {// Size width, height, // Offset (1.0f - width) * 0.5f, (1.0f - height) * 0.5f}; @@ -190,43 +189,43 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr image) const { wsi->end_frame(); } -void ParallelRDP::UpdateScreen(bool playing) const { - if(playing) { - n64::Core& core = n64::Core::GetInstance(); - n64::VI& vi = core.GetMem().mmio.vi; - command_processor->set_vi_register(VIRegister::Control, vi.status.raw); - command_processor->set_vi_register(VIRegister::Origin, vi.origin); - command_processor->set_vi_register(VIRegister::Width, vi.width); - command_processor->set_vi_register(VIRegister::Intr, vi.intr); - command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current); - command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw); - command_processor->set_vi_register(VIRegister::VSync, vi.vsync); - command_processor->set_vi_register(VIRegister::HSync, vi.hsync); - command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw); - command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw); - command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw); - command_processor->set_vi_register(VIRegister::VBurst, vi.vburst); - command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw); - command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw); - ScanoutOptions opts; - opts.persist_frame_on_invalid_input = true; - opts.vi.aa = true; - opts.vi.scale = true; - opts.vi.dither_filter = true; - opts.vi.divot_filter = true; - opts.vi.gamma_dither = true; - opts.downscale_steps = true; - opts.crop_overscan_pixels = true; - Util::IntrusivePtr image = command_processor->scanout(opts); - UpdateScreen(image); - command_processor->begin_frame_context(); - return; - } - +template <> +void ParallelRDP::UpdateScreen() const { + const n64::VI& vi = n64::Core::GetMem().mmio.vi; + command_processor->set_vi_register(VIRegister::Control, vi.status.raw); + command_processor->set_vi_register(VIRegister::Origin, vi.origin); + command_processor->set_vi_register(VIRegister::Width, vi.width); + command_processor->set_vi_register(VIRegister::Intr, vi.intr); + command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current); + command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw); + command_processor->set_vi_register(VIRegister::VSync, vi.vsync); + command_processor->set_vi_register(VIRegister::HSync, vi.hsync); + command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw); + command_processor->set_vi_register(VIRegister::HStart, vi.hstart.raw); + command_processor->set_vi_register(VIRegister::VStart, vi.vstart.raw); + command_processor->set_vi_register(VIRegister::VBurst, vi.vburst); + command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw); + command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw); + ScanoutOptions opts; + opts.persist_frame_on_invalid_input = true; + opts.vi.aa = true; + opts.vi.scale = true; + opts.vi.dither_filter = true; + opts.vi.divot_filter = true; + opts.vi.gamma_dither = true; + opts.downscale_steps = true; + opts.crop_overscan_pixels = true; + const Util::IntrusivePtr image = command_processor->scanout(opts); + UpdateScreen(image); + command_processor->begin_frame_context(); +} + +template <> +void ParallelRDP::UpdateScreen() const { UpdateScreen(static_cast>(nullptr)); } -void ParallelRDP::EnqueueCommand(int command_length, const u32 *buffer) const { +void ParallelRDP::EnqueueCommand(const int command_length, const u32 *buffer) const { command_processor->enqueue_command(command_length, buffer); } diff --git a/external/parallel-rdp/ParallelRDPWrapper.hpp b/external/parallel-rdp/ParallelRDPWrapper.hpp index d1f56c2d..50db0330 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.hpp +++ b/external/parallel-rdp/ParallelRDPWrapper.hpp @@ -17,11 +17,12 @@ public: void Init(const std::shared_ptr &, const std::shared_ptr &, const u8 *); - - void UpdateScreen(bool = true) const; + + template + void UpdateScreen() const; void EnqueueCommand(int, const u32 *) const; void OnFullSync() const; - bool IsFramerateUnlocked() const; + [[nodiscard]] bool IsFramerateUnlocked() const; void SetFramerateUnlocked(bool) const; std::shared_ptr wsi; diff --git a/src/frontend/ImGuiImpl/ProgressIndicators.hpp b/src/frontend/ImGuiImpl/ProgressIndicators.hpp new file mode 100644 index 00000000..fcd7b747 --- /dev/null +++ b/src/frontend/ImGuiImpl/ProgressIndicators.hpp @@ -0,0 +1,43 @@ +#pragma once +#include +#include + +namespace ImGui { +inline bool Spinner(const char* label, const float radius, const int thickness, const ImU32& color) { + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiContext & g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 pos = window->DC.CursorPos; + const ImVec2 size(radius*2, (radius + style.FramePadding.y)*2); + + const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y)); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, id)) + return false; + + // Render + window->DrawList->PathClear(); + + constexpr int num_segments = 30; + const int start = abs(ImSin(g.Time*1.8f)*(num_segments-5)); + + const float a_min = IM_PI * 2.0f * static_cast(start) / static_cast(num_segments); + constexpr float a_max = IM_PI*2.0f * (static_cast(num_segments) -3) / static_cast(num_segments); + + const auto centre = ImVec2(pos.x+radius, pos.y+radius+style.FramePadding.y); + + for (int i = 0; i < num_segments; i++) { + const float a = a_min + static_cast(i) / static_cast(num_segments) * (a_max - a_min); + window->DrawList->PathLineTo(ImVec2(centre.x + ImCos(a+g.Time*8) * radius, + centre.y + ImSin(a+g.Time*8) * radius)); + } + + window->DrawList->PathStroke(color, false, thickness); + return true; +} +} \ No newline at end of file diff --git a/src/frontend/ImGuiImpl/StatusBar.hpp b/src/frontend/ImGuiImpl/StatusBar.hpp index bbb9c76d..0a3dbf3d 100644 --- a/src/frontend/ImGuiImpl/StatusBar.hpp +++ b/src/frontend/ImGuiImpl/StatusBar.hpp @@ -1,7 +1,6 @@ #pragma once #include #include -#include namespace ImGui { inline bool BeginMainStatusBar() diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index 52ece1a8..969e545e 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -9,6 +10,10 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), settingsWindow(win SDL_InitSubSystem(SDL_INIT_GAMEPAD); SDL_AddGamepadMapping(gamecontrollerdb_str); + + std::thread fileWorker(&KaizenGui::FileWorker, this); + + fileWorker.detach(); } KaizenGui::~KaizenGui() { @@ -16,7 +21,7 @@ KaizenGui::~KaizenGui() { SDL_Quit(); } -void KaizenGui::QueryDevices(SDL_Event event) { +void KaizenGui::QueryDevices(const SDL_Event &event) { switch (event.type) { case SDL_EVENT_GAMEPAD_ADDED: if (!gamepad) { @@ -36,8 +41,8 @@ void KaizenGui::QueryDevices(SDL_Event event) { } } -void KaizenGui::HandleInput(SDL_Event event) { - n64::Core& core = n64::Core::GetInstance(); +void KaizenGui::HandleInput(const SDL_Event &event) { + const n64::Core& core = n64::Core::GetInstance(); n64::PIF &pif = n64::Core::GetMem().mmio.si.pif; switch(event.type) { case SDL_EVENT_GAMEPAD_AXIS_MOTION: @@ -52,7 +57,7 @@ void KaizenGui::HandleInput(SDL_Event event) { float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); if (xclamped < 0) { - xclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MAX)); + xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); } else { xclamped /= SDL_JOYSTICK_AXIS_MAX; } @@ -61,15 +66,15 @@ void KaizenGui::HandleInput(SDL_Event event) { float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); if (yclamped < 0) { - yclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN)); + yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); } else { yclamped /= SDL_JOYSTICK_AXIS_MAX; } yclamped *= 86; - pif.UpdateAxis(0, n64::Controller::Axis::Y, -yclamped); - pif.UpdateAxis(0, n64::Controller::Axis::X, xclamped); + pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); + pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast( xclamped)); } break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: @@ -90,7 +95,7 @@ void KaizenGui::HandleInput(SDL_Event event) { case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { - auto keys = SDL_GetKeyboardState(nullptr); + const auto keys = SDL_GetKeyboardState(nullptr); if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) { fileDialogOpen = true; } @@ -130,15 +135,25 @@ void KaizenGui::HandleInput(SDL_Event event) { pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]); pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]); pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]); - pif.UpdateAxis(0, n64::Controller::Axis::Y, keys[SDL_SCANCODE_UP] ? 86 : keys[SDL_SCANCODE_DOWN] ? -86 : 0); - pif.UpdateAxis(0, n64::Controller::Axis::X, keys[SDL_SCANCODE_LEFT] ? -86 : keys[SDL_SCANCODE_RIGHT] ? 86 : 0); + + if (keys[SDL_SCANCODE_UP]) pif.UpdateAxis(0, n64::Controller::Axis::Y, 86); + else pif.UpdateAxis(0, n64::Controller::Axis::Y, 0); + + if (keys[SDL_SCANCODE_DOWN]) pif.UpdateAxis(0, n64::Controller::Axis::Y, -86); + else pif.UpdateAxis(0, n64::Controller::Axis::Y, 0); + + if (keys[SDL_SCANCODE_LEFT]) pif.UpdateAxis(0, n64::Controller::Axis::X, -86); + else pif.UpdateAxis(0, n64::Controller::Axis::X, 0); + + if (keys[SDL_SCANCODE_RIGHT]) pif.UpdateAxis(0, n64::Controller::Axis::X, 86); + else pif.UpdateAxis(0, n64::Controller::Axis::X, 0); } break; default: break; } } -std::tuple, std::optional> RenderErrorMessageDetails() { +std::pair, std::optional> RenderErrorMessageDetails() { auto lastPC = Util::Error::GetLastPC(); if(lastPC.has_value()) { ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str()); @@ -146,10 +161,10 @@ std::tuple, std::optional> RenderE auto memoryAccess = Util::Error::GetMemoryAccess(); if(memoryAccess.has_value()) { - auto memoryAccessV = memoryAccess.value(); - ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", memoryAccessV.is_write ? "Writing" : "Reading", - (u8)memoryAccessV.size, memoryAccessV.address, - memoryAccessV.is_write ? std::format(" (value = 0x{:X})", memoryAccessV.written_val) : "") + const auto [is_write, size, address, written_val] = memoryAccess.value(); + ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", + static_cast(size), address, + is_write ? std::format(" (value = 0x{:X})", written_val) : "") .c_str()); } @@ -227,7 +242,7 @@ void KaizenGui::RenderUI() { settingsWindow.render(); debugger.render(); - ImVec2 center = ImGui::GetMainViewport()->GetCenter(); + const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) { @@ -258,9 +273,9 @@ void KaizenGui::RenderUI() { RenderErrorMessageDetails(); if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) { - bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - bool chooseAnother = ImGui::Button("Choose another ROM"); + const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); if(ignore || stop || chooseAnother) { Util::Error::SetHandled(); ImGui::CloseCurrentPopup(); @@ -306,10 +321,10 @@ void KaizenGui::RenderUI() { ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str()); auto [lastPC, memoryAccess] = RenderErrorMessageDetails(); - bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); - bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); - bool chooseAnother = ImGui::Button("Choose another ROM"); - bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; + const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine(); + const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine(); + const bool chooseAnother = ImGui::Button("Choose another ROM"); + const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false; if(ignore || stop || chooseAnother || openInDebugger) { Util::Error::SetHandled(); ImGui::CloseCurrentPopup(); @@ -346,6 +361,26 @@ void KaizenGui::RenderUI() { ImGui::EndMainStatusBar(); } + if (shouldDisplaySpinner) { + ImGui::SetNextWindowPos({static_cast(width) * 0.5f, static_cast(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + + ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration); + + ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive])); + ImGui::SameLine(); + + ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f); + ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).stem().c_str()); + ImGui::PopFont(); + + ImGui::End(); + + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + } + ImGui::Render(); if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); @@ -354,20 +389,22 @@ void KaizenGui::RenderUI() { if(fileDialogOpen) { fileDialogOpen = false; - const SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; - SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int filter) { - auto kaizen = (KaizenGui*)userdata; + constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}}; + SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) { + const auto kaizen = static_cast(userdata); if (!filelist) { panic("An error occured: {}", SDL_GetError()); - return; - } else if (!*filelist) { + } + + if (!*filelist) { warn("The user did not select any file."); warn("Most likely, the dialog was canceled."); return; } - kaizen->LoadROM(*filelist); + kaizen->fileToLoad = *filelist; + kaizen->shouldDisplaySpinner = true; }, this, window.getHandle(), filters, 3, nullptr, false); } @@ -375,17 +412,17 @@ void KaizenGui::RenderUI() { return; if(core.romLoaded) { - core.parallel.UpdateScreen(); + core.parallel.UpdateScreen(); return; } - core.parallel.UpdateScreen(false); + core.parallel.UpdateScreen(); } void KaizenGui::LoadROM(const std::string &path) noexcept { n64::Core& core = n64::Core::GetInstance(); core.LoadROM(path); - const auto gameNameDB = core.GetMem().rom.gameNameDB; + const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB; SDL_SetWindowTitle(window.getHandle(), ("Kaizen - " + gameNameDB).c_str()); } @@ -405,11 +442,14 @@ void KaizenGui::run() { case SDL_EVENT_WINDOW_RESTORED: minimized = false; break; + default: } QueryDevices(e); HandleInput(e); } + SDL_GetWindowSize(window.getHandle(), &width, &height); + emuThread.run(); RenderUI(); } diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index a884ac8b..7e9f1c05 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -27,10 +27,23 @@ public: static void LoadTAS(const std::string &path) noexcept; void LoadROM(const std::string &path) noexcept; private: + int width{}, height{}; bool aboutOpen = false; bool fileDialogOpen = false; bool quit = false; + bool shouldDisplaySpinner = false; + std::string fileToLoad; void RenderUI(); - void HandleInput(SDL_Event event); - void QueryDevices(SDL_Event event); + void HandleInput(const SDL_Event &event); + void QueryDevices(const SDL_Event &event); + + [[noreturn]] void FileWorker() { + while (true) { + if (!fileToLoad.empty()) { + LoadROM(fileToLoad); + shouldDisplaySpinner = false; + fileToLoad = ""; + } + } + } }; diff --git a/src/frontend/main.cpp b/src/frontend/main.cpp index 533e6bfa..65833999 100644 --- a/src/frontend/main.cpp +++ b/src/frontend/main.cpp @@ -1,7 +1,7 @@ #include #include -int main(int argc, char **argv) { +int main(const int argc, char **argv) { KaizenGui kaizenGui; cflags::cflags flags; flags.add_string_callback('\0', "rom", [&kaizenGui](const std::string& v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line"); diff --git a/src/utils/ErrorData.hpp b/src/utils/ErrorData.hpp index 54529b39..43de5032 100644 --- a/src/utils/ErrorData.hpp +++ b/src/utils/ErrorData.hpp @@ -14,7 +14,7 @@ struct Error { UNRECOVERABLE, } as_enum; - const char* as_c_str() { + [[nodiscard]] const char* as_c_str() const { switch(as_enum) { case NONE: return ""; case WARN: return "Warning"; @@ -70,7 +70,7 @@ struct Error { } as_enum; - const char* as_c_str() { + [[nodiscard]] const char* as_c_str() const { switch(as_enum) { case SCHEDULER_EOL: return "SCHEDULER_EOL"; case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN"; @@ -108,11 +108,9 @@ struct Error { }; template - void Throw (Severity severity, - Type type, - std::optional lastPC, - std::optional memoryAccess, - const std::format_string fmt, Args... args) { + void Throw (const Severity severity, const Type type, const std::optional lastPC, + const std::optional memoryAccess, + const std::format_string fmt, Args... args) { this->severity = severity; this->lastPC = lastPC; this->memoryAccess = memoryAccess; @@ -128,7 +126,7 @@ struct Error { static std::string& GetError() { return GetInstance().err; } static Severity& GetSeverity() { return GetInstance().severity; } static Type& GetType() { return GetInstance().type; } - static std::optional& GetLastPC() { return GetInstance().lastPC; } + static std::optional& GetLastPC() { return GetInstance().lastPC; } static std::optional& GetMemoryAccess() { return GetInstance().memoryAccess; } static bool IsHandled() { return GetSeverity().as_enum == Severity::NONE; @@ -145,7 +143,7 @@ private: std::string err; Severity severity = {}; Type type = {}; - std::optional lastPC = {}; + std::optional lastPC = {}; std::optional memoryAccess = {}; }; } \ No newline at end of file