mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-12-02 14:04:18 +01:00
1803 lines
63 KiB
C++
1803 lines
63 KiB
C++
#include "gui/wxgui.h"
|
|
#include "gui/GeneralSettings2.h"
|
|
#include "gui/CemuApp.h"
|
|
#include "gui/helpers/wxControlObject.h"
|
|
|
|
#include "util/helpers/helpers.h"
|
|
|
|
#include "Cafe/OS/libs/snd_core/ax.h"
|
|
|
|
#include <wx/collpane.h>
|
|
#include <wx/clrpicker.h>
|
|
#include <wx/cshelp.h>
|
|
#include <wx/textdlg.h>
|
|
#include <wx/hyperlink.h>
|
|
|
|
#include "config/CemuConfig.h"
|
|
|
|
#include "audio/IAudioAPI.h"
|
|
#if BOOST_OS_WINDOWS
|
|
#include "audio/DirectSoundAPI.h"
|
|
#include "audio/XAudio27API.h"
|
|
#endif
|
|
#include "audio/CubebAPI.h"
|
|
|
|
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
|
|
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
|
#include "Cafe/Account/Account.h"
|
|
|
|
#include <boost/tokenizer.hpp>
|
|
#include "util/helpers/SystemException.h"
|
|
#include "gui/dialogs/CreateAccount/wxCreateAccountDialog.h"
|
|
#include "config/PermanentStorage.h"
|
|
|
|
#if BOOST_OS_WINDOWS
|
|
#include <VersionHelpers.h>
|
|
#endif
|
|
|
|
#include "config/LaunchSettings.h"
|
|
#include "config/ActiveSettings.h"
|
|
#include "gui/helpers/wxHelpers.h"
|
|
|
|
#if BOOST_OS_LINUX || BOOST_OS_MACOS
|
|
#include "resource/embedded/resources.h"
|
|
#endif
|
|
|
|
#include "Cafe/CafeSystem.h"
|
|
#include "Cemu/ncrypto/ncrypto.h"
|
|
#include "Cafe/TitleList/TitleList.h"
|
|
#include "wxHelper.h"
|
|
|
|
const wxString kDirectSound(wxT("DirectSound"));
|
|
const wxString kXAudio27(wxT("XAudio2.7"));
|
|
const wxString kXAudio2(wxT("XAudio2"));
|
|
const wxString kCubeb(wxT("Cubeb"));
|
|
|
|
const wxString kPropertyPersistentId(wxT("PersistentId"));
|
|
const wxString kPropertyMiiName(wxT("MiiName"));
|
|
const wxString kPropertyBirthday(wxT("Birthday"));
|
|
const wxString kPropertyGender(wxT("Gender"));
|
|
const wxString kPropertyEmail(wxT("Email"));
|
|
const wxString kPropertyCountry(wxT("Country"));
|
|
|
|
wxDEFINE_EVENT(wxEVT_ACCOUNTLIST_REFRESH, wxCommandEvent);
|
|
|
|
class wxDeviceDescription : public wxClientData
|
|
{
|
|
public:
|
|
wxDeviceDescription(const IAudioAPI::DeviceDescriptionPtr& description) : m_description(description) {}
|
|
const IAudioAPI::DeviceDescriptionPtr& GetDescription() const { return m_description; }
|
|
private:
|
|
IAudioAPI::DeviceDescriptionPtr m_description;
|
|
};
|
|
|
|
class wxVulkanUUID : public wxClientData
|
|
{
|
|
public:
|
|
wxVulkanUUID(const VulkanRenderer::DeviceInfo& info)
|
|
: m_device_info(info) {}
|
|
const VulkanRenderer::DeviceInfo& GetDeviceInfo() const { return m_device_info; }
|
|
|
|
private:
|
|
VulkanRenderer::DeviceInfo m_device_info;
|
|
};
|
|
|
|
class wxAccountData : public wxClientData
|
|
{
|
|
public:
|
|
wxAccountData(const Account& account)
|
|
: m_account(account) {}
|
|
|
|
Account& GetAccount() { return m_account; }
|
|
const Account& GetAccount() const { return m_account; }
|
|
|
|
private:
|
|
Account m_account;
|
|
};
|
|
|
|
wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook)
|
|
{
|
|
auto* panel = new wxPanel(notebook);
|
|
auto* general_panel_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
{
|
|
auto* box = new wxStaticBox(panel, wxID_ANY, _("Interface"));
|
|
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
{
|
|
auto* first_row = new wxFlexGridSizer(0, 2, 0, 0);
|
|
first_row->SetFlexibleDirection(wxBOTH);
|
|
first_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
first_row->Add(new wxStaticText(box, wxID_ANY, _("Language"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
|
|
wxString language_choices[] = { _("Default"), _("English") };
|
|
m_language = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(language_choices), language_choices);
|
|
m_language->SetSelection(0);
|
|
m_language->SetToolTip(_("Changes the interface language of Cemu\nAvailable languages are stored in the translation directory\nA restart will be required after changing the language"));
|
|
for (const auto& language : wxGetApp().GetLanguages())
|
|
{
|
|
m_language->Append(language->Description);
|
|
}
|
|
|
|
first_row->Add(m_language, 0, wxALL | wxEXPAND, 5);
|
|
|
|
box_sizer->Add(first_row, 1, wxEXPAND, 5);
|
|
}
|
|
|
|
{
|
|
auto* second_row = new wxFlexGridSizer(0, 3, 0, 0);
|
|
second_row->SetFlexibleDirection(wxBOTH);
|
|
second_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
const int topflag = wxALIGN_CENTER_VERTICAL | wxALL;
|
|
m_save_window_position_size = new wxCheckBox(box, wxID_ANY, _("Remember main window position"));
|
|
m_save_window_position_size->SetToolTip(_("Restores the last known window position and size when starting Cemu"));
|
|
second_row->Add(m_save_window_position_size, 0, topflag, 5);
|
|
second_row->AddSpacer(10);
|
|
m_save_padwindow_position_size = new wxCheckBox(box, wxID_ANY, _("Remember pad window position"));
|
|
m_save_padwindow_position_size->SetToolTip(_("Restores the last known pad window position and size when opening it"));
|
|
second_row->Add(m_save_padwindow_position_size, 0, topflag, 5);
|
|
|
|
const int botflag = wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT | wxBOTTOM;
|
|
m_discord_presence = new wxCheckBox(box, wxID_ANY, _("Discord Presence"));
|
|
m_discord_presence->SetToolTip(_("Enables the Discord Rich Presence feature\nYou will also need to enable it in the Discord settings itself!"));
|
|
second_row->Add(m_discord_presence, 0, botflag, 5);
|
|
second_row->AddSpacer(10);
|
|
m_fullscreen_menubar = new wxCheckBox(box, wxID_ANY, _("Fullscreen menu bar"));
|
|
m_fullscreen_menubar->SetToolTip(_("Displays the menu bar when Cemu is running in fullscreen mode and the mouse cursor is moved to the top"));
|
|
second_row->Add(m_fullscreen_menubar, 0, botflag, 5);
|
|
|
|
m_auto_update = new wxCheckBox(box, wxID_ANY, _("Automatically check for updates"));
|
|
m_auto_update->SetToolTip(_("Automatically checks for new cemu versions on startup"));
|
|
second_row->Add(m_auto_update, 0, botflag, 5);
|
|
second_row->AddSpacer(10);
|
|
m_save_screenshot = new wxCheckBox(box, wxID_ANY, _("Save screenshot"));
|
|
m_save_screenshot->SetToolTip(_("Pressing the screenshot key (F12) will save a screenshot directly to the screenshots folder"));
|
|
second_row->Add(m_save_screenshot, 0, botflag, 5);
|
|
|
|
m_permanent_storage = new wxCheckBox(box, wxID_ANY, _("Use permanent storage"));
|
|
m_permanent_storage->SetToolTip(_("Cemu will remember your custom mlc path in %LOCALAPPDATA%/Cemu for new installations."));
|
|
second_row->Add(m_permanent_storage, 0, botflag, 5);
|
|
|
|
box_sizer->Add(second_row, 0, wxEXPAND, 5);
|
|
}
|
|
|
|
general_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
{
|
|
auto* box = new wxStaticBox(panel, wxID_ANY, _("MLC Path"));
|
|
auto* box_sizer = new wxStaticBoxSizer(box, wxHORIZONTAL);
|
|
|
|
m_mlc_path = new wxTextCtrl(box, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
|
|
m_mlc_path->SetMinSize(wxSize(150, -1));
|
|
m_mlc_path->Bind(wxEVT_CHAR, &GeneralSettings2::OnMLCPathChar, this);
|
|
m_mlc_path->SetToolTip(_("The mlc directory contains your save games and installed game update/dlc data"));
|
|
|
|
box_sizer->Add(m_mlc_path, 1, wxALL | wxEXPAND, 5);
|
|
|
|
auto* change_path = new wxButton(box, wxID_ANY, wxT("..."));
|
|
change_path->Bind(wxEVT_BUTTON, &GeneralSettings2::OnMLCPathSelect, this);
|
|
change_path->SetToolTip(_("Select a custom mlc path\nThe mlc path is used to store Wii U related files like save games, game updates and dlc data"));
|
|
box_sizer->Add(change_path, 0, wxALL, 5);
|
|
if (LaunchSettings::GetMLCPath().has_value())
|
|
change_path->Disable();
|
|
general_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
{
|
|
auto* general_gamepath_box = new wxStaticBox(panel, wxID_ANY, _("Game Paths"));
|
|
auto* general_gamepath_sizer = new wxStaticBoxSizer(general_gamepath_box, wxVERTICAL);
|
|
|
|
m_game_paths = new wxListBox(general_gamepath_box, wxID_ANY);
|
|
m_game_paths->SetMinSize(wxSize(150, 100));
|
|
m_game_paths->SetToolTip(_("Add the root directory of your game(s). It will scan all directories in it for games"));
|
|
general_gamepath_sizer->Add(m_game_paths, 1, wxALL | wxEXPAND, 5);
|
|
|
|
auto* general_gamepath_buttons = new wxFlexGridSizer(0, 2, 0, 0);
|
|
general_gamepath_buttons->SetFlexibleDirection(wxBOTH);
|
|
general_gamepath_buttons->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
auto* general_gamepath_add_button = new wxButton(general_gamepath_box, wxID_ANY, _("Add"));
|
|
general_gamepath_add_button->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAddPathClicked, this);
|
|
general_gamepath_add_button->SetToolTip(_("Adds a game path to scan for games displayed in the game list\nIf you have unpacked games, make sure to select the root folder of a game"));
|
|
general_gamepath_buttons->Add(general_gamepath_add_button, 0, wxALL, 5);
|
|
|
|
auto* general_gamepath_remove_button = new wxButton(general_gamepath_box, wxID_ANY, _("Remove"));
|
|
general_gamepath_remove_button->Bind(wxEVT_BUTTON, &GeneralSettings2::OnRemovePathClicked, this);
|
|
general_gamepath_remove_button->SetToolTip(_("Removes the currently selected game path from the game list"));
|
|
general_gamepath_buttons->Add(general_gamepath_remove_button, 0, wxALL, 5);
|
|
|
|
general_gamepath_sizer->Add(general_gamepath_buttons, 0, wxEXPAND, 5);
|
|
general_panel_sizer->Add(general_gamepath_sizer, 1, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
panel->SetSizerAndFit(general_panel_sizer);
|
|
|
|
return panel;
|
|
}
|
|
|
|
wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
|
|
{
|
|
// Graphics page
|
|
auto graphics_panel = new wxPanel(notebook);
|
|
auto graphics_panel_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
{
|
|
auto box = new wxStaticBox(graphics_panel, wxID_ANY, _("General"));
|
|
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
auto row = new wxFlexGridSizer(0, 2, 0, 0);
|
|
row->SetFlexibleDirection(wxBOTH);
|
|
row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
row->Add(new wxStaticText(box, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
|
|
sint32 api_size = 1;
|
|
wxString choices[2] = { "OpenGL" };
|
|
if (g_vulkan_available)
|
|
{
|
|
choices[1] = "Vulkan";
|
|
api_size = 2;
|
|
}
|
|
|
|
m_graphic_api = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, api_size, choices);
|
|
m_graphic_api->SetSelection(0);
|
|
if (api_size > 1)
|
|
m_graphic_api->SetToolTip(_("Select one of the available graphic back ends"));
|
|
row->Add(m_graphic_api, 0, wxALL, 5);
|
|
m_graphic_api->Bind(wxEVT_CHOICE, &GeneralSettings2::OnGraphicAPISelected, this);
|
|
|
|
row->Add(new wxStaticText(box, wxID_ANY, _("Graphics Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_graphic_device = new wxChoice(box, wxID_ANY, wxDefaultPosition, { 230, -1 }, api_size, choices);
|
|
m_graphic_device->SetSelection(0);
|
|
m_graphic_device->SetToolTip(_("Select the used graphic device"));
|
|
row->Add(m_graphic_device, 0, wxALL, 5);
|
|
|
|
row->Add(new wxStaticText(box, wxID_ANY, _("VSync")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_vsync = new wxChoice(box, wxID_ANY, wxDefaultPosition, { 230, -1 });
|
|
m_vsync->SetToolTip(_("Controls the vsync state"));
|
|
row->Add(m_vsync, 0, wxALL, 5);
|
|
|
|
box_sizer->Add(row, 0, wxEXPAND, 5);
|
|
|
|
auto* graphic_misc_row = new wxFlexGridSizer(0, 2, 0, 0);
|
|
|
|
m_async_compile = new wxCheckBox(box, wxID_ANY, _("Async shader compile"));
|
|
m_async_compile->SetToolTip(_("Enables async shader and pipeline compilation. Reduces stutter at the cost of objects not rendering for a short time.\nVulkan only"));
|
|
graphic_misc_row->Add(m_async_compile, 0, wxALL, 5);
|
|
|
|
m_gx2drawdone_sync = new wxCheckBox(box, wxID_ANY, _("Full sync at GX2DrawDone()"));
|
|
m_gx2drawdone_sync->SetToolTip(_("If synchronization is requested by the game, the emulated CPU will wait for the GPU to finish all operations.\nThis is more accurate behavior, but may cause lower performance"));
|
|
graphic_misc_row->Add(m_gx2drawdone_sync, 0, wxALL, 5);
|
|
|
|
box_sizer->Add(graphic_misc_row, 1, wxEXPAND, 5);
|
|
graphics_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
{
|
|
wxString choices[] = { _("Bilinear"), _("Bicubic"), _("Hermite"), _("Nearest Neighbor") };
|
|
m_upscale_filter = new wxRadioBox(graphics_panel, wxID_ANY, _("Upscale filter"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS);
|
|
m_upscale_filter->SetToolTip(_("Upscaling filters are used when the game resolution is smaller than the window size"));
|
|
graphics_panel_sizer->Add(m_upscale_filter, 0, wxALL | wxEXPAND, 5);
|
|
|
|
m_downscale_filter = new wxRadioBox(graphics_panel, wxID_ANY, _("Downscale filter"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS);
|
|
m_downscale_filter->SetToolTip(_("Downscaling filters are used when the game resolution is bigger than the window size"));
|
|
graphics_panel_sizer->Add(m_downscale_filter, 0, wxALL | wxEXPAND, 5);
|
|
}
|
|
|
|
{
|
|
wxString choices[] = { _("Keep aspect ratio"), _("Stretch") };
|
|
m_fullscreen_scaling = new wxRadioBox(graphics_panel, wxID_ANY, _("Fullscreen scaling"), wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 5, wxRA_SPECIFY_COLS);
|
|
m_fullscreen_scaling->SetToolTip(_("Controls the output aspect ratio when it doesn't match the ratio of the game"));
|
|
graphics_panel_sizer->Add(m_fullscreen_scaling, 0, wxALL | wxEXPAND, 5);
|
|
}
|
|
|
|
graphics_panel->SetSizerAndFit(graphics_panel_sizer);
|
|
return graphics_panel;
|
|
}
|
|
|
|
wxPanel* GeneralSettings2::AddAudioPage(wxNotebook* notebook)
|
|
{
|
|
auto audio_panel = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
|
auto audio_panel_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
{
|
|
auto box = new wxStaticBox(audio_panel, wxID_ANY, _("General"));
|
|
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
auto audio_general_row = new wxFlexGridSizer(0, 3, 0, 0);
|
|
audio_general_row->SetFlexibleDirection(wxBOTH);
|
|
audio_general_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
audio_general_row->Add(new wxStaticText(box, wxID_ANY, _("API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
|
|
m_audio_api = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr);
|
|
if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::DirectSound))
|
|
m_audio_api->Append(kDirectSound);
|
|
if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::XAudio27))
|
|
m_audio_api->Append(kXAudio27);
|
|
if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::XAudio2))
|
|
m_audio_api->Append(kXAudio2);
|
|
if (IAudioAPI::IsAudioAPIAvailable(IAudioAPI::Cubeb))
|
|
m_audio_api->Append(kCubeb);
|
|
|
|
m_audio_api->SetSelection(0);
|
|
m_audio_api->SetToolTip(_("Select one of the available audio back ends"));
|
|
audio_general_row->Add(m_audio_api, 0, wxALL, 5);
|
|
|
|
m_audio_api->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioAPISelected, this);
|
|
|
|
audio_general_row->AddSpacer(0);
|
|
|
|
audio_general_row->Add(new wxStaticText(box, wxID_ANY, _("Latency")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_audio_latency = new wxSlider(box, wxID_ANY, 2, 0, IAudioAPI::kBlockCount - 1, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL);
|
|
m_audio_latency->SetToolTip(_("Controls the amount of buffered audio data\nHigher values will create a delay in audio playback, but may avoid audio problems when emulation is too slow"));
|
|
audio_general_row->Add(m_audio_latency, 0, wxEXPAND | wxALL, 5);
|
|
auto latency_text = new wxStaticText(box, wxID_ANY, wxT("24ms"));
|
|
audio_general_row->Add(latency_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5);
|
|
m_audio_latency->Bind(wxEVT_SLIDER, &GeneralSettings2::OnLatencySliderChanged, this, wxID_ANY, wxID_ANY, new wxControlObject(latency_text));
|
|
m_audio_latency->Bind(wxEVT_SLIDER, &GeneralSettings2::OnAudioLatencyChanged, this);
|
|
|
|
box_sizer->Add(audio_general_row, 1, wxEXPAND, 5);
|
|
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
const wxString audio_channel_choices[] = { _("Mono"), _("Stereo") , _("Surround") };
|
|
{
|
|
auto box = new wxStaticBox(audio_panel, wxID_ANY, _("TV"));
|
|
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
auto audio_tv_row = new wxFlexGridSizer(0, 3, 0, 0);
|
|
audio_tv_row->SetFlexibleDirection(wxBOTH);
|
|
audio_tv_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
audio_tv_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_tv_device = new wxChoice(box, wxID_ANY);
|
|
m_tv_device->SetMinSize(wxSize(300, -1));
|
|
m_tv_device->SetToolTip(_("Select the active audio output device for Wii U TV"));
|
|
audio_tv_row->Add(m_tv_device, 0, wxEXPAND | wxALL, 5);
|
|
audio_tv_row->AddSpacer(0);
|
|
|
|
m_tv_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this);
|
|
|
|
audio_tv_row->Add(new wxStaticText(box, wxID_ANY, _("Channels")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
|
|
m_tv_channels = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(audio_channel_choices), audio_channel_choices);
|
|
|
|
m_tv_channels->SetSelection(1); // set default to stereo
|
|
m_tv_channels->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioChannelsSelected, this);
|
|
audio_tv_row->Add(m_tv_channels, 0, wxEXPAND | wxALL, 5);
|
|
audio_tv_row->AddSpacer(0);
|
|
|
|
audio_tv_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_tv_volume = new wxSlider(box, wxID_ANY, 100, 0, 100, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL);
|
|
audio_tv_row->Add(m_tv_volume, 0, wxEXPAND | wxALL, 5);
|
|
auto audio_tv_volume_text = new wxStaticText(box, wxID_ANY, wxT("100%"));
|
|
audio_tv_row->Add(audio_tv_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5);
|
|
|
|
m_tv_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_tv_volume_text));
|
|
m_tv_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this);
|
|
|
|
box_sizer->Add(audio_tv_row, 1, wxEXPAND, 5);
|
|
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
{
|
|
auto box = new wxStaticBox(audio_panel, wxID_ANY, _("Gamepad"));
|
|
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
auto audio_pad_row = new wxFlexGridSizer(0, 3, 0, 0);
|
|
audio_pad_row->SetFlexibleDirection(wxBOTH);
|
|
audio_pad_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
audio_pad_row->Add(new wxStaticText(box, wxID_ANY, _("Device")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_pad_device = new wxChoice(box, wxID_ANY, wxDefaultPosition);
|
|
m_pad_device->SetMinSize(wxSize(300, -1));
|
|
m_pad_device->SetToolTip(_("Select the active audio output device for Wii U GamePad"));
|
|
audio_pad_row->Add(m_pad_device, 0, wxEXPAND | wxALL, 5);
|
|
audio_pad_row->AddSpacer(0);
|
|
|
|
m_pad_device->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioDeviceSelected, this);
|
|
|
|
const wxString audio_channel_drc_choices[] = { _("Stereo") }; // stereo for now only
|
|
|
|
audio_pad_row->Add(new wxStaticText(box, wxID_ANY, _("Channels")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_pad_channels = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(audio_channel_drc_choices), audio_channel_drc_choices);
|
|
|
|
m_pad_channels->SetSelection(0); // set default to stereo
|
|
|
|
m_pad_channels->Bind(wxEVT_CHOICE, &GeneralSettings2::OnAudioChannelsSelected, this);
|
|
audio_pad_row->Add(m_pad_channels, 0, wxEXPAND | wxALL, 5);
|
|
audio_pad_row->AddSpacer(0);
|
|
|
|
audio_pad_row->Add(new wxStaticText(box, wxID_ANY, _("Volume")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
m_pad_volume = new wxSlider(box, wxID_ANY, 100, 0, 100);
|
|
audio_pad_row->Add(m_pad_volume, 0, wxEXPAND | wxALL, 5);
|
|
auto audio_pad_volume_text = new wxStaticText(box, wxID_ANY, wxT("100%"));
|
|
audio_pad_row->Add(audio_pad_volume_text, 0, wxALIGN_CENTER_VERTICAL | wxALL | wxALIGN_RIGHT, 5);
|
|
|
|
m_pad_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnSliderChangedPercent, this, wxID_ANY, wxID_ANY, new wxControlObject(audio_pad_volume_text));
|
|
m_pad_volume->Bind(wxEVT_SLIDER, &GeneralSettings2::OnVolumeChanged, this);
|
|
|
|
box_sizer->Add(audio_pad_row, 1, wxEXPAND, 5);
|
|
audio_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
audio_panel->SetSizerAndFit(audio_panel_sizer);
|
|
return audio_panel;
|
|
}
|
|
|
|
wxPanel* GeneralSettings2::AddOverlayPage(wxNotebook* notebook)
|
|
{
|
|
auto* panel = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
|
auto* panel_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
const wxString positions[]{ _("Disabled"), _("Top left"), _("Top center"), _("Top right"), _("Bottom left"), _("Bottom center"), _("Bottom right") };
|
|
const wxString text_scale[]{ "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%" };
|
|
{
|
|
auto box = new wxStaticBox(panel, wxID_ANY, _("Overlay"));
|
|
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
auto position_row = new wxFlexGridSizer(1, 0, 0, 0);
|
|
position_row->SetFlexibleDirection(wxBOTH);
|
|
position_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
{
|
|
position_row->Add(new wxStaticText(box, wxID_ANY, _("Position")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
|
m_overlay_position = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(positions), positions);
|
|
m_overlay_position->SetSelection(0);
|
|
m_overlay_position->SetToolTip(_("Controls the overlay which displays technical information while playing"));
|
|
position_row->Add(m_overlay_position, 0, wxALL, 5);
|
|
|
|
position_row->AddSpacer(25);
|
|
|
|
position_row->Add(new wxStaticText(box, wxID_ANY, _("Text Color")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5);
|
|
m_overlay_font_color = new wxColourPickerCtrl(box, wxID_ANY, *wxWHITE, wxDefaultPosition, wxDefaultSize, wxCLRP_SHOW_ALPHA);
|
|
m_overlay_font_color->SetToolTip(_("Sets the text color of the overlay"));
|
|
position_row->Add(m_overlay_font_color, 0, wxEXPAND | wxALL, 5);
|
|
|
|
position_row->AddSpacer(25);
|
|
|
|
position_row->Add(new wxStaticText(box, wxID_ANY, _("Scale")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5);
|
|
m_overlay_scale = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(text_scale), text_scale);
|
|
m_overlay_scale->SetToolTip(_("Sets the scale of the overlay text"));
|
|
position_row->Add(m_overlay_scale, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
box_sizer->Add(position_row, 0, wxEXPAND, 5);
|
|
|
|
auto settings2_row = new wxFlexGridSizer(0, 4, 2, 0);
|
|
{
|
|
m_overlay_fps = new wxCheckBox(box, wxID_ANY, _("FPS"));
|
|
m_overlay_fps->SetToolTip(_("The number of frames per second. Average over last 5 seconds"));
|
|
settings2_row->Add(m_overlay_fps, 0, wxALL, 5);
|
|
|
|
m_overlay_drawcalls = new wxCheckBox(box, wxID_ANY, _("Draw calls per frame"));
|
|
m_overlay_drawcalls->SetToolTip(_("The number of draw calls per frame. Average over last 5 seconds"));
|
|
settings2_row->Add(m_overlay_drawcalls, 0, wxALL, 5);
|
|
|
|
m_overlay_cpu = new wxCheckBox(box, wxID_ANY, _("CPU usage"));
|
|
m_overlay_cpu->SetToolTip(_("CPU usage of Cemu in percent"));
|
|
settings2_row->Add(m_overlay_cpu, 0, wxALL, 5);
|
|
|
|
m_overlay_cpu_per_core = new wxCheckBox(box, wxID_ANY, _("CPU per core usage"));
|
|
m_overlay_cpu_per_core->SetToolTip(_("Total cpu usage in percent for each core"));
|
|
settings2_row->Add(m_overlay_cpu_per_core, 0, wxALL, 5);
|
|
|
|
m_overlay_ram = new wxCheckBox(box, wxID_ANY, _("RAM usage"));
|
|
m_overlay_ram->SetToolTip(_("Cemu RAM usage in MB"));
|
|
settings2_row->Add(m_overlay_ram, 0, wxALL, 5);
|
|
|
|
m_overlay_vram = new wxCheckBox(box, wxID_ANY, _("VRAM usage"));
|
|
#if BOOST_OS_WINDOWS
|
|
using RtlGetVersion_t = LONG(WINAPI*)(PRTL_OSVERSIONINFOW lpVersionInformation);
|
|
const auto pRtlGetVersion = (RtlGetVersion_t)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
|
|
//if(IsWindows8Point1OrGreater()) requires manifest
|
|
RTL_OSVERSIONINFOW info{};
|
|
// Windows 8.1 6.3*
|
|
if (pRtlGetVersion && pRtlGetVersion(&info) == 0 && ((info.dwMajorVersion == 6 && info.dwMinorVersion >= 3) || info.dwMajorVersion > 6))
|
|
m_overlay_vram->SetToolTip(_("The VRAM usage of Cemu in MB"));
|
|
else
|
|
{
|
|
m_overlay_vram->SetToolTip(_("This option requires Win8.1+"));
|
|
m_overlay_vram->Disable();
|
|
}
|
|
#else
|
|
m_overlay_vram->SetToolTip(_("The VRAM usage of Cemu in MB"));
|
|
#endif
|
|
|
|
settings2_row->Add(m_overlay_vram, 0, wxALL, 5);
|
|
|
|
m_overlay_debug = new wxCheckBox(box, wxID_ANY, _("Debug"));
|
|
m_overlay_debug->SetToolTip(_("Displays internal debug information (Vulkan only)"));
|
|
settings2_row->Add(m_overlay_debug, 0, wxALL, 5);
|
|
}
|
|
box_sizer->Add(settings2_row, 0, wxEXPAND, 5);
|
|
|
|
panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
{
|
|
auto box = new wxStaticBox(panel, wxID_ANY, _("Notifications"));
|
|
auto box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
auto position_row = new wxFlexGridSizer(1, 0, 0, 0);
|
|
position_row->SetFlexibleDirection(wxBOTH);
|
|
position_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
{
|
|
position_row->Add(new wxStaticText(box, wxID_ANY, _("Position")), 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
|
|
|
m_notification_position = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(positions), positions);
|
|
m_notification_position->SetSelection(0);
|
|
m_notification_position->SetToolTip(_("Controls the notification position while playing"));
|
|
position_row->Add(m_notification_position, 0, wxALL, 5);
|
|
|
|
position_row->AddSpacer(25);
|
|
|
|
position_row->Add(new wxStaticText(box, wxID_ANY, _("Text Color")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5);
|
|
m_notification_font_color = new wxColourPickerCtrl(box, wxID_ANY, *wxWHITE, wxDefaultPosition, wxDefaultSize, wxCLRP_SHOW_ALPHA);
|
|
m_notification_font_color->SetToolTip(_("Sets the text color of notifications"));
|
|
position_row->Add(m_notification_font_color, 0, wxEXPAND | wxALL, 5);
|
|
|
|
position_row->Add(new wxStaticText(box, wxID_ANY, _("Scale")), 0, wxALL | wxALIGN_CENTRE_VERTICAL, 5);
|
|
m_notification_scale = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(text_scale), text_scale);
|
|
m_notification_scale->SetToolTip(_("Sets the scale of the notification text"));
|
|
position_row->Add(m_notification_scale, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
box_sizer->Add(position_row, 0, wxEXPAND, 5);
|
|
|
|
auto settings1_row = new wxFlexGridSizer(1, 0, 2, 0);
|
|
{
|
|
m_controller_profile_name = new wxCheckBox(box, wxID_ANY, _("Controller profiles"));
|
|
m_controller_profile_name->SetToolTip(_("Displays the active controller profile when starting a game"));
|
|
settings1_row->Add(m_controller_profile_name, 0, wxALL, 5);
|
|
|
|
m_controller_low_battery = new wxCheckBox(box, wxID_ANY, _("Low battery"));
|
|
m_controller_low_battery->SetToolTip(_("Shows a notification when a low controller battery has been detected"));
|
|
settings1_row->Add(m_controller_low_battery, 0, wxALL, 5);
|
|
|
|
m_shader_compiling = new wxCheckBox(box, wxID_ANY, _("Shader compiler"));
|
|
m_shader_compiling->SetToolTip(_("Shows a notification after shaders have been compiled"));
|
|
settings1_row->Add(m_shader_compiling, 0, wxALL, 5);
|
|
|
|
m_friends_data = new wxCheckBox(box, wxID_ANY, _("Friend list"));
|
|
m_friends_data->SetToolTip(_("Shows friend list related data if online"));
|
|
settings1_row->Add(m_friends_data, 0, wxALL, 5);
|
|
}
|
|
box_sizer->Add(settings1_row, 0, wxEXPAND, 5);
|
|
|
|
|
|
panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
panel->SetSizerAndFit(panel_sizer);
|
|
|
|
return panel;
|
|
}
|
|
|
|
wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
|
|
{
|
|
auto* online_panel = new wxPanel(notebook);
|
|
auto* online_panel_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
{
|
|
auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Account settings"));
|
|
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
auto* content = new wxFlexGridSizer(0, 4, 0, 0);
|
|
content->SetFlexibleDirection(wxBOTH);
|
|
content->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
content->AddGrowableCol(1, 1);
|
|
content->AddGrowableCol(2, 0);
|
|
content->AddGrowableCol(3, 0);
|
|
|
|
content->Add(new wxStaticText(box, wxID_ANY, _("Active account")), 1, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
|
|
m_active_account = new wxChoice(box, wxID_ANY);
|
|
m_active_account->SetMinSize({ 250, -1 });
|
|
content->Add(m_active_account, 0, wxEXPAND | wxALL, 5);
|
|
m_active_account->Bind(wxEVT_CHOICE, &GeneralSettings2::OnActiveAccountChanged, this);
|
|
|
|
m_create_account = new wxButton(box, wxID_ANY, _("Create"));
|
|
content->Add(m_create_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5);
|
|
m_create_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountCreate, this);
|
|
|
|
m_delete_account = new wxButton(box, wxID_ANY, _("Delete"));
|
|
content->Add(m_delete_account, 0, wxEXPAND | wxALL | wxALIGN_RIGHT, 5);
|
|
m_delete_account->Bind(wxEVT_BUTTON, &GeneralSettings2::OnAccountDelete, this);
|
|
|
|
box_sizer->Add(content, 1, wxEXPAND, 5);
|
|
|
|
online_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
|
|
if (CafeSystem::IsTitleRunning())
|
|
{
|
|
m_active_account->Enable(false);
|
|
m_create_account->Enable(false);
|
|
m_delete_account->Enable(false);
|
|
}
|
|
}
|
|
|
|
{
|
|
auto* box = new wxStaticBox(online_panel, wxID_ANY, _("Online settings"));
|
|
auto* box_sizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
|
|
|
m_online_enabled = new wxCheckBox(box, wxID_ANY, _("Enable online mode"));
|
|
m_online_enabled->Bind(wxEVT_CHECKBOX, &GeneralSettings2::OnOnlineEnable, this);
|
|
box_sizer->Add(m_online_enabled, 0, wxEXPAND | wxALL, 5);
|
|
|
|
auto* row = new wxFlexGridSizer(0, 2, 0, 0);
|
|
row->SetFlexibleDirection(wxBOTH);
|
|
row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
const wxImage tmp = wxBITMAP_PNG(PNG_ERROR).ConvertToImage();
|
|
m_validate_online = new wxBitmapButton(box, wxID_ANY, tmp.Scale(16, 16));
|
|
m_validate_online->Bind(wxEVT_BUTTON, &GeneralSettings2::OnShowOnlineValidator, this);
|
|
row->Add(m_validate_online, 0, wxEXPAND | wxALL, 5);
|
|
|
|
m_online_status = new wxStaticText(box, wxID_ANY, _("No account selected"));
|
|
row->Add(m_online_status, 1, wxALL | wxALIGN_CENTRE_VERTICAL, 5);
|
|
|
|
box_sizer->Add(row, 1, wxEXPAND, 5);
|
|
|
|
auto* tutorial_link = new wxHyperlinkCtrl(box, wxID_ANY, _("Online play tutorial"), "https://cemu.info/online-guide");
|
|
box_sizer->Add(tutorial_link, 0, wxALL, 5);
|
|
|
|
|
|
online_panel_sizer->Add(box_sizer, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
{
|
|
m_account_information = new wxCollapsiblePane(online_panel, wxID_ANY, _("Account information"));
|
|
#if BOOST_OS_WINDOWS
|
|
m_account_information->GetControlWidget()->SetBackgroundColour(*wxWHITE);
|
|
#endif
|
|
auto win = m_account_information->GetPane();
|
|
|
|
auto content = new wxBoxSizer(wxVERTICAL);
|
|
|
|
m_account_grid = new wxPropertyGrid(win, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_HIDE_MARGIN | wxPG_STATIC_SPLITTER);
|
|
m_account_grid->SetExtraStyle(wxPG_EX_HELP_AS_TOOLTIPS);
|
|
m_account_grid->SetMinSize({ 300, -1 });
|
|
//m_account_grid->Append(new wxPropertyCategory("Main"));
|
|
|
|
auto* persistent_id_gprop = m_account_grid->Append(new wxStringProperty(wxT("PersistentId"), kPropertyPersistentId));
|
|
persistent_id_gprop->SetHelpString(_("The persistent id is the internal folder name used for your saves"));
|
|
m_account_grid->SetPropertyReadOnly(persistent_id_gprop);
|
|
|
|
m_account_grid->Append(new wxStringProperty(_("Mii name"), kPropertyMiiName))->SetHelpString(_("The mii name is the profile name"));
|
|
m_account_grid->Append(new wxStringProperty(_("Birthday"), kPropertyBirthday));
|
|
|
|
wxPGChoices gender;
|
|
gender.Add(_("Female"), 0);
|
|
gender.Add(_("Male"), 1);
|
|
m_account_grid->Append(new wxEnumProperty("Gender", kPropertyGender, gender));
|
|
|
|
m_account_grid->Append(new wxStringProperty(_("Email"), kPropertyEmail));
|
|
|
|
wxPGChoices countries;
|
|
for (int i = 0; i < 195; ++i)
|
|
{
|
|
const auto country = NCrypto::GetCountryAsString(i);
|
|
if (country && (i == 0 || !boost::equals(country, "NN")))
|
|
{
|
|
countries.Add(country, i);
|
|
}
|
|
}
|
|
m_account_grid->Append(new wxEnumProperty(_("Country"), kPropertyCountry, countries));
|
|
|
|
m_account_grid->Bind(wxEVT_PG_CHANGED, &GeneralSettings2::OnAccountSettingsChanged, this);
|
|
|
|
content->Add(m_account_grid, 1, wxEXPAND | wxALL, 5);
|
|
|
|
win->SetSizer(content);
|
|
content->SetSizeHints(win);
|
|
|
|
online_panel_sizer->Add(m_account_information, 0, wxEXPAND | wxALL, 5);
|
|
}
|
|
|
|
online_panel->SetSizerAndFit(online_panel_sizer);
|
|
return online_panel;
|
|
}
|
|
|
|
wxPanel* GeneralSettings2::AddDebugPage(wxNotebook* notebook)
|
|
{
|
|
auto* panel = new wxPanel(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
|
auto* debug_panel_sizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0);
|
|
debug_row->SetFlexibleDirection(wxBOTH);
|
|
debug_row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
|
|
|
|
debug_row->Add(new wxStaticText(panel, wxID_ANY, _("Crash dump"), wxDefaultPosition, wxDefaultSize, 0), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
|
|
wxString dump_choices[] = { _("Disabled"), _("Lite"), _("Full") };
|
|
m_crash_dump = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, std::size(dump_choices), dump_choices);
|
|
m_crash_dump->SetSelection(0);
|
|
m_crash_dump->SetToolTip(_("Creates a dump when Cemu crashes\nOnly enable when requested by a developer!\nThe Full option will create a very large dump file (includes a full RAM dump of the Cemu process)"));
|
|
debug_row->Add(m_crash_dump, 0, wxALL | wxEXPAND, 5);
|
|
|
|
debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5);
|
|
|
|
panel->SetSizerAndFit(debug_panel_sizer);
|
|
|
|
return panel;
|
|
}
|
|
|
|
GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched)
|
|
: wxDialog(parent, wxID_ANY, _("General settings"), wxDefaultPosition, wxDefaultSize, wxCLOSE_BOX | wxCLIP_CHILDREN | wxCAPTION | wxRESIZE_BORDER), m_game_launched(game_launched)
|
|
{
|
|
SetIcon(wxICON(X_SETTINGS));
|
|
|
|
auto* sizer = new wxBoxSizer(wxVERTICAL);
|
|
auto* notebook = new wxNotebook(this, wxID_ANY);
|
|
|
|
notebook->AddPage(AddGeneralPage(notebook), _("General"));
|
|
notebook->AddPage(AddGraphicsPage(notebook), _("Graphics"));
|
|
notebook->AddPage(AddAudioPage(notebook), _("Audio"));
|
|
notebook->AddPage(AddOverlayPage(notebook), _("Overlay"));
|
|
notebook->AddPage(AddAccountPage(notebook), _("Account"));
|
|
notebook->AddPage(AddDebugPage(notebook), _("Debug"));
|
|
|
|
Bind(wxEVT_CLOSE_WINDOW, &GeneralSettings2::OnClose, this);
|
|
|
|
//
|
|
|
|
sizer->Add(notebook, 1, wxEXPAND | wxALL, 5);
|
|
|
|
SetSizerAndFit(sizer);
|
|
Layout();
|
|
Centre(wxBOTH);
|
|
|
|
//
|
|
|
|
UpdateOnlineAccounts();
|
|
UpdateAudioDeviceList();
|
|
|
|
ApplyConfig();
|
|
HandleGraphicsApiSelection();
|
|
|
|
DisableSettings(game_launched);
|
|
}
|
|
|
|
void GeneralSettings2::StoreConfig()
|
|
{
|
|
auto* app = (CemuApp*)wxTheApp;
|
|
auto& config = GetConfig();
|
|
|
|
config.use_discord_presence = m_discord_presence->IsChecked();
|
|
config.fullscreen_menubar = m_fullscreen_menubar->IsChecked();
|
|
config.check_update = m_auto_update->IsChecked();
|
|
config.save_screenshot = m_save_screenshot->IsChecked();
|
|
|
|
const bool use_ps = m_permanent_storage->IsChecked();
|
|
if(use_ps)
|
|
{
|
|
config.permanent_storage = use_ps;
|
|
try
|
|
{
|
|
|
|
PermanentStorage storage;
|
|
storage.RemoveStorage();
|
|
}
|
|
catch (...) {}
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
// delete permanent storage
|
|
PermanentStorage storage;
|
|
storage.RemoveStorage();
|
|
}
|
|
catch (...) {}
|
|
config.permanent_storage = use_ps;
|
|
}
|
|
|
|
if (!LaunchSettings::GetMLCPath().has_value())
|
|
config.SetMLCPath(m_mlc_path->GetValue().ToStdWstring(), false);
|
|
|
|
// -1 is default wx widget value -> set to dummy 0 so mainwindow and padwindow will update it
|
|
config.window_position = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
|
|
config.window_size = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
|
|
config.pad_position = m_save_padwindow_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
|
|
config.pad_size = m_save_padwindow_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
|
|
|
|
config.game_paths.clear();
|
|
for (auto& path : m_game_paths->GetStrings())
|
|
config.game_paths.emplace_back(path);
|
|
|
|
auto selection = m_language->GetSelection();
|
|
if (selection == 0)
|
|
GetConfig().language = wxLANGUAGE_DEFAULT;
|
|
else if (selection == 1)
|
|
GetConfig().language = wxLANGUAGE_ENGLISH;
|
|
else
|
|
{
|
|
const auto language = m_language->GetStringSelection();
|
|
for (const auto& lang : app->GetLanguages())
|
|
{
|
|
if (lang->Description == language)
|
|
{
|
|
GetConfig().language = lang->Language;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// audio
|
|
if (m_audio_api->GetStringSelection() == kDirectSound)
|
|
config.audio_api = IAudioAPI::DirectSound;
|
|
else if (m_audio_api->GetStringSelection() == kXAudio27)
|
|
config.audio_api = IAudioAPI::XAudio27;
|
|
else if (m_audio_api->GetStringSelection() == kXAudio2)
|
|
config.audio_api = IAudioAPI::XAudio2;
|
|
else if (m_audio_api->GetStringSelection() == kCubeb)
|
|
config.audio_api = IAudioAPI::Cubeb;
|
|
|
|
config.audio_delay = m_audio_latency->GetValue();
|
|
config.tv_channels = (AudioChannels)m_tv_channels->GetSelection();
|
|
//config.pad_channels = (AudioChannels)m_pad_channels->GetSelection();
|
|
config.pad_channels = kStereo; // (AudioChannels)m_pad_channels->GetSelection();
|
|
|
|
config.tv_volume = m_tv_volume->GetValue();
|
|
config.pad_volume = m_pad_volume->GetValue();
|
|
|
|
config.tv_device = L"";
|
|
const auto tv_device = m_tv_device->GetSelection();
|
|
if (tv_device != wxNOT_FOUND && tv_device != 0 && m_tv_device->HasClientObjectData())
|
|
{
|
|
const auto* device_description = (wxDeviceDescription*)m_tv_device->GetClientObject(tv_device);
|
|
if(device_description)
|
|
config.tv_device = device_description->GetDescription()->GetIdentifier();
|
|
}
|
|
|
|
config.pad_device = L"";
|
|
const auto pad_device = m_pad_device->GetSelection();
|
|
if (pad_device != wxNOT_FOUND && pad_device != 0 && m_pad_device->HasClientObjectData())
|
|
{
|
|
const auto* device_description = (wxDeviceDescription*)m_pad_device->GetClientObject(pad_device);
|
|
if (device_description)
|
|
config.pad_device = device_description->GetDescription()->GetIdentifier();
|
|
}
|
|
|
|
// graphics
|
|
config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection();
|
|
|
|
selection = m_graphic_device->GetSelection();
|
|
if(selection != wxNOT_FOUND)
|
|
{
|
|
const auto* info = (wxVulkanUUID*)m_graphic_device->GetClientObject(selection);
|
|
if(info)
|
|
config.graphic_device_uuid = info->GetDeviceInfo().uuid;
|
|
else
|
|
config.graphic_device_uuid = {};
|
|
}
|
|
else
|
|
config.graphic_device_uuid = {};
|
|
|
|
|
|
config.vsync = m_vsync->GetSelection();
|
|
config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked();
|
|
config.async_compile = m_async_compile->IsChecked();
|
|
|
|
config.upscale_filter = m_upscale_filter->GetSelection();
|
|
config.downscale_filter = m_downscale_filter->GetSelection();
|
|
config.fullscreen_scaling = m_fullscreen_scaling->GetSelection();
|
|
|
|
config.overlay.position = (ScreenPosition)m_overlay_position->GetSelection(); wxASSERT((int)config.overlay.position <= (int)ScreenPosition::kBottomRight);
|
|
config.overlay.text_color = m_overlay_font_color->GetColour().GetRGBA();
|
|
config.overlay.text_scale = m_overlay_scale->GetSelection() * 25 + 50;
|
|
|
|
config.overlay.fps = m_overlay_fps->GetValue();
|
|
config.overlay.drawcalls = m_overlay_drawcalls->GetValue();
|
|
config.overlay.cpu_usage = m_overlay_cpu->GetValue();
|
|
config.overlay.cpu_per_core_usage = m_overlay_cpu_per_core->GetValue();
|
|
config.overlay.ram_usage = m_overlay_ram->GetValue();
|
|
config.overlay.vram_usage = m_overlay_vram->GetValue();
|
|
config.overlay.debug = m_overlay_debug->GetValue();
|
|
|
|
config.notification.position = (ScreenPosition)m_notification_position->GetSelection(); wxASSERT((int)config.notification.position <= (int)ScreenPosition::kBottomRight);
|
|
config.notification.text_color = m_notification_font_color->GetColour().GetRGBA();
|
|
config.notification.text_scale = m_notification_scale->GetSelection() * 25 + 50;
|
|
config.notification.controller_profiles = m_controller_profile_name->GetValue();
|
|
config.notification.controller_battery = m_controller_low_battery->GetValue();
|
|
config.notification.shader_compiling = m_shader_compiling->GetValue();
|
|
config.notification.friends = m_friends_data->GetValue();
|
|
|
|
// account
|
|
const auto active_account = m_active_account->GetSelection();
|
|
if (active_account == wxNOT_FOUND)
|
|
config.account.m_persistent_id = config.account.m_persistent_id.GetInitValue();
|
|
else
|
|
config.account.m_persistent_id = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId();
|
|
|
|
config.account.online_enabled = m_online_enabled->GetValue();
|
|
|
|
// debug
|
|
config.crash_dump = (CrashDump)m_crash_dump->GetSelection();
|
|
|
|
g_config.Save();
|
|
}
|
|
|
|
GeneralSettings2::~GeneralSettings2()
|
|
{
|
|
Unbind(wxEVT_CLOSE_WINDOW, &GeneralSettings2::OnClose, this);
|
|
}
|
|
|
|
void GeneralSettings2::OnClose(wxCloseEvent& event)
|
|
{
|
|
StoreConfig();
|
|
if (m_has_account_change)
|
|
{
|
|
wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH);
|
|
GetParent()->ProcessWindowEvent(refresh_event);
|
|
}
|
|
event.Skip();
|
|
}
|
|
|
|
void GeneralSettings2::ValidateConfig()
|
|
{
|
|
g_config.Load();
|
|
|
|
auto& data = g_config.data();
|
|
// todo
|
|
//data.fullscreen_scaling = min(max(data.fullscreen_scaling,))
|
|
}
|
|
|
|
void GeneralSettings2::DisableSettings(bool game_launched)
|
|
{
|
|
|
|
}
|
|
|
|
void GeneralSettings2::OnAudioLatencyChanged(wxCommandEvent& event)
|
|
{
|
|
IAudioAPI::s_audioDelay = event.GetInt();
|
|
event.Skip();
|
|
}
|
|
|
|
void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
|
|
{
|
|
std::shared_lock lock(g_audioMutex);
|
|
if(event.GetEventObject() == m_pad_volume)
|
|
{
|
|
if (g_padAudio)
|
|
{
|
|
g_padAudio->SetVolume(event.GetInt());
|
|
g_padVolume = event.GetInt();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (g_tvAudio)
|
|
g_tvAudio->SetVolume(event.GetInt());
|
|
}
|
|
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void GeneralSettings2::OnInputVolumeChanged(wxCommandEvent& event)
|
|
{
|
|
std::shared_lock lock(g_audioMutex);
|
|
if (g_padAudio)
|
|
{
|
|
g_padAudio->SetInputVolume(event.GetInt());
|
|
g_padVolume = event.GetInt();
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void GeneralSettings2::OnSliderChangedPercent(wxCommandEvent& event)
|
|
{
|
|
const auto slider = dynamic_cast<wxSlider*>(event.GetEventObject());
|
|
wxASSERT(slider);
|
|
|
|
const auto control = dynamic_cast<wxControlObject*>(event.GetEventUserData());
|
|
wxASSERT(control);
|
|
|
|
auto slider_text = control->GetControl<wxStaticText>();
|
|
wxASSERT(slider_text);
|
|
|
|
const auto value = event.GetInt();
|
|
slider->SetValue(value);
|
|
slider_text->SetLabel(wxString::Format("%d%%", value));
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void GeneralSettings2::OnLatencySliderChanged(wxCommandEvent& event)
|
|
{
|
|
const auto slider = dynamic_cast<wxSlider*>(event.GetEventObject());
|
|
wxASSERT(slider);
|
|
|
|
const auto control = dynamic_cast<wxControlObject*>(event.GetEventUserData());
|
|
wxASSERT(control);
|
|
|
|
auto slider_text = control->GetControl<wxStaticText>();
|
|
wxASSERT(slider_text);
|
|
|
|
const auto value = event.GetInt();
|
|
slider->SetValue(value);
|
|
slider_text->SetLabel(wxString::Format("%dms", value * 12));
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void GeneralSettings2::UpdateAudioDeviceList()
|
|
{
|
|
m_tv_device->Clear();
|
|
m_pad_device->Clear();
|
|
|
|
m_tv_device->Append(_("Disabled"));
|
|
m_pad_device->Append(_("Disabled"));
|
|
|
|
const auto audio_api = (IAudioAPI::AudioAPI)GetConfig().audio_api;
|
|
const auto devices = IAudioAPI::GetDevices(audio_api);
|
|
for (auto& device : devices)
|
|
{
|
|
m_tv_device->Append(device->GetName(), new wxDeviceDescription(device));
|
|
m_pad_device->Append(device->GetName(), new wxDeviceDescription(device));
|
|
}
|
|
|
|
if(m_tv_device->GetCount() > 1)
|
|
m_tv_device->SetSelection(1);
|
|
else
|
|
m_tv_device->SetSelection(0);
|
|
|
|
m_pad_device->SetSelection(0);
|
|
|
|
// todo reset global instance of audio device
|
|
}
|
|
|
|
void GeneralSettings2::ResetAccountInformation()
|
|
{
|
|
m_account_grid->SetSplitterPosition(100);
|
|
m_active_account->SetSelection(0);
|
|
|
|
for(auto it = m_account_grid->GetIterator(); !it.AtEnd(); ++it)
|
|
{
|
|
(*it)->SetValueToUnspecified();
|
|
}
|
|
|
|
// refresh pane size
|
|
m_account_information->InvalidateBestSize();
|
|
#if BOOST_OS_WINDOWS
|
|
m_account_information->OnStateChange(GetBestSize());
|
|
#endif
|
|
}
|
|
|
|
void GeneralSettings2::OnAccountCreate(wxCommandEvent& event)
|
|
{
|
|
wxASSERT(Account::HasFreeAccountSlots());
|
|
|
|
wxCreateAccountDialog dialog(this);
|
|
if (dialog.ShowModal() == wxID_CANCEL)
|
|
return;
|
|
|
|
Account account(dialog.GetPersistentId(), dialog.GetMiiName().ToStdWstring());
|
|
account.Save();
|
|
Account::RefreshAccounts();
|
|
|
|
const int index = m_active_account->Append(account.ToString(), new wxAccountData(account));
|
|
|
|
// update ui
|
|
m_active_account->SetSelection(index);
|
|
UpdateAccountInformation();
|
|
|
|
m_create_account->Enable(m_active_account->GetCount() < 0xC);
|
|
m_delete_account->Enable(m_active_account->GetCount() > 1);
|
|
|
|
// send main window event
|
|
wxASSERT(GetParent());
|
|
wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH);
|
|
GetParent()->ProcessWindowEvent(refresh_event);
|
|
}
|
|
|
|
void GeneralSettings2::OnAccountDelete(wxCommandEvent& event)
|
|
{
|
|
if(m_active_account->GetCount() == 1)
|
|
{
|
|
wxMessageBox(_("Can't delete the only account!"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR, this);
|
|
return;
|
|
}
|
|
|
|
const auto selection = m_active_account->GetSelection();
|
|
wxASSERT(selection != wxNOT_FOUND);
|
|
auto* obj = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(selection));
|
|
wxASSERT(obj);
|
|
auto& account = obj->GetAccount();
|
|
|
|
const std::wstring format_str = _("Are you sure you want to delete the account {} with id {:x}?").ToStdWstring();
|
|
const std::wstring msg = fmt::format(fmt::runtime(format_str),
|
|
std::wstring{ account.GetMiiName() }, account.GetPersistentId());
|
|
|
|
const int answer = wxMessageBox(msg, _("Confirmation"), wxYES_NO | wxCENTRE | wxICON_QUESTION, this);
|
|
if (answer == wxNO)
|
|
return;
|
|
|
|
// todo: ask if saves should be deleted too?
|
|
|
|
const fs::path path = account.GetFileName();
|
|
try
|
|
{
|
|
fs::remove_all(path.parent_path());
|
|
m_active_account->Delete(selection);
|
|
m_active_account->SetSelection(0);
|
|
Account::RefreshAccounts();
|
|
UpdateAccountInformation();
|
|
|
|
m_create_account->Enable(m_active_account->GetCount() < 0xC);
|
|
m_delete_account->Enable(m_active_account->GetCount() > 1);
|
|
}
|
|
catch(const std::exception& ex)
|
|
{
|
|
SystemException sys(ex);
|
|
forceLog_printf((char*)sys.what());
|
|
}
|
|
|
|
}
|
|
|
|
void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event)
|
|
{
|
|
wxPGProperty* property = event.GetProperty();
|
|
if (!property)
|
|
return;
|
|
|
|
const wxAny value = property->GetValue();
|
|
if (value.IsNull())
|
|
return;
|
|
|
|
const auto selection = m_active_account->GetSelection();
|
|
wxASSERT(selection != wxNOT_FOUND);
|
|
auto* obj = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(selection));
|
|
wxASSERT(obj);
|
|
auto& account = obj->GetAccount();
|
|
|
|
// TODO make id changeable to free ids + current it?
|
|
bool refresh_accounts = false;
|
|
if (property->GetName() == kPropertyMiiName)
|
|
{
|
|
std::wstring new_name = value.As<wxString>().ToStdWstring();
|
|
if (new_name.empty())
|
|
new_name = L"default";
|
|
|
|
account.SetMiiName(new_name);
|
|
refresh_accounts = true;
|
|
}
|
|
else if (property->GetName() == kPropertyBirthday)
|
|
{
|
|
const std::string birthday = value.As<wxString>().ToStdString();
|
|
const boost::char_separator<char> sep{ "-" };
|
|
|
|
std::vector<std::string> tokens;
|
|
for (const auto& token : boost::tokenizer(birthday, sep))
|
|
{
|
|
tokens.emplace_back(token);
|
|
}
|
|
|
|
if (tokens.size() == 3)
|
|
{
|
|
account.SetBirthYear(ConvertString<uint16>(tokens[0]));
|
|
account.SetBirthMonth(ConvertString<uint8>(tokens[1]));
|
|
account.SetBirthDay(ConvertString<uint8>(tokens[2]));
|
|
}
|
|
}
|
|
else if (property->GetName() == kPropertyGender)
|
|
{
|
|
account.SetGender(value.As<int>());
|
|
}
|
|
else if (property->GetName() == kPropertyEmail)
|
|
{
|
|
account.SetEmail(value.As<wxString>().ToStdString());
|
|
|
|
}
|
|
else if (property->GetName() == kPropertyCountry)
|
|
{
|
|
account.SetCountry(value.As<int>());
|
|
}
|
|
else
|
|
cemu_assert_debug(false);
|
|
|
|
account.Save();
|
|
Account::RefreshAccounts(); // refresh internal account list
|
|
UpdateAccountInformation(); // refresh on invalid values
|
|
|
|
if(refresh_accounts)
|
|
{
|
|
wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH);
|
|
GetParent()->ProcessWindowEvent(refresh_event);
|
|
}
|
|
}
|
|
|
|
void GeneralSettings2::UpdateAccountInformation()
|
|
{
|
|
m_account_grid->SetSplitterPosition(100);
|
|
|
|
m_online_status->SetLabel(_("At least one issue has been found"));
|
|
|
|
const auto selection = m_active_account->GetSelection();
|
|
if(selection == wxNOT_FOUND)
|
|
{
|
|
m_validate_online->SetBitmap(wxBITMAP_PNG(PNG_ERROR).ConvertToImage().Scale(16, 16));
|
|
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE);
|
|
ResetAccountInformation();
|
|
return;
|
|
}
|
|
|
|
const auto* obj = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(selection));
|
|
wxASSERT(obj);
|
|
const auto& account = obj->GetAccount();
|
|
|
|
m_active_account->SetString(selection, account.ToString());
|
|
|
|
m_account_grid->GetProperty(kPropertyPersistentId)->SetValueFromString(fmt::format("{:x}", account.GetPersistentId()));
|
|
m_account_grid->GetProperty(kPropertyMiiName)->SetValueFromString(std::wstring{ account.GetMiiName() });
|
|
m_account_grid->GetProperty(kPropertyBirthday)->SetValueFromString(fmt::format("{:04d}-{:02d}-{:02d}", account.GetBirthYear(), account.GetBirthMonth(), account.GetBirthDay()));
|
|
|
|
const auto gender_property = m_account_grid->GetProperty(kPropertyGender); // gender 2 can be also female?
|
|
gender_property->SetChoiceSelection(std::min(gender_property->GetChoices().GetCount() - 1, (uint32)account.GetGender()));
|
|
|
|
m_account_grid->GetProperty(kPropertyEmail)->SetValueFromString(std::string{ account.GetEmail() });
|
|
|
|
auto* country_property = dynamic_cast<wxEnumProperty*>(m_account_grid->GetProperty(kPropertyCountry));
|
|
wxASSERT(country_property);
|
|
int index = (country_property)->GetIndexForValue(account.GetCountry());
|
|
if (index == wxNOT_FOUND)
|
|
index = 0;
|
|
country_property->SetChoiceSelection(index);
|
|
|
|
const bool online_valid = account.IsValidOnlineAccount() && ActiveSettings::HasRequiredOnlineFiles();
|
|
if (online_valid)
|
|
{
|
|
|
|
m_online_status->SetLabel(_("Your account is a valid online account"));
|
|
m_validate_online->SetBitmap(wxBITMAP_PNG(PNG_CHECK_YES).ConvertToImage().Scale(16, 16));
|
|
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() | wxBORDER_NONE);
|
|
}
|
|
else
|
|
{
|
|
m_validate_online->SetBitmap(wxBITMAP_PNG(PNG_ERROR).ConvertToImage().Scale(16, 16));
|
|
m_validate_online->SetWindowStyleFlag(m_validate_online->GetWindowStyleFlag() & ~wxBORDER_NONE);
|
|
}
|
|
|
|
// refresh pane size
|
|
m_account_grid->InvalidateBestSize();
|
|
//m_account_grid->GetParent()->FitInside();
|
|
//m_account_information->OnStateChange(GetBestSize()); idk..
|
|
}
|
|
|
|
void GeneralSettings2::UpdateOnlineAccounts()
|
|
{
|
|
m_active_account->Clear();
|
|
for(const auto& account : Account::GetAccounts())
|
|
{
|
|
m_active_account->Append(fmt::format(L"{} ({:x})", std::wstring{ account.GetMiiName() }, account.GetPersistentId()),
|
|
new wxAccountData(account));
|
|
}
|
|
|
|
m_active_account->SetSelection(0);
|
|
m_create_account->Enable(m_active_account->GetCount() < 0xC);
|
|
m_delete_account->Enable(m_active_account->GetCount() > 1);
|
|
UpdateAccountInformation();
|
|
}
|
|
|
|
void GeneralSettings2::HandleGraphicsApiSelection()
|
|
{
|
|
int selection = m_vsync->GetSelection();
|
|
if(selection == wxNOT_FOUND)
|
|
selection = GetConfig().vsync;
|
|
|
|
m_vsync->Clear();
|
|
if(m_graphic_api->GetSelection() == 0)
|
|
{
|
|
// OpenGL
|
|
m_vsync->AppendString(_("Off"));
|
|
m_vsync->AppendString(_("On"));
|
|
if (selection == 0)
|
|
m_vsync->Select(0);
|
|
else
|
|
m_vsync->Select(1);
|
|
|
|
m_graphic_device->Clear();
|
|
m_graphic_device->Disable();
|
|
|
|
m_gx2drawdone_sync->Enable();
|
|
m_async_compile->Disable();
|
|
}
|
|
else
|
|
{
|
|
// Vulkan
|
|
m_gx2drawdone_sync->Disable();
|
|
m_async_compile->Enable();
|
|
|
|
m_vsync->AppendString(_("Off"));
|
|
m_vsync->AppendString(_("Double buffering"));
|
|
m_vsync->AppendString(_("Triple buffering"));
|
|
|
|
m_vsync->AppendString(_("Match emulated display (Experimental)"));
|
|
|
|
m_vsync->Select(selection);
|
|
|
|
m_graphic_device->Enable();
|
|
auto devices = VulkanRenderer::GetDevices();
|
|
m_graphic_device->Clear();
|
|
if(!devices.empty())
|
|
{
|
|
for(const auto& device : devices)
|
|
{
|
|
m_graphic_device->Append(device.name, new wxVulkanUUID(device));
|
|
}
|
|
m_graphic_device->SetSelection(0);
|
|
|
|
const auto& config = GetConfig();
|
|
for(size_t i = 0; i < devices.size(); ++i)
|
|
{
|
|
if(config.graphic_device_uuid == devices[i].uuid)
|
|
{
|
|
m_graphic_device->SetSelection(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GeneralSettings2::ApplyConfig()
|
|
{
|
|
ValidateConfig();
|
|
auto& config = GetConfig();
|
|
|
|
if (LaunchSettings::GetMLCPath().has_value())
|
|
m_mlc_path->SetValue(wxString{ LaunchSettings::GetMLCPath().value().generic_wstring() });
|
|
else
|
|
m_mlc_path->SetValue(config.mlc_path.GetValue());
|
|
|
|
m_save_window_position_size->SetValue(config.window_position != Vector2i{-1,-1});
|
|
m_save_padwindow_position_size->SetValue(config.pad_position != Vector2i{-1,-1});
|
|
|
|
m_discord_presence->SetValue(config.use_discord_presence);
|
|
m_fullscreen_menubar->SetValue(config.fullscreen_menubar);
|
|
|
|
m_auto_update->SetValue(config.check_update);
|
|
m_save_screenshot->SetValue(config.save_screenshot);
|
|
|
|
m_permanent_storage->SetValue(config.permanent_storage);
|
|
|
|
for (auto& path : config.game_paths)
|
|
{
|
|
m_game_paths->Append(path);
|
|
}
|
|
|
|
const auto app = (CemuApp*)wxTheApp;
|
|
for (const auto& language : app->GetLanguages())
|
|
{
|
|
if (config.language == language->Language)
|
|
{
|
|
m_language->SetStringSelection(language->Description);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// graphics
|
|
m_graphic_api->SetSelection(config.graphic_api);
|
|
m_vsync->SetSelection(config.vsync);
|
|
m_async_compile->SetValue(config.async_compile);
|
|
m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync);
|
|
m_upscale_filter->SetSelection(config.upscale_filter);
|
|
m_downscale_filter->SetSelection(config.downscale_filter);
|
|
m_fullscreen_scaling->SetSelection(config.fullscreen_scaling);
|
|
|
|
wxASSERT((uint32)config.overlay.position < m_overlay_position->GetCount());
|
|
m_overlay_position->SetSelection((int)config.overlay.position);
|
|
m_overlay_font_color->SetColour(wxColour((unsigned long)config.overlay.text_color));
|
|
|
|
uint32 selection = (config.overlay.text_scale - 50) / 25;
|
|
wxASSERT(selection < m_overlay_scale->GetCount());
|
|
m_overlay_scale->SetSelection(selection);
|
|
|
|
m_overlay_fps->SetValue(config.overlay.fps);
|
|
m_overlay_drawcalls->SetValue(config.overlay.drawcalls);
|
|
m_overlay_cpu->SetValue(config.overlay.cpu_usage);
|
|
m_overlay_cpu_per_core->SetValue(config.overlay.cpu_per_core_usage);
|
|
m_overlay_ram->SetValue(config.overlay.ram_usage);
|
|
m_overlay_vram->SetValue(config.overlay.vram_usage);
|
|
m_overlay_debug->SetValue(config.overlay.debug);
|
|
|
|
wxASSERT((uint32)config.notification.position < m_notification_position->GetCount());
|
|
m_notification_position->SetSelection((int)config.notification.position);
|
|
m_notification_font_color->SetColour(wxColour((unsigned long)config.notification.text_color));
|
|
|
|
selection = (config.notification.text_scale - 50) / 25;
|
|
wxASSERT(selection < m_notification_scale->GetCount());
|
|
m_notification_scale->SetSelection(selection);
|
|
|
|
m_controller_profile_name->SetValue(config.notification.controller_profiles);
|
|
m_controller_low_battery->SetValue(config.notification.controller_battery);
|
|
m_shader_compiling->SetValue(config.notification.shader_compiling);
|
|
m_friends_data->SetValue(config.notification.friends);
|
|
|
|
// audio
|
|
if(config.audio_api == IAudioAPI::DirectSound)
|
|
m_audio_api->SetStringSelection(kDirectSound);
|
|
else if(config.audio_api == IAudioAPI::XAudio27)
|
|
m_audio_api->SetStringSelection(kXAudio27);
|
|
else if(config.audio_api == IAudioAPI::XAudio2)
|
|
m_audio_api->SetStringSelection(kXAudio2);
|
|
else if(config.audio_api == IAudioAPI::Cubeb)
|
|
m_audio_api->SetStringSelection(kCubeb);
|
|
|
|
SendSliderEvent(m_audio_latency, config.audio_delay);
|
|
|
|
m_tv_channels->SetSelection(config.tv_channels);
|
|
//m_pad_channels->SetSelection(config.pad_channels);
|
|
m_pad_channels->SetSelection(0);
|
|
|
|
SendSliderEvent(m_tv_volume, config.tv_volume);
|
|
|
|
if (!config.tv_device.empty() && m_tv_device->HasClientObjectData())
|
|
{
|
|
for(uint32 i = 0; i < m_tv_device->GetCount(); ++i)
|
|
{
|
|
const auto device_description = (wxDeviceDescription*)m_tv_device->GetClientObject(i);
|
|
if (device_description && config.tv_device == device_description->GetDescription()->GetIdentifier())
|
|
{
|
|
m_tv_device->SetSelection(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m_tv_device->SetSelection(0);
|
|
|
|
SendSliderEvent(m_pad_volume, config.pad_volume);
|
|
if (!config.pad_device.empty() && m_pad_device->HasClientObjectData())
|
|
{
|
|
for (uint32 i = 0; i < m_pad_device->GetCount(); ++i)
|
|
{
|
|
const auto device_description = (wxDeviceDescription*)m_pad_device->GetClientObject(i);
|
|
if (device_description && config.pad_device == device_description->GetDescription()->GetIdentifier())
|
|
{
|
|
m_pad_device->SetSelection(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m_pad_device->SetSelection(0);
|
|
|
|
// account
|
|
UpdateOnlineAccounts();
|
|
m_active_account->SetSelection(0);
|
|
for(uint32 i = 0; i < m_active_account->GetCount(); ++i)
|
|
{
|
|
const auto* obj = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(i));
|
|
wxASSERT(obj);
|
|
if(obj->GetAccount().GetPersistentId() == ActiveSettings::GetPersistentId())
|
|
{
|
|
m_active_account->SetSelection(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_online_enabled->SetValue(config.account.online_enabled);
|
|
UpdateAccountInformation();
|
|
|
|
// debug
|
|
m_crash_dump->SetSelection((int)config.crash_dump.GetValue());
|
|
}
|
|
|
|
void GeneralSettings2::OnOnlineEnable(wxCommandEvent& event)
|
|
{
|
|
event.Skip();
|
|
if (!m_online_enabled->GetValue())
|
|
return;
|
|
|
|
// show warning if player enables online mode
|
|
const auto result = wxMessageBox(_("Please be aware that online mode lets you connect to OFFICIAL servers and therefore there is a risk of getting banned.\nOnly proceed if you are willing to risk losing online access with your Wii U and/or NNID."),
|
|
_("Warning"), wxYES_NO | wxCENTRE | wxICON_EXCLAMATION, this);
|
|
if (result == wxNO)
|
|
m_online_enabled->SetValue(false);
|
|
}
|
|
|
|
|
|
void GeneralSettings2::OnAudioAPISelected(wxCommandEvent& event)
|
|
{
|
|
IAudioAPI::AudioAPI api;
|
|
if (m_audio_api->GetStringSelection() == kDirectSound)
|
|
api = IAudioAPI::DirectSound;
|
|
else if (m_audio_api->GetStringSelection() == kXAudio27)
|
|
api = IAudioAPI::XAudio27;
|
|
else if (m_audio_api->GetStringSelection() == kXAudio2)
|
|
api = IAudioAPI::XAudio2;
|
|
else if (m_audio_api->GetStringSelection() == kCubeb)
|
|
api = IAudioAPI::Cubeb;
|
|
else
|
|
{
|
|
wxFAIL_MSG("invalid audio api selected!");
|
|
return;
|
|
}
|
|
|
|
GetConfig().audio_api = api;
|
|
UpdateAudioDeviceList();
|
|
OnAudioDeviceSelected(event);
|
|
}
|
|
|
|
#define AX_FRAMES_PER_GROUP 4
|
|
|
|
void GeneralSettings2::UpdateAudioDevice()
|
|
{
|
|
auto& config = GetConfig();
|
|
|
|
// tv audio device
|
|
{
|
|
const auto selection = m_tv_device->GetSelection();
|
|
if (selection == wxNOT_FOUND)
|
|
{
|
|
cemu_assert_debug(false);
|
|
return;
|
|
}
|
|
|
|
if (m_tv_device->HasClientObjectData())
|
|
{
|
|
const auto description = (wxDeviceDescription*)m_tv_device->GetClientObject(selection);
|
|
if (description)
|
|
{
|
|
std::unique_lock lock(g_audioMutex);
|
|
|
|
sint32 channels;
|
|
if (m_game_launched && g_tvAudio)
|
|
channels = g_tvAudio->GetChannels();
|
|
else
|
|
{
|
|
switch (config.tv_channels)
|
|
{
|
|
case 0:
|
|
channels = 1;
|
|
break;
|
|
case 2:
|
|
channels = 6;
|
|
break;
|
|
default: // stereo
|
|
channels = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
g_tvAudio.reset();
|
|
g_tvAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
|
g_tvAudio->SetVolume(m_tv_volume->GetValue());
|
|
}
|
|
catch (std::runtime_error& ex)
|
|
{
|
|
forceLog_printf("can't initialize tv audio: %s", ex.what());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// pad audio device
|
|
{
|
|
const auto selection = m_pad_device->GetSelection();
|
|
if (selection == wxNOT_FOUND)
|
|
{
|
|
cemu_assert_debug(false);
|
|
return;
|
|
}
|
|
|
|
if (m_pad_device->HasClientObjectData())
|
|
{
|
|
const auto description = (wxDeviceDescription*)m_pad_device->GetClientObject(selection);
|
|
if (description)
|
|
{
|
|
std::unique_lock lock(g_audioMutex);
|
|
|
|
sint32 channels;
|
|
if (m_game_launched && g_padAudio)
|
|
channels = g_padAudio->GetChannels();
|
|
else
|
|
{
|
|
switch (config.pad_channels)
|
|
{
|
|
case 0:
|
|
channels = 1;
|
|
break;
|
|
case 2:
|
|
channels = 6;
|
|
break;
|
|
default: // stereo
|
|
channels = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
g_padAudio.reset();
|
|
g_padAudio = IAudioAPI::CreateDevice((IAudioAPI::AudioAPI)config.audio_api, description->GetDescription(), 48000, channels, snd_core::AX_SAMPLES_PER_3MS_48KHZ * AX_FRAMES_PER_GROUP, 16);
|
|
g_padAudio->SetVolume(m_pad_volume->GetValue());
|
|
g_padVolume = m_pad_volume->GetValue();
|
|
}
|
|
catch (std::runtime_error& ex)
|
|
{
|
|
forceLog_printf("can't initialize pad audio: %s", ex.what());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void GeneralSettings2::OnAudioDeviceSelected(wxCommandEvent& event)
|
|
{
|
|
UpdateAudioDevice();
|
|
}
|
|
|
|
void GeneralSettings2::OnAudioChannelsSelected(wxCommandEvent& event)
|
|
{
|
|
const auto obj = wxDynamicCast(event.GetEventObject(), wxChoice);
|
|
wxASSERT(obj);
|
|
if (obj->GetSelection() == wxNOT_FOUND)
|
|
return;
|
|
|
|
auto& config = GetConfig();
|
|
if (obj == m_tv_channels)
|
|
{
|
|
if (config.tv_channels == (AudioChannels)obj->GetSelection())
|
|
return;
|
|
|
|
config.tv_channels = (AudioChannels)obj->GetSelection();
|
|
}
|
|
else if (obj == m_pad_channels)
|
|
{
|
|
if (config.pad_channels == (AudioChannels)obj->GetSelection())
|
|
return;
|
|
|
|
config.pad_channels = (AudioChannels)obj->GetSelection();
|
|
}
|
|
else
|
|
cemu_assert_debug(false);
|
|
|
|
if(m_game_launched)
|
|
wxMessageBox(_("You have to restart the game in order to apply the new settings."), _("Information"), wxOK | wxCENTRE, this);
|
|
else
|
|
UpdateAudioDevice();
|
|
}
|
|
|
|
void GeneralSettings2::OnGraphicAPISelected(wxCommandEvent& event)
|
|
{
|
|
HandleGraphicsApiSelection();
|
|
}
|
|
|
|
void GeneralSettings2::OnAddPathClicked(wxCommandEvent& event)
|
|
{
|
|
wxDirDialog path_dialog(this, _("Select a directory containing games."), wxEmptyString, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
|
|
if (path_dialog.ShowModal() != wxID_OK || path_dialog.GetPath().empty())
|
|
return;
|
|
|
|
const auto path = path_dialog.GetPath();
|
|
// test if already included
|
|
for (auto& s : m_game_paths->GetStrings())
|
|
{
|
|
if (s == path)
|
|
return;
|
|
}
|
|
|
|
m_game_paths->Append(path);
|
|
m_reload_gamelist = true;
|
|
|
|
// trigger title list rescan with new path configuration
|
|
CafeTitleList::ClearScanPaths();
|
|
for (auto& it : m_game_paths->GetStrings())
|
|
CafeTitleList::AddScanPath(wxHelper::MakeFSPath(it));
|
|
CafeTitleList::Refresh();
|
|
}
|
|
|
|
void GeneralSettings2::OnRemovePathClicked(wxCommandEvent& event)
|
|
{
|
|
const auto selection = m_game_paths->GetSelection();
|
|
if (selection == wxNOT_FOUND)
|
|
return;
|
|
|
|
m_game_paths->Delete(selection);
|
|
m_reload_gamelist = true;
|
|
// trigger title list rescan with new path configuration
|
|
CafeTitleList::ClearScanPaths();
|
|
for (auto& it : m_game_paths->GetStrings())
|
|
CafeTitleList::AddScanPath(wxHelper::MakeFSPath(it));
|
|
CafeTitleList::Refresh();
|
|
}
|
|
|
|
void GeneralSettings2::OnActiveAccountChanged(wxCommandEvent& event)
|
|
{
|
|
UpdateAccountInformation();
|
|
m_has_account_change = true;
|
|
}
|
|
|
|
void GeneralSettings2::OnMLCPathSelect(wxCommandEvent& event)
|
|
{
|
|
if (!CemuApp::SelectMLCPath(this))
|
|
return;
|
|
|
|
m_mlc_path->SetValue(ActiveSettings::GetMlcPath().generic_string());
|
|
m_reload_gamelist = true;
|
|
m_mlc_modified = true;
|
|
CemuApp::CreateDefaultFiles();
|
|
}
|
|
|
|
void GeneralSettings2::OnMLCPathChar(wxKeyEvent& event)
|
|
{
|
|
if (LaunchSettings::GetMLCPath().has_value())
|
|
return;
|
|
|
|
if(event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_BACK)
|
|
{
|
|
m_mlc_path->SetValue(wxEmptyString);
|
|
m_reload_gamelist = true;
|
|
|
|
GetConfig().mlc_path = L"";
|
|
g_config.Save();
|
|
m_mlc_modified = true;
|
|
|
|
CemuApp::CreateDefaultFiles();
|
|
}
|
|
}
|
|
|
|
void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event)
|
|
{
|
|
const auto selection = m_active_account->GetSelection();
|
|
if (selection == wxNOT_FOUND)
|
|
return;
|
|
|
|
const auto* obj = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(selection));
|
|
wxASSERT(obj);
|
|
const auto& account = obj->GetAccount();
|
|
|
|
const auto validator = account.ValidateOnlineFiles();
|
|
if (validator) // everything valid? shouldn't happen
|
|
return;
|
|
|
|
std::wstringstream err;
|
|
err << L"The following error(s) have been found:" << std::endl;
|
|
|
|
if (validator.otp == OnlineValidator::FileState::Missing)
|
|
err << L"otp.bin missing in cemu root directory" << std::endl;
|
|
else if(validator.otp == OnlineValidator::FileState::Corrupted)
|
|
err << L"otp.bin is invalid" << std::endl;
|
|
|
|
if (validator.seeprom == OnlineValidator::FileState::Missing)
|
|
err << L"seeprom.bin missing in cemu root directory" << std::endl;
|
|
else if(validator.seeprom == OnlineValidator::FileState::Corrupted)
|
|
err << L"seeprom.bin is invalid" << std::endl;
|
|
|
|
if(!validator.missing_files.empty())
|
|
{
|
|
err << L"Missing certificate and key files:" << std::endl;
|
|
|
|
int counter = 0;
|
|
for (const auto& f : validator.missing_files)
|
|
{
|
|
err << f << std::endl;
|
|
|
|
++counter;
|
|
if(counter > 10)
|
|
{
|
|
err << L"..." << std::endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
err << std::endl;
|
|
}
|
|
|
|
if (!validator.valid_account)
|
|
{
|
|
err << L"The currently selected account is not a valid or dumped online account:\n" << boost::nowide::widen(fmt::format("{}", validator.account_error));
|
|
}
|
|
|
|
|
|
wxMessageBox(err.str(), _("Online Status"), wxOK | wxCENTRE | wxICON_INFORMATION);
|
|
}
|