diff --git a/src/core/settings.cpp b/src/core/settings.cpp index fa18e9632..0d128888c 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -22,8 +22,8 @@ void Settings::SetDefaults() bios_path = "scph1001.bin"; - memory_card_a_filename = "memory_card_a.mcd"; - memory_card_b_filename.clear(); + memory_card_a_path = "memory_card_a.mcd"; + memory_card_b_path.clear(); } void Settings::Load(const char* filename) @@ -46,8 +46,8 @@ void Settings::Load(const char* filename) bios_path = ini.GetValue("BIOS", "Path", "scph1001.bin"); - memory_card_a_filename = ini.GetValue("MemoryCard", "CardAPath", "memory_card_a.mcd"); - memory_card_b_filename = ini.GetValue("MemoryCard", "CardBPath", ""); + memory_card_a_path = ini.GetValue("MemoryCard", "CardAPath", "memory_card_a.mcd"); + memory_card_b_path = ini.GetValue("MemoryCard", "CardBPath", ""); } bool Settings::Save(const char* filename) const @@ -67,13 +67,13 @@ bool Settings::Save(const char* filename) const ini.SetValue("BIOS", "Path", bios_path.c_str()); - if (!memory_card_a_filename.empty()) - ini.SetValue("MemoryCard", "CardAPath", memory_card_a_filename.c_str()); + if (!memory_card_a_path.empty()) + ini.SetValue("MemoryCard", "CardAPath", memory_card_a_path.c_str()); else ini.DeleteValue("MemoryCard", "CardAPath", nullptr); - if (!memory_card_b_filename.empty()) - ini.SetValue("MemoryCard", "CardBPath", memory_card_b_filename.c_str()); + if (!memory_card_b_path.empty()) + ini.SetValue("MemoryCard", "CardBPath", memory_card_b_path.c_str()); else ini.DeleteValue("MemoryCard", "CardBPath", nullptr); @@ -82,6 +82,8 @@ bool Settings::Save(const char* filename) const } static std::array s_gpu_renderer_names = {{"D3D11", "OpenGL", "Software"}}; +static std::array s_gpu_renderer_display_names = { + {"Hardware (D3D11)", "Hardware (OpenGL)", "Software"}}; std::optional Settings::ParseRendererName(const char* str) { @@ -101,3 +103,8 @@ const char* Settings::GetRendererName(GPURenderer renderer) { return s_gpu_renderer_names[static_cast(renderer)]; } + +const char* Settings::GetRendererDisplayName(GPURenderer renderer) +{ + return s_gpu_renderer_display_names[static_cast(renderer)]; +} diff --git a/src/core/settings.h b/src/core/settings.h index 6f070f75d..62d18f205 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -8,12 +8,14 @@ struct Settings { HardwareD3D11, HardwareOpenGL, - Software + Software, + Count }; Settings(); bool start_paused = false; + bool speed_limiter_enabled = true; GPURenderer gpu_renderer = GPURenderer::Software; u32 gpu_resolution_scale = 1; @@ -21,6 +23,7 @@ struct Settings bool gpu_vsync = true; bool gpu_true_color = false; bool display_linear_filtering = true; + bool display_fullscreen = false; struct DebugSettings { @@ -38,8 +41,8 @@ struct Settings // TODO: Controllers, memory cards, etc. std::string bios_path; - std::string memory_card_a_filename; - std::string memory_card_b_filename; + std::string memory_card_a_path; + std::string memory_card_b_path; void SetDefaults(); void Load(const char* filename); @@ -47,4 +50,5 @@ struct Settings static std::optional ParseRendererName(const char* str); static const char* GetRendererName(GPURenderer renderer); + static const char* GetRendererDisplayName(GPURenderer renderer); }; diff --git a/src/core/system.cpp b/src/core/system.cpp index 5a26cc82d..6cf3c3386 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -376,16 +376,16 @@ void System::UpdateMemoryCards() m_pad->SetMemoryCard(0, nullptr); m_pad->SetMemoryCard(1, nullptr); - if (!m_settings.memory_card_a_filename.empty()) + if (!m_settings.memory_card_a_path.empty()) { - std::shared_ptr card = MemoryCard::Open(this, m_settings.memory_card_a_filename); + std::shared_ptr card = MemoryCard::Open(this, m_settings.memory_card_a_path); if (card) m_pad->SetMemoryCard(0, std::move(card)); } - if (!m_settings.memory_card_b_filename.empty()) + if (!m_settings.memory_card_b_path.empty()) { - std::shared_ptr card = MemoryCard::Open(this, m_settings.memory_card_b_filename); + std::shared_ptr card = MemoryCard::Open(this, m_settings.memory_card_b_path); if (card) m_pad->SetMemoryCard(1, std::move(card)); } diff --git a/src/duckstation/sdl_host_interface.cpp b/src/duckstation/sdl_host_interface.cpp index e5fec5ec0..70e6020a4 100644 --- a/src/duckstation/sdl_host_interface.cpp +++ b/src/duckstation/sdl_host_interface.cpp @@ -21,14 +21,10 @@ #include #include #include +#include #include Log_SetChannel(SDLHostInterface); -static constexpr std::array, 3> s_gpu_renderer_names = { - {{Settings::GPURenderer::HardwareD3D11, "Hardware (Direct3D 11)"}, - {Settings::GPURenderer::HardwareOpenGL, "Hardware (OpenGL)"}, - {Settings::GPURenderer::Software, "Software"}}}; - SDLHostInterface::SDLHostInterface() = default; SDLHostInterface::~SDLHostInterface() @@ -111,7 +107,8 @@ bool SDLHostInterface::CreateAudioStream() void SDLHostInterface::UpdateAudioVisualSync() { - const bool speed_limiter_enabled = m_speed_limiter_enabled && !m_speed_limiter_temp_disabled; + const bool speed_limiter_enabled = + !m_system || (m_system->GetSettings().speed_limiter_enabled && !m_speed_limiter_temp_disabled); const bool audio_sync_enabled = speed_limiter_enabled; const bool vsync_enabled = !m_system || (speed_limiter_enabled && m_system->GetSettings().gpu_vsync); Log_InfoPrintf("Syncing to %s%s", audio_sync_enabled ? "audio" : "", @@ -182,6 +179,8 @@ void SDLHostInterface::ResetPerformanceCounters() m_fps_timer.Reset(); } +void SDLHostInterface::SwitchGPURenderer() {} + void SDLHostInterface::ShutdownSystem() { m_system.reset(); @@ -518,9 +517,10 @@ void SDLHostInterface::HandleSDLKeyEvent(const SDL_Event* event) { if (pressed && !repeat && m_system) { - m_speed_limiter_enabled = !m_speed_limiter_enabled; + m_system->GetSettings().speed_limiter_enabled = !m_system->GetSettings().speed_limiter_enabled; UpdateAudioVisualSync(); - AddOSDMessage(m_speed_limiter_enabled ? "Speed limiter enabled." : "Speed limiter disabled."); + AddOSDMessage(m_system->GetSettings().speed_limiter_enabled ? "Speed limiter enabled." : + "Speed limiter disabled."); } } break; @@ -558,6 +558,9 @@ void SDLHostInterface::DrawImGui() else DrawPoweredOffWindow(); + if (m_settings_window_open) + DrawSettingsWindow(); + if (m_about_window_open) DrawAboutWindow(); @@ -637,8 +640,11 @@ void SDLHostInterface::DrawMainMenuBar() if (ImGui::BeginMenu("Settings")) { - if (ImGui::MenuItem("Enable Speed Limiter", nullptr, &m_speed_limiter_enabled, system_enabled)) + Settings& settings = m_system ? m_system->GetSettings() : m_settings; + if (ImGui::MenuItem("Enable Speed Limiter", nullptr, &settings.speed_limiter_enabled, system_enabled)) + { UpdateAudioVisualSync(); + } ImGui::Separator(); @@ -652,11 +658,12 @@ void SDLHostInterface::DrawMainMenuBar() if (ImGui::BeginMenu("Renderer")) { const Settings::GPURenderer current = m_system->GetSettings().gpu_renderer; - for (const auto& it : s_gpu_renderer_names) + for (u32 i = 0; i < static_cast(Settings::GPURenderer::Count); i++) { - if (ImGui::MenuItem(it.second, nullptr, current == it.first)) + if (ImGui::MenuItem(Settings::GetRendererDisplayName(static_cast(i)), nullptr, + i == static_cast(current))) { - m_system->GetSettings().gpu_renderer = it.first; + m_system->GetSettings().gpu_renderer = static_cast(i); m_system->RecreateGPU(); } } @@ -810,7 +817,8 @@ void SDLHostInterface::DrawPoweredOffWindow() ImGui::NewLine(); ImGui::SetCursorPosX(button_left); - ImGui::Button("Settings", button_size); + if (ImGui::Button("Settings", button_size)) + m_settings_window_open = true; ImGui::NewLine(); ImGui::SetCursorPosX(button_left); @@ -825,11 +833,178 @@ void SDLHostInterface::DrawPoweredOffWindow() ImGui::End(); } +static bool DrawSettingsSectionHeader(const char* title) +{ + return ImGui::CollapsingHeader(title, ImGuiTreeNodeFlags_DefaultOpen /* | ImGuiTreeNodeFlags_Leaf*/); +} + +void SDLHostInterface::DrawSettingsWindow() +{ + ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f), + ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); + ImGui::SetNextWindowSize(ImVec2(500, 400), ImGuiCond_FirstUseEver); + + if (!ImGui::Begin("Settings", &m_settings_window_open, ImGuiWindowFlags_NoResize)) + { + ImGui::End(); + return; + } + + Settings& settings = m_system ? m_system->GetSettings() : m_settings; + bool settings_changed = false; + bool gpu_settings_changed = false; + + if (ImGui::BeginTabBar("SettingsTabBar", 0)) + { + const float indent = 150.0f; + + if (ImGui::BeginTabItem("General")) + { + ImGui::Text("Region:"); + ImGui::SameLine(indent); + static int region = 0; + ImGui::Combo("##region", ®ion, "NTSC-U (US)\0NTSC-J (Japan)\0PAL (Europe, Australia)"); + + ImGui::Text("BIOS Path:"); + ImGui::SameLine(indent); + DrawFileChooser("##bios_path", &settings.bios_path); + + ImGui::Checkbox("Enable Speed Limiter", &settings.speed_limiter_enabled); + + ImGui::Checkbox("Pause On Start", &settings.start_paused); + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Memory Cards")) + { + for (int i = 0; i < 2; i++) + { + if (!DrawSettingsSectionHeader(TinyString::FromFormat("Card %c", 'A' + i))) + continue; + + ImGui::Text("Card %c", 'A' + i); + + ImGui::Text("Path:"); + ImGui::SameLine(indent); + + std::string* path_ptr = (i == 0) ? &settings.memory_card_a_path : &settings.memory_card_b_path; + if (DrawFileChooser(TinyString::FromFormat("##memcard_%c_path", 'a' + i), path_ptr)) + { + settings_changed = true; + if (m_system) + m_system->UpdateMemoryCards(); + } + + if (ImGui::Button("Eject")) + { + path_ptr->clear(); + settings_changed = true; + if (m_system) + m_system->UpdateMemoryCards(); + } + + ImGui::NewLine(); + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("GPU")) + { + if (DrawSettingsSectionHeader("Basic")) + { + ImGui::Text("Renderer:"); + ImGui::SameLine(indent); + + int gpu_renderer = static_cast(settings.gpu_renderer); + if (ImGui::Combo( + "##gpu_renderer", &gpu_renderer, + [](void*, int index, const char** out_text) { + *out_text = Settings::GetRendererDisplayName(static_cast(index)); + return true; + }, + nullptr, static_cast(Settings::GPURenderer::Count))) + { + settings.gpu_renderer = static_cast(gpu_renderer); + SwitchGPURenderer(); + } + } + + ImGui::NewLine(); + + if (DrawSettingsSectionHeader("Display Output")) + { + ImGui::Checkbox("Fullscreen", &settings.display_fullscreen); + if (ImGui::Checkbox("VSync", &settings.gpu_vsync)) + UpdateAudioVisualSync(); + ImGui::Checkbox("Linear Filtering", &settings.display_linear_filtering); + } + + ImGui::NewLine(); + + if (DrawSettingsSectionHeader("Enhancements")) + { + ImGui::Text("Resolution Scale:"); + ImGui::SameLine(indent); + + static constexpr std::array resolutions = {{ + "1x (1024x512)", + "2x (2048x1024)", + "3x (3072x1536)", + "4x (4096x2048)", + "5x (5120x2560)", + "6x (6144x3072)", + "7x (7168x3584)", + "8x (8192x4096)", + "9x (9216x4608)", + "10x (10240x5120)", + "11x (11264x5632)", + "12x (12288x6144)", + "13x (13312x6656)", + "14x (14336x7168)", + "15x (15360x7680)", + "16x (16384x8192)", + }}; + + int current_resolution_index = static_cast(settings.gpu_resolution_scale) - 1; + if (ImGui::Combo("##gpu_resolution_scale", ¤t_resolution_index, resolutions.data(), + static_cast(resolutions.size()))) + { + settings.gpu_resolution_scale = static_cast(current_resolution_index + 1); + gpu_settings_changed = true; + } + + ImGui::Checkbox("True 24-bit Color (disables dithering)", &settings.gpu_true_color); + } + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + const auto window_size = ImGui::GetWindowSize(); + ImGui::SetCursorPosX(window_size.x - 50.0f); + ImGui::SetCursorPosY(window_size.y - 30.0f); + if (ImGui::Button("Close")) + m_settings_window_open = false; + + ImGui::End(); + + if (settings_changed) + { + // TODO: Save to file + } + if (gpu_settings_changed && m_system) + m_system->GetGPU()->UpdateSettings(); +} + void SDLHostInterface::DrawAboutWindow() { ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x * 0.5f, ImGui::GetIO().DisplaySize.y * 0.5f), - ImGuiCond_Always, ImVec2(0.5f, 0.5f)); - if (!ImGui::Begin("About DuckStation", &m_about_window_open)) + ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + if (!ImGui::Begin("About DuckStation", &m_about_window_open, ImGuiWindowFlags_NoResize)) { ImGui::End(); return; @@ -903,6 +1078,26 @@ void SDLHostInterface::DrawDebugWindows() m_system->GetMDEC()->DrawDebugStateWindow(); } +bool SDLHostInterface::DrawFileChooser(const char* label, std::string* path, const char* filter /* = nullptr */) +{ + ImGui::SetNextItemWidth(ImGui::CalcItemWidth() - 50.0f); + bool result = ImGui::InputText(label, path); + ImGui::SameLine(); + + ImGui::SetNextItemWidth(50.0f); + if (ImGui::Button("...")) + { + nfdchar_t* out_path = nullptr; + if (NFD_OpenDialog(filter, path->c_str(), &out_path) == NFD_OKAY) + { + path->assign(out_path); + result = true; + } + } + + return result; +} + void SDLHostInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/) { OSDMessage msg; diff --git a/src/duckstation/sdl_host_interface.h b/src/duckstation/sdl_host_interface.h index 80d70e624..a9ef37adc 100644 --- a/src/duckstation/sdl_host_interface.h +++ b/src/duckstation/sdl_host_interface.h @@ -68,6 +68,7 @@ private: bool InitializeSystem(const char* filename = nullptr, const char* exp1_filename = nullptr); void ConnectDevices(); void ResetPerformanceCounters(); + void SwitchGPURenderer(); void ShutdownSystem(); // We only pass mouse input through if it's grabbed @@ -92,10 +93,12 @@ private: void DrawMainMenuBar(); void DrawPoweredOffWindow(); + void DrawSettingsWindow(); void DrawAboutWindow(); void DrawOSDMessages(); void DrawDebugMenu(); void DrawDebugWindows(); + bool DrawFileChooser(const char* label, std::string* path, const char* filter = nullptr); SDL_Window* m_window = nullptr; std::unique_ptr m_display; @@ -120,7 +123,7 @@ private: bool m_quit_request = false; bool m_frame_step_request = false; bool m_focus_main_menu_bar = false; + bool m_settings_window_open = false; bool m_about_window_open = false; - bool m_speed_limiter_enabled = true; bool m_speed_limiter_temp_disabled = false; };