Cemu/src/gui/MainWindow.cpp
2022-09-02 15:16:35 +02:00

2388 lines
79 KiB
C++

#include "gui/wxgui.h"
#include "gui/MainWindow.h"
#include "gui/guiWrapper.h"
#include <wx/mstream.h>
#include "gui/GameUpdateWindow.h"
#include "gui/PadViewFrame.h"
#include "gui/windows/TextureRelationViewer/TextureRelationWindow.h"
#include "gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h"
#include "audio/audioDebuggerWindow.h"
#include "gui/canvas/OpenGLCanvas.h"
#include "gui/canvas/VulkanCanvas.h"
#include "Cafe/OS/libs/nn_nfp/nn_nfp.h"
#include "Cafe/OS/libs/swkbd/swkbd.h"
#include "Cafe/IOSU/legacy/iosu_crypto.h"
#include "Cafe/GameProfile/GameProfile.h"
#include "gui/debugger/DebuggerWindow2.h"
#include "util/helpers/helpers.h"
#include "config/CemuConfig.h"
#include "Cemu/DiscordPresence/DiscordPresence.h"
#include "gui/GeneralSettings2.h"
#include "gui/GraphicPacksWindow2.h"
#include "gui/GameProfileWindow.h"
#include "gui/CemuApp.h"
#include "gui/CemuUpdateWindow.h"
#include "gui/helpers/wxCustomData.h"
#include "gui/LoggingWindow.h"
#include "config/ActiveSettings.h"
#include "config/LaunchSettings.h"
#include "Cafe/Filesystem/FST/FST.h"
#include "gui/TitleManager.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/GraphicPack/GraphicPack.h"
#include "Cafe/TitleList/GameInfo.h"
#ifdef _WIN32
#include <shellapi.h>
#endif
#include <boost/algorithm/string.hpp>
#include "util/helpers/SystemException.h"
#include "gui/DownloadGraphicPacksWindow.h"
#include "gui/GettingStartedDialog.h"
#include "gui/helpers/wxHelpers.h"
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
#include "gui/input/InputSettings2.h"
#include "input/InputManager.h"
#if BOOST_OS_WINDOWS
#define exit(__c) ExitProcess(__c)
#endif
#if BOOST_OS_LINUX || BOOST_OS_MACOS
#include "resource/embedded/resources.h"
#endif
#include "Cafe/TitleList/TitleInfo.h"
#include "Cafe/TitleList/TitleList.h"
#include "wxHelper.h"
extern WindowInfo g_window_info;
extern std::shared_mutex g_mutex;
wxDEFINE_EVENT(wxEVT_SET_WINDOW_TITLE, wxCommandEvent);
enum
{
// note - Cemuhook mirrors these ids, be careful about changing them
// ui elements
MAINFRAME_GAMELIST_ID = 20000, //wxID_HIGHEST + 1,
// file
MAINFRAME_MENU_ID_FILE_LOAD = 20100,
MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE,
MAINFRAME_MENU_ID_FILE_EXIT,
MAINFRAME_MENU_ID_FILE_END_EMULATION,
MAINFRAME_MENU_ID_FILE_RECENT_0,
MAINFRAME_MENU_ID_FILE_RECENT_LAST = MAINFRAME_MENU_ID_FILE_RECENT_0 + 15,
// options
MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN = 20200,
MAINFRAME_MENU_ID_OPTIONS_VSYNC,
MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW,
MAINFRAME_MENU_ID_OPTIONS_GRAPHIC,
MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2,
MAINFRAME_MENU_ID_OPTIONS_GENERAL,
MAINFRAME_MENU_ID_OPTIONS_GENERAL2,
MAINFRAME_MENU_ID_OPTIONS_AUDIO,
MAINFRAME_MENU_ID_OPTIONS_INPUT,
// options -> experimental
MAINFRAME_MENU_ID_EXPERIMENTAL_BOTW_WORKAROUND = 20300,
MAINFRAME_MENU_ID_EXPERIMENTAL_SYNC_TO_GX2DRAWDONE,
MAINFRAME_MENU_ID_EXPERIMENTAL_DISABLE_PRECOMPILED,
// options -> account
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
// options -> system language
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE = 20500,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_GERMAN,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ITALIAN,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_SPANISH,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_CHINESE,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_KOREAN,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_DUTCH,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_PORTUGUESE,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_RUSSIAN,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE,
// tools
MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER = 20600,
MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER,
MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER,
// cpu
// cpu->timer speed
MAINFRAME_MENU_ID_TIMER_SPEED_1X = 20700,
MAINFRAME_MENU_ID_TIMER_SPEED_2X = 20701,
MAINFRAME_MENU_ID_TIMER_SPEED_4X = 20702,
MAINFRAME_MENU_ID_TIMER_SPEED_8X = 20703,
MAINFRAME_MENU_ID_TIMER_SPEED_05X = 20704,
MAINFRAME_MENU_ID_TIMER_SPEED_025X = 20705,
MAINFRAME_MENU_ID_TIMER_SPEED_0125X = 20706,
// nfc->Touch NFC file
MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE = 21000,
MAINFRAME_MENU_ID_NFC_RECENT_0,
MAINFRAME_MENU_ID_NFC_RECENT_LAST = MAINFRAME_MENU_ID_NFC_RECENT_0 + 15,
// debug
MAINFRAME_MENU_ID_DEBUG_RESERVED = 21100,
MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN,
MAINFRAME_MENU_ID_DEBUG_VIEW_LOGGING_WINDOW,
MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_THREADS,
MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_DEBUGGER,
MAINFRAME_MENU_ID_DEBUG_VIEW_AUDIO_DEBUGGER,
MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS,
MAINFRAME_MENU_ID_DEBUG_SHOW_FRAME_PROFILER,
MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY,
MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS,
// debug->logging
MAINFRAME_MENU_ID_DEBUG_LOGGING_DISABLE_ALL = 21500,
MAINFRAME_MENU_ID_DEBUG_LOGGING0,
MAINFRAME_MENU_ID_DEBUG_LOGGING20 = MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 20,
MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO,
// debug->dump
MAINFRAME_MENU_ID_DEBUG_DUMP_TEXTURES = 21600,
MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS,
MAINFRAME_MENU_ID_DEBUG_DUMP_RAM,
MAINFRAME_MENU_ID_DEBUG_DUMP_FST,
MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS,
// help
MAINFRAME_MENU_ID_HELP_WEB = 21700,
MAINFRAME_MENU_ID_HELP_ABOUT,
MAINFRAME_MENU_ID_HELP_UPDATE,
MAINFRAME_MENU_ID_HELP_GETTING_STARTED,
// custom
MAINFRAME_ID_TIMER1 = 21800,
};
wxDEFINE_EVENT(wxEVT_REQUEST_GAMELIST_REFRESH, wxCommandEvent);
wxDEFINE_EVENT(wxEVT_LAUNCH_GAME, wxLaunchGameEvent);
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
EVT_TIMER(MAINFRAME_ID_TIMER1, MainWindow::OnTimer)
EVT_CLOSE(MainWindow::OnClose)
EVT_SIZE(MainWindow::OnSizeEvent)
EVT_MOVE(MainWindow::OnMove)
// file menu
EVT_MENU(MAINFRAME_MENU_ID_FILE_LOAD, MainWindow::OnFileMenu)
EVT_MENU(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, MainWindow::OnInstallUpdate)
EVT_MENU(MAINFRAME_MENU_ID_FILE_EXIT, MainWindow::OnFileExit)
EVT_MENU(MAINFRAME_MENU_ID_FILE_END_EMULATION, MainWindow::OnFileMenu)
EVT_MENU_RANGE(MAINFRAME_MENU_ID_FILE_RECENT_0 + 0, MAINFRAME_MENU_ID_FILE_RECENT_LAST, MainWindow::OnFileMenu)
// options -> region menu
EVT_MENU_RANGE(MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1, MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12, MainWindow::OnAccountSelect)
EVT_MENU_RANGE(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE, MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE, MainWindow::OnConsoleLanguage)
// options menu
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_AUDIO, MainWindow::OnOptionsInput)
EVT_MENU(MAINFRAME_MENU_ID_OPTIONS_INPUT, MainWindow::OnOptionsInput)
// tools menu
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, MainWindow::OnToolsInput)
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, MainWindow::OnToolsInput)
EVT_MENU(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, MainWindow::OnToolsInput)
//// cpu menu
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_8X, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_4X, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_2X, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_1X, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_05X, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_025X, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_TIMER_SPEED_0125X, MainWindow::OnDebugSetting)
// nfc menu
EVT_MENU(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, MainWindow::OnNFCMenu)
EVT_MENU_RANGE(MAINFRAME_MENU_ID_NFC_RECENT_0 + 0, MAINFRAME_MENU_ID_NFC_RECENT_LAST, MainWindow::OnNFCMenu)
// debug -> logging menu
EVT_MENU_RANGE(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 0, MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 19, MainWindow::OnDebugLoggingToggleFlagGeneric)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO, MainWindow::OnPPCInfoToggle)
// debug -> dump menu
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_TEXTURES, MainWindow::OnDebugDumpUsedTextures)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS, MainWindow::OnDebugDumpUsedShaders)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, MainWindow::OnDebugSetting)
// debug -> Other options
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_RAM, MainWindow::OnDebugSetting)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_DUMP_FST, MainWindow::OnDebugSetting)
// debug -> View ...
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_LOGGING_WINDOW, MainWindow::OnLoggingWindow)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_THREADS, MainWindow::OnDebugViewPPCThreads)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_DEBUGGER, MainWindow::OnDebugViewPPCDebugger)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_AUDIO_DEBUGGER, MainWindow::OnDebugViewAudioDebugger)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS, MainWindow::OnDebugViewTextureRelations)
EVT_MENU(MAINFRAME_MENU_ID_DEBUG_SHOW_FRAME_PROFILER, MainWindow::OnDebugSetting)
// help menu
EVT_MENU(MAINFRAME_MENU_ID_HELP_WEB, MainWindow::OnHelpVistWebpage)
EVT_MENU(MAINFRAME_MENU_ID_HELP_ABOUT, MainWindow::OnHelpAbout)
EVT_MENU(MAINFRAME_MENU_ID_HELP_UPDATE, MainWindow::OnHelpUpdate)
EVT_MENU(MAINFRAME_MENU_ID_HELP_GETTING_STARTED, MainWindow::OnHelpGettingStarted)
// misc
EVT_COMMAND(wxID_ANY, wxEVT_REQUEST_GAMELIST_REFRESH, MainWindow::OnRequestGameListRefresh)
EVT_COMMAND(wxID_ANY, wxEVT_GAMELIST_BEGIN_UPDATE, MainWindow::OnGameListBeginUpdate)
EVT_COMMAND(wxID_ANY, wxEVT_GAMELIST_END_UPDATE, MainWindow::OnGameListEndUpdate)
EVT_COMMAND(wxID_ANY, wxEVT_ACCOUNTLIST_REFRESH, MainWindow::OnAccountListRefresh)
EVT_COMMAND(wxID_ANY, wxEVT_SET_WINDOW_TITLE, MainWindow::OnSetWindowTitle)
wxEND_EVENT_TABLE()
class wxGameDropTarget : public wxFileDropTarget
{
public:
wxGameDropTarget(MainWindow* window) : m_window(window) {}
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override
{
if(!m_window->IsGameLaunched() && filenames.GetCount() == 1)
return m_window->FileLoad(filenames[0].wc_str(), wxLaunchGameEvent::INITIATED_BY::DRAG_AND_DROP);
return false;
}
private:
MainWindow* m_window;
};
class wxAmiiboDropTarget : public wxFileDropTarget
{
public:
wxAmiiboDropTarget(MainWindow* window) : m_window(window) {}
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override
{
if (!m_window->IsGameLaunched() || filenames.GetCount() != 1)
return false;
uint32 nfcError;
if (nnNfp_touchNfcTagFromFile(filenames[0].wc_str(), &nfcError))
{
GetConfig().AddRecentNfcFile((wchar_t*)filenames[0].wc_str());
m_window->UpdateNFCMenu();
return true;
}
else
{
if (nfcError == NFC_ERROR_NO_ACCESS)
wxMessageBox(_("Cannot open file"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
else if (nfcError == NFC_ERROR_INVALID_FILE_FORMAT)
wxMessageBox(_("Not a valid NFC NTAG215 file"), _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
}
private:
MainWindow* m_window;
};
MainWindow::MainWindow()
: wxFrame(nullptr, -1, GetInitialWindowTitle(), wxDefaultPosition, wxSize(1280, 720), wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN | wxRESIZE_BORDER)
{
gui_initHandleContextFromWxWidgetsWindow(g_window_info.window_main, this);
g_mainFrame = this;
RecreateMenu();
SetClientSize(1280, 720);
SetIcon(wxICON(M_WND_ICON128));
#if BOOST_OS_WINDOWS
HICON hWindowIcon = (HICON)LoadImageA(NULL, "M_WND_ICON16", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
SendMessage(this->GetHWND(), WM_SETICON, ICON_SMALL, (LPARAM)hWindowIcon);
#endif
auto* main_sizer = new wxBoxSizer(wxVERTICAL);
if (!LaunchSettings::GetLoadFile().has_value())
{
{
m_main_panel = new wxPanel(this);
auto* sizer = new wxBoxSizer(wxVERTICAL);
// game list
m_game_list = new wxGameList(m_main_panel, MAINFRAME_GAMELIST_ID);
m_game_list->Bind(wxEVT_OPEN_SETTINGS, [this](auto&) {OpenSettings(); });
m_game_list->SetDropTarget(new wxGameDropTarget(this));
sizer->Add(m_game_list, 1, wxEXPAND);
// info, warning bar
m_info_bar = new wxInfoBar(m_main_panel);
m_info_bar->SetShowHideEffects(wxSHOW_EFFECT_BLEND, wxSHOW_EFFECT_BLEND);
m_info_bar->SetEffectDuration(500);
sizer->Add(m_info_bar, 0, wxALL | wxEXPAND, 5);
m_main_panel->SetSizer(sizer);
main_sizer->Add(m_main_panel, 1, wxEXPAND, 0, nullptr);
}
}
else
{
// launching game via -g option. Dont setup or load game list
m_game_list = nullptr;
m_info_bar = nullptr;
}
SetSizer(main_sizer);
m_last_mouse_move_time = std::chrono::steady_clock::now();
m_timer = new wxTimer(this, MAINFRAME_ID_TIMER1);
m_timer->Start(500);
LoadSettings();
#ifdef ENABLE_DISCORD_RPC
auto& config = GetConfig();
if (config.use_discord_presence)
m_discord = std::make_unique<DiscordPresence>();
#endif
Bind(wxEVT_OPEN_GRAPHIC_PACK, &MainWindow::OnGraphicWindowOpen, this);
Bind(wxEVT_LAUNCH_GAME, &MainWindow::OnLaunchFromFile, this);
if (LaunchSettings::GetLoadFile().has_value())
{
MainWindow::RequestLaunchGame(LaunchSettings::GetLoadFile().value(), wxLaunchGameEvent::INITIATED_BY::COMMAND_LINE);
}
}
MainWindow::~MainWindow()
{
if (m_padView)
{
//delete m_padView;
m_padView->Destroy();
m_padView = nullptr;
}
m_timer->Stop();
std::unique_lock lock(g_mutex);
g_mainFrame = nullptr;
}
wxString MainWindow::GetInitialWindowTitle()
{
return BUILD_VERSION_WITH_NAME_STRING;
}
void MainWindow::ShowGettingStartedDialog()
{
GettingStartedDialog dia(this);
dia.ShowModal();
if (dia.HasGamePathChanged() || dia.HasMLCChanged())
m_game_list->ReloadGameEntries();
TogglePadView();
auto& config = GetConfig();
m_padViewMenuItem->Check(config.pad_open.GetValue());
m_fullscreenMenuItem->Check(config.fullscreen.GetValue());
}
namespace coreinit
{
void OSSchedulerEnd();
};
void MainWindow::OnClose(wxCloseEvent& event)
{
if(m_game_list)
m_game_list->OnClose(event);
if (!IsMaximized() && !gui_isFullScreen())
m_restored_size = GetSize();
SaveSettings();
m_timer->Stop();
event.Skip();
CafeSystem::ShutdownTitle();
DestroyCanvas();
}
bool MainWindow::InstallUpdate(const fs::path& metaFilePath)
{
try
{
GameUpdateWindow frame(*this, metaFilePath);
const int updateResult = frame.ShowModal();
if (updateResult == wxID_OK)
{
CafeTitleList::AddTitleFromPath(frame.GetTargetPath()); // this will also send a notification to the game list which will update the entry
wxMessageBox(_("Title installed!"), _("Success"));
return true;
}
else
{
if (frame.GetExceptionMessage().empty())
wxMessageBox(_("Title installation has been canceled!"));
else
{
throw std::runtime_error(frame.GetExceptionMessage());
}
}
}
catch(const AbortException&)
{
// ignored
}
catch (const std::exception& ex)
{
wxMessageBox(ex.what(), _("Update error"));
}
return false;
}
bool MainWindow::FileLoad(std::wstring fileName, wxLaunchGameEvent::INITIATED_BY initiatedBy)
{
const fs::path launchPath = fs::path(fileName);
TitleInfo launchTitle{ launchPath };
if (launchTitle.IsValid())
{
// the title might not be in the TitleList, so we add it as a temporary entry
CafeTitleList::AddTitleFromPath(launchPath);
// title is valid, launch from TitleId
TitleId baseTitleId;
if (!CafeTitleList::FindBaseTitleId(launchTitle.GetAppTitleId(), baseTitleId))
{
wxString t = _("Unable to launch game because the base files were not found.");
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitle(baseTitleId);
if (r == CafeSystem::STATUS_CODE::INVALID_RPX)
{
cemu_assert_debug(false);
return false;
}
else if (r == CafeSystem::STATUS_CODE::UNABLE_TO_MOUNT)
{
wxString t = _("Unable to mount title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nFile which failed to load:\n");
t.append(fileName);
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
else if (r != CafeSystem::STATUS_CODE::SUCCESS)
{
wxString t = _("Failed to launch game.");
t.append(fileName);
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
}
else //if (launchTitle.GetFormat() == TitleInfo::TitleDataFormat::INVALID_STRUCTURE )
{
// title is invalid, if its an RPX/ELF we can launch it directly
// otherwise its an error
CafeTitleFileType fileType = DetermineCafeSystemFileType(launchPath);
if (fileType == CafeTitleFileType::RPX || fileType == CafeTitleFileType::ELF)
{
CafeSystem::STATUS_CODE r = CafeSystem::PrepareForegroundTitleFromStandaloneRPX(launchPath);
if (r != CafeSystem::STATUS_CODE::SUCCESS)
{
cemu_assert_debug(false); // todo
wxString t = _("Failed to launch executable. Path: ");
t.append(fileName);
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
}
else if (initiatedBy == wxLaunchGameEvent::INITIATED_BY::GAME_LIST)
{
wxString t = _("Unable to launch title.\nMake sure the configured game paths are still valid and refresh the game list.\n\nPath which failed to load:\n");
t.append(fileName);
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
else if (initiatedBy == wxLaunchGameEvent::INITIATED_BY::MENU ||
initiatedBy == wxLaunchGameEvent::INITIATED_BY::COMMAND_LINE)
{
wxString t = _("Unable to launch game\nPath:\n");
t.append(fileName);
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
else
{
wxString t = _("Unable to launch game\nPath:\n");
t.append(fileName);
wxMessageBox(t, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
return false;
}
}
if(launchTitle.IsValid())
GetConfig().AddRecentlyLaunchedFile(launchTitle.GetPath().generic_wstring());
else
GetConfig().AddRecentlyLaunchedFile(fileName);
wxWindowUpdateLocker lock(this);
auto* main_sizer = GetSizer();
// remove old gamelist panel
if (m_main_panel)
{
m_main_panel->Hide();
main_sizer->Detach(m_main_panel);
}
// create render canvas rendering
m_game_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxWANTS_CHARS);
auto* sizer = new wxBoxSizer(wxVERTICAL);
// shouldn't be needed, but who knows
m_game_panel->Bind(wxEVT_KEY_UP, &MainWindow::OnKeyUp, this);
m_game_panel->Bind(wxEVT_CHAR, &MainWindow::OnChar, this);
m_game_panel->SetSizer(sizer);
main_sizer->Add(m_game_panel, 1, wxEXPAND, 0, nullptr);
m_game_launched = true;
m_loadMenuItem->Enable(false);
m_installUpdateMenuItem->Enable(false);
m_memorySearcherMenuItem->Enable(true);
if (m_game_list)
{
delete m_game_list;
m_game_list = nullptr;
}
const auto game_name = GetGameName(fileName);
m_launched_game_name = boost::nowide::narrow(game_name);
#ifdef ENABLE_DISCORD_RPC
if (m_discord)
m_discord->UpdatePresence(DiscordPresence::Playing, m_launched_game_name);
#endif
if (ActiveSettings::FullscreenEnabled())
SetFullScreen(true);
CreateCanvas();
CafeSystem::LaunchForegroundTitle();
RecreateMenu();
return true;
}
void MainWindow::OnLaunchFromFile(wxLaunchGameEvent& event)
{
if (event.GetPath().empty())
return;
FileLoad(event.GetPath().generic_wstring(), event.GetInitiatedBy());
}
void MainWindow::OnFileMenu(wxCommandEvent& event)
{
const auto menuId = event.GetId();
if (menuId == MAINFRAME_MENU_ID_FILE_LOAD)
{
const auto wildcard = wxStringFormat2(
"{}|*.wud;*.wux;*.wua;*.iso;*.rpx;*.elf"
"|{}|*.wud;*.wux;*.iso"
"|{}|*.wua"
"|{}|*.rpx;*.elf"
"|{}|*",
_("All Wii U files (wud, wux, wua, iso, rpx, elf)"),
_("Wii U image (wud, wux, iso, wad)"),
_("Wii U archive (wua)"),
_("Wii U executable (rpx, elf)"),
_("All files (*.*)")
);
wxFileDialog openFileDialog(this, _("Open file to launch"), wxEmptyString, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() == wxID_CANCEL)
return;
const wxString wxStrFilePath = openFileDialog.GetPath();
FileLoad(wxStrFilePath.wc_str(), wxLaunchGameEvent::INITIATED_BY::MENU);
}
else if (menuId >= MAINFRAME_MENU_ID_FILE_RECENT_0 && menuId <= MAINFRAME_MENU_ID_FILE_RECENT_LAST)
{
const auto& config = GetConfig();
const size_t index = menuId - MAINFRAME_MENU_ID_FILE_RECENT_0;
if (index < config.recent_launch_files.size())
{
const auto& path = config.recent_launch_files[index];
if (!path.empty())
FileLoad(path, wxLaunchGameEvent::INITIATED_BY::MENU);
}
}
else if (menuId == MAINFRAME_MENU_ID_FILE_END_EMULATION)
{
CafeSystem::ShutdownTitle();
DestroyCanvas();
m_game_launched = false;
RecreateMenu();
}
}
void MainWindow::OnInstallUpdate(wxCommandEvent& event)
{
while (true)
{
wxDirDialog openDirDialog(nullptr, _("Select folder of title to install"), "", wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST, wxDefaultPosition, wxDefaultSize, _("Select the folder that stores your update, DLC or base game files"));
int modalChoice = openDirDialog.ShowModal();
if (modalChoice == wxID_CANCEL)
break;
if (modalChoice == wxID_OK)
{
#if BOOST_OS_LINUX || BOOST_OS_MACOS
fs::path dirPath((const char*)(openDirDialog.GetPath().fn_str()));
#else
fs::path dirPath(openDirDialog.GetPath().fn_str());
#endif
if ((dirPath.filename() == "code" || dirPath.filename() == "content" || dirPath.filename() == "meta") && dirPath.has_parent_path())
{
if (!fs::exists(dirPath.parent_path() / "code") || !fs::exists(dirPath.parent_path() / "content") || !fs::exists(dirPath.parent_path() / "meta"))
{
wxMessageBox(wxStringFormat2(_("The (parent) folder of the title you selected is missing at least one of the required subfolders (\"code\", \"content\" and \"meta\")\nMake sure that the files are complete."), dirPath.filename().string()));
continue;
}
else
dirPath = dirPath.parent_path();
}
if (!fs::exists(dirPath))
wxMessageBox(_("The folder you have selected cannot be found on your system."));
else if (!fs::exists(dirPath / "meta" / "meta.xml"))
wxMessageBox(_("Unable to find the /meta/meta.xml file inside the selected folder."));
else
{
InstallUpdate(dirPath);
return;
}
}
}
}
void MainWindow::OnNFCMenu(wxCommandEvent& event)
{
if (event.GetId() == MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE)
{
wxFileDialog
openFileDialog(this, _("Open file to load"), "", "",
"All NFC files (bin, dat, nfc)|*.bin;*.dat;*.nfc|All files (*.*)|*", wxFD_OPEN | wxFD_FILE_MUST_EXIST); // TRANSLATE
if (openFileDialog.ShowModal() == wxID_CANCEL)
return;
wxString wxStrFilePath = openFileDialog.GetPath();
uint32 nfcError;
if (nnNfp_touchNfcTagFromFile(wxStrFilePath.wc_str(), &nfcError) == false)
{
if (nfcError == NFC_ERROR_NO_ACCESS)
wxMessageBox(_("Cannot open file"));
else if (nfcError == NFC_ERROR_INVALID_FILE_FORMAT)
wxMessageBox(_("Not a valid NFC NTAG215 file"));
}
else
{
GetConfig().AddRecentNfcFile((wchar_t*)wxStrFilePath.wc_str());
UpdateNFCMenu();
}
}
else if (event.GetId() >= MAINFRAME_MENU_ID_NFC_RECENT_0 && event.GetId() <= MAINFRAME_MENU_ID_NFC_RECENT_LAST)
{
const size_t index = event.GetId() - MAINFRAME_MENU_ID_NFC_RECENT_0;
auto& config = GetConfig();
if (index < config.recent_nfc_files.size())
{
const auto& path = config.recent_nfc_files[index];
if (!path.empty())
{
uint32 nfcError = 0;
if (nnNfp_touchNfcTagFromFile(path.c_str(), &nfcError) == false)
{
if (nfcError == NFC_ERROR_NO_ACCESS)
wxMessageBox(_("Cannot open file"));
else if (nfcError == NFC_ERROR_INVALID_FILE_FORMAT)
wxMessageBox(_("Not a valid NFC NTAG215 file"));
}
else
{
config.AddRecentNfcFile(path);
UpdateNFCMenu();
}
}
}
}
}
void MainWindow::OnFileExit(wxCommandEvent& event)
{
// todo: Safely clean up everything
SaveSettings();
exit(0);
}
void MainWindow::TogglePadView()
{
const auto& config = GetConfig();
if (config.pad_open)
{
if (m_padView)
return;
m_padView = new PadViewFrame(this);
m_padView->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnPadClose, this);
m_padView->Show(true);
m_padView->Initialize();
if (m_game_launched)
m_padView->InitializeRenderCanvas();
}
else if (m_padView)
{
m_padView->Destroy();
m_padView = nullptr;
}
}
#if BOOST_OS_WINDOWS
#ifndef DBT_DEVNODES_CHANGED
#define DBT_DEVNODES_CHANGED (0x0007)
#endif
WXLRESULT MainWindow::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
{
if (nMsg == WM_DEVICECHANGE)
{
if (wParam == DBT_DEVNODES_CHANGED)
{
InputManager::instance().on_device_changed();
}
}
return wxFrame::MSWWindowProc(nMsg, wParam, lParam);
}
#endif
void MainWindow::OpenSettings()
{
auto& config = GetConfig();
const auto language = config.language;
GeneralSettings2 frame(this, m_game_launched);
frame.ShowModal();
const bool paths_modified = frame.ShouldReloadGamelist();
const bool mlc_modified = frame.MLCModified();
frame.Destroy();
if (paths_modified)
m_game_list->ReloadGameEntries(false);
else
SaveSettings();
#ifdef ENABLE_DISCORD_RPC
if (config.use_discord_presence)
{
if (!m_discord)
{
m_discord = std::make_unique<DiscordPresence>();
if (!m_launched_game_name.empty())
m_discord->UpdatePresence(DiscordPresence::Playing, m_launched_game_name);
}
}
else
m_discord.reset();
#endif
if(config.check_update && !m_game_launched)
m_update_available = CemuUpdateWindow::IsUpdateAvailableAsync();
if (mlc_modified)
RecreateMenu();
if (!config.fullscreen_menubar && IsFullScreen())
SetMenuVisible(false);
if (language != config.language)
wxMessageBox(_("Cemu must be restarted to apply the selected UI language."), _("Information"), wxOK | wxCENTRE, this); // TODO: change language to newly selected one
}
void MainWindow::OnOptionsInput(wxCommandEvent& event)
{
switch (event.GetId())
{
case MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN:
{
const bool state = m_fullscreenMenuItem->IsChecked();
SetFullScreen(state);
break;
}
case MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW:
{
GetConfig().pad_open = !GetConfig().pad_open;
g_config.Save();
TogglePadView();
break;
}
case MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2:
{
if (m_graphic_pack_window)
return;
uint64 titleId = 0;
if (CafeSystem::IsTitleRunning())
titleId = CafeSystem::GetForegroundTitleId();
m_graphic_pack_window = new GraphicPacksWindow2(this, titleId);
m_graphic_pack_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnGraphicWindowClose, this);
m_graphic_pack_window->Show(true);
break;
}
case MAINFRAME_MENU_ID_OPTIONS_GENERAL2:
{
OpenSettings();
break;
}
case MAINFRAME_MENU_ID_OPTIONS_INPUT:
{
auto* frame = new InputSettings2(this);
frame->ShowModal();
frame->Destroy();
break;
}
}
}
void MainWindow::OnAccountSelect(wxCommandEvent& event)
{
const int index = event.GetId() - MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1;
const auto& accounts = Account::GetAccounts();
wxASSERT(index >= 0 && index < (int)accounts.size());
auto& config = GetConfig();
config.account.m_persistent_id = accounts[index].GetPersistentId();
// config.account.online_enabled.value = false; // reset online for safety
g_config.Save();
}
//void MainWindow::OnConsoleRegion(wxCommandEvent& event)
//{
// switch (event.GetId())
// {
// case MAINFRAME_MENU_ID_OPTIONS_REGION_AUTO:
// GetConfig().console_region = ConsoleRegion::Auto;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_JPN:
// GetConfig().console_region = ConsoleRegion::JPN;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_USA:
// GetConfig().console_region = ConsoleRegion::USA;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_EUR:
// GetConfig().console_region = ConsoleRegion::EUR;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_CHN:
// GetConfig().console_region = ConsoleRegion::CHN;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_KOR:
// GetConfig().console_region = ConsoleRegion::KOR;
// break;
// case MAINFRAME_MENU_ID_OPTIONS_REGION_TWN:
// GetConfig().console_region = ConsoleRegion::TWN;
// break;
// default:
// cemu_assert_debug(false);
// }
//
// g_config.Save();
//}
void MainWindow::OnConsoleLanguage(wxCommandEvent& event)
{
switch (event.GetId())
{
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE:
GetConfig().console_language = CafeConsoleLanguage::JA;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH:
GetConfig().console_language = CafeConsoleLanguage::EN;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH:
GetConfig().console_language = CafeConsoleLanguage::FR;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_GERMAN:
GetConfig().console_language = CafeConsoleLanguage::DE;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ITALIAN:
GetConfig().console_language = CafeConsoleLanguage::IT;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_SPANISH:
GetConfig().console_language = CafeConsoleLanguage::ES;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_CHINESE:
GetConfig().console_language = CafeConsoleLanguage::ZH;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_KOREAN:
GetConfig().console_language = CafeConsoleLanguage::KO;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_DUTCH:
GetConfig().console_language = CafeConsoleLanguage::NL;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_PORTUGUESE:
GetConfig().console_language = CafeConsoleLanguage::PT;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_RUSSIAN:
GetConfig().console_language = CafeConsoleLanguage::RU;
break;
case MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE:
GetConfig().console_language = CafeConsoleLanguage::TW;
break;
default:
cemu_assert_debug(false);
}
g_config.Save();
}
//void MainWindow::OnCPUMode(wxCommandEvent& event)
//{
// if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_SINGLECORE_INTERPRETER)
// GetConfig().cpu_mode = CPUMode::SinglecoreInterpreter;
// else if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_SINGLECORE_RECOMPILER)
// GetConfig().cpu_mode = CPUMode::SinglecoreRecompiler;
// else if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_DUALCORE_RECOMPILER)
// GetConfig().cpu_mode = CPUMode::DualcoreRecompiler;
// else if (event.GetId() == MAINFRAME_MENU_ID_CPU_MODE_TRIPLECORE_RECOMPILER)
// GetConfig().cpu_mode = CPUMode::TriplecoreRecompiler;
// else
// cemu_assert_debug(false);
//
// g_config.Save();
//}
void MainWindow::OnDebugSetting(wxCommandEvent& event)
{
if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN)
GetConfig().render_upside_down = event.IsChecked();
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS)
{
GetConfig().vk_accurate_barriers = event.IsChecked();
if(!GetConfig().vk_accurate_barriers)
wxMessageBox(_("Warning: Disabling the accurate barriers option will lead to flickering graphics but may improve performance. It is highly recommended to leave it turned on."), _("Accurate barriers are off"), wxOK);
}
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY)
ActiveSettings::EnableAudioOnlyAux(event.IsChecked());
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_RAM)
memory_createDump();
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_FST)
{
/* int msgBoxAnswer = wxMessageBox(_("All files from the currently running game will be dumped to /dump/<gamefolder>. This process can take a few minutes."),
_("Dump WUD"), wxOK | wxCANCEL | wxICON_WARNING);
if (msgBoxAnswer == wxOK)
{
volumeFST_dump(bootGame_getMountedWUD());
wxMessageBox(_("Dump complete"));
}*/
}
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_SHOW_FRAME_PROFILER)
{
ActiveSettings::EnableFrameProfiler(event.IsChecked());
}
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS)
{
// toggle debug -> dump -> curl requests
const bool state = event.IsChecked();
ActiveSettings::EnableDumpLibcurlRequests(state);
if (state)
{
try
{
const auto path = CemuApp::GetCemuPath(L"dump\\curl").ToStdWstring();
fs::create_directories(path);
}
catch (const std::exception& ex)
{
SystemException sys(ex);
forceLog_printf("error when creating dump curl folder: %s", sys.what());
ActiveSettings::EnableDumpLibcurlRequests(false);
}
}
}
else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_8X)
ActiveSettings::SetTimerShiftFactor(0);
else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_4X)
ActiveSettings::SetTimerShiftFactor(1);
else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_2X)
ActiveSettings::SetTimerShiftFactor(2);
else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_1X)
ActiveSettings::SetTimerShiftFactor(3);
else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_05X)
ActiveSettings::SetTimerShiftFactor(4);
else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_025X)
ActiveSettings::SetTimerShiftFactor(5);
else if (event.GetId() == MAINFRAME_MENU_ID_TIMER_SPEED_0125X)
ActiveSettings::SetTimerShiftFactor(6);
else
cemu_assert_debug(false);
g_config.Save();
}
void MainWindow::OnDebugLoggingToggleFlagGeneric(wxCommandEvent& event)
{
sint32 loggingIdBase = MAINFRAME_MENU_ID_DEBUG_LOGGING0;
sint32 id = event.GetId();
if (id >= loggingIdBase && id < (MAINFRAME_MENU_ID_DEBUG_LOGGING0 + 20))
{
cafeLog_setLoggingFlagEnable(id - loggingIdBase, event.IsChecked());
}
}
void MainWindow::OnPPCInfoToggle(wxCommandEvent& event)
{
GetConfig().advanced_ppc_logging = !GetConfig().advanced_ppc_logging.GetValue();
g_config.Save();
}
void MainWindow::OnDebugDumpUsedTextures(wxCommandEvent& event)
{
const bool value = event.IsChecked();
ActiveSettings::EnableDumpTextures(value);
if (value)
{
try
{
// create directory
const auto path = CemuApp::GetCemuPath(L"dump\\textures");
fs::create_directories(path.ToStdWstring());
}
catch (const std::exception& ex)
{
SystemException sys(ex);
forceLog_printf("can't create texture dump folder: %s", ex.what());
ActiveSettings::EnableDumpTextures(false);
}
}
}
void MainWindow::OnDebugDumpUsedShaders(wxCommandEvent& event)
{
const bool value = event.IsChecked();
ActiveSettings::EnableDumpShaders(value);
if (value)
{
try
{
// create directory
const auto path = CemuApp::GetCemuPath(L"dump\\shaders");
fs::create_directories(path.ToStdWstring());
}
catch (const std::exception & ex)
{
SystemException sys(ex);
forceLog_printf("can't create shaders dump folder: %s", ex.what());
ActiveSettings::EnableDumpShaders(false);
}
}
}
void MainWindow::OnLoggingWindow(wxCommandEvent& event)
{
if(m_logging_window)
return;
m_logging_window = new LoggingWindow(this);
m_logging_window->Bind(wxEVT_CLOSE_WINDOW,
[this](wxCloseEvent& event) {
m_logging_window = nullptr;
event.Skip();
});
m_logging_window->Show(true);
}
void MainWindow::OnDebugViewPPCThreads(wxCommandEvent& event)
{
auto frame = new DebugPPCThreadsWindow(*this);
frame->Show(true);
}
void MainWindow::OnDebugViewPPCDebugger(wxCommandEvent& event)
{
if (m_debugger_window && m_debugger_window->IsShown())
{
m_debugger_window->Close();
m_debugger_window = nullptr;
return;
}
auto rect = GetDesktopRect();
/*
sint32 new_width = max(rect.GetWidth() * 0.70, rect.GetWidth() - 850);
this->SetSize(new_width, 480);*/
this->SetSize(800, 450 + 50);
this->CenterOnScreen();
auto pos = this->GetPosition();
pos.y = std::min(pos.y + 200, rect.GetHeight() - 400);
this->SetPosition(pos);
m_debugger_window = new DebuggerWindow2(*this, rect);
m_debugger_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnDebuggerClose, this);
m_debugger_window->Show(true);
}
void MainWindow::OnDebugViewAudioDebugger(wxCommandEvent& event)
{
auto frame = new AudioDebuggerWindow(*this);
frame->Show(true);
}
void MainWindow::OnDebugViewTextureRelations(wxCommandEvent& event)
{
openTextureViewer(*this);
}
void MainWindow::ShowCursor(bool state)
{
#if BOOST_OS_WINDOWS
CURSORINFO info{};
info.cbSize = sizeof(CURSORINFO);
GetCursorInfo(&info);
const bool visible = info.flags == CURSOR_SHOWING;
if (state == visible)
return;
int counter = 0;
if(state)
{
do
{
counter = ::ShowCursor(TRUE);
} while (counter < 0);
}
else
{
do
{
counter = ::ShowCursor(FALSE);
} while (counter >= 0);
}
#else
if (state)
{
wxSetCursor(wxNullCursor); // restore system default cursor
}
else
{
wxSetCursor(wxCursor(wxCURSOR_BLANK));
}
#endif
}
uintptr_t MainWindow::GetRenderCanvasHWND()
{
// deprecated. We can use the global cross-platform window info structs now
#if BOOST_OS_WINDOWS
if (!m_render_canvas)
return 0;
return (uintptr_t)m_render_canvas->GetHWND();
#else
return 0;
#endif
}
wxRect MainWindow::GetDesktopRect()
{
const auto pos = GetPosition();
const auto middle = pos.x + GetSize().GetWidth() / 2;
const auto displayCount = wxDisplay::GetCount();
for (uint32 i = 0; i < displayCount; ++i)
{
wxDisplay display(i);
if (!display.IsOk())
continue;
const auto geo = display.GetGeometry();
if (geo.x <= middle && middle <= geo.x + geo.width)
return geo;
}
return { 0,0,800,600 };
}
void MainWindow::LoadSettings()
{
g_config.Load();
const auto& config = GetConfig();
if(config.check_update)
m_update_available = CemuUpdateWindow::IsUpdateAvailableAsync();
if (config.window_position != Vector2i{ -1,-1 })
this->SetPosition({ config.window_position.x, config.window_position.y });
if (config.window_size != Vector2i{ -1,-1 })
{
this->SetSize({ config.window_size.x, config.window_size.y });
if (config.window_maximized)
this->Maximize();
}
if (config.pad_position != Vector2i{ -1,-1 })
{
g_window_info.restored_pad_x = config.pad_position.x;
g_window_info.restored_pad_y = config.pad_position.y;
}
if (config.pad_size != Vector2i{ -1,-1 })
{
g_window_info.restored_pad_width = config.pad_size.x;
g_window_info.restored_pad_height = config.pad_size.y;
g_window_info.pad_maximized = config.pad_maximized;
}
this->TogglePadView();
if(m_game_list)
m_game_list->LoadConfig();
}
void MainWindow::SaveSettings()
{
auto lock = g_config.Lock();
auto& config = GetConfig();
if (config.window_position != Vector2i{ -1,-1 })
{
config.window_position.x = m_restored_position.x;
config.window_position.y = m_restored_position.y;
}
if (config.window_size != Vector2i{ -1,-1 })
{
config.window_size.x = m_restored_size.x;
config.window_size.y = m_restored_size.y;
config.window_maximized = IsMaximized();
}
else
{
config.window_maximized = false;
}
config.pad_open = m_padView != nullptr;
if (config.pad_position != Vector2i{ -1,-1 } && g_window_info.restored_pad_x != -1)
{
config.pad_position.x = g_window_info.restored_pad_x;
config.pad_position.y = g_window_info.restored_pad_y;
}
if (config.pad_size != Vector2i{ -1,-1 } && g_window_info.restored_pad_width != -1)
{
config.pad_size.x = g_window_info.restored_pad_width;
config.pad_size.y = g_window_info.restored_pad_height;
config.pad_maximized = g_window_info.pad_maximized;
}
else
{
config.pad_maximized = false;
}
if(m_game_list)
m_game_list->SaveConfig();
g_config.Save();
}
void MainWindow::OnMouseMove(wxMouseEvent& event)
{
event.Skip();
m_last_mouse_move_time = std::chrono::steady_clock::now();
m_mouse_position = wxGetMousePosition();
ShowCursor(true);
auto& instance = InputManager::instance();
std::unique_lock lock(instance.m_main_mouse.m_mutex);
instance.m_main_mouse.position = { event.GetPosition().x, event.GetPosition().y };
lock.unlock();
if (!IsFullScreen())
return;
const auto& config = GetConfig();
// if mouse goes to upper screen then show our menu in fullscreen mode
if (config.fullscreen_menubar)
SetMenuVisible(event.GetPosition().y < 50);
}
void MainWindow::OnMouseLeft(wxMouseEvent& event)
{
auto& instance = InputManager::instance();
std::scoped_lock lock(instance.m_main_mouse.m_mutex);
instance.m_main_mouse.left_down = event.ButtonDown(wxMOUSE_BTN_LEFT);
instance.m_main_mouse.position = { event.GetPosition().x, event.GetPosition().y };
if (event.ButtonDown(wxMOUSE_BTN_LEFT))
instance.m_main_mouse.left_down_toggle = true;
event.Skip();
}
void MainWindow::OnMouseRight(wxMouseEvent& event)
{
auto& instance = InputManager::instance();
std::scoped_lock lock(instance.m_main_mouse.m_mutex);
instance.m_main_mouse.right_down = event.ButtonDown(wxMOUSE_BTN_RIGHT);
instance.m_main_mouse.position = { event.GetPosition().x, event.GetPosition().y };
if(event.ButtonDown(wxMOUSE_BTN_RIGHT))
instance.m_main_mouse.right_down_toggle = true;
event.Skip();
}
void MainWindow::OnGameListBeginUpdate(wxCommandEvent& event)
{
if (m_game_list->IsShown())
m_info_bar->ShowMessage(_("Updating game list..."));
}
void MainWindow::OnGameListEndUpdate(wxCommandEvent& event)
{
m_info_bar->Dismiss();
}
void MainWindow::OnAccountListRefresh(wxCommandEvent& event)
{
RecreateMenu();
}
void MainWindow::OnRequestGameListRefresh(wxCommandEvent& event)
{
m_game_list->ReloadGameEntries(false);
}
void MainWindow::OnSetWindowTitle(wxCommandEvent& event)
{
this->SetTitle(event.GetString());
}
void MainWindow::OnKeyUp(wxKeyEvent& event)
{
event.Skip();
if (swkbd_hasKeyboardInputHook())
return;
const auto code = event.GetKeyCode();
if (code == WXK_ESCAPE)
SetFullScreen(false);
else if (code == WXK_RETURN && event.AltDown())
SetFullScreen(!IsFullScreen());
#ifdef PUBLIC_RELEASE
else if (code == WXK_F12)
#else
else if (code == WXK_F11)
#endif
g_window_info.has_screenshot_request = true; // async screenshot request
}
void MainWindow::OnChar(wxKeyEvent& event)
{
if (swkbd_hasKeyboardInputHook())
swkbd_keyInput(event.GetUnicodeKey());
event.Skip();
}
void MainWindow::OnToolsInput(wxCommandEvent& event)
{
const auto id = event.GetId();
switch (id)
{
case MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER:
{
if (m_toolWindow)
m_toolWindow->SetFocus();
else
{
m_toolWindow = new MemorySearcherTool(this);
m_toolWindow->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnMemorySearcherClose, this);
m_toolWindow->Show(true);
}
break;
}
case MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER:
case MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER:
{
const auto default_tab = id == MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER ? TitleManagerPage::TitleManager : TitleManagerPage::DownloadManager;
if (m_title_manager)
m_title_manager->SetFocusAndTab(default_tab);
else
{
m_title_manager = new TitleManager(this, default_tab);
m_title_manager->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event)
{
m_title_manager = nullptr;
event.Skip();
});
m_title_manager->Show();
}
}
break;
}
}
void MainWindow::OnGesturePan(wxPanGestureEvent& event)
{
auto& instance = InputManager::instance();
std::scoped_lock lock(instance.m_main_touch.m_mutex);
instance.m_main_touch.position = { event.GetPosition().x, event.GetPosition().y };
instance.m_main_touch.left_down = event.IsGestureStart() || !event.IsGestureEnd();
if (event.IsGestureStart() || !event.IsGestureEnd())
instance.m_main_touch.left_down_toggle = true;
event.Skip();
}
void MainWindow::OnGameLoaded()
{
if (m_debugger_window)
m_debugger_window->OnGameLoaded();
}
void MainWindow::AsyncSetTitle(std::string_view windowTitle)
{
wxCommandEvent set_title_event(wxEVT_SET_WINDOW_TITLE);
set_title_event.SetString(wxHelper::FromUtf8(windowTitle));
g_mainFrame->QueueEvent(set_title_event.Clone());
}
void MainWindow::CreateCanvas()
{
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
else
m_render_canvas = GLCanvas_Create(m_game_panel, wxSize(1280, 720), true);
// mouse events
m_render_canvas->Bind(wxEVT_MOTION, &MainWindow::OnMouseMove, this);
m_render_canvas->Bind(wxEVT_MOUSEWHEEL, &MainWindow::OnMouseWheel, this);
m_render_canvas->Bind(wxEVT_LEFT_DOWN, &MainWindow::OnMouseLeft, this);
m_render_canvas->Bind(wxEVT_LEFT_UP, &MainWindow::OnMouseLeft, this);
m_render_canvas->Bind(wxEVT_RIGHT_DOWN, &MainWindow::OnMouseRight, this);
m_render_canvas->Bind(wxEVT_RIGHT_UP, &MainWindow::OnMouseRight, this);
m_render_canvas->Bind(wxEVT_GESTURE_PAN, &MainWindow::OnGesturePan, this);
// key events
m_render_canvas->Bind(wxEVT_KEY_UP, &MainWindow::OnKeyUp, this);
m_render_canvas->Bind(wxEVT_CHAR, &MainWindow::OnChar, this);
m_render_canvas->SetDropTarget(new wxAmiiboDropTarget(this));
m_game_panel->GetSizer()->Add(m_render_canvas, 1, wxEXPAND, 0, nullptr);
GetSizer()->Layout();
m_render_canvas->SetFocus();
if (m_padView)
m_padView->InitializeRenderCanvas();
}
void MainWindow::DestroyCanvas()
{
if (m_padView)
{
m_padView->Destroy();
m_padView = nullptr;
}
if (m_render_canvas)
{
m_render_canvas->Destroy();
m_render_canvas = nullptr;
}
}
std::wstring MainWindow::GetGameName(std::wstring_view file_name)
{
fs::path path{ std::wstring{file_name} };
const auto extension = path.extension();
if (extension == ".wud" || extension == ".wux")
{
std::unique_ptr<FSTVolume> volume(FSTVolume::OpenFromDiscImage(path));
if (!volume)
return path.filename().generic_wstring();
bool foundFile = false;
std::vector<uint8> metaContent = volume->ExtractFile("meta/meta.xml", &foundFile);
if (!foundFile)
return path.filename().generic_wstring();
namespace xml = tinyxml2;
xml::XMLDocument doc;
doc.Parse((const char*)metaContent.data(), metaContent.size());
// parse meta.xml
xml::XMLElement* root = doc.FirstChildElement("menu");
if (root)
{
xml::XMLElement* element = root->FirstChildElement("longname_en");
if (element)
{
auto game_name = boost::nowide::widen(element->GetText());
const auto it = game_name.find(L'\n');
if (it != std::wstring::npos)
game_name.replace(it, 1, L" - ");
return game_name;
}
}
return path.filename().generic_wstring();
}
else if (extension == ".rpx")
{
if (path.has_parent_path() && path.parent_path().has_parent_path())
{
auto meta_xml = path.parent_path().parent_path() / "meta/meta.xml";
auto metaXmlData = FileStream::LoadIntoMemory(meta_xml);
if (metaXmlData)
{
namespace xml = tinyxml2;
xml::XMLDocument doc;
if (doc.Parse((const char*)metaXmlData->data(), metaXmlData->size()) == xml::XML_SUCCESS)
{
xml::XMLElement* root = doc.FirstChildElement("menu");
if (root)
{
xml::XMLElement* element = root->FirstChildElement("longname_en");
if (element)
{
auto game_name = boost::nowide::widen(element->GetText());
const auto it = game_name.find(L'\n');
if (it != std::wstring::npos)
game_name.replace(it, 1, L" - ");
return game_name;
}
}
}
}
}
}
return path.filename().generic_wstring();
}
void MainWindow::OnSizeEvent(wxSizeEvent& event)
{
if (!IsMaximized() && !gui_isFullScreen())
m_restored_size = GetSize();
const wxSize client_size = GetClientSize();
g_window_info.width = client_size.GetWidth();
g_window_info.height = client_size.GetHeight();
if (m_debugger_window && m_debugger_window->IsShown())
m_debugger_window->OnParentMove(GetPosition(), event.GetSize());
event.Skip();
VsyncDriver_notifyWindowPosChanged();
}
void MainWindow::OnMove(wxMoveEvent& event)
{
if (!IsMaximized() && !gui_isFullScreen())
m_restored_position = GetPosition();
if (m_debugger_window && m_debugger_window->IsShown())
m_debugger_window->OnParentMove(GetPosition(), GetSize());
VsyncDriver_notifyWindowPosChanged();
}
void MainWindow::OnDebuggerClose(wxCloseEvent& event)
{
m_debugger_window = nullptr;
event.Skip();
}
void MainWindow::OnPadClose(wxCloseEvent& event)
{
auto& config = GetConfig();
config.pad_open = false;
if (config.pad_position != Vector2i{ -1,-1 })
m_padView->GetPosition(&config.pad_position.x, &config.pad_position.y);
if (config.pad_size != Vector2i{ -1,-1 })
m_padView->GetSize(&config.pad_size.x, &config.pad_size.y);
g_config.Save();
// already deleted by wxwidget
m_padView = nullptr;
if (m_padViewMenuItem)
m_padViewMenuItem->Check(false);
event.Skip();
}
void MainWindow::OnMemorySearcherClose(wxCloseEvent& event)
{
m_toolWindow = nullptr;
event.Skip();
}
void MainWindow::OnMouseWheel(wxMouseEvent& event)
{
const float delta = event.GetWheelRotation(); // in 120 steps -> max reached ~480 (?)
auto& instance = InputManager::instance();
instance.m_mouse_wheel = (delta / 120.0f);
event.Skip();
}
void MainWindow::SetFullScreen(bool state)
{
// only update config entry if we dont't have launch parameters
if (!LaunchSettings::FullscreenEnabled().has_value())
{
GetConfig().fullscreen = state;
g_config.Save();
}
if (state && !m_game_launched)
return;
g_window_info.is_fullscreen = state;
m_fullscreenMenuItem->Check(state);
this->ShowFullScreen(state);
if (state)
m_menu_visible = false; // menu gets always disabled by wxFULLSCREEN_NOMENUBAR
else
SetMenuVisible(true);
}
void MainWindow::SetMenuVisible(bool state)
{
if (m_menu_visible == state)
return;
SetMenuBar(state ? m_menuBar : nullptr);
m_menu_visible = state;
}
void MainWindow::UpdateNFCMenu()
{
if (m_nfcMenuSeparator0)
{
m_nfcMenu->Remove(m_nfcMenuSeparator0->GetId());
m_nfcMenuSeparator0 = nullptr;
}
// remove recent files list
for (sint32 i = 0; i < CemuConfig::kMaxRecentEntries; i++)
{
if (m_nfcMenu->FindChildItem(MAINFRAME_MENU_ID_NFC_RECENT_0 + i) == nullptr)
continue;
m_nfcMenu->Remove(MAINFRAME_MENU_ID_NFC_RECENT_0 + i);
}
// add entries
const auto& config = GetConfig();
sint32 recentFileIndex = 0;
for (size_t i = 0; i < config.recent_nfc_files.size(); i++)
{
const auto& entry = config.recent_nfc_files[i];
if (entry.empty())
continue;
if (!fs::exists(entry))
continue;
if (recentFileIndex == 0)
m_nfcMenuSeparator0 = m_nfcMenu->AppendSeparator();
m_nfcMenu->Append(MAINFRAME_MENU_ID_NFC_RECENT_0 + i, fmt::format(L"{}. {}", recentFileIndex, entry ));
recentFileIndex++;
if (recentFileIndex >= 12)
break;
}
}
bool MainWindow::IsMenuHidden() const
{
return m_menu_visible;
}
void MainWindow::OnTimer(wxTimerEvent& event)
{
if(m_update_available.valid() && future_is_ready(m_update_available))
{
if(m_update_available.get())
{
wxMessageDialog dialog(this, _("There's a new update available.\nDo you want to update?"), _("Update notification"), wxCENTRE | wxYES_NO);
if(dialog.ShowModal() == wxID_YES)
{
CemuUpdateWindow update_window(this);
update_window.ShowModal();
update_window.Destroy();
}
}
m_update_available = {};
}
if (!IsFullScreen() || m_menu_visible)
return;
const auto mouse_position = wxGetMousePosition();
if(m_mouse_position != mouse_position)
{
m_last_mouse_move_time = std::chrono::steady_clock::now();
m_mouse_position = mouse_position;
ShowCursor(true);
return;
}
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_last_mouse_move_time);
if (diff.count() > 3000)
{
ShowCursor(false);
}
}
void MainWindow::OnHelpVistWebpage(wxCommandEvent& event) {}
#define BUILD_DATE __DATE__ " " __TIME__
class CemuAboutDialog : public wxDialog
{
public:
CemuAboutDialog(wxWindow* parent = NULL)
: wxDialog(NULL, wxID_ANY, "About Cemu", wxDefaultPosition, wxSize(500, 700))
{
Create(parent);
}
void Create(wxWindow* parent = NULL)
{
SetIcon(wxICON(M_WND_ICON128));
this->SetBackgroundColour(wxColour(0xFFFFFFFF));
wxScrolledWindow* scrolledWindow = new wxScrolledWindow(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
m_scrolledSizer = new wxBoxSizer(wxVERTICAL);
AddHeaderInfo(scrolledWindow, m_scrolledSizer);
m_scrolledSizer->AddSpacer(5);
AddLibInfo(scrolledWindow, m_scrolledSizer);
m_scrolledSizer->AddSpacer(5);
AddThanks(scrolledWindow, m_scrolledSizer);
scrolledWindow->SetSizer(m_scrolledSizer);
scrolledWindow->FitInside();
scrolledWindow->SetScrollRate(25, 25);
mainSizer->Add(scrolledWindow, wxSizerFlags(1).Expand().Border(wxLEFT | wxRIGHT, 10));
SetSizer(mainSizer);
CentreOnParent();
}
void AddHeaderInfo(wxWindow* parent, wxSizer* sizer)
{
char versionString[512];
sprintf(versionString, "Cemu\nVersion %s\nCompiled " BUILD_DATE "\nOriginal authors: Exzap, Petergov", BUILD_VERSION_STRING);
sizer->Add(new wxStaticText(parent, wxID_ANY, versionString), wxSizerFlags().Border(wxALL, 3).Border(wxTOP, 10));
sizer->Add(new wxHyperlinkCtrl(parent, -1, "https://cemu.info", "https://cemu.info"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 3));
sizer->AddSpacer(3);
sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4));
sizer->AddSpacer(5);
wxString extraInfo("" EMULATOR_NAME " is a Wii U emulator.\n\nWii and Wii U are trademarks of Nintendo.\n" EMULATOR_NAME " is not affiliated with Nintendo.");
sizer->Add(new wxStaticText(parent, wxID_ANY, extraInfo), wxSizerFlags());
}
void AddLibInfo(wxWindow* parent, wxSizer* sizer)
{
sizer->AddSpacer(3);
sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4));
sizer->AddSpacer(3);
sizer->Add(new wxStaticText(parent, wxID_ANY, "Used libraries and utilities:"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 2));
// zLib
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "zLib ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "http://www.zlib.net", "http://www.zlib.net"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// wxWidgets
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "wxWidgets ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.wxwidgets.org/", "https://www.wxwidgets.org/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// OpenSSL
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "OpenSSL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.openssl.org/", "https://www.openssl.org/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// libcurl
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "libcurl ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://curl.haxx.se/libcurl/", "https://curl.haxx.se/libcurl/"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// imgui
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "imgui ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/ocornut/imgui", "https://github.com/ocornut/imgui"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// fontawesome
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "fontawesome ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/FortAwesome/Font-Awesome", "https://github.com/FortAwesome/Font-Awesome"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// boost
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "boost ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://www.boost.org", "https://www.boost.org"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// libusb
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "libusb ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://libusb.info", "https://libusb.info"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// icons
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "icons from "), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://icons8.com", "https://icons8.com"), 0);
sizer->Add(lineSizer);
}
// Lato font (are we still using it?)
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "\"Lato\" font by tyPoland Lukasz Dziedzic (OFL, V1.1)"), 0);
sizer->Add(lineSizer);
}
// SDL
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "SDL ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "https://github.com/libsdl-org/SDL", "https://github.com/libsdl-org/SDL"), 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
// IH264
{
wxSizer* lineSizer = new wxBoxSizer(wxHORIZONTAL);
lineSizer->Add(new wxStaticText(parent, -1, "Modified ih264 from Android project ("), 0);
lineSizer->Add(new wxHyperlinkCtrl(parent, -1, "Source", "https://cemu.info/oss/ih264d.zip"), 0);
lineSizer->Add(new wxStaticText(parent, -1, " "), 0);
wxHyperlinkCtrl* noticeLink = new wxHyperlinkCtrl(parent, -1, "NOTICE", "");
noticeLink->Bind(wxEVT_LEFT_DOWN, [](wxMouseEvent& event)
{
fs::path tempPath = fs::temp_directory_path();
tempPath.append("NOTICE_IH264.txt");
FileStream* fs = FileStream::createFile2(tempPath);
if (!fs)
return;
fs->writeString(
"/******************************************************************************\r\n"
" *\r\n"
" * Copyright (C) 2015 The Android Open Source Project\r\n"
" *\r\n"
" * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n"
" * you may not use this file except in compliance with the License.\r\n"
" * You may obtain a copy of the License at:"
" *\r\n"
" * http://www.apache.org/licenses/LICENSE-2.0\r\n"
" *\r\n"
" * Unless required by applicable law or agreed to in writing, software\r\n"
" * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n"
" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n"
" * See the License for the specific language governing permissions and\r\n"
" * limitations under the License.\r\n"
" *\r\n"
" *****************************************************************************\r\n"
" * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore\r\n"
"*/\r\n"
"/*****************************************************************************/\r\n"
);
delete fs;
#ifdef _WIN32
ShellExecute(0, 0, tempPath.generic_wstring().c_str(), 0, 0, SW_SHOW);
#else
assert_dbg();
#endif
});
lineSizer->Add(noticeLink, 0);
lineSizer->Add(new wxStaticText(parent, -1, ")"), 0);
sizer->Add(lineSizer);
}
}
void AddThanks(wxWindow* parent, wxSizer* sizer)
{
sizer->AddSpacer(3);
sizer->Add(new wxStaticLine(parent), wxSizerFlags().Expand().Border(wxRIGHT, 4));
sizer->AddSpacer(3);
wxGridSizer* gridSizer = new wxGridSizer(1, 2, 0, 0);
sizer->AddSpacer(2);
sizer->Add(new wxStaticText(parent, wxID_ANY, "Thanks to our Patreon supporters:"), wxSizerFlags().Expand().Border(wxTOP | wxBOTTOM, 2));
std::vector<const char*> patreonSupporterNames{ "Maufeat", "lvlv", "F34R", "John Godgames", "Jameel Lewis", "skooks", "Cheesy", "Barrowsx", "Mored1984", "madmat007"
, "Kuhnnl", "Owen M", "lucianobugalu", "KimoMaka", "nick palma aka renaissance18", "TheGiantBros", "SpiGAndromeda"
, "Chimech0", "Nicolás Pino", "Pezzatti", "Barry Wallace", "REGNR8 Productions", "Lagia", "Freestyler316", "Dentora"
, "tactics", "Merola.C", "Ceigyx", "Mata", "BobSchneeder45", "fenixDG", "jjalapeno55", "FissionMetroid101", "Jetta88"
, "nesxdie", "Mikah", "PornfoxVR.com", "Hunter4everosa", "Bbzx", "Salim Sanehi", "FalloutpunkX", "NashOH-CL", "RaheemWala"
, "Faris Leonhart", "MahvZero", "PlaguedGuardian", "Stuffie", "CaptainLester", "Qtech", "Zaurexus", "Leonidas", "Artifesto"
, "Alca259", "SirWestofAsh", "Loli Co.", "The Technical Revolutionary", "MegaYama", "mitori", "Seymordius", "Adrian Josh Cruz", "Manuel Hoenings", "Just A Jabb"
, "pgantonio", "CannonXIII", "Lonewolf00708", "AlexsDesign.com", "NoskLo", "MrSirHaku", "xElite_V AKA William H. Johnson", "Zalnor", "Pig", "James \"SE4LS\"", "DairyOrange", "Horoko Lawrence", "bloodmc", "Officer Jenny", "Quasar", "Postposterous", "Jake Jackson", "Kaydax", "CthePredatorG"
, "Hengi", "Pyrochaser"};
wxString nameListLeft, nameListRight;
for (size_t i = 0; i < patreonSupporterNames.size(); i++)
{
const char* name = patreonSupporterNames[i];
wxString& nameList = ((i % 2) == 0) ? nameListLeft : nameListRight;
if (i >= 2)
nameList.append("\n");
nameList.append(name);
}
gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListLeft), wxSizerFlags());
gridSizer->Add(new wxStaticText(parent, wxID_ANY, nameListRight), wxSizerFlags());
sizer->AddSpacer(4);
sizer->Add(gridSizer, 1, wxEXPAND);
sizer->AddSpacer(2);
sizer->Add(new wxStaticText(parent, wxID_ANY, "Special thanks:"), wxSizerFlags().Expand().Border(wxTOP, 2));
sizer->Add(new wxStaticText(parent, wxID_ANY, "espes - Also try XQEMU!\nWaltzz92"), wxSizerFlags().Expand().Border(wxTOP, 1));
}
protected:
wxSizer* m_scrolledSizer;
};
void MainWindow::OnHelpAbout(wxCommandEvent& event)
{
CemuAboutDialog dlgAbout(this);
dlgAbout.ShowModal();
}
void MainWindow::OnHelpUpdate(wxCommandEvent& event)
{
CemuUpdateWindow test(this);
test.ShowModal();
test.Destroy();
}
void MainWindow::OnHelpGettingStarted(wxCommandEvent& event)
{
ShowGettingStartedDialog();
}
void MainWindow::RecreateMenu()
{
if (m_menuBar)
{
SetMenuBar(nullptr);
m_menuBar->Destroy();
m_menuBar = nullptr;
}
auto& config = GetConfig();
m_menuBar = new wxMenuBar;
// file submenu
m_fileMenu = new wxMenu;
if (!m_game_launched)
{
m_loadMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_LOAD, _("&Load"));
m_installUpdateMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_INSTALL_UPDATE, _("&Install game title, update or DLC"));
sint32 recentFileIndex = 0;
m_fileMenuSeparator0 = nullptr;
m_fileMenuSeparator1 = nullptr;
for (size_t i = 0; i < config.recent_launch_files.size(); i++)
{
const auto& entry = config.recent_launch_files[i];
if (entry.empty())
continue;
if (!fs::exists(entry))
continue;
if (recentFileIndex == 0)
m_fileMenuSeparator0 = m_fileMenu->AppendSeparator();
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_RECENT_0 + i, fmt::format(L"{}. {}", recentFileIndex, entry));
recentFileIndex++;
if (recentFileIndex >= 8)
break;
}
m_fileMenuSeparator1 = m_fileMenu->AppendSeparator();
}
else
{
// add 'Stop emulation' menu entry to file menu
#ifndef PUBLIC_RELEASE
m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_END_EMULATION, _("End emulation"));
#endif
}
m_exitMenuItem = m_fileMenu->Append(MAINFRAME_MENU_ID_FILE_EXIT, _("&Exit"));
m_menuBar->Append(m_fileMenu, _("&File"));
// options->account submenu
m_optionsAccountMenu = new wxMenu;
const auto account_id = ActiveSettings::GetPersistentId();
int index = 0;
for(const auto& account : Account::GetAccounts())
{
wxMenuItem* item = m_optionsAccountMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 + index, account.ToString());
item->Check(account_id == account.GetPersistentId());
if (m_game_launched || LaunchSettings::GetPersistentId().has_value())
item->Enable(false);
++index;
}
//optionsAccountMenu->AppendSeparator(); TODO
//optionsAccountMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 + index, _("Online enabled"))->Check(config.account.online_enabled);
// options->region submenu
//wxMenu* optionsRegionMenu = new wxMenu;
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_AUTO, _("&Auto"), wxEmptyString)->Check(config.console_region == ConsoleRegion::Auto);
////optionsRegionMenu->AppendSeparator();
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_USA, _("&USA"), wxEmptyString)->Check(config.console_region == ConsoleRegion::USA);
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_EUR, _("&Europe"), wxEmptyString)->Check(config.console_region == ConsoleRegion::EUR);
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_JPN, _("&Japan"), wxEmptyString)->Check(config.console_region == ConsoleRegion::JPN);
//// optionsRegionMenu->Append(MAINFRAME_MENU_ID_OPTIONS_REGION_AUS, wxT("&Australia"), wxEmptyString, wxITEM_RADIO)->Check(config_get()->region==3); -> Was merged into Europe?
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_CHN, _("&China"), wxEmptyString)->Check(config.console_region == ConsoleRegion::CHN);
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_KOR, _("&Korea"), wxEmptyString)->Check(config.console_region == ConsoleRegion::KOR);
//optionsRegionMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_REGION_TWN, _("&Taiwan"), wxEmptyString)->Check(config.console_region == ConsoleRegion::TWN);
// options->console language submenu
wxMenu* optionsConsoleLanguageMenu = new wxMenu;
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH, _("&English"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::EN);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE, _("&Japanese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::JA);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_FRENCH, _("&French"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::FR);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_GERMAN, _("&German"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::DE);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ITALIAN, _("&Italian"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::IT);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_SPANISH, _("&Spanish"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::ES);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_CHINESE, _("&Chinese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::ZH);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_KOREAN, _("&Korean"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::KO);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_DUTCH, _("&Dutch"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::NL);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_PORTUGUESE, _("&Portuguese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::PT);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_RUSSIAN, _("&Russian"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::RU);
optionsConsoleLanguageMenu->AppendRadioItem(MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_TAIWANESE, _("&Taiwanese"), wxEmptyString)->Check(config.console_language == CafeConsoleLanguage::TW);
// options submenu
wxMenu* optionsMenu = new wxMenu;
m_fullscreenMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, _("&Fullscreen"), wxEmptyString);
m_fullscreenMenuItem->Check(ActiveSettings::FullscreenEnabled());
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, _("&Graphic packs"));
//optionsMenu->AppendSubMenu(optionsVCAMenu, _("&GPU buffer cache accuracy"));
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
m_padViewMenuItem->Check(GetConfig().pad_open);
optionsMenu->AppendSeparator();
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GENERAL2, _("&General settings"));
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_INPUT, _("&Input settings"));
optionsMenu->AppendSeparator();
optionsMenu->AppendSubMenu(m_optionsAccountMenu, _("&Active account"));
//optionsMenu->AppendSubMenu(optionsRegionMenu, _("&Console region"));
optionsMenu->AppendSubMenu(optionsConsoleLanguageMenu, _("&Console language"));
m_menuBar->Append(optionsMenu, _("&Options"));
// tools submenu
wxMenu* toolsMenu = new wxMenu;
m_memorySearcherMenuItem = toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_MEMORY_SEARCHER, _("&Memory searcher"));
m_memorySearcherMenuItem->Enable(false);
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER, _("&Title Manager"));
toolsMenu->Append(MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER, _("&Download Manager"));
m_menuBar->Append(toolsMenu, _("&Tools"));
// cpu timer speed menu
wxMenu* timerSpeedMenu = new wxMenu;
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_1X, _("&1x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 3);
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_2X, _("&2x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 2);
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_4X, _("&4x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 1);
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_8X, _("&8x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 0);
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_05X, _("&0.5x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 4);
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_025X, _("&0.25x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 5);
timerSpeedMenu->AppendRadioItem(MAINFRAME_MENU_ID_TIMER_SPEED_0125X, _("&0.125x speed"), wxEmptyString)->Check(ActiveSettings::GetTimerShiftFactor() == 6);
// cpu submenu
wxMenu* cpuMenu = new wxMenu;
cpuMenu->AppendSubMenu(timerSpeedMenu, _("&Timer speed"));
m_menuBar->Append(cpuMenu, _("&CPU"));
// nfc submenu
wxMenu* nfcMenu = new wxMenu;
m_nfcMenu = nfcMenu;
nfcMenu->Append(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, _("&Scan NFC tag from file"))->Enable(false);
m_menuBar->Append(nfcMenu, _("&NFC"));
m_nfcMenuSeparator0 = nullptr;
// debug->logging submenu
wxMenu* debugLoggingMenu = new wxMenu;
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_UNSUPPORTED_API, _("&Unsupported API calls"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_UNSUPPORTED_API));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_COREINIT_LOGGING, _("&Coreinit Logging (OSReport/OSConsole)"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_COREINIT_LOGGING));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_FILE, _("&Coreinit File-Access"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_FILE));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_THREADSYNC, _("&Coreinit Thread-Synchronization API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_THREADSYNC));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_COREINIT_MEM, _("&Coreinit Memory API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_COREINIT_MEM));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_COREINIT_MP, _("&Coreinit MP API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_COREINIT_MP));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_COREINIT_THREAD, _("&Coreinit Thread API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_COREINIT_THREAD));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_NFP, _("&NN NFP"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_NFP));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_GX2, _("&GX2 API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_GX2));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_SNDAPI, _("&Audio API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_SNDAPI));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_INPUT, _("&Input API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_INPUT));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_SOCKET, _("&Socket API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_SOCKET));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_SAVE, _("&Save API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_SAVE));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_H264, _("&H264 API"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_H264));
debugLoggingMenu->AppendSeparator();
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_PATCHES, _("&Graphic pack patches"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_PATCHES));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_TEXTURE_CACHE, _("&Texture cache warnings"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_TEXTURE_CACHE));
debugLoggingMenu->AppendSeparator();
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_OPENGL, _("&OpenGL debug output"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_OPENGL));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_VULKAN_VALIDATION, _("&Vulkan validation layer (slow)"), wxEmptyString)->Check(cafeLog_isLoggingFlagEnabled(LOG_TYPE_VULKAN_VALIDATION));
#ifndef PUBLIC_RELEASE
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO, _("&Log PPC context for API"), wxEmptyString)->Check(cemuLog_advancedPPCLoggingEnabled());
#endif
m_loggingSubmenu = debugLoggingMenu;
// debug->dump submenu
wxMenu* debugDumpMenu = new wxMenu;
debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_TEXTURES, _("&Textures"), wxEmptyString)->Check(ActiveSettings::DumpTexturesEnabled());
debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_SHADERS, _("&Shaders"), wxEmptyString)->Check(ActiveSettings::DumpShadersEnabled());
debugDumpMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_DUMP_CURL_REQUESTS, _("&nlibcurl HTTP/HTTPS requests"), wxEmptyString);
// debug submenu
wxMenu* debugMenu = new wxMenu;
m_debugMenu = debugMenu;
debugMenu->AppendSubMenu(debugLoggingMenu, _("&Logging"));
debugMenu->AppendSubMenu(debugDumpMenu, _("&Dump"));
debugMenu->AppendSeparator();
auto upsidedownItem = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN, _("&Render upside-down"), wxEmptyString);
upsidedownItem->Check(ActiveSettings::RenderUpsideDownEnabled());
if(LaunchSettings::RenderUpsideDownEnabled().has_value())
upsidedownItem->Enable(false);
auto accurateBarriers = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, _("&Accurate barriers (Vulkan)"), wxEmptyString);
accurateBarriers->Check(GetConfig().vk_accurate_barriers);
debugMenu->AppendSeparator();
#ifndef PUBLIC_RELEASE
auto audioAuxOnly = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_AUDIO_AUX_ONLY, _("&Audio AUX only"), wxEmptyString);
audioAuxOnly->Check(ActiveSettings::AudioOutputOnlyAux());
#endif
#ifndef PUBLIC_RELEASE
debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_LOGGING_WINDOW, _("&Open logging window"));
#endif
debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_THREADS, _("&View PPC threads"));
debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_PPC_DEBUGGER, _("&View PPC debugger"));
debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_AUDIO_DEBUGGER, _("&View audio debugger"));
debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_VIEW_TEXTURE_RELATIONS, _("&View texture cache info"));
debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_SHOW_FRAME_PROFILER, _("&Show frame profiler"), wxEmptyString);
debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_DUMP_RAM, _("&Dump current RAM"));
// debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_DUMP_FST, _("&Dump WUD filesystem"))->Enable(false);
m_menuBar->Append(debugMenu, _("&Debug"));
// help menu
wxMenu* helpMenu = new wxMenu;
//helpMenu->Append(MAINFRAME_MENU_ID_HELP_WEB, wxT("&Visit website"));
//helpMenu->AppendSeparator();
m_check_update_menu = helpMenu->Append(MAINFRAME_MENU_ID_HELP_UPDATE, _("&Check for updates"));
helpMenu->Append(MAINFRAME_MENU_ID_HELP_GETTING_STARTED, _("&Getting started"));
helpMenu->AppendSeparator();
helpMenu->Append(MAINFRAME_MENU_ID_HELP_ABOUT, _("&About Cemu"));
m_menuBar->Append(helpMenu, _("&Help"));
SetMenuBar(m_menuBar);
m_menu_visible = true;
if (m_game_launched)
{
if (m_check_update_menu)
m_check_update_menu->Enable(false);
m_memorySearcherMenuItem->Enable(true);
m_nfcMenu->Enable(MAINFRAME_MENU_ID_NFC_TOUCH_NFC_FILE, true);
// disable OpenGL logging (currently cant be toggled after OpenGL backend is initialized)
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_OPENGL, false);
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + LOG_TYPE_VULKAN_VALIDATION, false);
UpdateNFCMenu();
}
// hide new menu in fullscreen
if (IsFullScreen())
SetMenuVisible(false);
}
void MainWindow::OnAfterCallShowErrorDialog()
{
//wxMessageBox((const wxString&)dialogText, (const wxString&)dialogTitle, wxICON_INFORMATION);
//wxDialog* dialog = new wxDialog(NULL,wxID_ANY,(const wxString&)dialogTitle,wxDefaultPosition,wxSize(310,170));
//dialog->ShowModal();
//dialogState = 1;
}
bool MainWindow::EnableOnlineMode() const
{
// TODO: not used anymore
//
// if enabling online mode, check if all requirements are met
std::wstring additionalErrorInfo;
const sint32 onlineReqError = iosuCrypt_checkRequirementsForOnlineMode(additionalErrorInfo);
bool enableOnline = false;
if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_OTP_MISSING)
{
wxMessageBox(_("otp.bin could not be found"), _("Error"), wxICON_ERROR);
}
else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_OTP_CORRUPTED)
{
wxMessageBox(_("otp.bin is corrupted or has invalid size"), _("Error"), wxICON_ERROR);
}
else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_SEEPROM_MISSING)
{
wxMessageBox(_("seeprom.bin could not be found"), _("Error"), wxICON_ERROR);
}
else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_SEEPROM_CORRUPTED)
{
wxMessageBox(_("seeprom.bin is corrupted or has invalid size"), _("Error"), wxICON_ERROR);
}
else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_MISSING_FILE)
{
std::wstring errorMessage = fmt::format(L"Unable to load a necessary file:\n{}", additionalErrorInfo);
wxMessageBox(errorMessage.c_str(), _("Error"), wxICON_ERROR);
}
else if (onlineReqError == IOS_CRYPTO_ONLINE_REQ_OK)
{
enableOnline = true;
}
else
{
wxMessageBox(_("Unknown error occured"), _("Error"), wxICON_ERROR);
}
//config_get()->enableOnlineMode = enableOnline;
return enableOnline;
}
void MainWindow::RestoreSettingsAfterGameExited()
{
RecreateMenu();
}
void MainWindow::UpdateSettingsAfterGameLaunch()
{
m_update_available = {};
//m_game_launched = true;
RecreateMenu();
}
std::string MainWindow::GetRegionString(uint32 region) const
{
switch (region)
{
case 0x1:
return "JPN";
case 0x2:
return "USA";
case 0x4:
return "EUR";
case 0x10:
return "CHN";
case 0x20:
return "KOR";
case 0x40:
return "TWN";
default:
return std::to_string(region);
}
}
void MainWindow::OnGraphicWindowClose(wxCloseEvent& event)
{
m_graphic_pack_window->Destroy();
m_graphic_pack_window = nullptr;
}
void MainWindow::OnGraphicWindowOpen(wxTitleIdEvent& event)
{
if (m_graphic_pack_window)
return;
m_graphic_pack_window = new GraphicPacksWindow2(this, event.GetTitleId());
m_graphic_pack_window->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnGraphicWindowClose, this);
m_graphic_pack_window->Show(true);
}
void MainWindow::RequestGameListRefresh()
{
auto* evt = new wxCommandEvent(wxEVT_REQUEST_GAMELIST_REFRESH);
wxQueueEvent(g_mainFrame, evt);
}
void MainWindow::RequestLaunchGame(fs::path filePath, wxLaunchGameEvent::INITIATED_BY initiatedBy)
{
wxLaunchGameEvent evt(filePath, initiatedBy);
wxPostEvent(g_mainFrame, evt);
}