#include "gui/wxgui.h" #include "gui/MainWindow.h" #include "gui/guiWrapper.h" #include #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 #endif #include #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(); #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 wxStringFormat2(EMULATOR_NAME" {}.{}{}", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR, EMULATOR_VERSION_SUFFIX); } 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(); 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::IsUpdateAvailable(); 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/. 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 cemuLog_log(LogType::Force, "MainWindow::ShowCursor - todo"); #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::IsUpdateAvailable(); 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 volume(FSTVolume::OpenFromDiscImage(path)); if (!volume) return path.filename().generic_wstring(); bool foundFile = false; std::vector 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::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 %d.%d\nCompiled " BUILD_DATE "\nOriginal authors: Exzap, Petergov", EMULATOR_VERSION_LEAD, EMULATOR_VERSION_MAJOR); 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 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); }