From 3062a35eb1297067446156c43e9d0df2f684edff Mon Sep 17 00:00:00 2001 From: boludoz Date: Sun, 15 Oct 2023 02:02:22 -0300 Subject: Improved shortcut: add games in applist for Windows, question for start game at fullscreen & better unicode support for some Windows path funcs. --- src/yuzu/game_list.cpp | 4 - src/yuzu/main.cpp | 490 ++++++++++++++++++++++++++++++------------------- src/yuzu/main.h | 22 ++- src/yuzu/util/util.cpp | 17 +- src/yuzu/util/util.h | 14 +- 5 files changed, 347 insertions(+), 200 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 2bb1a0239..fbe099661 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -566,10 +566,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); -#ifndef WIN32 QAction* create_applications_menu_shortcut = shortcut_menu->addAction(tr("Add to Applications Menu")); -#endif context_menu.addSeparator(); QAction* properties = context_menu.addAction(tr("Properties")); @@ -647,11 +645,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); }); -#ifndef WIN32 connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); }); -#endif connect(properties, &QAction::triggered, [this, path]() { emit OpenPerGameGeneralRequested(path); }); }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 1431cf2fe..e4dc717ed 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2840,170 +2840,350 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } -void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, - GameListShortcutTarget target) { - // Get path to yuzu executable - const QStringList args = QApplication::arguments(); - std::filesystem::path yuzu_command = args[0].toStdString(); +bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, + const std::string& comment, + const std::filesystem::path& icon_path, + const std::filesystem::path& command, + const std::string& arguments, const std::string& categories, + const std::string& keywords, const std::string& name) { - // If relative path, make it an absolute path - if (yuzu_command.c_str()[0] == '.') { - yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; - } + bool shortcut_succeeded = false; -#if defined(__linux__) - // Warn once if we are making a shortcut to a volatile AppImage - const std::string appimage_ending = - std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); - if (yuzu_command.string().ends_with(appimage_ending) && - !UISettings::values.shortcut_already_warned) { - if (QMessageBox::warning(this, tr("Create Shortcut"), - tr("This will create a shortcut to the current AppImage. This may " - "not work well if you update. Continue?"), - QMessageBox::StandardButton::Ok | - QMessageBox::StandardButton::Cancel) == - QMessageBox::StandardButton::Cancel) { - return; + // Replace characters that are illegal in Windows filenames + std::filesystem::path shortcut_path_full = + shortcut_path / Common::FS::UTF8FilenameSantizer(name); + +#if defined(__linux__) || defined(__FreeBSD__) + shortcut_path_full += ".desktop"; +#elif defined(_WIN32) + shortcut_path_full += ".lnk"; +#endif + + LOG_INFO(Common, "[GMainWindow::CreateShortcutLink] Create shortcut path: {}", + shortcut_path_full.string()); + +#if defined(__linux__) || defined(__FreeBSD__) + // This desktop file template was writing referencing + // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + try { + + // Plus 'Type' is required + if (name.empty()) { + LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Name is empty"); + shortcut_succeeded = false; + return shortcut_succeeded; } - UISettings::values.shortcut_already_warned = true; - } -#endif // __linux__ + std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); - std::filesystem::path target_directory{}; + if (shortcut_stream.is_open()) { - switch (target) { - case GameListShortcutTarget::Desktop: { - const QString desktop_path = - QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); - target_directory = desktop_path.toUtf8().toStdString(); - break; - } - case GameListShortcutTarget::Applications: { - const QString applications_path = - QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); - if (applications_path.isEmpty()) { - const char* home = std::getenv("HOME"); - if (home != nullptr) { - target_directory = std::filesystem::path(home) / ".local/share/applications"; + fmt::print(shortcut_stream, "[Desktop Entry]\n"); + fmt::print(shortcut_stream, "Type=Application\n"); + fmt::print(shortcut_stream, "Version=1.0\n"); + fmt::print(shortcut_stream, "Name={}\n", name); + + if (!comment.empty()) { + fmt::print(shortcut_stream, "Comment={}\n", comment); } + + if (std::filesystem::is_regular_file(icon_path)) { + fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); + } + + fmt::print(shortcut_stream, "TryExec={}\n", command.string()); + fmt::print(shortcut_stream, "Exec={}", command.string()); + + if (!arguments.empty()) { + fmt::print(shortcut_stream, " {}", arguments); + } + + fmt::print(shortcut_stream, "\n"); + + if (!categories.empty()) { + fmt::print(shortcut_stream, "Categories={}\n", categories); + } + + if (!keywords.empty()) { + fmt::print(shortcut_stream, "Keywords={}\n", keywords); + } + + shortcut_stream.close(); + return true; + } else { - target_directory = applications_path.toUtf8().toStdString(); + LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Failed to create shortcut"); + return false; } - break; + + shortcut_stream.close(); + } catch (const std::exception& e) { + LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Failed to create shortcut: {}", + e.what()); } - default: - return; +#elif defined(_WIN32) + // Initialize COM + auto hr = CoInitialize(NULL); + if (FAILED(hr)) { + return shortcut_succeeded; } - const QDir dir(QString::fromStdString(target_directory.generic_string())); - if (!dir.exists()) { - QMessageBox::critical(this, tr("Create Shortcut"), - tr("Cannot create shortcut. Path \"%1\" does not exist.") - .arg(QString::fromStdString(target_directory.generic_string())), - QMessageBox::StandardButton::Ok); - return; + IShellLinkW* ps1; + + auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, + (void**)&ps1); + + // The UTF-16 / UTF-8 conversion is broken in C++, it is necessary to perform these steps and + // resort to the native Windows function. + std::wstring wshortcut_path_full = Common::UTF8ToUTF16W(shortcut_path_full.string()); + std::wstring wicon_path = Common::UTF8ToUTF16W(icon_path.string()); + std::wstring wcommand = Common::UTF8ToUTF16W(command.string()); + std::wstring warguments = Common::UTF8ToUTF16W(arguments); + std::wstring wcomment = Common::UTF8ToUTF16W(comment); + + if (SUCCEEDED(hres)) { + if (std::filesystem::is_regular_file(command)) + hres = ps1->SetPath(wcommand.data()); + + if (SUCCEEDED(hres) && !arguments.empty()) + hres = ps1->SetArguments(warguments.data()); + + if (SUCCEEDED(hres) && !comment.empty()) + hres = ps1->SetDescription(wcomment.data()); + + if (SUCCEEDED(hres) && std::filesystem::is_regular_file(icon_path)) + hres = ps1->SetIconLocation(wicon_path.data(), 0); + + IPersistFile* pPersistFile = nullptr; + + if (SUCCEEDED(hres)) { + hres = ps1->QueryInterface(IID_IPersistFile, (void**)&pPersistFile); + + if (SUCCEEDED(hres) && pPersistFile != nullptr) { + hres = pPersistFile->Save(wshortcut_path_full.data(), TRUE); + if (SUCCEEDED(hres)) { + shortcut_succeeded = true; + } + } + } + + if (pPersistFile != nullptr) { + pPersistFile->Release(); + } + } else { + LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Failed to create IShellLinkWinstance"); } - const std::string game_file_name = std::filesystem::path(game_path).filename().string(); - // Determine full paths for icon and shortcut -#if defined(__linux__) || defined(__FreeBSD__) - const char* home = std::getenv("HOME"); - const std::filesystem::path home_path = (home == nullptr ? "~" : home); - const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); - - std::filesystem::path system_icons_path = - (xdg_data_home == nullptr ? home_path / ".local/share/" - : std::filesystem::path(xdg_data_home)) / - "icons/hicolor/256x256"; - if (!Common::FS::CreateDirs(system_icons_path)) { + ps1->Release(); + CoUninitialize(); +#endif + + if (shortcut_succeeded && std::filesystem::is_regular_file(shortcut_path_full)) { + LOG_INFO(Common, "[GMainWindow::CreateShortcutLink] Shortcut created"); + } else { + LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Shortcut error, failed to create it"); + shortcut_succeeded = false; + } + + return shortcut_succeeded; +} + +// Messages in pre-defined message boxes for less code spaghetti +bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, + const std::string title) { + QMessageBox::StandardButtons buttons; + int result = 0; + + switch (imsg) { + + case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES: + buttons = QMessageBox::Yes | QMessageBox::No; + + result = + QMessageBox::information(parent, tr("Create Shortcut"), + tr("Do you want to launch the game in fullscreen?"), buttons); + + LOG_INFO(Frontend, "Shortcut will launch in fullscreen"); + return (result == QMessageBox::No) ? false : true; + + case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: + QMessageBox::information( + parent, tr("Create Shortcut"), + tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); + LOG_INFO(Frontend, "Successfully created a shortcut to {}", title); + return true; + + case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: + result = QMessageBox::warning( + this, tr("Create Shortcut"), + tr("This will create a shortcut to the current AppImage. This may " + "not work well if you update. Continue?"), + QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel); + return (result == QMessageBox::StandardButton::Cancel) ? true : false; + case GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN: + buttons = QMessageBox::Ok; + QMessageBox::critical(parent, tr("Create Shortcut"), + tr("Cannot create shortcut in Apps. Restart yuzu as administrator."), + buttons); + LOG_ERROR(Frontend, "Cannot create shortcut in Apps. Restart yuzu as administrator."); + return true; + default: + buttons = QMessageBox::Ok; + QMessageBox::critical( + parent, tr("Create Shortcut"), + tr("Failed to create a shortcut to %1").arg(QString::fromStdString(title)), buttons); + LOG_ERROR(Frontend, "Failed to create a shortcut to {}", title); + return true; + } + + return true; +} + +bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, + std::filesystem::path& icons_path) { + + // Get path to Yuzu icons directory & icon extension + std::string ico_extension = "png"; +#if defined(_WIN32) + icons_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "icons"; + ico_extension = "ico"; +#elif defined(__linux__) || defined(__FreeBSD__) + icons_path = GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; +#endif + + // Create icons directory if it doesn't exist + if (!Common::FS::CreateDirs(icons_path)) { QMessageBox::critical( this, tr("Create Icon"), tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") - .arg(QString::fromStdString(system_icons_path)), + .arg(QString::fromStdString(icons_path.string())), QMessageBox::StandardButton::Ok); - return; + icons_path = ""; // Reset path + return false; } - std::filesystem::path icon_path = - system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) - : fmt::format("yuzu-{:016X}.png", program_id)); - const std::filesystem::path shortcut_path = - target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) - : fmt::format("yuzu-{:016X}.desktop", program_id)); -#elif defined(WIN32) - std::filesystem::path icons_path = - Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir); - std::filesystem::path icon_path = - icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name) - : fmt::format("yuzu-{:016X}.ico", program_id))); -#else - std::string icon_extension; -#endif - // Get title from game file - const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), - system->GetContentProvider()}; - const auto control = pm.GetControlMetadata(); - const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + // Create icon file path + icons_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension) + : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); + return true; +} - std::string title{fmt::format("{:016X}", program_id)}; +void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target) { - if (control.first != nullptr) { - title = control.first->GetApplicationName(); - } else { - loader->ReadTitle(title); + // Get path to yuzu executable + const QStringList args = QApplication::arguments(); + std::filesystem::path yuzu_command = args[0].toStdString(); + + // If relative path, make it an absolute path + if (yuzu_command.c_str()[0] == '.') { + yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; } - // Get icon from game file - std::vector icon_image_file{}; - if (control.second != nullptr) { - icon_image_file = control.second->ReadAllBytes(); - } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { - LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); + // Shortcut path + std::filesystem::path shortcut_path{}; + if (target == GameListShortcutTarget::Desktop) { + shortcut_path = Common::FS::GetDesktopPath(); + if (!std::filesystem::exists(shortcut_path)) { + shortcut_path = + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); + } + } else if (target == GameListShortcutTarget::Applications) { + +#if defined(_WIN32) + HANDLE hProcess = GetCurrentProcess(); + if (!IsUserAnAdmin()) { + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN, + ""); + return; + } + CloseHandle(hProcess); +#endif // _WIN32 + + shortcut_path = Common::FS::GetAppsShortcutsPath(); + if (!std::filesystem::exists(shortcut_path)) { + shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + .toStdString(); + } } - QImage icon_data = - QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); -#if defined(__linux__) || defined(__FreeBSD__) - // Convert and write the icon as a PNG - if (!icon_data.save(QString::fromStdString(icon_path.string()))) { - LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + // Icon path and title + std::string title; + std::filesystem::path icons_path; + if (std::filesystem::exists(shortcut_path)) { + + // Get title from game file + const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), + system->GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto loader = + Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + + title = fmt::format("{:016X}", program_id); + + if (control.first != nullptr) { + title = control.first->GetApplicationName(); + } else { + loader->ReadTitle(title); + } + + // Get icon from game file + std::vector icon_image_file{}; + if (control.second != nullptr) { + icon_image_file = control.second->ReadAllBytes(); + } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { + LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); + } + + QImage icon_data = + QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); + + if (GMainWindow::MakeShortcutIcoPath(program_id, title, icons_path)) { + if (!SaveIconToFile(icon_data, icons_path)) { + LOG_ERROR(Frontend, "Could not write icon to file"); + } + } + } else { - LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); - } -#elif defined(WIN32) - if (!SaveIconToFile(icon_path.string(), icon_data)) { - LOG_ERROR(Frontend, "Could not write icon to file"); + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, + title); + LOG_ERROR(Frontend, "[GMainWindow::OnGameListCreateShortcut] Invalid shortcut target"); return; } -#endif // __linux__ -#ifdef _WIN32 - // Replace characters that are illegal in Windows filenames by a dash - const std::string illegal_chars = "<>:\"/\\|?*"; - for (char c : illegal_chars) { - std::replace(title.begin(), title.end(), c, '_'); +// Special case for AppImages +#if defined(__linux__) + // Warn once if we are making a shortcut to a volatile AppImage + const std::string appimage_ending = + std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); + if (yuzu_command.string().ends_with(appimage_ending) && + !UISettings::values.shortcut_already_warned) { + if (GMainWindow::CreateShortcutMessagesGUI( + this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, title)) { + return; + } + UISettings::values.shortcut_already_warned = true; } - const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str(); -#endif +#endif // __linux__ + // Create shortcut + std::string arguments = fmt::format("-g \"{:s}\"", game_path); + if (GMainWindow::CreateShortcutMessagesGUI( + this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, title)) { + arguments = "-f " + arguments; + } const std::string comment = tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); - const std::string arguments = fmt::format("-g \"{:s}\"", game_path); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; - if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), - yuzu_command.string(), arguments, categories, keywords)) { - QMessageBox::critical(this, tr("Create Shortcut"), - tr("Failed to create a shortcut at %1") - .arg(QString::fromStdString(shortcut_path.string()))); + if (GMainWindow::CreateShortcutLink(shortcut_path, comment, icons_path, yuzu_command, arguments, + categories, keywords, title)) { + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS, + title); return; } - LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); - QMessageBox::information( - this, tr("Create Shortcut"), - tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, title); } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { @@ -3998,66 +4178,6 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file } } -bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, - const std::string& comment, const std::string& icon_path, - const std::string& command, const std::string& arguments, - const std::string& categories, const std::string& keywords) { -#if defined(__linux__) || defined(__FreeBSD__) - // This desktop file template was writing referencing - // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html - std::string shortcut_contents{}; - shortcut_contents.append("[Desktop Entry]\n"); - shortcut_contents.append("Type=Application\n"); - shortcut_contents.append("Version=1.0\n"); - shortcut_contents.append(fmt::format("Name={:s}\n", title)); - shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); - shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); - shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); - shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); - shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); - shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); - - std::ofstream shortcut_stream(shortcut_path); - if (!shortcut_stream.is_open()) { - LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); - return false; - } - shortcut_stream << shortcut_contents; - shortcut_stream.close(); - - return true; -#elif defined(WIN32) - IShellLinkW* shell_link; - auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, - (void**)&shell_link); - if (FAILED(hres)) { - return false; - } - shell_link->SetPath( - Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to - shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data()); - shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data()); - shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0); - - IPersistFile* persist_file; - hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file); - if (FAILED(hres)) { - return false; - } - - hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE); - if (FAILED(hres)) { - return false; - } - - persist_file->Release(); - shell_link->Release(); - - return true; -#endif - return false; -} - void GMainWindow::OnLoadAmiibo() { if (emu_thread == nullptr || !emu_thread->IsRunning()) { return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 270a40c5f..bf6756b48 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -151,6 +152,14 @@ class GMainWindow : public QMainWindow { UI_EMU_STOPPING, }; + const enum { + CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, + CREATE_SHORTCUT_MSGBOX_SUCCESS, + CREATE_SHORTCUT_MSGBOX_ERROR, + CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, + CREATE_SHORTCUT_MSGBOX_ADMIN, + }; + public: void filterBarSetChecked(bool state); void UpdateUITheme(); @@ -433,11 +442,14 @@ private: bool ConfirmShutdownGame(); QString GetTasStateDescription() const; - bool CreateShortcut(const std::string& shortcut_path, const std::string& title, - const std::string& comment, const std::string& icon_path, - const std::string& command, const std::string& arguments, - const std::string& categories, const std::string& keywords); - + bool CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, const std::string title); + bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, + std::filesystem::path& icons_path); + bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, + const std::filesystem::path& icon_path, + const std::filesystem::path& command, const std::string& arguments, + const std::string& categories, const std::string& keywords, + const std::string& name); /** * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog * The only difference is that it returns a boolean. diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index f2854c8ec..4d199ebd1 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -42,7 +42,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { return circle_pixmap; } -bool SaveIconToFile(const std::string_view path, const QImage& image) { +bool SaveIconToFile(const QImage& image, const std::filesystem::path& icon_path) { #if defined(WIN32) #pragma pack(push, 2) struct IconDir { @@ -73,7 +73,7 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { .id_count = static_cast(scale_sizes.size()), }; - Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, + Common::FS::IOFile icon_file(icon_path.string(), Common::FS::FileAccessMode::Write, Common::FS::FileType::BinaryFile); if (!icon_file.IsOpen()) { return false; @@ -135,7 +135,16 @@ bool SaveIconToFile(const std::string_view path, const QImage& image) { icon_file.Close(); return true; -#else - return false; +#elif defined(__linux__) || defined(__FreeBSD__) + // Convert and write the icon as a PNG + if (!image.save(QString::fromStdString(icon_path.string()))) { + LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + } else { + LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); + } + + return true; #endif + + return false; } diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 09c14ce3f..8839e160a 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h @@ -3,26 +3,36 @@ #pragma once +#include #include #include /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. + [[nodiscard]] QFont GetMonospaceFont(); /// Convert a size in bytes into a readable format (KiB, MiB, etc.) + [[nodiscard]] QString ReadableByteSize(qulonglong size); /** * Creates a circle pixmap from a specified color + * * @param color The color the pixmap shall have + * * @return QPixmap circle pixmap */ + [[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color); /** * Saves a windows icon to a file - * @param path The icons path + * * @param image The image to save + * + * @param path The icons path + * * @return bool If the operation succeeded */ -[[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image); + +[[nodiscard]] bool SaveIconToFile(const QImage& image, const std::filesystem::path& icon_path); -- cgit v1.2.3 From 0a75519ab590e250c94dc04b3f6072a69ef7e96b Mon Sep 17 00:00:00 2001 From: boludoz Date: Sun, 15 Oct 2023 03:16:29 -0300 Subject: Fixes and improvements --- src/common/fs/path_util.cpp | 35 +++++++++++++++++++++++++++-------- src/yuzu/main.h | 2 +- src/yuzu/util/util.cpp | 4 ++-- 3 files changed, 30 insertions(+), 11 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index a461161ed..f030939ce 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -363,13 +363,28 @@ fs::path GetDesktopPath() { LOG_ERROR(Common_Filesystem, "[GetDesktopPath] Failed to get the path to the desktop directory"); } + return fs::path{}; #else - fs::path shortcut_path = GetHomeDirectory() / "Desktop"; - if (fs::exists(shortcut_path)) { - return shortcut_path; + fs::path shortcut_path{}; + + // Array of possible desktop + std::vector desktop_paths = { + "Desktop", "Escritorio", "Bureau", "Skrivebord", "Plocha", + "Skrivbord", "Desktop", "Рабочий стол", "Pulpit", "Área de Trabalho", + "Робочий стіл", "Bureaublad", "デスクトップ", "桌面", "Pöytä", + "바탕 화면", "바탕 화면", "سطح المكتب", "دسکتاپ", "שולחן עבודה"}; + + for (auto& path : desktop_paths) { + if (fs::exists(GetHomeDirectory() / path)) { + return path; + } } + + LOG_ERROR(Common_Filesystem, + "[GetDesktopPath] Failed to get the path to the desktop directory"); + + return shortcut_path; #endif - return fs::path{}; } fs::path GetAppsShortcutsPath() { @@ -387,16 +402,20 @@ fs::path GetAppsShortcutsPath() { LOG_ERROR(Common_Filesystem, "[GetAppsShortcutsPath] Failed to get the path to the App Shortcuts directory"); } + + return fs::path{}; #else fs::path shortcut_path = GetHomeDirectory() / ".local/share/applications"; if (!fs::exists(shortcut_path)) { shortcut_path = std::filesystem::path("/usr/share/applications"); - return shortcut_path; - } else { - return shortcut_path; + if (!fs::exists(shortcut_path)) { + LOG_ERROR(Common_Filesystem, + "[GetAppsShortcutsPath] Failed to get the path to the App Shortcuts " + "directory"); + } } -#endif return fs::path{}; +#endif } // vvvvvvvvvv Deprecated vvvvvvvvvv // diff --git a/src/yuzu/main.h b/src/yuzu/main.h index bf6756b48..d7426607f 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -152,7 +152,7 @@ class GMainWindow : public QMainWindow { UI_EMU_STOPPING, }; - const enum { + enum { CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, CREATE_SHORTCUT_MSGBOX_SUCCESS, CREATE_SHORTCUT_MSGBOX_ERROR, diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 4d199ebd1..9755004ad 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -144,7 +144,7 @@ bool SaveIconToFile(const QImage& image, const std::filesystem::path& icon_path) } return true; -#endif - +#else return false; +#endif } -- cgit v1.2.3 From 4d4fe69223bd6cd053cbd2088e5925fb653a12d3 Mon Sep 17 00:00:00 2001 From: boludoz Date: Sun, 15 Oct 2023 14:44:23 -0300 Subject: Unnecessary feature removed --- src/common/fs/path_util.cpp | 70 --------------------------------------------- src/common/fs/path_util.h | 15 +--------- src/yuzu/main.cpp | 16 +++-------- src/yuzu/util/util.h | 3 -- 4 files changed, 5 insertions(+), 99 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index f030939ce..bccf953e4 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -348,76 +348,6 @@ fs::path GetBundleDirectory() { #endif -fs::path GetDesktopPath() { -#if defined(_WIN32) - PWSTR DesktopPath = nullptr; - - if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &DesktopPath))) { - std::wstring wideDesktopPath(DesktopPath); - CoTaskMemFree(DesktopPath); - - // UTF-16 filesystem lib to UTF-8 is broken, so we need to convert to UTF-8 with the with - // the Windows library (Filesystem converts the strings literally). - return fs::path{Common::UTF16ToUTF8(wideDesktopPath)}; - } else { - LOG_ERROR(Common_Filesystem, - "[GetDesktopPath] Failed to get the path to the desktop directory"); - } - return fs::path{}; -#else - fs::path shortcut_path{}; - - // Array of possible desktop - std::vector desktop_paths = { - "Desktop", "Escritorio", "Bureau", "Skrivebord", "Plocha", - "Skrivbord", "Desktop", "Рабочий стол", "Pulpit", "Área de Trabalho", - "Робочий стіл", "Bureaublad", "デスクトップ", "桌面", "Pöytä", - "바탕 화면", "바탕 화면", "سطح المكتب", "دسکتاپ", "שולחן עבודה"}; - - for (auto& path : desktop_paths) { - if (fs::exists(GetHomeDirectory() / path)) { - return path; - } - } - - LOG_ERROR(Common_Filesystem, - "[GetDesktopPath] Failed to get the path to the desktop directory"); - - return shortcut_path; -#endif -} - -fs::path GetAppsShortcutsPath() { -#if defined(_WIN32) - PWSTR AppShortcutsPath = nullptr; - - if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_CommonPrograms, 0, NULL, &AppShortcutsPath))) { - std::wstring wideAppShortcutsPath(AppShortcutsPath); - CoTaskMemFree(AppShortcutsPath); - - // UTF-16 filesystem lib to UTF-8 is broken, so we need to convert to UTF-8 with the with - // the Windows library (Filesystem converts the strings literally). - return fs::path{Common::UTF16ToUTF8(wideAppShortcutsPath)}; - } else { - LOG_ERROR(Common_Filesystem, - "[GetAppsShortcutsPath] Failed to get the path to the App Shortcuts directory"); - } - - return fs::path{}; -#else - fs::path shortcut_path = GetHomeDirectory() / ".local/share/applications"; - if (!fs::exists(shortcut_path)) { - shortcut_path = std::filesystem::path("/usr/share/applications"); - if (!fs::exists(shortcut_path)) { - LOG_ERROR(Common_Filesystem, - "[GetAppsShortcutsPath] Failed to get the path to the App Shortcuts " - "directory"); - } - } - return fs::path{}; -#endif -} - // vvvvvvvvvv Deprecated vvvvvvvvvv // std::string_view RemoveTrailingSlash(std::string_view path) { diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index b88a388d1..63801c924 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h @@ -244,6 +244,7 @@ void SetYuzuPath(YuzuPath yuzu_path, const Path& new_path) { * @returns The path of the current user's %APPDATA% directory. */ [[nodiscard]] std::filesystem::path GetAppDataRoamingDirectory(); + #else /** @@ -274,20 +275,6 @@ void SetYuzuPath(YuzuPath yuzu_path, const Path& new_path) { #endif -/** - * Gets the path of the current user's desktop directory. - * - * @returns The path of the current user's desktop directory. - */ -[[nodiscard]] std::filesystem::path GetDesktopPath(); - -/** - * Gets the path of the current user's apps directory. - * - * @returns The path of the current user's apps directory. - */ -[[nodiscard]] std::filesystem::path GetAppsShortcutsPath(); - // vvvvvvvvvv Deprecated vvvvvvvvvv // // Removes the final '/' or '\' if one exists diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e4dc717ed..fd68ac9b6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3082,13 +3082,9 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga // Shortcut path std::filesystem::path shortcut_path{}; if (target == GameListShortcutTarget::Desktop) { - shortcut_path = Common::FS::GetDesktopPath(); - if (!std::filesystem::exists(shortcut_path)) { - shortcut_path = - QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); - } + shortcut_path = + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); } else if (target == GameListShortcutTarget::Applications) { - #if defined(_WIN32) HANDLE hProcess = GetCurrentProcess(); if (!IsUserAnAdmin()) { @@ -3098,12 +3094,8 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } CloseHandle(hProcess); #endif // _WIN32 - - shortcut_path = Common::FS::GetAppsShortcutsPath(); - if (!std::filesystem::exists(shortcut_path)) { - shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) - .toStdString(); - } + shortcut_path = + QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); } // Icon path and title diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 8839e160a..31e82ff67 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h @@ -8,11 +8,9 @@ #include /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. - [[nodiscard]] QFont GetMonospaceFont(); /// Convert a size in bytes into a readable format (KiB, MiB, etc.) - [[nodiscard]] QString ReadableByteSize(qulonglong size); /** @@ -34,5 +32,4 @@ * * @return bool If the operation succeeded */ - [[nodiscard]] bool SaveIconToFile(const QImage& image, const std::filesystem::path& icon_path); -- cgit v1.2.3 From 9ffa1801c75073afb851351ecdd1a8b40e33cc5c Mon Sep 17 00:00:00 2001 From: boludoz Date: Sun, 15 Oct 2023 20:57:06 -0300 Subject: Typing and formatting errors fixed. --- src/common/fs/fs_util.cpp | 44 ++++++++++++++++++++++---------------------- src/common/fs/fs_util.h | 8 ++++---- src/common/fs/path_util.cpp | 8 +++----- src/yuzu/main.cpp | 24 +++++++++++------------- src/yuzu/util/util.cpp | 3 +-- src/yuzu/util/util.h | 10 ++-------- 6 files changed, 43 insertions(+), 54 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index 442f63728..e77958224 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -36,21 +36,21 @@ std::string PathToUTF8String(const std::filesystem::path& path) { return ToUTF8String(path.u8string()); } -std::u8string U8FilenameSantizer(const std::u8string_view u8filename) { - std::u8string u8path_santized{u8filename.begin(), u8filename.end()}; - size_t eSizeSanitized = u8path_santized.size(); +std::u8string U8FilenameSanitizer(const std::u8string_view u8filename) { + std::u8string u8path_sanitized{u8filename.begin(), u8filename.end()}; + size_t eSizeSanitized = u8path_sanitized.size(); - // Special case for ":", for example: 'Pepe: La secuela' --> 'Pepe - La - // secuela' or 'Pepe : La secuela' --> 'Pepe - La secuela' + // The name is improved to make it look more beautiful and prohibited characters and shapes are + // removed. Switch is used since it is better with many conditions. for (size_t i = 0; i < eSizeSanitized; i++) { - switch (u8path_santized[i]) { + switch (u8path_sanitized[i]) { case u8':': if (i == 0 || i == eSizeSanitized - 1) { - u8path_santized.replace(i, 1, u8"_"); - } else if (u8path_santized[i - 1] == u8' ') { - u8path_santized.replace(i, 1, u8"-"); + u8path_sanitized.replace(i, 1, u8"_"); + } else if (u8path_sanitized[i - 1] == u8' ') { + u8path_sanitized.replace(i, 1, u8"-"); } else { - u8path_santized.replace(i, 1, u8" -"); + u8path_sanitized.replace(i, 1, u8" -"); eSizeSanitized++; } break; @@ -63,36 +63,36 @@ std::u8string U8FilenameSantizer(const std::u8string_view u8filename) { case u8'>': case u8'|': case u8'\0': - u8path_santized.replace(i, 1, u8"_"); + u8path_sanitized.replace(i, 1, u8"_"); break; default: break; } } - // Delete duplicated spaces || Delete duplicated dots (MacOS i think) + // Delete duplicated spaces and dots for (size_t i = 0; i < eSizeSanitized - 1; i++) { - if ((u8path_santized[i] == u8' ' && u8path_santized[i + 1] == u8' ') || - (u8path_santized[i] == u8'.' && u8path_santized[i + 1] == u8'.')) { - u8path_santized.erase(i, 1); + if ((u8path_sanitized[i] == u8' ' && u8path_sanitized[i + 1] == u8' ') || + (u8path_sanitized[i] == u8'.' && u8path_sanitized[i + 1] == u8'.')) { + u8path_sanitized.erase(i, 1); i--; } } - // Delete all spaces and dots at the end (Windows almost) - while (u8path_santized.back() == u8' ' || u8path_santized.back() == u8'.') { - u8path_santized.pop_back(); + // Delete all spaces and dots at the end of the name + while (u8path_sanitized.back() == u8' ' || u8path_sanitized.back() == u8'.') { + u8path_sanitized.pop_back(); } - if (u8path_santized.empty()) { + if (u8path_sanitized.empty()) { return u8""; } - return u8path_santized; + return u8path_sanitized; } -std::string UTF8FilenameSantizer(const std::string_view filename) { - return ToUTF8String(U8FilenameSantizer(ToU8String(filename))); +std::string UTF8FilenameSanitizer(const std::string_view filename) { + return ToUTF8String(U8FilenameSanitizer(ToU8String(filename))); } } // namespace Common::FS diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h index dbb4f5a9a..daec1f8cb 100644 --- a/src/common/fs/fs_util.h +++ b/src/common/fs/fs_util.h @@ -87,19 +87,19 @@ concept IsChar = std::same_as; * * @param u8_string dirty encoded filename string * - * @returns utf8_string santized filename string + * @returns utf8_string sanitized filename string * */ -[[nodiscard]] std::u8string U8FilenameSantizer(const std::u8string_view u8filename); +[[nodiscard]] std::u8string U8FilenameSanitizer(const std::u8string_view u8filename); /** * Fix filename (remove invalid characters) * * @param utf8_string dirty encoded filename string * - * @returns utf8_string santized filename string + * @returns utf8_string sanitized filename string * */ -[[nodiscard]] std::string UTF8FilenameSantizer(const std::string_view filename); +[[nodiscard]] std::string UTF8FilenameSanitizer(const std::string_view filename); } // namespace Common::FS \ No newline at end of file diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index bccf953e4..3d88fcf4f 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -260,9 +260,8 @@ fs::path GetExeDirectory() { // the Windows library (Filesystem converts the strings literally). return fs::path{Common::UTF16ToUTF8(wideExePath)}.parent_path(); } else { - LOG_ERROR(Common_Filesystem, - "[GetExeDirectory] Failed to get the path to the executable of the current " - "process"); + LOG_ERROR(Common_Filesystem, "Failed to get the path to the executable of the current " + "process"); } return fs::path{}; @@ -279,8 +278,7 @@ fs::path GetAppDataRoamingDirectory() { // the Windows library (Filesystem converts the strings literally). return fs::path{Common::UTF16ToUTF8(wideAppdataRoamingPath)}; } else { - LOG_ERROR(Common_Filesystem, - "[GetAppDataRoamingDirectory] Failed to get the path to the %APPDATA% directory"); + LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory"); } return fs::path{}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index fd68ac9b6..c3c12eeec 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2851,7 +2851,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, // Replace characters that are illegal in Windows filenames std::filesystem::path shortcut_path_full = - shortcut_path / Common::FS::UTF8FilenameSantizer(name); + shortcut_path / Common::FS::UTF8FilenameSanitizer(name); #if defined(__linux__) || defined(__FreeBSD__) shortcut_path_full += ".desktop"; @@ -2859,8 +2859,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, shortcut_path_full += ".lnk"; #endif - LOG_INFO(Common, "[GMainWindow::CreateShortcutLink] Create shortcut path: {}", - shortcut_path_full.string()); + LOG_ERROR(Frontend, "Create shortcut path: {}", shortcut_path_full.string()); #if defined(__linux__) || defined(__FreeBSD__) // This desktop file template was writing referencing @@ -2869,7 +2868,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, // Plus 'Type' is required if (name.empty()) { - LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Name is empty"); + LOG_ERROR(Frontend, "Name is empty"); shortcut_succeeded = false; return shortcut_succeeded; } @@ -2911,14 +2910,13 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, return true; } else { - LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Failed to create shortcut"); + LOG_ERROR(Frontend, "Failed to create shortcut"); return false; } shortcut_stream.close(); } catch (const std::exception& e) { - LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Failed to create shortcut: {}", - e.what()); + LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); } #elif defined(_WIN32) // Initialize COM @@ -2970,7 +2968,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, pPersistFile->Release(); } } else { - LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Failed to create IShellLinkWinstance"); + LOG_ERROR(Frontend, "Failed to create IShellLinkWinstance"); } ps1->Release(); @@ -2978,9 +2976,9 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, #endif if (shortcut_succeeded && std::filesystem::is_regular_file(shortcut_path_full)) { - LOG_INFO(Common, "[GMainWindow::CreateShortcutLink] Shortcut created"); + LOG_ERROR(Frontend, "Shortcut created"); } else { - LOG_ERROR(Common, "[GMainWindow::CreateShortcutLink] Shortcut error, failed to create it"); + LOG_ERROR(Frontend, "Shortcut error, failed to create it"); shortcut_succeeded = false; } @@ -3047,7 +3045,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi icons_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "icons"; ico_extension = "ico"; #elif defined(__linux__) || defined(__FreeBSD__) - icons_path = GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; + icons_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; #endif // Create icons directory if it doesn't exist @@ -3130,7 +3128,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); if (GMainWindow::MakeShortcutIcoPath(program_id, title, icons_path)) { - if (!SaveIconToFile(icon_data, icons_path)) { + if (!SaveIconToFile(icons_path, icon_data)) { LOG_ERROR(Frontend, "Could not write icon to file"); } } @@ -3138,7 +3136,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } else { GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, title); - LOG_ERROR(Frontend, "[GMainWindow::OnGameListCreateShortcut] Invalid shortcut target"); + LOG_ERROR(Frontend, "Invalid shortcut target"); return; } diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 9755004ad..7b2a47496 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -42,7 +42,7 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { return circle_pixmap; } -bool SaveIconToFile(const QImage& image, const std::filesystem::path& icon_path) { +bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image) { #if defined(WIN32) #pragma pack(push, 2) struct IconDir { @@ -142,7 +142,6 @@ bool SaveIconToFile(const QImage& image, const std::filesystem::path& icon_path) } else { LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); } - return true; #else return false; diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 31e82ff67..4094cf6c2 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h @@ -15,21 +15,15 @@ /** * Creates a circle pixmap from a specified color - * * @param color The color the pixmap shall have - * * @return QPixmap circle pixmap */ - [[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color); /** * Saves a windows icon to a file - * - * @param image The image to save - * * @param path The icons path - * + * @param image The image to save * @return bool If the operation succeeded */ -[[nodiscard]] bool SaveIconToFile(const QImage& image, const std::filesystem::path& icon_path); +[[nodiscard]] bool SaveIconToFile(const std::filesystem::path& icon_path, const QImage& image); -- cgit v1.2.3 From 74961d4dfb394c098da494f6beacdd994c089566 Mon Sep 17 00:00:00 2001 From: boludoz Date: Sun, 15 Oct 2023 21:40:10 -0300 Subject: Less code, simpler, better. --- src/common/fs/fs_util.cpp | 59 ----------------------------------------------- src/common/fs/fs_util.h | 22 +----------------- src/yuzu/main.cpp | 16 +++++++++++-- 3 files changed, 15 insertions(+), 82 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index e77958224..813a713c3 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -36,63 +36,4 @@ std::string PathToUTF8String(const std::filesystem::path& path) { return ToUTF8String(path.u8string()); } -std::u8string U8FilenameSanitizer(const std::u8string_view u8filename) { - std::u8string u8path_sanitized{u8filename.begin(), u8filename.end()}; - size_t eSizeSanitized = u8path_sanitized.size(); - - // The name is improved to make it look more beautiful and prohibited characters and shapes are - // removed. Switch is used since it is better with many conditions. - for (size_t i = 0; i < eSizeSanitized; i++) { - switch (u8path_sanitized[i]) { - case u8':': - if (i == 0 || i == eSizeSanitized - 1) { - u8path_sanitized.replace(i, 1, u8"_"); - } else if (u8path_sanitized[i - 1] == u8' ') { - u8path_sanitized.replace(i, 1, u8"-"); - } else { - u8path_sanitized.replace(i, 1, u8" -"); - eSizeSanitized++; - } - break; - case u8'\\': - case u8'/': - case u8'*': - case u8'?': - case u8'\"': - case u8'<': - case u8'>': - case u8'|': - case u8'\0': - u8path_sanitized.replace(i, 1, u8"_"); - break; - default: - break; - } - } - - // Delete duplicated spaces and dots - for (size_t i = 0; i < eSizeSanitized - 1; i++) { - if ((u8path_sanitized[i] == u8' ' && u8path_sanitized[i + 1] == u8' ') || - (u8path_sanitized[i] == u8'.' && u8path_sanitized[i + 1] == u8'.')) { - u8path_sanitized.erase(i, 1); - i--; - } - } - - // Delete all spaces and dots at the end of the name - while (u8path_sanitized.back() == u8' ' || u8path_sanitized.back() == u8'.') { - u8path_sanitized.pop_back(); - } - - if (u8path_sanitized.empty()) { - return u8""; - } - - return u8path_sanitized; -} - -std::string UTF8FilenameSanitizer(const std::string_view filename) { - return ToUTF8String(U8FilenameSanitizer(ToU8String(filename))); -} - } // namespace Common::FS diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h index daec1f8cb..2492a9f94 100644 --- a/src/common/fs/fs_util.h +++ b/src/common/fs/fs_util.h @@ -82,24 +82,4 @@ concept IsChar = std::same_as; */ [[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path); -/** - * Fix filename (remove invalid characters) - * - * @param u8_string dirty encoded filename string - * - * @returns utf8_string sanitized filename string - * - */ -[[nodiscard]] std::u8string U8FilenameSanitizer(const std::u8string_view u8filename); - -/** - * Fix filename (remove invalid characters) - * - * @param utf8_string dirty encoded filename string - * - * @returns utf8_string sanitized filename string - * - */ -[[nodiscard]] std::string UTF8FilenameSanitizer(const std::string_view filename); - -} // namespace Common::FS \ No newline at end of file +} // namespace Common::FS diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c3c12eeec..f2b91a7f3 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2849,9 +2849,21 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, bool shortcut_succeeded = false; + // Copy characters if they are not illegal in Windows filenames + std::string filename = ""; + const std::string illegal_chars = "<>:\"/\\|?*"; + filename.reserve(name.size()); + std::copy_if(name.begin(), name.end(), std::back_inserter(filename), + [&illegal_chars](char c) { return illegal_chars.find(c) == std::string::npos; }); + + if (filename.empty()) { + LOG_ERROR(Frontend, "Filename is empty"); + shortcut_succeeded = false; + return shortcut_succeeded; + } + // Replace characters that are illegal in Windows filenames - std::filesystem::path shortcut_path_full = - shortcut_path / Common::FS::UTF8FilenameSanitizer(name); + std::filesystem::path shortcut_path_full = shortcut_path / filename; #if defined(__linux__) || defined(__FreeBSD__) shortcut_path_full += ".desktop"; -- cgit v1.2.3 From 26417da5d36ea5aae4410a5c35405e27f39661d2 Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 03:26:40 -0300 Subject: Some improvements (suggestions) --- src/common/fs/path_util.cpp | 10 +-- src/yuzu/main.cpp | 158 +++++++++++++++++++++----------------------- src/yuzu/main.h | 2 +- 3 files changed, 79 insertions(+), 91 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 3d88fcf4f..bcd64156e 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -254,11 +254,8 @@ fs::path GetExeDirectory() { WCHAR exe_path[MAX_PATH]; if (SUCCEEDED(GetModuleFileNameW(nullptr, exe_path, MAX_PATH))) { - std::wstring wideExePath(exe_path); - - // UTF-16 filesystem lib to UTF-8 is broken, so we need to convert to UTF-8 with the with - // the Windows library (Filesystem converts the strings literally). - return fs::path{Common::UTF16ToUTF8(wideExePath)}.parent_path(); + std::wstring wide_exe_path(exe_path); + return fs::path{Common::UTF16ToUTF8(wide_exe_path)}.parent_path(); } else { LOG_ERROR(Common_Filesystem, "Failed to get the path to the executable of the current " "process"); @@ -273,9 +270,6 @@ fs::path GetAppDataRoamingDirectory() { if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appdata_roaming_path))) { std::wstring wideAppdataRoamingPath(appdata_roaming_path); CoTaskMemFree(appdata_roaming_path); - - // UTF-16 filesystem lib to UTF-8 is broken, so we need to convert to UTF-8 with the with - // the Windows library (Filesystem converts the strings literally). return fs::path{Common::UTF16ToUTF8(wideAppdataRoamingPath)}; } else { LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory"); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f2b91a7f3..36f9551a1 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2847,8 +2847,6 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& arguments, const std::string& categories, const std::string& keywords, const std::string& name) { - bool shortcut_succeeded = false; - // Copy characters if they are not illegal in Windows filenames std::string filename = ""; const std::string illegal_chars = "<>:\"/\\|?*"; @@ -2858,22 +2856,13 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, if (filename.empty()) { LOG_ERROR(Frontend, "Filename is empty"); - shortcut_succeeded = false; - return shortcut_succeeded; + return false; } - // Replace characters that are illegal in Windows filenames + // Append .desktop or .lnk extension std::filesystem::path shortcut_path_full = shortcut_path / filename; -#if defined(__linux__) || defined(__FreeBSD__) - shortcut_path_full += ".desktop"; -#elif defined(_WIN32) - shortcut_path_full += ".lnk"; -#endif - - LOG_ERROR(Frontend, "Create shortcut path: {}", shortcut_path_full.string()); - -#if defined(__linux__) || defined(__FreeBSD__) +#if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD // This desktop file template was writing referencing // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html try { @@ -2881,9 +2870,13 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, // Plus 'Type' is required if (name.empty()) { LOG_ERROR(Frontend, "Name is empty"); - shortcut_succeeded = false; - return shortcut_succeeded; + return false; } + + // Append .desktop extension + shortcut_path_full += ".desktop"; + + // Create shortcut file std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); if (shortcut_stream.is_open()) { @@ -2923,78 +2916,82 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, } else { LOG_ERROR(Frontend, "Failed to create shortcut"); - return false; } shortcut_stream.close(); } catch (const std::exception& e) { LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); } -#elif defined(_WIN32) - // Initialize COM - auto hr = CoInitialize(NULL); - if (FAILED(hr)) { - return shortcut_succeeded; - } + return false; +#elif defined(_WIN32) // Windows + HRESULT hr = CoInitialize(NULL); + if (SUCCEEDED(hr)) { - IShellLinkW* ps1; + IShellLinkW* ps1 = nullptr; + IPersistFile* persist_file = nullptr; - auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, - (void**)&ps1); + SCOPE_EXIT({ + if (persist_file != nullptr) { + persist_file->Release(); + } + if (ps1 != nullptr) { + ps1->Release(); + } - // The UTF-16 / UTF-8 conversion is broken in C++, it is necessary to perform these steps and - // resort to the native Windows function. - std::wstring wshortcut_path_full = Common::UTF8ToUTF16W(shortcut_path_full.string()); - std::wstring wicon_path = Common::UTF8ToUTF16W(icon_path.string()); - std::wstring wcommand = Common::UTF8ToUTF16W(command.string()); - std::wstring warguments = Common::UTF8ToUTF16W(arguments); - std::wstring wcomment = Common::UTF8ToUTF16W(comment); + CoUninitialize(); + }); - if (SUCCEEDED(hres)) { - if (std::filesystem::is_regular_file(command)) - hres = ps1->SetPath(wcommand.data()); + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + IID_IShellLinkW, (void**)&ps1); - if (SUCCEEDED(hres) && !arguments.empty()) - hres = ps1->SetArguments(warguments.data()); + std::wstring wshortcut_path_full = Common::UTF8ToUTF16W(shortcut_path_full.string()); + std::wstring wicon_path = Common::UTF8ToUTF16W(icon_path.string()); + std::wstring wcommand = Common::UTF8ToUTF16W(command.string()); + std::wstring warguments = Common::UTF8ToUTF16W(arguments); + std::wstring wcomment = Common::UTF8ToUTF16W(comment); + + if (SUCCEEDED(hres)) { + if (std::filesystem::is_regular_file(command)) { + hres = ps1->SetPath(wcommand.data()); + } else { + LOG_ERROR(Frontend, "Command is not a regular file"); + return false; + } - if (SUCCEEDED(hres) && !comment.empty()) - hres = ps1->SetDescription(wcomment.data()); + if (SUCCEEDED(hres) && !arguments.empty()) { + hres = ps1->SetArguments(warguments.data()); + } - if (SUCCEEDED(hres) && std::filesystem::is_regular_file(icon_path)) - hres = ps1->SetIconLocation(wicon_path.data(), 0); + if (SUCCEEDED(hres) && !comment.empty()) { + hres = ps1->SetDescription(wcomment.data()); + } - IPersistFile* pPersistFile = nullptr; + if (SUCCEEDED(hres) && std::filesystem::is_regular_file(icon_path)) { + hres = ps1->SetIconLocation(wicon_path.data(), 0); + } - if (SUCCEEDED(hres)) { - hres = ps1->QueryInterface(IID_IPersistFile, (void**)&pPersistFile); + if (SUCCEEDED(hres)) { + hres = ps1->QueryInterface(IID_IPersistFile, (void**)&persist_file); + } - if (SUCCEEDED(hres) && pPersistFile != nullptr) { - hres = pPersistFile->Save(wshortcut_path_full.data(), TRUE); + if (SUCCEEDED(hres) && persist_file != nullptr) { + // Append .lnk extension and save shortcut + shortcut_path_full += ".lnk"; + hres = persist_file->Save(wshortcut_path_full.data(), TRUE); if (SUCCEEDED(hres)) { - shortcut_succeeded = true; + return true; + } else { + LOG_ERROR(Frontend, "Failed to create shortcut"); } } + } else { + LOG_ERROR(Frontend, "Failed to create CoCreateInstance"); } - - if (pPersistFile != nullptr) { - pPersistFile->Release(); - } - } else { - LOG_ERROR(Frontend, "Failed to create IShellLinkWinstance"); } - - ps1->Release(); - CoUninitialize(); + return false; +#else // Unsupported platform + return false; #endif - - if (shortcut_succeeded && std::filesystem::is_regular_file(shortcut_path_full)) { - LOG_ERROR(Frontend, "Shortcut created"); - } else { - LOG_ERROR(Frontend, "Shortcut error, failed to create it"); - shortcut_succeeded = false; - } - - return shortcut_succeeded; } // Messages in pre-defined message boxes for less code spaghetti @@ -3049,31 +3046,31 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, } bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, - std::filesystem::path& icons_path) { + std::filesystem::path& out_icon_path) { // Get path to Yuzu icons directory & icon extension std::string ico_extension = "png"; #if defined(_WIN32) - icons_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "icons"; + out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "icons"; ico_extension = "ico"; #elif defined(__linux__) || defined(__FreeBSD__) - icons_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; + out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; #endif // Create icons directory if it doesn't exist - if (!Common::FS::CreateDirs(icons_path)) { + if (!Common::FS::CreateDirs(out_icon_path)) { QMessageBox::critical( this, tr("Create Icon"), tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") - .arg(QString::fromStdString(icons_path.string())), + .arg(QString::fromStdString(out_icon_path.string())), QMessageBox::StandardButton::Ok); - icons_path = ""; // Reset path + out_icon_path = ""; // Reset path return false; } // Create icon file path - icons_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension) - : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); + out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension) + : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); return true; } @@ -3096,13 +3093,11 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); } else if (target == GameListShortcutTarget::Applications) { #if defined(_WIN32) - HANDLE hProcess = GetCurrentProcess(); if (!IsUserAnAdmin()) { GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN, ""); return; } - CloseHandle(hProcess); #endif // _WIN32 shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); @@ -3110,9 +3105,8 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga // Icon path and title std::string title; - std::filesystem::path icons_path; + std::filesystem::path out_icon_path; if (std::filesystem::exists(shortcut_path)) { - // Get title from game file const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), system->GetContentProvider()}; @@ -3139,8 +3133,8 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga QImage icon_data = QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); - if (GMainWindow::MakeShortcutIcoPath(program_id, title, icons_path)) { - if (!SaveIconToFile(icons_path, icon_data)) { + if (GMainWindow::MakeShortcutIcoPath(program_id, title, out_icon_path)) { + if (!SaveIconToFile(out_icon_path, icon_data)) { LOG_ERROR(Frontend, "Could not write icon to file"); } } @@ -3178,8 +3172,8 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; - if (GMainWindow::CreateShortcutLink(shortcut_path, comment, icons_path, yuzu_command, arguments, - categories, keywords, title)) { + if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command, + arguments, categories, keywords, title)) { GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS, title); return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index d7426607f..d3a1bf5b9 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -444,7 +444,7 @@ private: QString GetTasStateDescription() const; bool CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, const std::string title); bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, - std::filesystem::path& icons_path); + std::filesystem::path& out_icon_path); bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, const std::filesystem::path& icon_path, const std::filesystem::path& command, const std::string& arguments, -- cgit v1.2.3 From 89d3e81be8b923711a548b0973276353392449a6 Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 16:01:46 -0300 Subject: Sugestions and fixes. --- src/common/fs/path_util.cpp | 4 +- src/yuzu/main.cpp | 104 ++++++++++++++++++++++---------------------- src/yuzu/main.h | 2 +- 3 files changed, 56 insertions(+), 54 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index bcd64156e..60c5e477e 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -268,9 +268,9 @@ fs::path GetAppDataRoamingDirectory() { PWSTR appdata_roaming_path = nullptr; if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appdata_roaming_path))) { - std::wstring wideAppdataRoamingPath(appdata_roaming_path); + std::wstring wide_appdata_roaming_path(appdata_roaming_path); CoTaskMemFree(appdata_roaming_path); - return fs::path{Common::UTF16ToUTF8(wideAppdataRoamingPath)}; + return fs::path{Common::UTF16ToUTF8(wide_appdata_roaming_path)}; } else { LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory"); } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 36f9551a1..feb455763 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2859,11 +2859,15 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, return false; } - // Append .desktop or .lnk extension + if (!std::filesystem::is_regular_file(command)) { + LOG_ERROR(Frontend, "Command is not a regular file"); + return false; + } + std::filesystem::path shortcut_path_full = shortcut_path / filename; #if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD - // This desktop file template was writing referencing + // Reference for the desktop file template: // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html try { @@ -2911,17 +2915,17 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, fmt::print(shortcut_stream, "Keywords={}\n", keywords); } + // Flush and close file + shortcut_stream.flush(); shortcut_stream.close(); return true; - } else { LOG_ERROR(Frontend, "Failed to create shortcut"); } - - shortcut_stream.close(); } catch (const std::exception& e) { LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); } + shortcut_stream.close(); return false; #elif defined(_WIN32) // Windows HRESULT hr = CoInitialize(NULL); @@ -2944,19 +2948,15 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (void**)&ps1); - std::wstring wshortcut_path_full = Common::UTF8ToUTF16W(shortcut_path_full.string()); + std::wstring wshortcut_path_full = + Common::UTF8ToUTF16W((shortcut_path_full).string() + ".lnk"); std::wstring wicon_path = Common::UTF8ToUTF16W(icon_path.string()); std::wstring wcommand = Common::UTF8ToUTF16W(command.string()); std::wstring warguments = Common::UTF8ToUTF16W(arguments); std::wstring wcomment = Common::UTF8ToUTF16W(comment); if (SUCCEEDED(hres)) { - if (std::filesystem::is_regular_file(command)) { - hres = ps1->SetPath(wcommand.data()); - } else { - LOG_ERROR(Frontend, "Command is not a regular file"); - return false; - } + hres = ps1->SetPath(wcommand.data()); if (SUCCEEDED(hres) && !arguments.empty()) { hres = ps1->SetArguments(warguments.data()); @@ -2975,8 +2975,6 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, } if (SUCCEEDED(hres) && persist_file != nullptr) { - // Append .lnk extension and save shortcut - shortcut_path_full += ".lnk"; hres = persist_file->Save(wshortcut_path_full.data(), TRUE); if (SUCCEEDED(hres)) { return true; @@ -2995,9 +2993,10 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, } // Messages in pre-defined message boxes for less code spaghetti -bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, - const std::string title) { +bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) { QMessageBox::StandardButtons buttons; + std::string_view game_title_sv = game_title.toStdString(); + int result = 0; switch (imsg) { @@ -3013,18 +3012,18 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, return (result == QMessageBox::No) ? false : true; case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: - QMessageBox::information( - parent, tr("Create Shortcut"), - tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); - LOG_INFO(Frontend, "Successfully created a shortcut to {}", title); + QMessageBox::information(parent, tr("Create Shortcut"), + tr("Successfully created a shortcut to %1").arg(game_title)); + LOG_INFO(Frontend, "Successfully created a shortcut to {}", game_title_sv); return true; case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: - result = QMessageBox::warning( - this, tr("Create Shortcut"), - tr("This will create a shortcut to the current AppImage. This may " - "not work well if you update. Continue?"), - QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel); + buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel; + result = + QMessageBox::warning(this, tr("Create Shortcut"), + tr("This will create a shortcut to the current AppImage. This may " + "not work well if you update. Continue?"), + buttons); return (result == QMessageBox::StandardButton::Cancel) ? true : false; case GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN: buttons = QMessageBox::Ok; @@ -3035,10 +3034,9 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, return true; default: buttons = QMessageBox::Ok; - QMessageBox::critical( - parent, tr("Create Shortcut"), - tr("Failed to create a shortcut to %1").arg(QString::fromStdString(title)), buttons); - LOG_ERROR(Frontend, "Failed to create a shortcut to {}", title); + QMessageBox::critical(parent, tr("Create Shortcut"), + tr("Failed to create a shortcut to %1").arg(game_title), buttons); + LOG_ERROR(Frontend, "Failed to create a shortcut to {}", game_title_sv); return true; } @@ -3077,6 +3075,10 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target) { + std::string game_title; + QString qt_game_title; + std::filesystem::path out_icon_path; + // Get path to yuzu executable const QStringList args = QApplication::arguments(); std::filesystem::path yuzu_command = args[0].toStdString(); @@ -3092,20 +3094,11 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga shortcut_path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toStdString(); } else if (target == GameListShortcutTarget::Applications) { -#if defined(_WIN32) - if (!IsUserAnAdmin()) { - GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN, - ""); - return; - } -#endif // _WIN32 shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); } // Icon path and title - std::string title; - std::filesystem::path out_icon_path; if (std::filesystem::exists(shortcut_path)) { // Get title from game file const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), @@ -3114,14 +3107,16 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); - title = fmt::format("{:016X}", program_id); + game_title = fmt::format("{:016X}", program_id); if (control.first != nullptr) { - title = control.first->GetApplicationName(); + game_title = control.first->GetApplicationName(); } else { - loader->ReadTitle(title); + loader->ReadTitle(game_title); } + qt_game_title = QString::fromStdString(game_title); + // Get icon from game file std::vector icon_image_file{}; if (control.second != nullptr) { @@ -3133,7 +3128,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga QImage icon_data = QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); - if (GMainWindow::MakeShortcutIcoPath(program_id, title, out_icon_path)) { + if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { if (!SaveIconToFile(out_icon_path, icon_data)) { LOG_ERROR(Frontend, "Could not write icon to file"); } @@ -3141,20 +3136,26 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } else { GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, - title); + qt_game_title); LOG_ERROR(Frontend, "Invalid shortcut target"); return; } -// Special case for AppImages -#if defined(__linux__) +#if defined(_WIN32) + if (!IsUserAnAdmin() && target == GameListShortcutTarget::Applications) { + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN, + qt_game_title); + return; + } +#elif defined(__linux__) + // Special case for AppImages // Warn once if we are making a shortcut to a volatile AppImage const std::string appimage_ending = std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); if (yuzu_command.string().ends_with(appimage_ending) && !UISettings::values.shortcut_already_warned) { if (GMainWindow::CreateShortcutMessagesGUI( - this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, title)) { + this, GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, qt_game_title)) { return; } UISettings::values.shortcut_already_warned = true; @@ -3164,22 +3165,23 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga // Create shortcut std::string arguments = fmt::format("-g \"{:s}\"", game_path); if (GMainWindow::CreateShortcutMessagesGUI( - this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, title)) { + this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) { arguments = "-f " + arguments; } const std::string comment = - tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); + tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(game_title)).toStdString(); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; if (GMainWindow::CreateShortcutLink(shortcut_path, comment, out_icon_path, yuzu_command, - arguments, categories, keywords, title)) { + arguments, categories, keywords, game_title)) { GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS, - title); + qt_game_title); return; } - GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, title); + GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, + qt_game_title); } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index d3a1bf5b9..41902cc8d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -442,7 +442,7 @@ private: bool ConfirmShutdownGame(); QString GetTasStateDescription() const; - bool CreateShortcutMessagesGUI(QWidget* parent, const int& imsg, const std::string title); + bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title); bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path); bool CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, -- cgit v1.2.3 From d759de9f96faa8f530b3724b79ccc04ab0d01a09 Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 16:11:24 -0300 Subject: More missed suggestions --- src/yuzu/main.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index feb455763..90bb61c15 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2995,12 +2995,10 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, // Messages in pre-defined message boxes for less code spaghetti bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) { QMessageBox::StandardButtons buttons; - std::string_view game_title_sv = game_title.toStdString(); int result = 0; switch (imsg) { - case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES: buttons = QMessageBox::Yes | QMessageBox::No; @@ -3009,12 +3007,11 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt tr("Do you want to launch the game in fullscreen?"), buttons); LOG_INFO(Frontend, "Shortcut will launch in fullscreen"); - return (result == QMessageBox::No) ? false : true; + return result == QMessageBox::Yes; case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: QMessageBox::information(parent, tr("Create Shortcut"), tr("Successfully created a shortcut to %1").arg(game_title)); - LOG_INFO(Frontend, "Successfully created a shortcut to {}", game_title_sv); return true; case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: @@ -3024,7 +3021,7 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt tr("This will create a shortcut to the current AppImage. This may " "not work well if you update. Continue?"), buttons); - return (result == QMessageBox::StandardButton::Cancel) ? true : false; + return result == QMessageBox::StandardButton::Ok; case GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN: buttons = QMessageBox::Ok; QMessageBox::critical(parent, tr("Create Shortcut"), @@ -3036,7 +3033,6 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt buttons = QMessageBox::Ok; QMessageBox::critical(parent, tr("Create Shortcut"), tr("Failed to create a shortcut to %1").arg(game_title), buttons); - LOG_ERROR(Frontend, "Failed to create a shortcut to {}", game_title_sv); return true; } -- cgit v1.2.3 From ae88d01d8dfc9c39206d3fb37938e44a3480c70f Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 16:47:21 -0300 Subject: .clear() instead = ""; and switch improved. --- src/yuzu/main.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 90bb61c15..82b38fc07 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2997,23 +2997,17 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt QMessageBox::StandardButtons buttons; int result = 0; - switch (imsg) { case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES: buttons = QMessageBox::Yes | QMessageBox::No; - result = QMessageBox::information(parent, tr("Create Shortcut"), tr("Do you want to launch the game in fullscreen?"), buttons); - - LOG_INFO(Frontend, "Shortcut will launch in fullscreen"); - return result == QMessageBox::Yes; - + return (result == QMessageBox::Yes); case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: QMessageBox::information(parent, tr("Create Shortcut"), tr("Successfully created a shortcut to %1").arg(game_title)); - return true; - + break; case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel; result = @@ -3021,22 +3015,20 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt tr("This will create a shortcut to the current AppImage. This may " "not work well if you update. Continue?"), buttons); - return result == QMessageBox::StandardButton::Ok; + return (result == QMessageBox::Ok); case GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN: buttons = QMessageBox::Ok; QMessageBox::critical(parent, tr("Create Shortcut"), tr("Cannot create shortcut in Apps. Restart yuzu as administrator."), buttons); - LOG_ERROR(Frontend, "Cannot create shortcut in Apps. Restart yuzu as administrator."); - return true; + break; default: buttons = QMessageBox::Ok; QMessageBox::critical(parent, tr("Create Shortcut"), tr("Failed to create a shortcut to %1").arg(game_title), buttons); - return true; + break; } - - return true; + return false; } bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, @@ -3058,7 +3050,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") .arg(QString::fromStdString(out_icon_path.string())), QMessageBox::StandardButton::Ok); - out_icon_path = ""; // Reset path + out_icon_path.clear(); return false; } @@ -3165,7 +3157,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga arguments = "-f " + arguments; } const std::string comment = - tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(game_title)).toStdString(); + tr("Start %1 with the yuzu Emulator").arg(qt_game_title).toStdString(); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; -- cgit v1.2.3 From de0b35b97456313498785facccad5980cea737f7 Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 16:54:51 -0300 Subject: Comment using fmt instead qt. --- src/yuzu/main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 82b38fc07..54b36552d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3156,8 +3156,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) { arguments = "-f " + arguments; } - const std::string comment = - tr("Start %1 with the yuzu Emulator").arg(qt_game_title).toStdString(); + const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; -- cgit v1.2.3 From 1ae0f0f3f64446b3128da0f1b1151d95739c9a6e Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 18:59:21 -0300 Subject: shortcut_stream.close(); fixed --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 54b36552d..e49de921c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2922,10 +2922,10 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, } else { LOG_ERROR(Frontend, "Failed to create shortcut"); } + shortcut_stream.close(); } catch (const std::exception& e) { LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); } - shortcut_stream.close(); return false; #elif defined(_WIN32) // Windows HRESULT hr = CoInitialize(NULL); -- cgit v1.2.3 From 1afe6d51eed4a420bb7289955333a2bb10c06b99 Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 23:42:45 -0300 Subject: More @liamwhite suggestions applied. --- src/common/fs/path_util.cpp | 12 +-- src/yuzu/main.cpp | 200 +++++++++++++++++++------------------------- 2 files changed, 90 insertions(+), 122 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 60c5e477e..732c1559f 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp @@ -252,29 +252,23 @@ void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { fs::path GetExeDirectory() { WCHAR exe_path[MAX_PATH]; - if (SUCCEEDED(GetModuleFileNameW(nullptr, exe_path, MAX_PATH))) { std::wstring wide_exe_path(exe_path); return fs::path{Common::UTF16ToUTF8(wide_exe_path)}.parent_path(); - } else { - LOG_ERROR(Common_Filesystem, "Failed to get the path to the executable of the current " - "process"); } - + LOG_ERROR(Common_Filesystem, "Failed to get the path to the executable of the current " + "process"); return fs::path{}; } fs::path GetAppDataRoamingDirectory() { PWSTR appdata_roaming_path = nullptr; - if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appdata_roaming_path))) { std::wstring wide_appdata_roaming_path(appdata_roaming_path); CoTaskMemFree(appdata_roaming_path); return fs::path{Common::UTF16ToUTF8(wide_appdata_roaming_path)}; - } else { - LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory"); } - + LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory"); return fs::path{}; } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e49de921c..75fbd042a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2845,7 +2845,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::filesystem::path& icon_path, const std::filesystem::path& command, const std::string& arguments, const std::string& categories, - const std::string& keywords, const std::string& name) { + const std::string& keywords, const std::string& name) try { // Copy characters if they are not illegal in Windows filenames std::string filename = ""; @@ -2869,145 +2869,120 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, #if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD // Reference for the desktop file template: // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html - try { - // Plus 'Type' is required - if (name.empty()) { - LOG_ERROR(Frontend, "Name is empty"); - return false; - } - - // Append .desktop extension - shortcut_path_full += ".desktop"; - - // Create shortcut file - std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); - - if (shortcut_stream.is_open()) { - - fmt::print(shortcut_stream, "[Desktop Entry]\n"); - fmt::print(shortcut_stream, "Type=Application\n"); - fmt::print(shortcut_stream, "Version=1.0\n"); - fmt::print(shortcut_stream, "Name={}\n", name); - - if (!comment.empty()) { - fmt::print(shortcut_stream, "Comment={}\n", comment); - } - - if (std::filesystem::is_regular_file(icon_path)) { - fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); - } - - fmt::print(shortcut_stream, "TryExec={}\n", command.string()); - fmt::print(shortcut_stream, "Exec={}", command.string()); - - if (!arguments.empty()) { - fmt::print(shortcut_stream, " {}", arguments); - } - - fmt::print(shortcut_stream, "\n"); + // Plus 'Type' is required + if (name.empty()) { + LOG_ERROR(Frontend, "Name is empty"); + return false; + } - if (!categories.empty()) { - fmt::print(shortcut_stream, "Categories={}\n", categories); - } + // Append .desktop extension + shortcut_path_full += ".desktop"; - if (!keywords.empty()) { - fmt::print(shortcut_stream, "Keywords={}\n", keywords); - } + // Create shortcut file + std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); + if (!shortcut_stream.is_open()) { + LOG_ERROR(Frontend, "Failed to create shortcut"); + return false; + } - // Flush and close file - shortcut_stream.flush(); - shortcut_stream.close(); - return true; - } else { - LOG_ERROR(Frontend, "Failed to create shortcut"); - } - shortcut_stream.close(); - } catch (const std::exception& e) { - LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); + fmt::print(shortcut_stream, "[Desktop Entry]\n"); + fmt::print(shortcut_stream, "Type=Application\n"); + fmt::print(shortcut_stream, "Version=1.0\n"); + fmt::print(shortcut_stream, "Name={}\n", name); + if (!comment.empty()) { + fmt::print(shortcut_stream, "Comment={}\n", comment); } - return false; + if (std::filesystem::is_regular_file(icon_path)) { + fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); + } + fmt::print(shortcut_stream, "TryExec={}\n", command.string()); + fmt::print(shortcut_stream, "Exec={}", command.string()); + if (!arguments.empty()) { + fmt::print(shortcut_stream, " {}", arguments); + } + fmt::print(shortcut_stream, "\n"); + if (!categories.empty()) { + fmt::print(shortcut_stream, "Categories={}\n", categories); + } + if (!keywords.empty()) { + fmt::print(shortcut_stream, "Keywords={}\n", keywords); + } + return true; #elif defined(_WIN32) // Windows HRESULT hr = CoInitialize(NULL); - if (SUCCEEDED(hr)) { - - IShellLinkW* ps1 = nullptr; - IPersistFile* persist_file = nullptr; - - SCOPE_EXIT({ - if (persist_file != nullptr) { - persist_file->Release(); - } - if (ps1 != nullptr) { - ps1->Release(); - } - - CoUninitialize(); - }); - - HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, - IID_IShellLinkW, (void**)&ps1); - - std::wstring wshortcut_path_full = - Common::UTF8ToUTF16W((shortcut_path_full).string() + ".lnk"); - std::wstring wicon_path = Common::UTF8ToUTF16W(icon_path.string()); - std::wstring wcommand = Common::UTF8ToUTF16W(command.string()); - std::wstring warguments = Common::UTF8ToUTF16W(arguments); - std::wstring wcomment = Common::UTF8ToUTF16W(comment); + if (FAILED(hr)) { + LOG_ERROR(Frontend, "CoInitialize failed"); + return false; + } - if (SUCCEEDED(hres)) { - hres = ps1->SetPath(wcommand.data()); + IShellLinkW* ps1 = nullptr; + IPersistFile* persist_file = nullptr; - if (SUCCEEDED(hres) && !arguments.empty()) { - hres = ps1->SetArguments(warguments.data()); - } - - if (SUCCEEDED(hres) && !comment.empty()) { - hres = ps1->SetDescription(wcomment.data()); - } + SCOPE_EXIT({ + if (persist_file != nullptr) { + persist_file->Release(); + } + if (ps1 != nullptr) { + ps1->Release(); + } - if (SUCCEEDED(hres) && std::filesystem::is_regular_file(icon_path)) { - hres = ps1->SetIconLocation(wicon_path.data(), 0); - } + CoUninitialize(); + }); - if (SUCCEEDED(hres)) { - hres = ps1->QueryInterface(IID_IPersistFile, (void**)&persist_file); - } + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, + (void**)&ps1); - if (SUCCEEDED(hres) && persist_file != nullptr) { - hres = persist_file->Save(wshortcut_path_full.data(), TRUE); - if (SUCCEEDED(hres)) { - return true; - } else { - LOG_ERROR(Frontend, "Failed to create shortcut"); - } - } - } else { - LOG_ERROR(Frontend, "Failed to create CoCreateInstance"); - } + if (SUCCEEDED(hres)) { + hres = ps1->SetPath(Common::UTF8ToUTF16W(command.string()).data()); + } else { + LOG_ERROR(Frontend, "Failed to create CoCreateInstance"); + return false; + } + if (SUCCEEDED(hres) && !arguments.empty()) { + hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data()); } + if (SUCCEEDED(hres) && !comment.empty()) { + hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data()); + } + if (SUCCEEDED(hres) && std::filesystem::is_regular_file(icon_path)) { + hres = ps1->SetIconLocation(Common::UTF8ToUTF16W(icon_path.string()).data(), 0); + } + if (SUCCEEDED(hres)) { + hres = ps1->QueryInterface(IID_IPersistFile, (void**)&persist_file); + } + if (SUCCEEDED(hres) && persist_file != nullptr) { + hres = persist_file->Save( + Common::UTF8ToUTF16W((shortcut_path_full).string() + ".lnk").data(), TRUE); + } + if (SUCCEEDED(hres)) { + return true; + } + LOG_ERROR(Frontend, "Failed to create shortcut"); return false; #else // Unsupported platform return false; #endif +} catch (const std::exception& e) { + LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); + return false; } // Messages in pre-defined message boxes for less code spaghetti bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) { - QMessageBox::StandardButtons buttons; - int result = 0; + QMessageBox::StandardButtons buttons; switch (imsg) { case GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES: buttons = QMessageBox::Yes | QMessageBox::No; result = QMessageBox::information(parent, tr("Create Shortcut"), tr("Do you want to launch the game in fullscreen?"), buttons); - return (result == QMessageBox::Yes); + return result == QMessageBox::Yes; case GMainWindow::CREATE_SHORTCUT_MSGBOX_SUCCESS: QMessageBox::information(parent, tr("Create Shortcut"), tr("Successfully created a shortcut to %1").arg(game_title)); - break; + return false; case GMainWindow::CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING: buttons = QMessageBox::StandardButton::Ok | QMessageBox::StandardButton::Cancel; result = @@ -3015,20 +2990,19 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt tr("This will create a shortcut to the current AppImage. This may " "not work well if you update. Continue?"), buttons); - return (result == QMessageBox::Ok); + return result == QMessageBox::Ok; case GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN: buttons = QMessageBox::Ok; QMessageBox::critical(parent, tr("Create Shortcut"), tr("Cannot create shortcut in Apps. Restart yuzu as administrator."), buttons); - break; + return false; default: buttons = QMessageBox::Ok; QMessageBox::critical(parent, tr("Create Shortcut"), tr("Failed to create a shortcut to %1").arg(game_title), buttons); - break; + return false; } - return false; } bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, -- cgit v1.2.3 From fc4b45ebd344a3e3d571b8516e73cd9afe824a9b Mon Sep 17 00:00:00 2001 From: boludoz Date: Mon, 16 Oct 2023 23:50:09 -0300 Subject: Moved check. --- src/yuzu/main.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 75fbd042a..0e0d7ef0e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2847,6 +2847,13 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& arguments, const std::string& categories, const std::string& keywords, const std::string& name) try { +#if defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32) // Linux, FreeBSD and Windows + // Plus 'name' is required + if (name.empty()) { + LOG_ERROR(Frontend, "Name is empty"); + return false; + } + // Copy characters if they are not illegal in Windows filenames std::string filename = ""; const std::string illegal_chars = "<>:\"/\\|?*"; @@ -2865,17 +2872,12 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, } std::filesystem::path shortcut_path_full = shortcut_path / filename; +#endif #if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD // Reference for the desktop file template: // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html - // Plus 'Type' is required - if (name.empty()) { - LOG_ERROR(Frontend, "Name is empty"); - return false; - } - // Append .desktop extension shortcut_path_full += ".desktop"; -- cgit v1.2.3 From 9908434c14d68faa7fde933258aafd7da15b3b3b Mon Sep 17 00:00:00 2001 From: boludoz Date: Tue, 17 Oct 2023 02:57:35 -0300 Subject: Final refactorization --- src/yuzu/main.cpp | 131 +++++++++++++++++++----------------------------------- 1 file changed, 45 insertions(+), 86 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0e0d7ef0e..01b64e165 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2846,48 +2846,13 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::filesystem::path& command, const std::string& arguments, const std::string& categories, const std::string& keywords, const std::string& name) try { - -#if defined(__linux__) || defined(__FreeBSD__) || defined(_WIN32) // Linux, FreeBSD and Windows - // Plus 'name' is required - if (name.empty()) { - LOG_ERROR(Frontend, "Name is empty"); - return false; - } - - // Copy characters if they are not illegal in Windows filenames - std::string filename = ""; - const std::string illegal_chars = "<>:\"/\\|?*"; - filename.reserve(name.size()); - std::copy_if(name.begin(), name.end(), std::back_inserter(filename), - [&illegal_chars](char c) { return illegal_chars.find(c) == std::string::npos; }); - - if (filename.empty()) { - LOG_ERROR(Frontend, "Filename is empty"); - return false; - } - - if (!std::filesystem::is_regular_file(command)) { - LOG_ERROR(Frontend, "Command is not a regular file"); - return false; - } - - std::filesystem::path shortcut_path_full = shortcut_path / filename; -#endif - #if defined(__linux__) || defined(__FreeBSD__) // Linux and FreeBSD - // Reference for the desktop file template: - // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html - - // Append .desktop extension - shortcut_path_full += ".desktop"; - - // Create shortcut file + std::filesystem::path shortcut_path_full = shortcut_path / (name + ".desktop"); std::ofstream shortcut_stream(shortcut_path_full, std::ios::binary | std::ios::trunc); if (!shortcut_stream.is_open()) { LOG_ERROR(Frontend, "Failed to create shortcut"); return false; } - fmt::print(shortcut_stream, "[Desktop Entry]\n"); fmt::print(shortcut_stream, "Type=Application\n"); fmt::print(shortcut_stream, "Version=1.0\n"); @@ -2899,11 +2864,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, fmt::print(shortcut_stream, "Icon={}\n", icon_path.string()); } fmt::print(shortcut_stream, "TryExec={}\n", command.string()); - fmt::print(shortcut_stream, "Exec={}", command.string()); - if (!arguments.empty()) { - fmt::print(shortcut_stream, " {}", arguments); - } - fmt::print(shortcut_stream, "\n"); + fmt::print(shortcut_stream, "Exec={} {}\n", command.string(), arguments); if (!categories.empty()) { fmt::print(shortcut_stream, "Categories={}\n", categories); } @@ -2912,15 +2873,14 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, } return true; #elif defined(_WIN32) // Windows - HRESULT hr = CoInitialize(NULL); + HRESULT hr = CoInitialize(nullptr); if (FAILED(hr)) { LOG_ERROR(Frontend, "CoInitialize failed"); return false; } - + SCOPE_EXIT({ CoUninitialize(); }); IShellLinkW* ps1 = nullptr; IPersistFile* persist_file = nullptr; - SCOPE_EXIT({ if (persist_file != nullptr) { persist_file->Release(); @@ -2928,40 +2888,50 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, if (ps1 != nullptr) { ps1->Release(); } - - CoUninitialize(); }); - - HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, - (void**)&ps1); - - if (SUCCEEDED(hres)) { - hres = ps1->SetPath(Common::UTF8ToUTF16W(command.string()).data()); - } else { - LOG_ERROR(Frontend, "Failed to create CoCreateInstance"); + HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, + reinterpret_cast(&ps1)); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to create IShellLinkW instance"); return false; } - if (SUCCEEDED(hres) && !arguments.empty()) { + hres = ps1->SetPath(command.c_str()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set path"); + return false; + } + if (!arguments.empty()) { hres = ps1->SetArguments(Common::UTF8ToUTF16W(arguments).data()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set arguments"); + return false; + } } - if (SUCCEEDED(hres) && !comment.empty()) { + if (!comment.empty()) { hres = ps1->SetDescription(Common::UTF8ToUTF16W(comment).data()); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set description"); + return false; + } } - if (SUCCEEDED(hres) && std::filesystem::is_regular_file(icon_path)) { - hres = ps1->SetIconLocation(Common::UTF8ToUTF16W(icon_path.string()).data(), 0); - } - if (SUCCEEDED(hres)) { - hres = ps1->QueryInterface(IID_IPersistFile, (void**)&persist_file); + if (std::filesystem::is_regular_file(icon_path)) { + hres = ps1->SetIconLocation(icon_path.c_str(), 0); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to set icon location"); + return false; + } } - if (SUCCEEDED(hres) && persist_file != nullptr) { - hres = persist_file->Save( - Common::UTF8ToUTF16W((shortcut_path_full).string() + ".lnk").data(), TRUE); + hres = ps1->QueryInterface(IID_IPersistFile, reinterpret_cast(&persist_file)); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to get IPersistFile interface"); + return false; } - if (SUCCEEDED(hres)) { - return true; + hres = persist_file->Save(std::filesystem::path{shortcut_path / (name + ".lnk")}.c_str(), TRUE); + if (FAILED(hres)) { + LOG_ERROR(Frontend, "Failed to save shortcut"); + return false; } - LOG_ERROR(Frontend, "Failed to create shortcut"); - return false; + return true; #else // Unsupported platform return false; #endif @@ -2969,7 +2939,6 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, LOG_ERROR(Frontend, "Failed to create shortcut: {}", e.what()); return false; } - // Messages in pre-defined message boxes for less code spaghetti bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title) { int result = 0; @@ -3009,7 +2978,6 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path) { - // Get path to Yuzu icons directory & icon extension std::string ico_extension = "png"; #if defined(_WIN32) @@ -3038,20 +3006,16 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target) { - std::string game_title; QString qt_game_title; std::filesystem::path out_icon_path; - // Get path to yuzu executable const QStringList args = QApplication::arguments(); std::filesystem::path yuzu_command = args[0].toStdString(); - // If relative path, make it an absolute path if (yuzu_command.c_str()[0] == '.') { yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; } - // Shortcut path std::filesystem::path shortcut_path{}; if (target == GameListShortcutTarget::Desktop) { @@ -3061,7 +3025,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga shortcut_path = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); } - // Icon path and title if (std::filesystem::exists(shortcut_path)) { // Get title from game file @@ -3070,17 +3033,20 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga const auto control = pm.GetControlMetadata(); const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); - game_title = fmt::format("{:016X}", program_id); - if (control.first != nullptr) { game_title = control.first->GetApplicationName(); } else { loader->ReadTitle(game_title); } - + // Delete illegal characters from title + const std::string illegal_chars = "<>:\"/\\|?*."; + for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) { + if (illegal_chars.find(*it) != std::string::npos) { + game_title.erase(it.base() - 1); + } + } qt_game_title = QString::fromStdString(game_title); - // Get icon from game file std::vector icon_image_file{}; if (control.second != nullptr) { @@ -3088,23 +3054,19 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); } - QImage icon_data = QImage::fromData(icon_image_file.data(), static_cast(icon_image_file.size())); - if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { if (!SaveIconToFile(out_icon_path, icon_data)) { LOG_ERROR(Frontend, "Could not write icon to file"); } } - } else { GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, qt_game_title); LOG_ERROR(Frontend, "Invalid shortcut target"); return; } - #if defined(_WIN32) if (!IsUserAnAdmin() && target == GameListShortcutTarget::Applications) { GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN, @@ -3125,7 +3087,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga UISettings::values.shortcut_already_warned = true; } #endif // __linux__ - // Create shortcut std::string arguments = fmt::format("-g \"{:s}\"", game_path); if (GMainWindow::CreateShortcutMessagesGUI( @@ -3142,7 +3103,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga qt_game_title); return; } - GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, qt_game_title); } @@ -4177,7 +4137,6 @@ void GMainWindow::OnLoadAmiibo() { bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) { - QMessageBox* box_dialog = new QMessageBox(parent); box_dialog->setWindowTitle(title); box_dialog->setText(text); -- cgit v1.2.3 From 2a7edda70ac7bfcce6a830c814a6b8cd559b8c03 Mon Sep 17 00:00:00 2001 From: boludoz Date: Wed, 18 Oct 2023 01:20:46 -0300 Subject: Deleted admin requisite (maybe it was another mistake). --- src/yuzu/main.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 01b64e165..37f1c0bc4 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -3067,13 +3067,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga LOG_ERROR(Frontend, "Invalid shortcut target"); return; } -#if defined(_WIN32) - if (!IsUserAnAdmin() && target == GameListShortcutTarget::Applications) { - GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN, - qt_game_title); - return; - } -#elif defined(__linux__) +#if defined(__linux__) // Special case for AppImages // Warn once if we are making a shortcut to a volatile AppImage const std::string appimage_ending = -- cgit v1.2.3 From 4051bbbed7f0160a6565212affbbb24553a9b510 Mon Sep 17 00:00:00 2001 From: boludoz Date: Wed, 18 Oct 2023 01:26:50 -0300 Subject: Useless code removed related to admin privileges, if it is not an error we can add it later, that is what git is for. --- src/yuzu/main.cpp | 6 ------ src/yuzu/main.h | 1 - 2 files changed, 7 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 37f1c0bc4..73cd06478 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2962,12 +2962,6 @@ bool GMainWindow::CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QSt "not work well if you update. Continue?"), buttons); return result == QMessageBox::Ok; - case GMainWindow::CREATE_SHORTCUT_MSGBOX_ADMIN: - buttons = QMessageBox::Ok; - QMessageBox::critical(parent, tr("Create Shortcut"), - tr("Cannot create shortcut in Apps. Restart yuzu as administrator."), - buttons); - return false; default: buttons = QMessageBox::Ok; QMessageBox::critical(parent, tr("Create Shortcut"), diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 41902cc8d..d203e5301 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -157,7 +157,6 @@ class GMainWindow : public QMainWindow { CREATE_SHORTCUT_MSGBOX_SUCCESS, CREATE_SHORTCUT_MSGBOX_ERROR, CREATE_SHORTCUT_MSGBOX_APPVOLATILE_WARNING, - CREATE_SHORTCUT_MSGBOX_ADMIN, }; public: -- cgit v1.2.3 From ac6290bea76e9719ef7806dab7f92ab53fdd27d0 Mon Sep 17 00:00:00 2001 From: boludoz Date: Wed, 18 Oct 2023 02:35:23 -0300 Subject: TODO: Implement shortcut creation for Apple. --- src/yuzu/game_list.cpp | 6 ++++++ src/yuzu/main.cpp | 8 ++++++-- src/yuzu/main.h | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index fbe099661..90433e245 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -564,10 +564,13 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); +// TODO: Implement shortcut creation for macOS +#if !defined(__APPLE__) QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); QAction* create_applications_menu_shortcut = shortcut_menu->addAction(tr("Add to Applications Menu")); +#endif context_menu.addSeparator(); QAction* properties = context_menu.addAction(tr("Properties")); @@ -642,12 +645,15 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); +// TODO: Implement shortcut creation for macOS +#if !defined(__APPLE__) connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); }); connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); }); +#endif connect(properties, &QAction::triggered, [this, path]() { emit OpenPerGameGeneralRequested(path); }); }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 73cd06478..ec3eb7536 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2839,7 +2839,8 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } - +// TODO: Implement shortcut creation for macOS +#if !defined(__APPLE__) bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, const std::filesystem::path& icon_path, @@ -2997,9 +2998,11 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); return true; } - +#endif // !defined(__APPLE__) void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target) { +// TODO: Implement shortcut creation for macOS +#if !defined(__APPLE__) std::string game_title; QString qt_game_title; std::filesystem::path out_icon_path; @@ -3093,6 +3096,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, qt_game_title); +#endif } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index d203e5301..7a1a97f33 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -441,6 +441,8 @@ private: bool ConfirmShutdownGame(); QString GetTasStateDescription() const; +// TODO: Implement shortcut creation for macOS +#if !defined(__APPLE__) bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title); bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path); @@ -449,6 +451,7 @@ private: const std::filesystem::path& command, const std::string& arguments, const std::string& categories, const std::string& keywords, const std::string& name); +#endif /** * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog * The only difference is that it returns a boolean. -- cgit v1.2.3 From ae2130470effa72c3ea1ffc045e9b6b2a77b23d3 Mon Sep 17 00:00:00 2001 From: boludoz Date: Wed, 18 Oct 2023 19:30:21 -0300 Subject: Reverted dirty code in main. --- src/yuzu/main.cpp | 8 ++------ src/yuzu/main.h | 3 --- 2 files changed, 2 insertions(+), 9 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ec3eb7536..73cd06478 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2839,8 +2839,7 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } -// TODO: Implement shortcut creation for macOS -#if !defined(__APPLE__) + bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, const std::string& comment, const std::filesystem::path& icon_path, @@ -2998,11 +2997,9 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); return true; } -#endif // !defined(__APPLE__) + void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, GameListShortcutTarget target) { -// TODO: Implement shortcut creation for macOS -#if !defined(__APPLE__) std::string game_title; QString qt_game_title; std::filesystem::path out_icon_path; @@ -3096,7 +3093,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR, qt_game_title); -#endif } void GMainWindow::OnGameListOpenDirectory(const QString& directory) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 7a1a97f33..d203e5301 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -441,8 +441,6 @@ private: bool ConfirmShutdownGame(); QString GetTasStateDescription() const; -// TODO: Implement shortcut creation for macOS -#if !defined(__APPLE__) bool CreateShortcutMessagesGUI(QWidget* parent, int imsg, const QString& game_title); bool MakeShortcutIcoPath(const u64 program_id, const std::string_view game_file_name, std::filesystem::path& out_icon_path); @@ -451,7 +449,6 @@ private: const std::filesystem::path& command, const std::string& arguments, const std::string& categories, const std::string& keywords, const std::string& name); -#endif /** * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog * The only difference is that it returns a boolean. -- cgit v1.2.3 From 6a7123826a426ed195257da24d75170bfc56f670 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 25 Oct 2023 21:26:56 -0400 Subject: qt: remove duplicate exit confirmation setting --- src/yuzu/configuration/shared_translation.cpp | 1 - src/yuzu/main.cpp | 12 +++++++++--- src/yuzu/uisettings.h | 4 ---- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 3fe448f27..1434b1a56 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -156,7 +156,6 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { // Ui General INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); - INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0df163029..2b430c40a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2174,6 +2174,7 @@ void GMainWindow::ShutdownGame() { return; } + play_time_manager->Stop(); OnShutdownBegin(); OnEmulationStopTimeExpired(); OnEmulationStopped(); @@ -3484,7 +3485,7 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) { } void GMainWindow::OnExit() { - OnStopGame(); + ShutdownGame(); } void GMainWindow::OnSaveConfig() { @@ -4847,7 +4848,12 @@ bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installe } bool GMainWindow::ConfirmClose() { - if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { + if (emu_thread == nullptr || + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Never) { + return true; + } + if (!system->GetExitLocked() && + UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Based_On_Game) { return true; } const auto text = tr("Are you sure you want to close yuzu?"); @@ -4952,7 +4958,7 @@ bool GMainWindow::ConfirmChangeGame() { } bool GMainWindow::ConfirmForceLockedExit() { - if (emu_thread == nullptr || !UISettings::values.confirm_before_closing) { + if (emu_thread == nullptr) { return true; } const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index b62ff620c..77d992c54 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -93,10 +93,6 @@ struct Values { Setting show_filter_bar{linkage, true, "showFilterBar", Category::Ui}; Setting show_status_bar{linkage, true, "showStatusBar", Category::Ui}; - Setting confirm_before_closing{ - linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, - true, true}; - SwitchableSetting confirm_before_stopping{linkage, ConfirmStop::Ask_Always, "confirmStop", -- cgit v1.2.3 From b0c6bf497a1eabec14c116b710dcc757e77455bf Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 31 Oct 2023 20:11:14 -0400 Subject: romfs: fix extraction of single-directory root --- src/core/file_sys/romfs.cpp | 44 ++++++++-------------- src/core/file_sys/romfs.h | 9 +---- .../hle/service/am/applets/applet_web_browser.cpp | 3 +- src/yuzu/main.cpp | 2 +- 4 files changed, 18 insertions(+), 40 deletions(-) (limited to 'src/yuzu') diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index 1c580de57..1eb1f439a 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -35,13 +35,14 @@ struct RomFSHeader { static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); struct DirectoryEntry { + u32_le parent; u32_le sibling; u32_le child_dir; u32_le child_file; u32_le hash; u32_le name_length; }; -static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size."); +static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size."); struct FileEntry { u32_le parent; @@ -64,25 +65,22 @@ std::pair GetEntry(const VirtualFile& file, std::size_t offs return {entry, string}; } -void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, - u32 this_file_offset, std::shared_ptr parent) { - while (true) { +void ProcessFile(const VirtualFile& file, std::size_t file_offset, std::size_t data_offset, + u32 this_file_offset, std::shared_ptr& parent) { + while (this_file_offset != ROMFS_ENTRY_EMPTY) { auto entry = GetEntry(file, file_offset + this_file_offset); parent->AddFile(std::make_shared( file, entry.first.size, entry.first.offset + data_offset, entry.second)); - if (entry.first.sibling == ROMFS_ENTRY_EMPTY) - break; - this_file_offset = entry.first.sibling; } } -void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, +void ProcessDirectory(const VirtualFile& file, std::size_t dir_offset, std::size_t file_offset, std::size_t data_offset, u32 this_dir_offset, - std::shared_ptr parent) { - while (true) { + std::shared_ptr& parent) { + while (this_dir_offset != ROMFS_ENTRY_EMPTY) { auto entry = GetEntry(file, dir_offset + this_dir_offset); auto current = std::make_shared( std::vector{}, std::vector{}, entry.second); @@ -97,14 +95,12 @@ void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file } parent->AddDirectory(current); - if (entry.first.sibling == ROMFS_ENTRY_EMPTY) - break; this_dir_offset = entry.first.sibling; } } } // Anonymous namespace -VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { +VirtualDir ExtractRomFS(VirtualFile file) { RomFSHeader header{}; if (file->ReadObject(&header) != sizeof(RomFSHeader)) return nullptr; @@ -113,27 +109,17 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { return nullptr; const u64 file_offset = header.file_meta.offset; - const u64 dir_offset = header.directory_meta.offset + 4; - - auto root = - std::make_shared(std::vector{}, std::vector{}, - file->GetName(), file->GetContainingDirectory()); - - ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root); + const u64 dir_offset = header.directory_meta.offset; - VirtualDir out = std::move(root); + auto root_container = std::make_shared(); - if (type == RomFSExtractionType::SingleDiscard) - return out->GetSubdirectories().front(); + ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root_container); - while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { - if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" && - type == RomFSExtractionType::Truncated) - break; - out = out->GetSubdirectories().front(); + if (auto root = root_container->GetSubdirectory(""); root) { + return std::make_shared(std::move(root)); } - return std::make_shared(std::move(out)); + return nullptr; } VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) { diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 5d7f0c2a8..b75ff1aad 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h @@ -7,16 +7,9 @@ namespace FileSys { -enum class RomFSExtractionType { - Full, // Includes data directory - Truncated, // Traverses into data directory - SingleDiscard, // Traverses into the first subdirectory of root -}; - // Converts a RomFS binary blob to VFS Filesystem // Returns nullptr on failure -VirtualDir ExtractRomFS(VirtualFile file, - RomFSExtractionType type = RomFSExtractionType::Truncated); +VirtualDir ExtractRomFS(VirtualFile file); // Converts a VFS filesystem into a RomFS binary // Returns nullptr on failure diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp index 1c9a1dc29..b0ea2b381 100644 --- a/src/core/hle/service/am/applets/applet_web_browser.cpp +++ b/src/core/hle/service/am/applets/applet_web_browser.cpp @@ -330,8 +330,7 @@ void WebBrowser::ExtractOfflineRomFS() { LOG_DEBUG(Service_AM, "Extracting RomFS to {}", Common::FS::PathToUTF8String(offline_cache_dir)); - const auto extracted_romfs_dir = - FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); + const auto extracted_romfs_dir = FileSys::ExtractRomFS(offline_romfs); const auto temp_dir = system.GetFilesystem()->CreateDirectory( Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 0df163029..db9da6dc8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2737,7 +2737,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa return; } - const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); + const auto extracted = FileSys::ExtractRomFS(romfs); if (extracted == nullptr) { failed(); return; -- cgit v1.2.3 From 90aa937593e53a5d5e070fb623b228578b0b225f Mon Sep 17 00:00:00 2001 From: Kelebek1 Date: Sat, 4 Nov 2023 18:25:40 +0000 Subject: Convert files to LF eol --- .../adsp/apps/opus/opus_decode_object.cpp | 214 ++++----- .../apps/opus/opus_multistream_decode_object.cpp | 222 +++++----- src/audio_core/opus/decoder.cpp | 358 +++++++-------- src/audio_core/opus/decoder.h | 106 ++--- src/audio_core/opus/decoder_manager.cpp | 204 ++++----- src/audio_core/opus/decoder_manager.h | 76 ++-- src/audio_core/opus/hardware_opus.cpp | 482 ++++++++++----------- src/audio_core/opus/hardware_opus.h | 90 ++-- src/yuzu/configuration/config.h | 2 +- src/yuzu/configuration/configure_camera.h | 2 +- src/yuzu/configuration/configure_input.h | 2 +- src/yuzu/configuration/configure_input_player.h | 2 +- src/yuzu/configuration/configure_per_game.h | 2 +- src/yuzu/configuration/configure_ringcon.h | 2 +- src/yuzu/configuration/configure_tas.h | 2 +- .../configuration/configure_touchscreen_advanced.h | 2 +- 16 files changed, 884 insertions(+), 884 deletions(-) (limited to 'src/yuzu') diff --git a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp index 2c16d3769..e2b9eb566 100644 --- a/src/audio_core/adsp/apps/opus/opus_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_decode_object.cpp @@ -1,107 +1,107 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "audio_core/adsp/apps/opus/opus_decode_object.h" -#include "common/assert.h" - -namespace AudioCore::ADSP::OpusDecoder { -namespace { -bool IsValidChannelCount(u32 channel_count) { - return channel_count == 1 || channel_count == 2; -} -} // namespace - -u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { - if (!IsValidChannelCount(channel_count)) { - return 0; - } - return static_cast(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); -} - -OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { - auto* new_decoder = reinterpret_cast(buffer); - auto* comparison = reinterpret_cast(buffer2); - - if (new_decoder->magic == DecodeObjectMagic) { - if (!new_decoder->initialized || - (new_decoder->initialized && new_decoder->self == comparison)) { - new_decoder->state_valid = true; - } - } else { - new_decoder->initialized = false; - new_decoder->state_valid = true; - } - return *new_decoder; -} - -s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { - if (!state_valid) { - return OPUS_INVALID_STATE; - } - - if (initialized) { - return OPUS_OK; - } - - // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include - // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer - // provided. - // We could use _create and have libopus allocate it for us, but then we have to separately - // track which decoder is being used between this and multistream in order to call the correct - // destroy from the host side. - // This is a bit cringe, but is safe as these objects are only ever initialized inside the given - // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. - decoder = (LibOpusDecoder*)(this + 1); - s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); - if (ret == OPUS_OK) { - magic = DecodeObjectMagic; - initialized = true; - state_valid = true; - self = this; - final_range = 0; - } - return ret; -} - -s32 OpusDecodeObject::Shutdown() { - if (!state_valid) { - return OPUS_INVALID_STATE; - } - - if (initialized) { - magic = 0x0; - initialized = false; - state_valid = false; - self = nullptr; - final_range = 0; - decoder = nullptr; - } - return OPUS_OK; -} - -s32 OpusDecodeObject::ResetDecoder() { - return opus_decoder_ctl(decoder, OPUS_RESET_STATE); -} - -s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, - u64 input_data, u64 input_data_size) { - ASSERT(initialized); - out_sample_count = 0; - - if (!state_valid) { - return OPUS_INVALID_STATE; - } - - auto ret_code_or_samples = opus_decode( - decoder, reinterpret_cast(input_data), static_cast(input_data_size), - reinterpret_cast(output_data), static_cast(output_data_size), 0); - - if (ret_code_or_samples < OPUS_OK) { - return ret_code_or_samples; - } - - out_sample_count = ret_code_or_samples; - return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); -} - -} // namespace AudioCore::ADSP::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_decode_object.h" +#include "common/assert.h" + +namespace AudioCore::ADSP::OpusDecoder { +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} +} // namespace + +u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { + if (!IsValidChannelCount(channel_count)) { + return 0; + } + return static_cast(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count); +} + +OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { + auto* new_decoder = reinterpret_cast(buffer); + auto* comparison = reinterpret_cast(buffer2); + + if (new_decoder->magic == DecodeObjectMagic) { + if (!new_decoder->initialized || + (new_decoder->initialized && new_decoder->self == comparison)) { + new_decoder->state_valid = true; + } + } else { + new_decoder->initialized = false; + new_decoder->state_valid = true; + } + return *new_decoder; +} + +s32 OpusDecodeObject::InitializeDecoder(u32 sample_rate, u32 channel_count) { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + return OPUS_OK; + } + + // Unfortunately libopus does not expose the OpusDecoder struct publicly, so we can't include + // it in this class. Nintendo does not allocate memory, which is why we have a workbuffer + // provided. + // We could use _create and have libopus allocate it for us, but then we have to separately + // track which decoder is being used between this and multistream in order to call the correct + // destroy from the host side. + // This is a bit cringe, but is safe as these objects are only ever initialized inside the given + // workbuffer, and GetWorkBufferSize will guarantee there's enough space to follow. + decoder = (LibOpusDecoder*)(this + 1); + s32 ret = opus_decoder_init(decoder, sample_rate, channel_count); + if (ret == OPUS_OK) { + magic = DecodeObjectMagic; + initialized = true; + state_valid = true; + self = this; + final_range = 0; + } + return ret; +} + +s32 OpusDecodeObject::Shutdown() { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + magic = 0x0; + initialized = false; + state_valid = false; + self = nullptr; + final_range = 0; + decoder = nullptr; + } + return OPUS_OK; +} + +s32 OpusDecodeObject::ResetDecoder() { + return opus_decoder_ctl(decoder, OPUS_RESET_STATE); +} + +s32 OpusDecodeObject::Decode(u32& out_sample_count, u64 output_data, u64 output_data_size, + u64 input_data, u64 input_data_size) { + ASSERT(initialized); + out_sample_count = 0; + + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + auto ret_code_or_samples = opus_decode( + decoder, reinterpret_cast(input_data), static_cast(input_data_size), + reinterpret_cast(output_data), static_cast(output_data_size), 0); + + if (ret_code_or_samples < OPUS_OK) { + return ret_code_or_samples; + } + + out_sample_count = ret_code_or_samples; + return opus_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); +} + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp index f6d362e68..7f1ed0450 100644 --- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp @@ -1,111 +1,111 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" -#include "common/assert.h" - -namespace AudioCore::ADSP::OpusDecoder { - -namespace { -bool IsValidChannelCount(u32 channel_count) { - return channel_count == 1 || channel_count == 2; -} - -bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { - return total_stream_count > 0 && stereo_stream_count > 0 && - stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); -} -} // namespace - -u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, - u32 stereo_stream_count) { - if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { - return static_cast(sizeof(OpusMultiStreamDecodeObject)) + - opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); - } - return 0; -} - -OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { - auto* new_decoder = reinterpret_cast(buffer); - auto* comparison = reinterpret_cast(buffer2); - - if (new_decoder->magic == DecodeMultiStreamObjectMagic) { - if (!new_decoder->initialized || - (new_decoder->initialized && new_decoder->self == comparison)) { - new_decoder->state_valid = true; - } - } else { - new_decoder->initialized = false; - new_decoder->state_valid = true; - } - return *new_decoder; -} - -s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, - u32 channel_count, u32 stereo_stream_count, - u8* mappings) { - if (!state_valid) { - return OPUS_INVALID_STATE; - } - - if (initialized) { - return OPUS_OK; - } - - // See OpusDecodeObject::InitializeDecoder for an explanation of this - decoder = (LibOpusMSDecoder*)(this + 1); - s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, - stereo_stream_count, mappings); - if (ret == OPUS_OK) { - magic = DecodeMultiStreamObjectMagic; - initialized = true; - state_valid = true; - self = this; - final_range = 0; - } - return ret; -} - -s32 OpusMultiStreamDecodeObject::Shutdown() { - if (!state_valid) { - return OPUS_INVALID_STATE; - } - - if (initialized) { - magic = 0x0; - initialized = false; - state_valid = false; - self = nullptr; - final_range = 0; - decoder = nullptr; - } - return OPUS_OK; -} - -s32 OpusMultiStreamDecodeObject::ResetDecoder() { - return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); -} - -s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, - u64 output_data_size, u64 input_data, u64 input_data_size) { - ASSERT(initialized); - out_sample_count = 0; - - if (!state_valid) { - return OPUS_INVALID_STATE; - } - - auto ret_code_or_samples = opus_multistream_decode( - decoder, reinterpret_cast(input_data), static_cast(input_data_size), - reinterpret_cast(output_data), static_cast(output_data_size), 0); - - if (ret_code_or_samples < OPUS_OK) { - return ret_code_or_samples; - } - - out_sample_count = ret_code_or_samples; - return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); -} - -} // namespace AudioCore::ADSP::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_multistream_decode_object.h" +#include "common/assert.h" + +namespace AudioCore::ADSP::OpusDecoder { + +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} + +bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { + return total_stream_count > 0 && stereo_stream_count > 0 && + stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); +} +} // namespace + +u32 OpusMultiStreamDecodeObject::GetWorkBufferSize(u32 total_stream_count, + u32 stereo_stream_count) { + if (IsValidStreamCounts(total_stream_count, stereo_stream_count)) { + return static_cast(sizeof(OpusMultiStreamDecodeObject)) + + opus_multistream_decoder_get_size(total_stream_count, stereo_stream_count); + } + return 0; +} + +OpusMultiStreamDecodeObject& OpusMultiStreamDecodeObject::Initialize(u64 buffer, u64 buffer2) { + auto* new_decoder = reinterpret_cast(buffer); + auto* comparison = reinterpret_cast(buffer2); + + if (new_decoder->magic == DecodeMultiStreamObjectMagic) { + if (!new_decoder->initialized || + (new_decoder->initialized && new_decoder->self == comparison)) { + new_decoder->state_valid = true; + } + } else { + new_decoder->initialized = false; + new_decoder->state_valid = true; + } + return *new_decoder; +} + +s32 OpusMultiStreamDecodeObject::InitializeDecoder(u32 sample_rate, u32 total_stream_count, + u32 channel_count, u32 stereo_stream_count, + u8* mappings) { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + return OPUS_OK; + } + + // See OpusDecodeObject::InitializeDecoder for an explanation of this + decoder = (LibOpusMSDecoder*)(this + 1); + s32 ret = opus_multistream_decoder_init(decoder, sample_rate, channel_count, total_stream_count, + stereo_stream_count, mappings); + if (ret == OPUS_OK) { + magic = DecodeMultiStreamObjectMagic; + initialized = true; + state_valid = true; + self = this; + final_range = 0; + } + return ret; +} + +s32 OpusMultiStreamDecodeObject::Shutdown() { + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + if (initialized) { + magic = 0x0; + initialized = false; + state_valid = false; + self = nullptr; + final_range = 0; + decoder = nullptr; + } + return OPUS_OK; +} + +s32 OpusMultiStreamDecodeObject::ResetDecoder() { + return opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); +} + +s32 OpusMultiStreamDecodeObject::Decode(u32& out_sample_count, u64 output_data, + u64 output_data_size, u64 input_data, u64 input_data_size) { + ASSERT(initialized); + out_sample_count = 0; + + if (!state_valid) { + return OPUS_INVALID_STATE; + } + + auto ret_code_or_samples = opus_multistream_decode( + decoder, reinterpret_cast(input_data), static_cast(input_data_size), + reinterpret_cast(output_data), static_cast(output_data_size), 0); + + if (ret_code_or_samples < OPUS_OK) { + return ret_code_or_samples; + } + + out_sample_count = ret_code_or_samples; + return opus_multistream_decoder_ctl(decoder, OPUS_GET_FINAL_RANGE_REQUEST, &final_range); +} + +} // namespace AudioCore::ADSP::OpusDecoder diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp index 5b23fce14..c6fd45f47 100644 --- a/src/audio_core/opus/decoder.cpp +++ b/src/audio_core/opus/decoder.cpp @@ -1,179 +1,179 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "audio_core/opus/decoder.h" -#include "audio_core/opus/hardware_opus.h" -#include "audio_core/opus/parameters.h" -#include "common/alignment.h" -#include "common/swap.h" -#include "core/core.h" - -namespace AudioCore::OpusDecoder { -using namespace Service::Audio; -namespace { -OpusPacketHeader ReverseHeader(OpusPacketHeader header) { - OpusPacketHeader out; - out.size = Common::swap32(header.size); - out.final_range = Common::swap32(header.final_range); - return out; -} -} // namespace - -OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) - : system{system_}, hardware_opus{hardware_opus_} {} - -OpusDecoder::~OpusDecoder() { - if (decode_object_initialized) { - hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); - } -} - -Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, - u64 transfer_memory_size) { - auto frame_size{params.use_large_frame_size ? 5760 : 1920}; - shared_buffer_size = transfer_memory_size; - shared_buffer = std::make_unique(shared_buffer_size); - shared_memory_mapped = true; - - buffer_size = - Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); - - out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; - size_t in_data_size{0x600u}; - in_data = {out_data.data() - in_data_size, in_data_size}; - - ON_RESULT_FAILURE { - if (shared_memory_mapped) { - shared_memory_mapped = false; - ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); - } - }; - - R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, - shared_buffer.get(), shared_buffer_size)); - - sample_rate = params.sample_rate; - channel_count = params.channel_count; - use_large_frame_size = params.use_large_frame_size; - decode_object_initialized = true; - R_SUCCEED(); -} - -Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, - Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { - auto frame_size{params.use_large_frame_size ? 5760 : 1920}; - shared_buffer_size = transfer_memory_size; - shared_buffer = std::make_unique(shared_buffer_size); - shared_memory_mapped = true; - - buffer_size = - Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); - - out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; - size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; - in_data = {out_data.data() - in_data_size, in_data_size}; - - ON_RESULT_FAILURE { - if (shared_memory_mapped) { - shared_memory_mapped = false; - ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); - } - }; - - R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( - params.sample_rate, params.channel_count, params.total_stream_count, - params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), - shared_buffer_size)); - - sample_rate = params.sample_rate; - channel_count = params.channel_count; - total_stream_count = params.total_stream_count; - stereo_stream_count = params.stereo_stream_count; - use_large_frame_size = params.use_large_frame_size; - decode_object_initialized = true; - R_SUCCEED(); -} - -Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, - u32* out_sample_count, std::span input_data, - std::span output_data, bool reset) { - u32 out_samples; - u64 time_taken{}; - - R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); - - auto* header_p{reinterpret_cast(input_data.data())}; - OpusPacketHeader header{ReverseHeader(*header_p)}; - - R_UNLESS(in_data.size_bytes() >= header.size && - header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), - ResultBufferTooSmall); - - if (!shared_memory_mapped) { - R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); - shared_memory_mapped = true; - } - - std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); - - R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), - channel_count, in_data.data(), header.size, - shared_buffer.get(), time_taken, reset)); - - std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); - - *out_data_size = header.size + sizeof(OpusPacketHeader); - *out_sample_count = out_samples; - if (out_time_taken) { - *out_time_taken = time_taken / 1000; - } - R_SUCCEED(); -} - -Result OpusDecoder::SetContext([[maybe_unused]] std::span context) { - R_SUCCEED_IF(shared_memory_mapped); - shared_memory_mapped = true; - R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); -} - -Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, - u32* out_sample_count, - std::span input_data, - std::span output_data, bool reset) { - u32 out_samples; - u64 time_taken{}; - - R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); - - auto* header_p{reinterpret_cast(input_data.data())}; - OpusPacketHeader header{ReverseHeader(*header_p)}; - - LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", - header.size, input_data.size_bytes(), in_data.size_bytes()); - - R_UNLESS(in_data.size_bytes() >= header.size && - header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), - ResultBufferTooSmall); - - if (!shared_memory_mapped) { - R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); - shared_memory_mapped = true; - } - - std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); - - R_TRY(hardware_opus.DecodeInterleavedForMultiStream( - out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), - header.size, shared_buffer.get(), time_taken, reset)); - - std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); - - *out_data_size = header.size + sizeof(OpusPacketHeader); - *out_sample_count = out_samples; - if (out_time_taken) { - *out_time_taken = time_taken / 1000; - } - R_SUCCEED(); -} - -} // namespace AudioCore::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/opus/decoder.h" +#include "audio_core/opus/hardware_opus.h" +#include "audio_core/opus/parameters.h" +#include "common/alignment.h" +#include "common/swap.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +using namespace Service::Audio; +namespace { +OpusPacketHeader ReverseHeader(OpusPacketHeader header) { + OpusPacketHeader out; + out.size = Common::swap32(header.size); + out.final_range = Common::swap32(header.final_range); + return out; +} +} // namespace + +OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) + : system{system_}, hardware_opus{hardware_opus_} {} + +OpusDecoder::~OpusDecoder() { + if (decode_object_initialized) { + hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); + } +} + +Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size) { + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + shared_buffer_size = transfer_memory_size; + shared_buffer = std::make_unique(shared_buffer_size); + shared_memory_mapped = true; + + buffer_size = + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); + + out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; + size_t in_data_size{0x600u}; + in_data = {out_data.data() - in_data_size, in_data_size}; + + ON_RESULT_FAILURE { + if (shared_memory_mapped) { + shared_memory_mapped = false; + ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); + } + }; + + R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, + shared_buffer.get(), shared_buffer_size)); + + sample_rate = params.sample_rate; + channel_count = params.channel_count; + use_large_frame_size = params.use_large_frame_size; + decode_object_initialized = true; + R_SUCCEED(); +} + +Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, + Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + shared_buffer_size = transfer_memory_size; + shared_buffer = std::make_unique(shared_buffer_size); + shared_memory_mapped = true; + + buffer_size = + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); + + out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; + size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; + in_data = {out_data.data() - in_data_size, in_data_size}; + + ON_RESULT_FAILURE { + if (shared_memory_mapped) { + shared_memory_mapped = false; + ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); + } + }; + + R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( + params.sample_rate, params.channel_count, params.total_stream_count, + params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), + shared_buffer_size)); + + sample_rate = params.sample_rate; + channel_count = params.channel_count; + total_stream_count = params.total_stream_count; + stereo_stream_count = params.stereo_stream_count; + use_large_frame_size = params.use_large_frame_size; + decode_object_initialized = true; + R_SUCCEED(); +} + +Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, std::span input_data, + std::span output_data, bool reset) { + u32 out_samples; + u64 time_taken{}; + + R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); + + auto* header_p{reinterpret_cast(input_data.data())}; + OpusPacketHeader header{ReverseHeader(*header_p)}; + + R_UNLESS(in_data.size_bytes() >= header.size && + header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), + ResultBufferTooSmall); + + if (!shared_memory_mapped) { + R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); + shared_memory_mapped = true; + } + + std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); + + R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), + channel_count, in_data.data(), header.size, + shared_buffer.get(), time_taken, reset)); + + std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); + + *out_data_size = header.size + sizeof(OpusPacketHeader); + *out_sample_count = out_samples; + if (out_time_taken) { + *out_time_taken = time_taken / 1000; + } + R_SUCCEED(); +} + +Result OpusDecoder::SetContext([[maybe_unused]] std::span context) { + R_SUCCEED_IF(shared_memory_mapped); + shared_memory_mapped = true; + R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); +} + +Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, + std::span input_data, + std::span output_data, bool reset) { + u32 out_samples; + u64 time_taken{}; + + R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); + + auto* header_p{reinterpret_cast(input_data.data())}; + OpusPacketHeader header{ReverseHeader(*header_p)}; + + LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", + header.size, input_data.size_bytes(), in_data.size_bytes()); + + R_UNLESS(in_data.size_bytes() >= header.size && + header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), + ResultBufferTooSmall); + + if (!shared_memory_mapped) { + R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); + shared_memory_mapped = true; + } + + std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); + + R_TRY(hardware_opus.DecodeInterleavedForMultiStream( + out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), + header.size, shared_buffer.get(), time_taken, reset)); + + std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); + + *out_data_size = header.size + sizeof(OpusPacketHeader); + *out_sample_count = out_samples; + if (out_time_taken) { + *out_time_taken = time_taken / 1000; + } + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h index d08d8a4a4..fd728958a 100644 --- a/src/audio_core/opus/decoder.h +++ b/src/audio_core/opus/decoder.h @@ -1,53 +1,53 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include - -#include "audio_core/opus/parameters.h" -#include "common/common_types.h" -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/service/audio/errors.h" - -namespace Core { -class System; -} - -namespace AudioCore::OpusDecoder { -class HardwareOpus; - -class OpusDecoder { -public: - explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); - ~OpusDecoder(); - - Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, - u64 transfer_memory_size); - Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, - u64 transfer_memory_size); - Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, - std::span input_data, std::span output_data, bool reset); - Result SetContext([[maybe_unused]] std::span context); - Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, - u32* out_sample_count, std::span input_data, - std::span output_data, bool reset); - -private: - Core::System& system; - HardwareOpus& hardware_opus; - std::unique_ptr shared_buffer{}; - u64 shared_buffer_size; - std::span in_data{}; - std::span out_data{}; - u64 buffer_size{}; - s32 sample_rate{}; - s32 channel_count{}; - bool use_large_frame_size{false}; - s32 total_stream_count{}; - s32 stereo_stream_count{}; - bool shared_memory_mapped{false}; - bool decode_object_initialized{false}; -}; - -} // namespace AudioCore::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "audio_core/opus/parameters.h" +#include "common/common_types.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/service/audio/errors.h" + +namespace Core { +class System; +} + +namespace AudioCore::OpusDecoder { +class HardwareOpus; + +class OpusDecoder { +public: + explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); + ~OpusDecoder(); + + Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size); + Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, + u64 transfer_memory_size); + Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, + std::span input_data, std::span output_data, bool reset); + Result SetContext([[maybe_unused]] std::span context); + Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, + u32* out_sample_count, std::span input_data, + std::span output_data, bool reset); + +private: + Core::System& system; + HardwareOpus& hardware_opus; + std::unique_ptr shared_buffer{}; + u64 shared_buffer_size; + std::span in_data{}; + std::span out_data{}; + u64 buffer_size{}; + s32 sample_rate{}; + s32 channel_count{}; + bool use_large_frame_size{false}; + s32 total_stream_count{}; + s32 stereo_stream_count{}; + bool shared_memory_mapped{false}; + bool decode_object_initialized{false}; +}; + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp index fdeccdf50..1464880a1 100644 --- a/src/audio_core/opus/decoder_manager.cpp +++ b/src/audio_core/opus/decoder_manager.cpp @@ -1,102 +1,102 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "audio_core/adsp/apps/opus/opus_decoder.h" -#include "audio_core/opus/decoder_manager.h" -#include "common/alignment.h" -#include "core/core.h" - -namespace AudioCore::OpusDecoder { -using namespace Service::Audio; - -namespace { -bool IsValidChannelCount(u32 channel_count) { - return channel_count == 1 || channel_count == 2; -} - -bool IsValidMultiStreamChannelCount(u32 channel_count) { - return channel_count > 0 && channel_count <= OpusStreamCountMax; -} - -bool IsValidSampleRate(u32 sample_rate) { - return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || - sample_rate == 24'000 || sample_rate == 48'000; -} - -bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { - return total_stream_count > 0 && static_cast(stereo_stream_count) >= 0 && - stereo_stream_count <= total_stream_count && - total_stream_count + stereo_stream_count <= channel_count; -} - -} // namespace - -OpusDecoderManager::OpusDecoderManager(Core::System& system_) - : system{system_}, hardware_opus{system} { - for (u32 i = 0; i < MaxChannels; i++) { - required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); - } -} - -Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { - OpusParametersEx ex{ - .sample_rate = params.sample_rate, - .channel_count = params.channel_count, - .use_large_frame_size = false, - }; - R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); -} - -Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { - R_RETURN(GetWorkBufferSizeExEx(params, out_size)); -} - -Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { - R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); - R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); - - auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; - auto frame_size{params.use_large_frame_size ? 5760 : 1920}; - work_buffer_size += - Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); - out_size = work_buffer_size + 0x600; - R_SUCCEED(); -} - -Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, - u64& out_size) { - OpusMultiStreamParametersEx ex{ - .sample_rate = params.sample_rate, - .channel_count = params.channel_count, - .total_stream_count = params.total_stream_count, - .stereo_stream_count = params.stereo_stream_count, - .use_large_frame_size = false, - .mappings = {}, - }; - R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); -} - -Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, - u64& out_size) { - R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); -} - -Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, - u64& out_size) { - R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); - R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); - R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, - params.stereo_stream_count), - ResultInvalidOpusSampleRate); - - auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( - params.total_stream_count, params.stereo_stream_count)}; - auto frame_size{params.use_large_frame_size ? 5760 : 1920}; - work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); - work_buffer_size += - Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); - out_size = work_buffer_size; - R_SUCCEED(); -} - -} // namespace AudioCore::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "audio_core/adsp/apps/opus/opus_decoder.h" +#include "audio_core/opus/decoder_manager.h" +#include "common/alignment.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +using namespace Service::Audio; + +namespace { +bool IsValidChannelCount(u32 channel_count) { + return channel_count == 1 || channel_count == 2; +} + +bool IsValidMultiStreamChannelCount(u32 channel_count) { + return channel_count > 0 && channel_count <= OpusStreamCountMax; +} + +bool IsValidSampleRate(u32 sample_rate) { + return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || + sample_rate == 24'000 || sample_rate == 48'000; +} + +bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { + return total_stream_count > 0 && static_cast(stereo_stream_count) >= 0 && + stereo_stream_count <= total_stream_count && + total_stream_count + stereo_stream_count <= channel_count; +} + +} // namespace + +OpusDecoderManager::OpusDecoderManager(Core::System& system_) + : system{system_}, hardware_opus{system} { + for (u32 i = 0; i < MaxChannels; i++) { + required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); + } +} + +Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { + OpusParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .use_large_frame_size = false, + }; + R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { + R_RETURN(GetWorkBufferSizeExEx(params, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { + R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); + R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); + + auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + work_buffer_size += + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); + out_size = work_buffer_size + 0x600; + R_SUCCEED(); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, + u64& out_size) { + OpusMultiStreamParametersEx ex{ + .sample_rate = params.sample_rate, + .channel_count = params.channel_count, + .total_stream_count = params.total_stream_count, + .stereo_stream_count = params.stereo_stream_count, + .use_large_frame_size = false, + .mappings = {}, + }; + R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, + u64& out_size) { + R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); +} + +Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, + u64& out_size) { + R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); + R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); + R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, + params.stereo_stream_count), + ResultInvalidOpusSampleRate); + + auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( + params.total_stream_count, params.stereo_stream_count)}; + auto frame_size{params.use_large_frame_size ? 5760 : 1920}; + work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); + work_buffer_size += + Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); + out_size = work_buffer_size; + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h index 466e1967b..70ebc4bab 100644 --- a/src/audio_core/opus/decoder_manager.h +++ b/src/audio_core/opus/decoder_manager.h @@ -1,38 +1,38 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "audio_core/opus/hardware_opus.h" -#include "audio_core/opus/parameters.h" -#include "common/common_types.h" -#include "core/hle/service/audio/errors.h" - -namespace Core { -class System; -} - -namespace AudioCore::OpusDecoder { - -class OpusDecoderManager { -public: - OpusDecoderManager(Core::System& system); - - HardwareOpus& GetHardwareOpus() { - return hardware_opus; - } - - Result GetWorkBufferSize(OpusParameters& params, u64& out_size); - Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); - Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); - Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); - Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); - Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); - -private: - Core::System& system; - HardwareOpus hardware_opus; - std::array required_workbuffer_sizes{}; -}; - -} // namespace AudioCore::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "audio_core/opus/hardware_opus.h" +#include "audio_core/opus/parameters.h" +#include "common/common_types.h" +#include "core/hle/service/audio/errors.h" + +namespace Core { +class System; +} + +namespace AudioCore::OpusDecoder { + +class OpusDecoderManager { +public: + OpusDecoderManager(Core::System& system); + + HardwareOpus& GetHardwareOpus() { + return hardware_opus; + } + + Result GetWorkBufferSize(OpusParameters& params, u64& out_size); + Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); + Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); + Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); + Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); + Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); + +private: + Core::System& system; + HardwareOpus hardware_opus; + std::array required_workbuffer_sizes{}; +}; + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp index d6544dcb0..5ff71ab2d 100644 --- a/src/audio_core/opus/hardware_opus.cpp +++ b/src/audio_core/opus/hardware_opus.cpp @@ -1,241 +1,241 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "audio_core/audio_core.h" -#include "audio_core/opus/hardware_opus.h" -#include "core/core.h" - -namespace AudioCore::OpusDecoder { -namespace { -using namespace Service::Audio; - -static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { - s32 error{static_cast(error_code)}; - ASSERT(error <= OPUS_OK); - switch (error) { - case OPUS_ALLOC_FAIL: - R_THROW(ResultLibOpusAllocFail); - case OPUS_INVALID_STATE: - R_THROW(ResultLibOpusInvalidState); - case OPUS_UNIMPLEMENTED: - R_THROW(ResultLibOpusUnimplemented); - case OPUS_INVALID_PACKET: - R_THROW(ResultLibOpusInvalidPacket); - case OPUS_INTERNAL_ERROR: - R_THROW(ResultLibOpusInternalError); - case OPUS_BUFFER_TOO_SMALL: - R_THROW(ResultBufferTooSmall); - case OPUS_BAD_ARG: - R_THROW(ResultLibOpusBadArg); - case OPUS_OK: - R_RETURN(ResultSuccess); - } - UNREACHABLE(); -} - -} // namespace - -HardwareOpus::HardwareOpus(Core::System& system_) - : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { - opus_decoder.SetSharedMemory(shared_memory); -} - -u64 HardwareOpus::GetWorkBufferSize(u32 channel) { - if (!opus_decoder.IsRunning()) { - return 0; - } - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = channel; - opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); - return 0; - } - return shared_memory.dsp_return_data[0]; -} - -u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = total_stream_count; - shared_memory.host_send_data[1] = stereo_stream_count; - opus_decoder.Send(ADSP::Direction::DSP, - ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); - return 0; - } - return shared_memory.dsp_return_data[0]; -} - -Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, - u64 buffer_size) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = buffer_size; - shared_memory.host_send_data[2] = sample_rate; - shared_memory.host_send_data[3] = channel_count; - - opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); - R_THROW(ResultInvalidOpusDSPReturnCode); - } - - R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); -} - -Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, - u32 total_stream_count, - u32 stereo_stream_count, void* mappings, - void* buffer, u64 buffer_size) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = buffer_size; - shared_memory.host_send_data[2] = sample_rate; - shared_memory.host_send_data[3] = channel_count; - shared_memory.host_send_data[4] = total_stream_count; - shared_memory.host_send_data[5] = stereo_stream_count; - - ASSERT(channel_count <= MaxChannels); - std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); - - opus_decoder.Send(ADSP::Direction::DSP, - ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); - R_THROW(ResultInvalidOpusDSPReturnCode); - } - - R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); -} - -Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = buffer_size; - - opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, - "Expected Opus shutdown code {}, got {}", - ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); - - R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); -} - -Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = buffer_size; - - opus_decoder.Send(ADSP::Direction::DSP, - ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, - "Expected Opus shutdown code {}, got {}", - ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); - - R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); -} - -Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, - u64 output_data_size, u32 channel_count, void* input_data, - u64 input_data_size, void* buffer, u64& out_time_taken, - bool reset) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = (u64)input_data; - shared_memory.host_send_data[2] = input_data_size; - shared_memory.host_send_data[3] = (u64)output_data; - shared_memory.host_send_data[4] = output_data_size; - shared_memory.host_send_data[5] = 0; - shared_memory.host_send_data[6] = reset; - - opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); - R_THROW(ResultInvalidOpusDSPReturnCode); - } - - auto error_code{static_cast(shared_memory.dsp_return_data[0])}; - if (error_code == OPUS_OK) { - out_sample_count = static_cast(shared_memory.dsp_return_data[1]); - out_time_taken = 1000 * shared_memory.dsp_return_data[2]; - } - R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); -} - -Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, - u64 output_data_size, u32 channel_count, - void* input_data, u64 input_data_size, - void* buffer, u64& out_time_taken, - bool reset) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = (u64)input_data; - shared_memory.host_send_data[2] = input_data_size; - shared_memory.host_send_data[3] = (u64)output_data; - shared_memory.host_send_data[4] = output_data_size; - shared_memory.host_send_data[5] = 0; - shared_memory.host_send_data[6] = reset; - - opus_decoder.Send(ADSP::Direction::DSP, - ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); - R_THROW(ResultInvalidOpusDSPReturnCode); - } - - auto error_code{static_cast(shared_memory.dsp_return_data[0])}; - if (error_code == OPUS_OK) { - out_sample_count = static_cast(shared_memory.dsp_return_data[1]); - out_time_taken = 1000 * shared_memory.dsp_return_data[2]; - } - R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); -} - -Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = buffer_size; - - opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::MapMemoryOK, msg); - R_THROW(ResultInvalidOpusDSPReturnCode); - } - R_SUCCEED(); -} - -Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { - std::scoped_lock l{mutex}; - shared_memory.host_send_data[0] = (u64)buffer; - shared_memory.host_send_data[1] = buffer_size; - - opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); - auto msg = opus_decoder.Receive(ADSP::Direction::Host); - if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { - LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", - ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); - R_THROW(ResultInvalidOpusDSPReturnCode); - } - R_SUCCEED(); -} - -} // namespace AudioCore::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "audio_core/audio_core.h" +#include "audio_core/opus/hardware_opus.h" +#include "core/core.h" + +namespace AudioCore::OpusDecoder { +namespace { +using namespace Service::Audio; + +static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { + s32 error{static_cast(error_code)}; + ASSERT(error <= OPUS_OK); + switch (error) { + case OPUS_ALLOC_FAIL: + R_THROW(ResultLibOpusAllocFail); + case OPUS_INVALID_STATE: + R_THROW(ResultLibOpusInvalidState); + case OPUS_UNIMPLEMENTED: + R_THROW(ResultLibOpusUnimplemented); + case OPUS_INVALID_PACKET: + R_THROW(ResultLibOpusInvalidPacket); + case OPUS_INTERNAL_ERROR: + R_THROW(ResultLibOpusInternalError); + case OPUS_BUFFER_TOO_SMALL: + R_THROW(ResultBufferTooSmall); + case OPUS_BAD_ARG: + R_THROW(ResultLibOpusBadArg); + case OPUS_OK: + R_RETURN(ResultSuccess); + } + UNREACHABLE(); +} + +} // namespace + +HardwareOpus::HardwareOpus(Core::System& system_) + : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { + opus_decoder.SetSharedMemory(shared_memory); +} + +u64 HardwareOpus::GetWorkBufferSize(u32 channel) { + if (!opus_decoder.IsRunning()) { + return 0; + } + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = channel; + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); + return 0; + } + return shared_memory.dsp_return_data[0]; +} + +u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = total_stream_count; + shared_memory.host_send_data[1] = stereo_stream_count; + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); + return 0; + } + return shared_memory.dsp_return_data[0]; +} + +Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, + u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + shared_memory.host_send_data[2] = sample_rate; + shared_memory.host_send_data[3] = channel_count; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, + u32 total_stream_count, + u32 stereo_stream_count, void* mappings, + void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + shared_memory.host_send_data[2] = sample_rate; + shared_memory.host_send_data[3] = channel_count; + shared_memory.host_send_data[4] = total_stream_count; + shared_memory.host_send_data[5] = stereo_stream_count; + + ASSERT(channel_count <= MaxChannels); + std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, + "Expected Opus shutdown code {}, got {}", + ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, + "Expected Opus shutdown code {}, got {}", + ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); + + R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); +} + +Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, void* input_data, + u64 input_data_size, void* buffer, u64& out_time_taken, + bool reset) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = (u64)input_data; + shared_memory.host_send_data[2] = input_data_size; + shared_memory.host_send_data[3] = (u64)output_data; + shared_memory.host_send_data[4] = output_data_size; + shared_memory.host_send_data[5] = 0; + shared_memory.host_send_data[6] = reset; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + auto error_code{static_cast(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { + out_sample_count = static_cast(shared_memory.dsp_return_data[1]); + out_time_taken = 1000 * shared_memory.dsp_return_data[2]; + } + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); +} + +Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, + void* input_data, u64 input_data_size, + void* buffer, u64& out_time_taken, + bool reset) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = (u64)input_data; + shared_memory.host_send_data[2] = input_data_size; + shared_memory.host_send_data[3] = (u64)output_data; + shared_memory.host_send_data[4] = output_data_size; + shared_memory.host_send_data[5] = 0; + shared_memory.host_send_data[6] = reset; + + opus_decoder.Send(ADSP::Direction::DSP, + ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + + auto error_code{static_cast(shared_memory.dsp_return_data[0])}; + if (error_code == OPUS_OK) { + out_sample_count = static_cast(shared_memory.dsp_return_data[1]); + out_time_taken = 1000 * shared_memory.dsp_return_data[2]; + } + R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); +} + +Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::MapMemoryOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + R_SUCCEED(); +} + +Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { + std::scoped_lock l{mutex}; + shared_memory.host_send_data[0] = (u64)buffer; + shared_memory.host_send_data[1] = buffer_size; + + opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); + auto msg = opus_decoder.Receive(ADSP::Direction::Host); + if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { + LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", + ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); + R_THROW(ResultInvalidOpusDSPReturnCode); + } + R_SUCCEED(); +} + +} // namespace AudioCore::OpusDecoder diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h index 7013a6b40..b10184baa 100644 --- a/src/audio_core/opus/hardware_opus.h +++ b/src/audio_core/opus/hardware_opus.h @@ -1,45 +1,45 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "audio_core/adsp/apps/opus/opus_decoder.h" -#include "audio_core/adsp/apps/opus/shared_memory.h" -#include "audio_core/adsp/mailbox.h" -#include "core/hle/service/audio/errors.h" - -namespace AudioCore::OpusDecoder { -class HardwareOpus { -public: - HardwareOpus(Core::System& system); - - u64 GetWorkBufferSize(u32 channel); - u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); - - Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, - u64 buffer_size); - Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, - u32 totaL_stream_count, u32 stereo_stream_count, - void* mappings, void* buffer, u64 buffer_size); - Result ShutdownDecodeObject(void* buffer, u64 buffer_size); - Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); - Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, - u32 channel_count, void* input_data, u64 input_data_size, void* buffer, - u64& out_time_taken, bool reset); - Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, - u64 output_data_size, u32 channel_count, - void* input_data, u64 input_data_size, void* buffer, - u64& out_time_taken, bool reset); - Result MapMemory(void* buffer, u64 buffer_size); - Result UnmapMemory(void* buffer, u64 buffer_size); - -private: - Core::System& system; - std::mutex mutex; - ADSP::OpusDecoder::OpusDecoder& opus_decoder; - ADSP::OpusDecoder::SharedMemory shared_memory; -}; -} // namespace AudioCore::OpusDecoder +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#include "audio_core/adsp/apps/opus/opus_decoder.h" +#include "audio_core/adsp/apps/opus/shared_memory.h" +#include "audio_core/adsp/mailbox.h" +#include "core/hle/service/audio/errors.h" + +namespace AudioCore::OpusDecoder { +class HardwareOpus { +public: + HardwareOpus(Core::System& system); + + u64 GetWorkBufferSize(u32 channel); + u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); + + Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, + u64 buffer_size); + Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, + u32 totaL_stream_count, u32 stereo_stream_count, + void* mappings, void* buffer, u64 buffer_size); + Result ShutdownDecodeObject(void* buffer, u64 buffer_size); + Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); + Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, + u32 channel_count, void* input_data, u64 input_data_size, void* buffer, + u64& out_time_taken, bool reset); + Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, + u64 output_data_size, u32 channel_count, + void* input_data, u64 input_data_size, void* buffer, + u64& out_time_taken, bool reset); + Result MapMemory(void* buffer, u64 buffer_size); + Result UnmapMemory(void* buffer, u64 buffer_size); + +private: + Core::System& system; + std::mutex mutex; + ADSP::OpusDecoder::OpusDecoder& opus_decoder; + ADSP::OpusDecoder::SharedMemory shared_memory; +}; +} // namespace AudioCore::OpusDecoder diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 74ec4f771..1589ba057 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project +// SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_camera.h b/src/yuzu/configuration/configure_camera.h index 9a90512b3..3d822da7b 100644 --- a/src/yuzu/configuration/configure_camera.h +++ b/src/yuzu/configuration/configure_camera.h @@ -1,4 +1,4 @@ -// Text : Copyright 2022 yuzu Emulator Project +// Text : Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index 136cd3a0a..beb503dae 100644 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h index d3255d2b4..fda09e925 100644 --- a/src/yuzu/configuration/configure_input_player.h +++ b/src/yuzu/configuration/configure_input_player.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index 1a727f32c..cc2513001 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_ringcon.h b/src/yuzu/configuration/configure_ringcon.h index b23c27906..6fd95e2b8 100644 --- a/src/yuzu/configuration/configure_ringcon.h +++ b/src/yuzu/configuration/configure_ringcon.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_tas.h b/src/yuzu/configuration/configure_tas.h index 4a6b0ba4e..a91891906 100644 --- a/src/yuzu/configuration/configure_tas.h +++ b/src/yuzu/configuration/configure_tas.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.h b/src/yuzu/configuration/configure_touchscreen_advanced.h index 034dc0d46..b6fdffdc8 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.h +++ b/src/yuzu/configuration/configure_touchscreen_advanced.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2016 Citra Emulator Project +// SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once -- cgit v1.2.3 From f07484bc64e6c6f178f4891f1c86ebb23d14dbe7 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Sat, 4 Nov 2023 14:05:05 -0600 Subject: core: hid: Signal color updates --- src/core/hid/emulated_controller.cpp | 37 +++++++++++++++------- src/core/hid/emulated_controller.h | 3 ++ src/yuzu/configuration/configure_input.cpp | 2 +- .../configuration/configure_input_advanced.cpp | 8 +++-- src/yuzu/configuration/configure_input_advanced.h | 8 ++++- 5 files changed, 42 insertions(+), 16 deletions(-) (limited to 'src/yuzu') diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 8e2894449..b08a71446 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -96,18 +96,7 @@ void EmulatedController::ReloadFromSettings() { } controller.color_values = {}; - controller.colors_state.fullkey = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.left = { - .body = GetNpadColor(player.body_color_left), - .button = GetNpadColor(player.button_color_left), - }; - controller.colors_state.right = { - .body = GetNpadColor(player.body_color_right), - .button = GetNpadColor(player.button_color_right), - }; + ReloadColorsFromSettings(); ring_params[0] = Common::ParamPackage(Settings::values.ringcon_analogs); @@ -128,6 +117,30 @@ void EmulatedController::ReloadFromSettings() { ReloadInput(); } +void EmulatedController::ReloadColorsFromSettings() { + const auto player_index = NpadIdTypeToIndex(npad_id_type); + const auto& player = Settings::values.players.GetValue()[player_index]; + + // Avoid updating colors if overridden by physical controller + if (controller.color_values[LeftIndex].body != 0 && + controller.color_values[RightIndex].body != 0) { + return; + } + + controller.colors_state.fullkey = { + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), + }; + controller.colors_state.left = { + .body = GetNpadColor(player.body_color_left), + .button = GetNpadColor(player.button_color_left), + }; + controller.colors_state.right = { + .body = GetNpadColor(player.body_color_right), + .button = GetNpadColor(player.button_color_right), + }; +} + void EmulatedController::LoadDevices() { // TODO(german77): Use more buttons to detect the correct device const auto left_joycon = button_params[Settings::NativeButton::DRight]; diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index d4500583e..ea18c2343 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -253,6 +253,9 @@ public: /// Overrides current mapped devices with the stored configuration and reloads all input devices void ReloadFromSettings(); + /// Updates current colors with the ones stored in the configuration + void ReloadColorsFromSettings(); + /// Saves the current mapped configuration void SaveCurrentConfig(); diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 3dcad2701..02e23cce6 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -152,7 +152,7 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged, [this](bool is_handheld) { UpdateDockedState(is_handheld); }); - advanced = new ConfigureInputAdvanced(this); + advanced = new ConfigureInputAdvanced(hid_core, this); ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced)); ui->tabAdvanced->layout()->addWidget(advanced); diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 3cfd5d439..441cea3f6 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -4,11 +4,13 @@ #include #include "common/settings.h" #include "core/core.h" +#include "core/hid/emulated_controller.h" +#include "core/hid/hid_core.h" #include "ui_configure_input_advanced.h" #include "yuzu/configuration/configure_input_advanced.h" -ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent) - : QWidget(parent), ui(std::make_unique()) { +ConfigureInputAdvanced::ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent) + : QWidget(parent), ui(std::make_unique()), hid_core{hid_core_} { ui->setupUi(this); controllers_color_buttons = {{ @@ -123,6 +125,8 @@ void ConfigureInputAdvanced::ApplyConfiguration() { player.button_color_left = colors[1]; player.body_color_right = colors[2]; player.button_color_right = colors[3]; + + hid_core.GetEmulatedControllerByIndex(player_idx)->ReloadColorsFromSettings(); } Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked(); diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h index fc1230284..41f822c4a 100644 --- a/src/yuzu/configuration/configure_input_advanced.h +++ b/src/yuzu/configuration/configure_input_advanced.h @@ -14,11 +14,15 @@ namespace Ui { class ConfigureInputAdvanced; } +namespace Core::HID { +class HIDCore; +} // namespace Core::HID + class ConfigureInputAdvanced : public QWidget { Q_OBJECT public: - explicit ConfigureInputAdvanced(QWidget* parent = nullptr); + explicit ConfigureInputAdvanced(Core::HID::HIDCore& hid_core_, QWidget* parent = nullptr); ~ConfigureInputAdvanced() override; void ApplyConfiguration(); @@ -44,4 +48,6 @@ private: std::array, 8> controllers_colors; std::array, 8> controllers_color_buttons; + + Core::HID::HIDCore& hid_core; }; -- cgit v1.2.3 From 4b8b223db2ed0f79ce81083128fe8085786877cc Mon Sep 17 00:00:00 2001 From: Franco M Date: Sun, 5 Nov 2023 00:39:43 +0000 Subject: modified: src/yuzu/main.cpp --- src/yuzu/main.cpp | 73 ++++++------------------------------------------------- 1 file changed, 7 insertions(+), 66 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c8efd1917..6b6feb1e6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "core/loader/nca.h" #include "core/tools/renderdoc.h" #ifdef __APPLE__ @@ -2856,6 +2857,7 @@ bool GMainWindow::CreateShortcutLink(const std::filesystem::path& shortcut_path, LOG_ERROR(Frontend, "Failed to create shortcut"); return false; } + // TODO: Migrate fmt::print to std::print in futures STD C++ 23. fmt::print(shortcut_stream, "[Desktop Entry]\n"); fmt::print(shortcut_stream, "Type=Application\n"); fmt::print(shortcut_stream, "Version=1.0\n"); @@ -2983,7 +2985,6 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi #elif defined(__linux__) || defined(__FreeBSD__) out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; #endif - // Create icons directory if it doesn't exist if (!Common::FS::CreateDirs(out_icon_path)) { QMessageBox::critical( @@ -2996,8 +2997,8 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi } // Create icon file path - out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension) - : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); + out_icon_path /= (program_id == 0 ? std::format("yuzu-{}.{}", game_file_name, ico_extension) + : std::format("yuzu-{:016X}.{}", program_id, ico_extension)); return true; } @@ -3030,7 +3031,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga const auto control = pm.GetControlMetadata(); const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); - game_title = fmt::format("{:016X}", program_id); + game_title = std::format("{:016X}", program_id); if (control.first != nullptr) { game_title = control.first->GetApplicationName(); } else { @@ -3079,12 +3080,12 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } #endif // __linux__ // Create shortcut - std::string arguments = fmt::format("-g \"{:s}\"", game_path); + std::string arguments = std::format("-g \"{:s}\"", game_path); if (GMainWindow::CreateShortcutMessagesGUI( this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) { arguments = "-f " + arguments; } - const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title); + const std::string comment = std::format("Start {:s} with the yuzu Emulator", game_title); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; @@ -4090,66 +4091,6 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file } } -bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, - const std::string& comment, const std::string& icon_path, - const std::string& command, const std::string& arguments, - const std::string& categories, const std::string& keywords) { -#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) - // This desktop file template was writing referencing - // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html - std::string shortcut_contents{}; - shortcut_contents.append("[Desktop Entry]\n"); - shortcut_contents.append("Type=Application\n"); - shortcut_contents.append("Version=1.0\n"); - shortcut_contents.append(fmt::format("Name={:s}\n", title)); - shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); - shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); - shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); - shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); - shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); - shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); - - std::ofstream shortcut_stream(shortcut_path); - if (!shortcut_stream.is_open()) { - LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); - return false; - } - shortcut_stream << shortcut_contents; - shortcut_stream.close(); - - return true; -#elif defined(WIN32) - IShellLinkW* shell_link; - auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, - (void**)&shell_link); - if (FAILED(hres)) { - return false; - } - shell_link->SetPath( - Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to - shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data()); - shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data()); - shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0); - - IPersistFile* persist_file; - hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file); - if (FAILED(hres)) { - return false; - } - - hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE); - if (FAILED(hres)) { - return false; - } - - persist_file->Release(); - shell_link->Release(); - - return true; -#endif - return false; -} - void GMainWindow::OnLoadAmiibo() { if (emu_thread == nullptr || !emu_thread->IsRunning()) { return; -- cgit v1.2.3 From 5323d9f6b3cba683522d90fbd9f28b6e11b9348b Mon Sep 17 00:00:00 2001 From: german77 Date: Sun, 5 Nov 2023 09:00:45 -0600 Subject: service: acc: Ensure proper profile size --- src/core/hle/service/acc/acc.cpp | 56 ++++++++++++++++++---- .../configuration/configure_profile_manager.cpp | 6 +-- 2 files changed, 50 insertions(+), 12 deletions(-) (limited to 'src/yuzu') diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 1b1c8190e..f21553644 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -3,11 +3,13 @@ #include #include + #include "common/common_types.h" #include "common/fs/file.h" #include "common/fs/path_util.h" #include "common/logging/log.h" #include "common/polyfill_ranges.h" +#include "common/stb.h" #include "common/string_util.h" #include "common/swap.h" #include "core/constants.h" @@ -38,9 +40,36 @@ static std::filesystem::path GetImagePath(const Common::UUID& uuid) { fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormattedString()); } -static constexpr u32 SanitizeJPEGSize(std::size_t size) { +static void JPGToMemory(void* context, void* data, int len) { + std::vector* jpg_image = static_cast*>(context); + unsigned char* jpg = static_cast(data); + jpg_image->insert(jpg_image->end(), jpg, jpg + len); +} + +static void SanitizeJPEGImageSize(std::vector& image) { constexpr std::size_t max_jpeg_image_size = 0x20000; - return static_cast(std::min(size, max_jpeg_image_size)); + constexpr int profile_dimensions = 256; + int original_width, original_height, color_channels; + + const auto plain_image = + stbi_load_from_memory(image.data(), static_cast(image.size()), &original_width, + &original_height, &color_channels, STBI_rgb); + + // Resize image to match 256*256 + if (original_width != profile_dimensions || original_height != profile_dimensions) { + // Use vector instead of array to avoid overflowing the stack + std::vector out_image(profile_dimensions * profile_dimensions * STBI_rgb); + stbir_resize_uint8_srgb(plain_image, original_width, original_height, 0, out_image.data(), + profile_dimensions, profile_dimensions, 0, STBI_rgb, 0, + STBIR_FILTER_BOX); + image.clear(); + if (!stbi_write_jpg_to_func(JPGToMemory, &image, profile_dimensions, profile_dimensions, + STBI_rgb, out_image.data(), 0)) { + LOG_ERROR(Service_ACC, "Failed to resize the user provided image."); + } + } + + image.resize(std::min(image.size(), max_jpeg_image_size)); } class IManagerForSystemService final : public ServiceFramework { @@ -339,19 +368,20 @@ protected: LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); - rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); + rb.Push(static_cast(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); return; } - const u32 size = SanitizeJPEGSize(image.GetSize()); - std::vector buffer(size); + std::vector buffer(image.GetSize()); if (image.Read(buffer) != buffer.size()) { LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); } + SanitizeJPEGImageSize(buffer); + ctx.WriteBuffer(buffer); - rb.Push(size); + rb.Push(static_cast(buffer.size())); } void GetImageSize(HLERequestContext& ctx) { @@ -365,10 +395,18 @@ protected: if (!image.IsOpen()) { LOG_WARNING(Service_ACC, "Failed to load user provided image! Falling back to built-in backup..."); - rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); - } else { - rb.Push(SanitizeJPEGSize(image.GetSize())); + rb.Push(static_cast(Core::Constants::ACCOUNT_BACKUP_JPEG.size())); + return; } + + std::vector buffer(image.GetSize()); + + if (image.Read(buffer) != buffer.size()) { + LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); + } + + SanitizeJPEGImageSize(buffer); + rb.Push(static_cast(buffer.size())); } void Store(HLERequestContext& ctx) { diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index a47089988..6d2219bf5 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -306,10 +306,10 @@ void ConfigureProfileManager::SetUserImage() { return; } - // Some games crash when the profile image is too big. Resize any image bigger than 256x256 + // Profile image must be 256x256 QImage image(image_path); - if (image.width() > 256 || image.height() > 256) { - image = image.scaled(256, 256, Qt::KeepAspectRatio); + if (image.width() != 256 || image.height() != 256) { + image = image.scaled(256, 256, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); if (!image.save(image_path)) { QMessageBox::warning(this, tr("Error resizing user image"), tr("Unable to resize image")); -- cgit v1.2.3 From 507f360a81f6938a06643eab0df4dc24f3e89307 Mon Sep 17 00:00:00 2001 From: german77 Date: Fri, 3 Nov 2023 23:43:41 -0600 Subject: yuzu: Only store games in the recently played list --- src/core/hle/service/am/applets/applets.h | 24 ++++++++++++++++++++++++ src/yuzu/main.cpp | 11 +++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) (limited to 'src/yuzu') diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index f02bbc450..a6c322458 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h @@ -69,6 +69,30 @@ enum class AppletId : u32 { MyPage = 0x1A, }; +enum class AppletProgramId : u64 { + QLaunch = 0x0100000000001000ull, + Auth = 0x0100000000001001ull, + Cabinet = 0x0100000000001002ull, + Controller = 0x0100000000001003ull, + DataErase = 0x0100000000001004ull, + Error = 0x0100000000001005ull, + NetConnect = 0x0100000000001006ull, + ProfileSelect = 0x0100000000001007ull, + SoftwareKeyboard = 0x0100000000001008ull, + MiiEdit = 0x0100000000001009ull, + Web = 0x010000000000100Aull, + Shop = 0x010000000000100Bull, + OverlayDisplay = 0x01000000000100Cull, + PhotoViewer = 0x01000000000100Dull, + Settings = 0x010000000000100Eull, + OfflineWeb = 0x010000000000100Full, + LoginShare = 0x0100000000001010ull, + WebAuth = 0x0100000000001011ull, + Starter = 0x0100000000001012ull, + MyPage = 0x0100000000001013ull, + MaxProgramId = 0x0100000000001FFFull, +}; + enum class LibraryAppletMode : u32 { AllForeground = 0, Background = 1, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index db9da6dc8..d1a53614c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1908,7 +1908,10 @@ void GMainWindow::ConfigureFilesystemProvider(const std::string& filepath) { void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index, StartGameType type, AmLaunchType launch_type) { LOG_INFO(Frontend, "yuzu starting..."); - StoreRecentFile(filename); // Put the filename on top of the list + + if (program_id > static_cast(Service::AM::Applets::AppletProgramId::MaxProgramId)) { + StoreRecentFile(filename); // Put the filename on top of the list + } // Save configurations UpdateUISettings(); @@ -4272,7 +4275,7 @@ void GMainWindow::OnToggleStatusBar() { } void GMainWindow::OnAlbum() { - constexpr u64 AlbumId = 0x010000000000100Dull; + constexpr u64 AlbumId = static_cast(Service::AM::Applets::AppletProgramId::PhotoViewer); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4295,7 +4298,7 @@ void GMainWindow::OnAlbum() { } void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { - constexpr u64 CabinetId = 0x0100000000001002ull; + constexpr u64 CabinetId = static_cast(Service::AM::Applets::AppletProgramId::Cabinet); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), @@ -4319,7 +4322,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { } void GMainWindow::OnMiiEdit() { - constexpr u64 MiiEditId = 0x0100000000001009ull; + constexpr u64 MiiEditId = static_cast(Service::AM::Applets::AppletProgramId::MiiEdit); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { QMessageBox::warning(this, tr("No firmware available"), -- cgit v1.2.3 From 8d0d0e1c7a95858dc5eaffe0eb0484815b491d9d Mon Sep 17 00:00:00 2001 From: Franco M Date: Tue, 7 Nov 2023 02:32:19 +0000 Subject: Fixed clang --- src/yuzu/main.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6b6feb1e6..2402ea6f5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4,13 +4,14 @@ #include #include #include +#include #include #include #include #include -#include #include "core/loader/nca.h" #include "core/tools/renderdoc.h" + #ifdef __APPLE__ #include // for chdir #endif @@ -5331,4 +5332,4 @@ int main(int argc, char* argv[]) { int result = app.exec(); detached_tasks.WaitForAllTasks(); return result; -} +} \ No newline at end of file -- cgit v1.2.3 From edce713fc92881bea1f6bb049b06efb2e86d81fa Mon Sep 17 00:00:00 2001 From: Lucas Reis Date: Tue, 7 Nov 2023 22:47:02 -0400 Subject: Allocate resources for test window before getting system info --- src/yuzu/vk_device_info.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/yuzu') diff --git a/src/yuzu/vk_device_info.cpp b/src/yuzu/vk_device_info.cpp index 92f10d315..ab0d39c25 100644 --- a/src/yuzu/vk_device_info.cpp +++ b/src/yuzu/vk_device_info.cpp @@ -31,6 +31,7 @@ void PopulateRecords(std::vector& records, QWindow* window) try { // Create a test window with a Vulkan surface type for checking present modes. QWindow test_window(window); test_window.setSurfaceType(QWindow::VulkanSurface); + test_window.create(); auto wsi = QtCommon::GetWindowSystemInfo(&test_window); vk::InstanceDispatch dld; -- cgit v1.2.3 From 71cdfa6ad576c721227595cac8170fb9095f23e3 Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:25:30 -0500 Subject: shared_translation: Call tr for each string Qt can't parse tr called within a macro, so we must call it on each string. shared_translation: Remove redundant include --- src/yuzu/configuration/shared_translation.cpp | 517 +++++++++++++------------- 1 file changed, 268 insertions(+), 249 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index 1434b1a56..a7b5def32 100644 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -1,17 +1,18 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "common/time_zone.h" #include "yuzu/configuration/shared_translation.h" #include #include #include #include +#include #include #include "common/settings.h" #include "common/settings_enums.h" #include "common/settings_setting.h" +#include "common/time_zone.h" #include "yuzu/uisettings.h" namespace ConfigurationShared { @@ -21,123 +22,135 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { const auto& tr = [parent](const char* text) -> QString { return parent->tr(text); }; #define INSERT(SETTINGS, ID, NAME, TOOLTIP) \ - translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{tr((NAME)), tr((TOOLTIP))}}) + translations->insert(std::pair{SETTINGS::values.ID.Id(), std::pair{(NAME), (TOOLTIP)}}) // A setting can be ignored by giving it a blank name // Audio - INSERT(Settings, sink_id, "Output Engine:", ""); - INSERT(Settings, audio_output_device_id, "Output Device:", ""); - INSERT(Settings, audio_input_device_id, "Input Device:", ""); - INSERT(Settings, audio_muted, "Mute audio", ""); - INSERT(Settings, volume, "Volume:", ""); - INSERT(Settings, dump_audio_commands, "", ""); - INSERT(UISettings, mute_when_in_background, "Mute audio when in background", ""); + INSERT(Settings, sink_id, tr("Output Engine:"), QStringLiteral()); + INSERT(Settings, audio_output_device_id, tr("Output Device:"), QStringLiteral()); + INSERT(Settings, audio_input_device_id, tr("Input Device:"), QStringLiteral()); + INSERT(Settings, audio_muted, tr("Mute audio"), QStringLiteral()); + INSERT(Settings, volume, tr("Volume:"), QStringLiteral()); + INSERT(Settings, dump_audio_commands, QStringLiteral(), QStringLiteral()); + INSERT(UISettings, mute_when_in_background, tr("Mute audio when in background"), + QStringLiteral()); // Core - INSERT(Settings, use_multi_core, "Multicore CPU Emulation", ""); - INSERT(Settings, memory_layout_mode, "Memory Layout", ""); - INSERT(Settings, use_speed_limit, "", ""); - INSERT(Settings, speed_limit, "Limit Speed Percent", ""); + INSERT(Settings, use_multi_core, tr("Multicore CPU Emulation"), QStringLiteral()); + INSERT(Settings, memory_layout_mode, tr("Memory Layout"), QStringLiteral()); + INSERT(Settings, use_speed_limit, QStringLiteral(), QStringLiteral()); + INSERT(Settings, speed_limit, tr("Limit Speed Percent"), QStringLiteral()); // Cpu - INSERT(Settings, cpu_accuracy, "Accuracy:", ""); + INSERT(Settings, cpu_accuracy, tr("Accuracy:"), QStringLiteral()); // Cpu Debug // Cpu Unsafe - INSERT(Settings, cpuopt_unsafe_unfuse_fma, - "Unfuse FMA (improve performance on CPUs without FMA)", - "This option improves speed by reducing accuracy of fused-multiply-add instructions on " - "CPUs without native FMA support."); - INSERT(Settings, cpuopt_unsafe_reduce_fp_error, "Faster FRSQRTE and FRECPE", - "This option improves the speed of some approximate floating-point functions by using " - "less accurate native approximations."); - INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, "Faster ASIMD instructions (32 bits only)", - "This option improves the speed of 32 bits ASIMD floating-point functions by running " - "with incorrect rounding modes."); - INSERT(Settings, cpuopt_unsafe_inaccurate_nan, "Inaccurate NaN handling", - "This option improves speed by removing NaN checking. Please note this also reduces " - "accuracy of certain floating-point instructions."); INSERT( - Settings, cpuopt_unsafe_fastmem_check, "Disable address space checks", - "This option improves speed by eliminating a safety check before every memory read/write " - "in guest. Disabling it may allow a game to read/write the emulator's memory."); - INSERT(Settings, cpuopt_unsafe_ignore_global_monitor, "Ignore global monitor", - "This option improves speed by relying only on the semantics of cmpxchg to ensure " + Settings, cpuopt_unsafe_unfuse_fma, + tr("Unfuse FMA (improve performance on CPUs without FMA)"), + tr("This option improves speed by reducing accuracy of fused-multiply-add instructions on " + "CPUs without native FMA support.")); + INSERT( + Settings, cpuopt_unsafe_reduce_fp_error, tr("Faster FRSQRTE and FRECPE"), + tr("This option improves the speed of some approximate floating-point functions by using " + "less accurate native approximations.")); + INSERT(Settings, cpuopt_unsafe_ignore_standard_fpcr, + tr("Faster ASIMD instructions (32 bits only)"), + tr("This option improves the speed of 32 bits ASIMD floating-point functions by running " + "with incorrect rounding modes.")); + INSERT(Settings, cpuopt_unsafe_inaccurate_nan, tr("Inaccurate NaN handling"), + tr("This option improves speed by removing NaN checking. Please note this also reduces " + "accuracy of certain floating-point instructions.")); + INSERT(Settings, cpuopt_unsafe_fastmem_check, tr("Disable address space checks"), + tr("This option improves speed by eliminating a safety check before every memory " + "read/write " + "in guest. Disabling it may allow a game to read/write the emulator's memory.")); + INSERT( + Settings, cpuopt_unsafe_ignore_global_monitor, tr("Ignore global monitor"), + tr("This option improves speed by relying only on the semantics of cmpxchg to ensure " "safety of exclusive access instructions. Please note this may result in deadlocks and " - "other race conditions."); + "other race conditions.")); // Renderer - INSERT(Settings, renderer_backend, "API:", ""); - INSERT(Settings, vulkan_device, "Device:", ""); - INSERT(Settings, shader_backend, "Shader Backend:", ""); - INSERT(Settings, resolution_setup, "Resolution:", ""); - INSERT(Settings, scaling_filter, "Window Adapting Filter:", ""); - INSERT(Settings, fsr_sharpening_slider, "FSR Sharpness:", ""); - INSERT(Settings, anti_aliasing, "Anti-Aliasing Method:", ""); - INSERT(Settings, fullscreen_mode, "Fullscreen Mode:", ""); - INSERT(Settings, aspect_ratio, "Aspect Ratio:", ""); - INSERT(Settings, use_disk_shader_cache, "Use disk pipeline cache", ""); - INSERT(Settings, use_asynchronous_gpu_emulation, "Use asynchronous GPU emulation", ""); - INSERT(Settings, nvdec_emulation, "NVDEC emulation:", ""); - INSERT(Settings, accelerate_astc, "ASTC Decoding Method:", ""); - INSERT(Settings, astc_recompression, "ASTC Recompression Method:", ""); - INSERT(Settings, vsync_mode, "VSync Mode:", - "FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " + INSERT(Settings, renderer_backend, tr("API:"), QStringLiteral()); + INSERT(Settings, vulkan_device, tr("Device:"), QStringLiteral()); + INSERT(Settings, shader_backend, tr("Shader Backend:"), QStringLiteral()); + INSERT(Settings, resolution_setup, tr("Resolution:"), QStringLiteral()); + INSERT(Settings, scaling_filter, tr("Window Adapting Filter:"), QStringLiteral()); + INSERT(Settings, fsr_sharpening_slider, tr("FSR Sharpness:"), QStringLiteral()); + INSERT(Settings, anti_aliasing, tr("Anti-Aliasing Method:"), QStringLiteral()); + INSERT(Settings, fullscreen_mode, tr("Fullscreen Mode:"), QStringLiteral()); + INSERT(Settings, aspect_ratio, tr("Aspect Ratio:"), QStringLiteral()); + INSERT(Settings, use_disk_shader_cache, tr("Use disk pipeline cache"), QStringLiteral()); + INSERT(Settings, use_asynchronous_gpu_emulation, tr("Use asynchronous GPU emulation"), + QStringLiteral()); + INSERT(Settings, nvdec_emulation, tr("NVDEC emulation:"), QStringLiteral()); + INSERT(Settings, accelerate_astc, tr("ASTC Decoding Method:"), QStringLiteral()); + INSERT(Settings, astc_recompression, tr("ASTC Recompression Method:"), QStringLiteral()); + INSERT( + Settings, vsync_mode, tr("VSync Mode:"), + tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " "refresh rate.\nFIFO Relaxed is similar to FIFO but allows tearing as it recovers from " "a slow down.\nMailbox can have lower latency than FIFO and does not tear but may drop " "frames.\nImmediate (no synchronization) just presents whatever is available and can " - "exhibit tearing."); - INSERT(Settings, bg_red, "", ""); - INSERT(Settings, bg_green, "", ""); - INSERT(Settings, bg_blue, "", ""); + "exhibit tearing.")); + INSERT(Settings, bg_red, QStringLiteral(), QStringLiteral()); + INSERT(Settings, bg_green, QStringLiteral(), QStringLiteral()); + INSERT(Settings, bg_blue, QStringLiteral(), QStringLiteral()); // Renderer (Advanced Graphics) - INSERT(Settings, async_presentation, "Enable asynchronous presentation (Vulkan only)", ""); - INSERT(Settings, renderer_force_max_clock, "Force maximum clocks (Vulkan only)", - "Runs work in the background while waiting for graphics commands to keep the GPU from " - "lowering its clock speed."); - INSERT(Settings, max_anisotropy, "Anisotropic Filtering:", ""); - INSERT(Settings, gpu_accuracy, "Accuracy Level:", ""); - INSERT(Settings, use_asynchronous_shaders, "Use asynchronous shader building (Hack)", - "Enables asynchronous shader compilation, which may reduce shader stutter. This feature " - "is experimental."); - INSERT(Settings, use_fast_gpu_time, "Use Fast GPU Time (Hack)", - "Enables Fast GPU Time. This option will force most games to run at their highest " - "native resolution."); - INSERT(Settings, use_vulkan_driver_pipeline_cache, "Use Vulkan pipeline cache", - "Enables GPU vendor-specific pipeline cache. This option can improve shader loading " - "time significantly in cases where the Vulkan driver does not store pipeline cache " - "files internally."); - INSERT(Settings, enable_compute_pipelines, "Enable Compute Pipelines (Intel Vulkan Only)", - "Enable compute pipelines, required by some games.\nThis setting only exists for Intel " + INSERT(Settings, async_presentation, tr("Enable asynchronous presentation (Vulkan only)"), + QStringLiteral()); + INSERT( + Settings, renderer_force_max_clock, tr("Force maximum clocks (Vulkan only)"), + tr("Runs work in the background while waiting for graphics commands to keep the GPU from " + "lowering its clock speed.")); + INSERT(Settings, max_anisotropy, tr("Anisotropic Filtering:"), QStringLiteral()); + INSERT(Settings, gpu_accuracy, tr("Accuracy Level:"), QStringLiteral()); + INSERT( + Settings, use_asynchronous_shaders, tr("Use asynchronous shader building (Hack)"), + tr("Enables asynchronous shader compilation, which may reduce shader stutter. This feature " + "is experimental.")); + INSERT(Settings, use_fast_gpu_time, tr("Use Fast GPU Time (Hack)"), + tr("Enables Fast GPU Time. This option will force most games to run at their highest " + "native resolution.")); + INSERT(Settings, use_vulkan_driver_pipeline_cache, tr("Use Vulkan pipeline cache"), + tr("Enables GPU vendor-specific pipeline cache. This option can improve shader loading " + "time significantly in cases where the Vulkan driver does not store pipeline cache " + "files internally.")); + INSERT( + Settings, enable_compute_pipelines, tr("Enable Compute Pipelines (Intel Vulkan Only)"), + tr("Enable compute pipelines, required by some games.\nThis setting only exists for Intel " "proprietary drivers, and may crash if enabled.\nCompute pipelines are always enabled " - "on all other drivers."); - INSERT(Settings, use_reactive_flushing, "Enable Reactive Flushing", - "Uses reactive flushing instead of predictive flushing, allowing more accurate memory " - "syncing."); - INSERT(Settings, use_video_framerate, "Sync to framerate of video playback", - "Run the game at normal speed during video playback, even when the framerate is " - "unlocked."); - INSERT(Settings, barrier_feedback_loops, "Barrier feedback loops", - "Improves rendering of transparency effects in specific games."); + "on all other drivers.")); + INSERT( + Settings, use_reactive_flushing, tr("Enable Reactive Flushing"), + tr("Uses reactive flushing instead of predictive flushing, allowing more accurate memory " + "syncing.")); + INSERT(Settings, use_video_framerate, tr("Sync to framerate of video playback"), + tr("Run the game at normal speed during video playback, even when the framerate is " + "unlocked.")); + INSERT(Settings, barrier_feedback_loops, tr("Barrier feedback loops"), + tr("Improves rendering of transparency effects in specific games.")); // Renderer (Debug) // System - INSERT(Settings, rng_seed, "RNG Seed", ""); - INSERT(Settings, rng_seed_enabled, "", ""); - INSERT(Settings, device_name, "Device Name", ""); - INSERT(Settings, custom_rtc, "Custom RTC", ""); - INSERT(Settings, custom_rtc_enabled, "", ""); - INSERT(Settings, language_index, - "Language:", "Note: this can be overridden when region setting is auto-select"); - INSERT(Settings, region_index, "Region:", ""); - INSERT(Settings, time_zone_index, "Time Zone:", ""); - INSERT(Settings, sound_index, "Sound Output Mode:", ""); - INSERT(Settings, use_docked_mode, "Console Mode:", ""); - INSERT(Settings, current_user, "", ""); + INSERT(Settings, rng_seed, tr("RNG Seed"), QStringLiteral()); + INSERT(Settings, rng_seed_enabled, QStringLiteral(), QStringLiteral()); + INSERT(Settings, device_name, tr("Device Name"), QStringLiteral()); + INSERT(Settings, custom_rtc, tr("Custom RTC"), QStringLiteral()); + INSERT(Settings, custom_rtc_enabled, QStringLiteral(), QStringLiteral()); + INSERT(Settings, language_index, tr("Language:"), + tr("Note: this can be overridden when region setting is auto-select")); + INSERT(Settings, region_index, tr("Region:"), QStringLiteral()); + INSERT(Settings, time_zone_index, tr("Time Zone:"), QStringLiteral()); + INSERT(Settings, sound_index, tr("Sound Output Mode:"), QStringLiteral()); + INSERT(Settings, use_docked_mode, tr("Console Mode:"), QStringLiteral()); + INSERT(Settings, current_user, QStringLiteral(), QStringLiteral()); // Controls @@ -154,11 +167,14 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { // Ui // Ui General - INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); - INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); - INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); - INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); - INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); + INSERT(UISettings, select_user_on_boot, tr("Prompt for user on game boot"), QStringLiteral()); + INSERT(UISettings, pause_when_in_background, tr("Pause emulation when in background"), + QStringLiteral()); + INSERT(UISettings, confirm_before_stopping, tr("Confirm before stopping emulation"), + QStringLiteral()); + INSERT(UISettings, hide_mouse, tr("Hide mouse on inactivity"), QStringLiteral()); + INSERT(UISettings, controller_applet_disabled, tr("Disable controller applet"), + QStringLiteral()); // Ui Debugging @@ -178,140 +194,141 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) { return parent->tr(text, context); }; -#define PAIR(ENUM, VALUE, TRANSLATION) \ - { static_cast(Settings::ENUM::VALUE), tr(TRANSLATION) } -#define CTX_PAIR(ENUM, VALUE, TRANSLATION, CONTEXT) \ - { static_cast(Settings::ENUM::VALUE), tr(TRANSLATION, CONTEXT) } +#define PAIR(ENUM, VALUE, TRANSLATION) {static_cast(Settings::ENUM::VALUE), (TRANSLATION)} // Intentionally skipping VSyncMode to let the UI fill that one out translations->insert({Settings::EnumMetadata::Index(), { - PAIR(AstcDecodeMode, Cpu, "CPU"), - PAIR(AstcDecodeMode, Gpu, "GPU"), - PAIR(AstcDecodeMode, CpuAsynchronous, "CPU Asynchronous"), - }}); - translations->insert({Settings::EnumMetadata::Index(), - { - PAIR(AstcRecompression, Uncompressed, "Uncompressed (Best quality)"), - PAIR(AstcRecompression, Bc1, "BC1 (Low quality)"), - PAIR(AstcRecompression, Bc3, "BC3 (Medium quality)"), + PAIR(AstcDecodeMode, Cpu, tr("CPU")), + PAIR(AstcDecodeMode, Gpu, tr("GPU")), + PAIR(AstcDecodeMode, CpuAsynchronous, tr("CPU Asynchronous")), }}); + translations->insert( + {Settings::EnumMetadata::Index(), + { + PAIR(AstcRecompression, Uncompressed, tr("Uncompressed (Best quality)")), + PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")), + PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")), + }}); translations->insert({Settings::EnumMetadata::Index(), { #ifdef HAS_OPENGL - PAIR(RendererBackend, OpenGL, "OpenGL"), + PAIR(RendererBackend, OpenGL, tr("OpenGL")), #endif - PAIR(RendererBackend, Vulkan, "Vulkan"), - PAIR(RendererBackend, Null, "Null"), - }}); - translations->insert({Settings::EnumMetadata::Index(), - { - PAIR(ShaderBackend, Glsl, "GLSL"), - PAIR(ShaderBackend, Glasm, "GLASM (Assembly Shaders, NVIDIA Only)"), - PAIR(ShaderBackend, SpirV, "SPIR-V (Experimental, Mesa Only)"), + PAIR(RendererBackend, Vulkan, tr("Vulkan")), + PAIR(RendererBackend, Null, tr("Null")), }}); + translations->insert( + {Settings::EnumMetadata::Index(), + { + PAIR(ShaderBackend, Glsl, tr("GLSL")), + PAIR(ShaderBackend, Glasm, tr("GLASM (Assembly Shaders, NVIDIA Only)")), + PAIR(ShaderBackend, SpirV, tr("SPIR-V (Experimental, Mesa Only)")), + }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(GpuAccuracy, Normal, "Normal"), - PAIR(GpuAccuracy, High, "High"), - PAIR(GpuAccuracy, Extreme, "Extreme"), - }}); - translations->insert({Settings::EnumMetadata::Index(), - { - PAIR(CpuAccuracy, Auto, "Auto"), - PAIR(CpuAccuracy, Accurate, "Accurate"), - PAIR(CpuAccuracy, Unsafe, "Unsafe"), - PAIR(CpuAccuracy, Paranoid, "Paranoid (disables most optimizations)"), + PAIR(GpuAccuracy, Normal, tr("Normal")), + PAIR(GpuAccuracy, High, tr("High")), + PAIR(GpuAccuracy, Extreme, tr("Extreme")), }}); + translations->insert( + {Settings::EnumMetadata::Index(), + { + PAIR(CpuAccuracy, Auto, tr("Auto")), + PAIR(CpuAccuracy, Accurate, tr("Accurate")), + PAIR(CpuAccuracy, Unsafe, tr("Unsafe")), + PAIR(CpuAccuracy, Paranoid, tr("Paranoid (disables most optimizations)")), + }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(FullscreenMode, Borderless, "Borderless Windowed"), - PAIR(FullscreenMode, Exclusive, "Exclusive Fullscreen"), + PAIR(FullscreenMode, Borderless, tr("Borderless Windowed")), + PAIR(FullscreenMode, Exclusive, tr("Exclusive Fullscreen")), }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(NvdecEmulation, Off, "No Video Output"), - PAIR(NvdecEmulation, Cpu, "CPU Video Decoding"), - PAIR(NvdecEmulation, Gpu, "GPU Video Decoding (Default)"), - }}); - translations->insert({Settings::EnumMetadata::Index(), - { - PAIR(ResolutionSetup, Res1_2X, "0.5X (360p/540p) [EXPERIMENTAL]"), - PAIR(ResolutionSetup, Res3_4X, "0.75X (540p/810p) [EXPERIMENTAL]"), - PAIR(ResolutionSetup, Res1X, "1X (720p/1080p)"), - PAIR(ResolutionSetup, Res3_2X, "1.5X (1080p/1620p) [EXPERIMENTAL]"), - PAIR(ResolutionSetup, Res2X, "2X (1440p/2160p)"), - PAIR(ResolutionSetup, Res3X, "3X (2160p/3240p)"), - PAIR(ResolutionSetup, Res4X, "4X (2880p/4320p)"), - PAIR(ResolutionSetup, Res5X, "5X (3600p/5400p)"), - PAIR(ResolutionSetup, Res6X, "6X (4320p/6480p)"), - PAIR(ResolutionSetup, Res7X, "7X (5040p/7560p)"), - PAIR(ResolutionSetup, Res8X, "8X (5760p/8640p)"), + PAIR(NvdecEmulation, Off, tr("No Video Output")), + PAIR(NvdecEmulation, Cpu, tr("CPU Video Decoding")), + PAIR(NvdecEmulation, Gpu, tr("GPU Video Decoding (Default)")), }}); + translations->insert( + {Settings::EnumMetadata::Index(), + { + PAIR(ResolutionSetup, Res1_2X, tr("0.5X (360p/540p) [EXPERIMENTAL]")), + PAIR(ResolutionSetup, Res3_4X, tr("0.75X (540p/810p) [EXPERIMENTAL]")), + PAIR(ResolutionSetup, Res1X, tr("1X (720p/1080p)")), + PAIR(ResolutionSetup, Res3_2X, tr("1.5X (1080p/1620p) [EXPERIMENTAL]")), + PAIR(ResolutionSetup, Res2X, tr("2X (1440p/2160p)")), + PAIR(ResolutionSetup, Res3X, tr("3X (2160p/3240p)")), + PAIR(ResolutionSetup, Res4X, tr("4X (2880p/4320p)")), + PAIR(ResolutionSetup, Res5X, tr("5X (3600p/5400p)")), + PAIR(ResolutionSetup, Res6X, tr("6X (4320p/6480p)")), + PAIR(ResolutionSetup, Res7X, tr("7X (5040p/7560p)")), + PAIR(ResolutionSetup, Res8X, tr("8X (5760p/8640p)")), + }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(ScalingFilter, NearestNeighbor, "Nearest Neighbor"), - PAIR(ScalingFilter, Bilinear, "Bilinear"), - PAIR(ScalingFilter, Bicubic, "Bicubic"), - PAIR(ScalingFilter, Gaussian, "Gaussian"), - PAIR(ScalingFilter, ScaleForce, "ScaleForce"), - PAIR(ScalingFilter, Fsr, "AMD FidelityFX™️ Super Resolution"), + PAIR(ScalingFilter, NearestNeighbor, tr("Nearest Neighbor")), + PAIR(ScalingFilter, Bilinear, tr("Bilinear")), + PAIR(ScalingFilter, Bicubic, tr("Bicubic")), + PAIR(ScalingFilter, Gaussian, tr("Gaussian")), + PAIR(ScalingFilter, ScaleForce, tr("ScaleForce")), + PAIR(ScalingFilter, Fsr, tr("AMD FidelityFX™️ Super Resolution")), }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(AntiAliasing, None, "None"), - PAIR(AntiAliasing, Fxaa, "FXAA"), - PAIR(AntiAliasing, Smaa, "SMAA"), + PAIR(AntiAliasing, None, tr("None")), + PAIR(AntiAliasing, Fxaa, tr("FXAA")), + PAIR(AntiAliasing, Smaa, tr("SMAA")), }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(AspectRatio, R16_9, "Default (16:9)"), - PAIR(AspectRatio, R4_3, "Force 4:3"), - PAIR(AspectRatio, R21_9, "Force 21:9"), - PAIR(AspectRatio, R16_10, "Force 16:10"), - PAIR(AspectRatio, Stretch, "Stretch to Window"), + PAIR(AspectRatio, R16_9, tr("Default (16:9)")), + PAIR(AspectRatio, R4_3, tr("Force 4:3")), + PAIR(AspectRatio, R21_9, tr("Force 21:9")), + PAIR(AspectRatio, R16_10, tr("Force 16:10")), + PAIR(AspectRatio, Stretch, tr("Stretch to Window")), }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(AnisotropyMode, Automatic, "Automatic"), - PAIR(AnisotropyMode, Default, "Default"), - PAIR(AnisotropyMode, X2, "2x"), - PAIR(AnisotropyMode, X4, "4x"), - PAIR(AnisotropyMode, X8, "8x"), - PAIR(AnisotropyMode, X16, "16x"), + PAIR(AnisotropyMode, Automatic, tr("Automatic")), + PAIR(AnisotropyMode, Default, tr("Default")), + PAIR(AnisotropyMode, X2, tr("2x")), + PAIR(AnisotropyMode, X4, tr("4x")), + PAIR(AnisotropyMode, X8, tr("8x")), + PAIR(AnisotropyMode, X16, tr("16x")), }}); translations->insert( {Settings::EnumMetadata::Index(), { - PAIR(Language, Japanese, "Japanese (日本語)"), - PAIR(Language, EnglishAmerican, "American English"), - PAIR(Language, French, "French (français)"), - PAIR(Language, German, "German (Deutsch)"), - PAIR(Language, Italian, "Italian (italiano)"), - PAIR(Language, Spanish, "Spanish (español)"), - PAIR(Language, Chinese, "Chinese"), - PAIR(Language, Korean, "Korean (한국어)"), - PAIR(Language, Dutch, "Dutch (Nederlands)"), - PAIR(Language, Portuguese, "Portuguese (português)"), - PAIR(Language, Russian, "Russian (Русский)"), - PAIR(Language, Taiwanese, "Taiwanese"), - PAIR(Language, EnglishBritish, "British English"), - PAIR(Language, FrenchCanadian, "Canadian French"), - PAIR(Language, SpanishLatin, "Latin American Spanish"), - PAIR(Language, ChineseSimplified, "Simplified Chinese"), - PAIR(Language, ChineseTraditional, "Traditional Chinese (正體中文)"), - PAIR(Language, PortugueseBrazilian, "Brazilian Portuguese (português do Brasil)"), + PAIR(Language, Japanese, tr("Japanese (日本語)")), + PAIR(Language, EnglishAmerican, tr("American English")), + PAIR(Language, French, tr("French (français)")), + PAIR(Language, German, tr("German (Deutsch)")), + PAIR(Language, Italian, tr("Italian (italiano)")), + PAIR(Language, Spanish, tr("Spanish (español)")), + PAIR(Language, Chinese, tr("Chinese")), + PAIR(Language, Korean, tr("Korean (한국어)")), + PAIR(Language, Dutch, tr("Dutch (Nederlands)")), + PAIR(Language, Portuguese, tr("Portuguese (português)")), + PAIR(Language, Russian, tr("Russian (Русский)")), + PAIR(Language, Taiwanese, tr("Taiwanese")), + PAIR(Language, EnglishBritish, tr("British English")), + PAIR(Language, FrenchCanadian, tr("Canadian French")), + PAIR(Language, SpanishLatin, tr("Latin American Spanish")), + PAIR(Language, ChineseSimplified, tr("Simplified Chinese")), + PAIR(Language, ChineseTraditional, tr("Traditional Chinese (正體中文)")), + PAIR(Language, PortugueseBrazilian, tr("Brazilian Portuguese (português do Brasil)")), }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(Region, Japan, "Japan"), - PAIR(Region, Usa, "USA"), - PAIR(Region, Europe, "Europe"), - PAIR(Region, Australia, "Australia"), - PAIR(Region, China, "China"), - PAIR(Region, Korea, "Korea"), - PAIR(Region, Taiwan, "Taiwan"), + PAIR(Region, Japan, tr("Japan")), + PAIR(Region, Usa, tr("USA")), + PAIR(Region, Europe, tr("Europe")), + PAIR(Region, Australia, tr("Australia")), + PAIR(Region, China, tr("China")), + PAIR(Region, Korea, tr("Korea")), + PAIR(Region, Taiwan, tr("Taiwan")), }}); translations->insert( {Settings::EnumMetadata::Index(), @@ -323,72 +340,74 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) { {static_cast(Settings::TimeZone::Default), tr("Default (%1)", "Default time zone") .arg(QString::fromStdString(Common::TimeZone::GetDefaultTimeZone()))}, - PAIR(TimeZone, Cet, "CET"), - PAIR(TimeZone, Cst6Cdt, "CST6CDT"), - PAIR(TimeZone, Cuba, "Cuba"), - PAIR(TimeZone, Eet, "EET"), - PAIR(TimeZone, Egypt, "Egypt"), - PAIR(TimeZone, Eire, "Eire"), - PAIR(TimeZone, Est, "EST"), - PAIR(TimeZone, Est5Edt, "EST5EDT"), - PAIR(TimeZone, Gb, "GB"), - PAIR(TimeZone, GbEire, "GB-Eire"), - PAIR(TimeZone, Gmt, "GMT"), - PAIR(TimeZone, GmtPlusZero, "GMT+0"), - PAIR(TimeZone, GmtMinusZero, "GMT-0"), - PAIR(TimeZone, GmtZero, "GMT0"), - PAIR(TimeZone, Greenwich, "Greenwich"), - PAIR(TimeZone, Hongkong, "Hongkong"), - PAIR(TimeZone, Hst, "HST"), - PAIR(TimeZone, Iceland, "Iceland"), - PAIR(TimeZone, Iran, "Iran"), - PAIR(TimeZone, Israel, "Israel"), - PAIR(TimeZone, Jamaica, "Jamaica"), - PAIR(TimeZone, Japan, "Japan"), - PAIR(TimeZone, Kwajalein, "Kwajalein"), - PAIR(TimeZone, Libya, "Libya"), - PAIR(TimeZone, Met, "MET"), - PAIR(TimeZone, Mst, "MST"), - PAIR(TimeZone, Mst7Mdt, "MST7MDT"), - PAIR(TimeZone, Navajo, "Navajo"), - PAIR(TimeZone, Nz, "NZ"), - PAIR(TimeZone, NzChat, "NZ-CHAT"), - PAIR(TimeZone, Poland, "Poland"), - PAIR(TimeZone, Portugal, "Portugal"), - PAIR(TimeZone, Prc, "PRC"), - PAIR(TimeZone, Pst8Pdt, "PST8PDT"), - PAIR(TimeZone, Roc, "ROC"), - PAIR(TimeZone, Rok, "ROK"), - PAIR(TimeZone, Singapore, "Singapore"), - PAIR(TimeZone, Turkey, "Turkey"), - PAIR(TimeZone, Uct, "UCT"), - PAIR(TimeZone, Universal, "Universal"), - PAIR(TimeZone, Utc, "UTC"), - PAIR(TimeZone, WSu, "W-SU"), - PAIR(TimeZone, Wet, "WET"), - PAIR(TimeZone, Zulu, "Zulu"), + PAIR(TimeZone, Cet, tr("CET")), + PAIR(TimeZone, Cst6Cdt, tr("CST6CDT")), + PAIR(TimeZone, Cuba, tr("Cuba")), + PAIR(TimeZone, Eet, tr("EET")), + PAIR(TimeZone, Egypt, tr("Egypt")), + PAIR(TimeZone, Eire, tr("Eire")), + PAIR(TimeZone, Est, tr("EST")), + PAIR(TimeZone, Est5Edt, tr("EST5EDT")), + PAIR(TimeZone, Gb, tr("GB")), + PAIR(TimeZone, GbEire, tr("GB-Eire")), + PAIR(TimeZone, Gmt, tr("GMT")), + PAIR(TimeZone, GmtPlusZero, tr("GMT+0")), + PAIR(TimeZone, GmtMinusZero, tr("GMT-0")), + PAIR(TimeZone, GmtZero, tr("GMT0")), + PAIR(TimeZone, Greenwich, tr("Greenwich")), + PAIR(TimeZone, Hongkong, tr("Hongkong")), + PAIR(TimeZone, Hst, tr("HST")), + PAIR(TimeZone, Iceland, tr("Iceland")), + PAIR(TimeZone, Iran, tr("Iran")), + PAIR(TimeZone, Israel, tr("Israel")), + PAIR(TimeZone, Jamaica, tr("Jamaica")), + PAIR(TimeZone, Japan, tr("Japan")), + PAIR(TimeZone, Kwajalein, tr("Kwajalein")), + PAIR(TimeZone, Libya, tr("Libya")), + PAIR(TimeZone, Met, tr("MET")), + PAIR(TimeZone, Mst, tr("MST")), + PAIR(TimeZone, Mst7Mdt, tr("MST7MDT")), + PAIR(TimeZone, Navajo, tr("Navajo")), + PAIR(TimeZone, Nz, tr("NZ")), + PAIR(TimeZone, NzChat, tr("NZ-CHAT")), + PAIR(TimeZone, Poland, tr("Poland")), + PAIR(TimeZone, Portugal, tr("Portugal")), + PAIR(TimeZone, Prc, tr("PRC")), + PAIR(TimeZone, Pst8Pdt, tr("PST8PDT")), + PAIR(TimeZone, Roc, tr("ROC")), + PAIR(TimeZone, Rok, tr("ROK")), + PAIR(TimeZone, Singapore, tr("Singapore")), + PAIR(TimeZone, Turkey, tr("Turkey")), + PAIR(TimeZone, Uct, tr("UCT")), + PAIR(TimeZone, Universal, tr("Universal")), + PAIR(TimeZone, Utc, tr("UTC")), + PAIR(TimeZone, WSu, tr("W-SU")), + PAIR(TimeZone, Wet, tr("WET")), + PAIR(TimeZone, Zulu, tr("Zulu")), }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(AudioMode, Mono, "Mono"), - PAIR(AudioMode, Stereo, "Stereo"), - PAIR(AudioMode, Surround, "Surround"), + PAIR(AudioMode, Mono, tr("Mono")), + PAIR(AudioMode, Stereo, tr("Stereo")), + PAIR(AudioMode, Surround, tr("Surround")), }}); translations->insert({Settings::EnumMetadata::Index(), { - PAIR(MemoryLayout, Memory_4Gb, "4GB DRAM (Default)"), - PAIR(MemoryLayout, Memory_6Gb, "6GB DRAM (Unsafe)"), - PAIR(MemoryLayout, Memory_8Gb, "8GB DRAM (Unsafe)"), + PAIR(MemoryLayout, Memory_4Gb, tr("4GB DRAM (Default)")), + PAIR(MemoryLayout, Memory_6Gb, tr("6GB DRAM (Unsafe)")), + PAIR(MemoryLayout, Memory_8Gb, tr("8GB DRAM (Unsafe)")), + }}); + translations->insert({Settings::EnumMetadata::Index(), + { + PAIR(ConsoleMode, Docked, tr("Docked")), + PAIR(ConsoleMode, Handheld, tr("Handheld")), }}); - translations->insert( - {Settings::EnumMetadata::Index(), - {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); translations->insert( {Settings::EnumMetadata::Index(), { - PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), - PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), - PAIR(ConfirmStop, Ask_Never, "Never ask"), + PAIR(ConfirmStop, Ask_Always, tr("Always ask (Default)")), + PAIR(ConfirmStop, Ask_Based_On_Game, tr("Only if game specifies not to stop")), + PAIR(ConfirmStop, Ask_Never, tr("Never ask")), }}); #undef PAIR -- cgit v1.2.3 From cb3559539a33da15859bc5f1ded24bdb41f4bd7f Mon Sep 17 00:00:00 2001 From: lat9nq <22451773+lat9nq@users.noreply.github.com> Date: Wed, 8 Nov 2023 11:27:13 -0500 Subject: CMakeLists: Add option to call lupdate directly qt_create_translation silently fails to run at all on my system. Since there is no error, I was unable to determine a fix. This sidesteps the convenience function by setting up the rules ourselves. This is left as an option since this path likely does not work on Windows. --- src/yuzu/CMakeLists.txt | 51 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 33e1fb663..181b2817c 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -252,6 +252,7 @@ file(GLOB_RECURSE THEMES ${PROJECT_SOURCE_DIR}/dist/qt_themes/*) if (ENABLE_QT_TRANSLATION) set(YUZU_QT_LANGUAGES "${PROJECT_SOURCE_DIR}/dist/languages" CACHE PATH "Path to the translation bundle for the Qt frontend") option(GENERATE_QT_TRANSLATION "Generate en.ts as the translation source file" OFF) + option(WORKAROUND_BROKEN_LUPDATE "Run lupdate directly through CMake if Qt's convenience wrappers don't work" OFF) # Update source TS file if enabled if (GENERATE_QT_TRANSLATION) @@ -259,19 +260,51 @@ if (ENABLE_QT_TRANSLATION) # these calls to qt_create_translation also creates a rule to generate en.qm which conflicts with providing english plurals # so we have to set a OUTPUT_LOCATION so that we don't have multiple rules to generate en.qm set_source_files_properties(${YUZU_QT_LANGUAGES}/en.ts PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations") - qt_create_translation(QM_FILES - ${SRCS} - ${UIS} - ${YUZU_QT_LANGUAGES}/en.ts - OPTIONS - -source-language en_US - -target-language en_US - ) + if (WORKAROUND_BROKEN_LUPDATE) + add_custom_command(OUTPUT ${YUZU_QT_LANGUAGES}/en.ts + COMMAND lupdate + -source-language en_US + -target-language en_US + ${SRCS} + ${UIS} + -ts ${YUZU_QT_LANGUAGES}/en.ts + DEPENDS + ${SRCS} + ${UIS} + WORKING_DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR} + ) + else() + qt_create_translation(QM_FILES + ${SRCS} + ${UIS} + ${YUZU_QT_LANGUAGES}/en.ts + OPTIONS + -source-language en_US + -target-language en_US + ) + endif() # Generate plurals into dist/english_plurals/generated_en.ts so it can be used to revise dist/english_plurals/en.ts set(GENERATED_PLURALS_FILE ${PROJECT_SOURCE_DIR}/dist/english_plurals/generated_en.ts) set_source_files_properties(${GENERATED_PLURALS_FILE} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/plurals") - qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) + if (WORKAROUND_BROKEN_LUPDATE) + add_custom_command(OUTPUT ${GENERATED_PLURALS_FILE} + COMMAND lupdate + -source-language en_US + -target-language en_US + ${SRCS} + ${UIS} + -ts ${GENERATED_PLURALS_FILE} + DEPENDS + ${SRCS} + ${UIS} + WORKING_DIRECTORY + ${CMAKE_CURRENT_SOURCE_DIR} + ) + else() + qt_create_translation(QM_FILES ${SRCS} ${UIS} ${GENERATED_PLURALS_FILE} OPTIONS -pluralonly -source-language en_US -target-language en_US) + endif() add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts ${GENERATED_PLURALS_FILE}) endif() -- cgit v1.2.3 From c7b31d24b979c36e2a9e59e7ae6876ffbc82a81e Mon Sep 17 00:00:00 2001 From: Franco M Date: Wed, 8 Nov 2023 21:04:30 +0000 Subject: Final change, i think --- src/yuzu/main.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 2402ea6f5..442408280 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -2998,8 +2997,8 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi } // Create icon file path - out_icon_path /= (program_id == 0 ? std::format("yuzu-{}.{}", game_file_name, ico_extension) - : std::format("yuzu-{:016X}.{}", program_id, ico_extension)); + out_icon_path /= (program_id == 0 ? fmt::format("yuzu-{}.{}", game_file_name, ico_extension) + : fmt::format("yuzu-{:016X}.{}", program_id, ico_extension)); return true; } @@ -3032,7 +3031,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga const auto control = pm.GetControlMetadata(); const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); - game_title = std::format("{:016X}", program_id); + game_title = fmt::format("{:016X}", program_id); if (control.first != nullptr) { game_title = control.first->GetApplicationName(); } else { @@ -3081,12 +3080,12 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga } #endif // __linux__ // Create shortcut - std::string arguments = std::format("-g \"{:s}\"", game_path); + std::string arguments = fmt::format("-g \"{:s}\"", game_path); if (GMainWindow::CreateShortcutMessagesGUI( this, GMainWindow::CREATE_SHORTCUT_MSGBOX_FULLSCREEN_YES, qt_game_title)) { arguments = "-f " + arguments; } - const std::string comment = std::format("Start {:s} with the yuzu Emulator", game_title); + const std::string comment = fmt::format("Start {:s} with the yuzu Emulator", game_title); const std::string categories = "Game;Emulator;Qt;"; const std::string keywords = "Switch;Nintendo;"; -- cgit v1.2.3 From f3053920bf75858b702b13a2d80589a386c397b9 Mon Sep 17 00:00:00 2001 From: Franco M Date: Thu, 9 Nov 2023 03:37:06 +0000 Subject: Minor changes --- src/yuzu/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 442408280..a4ad9d8a8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2980,7 +2980,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi // Get path to Yuzu icons directory & icon extension std::string ico_extension = "png"; #if defined(_WIN32) - out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "icons"; + out_icon_path = Common::FS::YuzuPath::IconsDir; ico_extension = "ico"; #elif defined(__linux__) || defined(__FreeBSD__) out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; @@ -5331,4 +5331,4 @@ int main(int argc, char* argv[]) { int result = app.exec(); detached_tasks.WaitForAllTasks(); return result; -} \ No newline at end of file +} -- cgit v1.2.3 From c9038af29e05458bd2f647158cc2903132c4c560 Mon Sep 17 00:00:00 2001 From: Franco M Date: Thu, 9 Nov 2023 04:53:10 +0000 Subject: Fix out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir); --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a4ad9d8a8..e0a656a5e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2980,7 +2980,7 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi // Get path to Yuzu icons directory & icon extension std::string ico_extension = "png"; #if defined(_WIN32) - out_icon_path = Common::FS::YuzuPath::IconsDir; + out_icon_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::IconsDir); ico_extension = "ico"; #elif defined(__linux__) || defined(__FreeBSD__) out_icon_path = Common::FS::GetDataDirectory("XDG_DATA_HOME") / "icons/hicolor/256x256"; -- cgit v1.2.3 From 9169cbf72835bf12434c9f9f1fea11f03d40437f Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Thu, 9 Nov 2023 20:11:13 -0600 Subject: yuzu: Save mute when in background setting --- src/common/settings.cpp | 2 ++ src/common/settings_common.h | 1 + src/yuzu/configuration/config.cpp | 2 ++ src/yuzu/configuration/configure_audio.cpp | 10 +++++++--- src/yuzu/uisettings.h | 10 +++++++--- 5 files changed, 19 insertions(+), 6 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 98b43e49c..51717be06 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -203,6 +203,8 @@ const char* TranslateCategory(Category category) { case Category::Ui: case Category::UiGeneral: return "UI"; + case Category::UiAudio: + return "UiAudio"; case Category::UiLayout: return "UiLayout"; case Category::UiGameList: diff --git a/src/common/settings_common.h b/src/common/settings_common.h index 1800ab10d..7943223eb 100644 --- a/src/common/settings_common.h +++ b/src/common/settings_common.h @@ -32,6 +32,7 @@ enum class Category : u32 { AddOns, Controls, Ui, + UiAudio, UiGeneral, UiLayout, UiGameList, diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index baa3e55f3..42bbb2964 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -360,6 +360,7 @@ void Config::ReadAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); ReadCategory(Settings::Category::Audio); + ReadCategory(Settings::Category::UiAudio); qt_config->endGroup(); } @@ -900,6 +901,7 @@ void Config::SaveAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); WriteCategory(Settings::Category::Audio); + WriteCategory(Settings::Category::UiAudio); qt_config->endGroup(); } diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 81dd51ad3..9b6ef47a7 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -38,17 +38,21 @@ void ConfigureAudio::Setup(const ConfigurationShared::Builder& builder) { std::map hold; - auto push = [&](Settings::Category category) { + auto push_settings = [&](Settings::Category category) { for (auto* setting : Settings::values.linkage.by_category[category]) { settings.push_back(setting); } + }; + + auto push_ui_settings = [&](Settings::Category category) { for (auto* setting : UISettings::values.linkage.by_category[category]) { settings.push_back(setting); } }; - push(Settings::Category::Audio); - push(Settings::Category::SystemAudio); + push_settings(Settings::Category::Audio); + push_settings(Settings::Category::SystemAudio); + push_ui_settings(Settings::Category::UiAudio); for (auto* setting : settings) { auto* widget = builder.BuildWidget(setting, apply_funcs); diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 77d992c54..3485a6347 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -109,9 +109,13 @@ struct Values { Settings::Specialization::Default, true, true}; - Setting mute_when_in_background{ - linkage, false, "muteWhenInBackground", Category::Audio, Settings::Specialization::Default, - true, true}; + Setting mute_when_in_background{linkage, + false, + "muteWhenInBackground", + Category::UiAudio, + Settings::Specialization::Default, + true, + true}; Setting hide_mouse{ linkage, true, "hideInactiveMouse", Category::UiGeneral, Settings::Specialization::Default, true, true}; -- cgit v1.2.3 From 9e331f9957bbc7aec82cd8f96c67373e115bcd68 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Thu, 9 Nov 2023 20:30:00 -0600 Subject: yuzu: Make mute audio persistent --- src/common/settings.h | 2 +- src/yuzu/main.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/settings.h b/src/common/settings.h index 9317075f7..e899f1ae6 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -153,7 +153,7 @@ struct Values { true, true}; Setting audio_muted{ - linkage, false, "audio_muted", Category::Audio, Specialization::Default, false, true}; + linkage, false, "audio_muted", Category::Audio, Specialization::Default, true, true}; Setting dump_audio_commands{ linkage, false, "dump_audio_commands", Category::Audio, Specialization::Default, false}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 91aba118a..1bf173efb 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1064,12 +1064,6 @@ void GMainWindow::InitializeWidgets() { volume_slider->setObjectName(QStringLiteral("volume_slider")); volume_slider->setMaximum(200); volume_slider->setPageStep(5); - connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { - Settings::values.audio_muted = false; - const auto volume = static_cast(percentage); - Settings::values.volume.SetValue(volume); - UpdateVolumeUI(); - }); volume_popup->layout()->addWidget(volume_slider); volume_button = new VolumeButton(); @@ -1077,6 +1071,12 @@ void GMainWindow::InitializeWidgets() { volume_button->setFocusPolicy(Qt::NoFocus); volume_button->setCheckable(true); UpdateVolumeUI(); + connect(volume_slider, &QSlider::valueChanged, this, [this](int percentage) { + Settings::values.audio_muted = false; + const auto volume = static_cast(percentage); + Settings::values.volume.SetValue(volume); + UpdateVolumeUI(); + }); connect(volume_button, &QPushButton::clicked, this, [&] { UpdateVolumeUI(); volume_popup->setVisible(!volume_popup->isVisible()); -- cgit v1.2.3 From 0c032d3f2fca46569562d968b3edb37d972d0975 Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 11 Nov 2023 10:12:11 -0600 Subject: yuzu: Keep homebrew on the recently played list --- src/yuzu/main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index ce0c71021..d2a054eaa 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1909,7 +1909,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t StartGameType type, AmLaunchType launch_type) { LOG_INFO(Frontend, "yuzu starting..."); - if (program_id > static_cast(Service::AM::Applets::AppletProgramId::MaxProgramId)) { + if (program_id == 0 || + program_id > static_cast(Service::AM::Applets::AppletProgramId::MaxProgramId)) { StoreRecentFile(filename); // Put the filename on top of the list } @@ -4295,7 +4296,7 @@ void GMainWindow::OnAlbum() { const auto filename = QString::fromStdString(album_nca->GetFullPath()); UISettings::values.roms_path = QFileInfo(filename).path(); - BootGame(filename); + BootGame(filename, AlbumId); } void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { @@ -4319,7 +4320,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); UISettings::values.roms_path = QFileInfo(filename).path(); - BootGame(filename); + BootGame(filename, CabinetId); } void GMainWindow::OnMiiEdit() { @@ -4342,7 +4343,7 @@ void GMainWindow::OnMiiEdit() { const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); UISettings::values.roms_path = QFileInfo(filename).path(); - BootGame(filename); + BootGame(filename, MiiEditId); } void GMainWindow::OnCaptureScreenshot() { -- cgit v1.2.3 From ae57a99d7d2063661cc15e76e8183122d8e0bc1b Mon Sep 17 00:00:00 2001 From: german77 Date: Sat, 11 Nov 2023 20:54:06 -0600 Subject: core: hid: Split SL and SR buttons --- src/common/settings_input.cpp | 9 +- src/common/settings_input.h | 7 +- src/core/hid/emulated_controller.cpp | 28 +- src/core/hle/service/hid/controllers/npad.cpp | 6 +- src/input_common/drivers/gc_adapter.cpp | 8 +- src/input_common/drivers/joycon.cpp | 8 +- src/input_common/drivers/sdl_driver.cpp | 20 +- src/input_common/drivers/sdl_driver.h | 2 +- src/input_common/drivers/udp_client.cpp | 8 +- src/yuzu/configuration/config.cpp | 1 + src/yuzu/configuration/configure_input_player.cpp | 36 +- src/yuzu/configuration/configure_input_player.ui | 389 ++++++++++++++------- .../configure_input_player_widget.cpp | 45 ++- 13 files changed, 382 insertions(+), 185 deletions(-) (limited to 'src/yuzu') diff --git a/src/common/settings_input.cpp b/src/common/settings_input.cpp index 0a6eea3cf..a6007e7b2 100644 --- a/src/common/settings_input.cpp +++ b/src/common/settings_input.cpp @@ -6,10 +6,11 @@ namespace Settings { namespace NativeButton { const std::array mapping = {{ - "button_a", "button_b", "button_x", "button_y", "button_lstick", - "button_rstick", "button_l", "button_r", "button_zl", "button_zr", - "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", - "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot", + "button_a", "button_b", "button_x", "button_y", "button_lstick", + "button_rstick", "button_l", "button_r", "button_zl", "button_zr", + "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright", + "button_ddown", "button_slleft", "button_srleft", "button_home", "button_screenshot", + "button_slright", "button_srright", }}; } diff --git a/src/common/settings_input.h b/src/common/settings_input.h index 46f38c703..53a95ef8f 100644 --- a/src/common/settings_input.h +++ b/src/common/settings_input.h @@ -29,12 +29,15 @@ enum Values : int { DRight, DDown, - SL, - SR, + SLLeft, + SRLeft, Home, Screenshot, + SLRight, + SRRight, + NumButtons, }; diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index b08a71446..34927cddd 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -243,10 +243,12 @@ void EmulatedController::LoadTASParams() { tas_button_params[Settings::NativeButton::DUp].Set("button", 13); tas_button_params[Settings::NativeButton::DRight].Set("button", 14); tas_button_params[Settings::NativeButton::DDown].Set("button", 15); - tas_button_params[Settings::NativeButton::SL].Set("button", 16); - tas_button_params[Settings::NativeButton::SR].Set("button", 17); + tas_button_params[Settings::NativeButton::SLLeft].Set("button", 16); + tas_button_params[Settings::NativeButton::SRLeft].Set("button", 17); tas_button_params[Settings::NativeButton::Home].Set("button", 18); tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19); + tas_button_params[Settings::NativeButton::SLRight].Set("button", 20); + tas_button_params[Settings::NativeButton::SRRight].Set("button", 21); tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); @@ -296,10 +298,12 @@ void EmulatedController::LoadVirtualGamepadParams() { virtual_button_params[Settings::NativeButton::DUp].Set("button", 13); virtual_button_params[Settings::NativeButton::DRight].Set("button", 14); virtual_button_params[Settings::NativeButton::DDown].Set("button", 15); - virtual_button_params[Settings::NativeButton::SL].Set("button", 16); - virtual_button_params[Settings::NativeButton::SR].Set("button", 17); + virtual_button_params[Settings::NativeButton::SLLeft].Set("button", 16); + virtual_button_params[Settings::NativeButton::SRLeft].Set("button", 17); virtual_button_params[Settings::NativeButton::Home].Set("button", 18); virtual_button_params[Settings::NativeButton::Screenshot].Set("button", 19); + virtual_button_params[Settings::NativeButton::SLRight].Set("button", 20); + virtual_button_params[Settings::NativeButton::SRRight].Set("button", 21); virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0); virtual_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1); @@ -867,12 +871,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback controller.npad_button_state.down.Assign(current_status.value); controller.debug_pad_button_state.d_down.Assign(current_status.value); break; - case Settings::NativeButton::SL: + case Settings::NativeButton::SLLeft: controller.npad_button_state.left_sl.Assign(current_status.value); + break; + case Settings::NativeButton::SLRight: controller.npad_button_state.right_sl.Assign(current_status.value); break; - case Settings::NativeButton::SR: + case Settings::NativeButton::SRLeft: controller.npad_button_state.left_sr.Assign(current_status.value); + break; + case Settings::NativeButton::SRRight: controller.npad_button_state.right_sr.Assign(current_status.value); break; case Settings::NativeButton::Home: @@ -1890,12 +1898,16 @@ NpadButton EmulatedController::GetTurboButtonMask() const { case Settings::NativeButton::DDown: button_mask.down.Assign(1); break; - case Settings::NativeButton::SL: + case Settings::NativeButton::SLLeft: button_mask.left_sl.Assign(1); + break; + case Settings::NativeButton::SLRight: button_mask.right_sl.Assign(1); break; - case Settings::NativeButton::SR: + case Settings::NativeButton::SRLeft: button_mask.left_sr.Assign(1); + break; + case Settings::NativeButton::SRRight: button_mask.right_sr.Assign(1); break; default: diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 21695bda2..d46bf917e 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -457,12 +457,14 @@ void Controller_NPad::RequestPadStateUpdate(Core::HID::NpadIdType npad_id) { pad_entry.l_stick = stick_state.left; } - if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft) { + if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft || + controller_type == Core::HID::NpadStyleIndex::JoyconDual) { pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl); pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr); } - if (controller_type == Core::HID::NpadStyleIndex::JoyconRight) { + if (controller_type == Core::HID::NpadStyleIndex::JoyconRight || + controller_type == Core::HID::NpadStyleIndex::JoyconDual) { pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl); pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr); } diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp index 3ad34884d..1ff296af5 100644 --- a/src/input_common/drivers/gc_adapter.cpp +++ b/src/input_common/drivers/gc_adapter.cpp @@ -415,7 +415,7 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p // This list is missing ZL/ZR since those are not considered buttons. // We will add those afterwards // This list also excludes any button that can't be really mapped - static constexpr std::array, 12> + static constexpr std::array, 14> switch_to_gcadapter_button = { std::pair{Settings::NativeButton::A, PadButton::ButtonA}, {Settings::NativeButton::B, PadButton::ButtonB}, @@ -426,8 +426,10 @@ ButtonMapping GCAdapter::GetButtonMappingForDevice(const Common::ParamPackage& p {Settings::NativeButton::DUp, PadButton::ButtonUp}, {Settings::NativeButton::DRight, PadButton::ButtonRight}, {Settings::NativeButton::DDown, PadButton::ButtonDown}, - {Settings::NativeButton::SL, PadButton::TriggerL}, - {Settings::NativeButton::SR, PadButton::TriggerR}, + {Settings::NativeButton::SLLeft, PadButton::TriggerL}, + {Settings::NativeButton::SRLeft, PadButton::TriggerR}, + {Settings::NativeButton::SLRight, PadButton::TriggerL}, + {Settings::NativeButton::SRRight, PadButton::TriggerR}, {Settings::NativeButton::R, PadButton::TriggerZ}, }; if (!params.Has("port")) { diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 0aca5a3a3..72d2951f3 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -680,8 +680,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par Common::ParamPackage sr_button_params = button_params; sl_button_params.Set("button", static_cast(Joycon::PadButton::LeftSL)); sr_button_params.Set("button", static_cast(Joycon::PadButton::LeftSR)); - mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); - mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SLLeft, std::move(sl_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SRLeft, std::move(sr_button_params)); } // Map SL and SR buttons for right joycons @@ -693,8 +693,8 @@ ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& par Common::ParamPackage sr_button_params = button_params; sl_button_params.Set("button", static_cast(Joycon::PadButton::RightSL)); sr_button_params.Set("button", static_cast(Joycon::PadButton::RightSR)); - mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params)); - mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SLRight, std::move(sl_button_params)); + mapping.insert_or_assign(Settings::NativeButton::SRRight, std::move(sr_button_params)); } return mapping; diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 66e3ae9af..78f458afe 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -828,16 +828,18 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p ButtonBindings SDLDriver::GetDefaultButtonBinding( const std::shared_ptr& joystick) const { // Default SL/SR mapping for other controllers - auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; - auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + auto sll_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + auto srl_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; + auto slr_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; + auto srr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; if (joystick->IsJoyconLeft()) { - sl_button = SDL_CONTROLLER_BUTTON_PADDLE2; - sr_button = SDL_CONTROLLER_BUTTON_PADDLE4; + sll_button = SDL_CONTROLLER_BUTTON_PADDLE2; + srl_button = SDL_CONTROLLER_BUTTON_PADDLE4; } if (joystick->IsJoyconRight()) { - sl_button = SDL_CONTROLLER_BUTTON_PADDLE3; - sr_button = SDL_CONTROLLER_BUTTON_PADDLE1; + slr_button = SDL_CONTROLLER_BUTTON_PADDLE3; + srr_button = SDL_CONTROLLER_BUTTON_PADDLE1; } return { @@ -855,8 +857,10 @@ ButtonBindings SDLDriver::GetDefaultButtonBinding( {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, - {Settings::NativeButton::SL, sl_button}, - {Settings::NativeButton::SR, sr_button}, + {Settings::NativeButton::SLLeft, sll_button}, + {Settings::NativeButton::SRLeft, srl_button}, + {Settings::NativeButton::SLRight, slr_button}, + {Settings::NativeButton::SRRight, srr_button}, {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, }; diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index fcba4e3c6..08e49a0da 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -24,7 +24,7 @@ namespace InputCommon { class SDLJoystick; using ButtonBindings = - std::array, 18>; + std::array, 20>; using ZButtonBindings = std::array, 2>; diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index 77db60e92..60821b31a 100644 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -396,7 +396,7 @@ std::vector UDPClient::GetInputDevices() const { ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& params) { // This list excludes any button that can't be really mapped - static constexpr std::array, 20> + static constexpr std::array, 22> switch_to_dsu_button = { std::pair{Settings::NativeButton::A, PadButton::Circle}, {Settings::NativeButton::B, PadButton::Cross}, @@ -412,8 +412,10 @@ ButtonMapping UDPClient::GetButtonMappingForDevice(const Common::ParamPackage& p {Settings::NativeButton::R, PadButton::R1}, {Settings::NativeButton::ZL, PadButton::L2}, {Settings::NativeButton::ZR, PadButton::R2}, - {Settings::NativeButton::SL, PadButton::L2}, - {Settings::NativeButton::SR, PadButton::R2}, + {Settings::NativeButton::SLLeft, PadButton::L2}, + {Settings::NativeButton::SRLeft, PadButton::R2}, + {Settings::NativeButton::SLRight, PadButton::L2}, + {Settings::NativeButton::SRRight, PadButton::R2}, {Settings::NativeButton::LStick, PadButton::L3}, {Settings::NativeButton::RStick, PadButton::R3}, {Settings::NativeButton::Home, PadButton::Home}, diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index baa3e55f3..09cd8d180 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -35,6 +35,7 @@ const std::array Config::default_button Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, + Qt::Key_Q, Qt::Key_E, }; const std::array Config::default_motions = { diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 576f5b571..9259e2a5d 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -322,11 +322,12 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i setFocusPolicy(Qt::ClickFocus); button_map = { - ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, - ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, - ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, - ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, - ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot, + ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY, + ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR, + ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus, + ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown, + ui->buttonSLLeft, ui->buttonSRLeft, ui->buttonHome, ui->buttonScreenshot, + ui->buttonSLRight, ui->buttonSRRight, }; analog_map_buttons = {{ @@ -1181,10 +1182,13 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { // List of all the widgets that will be hidden by any of the following layouts that need // "unhidden" after the controller type changes - const std::array layout_show = { - ui->buttonShoulderButtonsSLSR, + const std::array layout_show = { + ui->buttonShoulderButtonsSLSRLeft, + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget3, + ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft, ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft, @@ -1202,16 +1206,19 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { std::vector layout_hidden; switch (layout) { case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::JoyconDual: case Core::HID::NpadStyleIndex::Handheld: layout_hidden = { - ui->buttonShoulderButtonsSLSR, + ui->buttonShoulderButtonsSLSRLeft, + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget4, }; break; case Core::HID::NpadStyleIndex::JoyconLeft: layout_hidden = { + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget3, ui->buttonShoulderButtonsRight, ui->buttonMiscButtonsPlusHome, ui->bottomRight, @@ -1219,16 +1226,17 @@ void ConfigureInputPlayer::UpdateControllerAvailableButtons() { break; case Core::HID::NpadStyleIndex::JoyconRight: layout_hidden = { - ui->horizontalSpacerShoulderButtonsWidget, - ui->buttonShoulderButtonsLeft, - ui->buttonMiscButtonsMinusScreenshot, - ui->bottomLeft, + ui->buttonShoulderButtonsSLSRLeft, ui->horizontalSpacerShoulderButtonsWidget, + ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonShoulderButtonsLeft, + ui->buttonMiscButtonsMinusScreenshot, ui->bottomLeft, }; break; case Core::HID::NpadStyleIndex::GameCube: layout_hidden = { - ui->buttonShoulderButtonsSLSR, + ui->buttonShoulderButtonsSLSRLeft, + ui->buttonShoulderButtonsSLSRRight, ui->horizontalSpacerShoulderButtonsWidget2, + ui->horizontalSpacerShoulderButtonsWidget4, ui->buttonMiscButtonsMinusGroup, ui->buttonMiscButtonsScreenshotGroup, }; diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 611a79477..5518cccd1 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -1208,6 +1208,159 @@ 3 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + SL + + + Qt::AlignCenter + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 0 + + + + + 68 + 16777215 + + + + min-width: 68px; + + + SL + + + + + + + + + + SR + + + Qt::AlignCenter + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 0 + + + + + 68 + 16777215 + + + + min-width: 68px; + + + SR + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + @@ -1830,125 +1983,125 @@ - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - SL - - - Qt::AlignCenter - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - - 68 - 0 - - - - - 68 - 16777215 - - - - min-width: 68px; - - - SL - - - - - - - - - - SR - - - Qt::AlignCenter - - - - 3 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - - 68 - 0 - - - - - 68 - 16777215 - - - - min-width: 68px; - - - SR - - - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + SL + + + Qt::AlignCenter + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 0 + + + + + 68 + 16777215 + + + + min-width: 68px; + + + SL + + + + + + + + + + SR + + + Qt::AlignCenter + + + + 3 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 68 + 0 + + + + + 68 + 16777215 + + + + min-width: 68px; + + + SR + + + + + + - - - - - + + diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp index a188eef92..550cff9a0 100644 --- a/src/yuzu/configuration/configure_input_player_widget.cpp +++ b/src/yuzu/configuration/configure_input_player_widget.cpp @@ -297,8 +297,8 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) // Sideview SL and SR buttons button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(59, 52), button_values[SR], 5, 12, Direction::Left); - DrawRoundButton(p, center + QPoint(59, -69), button_values[SL], 5, 12, Direction::Left); + DrawRoundButton(p, center + QPoint(59, 52), button_values[SRLeft], 5, 12, Direction::Left); + DrawRoundButton(p, center + QPoint(59, -69), button_values[SLLeft], 5, 12, Direction::Left); DrawLeftBody(p, center); @@ -353,8 +353,10 @@ void PlayerControlPreview::DrawLeftController(QPainter& p, const QPointF center) // SR and SL buttons p.setPen(colors.outline); button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(155, 52), button_values[SR], 5.2f, 12, Direction::None, 4); - DrawRoundButton(p, center + QPoint(155, -69), button_values[SL], 5.2f, 12, Direction::None, 4); + DrawRoundButton(p, center + QPoint(155, 52), button_values[SRLeft], 5.2f, 12, Direction::None, + 4); + DrawRoundButton(p, center + QPoint(155, -69), button_values[SLLeft], 5.2f, 12, Direction::None, + 4); // SR and SL text p.setPen(colors.transparent); @@ -428,8 +430,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center // Sideview SL and SR buttons button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(-59, 52), button_values[SL], 5, 11, Direction::Right); - DrawRoundButton(p, center + QPoint(-59, -69), button_values[SR], 5, 11, Direction::Right); + DrawRoundButton(p, center + QPoint(-59, 52), button_values[SLRight], 5, 11, + Direction::Right); + DrawRoundButton(p, center + QPoint(-59, -69), button_values[SRRight], 5, 11, + Direction::Right); DrawRightBody(p, center); @@ -484,8 +488,10 @@ void PlayerControlPreview::DrawRightController(QPainter& p, const QPointF center // SR and SL buttons p.setPen(colors.outline); button_color = colors.slider_button; - DrawRoundButton(p, center + QPoint(-155, 52), button_values[SL], 5, 12, Direction::None, 4.0f); - DrawRoundButton(p, center + QPoint(-155, -69), button_values[SR], 5, 12, Direction::None, 4.0f); + DrawRoundButton(p, center + QPoint(-155, 52), button_values[SLRight], 5, 12, Direction::None, + 4.0f); + DrawRoundButton(p, center + QPoint(-155, -69), button_values[SRRight], 5, 12, Direction::None, + 4.0f); // SR and SL text p.setPen(colors.transparent); @@ -557,6 +563,19 @@ void PlayerControlPreview::DrawDualController(QPainter& p, const QPointF center) DrawRoundButton(p, center + QPoint(-154, -72), button_values[Minus], 7, 4, Direction::Up, 1); + // Left SR and SL sideview buttons + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(-20, -62), button_values[SLLeft], 4, 11, + Direction::Left); + DrawRoundButton(p, center + QPoint(-20, 47), button_values[SRLeft], 4, 11, Direction::Left); + + // Right SR and SL sideview buttons + button_color = colors.slider_button; + DrawRoundButton(p, center + QPoint(20, 47), button_values[SLRight], 4, 11, + Direction::Right); + DrawRoundButton(p, center + QPoint(20, -62), button_values[SRRight], 4, 11, + Direction::Right); + DrawDualBody(p, center); // Right trigger top view @@ -1792,16 +1811,6 @@ void PlayerControlPreview::DrawDualBody(QPainter& p, const QPointF center) { p.setBrush(colors.right); DrawPolygon(p, qright_joycon_topview); - // Right SR and SL sideview buttons - p.setPen(colors.outline); - p.setBrush(colors.slider_button); - DrawRoundRectangle(p, center + QPoint(19, 47), 7, 22, 1); - DrawRoundRectangle(p, center + QPoint(19, -62), 7, 22, 1); - - // Left SR and SL sideview buttons - DrawRoundRectangle(p, center + QPoint(-19, 47), 7, 22, 1); - DrawRoundRectangle(p, center + QPoint(-19, -62), 7, 22, 1); - // Right Sideview body p.setBrush(colors.slider); DrawPolygon(p, qright_joycon_slider); -- cgit v1.2.3 From efc50485b80e4fde656897b7c9c32f2dbb78977b Mon Sep 17 00:00:00 2001 From: GPUCode Date: Sat, 9 Sep 2023 17:28:06 +0300 Subject: renderer_vulkan: Introduce separate cmd buffer for uploads --- src/common/settings.h | 2 + src/video_core/CMakeLists.txt | 1 + src/video_core/buffer_cache/buffer_cache.h | 97 ++++++++-------------- src/video_core/buffer_cache/buffer_cache_base.h | 4 - src/video_core/buffer_cache/usage_tracker.h | 79 ++++++++++++++++++ src/video_core/renderer_opengl/gl_buffer_cache.cpp | 7 +- src/video_core/renderer_opengl/gl_buffer_cache.h | 17 +++- src/video_core/renderer_vulkan/vk_buffer_cache.cpp | 36 +++++++- src/video_core/renderer_vulkan/vk_buffer_cache.h | 21 ++++- .../renderer_vulkan/vk_master_semaphore.cpp | 29 ++++--- .../renderer_vulkan/vk_master_semaphore.h | 14 ++-- src/video_core/renderer_vulkan/vk_scheduler.cpp | 20 +++-- src/video_core/renderer_vulkan/vk_scheduler.h | 21 +++-- src/video_core/renderer_vulkan/vk_smaa.cpp | 4 +- .../renderer_vulkan/vk_staging_buffer_pool.h | 4 + src/video_core/texture_cache/slot_vector.h | 4 + src/video_core/vulkan_common/vulkan_wrapper.h | 4 + src/yuzu/configuration/configure_debug.cpp | 3 + src/yuzu/configuration/configure_debug.ui | 86 ++++++++++--------- 19 files changed, 309 insertions(+), 144 deletions(-) create mode 100644 src/video_core/buffer_cache/usage_tracker.h (limited to 'src/yuzu') diff --git a/src/common/settings.h b/src/common/settings.h index 9317075f7..0455241b3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -358,6 +358,8 @@ struct Values { Category::RendererDebug}; // TODO: remove this once AMDVLK supports VK_EXT_depth_bias_control bool renderer_amdvlk_depth_bias_workaround{}; + Setting disable_buffer_reorder{linkage, false, "disable_buffer_reorder", + Category::RendererDebug}; // System SwitchableSetting language_index{linkage, diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index cf9266d54..336532e0b 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(video_core STATIC buffer_cache/buffer_cache.cpp buffer_cache/buffer_cache.h buffer_cache/memory_tracker_base.h + buffer_cache/usage_tracker.h buffer_cache/word_manager.h cache_types.h cdma_pusher.cpp diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 081a574e8..813b68963 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -67,6 +67,7 @@ void BufferCache

::TickFrame() { if (!channel_state) { return; } + runtime.TickFrame(slot_buffers); // Calculate hits and shots and move hit bits to the right const u32 hits = std::reduce(channel_state->uniform_cache_hits.begin(), @@ -230,7 +231,10 @@ bool BufferCache

::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am for (const IntervalType& add_interval : tmp_intervals) { common_ranges.add(add_interval); } - runtime.CopyBuffer(dest_buffer, src_buffer, copies); + const auto& copy = copies[0]; + src_buffer.MarkUsage(copy.src_offset, copy.size); + dest_buffer.MarkUsage(copy.dst_offset, copy.size); + runtime.CopyBuffer(dest_buffer, src_buffer, copies, true); if (has_new_downloads) { memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); } @@ -258,9 +262,10 @@ bool BufferCache

::DMAClear(GPUVAddr dst_address, u64 amount, u32 value) { common_ranges.subtract(subtract_interval); const BufferId buffer = FindBuffer(*cpu_dst_address, static_cast(size)); - auto& dest_buffer = slot_buffers[buffer]; + Buffer& dest_buffer = slot_buffers[buffer]; const u32 offset = dest_buffer.Offset(*cpu_dst_address); runtime.ClearBuffer(dest_buffer, offset, size, value); + dest_buffer.MarkUsage(offset, size); return true; } @@ -603,6 +608,7 @@ void BufferCache

::CommitAsyncFlushesHigh() { VAddr orig_cpu_addr = static_cast(second_copy.src_offset); const IntervalType base_interval{orig_cpu_addr, orig_cpu_addr + copy.size}; async_downloads += std::make_pair(base_interval, 1); + buffer.MarkUsage(copy.src_offset, copy.size); runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); normalized_copies.push_back(second_copy); } @@ -621,8 +627,9 @@ void BufferCache

::CommitAsyncFlushesHigh() { // Have in mind the staging buffer offset for the copy copy.dst_offset += download_staging.offset; const std::array copies{copy}; - runtime.CopyBuffer(download_staging.buffer, slot_buffers[buffer_id], copies, - false); + Buffer& buffer = slot_buffers[buffer_id]; + buffer.MarkUsage(copy.src_offset, copy.size); + runtime.CopyBuffer(download_staging.buffer, buffer, copies, false); } runtime.PostCopyBarrier(); runtime.Finish(); @@ -742,7 +749,7 @@ void BufferCache

::BindHostIndexBuffer() { {BufferCopy{.src_offset = upload_staging.offset, .dst_offset = 0, .size = size}}}; std::memcpy(upload_staging.mapped_span.data(), draw_state.inline_index_draw_indexes.data(), size); - runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true); } else { buffer.ImmediateUpload(0, draw_state.inline_index_draw_indexes); } @@ -754,6 +761,7 @@ void BufferCache

::BindHostIndexBuffer() { offset + draw_state.index_buffer.first * draw_state.index_buffer.FormatSizeInBytes(); runtime.BindIndexBuffer(buffer, new_offset, size); } else { + buffer.MarkUsage(offset, size); runtime.BindIndexBuffer(draw_state.topology, draw_state.index_buffer.format, draw_state.index_buffer.first, draw_state.index_buffer.count, buffer, offset, size); @@ -790,6 +798,7 @@ void BufferCache

::BindHostVertexBuffers() { const u32 stride = maxwell3d->regs.vertex_streams[index].stride; const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, binding.size); host_bindings.buffers.push_back(&buffer); host_bindings.offsets.push_back(offset); @@ -895,6 +904,7 @@ void BufferCache

::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32 if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { channel_state->uniform_buffer_binding_sizes[stage][binding_index] = size; } + buffer.MarkUsage(offset, size); if constexpr (NEEDS_BIND_UNIFORM_INDEX) { runtime.BindUniformBuffer(stage, binding_index, buffer, offset, size); } else { @@ -913,6 +923,7 @@ void BufferCache

::BindHostGraphicsStorageBuffers(size_t stage) { SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); const bool is_written = ((channel_state->written_storage_buffers[stage] >> index) & 1) != 0; if (is_written) { @@ -943,6 +954,7 @@ void BufferCache

::BindHostGraphicsTextureBuffers(size_t stage) { const u32 offset = buffer.Offset(binding.cpu_addr); const PixelFormat format = binding.format; + buffer.MarkUsage(offset, size); if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { if (((channel_state->image_texture_buffers[stage] >> index) & 1) != 0) { runtime.BindImageBuffer(buffer, offset, size, format); @@ -975,9 +987,10 @@ void BufferCache

::BindHostTransformFeedbackBuffers() { MarkWrittenBuffer(binding.buffer_id, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); host_bindings.buffers.push_back(&buffer); host_bindings.offsets.push_back(offset); - host_bindings.sizes.push_back(binding.size); + host_bindings.sizes.push_back(size); } if (host_bindings.buffers.size() > 0) { runtime.BindTransformFeedbackBuffers(host_bindings); @@ -1001,6 +1014,7 @@ void BufferCache

::BindHostComputeUniformBuffers() { SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); if constexpr (NEEDS_BIND_UNIFORM_INDEX) { runtime.BindComputeUniformBuffer(binding_index, buffer, offset, size); ++binding_index; @@ -1021,6 +1035,7 @@ void BufferCache

::BindHostComputeStorageBuffers() { SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); + buffer.MarkUsage(offset, size); const bool is_written = ((channel_state->written_compute_storage_buffers >> index) & 1) != 0; @@ -1053,6 +1068,7 @@ void BufferCache

::BindHostComputeTextureBuffers() { const u32 offset = buffer.Offset(binding.cpu_addr); const PixelFormat format = binding.format; + buffer.MarkUsage(offset, size); if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { if (((channel_state->image_compute_texture_buffers >> index) & 1) != 0) { runtime.BindImageBuffer(buffer, offset, size, format); @@ -1172,10 +1188,11 @@ void BufferCache

::UpdateVertexBuffer(u32 index) { if (!gpu_memory->IsWithinGPUAddressRange(gpu_addr_end)) { size = static_cast(gpu_memory->MaxContinuousRange(gpu_addr_begin, size)); } + const BufferId buffer_id = FindBuffer(*cpu_addr, size); channel_state->vertex_buffers[index] = Binding{ .cpu_addr = *cpu_addr, .size = size, - .buffer_id = FindBuffer(*cpu_addr, size), + .buffer_id = buffer_id, }; } @@ -1406,7 +1423,8 @@ void BufferCache

::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id, .dst_offset = dst_base_offset, .size = overlap.SizeBytes(), }); - runtime.CopyBuffer(new_buffer, overlap, copies); + new_buffer.MarkUsage(copies[0].dst_offset, copies[0].size); + runtime.CopyBuffer(new_buffer, overlap, copies, true); DeleteBuffer(overlap_id, true); } @@ -1419,7 +1437,9 @@ BufferId BufferCache

::CreateBuffer(VAddr cpu_addr, u32 wanted_size) { const u32 size = static_cast(overlap.end - overlap.begin); const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size); auto& new_buffer = slot_buffers[new_buffer_id]; - runtime.ClearBuffer(new_buffer, 0, new_buffer.SizeBytes(), 0); + const size_t size_bytes = new_buffer.SizeBytes(); + runtime.ClearBuffer(new_buffer, 0, size_bytes, 0); + new_buffer.MarkUsage(0, size_bytes); for (const BufferId overlap_id : overlap.ids) { JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap); } @@ -1472,11 +1492,6 @@ void BufferCache

::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept { template bool BufferCache

::SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size) { - return SynchronizeBufferImpl(buffer, cpu_addr, size); -} - -template -bool BufferCache

::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size) { boost::container::small_vector copies; u64 total_size_bytes = 0; u64 largest_copy = 0; @@ -1498,51 +1513,6 @@ bool BufferCache

::SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 s return false; } -template -bool BufferCache

::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size) { - boost::container::small_vector copies; - u64 total_size_bytes = 0; - u64 largest_copy = 0; - IntervalSet found_sets{}; - auto make_copies = [&] { - for (auto& interval : found_sets) { - const std::size_t sub_size = interval.upper() - interval.lower(); - const VAddr cpu_addr_ = interval.lower(); - copies.push_back(BufferCopy{ - .src_offset = total_size_bytes, - .dst_offset = cpu_addr_ - buffer.CpuAddr(), - .size = sub_size, - }); - total_size_bytes += sub_size; - largest_copy = std::max(largest_copy, sub_size); - } - const std::span copies_span(copies.data(), copies.size()); - UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); - }; - memory_tracker.ForEachUploadRange(cpu_addr, size, [&](u64 cpu_addr_out, u64 range_size) { - const VAddr base_adr = cpu_addr_out; - const VAddr end_adr = base_adr + range_size; - const IntervalType add_interval{base_adr, end_adr}; - found_sets.add(add_interval); - }); - if (found_sets.empty()) { - return true; - } - const IntervalType search_interval{cpu_addr, cpu_addr + size}; - auto it = common_ranges.lower_bound(search_interval); - auto it_end = common_ranges.upper_bound(search_interval); - if (it == common_ranges.end()) { - make_copies(); - return false; - } - while (it != it_end) { - found_sets.subtract(*it); - it++; - } - make_copies(); - return false; -} - template void BufferCache

::UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, std::span copies) { @@ -1591,7 +1561,8 @@ void BufferCache

::MappedUploadMemory([[maybe_unused]] Buffer& buffer, // Apply the staging offset copy.src_offset += upload_staging.offset; } - runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + const bool can_reorder = runtime.CanReorderUpload(buffer, copies); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder); } } @@ -1633,7 +1604,8 @@ void BufferCache

::InlineMemoryImplementation(VAddr dest_address, size_t copy_ }}; u8* const src_pointer = upload_staging.mapped_span.data(); std::memcpy(src_pointer, inlined_buffer.data(), copy_size); - runtime.CopyBuffer(buffer, upload_staging.buffer, copies); + const bool can_reorder = runtime.CanReorderUpload(buffer, copies); + runtime.CopyBuffer(buffer, upload_staging.buffer, copies, true, can_reorder); } else { buffer.ImmediateUpload(buffer.Offset(dest_address), inlined_buffer.first(copy_size)); } @@ -1686,8 +1658,9 @@ void BufferCache

::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si for (BufferCopy& copy : copies) { // Modify copies to have the staging offset in mind copy.dst_offset += download_staging.offset; + buffer.MarkUsage(copy.src_offset, copy.size); } - runtime.CopyBuffer(download_staging.buffer, buffer, copies_span); + runtime.CopyBuffer(download_staging.buffer, buffer, copies_span, true); runtime.Finish(); for (const BufferCopy& copy : copies) { const VAddr copy_cpu_addr = buffer.CpuAddr() + copy.src_offset; diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index eed267361..d6d696d8c 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -529,10 +529,6 @@ private: bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size); - bool SynchronizeBufferImpl(Buffer& buffer, VAddr cpu_addr, u32 size); - - bool SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr, u32 size); - void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy, std::span copies); diff --git a/src/video_core/buffer_cache/usage_tracker.h b/src/video_core/buffer_cache/usage_tracker.h new file mode 100644 index 000000000..ab05fe415 --- /dev/null +++ b/src/video_core/buffer_cache/usage_tracker.h @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/common_types.h" + +namespace VideoCommon { + +class UsageTracker { + static constexpr size_t BYTES_PER_BIT_SHIFT = 6; + static constexpr size_t PAGE_SHIFT = 6 + BYTES_PER_BIT_SHIFT; + static constexpr size_t PAGE_BYTES = 1 << PAGE_SHIFT; + +public: + explicit UsageTracker(size_t size) { + const size_t num_pages = (size >> PAGE_SHIFT) + 1; + pages.resize(num_pages, 0ULL); + } + + void Reset() noexcept { + std::ranges::fill(pages, 0ULL); + } + + void Track(u64 offset, u64 size) noexcept { + const size_t page = offset >> PAGE_SHIFT; + const size_t page_end = (offset + size) >> PAGE_SHIFT; + TrackPage(page, offset, size); + if (page == page_end) { + return; + } + for (size_t i = page + 1; i < page_end; i++) { + pages[i] = ~u64{0}; + } + const size_t offset_end = offset + size; + const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES); + TrackPage(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); + } + + [[nodiscard]] bool IsUsed(u64 offset, u64 size) const noexcept { + const size_t page = offset >> PAGE_SHIFT; + const size_t page_end = (offset + size) >> PAGE_SHIFT; + if (IsPageUsed(page, offset, size)) { + return true; + } + for (size_t i = page + 1; i < page_end; i++) { + if (pages[i] != 0) { + return true; + } + } + const size_t offset_end = offset + size; + const size_t offset_end_page_aligned = Common::AlignDown(offset_end, PAGE_BYTES); + return IsPageUsed(page_end, offset_end_page_aligned, offset_end - offset_end_page_aligned); + } + +private: + void TrackPage(u64 page, u64 offset, u64 size) noexcept { + const size_t offset_in_page = offset % PAGE_BYTES; + const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; + const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; + const size_t mask = ~u64{0} >> (64 - num_bits); + pages[page] |= (~u64{0} & mask) << first_bit; + } + + bool IsPageUsed(u64 page, u64 offset, u64 size) const noexcept { + const size_t offset_in_page = offset % PAGE_BYTES; + const size_t first_bit = offset_in_page >> BYTES_PER_BIT_SHIFT; + const size_t num_bits = std::min(size, PAGE_BYTES) >> BYTES_PER_BIT_SHIFT; + const size_t mask = ~u64{0} >> (64 - num_bits); + const size_t mask2 = (~u64{0} & mask) << first_bit; + return (pages[page] & mask2) != 0; + } + +private: + std::vector pages; +}; + +} // namespace VideoCommon diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 38d553d3c..dfd696de6 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -178,13 +178,14 @@ void BufferCacheRuntime::CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, } void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, - std::span copies, bool barrier) { + std::span copies, bool barrier, + bool) { CopyBuffer(dst_buffer.Handle(), src_buffer, copies, barrier); } void BufferCacheRuntime::CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, - std::span copies) { - CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies); + std::span copies, bool) { + CopyBuffer(dst_buffer.Handle(), src_buffer.Handle(), copies, true); } void BufferCacheRuntime::PreCopyBarrier() { diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 41b746f3b..feccf06f9 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -30,6 +30,8 @@ public: void MakeResident(GLenum access) noexcept; + void MarkUsage(u64 offset, u64 size) {} + [[nodiscard]] GLuint View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format); [[nodiscard]] GLuint64EXT HostGpuAddr() const noexcept { @@ -66,22 +68,29 @@ public: [[nodiscard]] StagingBufferMap DownloadStagingBuffer(size_t size); + bool CanReorderUpload(const Buffer&, std::span) { + return false; + } + void CopyBuffer(GLuint dst_buffer, GLuint src_buffer, - std::span copies, bool barrier = true); + std::span copies, bool barrier); void CopyBuffer(GLuint dst_buffer, Buffer& src_buffer, - std::span copies, bool barrier = true); + std::span copies, bool barrier); void CopyBuffer(Buffer& dst_buffer, GLuint src_buffer, - std::span copies, bool barrier = true); + std::span copies, bool barrier, + bool can_reorder_upload = false); void CopyBuffer(Buffer& dst_buffer, Buffer& src_buffer, - std::span copies); + std::span copies, bool); void PreCopyBarrier(); void PostCopyBarrier(); void Finish(); + void TickFrame(VideoCommon::SlotVector&) noexcept {} + void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value); void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index d8148e89a..7691cc2ba 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -79,13 +79,13 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo } // Anonymous namespace Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) - : VideoCommon::BufferBase(null_params) {} + : VideoCommon::BufferBase(null_params), tracker{4096} {} Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, VAddr cpu_addr_, u64 size_bytes_) : VideoCommon::BufferBase(rasterizer_, cpu_addr_, size_bytes_), - device{&runtime.device}, buffer{ - CreateBuffer(*device, runtime.memory_allocator, SizeBytes())} { + device{&runtime.device}, buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, + tracker{SizeBytes()} { if (runtime.device.HasDebuggingToolAttached()) { buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); } @@ -355,12 +355,31 @@ bool BufferCacheRuntime::CanReportMemoryUsage() const { return device.CanReportMemoryUsage(); } +void BufferCacheRuntime::TickFrame(VideoCommon::SlotVector& slot_buffers) noexcept { + for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { + it->ResetUsageTracking(); + } +} + void BufferCacheRuntime::Finish() { scheduler.Finish(); } +bool BufferCacheRuntime::CanReorderUpload(const Buffer& buffer, + std::span copies) { + if (Settings::values.disable_buffer_reorder) { + return false; + } + const bool can_use_upload_cmdbuf = + std::ranges::all_of(copies, [&](const VideoCommon::BufferCopy& copy) { + return !buffer.IsRegionUsed(copy.dst_offset, copy.size); + }); + return can_use_upload_cmdbuf; +} + void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, - std::span copies, bool barrier) { + std::span copies, bool barrier, + bool can_reorder_upload) { if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) { return; } @@ -376,9 +395,18 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, }; + // Measuring a popular game, this number never exceeds the specified size once data is warmed up boost::container::small_vector vk_copies(copies.size()); std::ranges::transform(copies, vk_copies.begin(), MakeBufferCopy); + if (src_buffer == staging_pool.StreamBuf() && can_reorder_upload) { + scheduler.RecordWithUploadBuffer([src_buffer, dst_buffer, vk_copies]( + vk::CommandBuffer, vk::CommandBuffer upload_cmdbuf) { + upload_cmdbuf.CopyBuffer(src_buffer, dst_buffer, vk_copies); + }); + return; + } + scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([src_buffer, dst_buffer, vk_copies, barrier](vk::CommandBuffer cmdbuf) { if (barrier) { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 95446c732..4416a902f 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -5,6 +5,7 @@ #include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/buffer_cache/memory_tracker_base.h" +#include "video_core/buffer_cache/usage_tracker.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" @@ -34,6 +35,18 @@ public: return *buffer; } + [[nodiscard]] bool IsRegionUsed(u64 offset, u64 size) const noexcept { + return tracker.IsUsed(offset, size); + } + + void MarkUsage(u64 offset, u64 size) noexcept { + tracker.Track(offset, size); + } + + void ResetUsageTracking() noexcept { + tracker.Reset(); + } + operator VkBuffer() const noexcept { return *buffer; } @@ -49,6 +62,7 @@ private: const Device* device{}; vk::Buffer buffer; std::vector views; + VideoCommon::UsageTracker tracker; }; class QuadArrayIndexBuffer; @@ -67,6 +81,8 @@ public: ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool); + void TickFrame(VideoCommon::SlotVector& slot_buffers) noexcept; + void Finish(); u64 GetDeviceLocalMemory() const; @@ -79,12 +95,15 @@ public: [[nodiscard]] StagingBufferRef DownloadStagingBuffer(size_t size, bool deferred = false); + bool CanReorderUpload(const Buffer& buffer, std::span copies); + void FreeDeferredStagingBuffer(StagingBufferRef& ref); void PreCopyBarrier(); void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, - std::span copies, bool barrier = true); + std::span copies, bool barrier, + bool can_reorder_upload = false); void PostCopyBarrier(); diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index 6b288b994..ac8b6e838 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -100,12 +100,14 @@ void MasterSemaphore::Wait(u64 tick) { Refresh(); } -VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick) { +VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick) { if (semaphore) { - return SubmitQueueTimeline(cmdbuf, signal_semaphore, wait_semaphore, host_tick); + return SubmitQueueTimeline(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, + host_tick); } else { - return SubmitQueueFence(cmdbuf, signal_semaphore, wait_semaphore, host_tick); + return SubmitQueueFence(cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, host_tick); } } @@ -115,6 +117,7 @@ static constexpr std::array wait_stage_masks{ }; VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, + vk::CommandBuffer& upload_cmdbuf, VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick) { const VkSemaphore timeline_semaphore = *semaphore; @@ -123,6 +126,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, const std::array signal_values{host_tick, u64(0)}; const std::array signal_semaphores{timeline_semaphore, signal_semaphore}; + const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; + const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; const VkTimelineSemaphoreSubmitInfo timeline_si{ .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, @@ -138,8 +143,8 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, .waitSemaphoreCount = num_wait_semaphores, .pWaitSemaphores = &wait_semaphore, .pWaitDstStageMask = wait_stage_masks.data(), - .commandBufferCount = 1, - .pCommandBuffers = cmdbuf.address(), + .commandBufferCount = static_cast(cmdbuffers.size()), + .pCommandBuffers = cmdbuffers.data(), .signalSemaphoreCount = num_signal_semaphores, .pSignalSemaphores = signal_semaphores.data(), }; @@ -147,19 +152,23 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, return device.GetGraphicsQueue().Submit(submit_info); } -VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick) { +VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf, + vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick) { const u32 num_signal_semaphores = signal_semaphore ? 1 : 0; const u32 num_wait_semaphores = wait_semaphore ? 1 : 0; + const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; + const VkSubmitInfo submit_info{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = nullptr, .waitSemaphoreCount = num_wait_semaphores, .pWaitSemaphores = &wait_semaphore, .pWaitDstStageMask = wait_stage_masks.data(), - .commandBufferCount = 1, - .pCommandBuffers = cmdbuf.address(), + .commandBufferCount = static_cast(cmdbuffers.size()), + .pCommandBuffers = cmdbuffers.data(), .signalSemaphoreCount = num_signal_semaphores, .pSignalSemaphores = &signal_semaphore, }; diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h index 3f599d7bd..7dfb93ffb 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.h +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h @@ -52,14 +52,16 @@ public: void Wait(u64 tick); /// Submits the device graphics queue, updating the tick as necessary - VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick); + VkResult SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, u64 host_tick); private: - VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick); - VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, VkSemaphore signal_semaphore, - VkSemaphore wait_semaphore, u64 host_tick); + VkResult SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick); + VkResult SubmitQueueFence(vk::CommandBuffer& cmdbuf, vk::CommandBuffer& upload_cmdbuf, + VkSemaphore signal_semaphore, VkSemaphore wait_semaphore, + u64 host_tick); void WaitThread(std::stop_token token); diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3be7837f4..f1a9406ce 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -22,11 +22,12 @@ namespace Vulkan { MICROPROFILE_DECLARE(Vulkan_WaitForWorker); -void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) { +void Scheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf, + vk::CommandBuffer upload_cmdbuf) { auto command = first; while (command != nullptr) { auto next = command->GetNext(); - command->Execute(cmdbuf); + command->Execute(cmdbuf, upload_cmdbuf); command->~Command(); command = next; } @@ -180,7 +181,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) { // Perform the work, tracking whether the chunk was a submission // before executing. const bool has_submit = work->HasSubmit(); - work->ExecuteAll(current_cmdbuf); + work->ExecuteAll(current_cmdbuf, current_upload_cmdbuf); // If the chunk was a submission, reallocate the command buffer. if (has_submit) { @@ -205,6 +206,13 @@ void Scheduler::AllocateWorkerCommandBuffer() { .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, .pInheritanceInfo = nullptr, }); + current_upload_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader()); + current_upload_cmdbuf.Begin({ + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = nullptr, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + .pInheritanceInfo = nullptr, + }); } u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { @@ -212,7 +220,9 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se InvalidateState(); const u64 signal_value = master_semaphore->NextTick(); - Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) { + RecordWithUploadBuffer([signal_semaphore, wait_semaphore, signal_value, + this](vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) { + upload_cmdbuf.End(); cmdbuf.End(); if (on_submit) { @@ -221,7 +231,7 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se std::scoped_lock lock{submit_mutex}; switch (const VkResult result = master_semaphore->SubmitQueue( - cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { + cmdbuf, upload_cmdbuf, signal_semaphore, wait_semaphore, signal_value)) { case VK_SUCCESS: break; case VK_ERROR_DEVICE_LOST: diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index da03803aa..f8d8ca80a 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -80,7 +80,8 @@ public: /// Send work to a separate thread. template - void Record(T&& command) { + requires std::is_invocable_v + void RecordWithUploadBuffer(T&& command) { if (chunk->Record(command)) { return; } @@ -88,6 +89,15 @@ public: (void)chunk->Record(command); } + template + requires std::is_invocable_v + void Record(T&& c) { + this->RecordWithUploadBuffer( + [command = std::move(c)](vk::CommandBuffer cmdbuf, vk::CommandBuffer) { + command(cmdbuf); + }); + } + /// Returns the current command buffer tick. [[nodiscard]] u64 CurrentTick() const noexcept { return master_semaphore->CurrentTick(); @@ -119,7 +129,7 @@ private: public: virtual ~Command() = default; - virtual void Execute(vk::CommandBuffer cmdbuf) const = 0; + virtual void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const = 0; Command* GetNext() const { return next; @@ -142,8 +152,8 @@ private: TypedCommand(TypedCommand&&) = delete; TypedCommand& operator=(TypedCommand&&) = delete; - void Execute(vk::CommandBuffer cmdbuf) const override { - command(cmdbuf); + void Execute(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf) const override { + command(cmdbuf, upload_cmdbuf); } private: @@ -152,7 +162,7 @@ private: class CommandChunk final { public: - void ExecuteAll(vk::CommandBuffer cmdbuf); + void ExecuteAll(vk::CommandBuffer cmdbuf, vk::CommandBuffer upload_cmdbuf); template bool Record(T& command) { @@ -228,6 +238,7 @@ private: VideoCommon::QueryCacheBase* query_cache = nullptr; vk::CommandBuffer current_cmdbuf; + vk::CommandBuffer current_upload_cmdbuf; std::unique_ptr chunk; std::function on_submit; diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/vk_smaa.cpp index 5efd7d66e..70644ea82 100644 --- a/src/video_core/renderer_vulkan/vk_smaa.cpp +++ b/src/video_core/renderer_vulkan/vk_smaa.cpp @@ -672,7 +672,7 @@ void SMAA::UploadImages(Scheduler& scheduler) { UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); - scheduler.Record([&](vk::CommandBuffer& cmdbuf) { + scheduler.Record([&](vk::CommandBuffer cmdbuf) { for (auto& images : m_dynamic_images) { for (size_t i = 0; i < MaxDynamicImage; i++) { ClearColorImage(cmdbuf, *images.images[i]); @@ -707,7 +707,7 @@ VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_ UpdateDescriptorSets(source_image_view, image_index); scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([=, this](vk::CommandBuffer& cmdbuf) { + scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index d3deb9072..f63a20327 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -36,6 +36,10 @@ public: StagingBufferRef Request(size_t size, MemoryUsage usage, bool deferred = false); void FreeDeferred(StagingBufferRef& ref); + [[nodiscard]] VkBuffer StreamBuf() const noexcept { + return *stream_buffer; + } + void TickFrame(); private: diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h index 9df6a2903..3ffa2a661 100644 --- a/src/video_core/texture_cache/slot_vector.h +++ b/src/video_core/texture_cache/slot_vector.h @@ -138,6 +138,10 @@ public: return Iterator(this, SlotId{SlotId::INVALID_INDEX}); } + [[nodiscard]] size_t size() const noexcept { + return values_capacity - free_list.size(); + } + private: struct NonTrivialDummy { NonTrivialDummy() noexcept {} diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 0487cd3b6..a0c70797f 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -1101,6 +1101,10 @@ public: return &handle; } + VkCommandBuffer operator*() const noexcept { + return handle; + } + void Begin(const VkCommandBufferBeginInfo& begin_info) const { Check(dld->vkBeginCommandBuffer(handle, &begin_info)); } diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index ef421c754..1010038b7 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp @@ -51,6 +51,8 @@ void ConfigureDebug::SetConfiguration() { ui->enable_all_controllers->setChecked(Settings::values.enable_all_controllers.GetValue()); ui->enable_renderdoc_hotkey->setEnabled(runtime_lock); ui->enable_renderdoc_hotkey->setChecked(Settings::values.enable_renderdoc_hotkey.GetValue()); + ui->disable_buffer_reorder->setEnabled(runtime_lock); + ui->disable_buffer_reorder->setChecked(Settings::values.disable_buffer_reorder.GetValue()); ui->enable_graphics_debugging->setEnabled(runtime_lock); ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug.GetValue()); ui->enable_shader_feedback->setEnabled(runtime_lock); @@ -96,6 +98,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.enable_all_controllers = ui->enable_all_controllers->isChecked(); Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); Settings::values.enable_renderdoc_hotkey = ui->enable_renderdoc_hotkey->isChecked(); + Settings::values.disable_buffer_reorder = ui->disable_buffer_reorder->isChecked(); Settings::values.renderer_shader_feedback = ui->enable_shader_feedback->isChecked(); Settings::values.cpu_debug_mode = ui->enable_cpu_debugging->isChecked(); Settings::values.enable_nsight_aftermath = ui->enable_nsight_aftermath->isChecked(); diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui index 76fe98924..22b51f39c 100644 --- a/src/yuzu/configuration/configure_debug.ui +++ b/src/yuzu/configuration/configure_debug.ui @@ -271,19 +271,6 @@ - - - true - - - When checked, it disables the macro HLE functions. Enabling this makes games run slower - - - Disable Macro HLE - - - - true @@ -306,17 +293,27 @@ - - + + + + true + - When checked, yuzu will log statistics about the compiled pipeline cache + When checked, it will dump all the original assembler shaders from the disk shader cache or game as found - Enable Shader Feedback + Dump Game Shaders - + + + + Enable Renderdoc Hotkey + + + + true @@ -330,20 +327,17 @@ - - - Qt::Vertical + + + true - - QSizePolicy::Preferred + + When checked, it disables the macro HLE functions. Enabling this makes games run slower - - - 20 - 0 - + + Disable Macro HLE - + @@ -358,23 +352,39 @@ - - - - true + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + 20 + 0 + + + + + + - When checked, it will dump all the original assembler shaders from the disk shader cache or game as found + When checked, yuzu will log statistics about the compiled pipeline cache - Dump Game Shaders + Enable Shader Feedback - - + + + + <html><head/><body><p>When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.</p></body></html> + - Enable Renderdoc Hotkey + Disable Buffer Reorder -- cgit v1.2.3 From c9cd938dfd8fc40ec58d61dc453bc31d3b811496 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Tue, 14 Nov 2023 21:34:27 -0600 Subject: service: hid: Split hid.cpp into individual interfaces --- src/core/CMakeLists.txt | 8 + src/core/hid/emulated_console.h | 8 - src/core/hid/hid_types.h | 8 + src/core/hid/input_interpreter.cpp | 7 +- src/core/hle/service/hid/hid.cpp | 2858 +----------------------- src/core/hle/service/hid/hid.h | 212 +- src/core/hle/service/hid/hid_debug_server.cpp | 159 ++ src/core/hle/service/hid/hid_debug_server.h | 26 + src/core/hle/service/hid/hid_server.cpp | 2269 +++++++++++++++++++ src/core/hle/service/hid/hid_server.h | 138 ++ src/core/hle/service/hid/hid_system_server.cpp | 304 +++ src/core/hle/service/hid/hid_system_server.h | 40 + src/core/hle/service/hid/irs.h | 5 +- src/core/hle/service/hid/resource_manager.cpp | 192 ++ src/core/hle/service/hid/resource_manager.h | 106 + src/core/memory/cheat_engine.cpp | 7 +- src/yuzu/applets/qt_controller.cpp | 1 - 17 files changed, 3277 insertions(+), 3071 deletions(-) create mode 100644 src/core/hle/service/hid/hid_debug_server.cpp create mode 100644 src/core/hle/service/hid/hid_debug_server.h create mode 100644 src/core/hle/service/hid/hid_server.cpp create mode 100644 src/core/hle/service/hid/hid_server.h create mode 100644 src/core/hle/service/hid/hid_system_server.cpp create mode 100644 src/core/hle/service/hid/hid_system_server.h create mode 100644 src/core/hle/service/hid/resource_manager.cpp create mode 100644 src/core/hle/service/hid/resource_manager.h (limited to 'src/yuzu') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8be3bdd08..3aa2e4340 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -521,11 +521,19 @@ add_library(core STATIC hle/service/grc/grc.h hle/service/hid/hid.cpp hle/service/hid/hid.h + hle/service/hid/hid_debug_server.cpp + hle/service/hid/hid_debug_server.h + hle/service/hid/hid_server.cpp + hle/service/hid/hid_server.h + hle/service/hid/hid_system_server.cpp + hle/service/hid/hid_system_server.h hle/service/hid/hidbus.cpp hle/service/hid/hidbus.h hle/service/hid/irs.cpp hle/service/hid/irs.h hle/service/hid/irs_ring_lifo.h + hle/service/hid/resource_manager.cpp + hle/service/hid/resource_manager.h hle/service/hid/ring_lifo.h hle/service/hid/xcd.cpp hle/service/hid/xcd.h diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h index 79114bb6d..fae15a556 100644 --- a/src/core/hid/emulated_console.h +++ b/src/core/hid/emulated_console.h @@ -38,14 +38,6 @@ using TouchParams = std::array; using ConsoleMotionValues = ConsoleMotionInfo; using TouchValues = std::array; -struct TouchFinger { - u64 last_touch{}; - Common::Point position{}; - u32 id{}; - TouchAttribute attribute{}; - bool pressed{}; -}; - // Contains all motion related data that is used on the services struct ConsoleMotion { Common::Vec3f accel{}; diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 7ba75a50c..9d48cd90e 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -356,6 +356,14 @@ struct TouchState { }; static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); +struct TouchFinger { + u64 last_touch{}; + Common::Point position{}; + u32 id{}; + TouchAttribute attribute{}; + bool pressed{}; +}; + // This is nn::hid::TouchScreenConfigurationForNx struct TouchScreenConfigurationForNx { TouchScreenModeForNx mode{TouchScreenModeForNx::UseSystemSetting}; diff --git a/src/core/hid/input_interpreter.cpp b/src/core/hid/input_interpreter.cpp index 76d6b8ab0..11359f318 100644 --- a/src/core/hid/input_interpreter.cpp +++ b/src/core/hid/input_interpreter.cpp @@ -5,13 +5,14 @@ #include "core/hid/hid_types.h" #include "core/hid/input_interpreter.h" #include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/sm/sm.h" InputInterpreter::InputInterpreter(Core::System& system) : npad{system.ServiceManager() - .GetService("hid") - ->GetAppletResource() + .GetService("hid") + ->GetResourceManager() ->GetController(Service::HID::HidController::NPad)} { ResetButtonStates(); } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 1d4101be9..801a4d08f 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -1,2862 +1,36 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include "common/common_types.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/core.h" -#include "core/core_timing.h" -#include "core/hid/hid_core.h" -#include "core/hle/kernel/k_readable_event.h" -#include "core/hle/kernel/k_shared_memory.h" -#include "core/hle/kernel/k_transfer_memory.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_debug_server.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/hid_system_server.h" #include "core/hle/service/hid/hidbus.h" #include "core/hle/service/hid/irs.h" +#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/hid/xcd.h" -#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/server_manager.h" -#include "core/memory.h" -#include "core/hle/service/hid/controllers/console_sixaxis.h" -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/hid/controllers/debug_pad.h" -#include "core/hle/service/hid/controllers/gesture.h" -#include "core/hle/service/hid/controllers/keyboard.h" -#include "core/hle/service/hid/controllers/mouse.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/controllers/palma.h" -#include "core/hle/service/hid/controllers/stubbed.h" -#include "core/hle/service/hid/controllers/touchscreen.h" -#include "core/hle/service/hid/controllers/xpad.h" - -namespace Service::HID { - -// Updating period for each HID device. -// Period time is obtained by measuring the number of samples in a second on HW using a homebrew -// Correct npad_update_ns is 4ms this is overclocked to lower input lag -constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) -constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) -constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) -constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) - -IAppletResource::IAppletResource(Core::System& system_, - KernelHelpers::ServiceContext& service_context_) - : ServiceFramework{system_, "IAppletResource"}, service_context{service_context_} { - static const FunctionInfo functions[] = { - {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, - }; - RegisterHandlers(functions); - u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); - MakeController(HidController::DebugPad, shared_memory); - MakeController(HidController::Touchscreen, shared_memory); - MakeController(HidController::Mouse, shared_memory); - MakeController(HidController::Keyboard, shared_memory); - MakeController(HidController::XPad, shared_memory); - MakeController(HidController::HomeButton, shared_memory); - MakeController(HidController::SleepButton, shared_memory); - MakeController(HidController::CaptureButton, shared_memory); - MakeController(HidController::InputDetector, shared_memory); - MakeController(HidController::UniquePad, shared_memory); - MakeControllerWithServiceContext(HidController::NPad, shared_memory); - MakeController(HidController::Gesture, shared_memory); - MakeController(HidController::ConsoleSixAxisSensor, shared_memory); - MakeController(HidController::DebugMouse, shared_memory); - MakeControllerWithServiceContext(HidController::Palma, shared_memory); - - // Homebrew doesn't try to activate some controllers, so we activate them by default - GetController(HidController::NPad).ActivateController(); - GetController(HidController::Touchscreen).ActivateController(); - - GetController(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); - GetController(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); - GetController(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); - GetController(HidController::InputDetector).SetCommonHeaderOffset(0x5200); - GetController(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); - GetController(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00); - - // Register update callbacks - npad_update_event = Core::Timing::CreateEvent( - "HID::UpdatePadCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional { - const auto guard = LockService(); - UpdateNpad(user_data, ns_late); - return std::nullopt; - }); - default_update_event = Core::Timing::CreateEvent( - "HID::UpdateDefaultCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional { - const auto guard = LockService(); - UpdateControllers(user_data, ns_late); - return std::nullopt; - }); - mouse_keyboard_update_event = Core::Timing::CreateEvent( - "HID::UpdateMouseKeyboardCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional { - const auto guard = LockService(); - UpdateMouseKeyboard(user_data, ns_late); - return std::nullopt; - }); - motion_update_event = Core::Timing::CreateEvent( - "HID::UpdateMotionCallback", - [this](std::uintptr_t user_data, s64 time, - std::chrono::nanoseconds ns_late) -> std::optional { - const auto guard = LockService(); - UpdateMotion(user_data, ns_late); - return std::nullopt; - }); - - system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); - system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, - default_update_event); - system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, - mouse_keyboard_update_event); - system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, - motion_update_event); - - system.HIDCore().ReloadInputDevices(); -} - -void IAppletResource::ActivateController(HidController controller) { - controllers[static_cast(controller)]->ActivateController(); -} - -void IAppletResource::DeactivateController(HidController controller) { - controllers[static_cast(controller)]->DeactivateController(); -} - -IAppletResource::~IAppletResource() { - system.CoreTiming().UnscheduleEvent(npad_update_event, 0); - system.CoreTiming().UnscheduleEvent(default_update_event, 0); - system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); - system.CoreTiming().UnscheduleEvent(motion_update_event, 0); -} - -void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&system.Kernel().GetHidSharedMem()); -} - -void IAppletResource::UpdateControllers(std::uintptr_t user_data, - std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - for (const auto& controller : controllers) { - // Keyboard has it's own update event - if (controller == controllers[static_cast(HidController::Keyboard)]) { - continue; - } - // Mouse has it's own update event - if (controller == controllers[static_cast(HidController::Mouse)]) { - continue; - } - // Npad has it's own update event - if (controller == controllers[static_cast(HidController::NPad)]) { - continue; - } - controller->OnUpdate(core_timing); - } -} - -void IAppletResource::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - controllers[static_cast(HidController::NPad)]->OnUpdate(core_timing); -} - -void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data, - std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - controllers[static_cast(HidController::Mouse)]->OnUpdate(core_timing); - controllers[static_cast(HidController::Keyboard)]->OnUpdate(core_timing); -} - -void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { - auto& core_timing = system.CoreTiming(); - - controllers[static_cast(HidController::NPad)]->OnMotionUpdate(core_timing); -} - -class IActiveVibrationDeviceList final : public ServiceFramework { -public: - explicit IActiveVibrationDeviceList(Core::System& system_, - std::shared_ptr applet_resource_) - : ServiceFramework{system_, "IActiveVibrationDeviceList"}, - applet_resource(applet_resource_) { - // clang-format off - static const FunctionInfo functions[] = { - {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, - }; - // clang-format on - - RegisterHandlers(functions); - } - -private: - void InitializeVibrationDevice(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto vibration_device_handle{rp.PopRaw()}; - - if (applet_resource != nullptr) { - applet_resource->GetController(HidController::NPad) - .InitializeVibrationDevice(vibration_device_handle); - } - - LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", - vibration_device_handle.npad_type, vibration_device_handle.npad_id, - vibration_device_handle.device_index); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - std::shared_ptr applet_resource; -}; - -std::shared_ptr Hid::GetAppletResource() { - if (applet_resource == nullptr) { - applet_resource = std::make_shared(system, service_context); - } - - return applet_resource; -} - -Hid::Hid(Core::System& system_, std::shared_ptr applet_resource_) - : ServiceFramework{system_, "hid"}, applet_resource{applet_resource_}, service_context{ - system_, - service_name} { - // clang-format off - static const FunctionInfo functions[] = { - {0, &Hid::CreateAppletResource, "CreateAppletResource"}, - {1, &Hid::ActivateDebugPad, "ActivateDebugPad"}, - {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"}, - {21, &Hid::ActivateMouse, "ActivateMouse"}, - {26, nullptr, "ActivateDebugMouse"}, - {31, &Hid::ActivateKeyboard, "ActivateKeyboard"}, - {32, &Hid::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"}, - {40, nullptr, "AcquireXpadIdEventHandle"}, - {41, nullptr, "ReleaseXpadIdEventHandle"}, - {51, &Hid::ActivateXpad, "ActivateXpad"}, - {55, &Hid::GetXpadIDs, "GetXpadIds"}, - {56, nullptr, "ActivateJoyXpad"}, - {58, nullptr, "GetJoyXpadLifoHandle"}, - {59, nullptr, "GetJoyXpadIds"}, - {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, - {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, - {62, nullptr, "GetSixAxisSensorLifoHandle"}, - {63, nullptr, "ActivateJoySixAxisSensor"}, - {64, nullptr, "DeactivateJoySixAxisSensor"}, - {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, - {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, - {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, - {68, &Hid::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, - {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, - {70, &Hid::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, - {71, &Hid::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, - {72, &Hid::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"}, - {73, nullptr, "SetAccelerometerParameters"}, - {74, nullptr, "GetAccelerometerParameters"}, - {75, nullptr, "ResetAccelerometerParameters"}, - {76, nullptr, "SetAccelerometerPlayMode"}, - {77, nullptr, "GetAccelerometerPlayMode"}, - {78, nullptr, "ResetAccelerometerPlayMode"}, - {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, - {80, &Hid::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, - {81, &Hid::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, - {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, - {83, &Hid::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, - {84, &Hid::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, - {85, &Hid::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, - {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, - {87, &Hid::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, - {88, &Hid::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, - {89, &Hid::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, - {91, &Hid::ActivateGesture, "ActivateGesture"}, - {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, - {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, - {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, - {103, &Hid::ActivateNpad, "ActivateNpad"}, - {104, &Hid::DeactivateNpad, "DeactivateNpad"}, - {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, - {107, &Hid::DisconnectNpad, "DisconnectNpad"}, - {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, - {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, - {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, - {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, - {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, - {123, &Hid::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"}, - {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, - {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, - {126, &Hid::StartLrAssignmentMode, "StartLrAssignmentMode"}, - {127, &Hid::StopLrAssignmentMode, "StopLrAssignmentMode"}, - {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, - {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, - {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, - {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, - {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, - {133, &Hid::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"}, - {134, &Hid::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, - {135, &Hid::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"}, - {136, &Hid::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"}, - {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, - {201, &Hid::SendVibrationValue, "SendVibrationValue"}, - {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"}, - {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, - {204, &Hid::PermitVibration, "PermitVibration"}, - {205, &Hid::IsVibrationPermitted, "IsVibrationPermitted"}, - {206, &Hid::SendVibrationValues, "SendVibrationValues"}, - {207, &Hid::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"}, - {208, &Hid::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"}, - {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, - {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"}, - {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, - {212, nullptr, "SendVibrationValueInBool"}, - {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, - {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, - {302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, - {303, &Hid::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, - {304, &Hid::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, - {305, &Hid::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, - {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, - {307, &Hid::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, - {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, - {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, - {310, &Hid::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, - {400, &Hid::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, - {401, nullptr, "EnableUsbFullKeyController"}, - {402, nullptr, "IsUsbFullKeyControllerConnected"}, - {403, nullptr, "HasBattery"}, - {404, nullptr, "HasLeftRightBattery"}, - {405, nullptr, "GetNpadInterfaceType"}, - {406, nullptr, "GetNpadLeftRightInterfaceType"}, - {407, nullptr, "GetNpadOfHighestBatteryLevel"}, - {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, - {500, &Hid::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, - {501, &Hid::InitializePalma, "InitializePalma"}, - {502, &Hid::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, - {503, &Hid::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, - {504, &Hid::PlayPalmaActivity, "PlayPalmaActivity"}, - {505, &Hid::SetPalmaFrModeType, "SetPalmaFrModeType"}, - {506, &Hid::ReadPalmaStep, "ReadPalmaStep"}, - {507, &Hid::EnablePalmaStep, "EnablePalmaStep"}, - {508, &Hid::ResetPalmaStep, "ResetPalmaStep"}, - {509, &Hid::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, - {510, &Hid::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, - {511, &Hid::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, - {512, &Hid::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, - {513, &Hid::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, - {514, &Hid::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, - {515, &Hid::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, - {516, &Hid::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, - {517, &Hid::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, - {518, &Hid::SuspendPalmaFeature, "SuspendPalmaFeature"}, - {519, &Hid::GetPalmaOperationResult, "GetPalmaOperationResult"}, - {520, &Hid::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, - {521, &Hid::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, - {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, - {523, &Hid::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, - {524, &Hid::PairPalma, "PairPalma"}, - {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"}, - {526, &Hid::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, - {527, &Hid::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, - {528, &Hid::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, - {529, &Hid::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, - {1000, &Hid::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, - {1001, &Hid::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, - {1002, &Hid::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, - {1003, &Hid::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, - {2000, nullptr, "ActivateDigitizer"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -Hid::~Hid() = default; - -void Hid::CreateAppletResource(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - if (applet_resource == nullptr) { - applet_resource = std::make_shared(system, service_context); - } - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(applet_resource); -} - -void Hid::ActivateDebugPad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->ActivateController(HidController::DebugPad); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateTouchScreen(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->ActivateController(HidController::Touchscreen); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateMouse(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->ActivateController(HidController::Mouse); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateKeyboard(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->ActivateController(HidController::Keyboard); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SendKeyboardLockKeyEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto flags{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateXpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 basic_xpad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - applet_resource->ActivateController(HidController::XPad); - - LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", - parameters.basic_xpad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetXpadIDs(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(0); -} - -void Hid::ActivateSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 basic_xpad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - // This function does nothing on 10.0.0+ - - LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", - parameters.basic_xpad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::DeactivateSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 basic_xpad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - // This function does nothing on 10.0.0+ - - LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", - parameters.basic_xpad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StartSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::StopSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - bool is_enabled{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = - controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(is_enabled); -} - -void Hid::EnableSixAxisSensorFusion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool enable_sixaxis_sensor_fusion; - INSERT_PADDING_BYTES_NOINIT(3); - Core::HID::SixAxisSensorHandle sixaxis_handle; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, - parameters.enable_sixaxis_sensor_fusion); - - LOG_DEBUG(Service_HID, - "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " - "device_index={}, applet_resource_user_id={}", - parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, - parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - Core::HID::SixAxisSensorFusionParameters sixaxis_fusion; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = - controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " - "parameter2={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1, - parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; - const auto& controller = - GetAppletResource()->GetController(HidController::NPad); - const auto result = - controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.PushRaw(fusion_parameters); -} - -void Hid::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - // Since these parameters are unknown just use what HW outputs - const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ - .parameter1 = 0.03f, - .parameter2 = 0.4f, - }; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result1 = - controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); - const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - if (result1.IsError()) { - rb.Push(result1); - return; - } - rb.Push(result2); -} - -void Hid::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto sixaxis_handle{rp.PopRaw()}; - const auto drift_mode{rp.PopEnum()}; - const auto applet_resource_user_id{rp.Pop()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " - "applet_resource_user_id={}", - sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, - drift_mode, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.PushEnum(drift_mode); -} - -void Hid::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsSixAxisSensorAtRest(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - bool is_at_rest{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_at_rest); -} - -void Hid::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - bool is_firmware_available{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, - is_firmware_available); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_firmware_available); -} - -void Hid::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool enabled; - Core::HID::SixAxisSensorHandle sixaxis_handle; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( - parameters.sixaxis_handle, parameters.enabled); - - LOG_DEBUG(Service_HID, - "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " - "applet_resource_user_id={}", - parameters.enabled, parameters.sixaxis_handle.npad_type, - parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - bool is_unaltered_sisxaxis_enabled{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( - parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); - - LOG_DEBUG( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(is_unaltered_sisxaxis_enabled); -} - -void Hid::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - Core::HID::SixAxisSensorCalibrationParameter calibration{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = - controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - if (result.IsSuccess()) { - ctx.WriteBuffer(calibration); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::GetSixAxisSensorIcInformation(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - Core::HID::SixAxisSensorIcInformation ic_information{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = - controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - if (result.IsSuccess()) { - ctx.WriteBuffer(ic_information); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::SixAxisSensorHandle sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = - controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); - - LOG_WARNING( - Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ActivateGesture(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - u32 unknown; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - applet_resource->ActivateController(HidController::Gesture); - - LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", - parameters.unknown, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetSupportedNpadStyleSet(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadStyleSet supported_styleset; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - applet_resource->GetController(HidController::NPad) - .SetSupportedStyleSet({parameters.supported_styleset}); - - LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", - parameters.supported_styleset, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetSupportedNpadStyleSet(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController(HidController::NPad) - .GetSupportedStyleSet() - .raw); -} - -void Hid::SetSupportedNpadIdType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - const auto result = applet_resource->GetController(HidController::NPad) - .SetSupportedNpadIdTypes(ctx.ReadBuffer()); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ActivateNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->ActivateController(HidController::NPad); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::DeactivateNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->DeactivateController(HidController::NPad); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - u64 unknown; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", - parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); - - // Games expect this event to be signaled after calling this function - applet_resource->GetController(HidController::NPad) - .SignalStyleSetChangedEvent(parameters.npad_id); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(applet_resource->GetController(HidController::NPad) - .GetStyleSetChangedEvent(parameters.npad_id)); -} - -void Hid::DisconnectNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - controller.DisconnectNpad(parameters.npad_id); - - LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPlayerLedPattern(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id{rp.PopEnum()}; - - Core::HID::LedPattern pattern{0, 0, 0, 0}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.GetLedPattern(npad_id, pattern); - - LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(pattern.raw); -} - -void Hid::ActivateNpadWithRevision(HLERequestContext& ctx) { - // Should have no effect with how our npad sets up the data - IPC::RequestParser rp{ctx}; - struct Parameters { - s32 revision; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - applet_resource->ActivateController(HidController::NPad); - - LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadJoyHoldType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - const auto hold_type{rp.PopEnum()}; - - applet_resource->GetController(HidController::NPad).SetHoldType(hold_type); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", - applet_resource_user_id, hold_type); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetNpadJoyHoldType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController(HidController::NPad).GetHoldType()); -} - -void Hid::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - controller.SetNpadMode(new_npad_id, parameters.npad_id, - Controller_NPad::NpadJoyDeviceType::Left, - Controller_NPad::NpadJoyAssignmentMode::Single); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Controller_NPad::NpadJoyDeviceType npad_joy_device_type; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", - parameters.npad_id, parameters.applet_resource_user_id, - parameters.npad_joy_device_type); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - controller.SetNpadMode(new_npad_id, parameters.npad_id, {}, - Controller_NPad::NpadJoyAssignmentMode::Dual); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, - parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id_1{rp.PopEnum()}; - const auto npad_id_2{rp.PopEnum()}; - const auto applet_resource_user_id{rp.Pop()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); - - LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", - npad_id_1, npad_id_2, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::StartLrAssignmentMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->GetController(HidController::NPad).StartLRAssignmentMode(); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StopLrAssignmentMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->GetController(HidController::NPad).StopLRAssignmentMode(); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadHandheldActivationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - const auto activation_mode{rp.PopEnum()}; - - applet_resource->GetController(HidController::NPad) - .SetNpadHandheldActivationMode(activation_mode); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", - applet_resource_user_id, activation_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetNpadHandheldActivationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController(HidController::NPad) - .GetNpadHandheldActivationMode()); -} - -void Hid::SwapNpadAssignment(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id_1{rp.PopEnum()}; - const auto npad_id_2{rp.PopEnum()}; - const auto applet_resource_user_id{rp.Pop()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); - - LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", - npad_id_1, npad_id_2, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - bool is_enabled = false; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = - controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); - - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", - parameters.npad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push(is_enabled); -} - -void Hid::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool is_enabled; - INSERT_PADDING_BYTES_NOINIT(3); - Core::HID::NpadIdType npad_id; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( - parameters.is_enabled, parameters.npad_id); - - LOG_DEBUG(Service_HID, - "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", - parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Controller_NPad::NpadJoyDeviceType npad_joy_device_type; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - Core::HID::NpadIdType new_npad_id{}; - auto& controller = GetAppletResource()->GetController(HidController::NPad); - const auto is_reassigned = - controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, - Controller_NPad::NpadJoyAssignmentMode::Single); - - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", - parameters.npad_id, parameters.applet_resource_user_id, - parameters.npad_joy_device_type); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.Push(is_reassigned); - rb.PushEnum(new_npad_id); -} - -void Hid::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool analog_stick_use_center_clamp; - INSERT_PADDING_BYTES_NOINIT(7); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - GetAppletResource() - ->GetController(HidController::NPad) - .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); - - LOG_WARNING(Service_HID, - "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", - parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadStyleSet npad_styleset; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::HID::NpadButton button; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", - parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetVibrationDeviceInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto vibration_device_handle{rp.PopRaw()}; - const auto& controller = - GetAppletResource()->GetController(HidController::NPad); - - Core::HID::VibrationDeviceInfo vibration_device_info; - bool check_device_index = false; - - switch (vibration_device_handle.npad_type) { - case Core::HID::NpadStyleIndex::ProController: - case Core::HID::NpadStyleIndex::Handheld: - case Core::HID::NpadStyleIndex::JoyconDual: - case Core::HID::NpadStyleIndex::JoyconLeft: - case Core::HID::NpadStyleIndex::JoyconRight: - vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; - check_device_index = true; - break; - case Core::HID::NpadStyleIndex::GameCube: - vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; - break; - case Core::HID::NpadStyleIndex::N64: - vibration_device_info.type = Core::HID::VibrationDeviceType::N64; - break; - default: - vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; - break; - } - - vibration_device_info.position = Core::HID::VibrationDevicePosition::None; - if (check_device_index) { - switch (vibration_device_handle.device_index) { - case Core::HID::DeviceIndex::Left: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; - break; - case Core::HID::DeviceIndex::Right: - vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; - break; - case Core::HID::DeviceIndex::None: - default: - ASSERT_MSG(false, "DeviceIndex should never be None!"); - break; - } - } - - LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", - vibration_device_info.type, vibration_device_info.position); - - const auto result = controller.IsDeviceHandleValid(vibration_device_handle); - if (result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushRaw(vibration_device_info); -} - -void Hid::SendVibrationValue(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - Core::HID::VibrationValue vibration_value; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - applet_resource->GetController(HidController::NPad) - .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetActualVibrationValue(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(applet_resource->GetController(HidController::NPad) - .GetLastVibration(parameters.vibration_device_handle)); -} - -void Hid::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - rb.Push(ResultSuccess); - rb.PushIpcInterface(system, applet_resource); -} - -void Hid::PermitVibration(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto can_vibrate{rp.Pop()}; - - // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value - // by converting it to a bool - Settings::values.vibration_enabled.SetValue(can_vibrate); - - LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsVibrationPermitted(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "called"); - - // nnSDK checks if a float is greater than zero. We return the bool we stored earlier - const auto is_enabled = Settings::values.vibration_enabled.GetValue(); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_enabled); -} - -void Hid::SendVibrationValues(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - const auto handle_data = ctx.ReadBuffer(0); - const auto handle_count = ctx.GetReadBufferNumElements(0); - const auto vibration_data = ctx.ReadBuffer(1); - const auto vibration_count = ctx.GetReadBufferNumElements(1); - - auto vibration_device_handles = - std::span(reinterpret_cast(handle_data.data()), - handle_count); - auto vibration_values = std::span( - reinterpret_cast(vibration_data.data()), vibration_count); - - applet_resource->GetController(HidController::NPad) - .VibrateControllers(vibration_device_handles, vibration_values); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SendVibrationGcErmCommand(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::HID::VibrationGcErmCommand gc_erm_command; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - /** - * Note: This uses yuzu-specific behavior such that the StopHard command produces - * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, - * in order to differentiate between Stop and StopHard commands. - * This is done to reuse the controller vibration functions made for regular controllers. - */ - const auto vibration_value = [parameters] { - switch (parameters.gc_erm_command) { - case Core::HID::VibrationGcErmCommand::Stop: - return Core::HID::VibrationValue{ - .low_amplitude = 0.0f, - .low_frequency = 160.0f, - .high_amplitude = 0.0f, - .high_frequency = 320.0f, - }; - case Core::HID::VibrationGcErmCommand::Start: - return Core::HID::VibrationValue{ - .low_amplitude = 1.0f, - .low_frequency = 160.0f, - .high_amplitude = 1.0f, - .high_frequency = 320.0f, - }; - case Core::HID::VibrationGcErmCommand::StopHard: - return Core::HID::VibrationValue{ - .low_amplitude = 0.0f, - .low_frequency = 0.0f, - .high_amplitude = 0.0f, - .high_frequency = 0.0f, - }; - default: - return Core::HID::DEFAULT_VIBRATION_VALUE; - } - }(); - - applet_resource->GetController(HidController::NPad) - .VibrateController(parameters.vibration_device_handle, vibration_value); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " - "gc_erm_command={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, - parameters.gc_erm_command); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - - const auto parameters{rp.PopRaw()}; - - const auto last_vibration = applet_resource->GetController(HidController::NPad) - .GetLastVibration(parameters.vibration_device_handle); - - const auto gc_erm_command = [last_vibration] { - if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { - return Core::HID::VibrationGcErmCommand::Start; - } - - /** - * Note: This uses yuzu-specific behavior such that the StopHard command produces - * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function - * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. - * This is done to reuse the controller vibration functions made for regular controllers. - */ - if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { - return Core::HID::VibrationGcErmCommand::StopHard; - } - - return Core::HID::VibrationGcErmCommand::Stop; - }(); - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(gc_erm_command); -} - -void Hid::BeginPermitVibrationSession(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->GetController(HidController::NPad) - .SetPermitVibrationSession(true); - - LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::EndPermitVibrationSession(HLERequestContext& ctx) { - applet_resource->GetController(HidController::NPad) - .SetPermitVibrationSession(false); - - LOG_DEBUG(Service_HID, "called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsVibrationDeviceMounted(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::VibrationDeviceHandle vibration_device_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_DEBUG(Service_HID, - "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", - parameters.vibration_device_handle.npad_type, - parameters.vibration_device_handle.npad_id, - parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(applet_resource->GetController(HidController::NPad) - .IsVibrationDeviceMounted(parameters.vibration_device_handle)); -} - -void Hid::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->ActivateController(HidController::ConsoleSixAxisSensor); - - LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StartConsoleSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", - parameters.console_sixaxis_handle.unknown_1, - parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StopConsoleSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", - parameters.console_sixaxis_handle.unknown_1, - parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ActivateSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->ActivateController(HidController::ConsoleSixAxisSensor); - - LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StartSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::StopSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - const auto t_mem_1_size{rp.Pop()}; - const auto t_mem_2_size{rp.Pop()}; - const auto t_mem_1_handle{ctx.GetCopyHandle(0)}; - const auto t_mem_2_handle{ctx.GetCopyHandle(1)}; - - ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); - ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); - - auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_1_handle); - - if (t_mem_1.IsNull()) { - LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_2_handle); - - if (t_mem_2.IsNull()) { - LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size"); - ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); - - // Activate console six axis controller - applet_resource->GetController(HidController::ConsoleSixAxisSensor) - .ActivateController(); - - applet_resource->GetController(HidController::ConsoleSixAxisSensor) - .SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); - - LOG_WARNING(Service_HID, - "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " - "applet_resource_user_id={}", - t_mem_1_handle, t_mem_2_handle, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - - applet_resource->GetController(HidController::ConsoleSixAxisSensor) - .ResetTimestamp(); - - LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(false); -} - -void Hid::GetPalmaConnectionHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::HID::NpadIdType npad_id; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", - parameters.npad_id, parameters.applet_resource_user_id); - - Controller_Palma::PalmaConnectionHandle handle; - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.PushRaw(handle); -} - -void Hid::InitializePalma(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = controller.InitializePalma(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController(HidController::Palma); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); -} - -void Hid::GetPalmaOperationInfo(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - Controller_Palma::PalmaOperationType operation_type; - Controller_Palma::PalmaOperationData data; - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); - - if (result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - } - - ctx.WriteBuffer(data); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push(static_cast(operation_type)); -} - -void Hid::PlayPalmaActivity(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - const auto palma_activity{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", - connection_handle.npad_id, palma_activity); - - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::SetPalmaFrModeType(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - const auto fr_mode{rp.PopEnum()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", - connection_handle.npad_id, fr_mode); - - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ReadPalmaStep(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = controller.ReadPalmaStep(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::EnablePalmaStep(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool is_enabled; - INSERT_PADDING_WORDS_NOINIT(1); - Controller_Palma::PalmaConnectionHandle connection_handle; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", - parameters.connection_handle.npad_id, parameters.is_enabled); - - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = - controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ResetPalmaStep(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - auto& controller = GetAppletResource()->GetController(HidController::Palma); - const auto result = controller.ResetPalmaStep(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ReadPalmaApplicationSection(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaApplicationSection(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ReadPalmaUniqueCode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController(HidController::Palma) - .ReadPalmaUniqueCode(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController(HidController::Palma) - .SetPalmaUniqueCodeInvalid(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaActivityEntry(HLERequestContext& ctx) { - LOG_CRITICAL(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - const auto unknown{rp.Pop()}; - - [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", - connection_handle.npad_id, unknown); - - applet_resource->GetController(HidController::Palma) - .WritePalmaRgbLedPatternEntry(connection_handle, unknown); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::WritePalmaWaveEntry(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - const auto wave_set{rp.PopEnum()}; - const auto unknown{rp.Pop()}; - const auto t_mem_size{rp.Pop()}; - const auto t_mem_handle{ctx.GetCopyHandle(0)}; - const auto size{rp.Pop()}; - - ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); - - auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject( - t_mem_handle); - - if (t_mem.IsNull()) { - LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); - - LOG_WARNING(Service_HID, - "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " - "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", - connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); - - applet_resource->GetController(HidController::Palma) - .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - s32 database_id_version; - INSERT_PADDING_WORDS_NOINIT(1); - Controller_Palma::PalmaConnectionHandle connection_handle; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", - parameters.connection_handle.npad_id, parameters.database_id_version); - - applet_resource->GetController(HidController::Palma) - .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, - parameters.database_id_version); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController(HidController::Palma) - .GetPalmaDataBaseIdentificationVersion(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SuspendPalmaFeature(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPalmaOperationResult(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - const auto result = applet_resource->GetController(HidController::Palma) - .GetPalmaOperationResult(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void Hid::ReadPalmaPlayLog(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::ResetPalmaPlayLog(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetIsPalmaAllConnectable(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - bool is_palma_all_connectable; - INSERT_PADDING_BYTES_NOINIT(7); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, - "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", - parameters.is_palma_all_connectable, parameters.applet_resource_user_id); - - applet_resource->GetController(HidController::Palma) - .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetIsPalmaPairedConnectable(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::PairPalma(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto connection_handle{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); - - applet_resource->GetController(HidController::Palma) - .PairPalma(connection_handle); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetPalmaBoostMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto palma_boost_mode{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); - - applet_resource->GetController(HidController::Palma) - .SetPalmaBoostMode(palma_boost_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::CancelWritePalmaWaveEntry(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::EnablePalmaBoostMode(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetPalmaBluetoothAddress(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetDisallowedPalmaConnection(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::SetNpadCommunicationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop()}; - const auto communication_mode{rp.PopEnum()}; - - applet_resource->GetController(HidController::NPad) - .SetNpadCommunicationMode(communication_mode); - - LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", - applet_resource_user_id, communication_mode); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::GetNpadCommunicationMode(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - - LOG_WARNING(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(ResultSuccess); - rb.PushEnum(applet_resource->GetController(HidController::NPad) - .GetNpadCommunicationMode()); -} - -void Hid::SetTouchScreenConfiguration(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto touchscreen_mode{rp.PopRaw()}; - const auto applet_resource_user_id{rp.Pop()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", - touchscreen_mode.mode, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); -} - -void Hid::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - s32 unknown; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", - parameters.unknown, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(false); -} - -class HidDbg final : public ServiceFramework { -public: - explicit HidDbg(Core::System& system_) : ServiceFramework{system_, "hid:dbg"} { - // clang-format off - static const FunctionInfo functions[] = { - {0, nullptr, "DeactivateDebugPad"}, - {1, nullptr, "SetDebugPadAutoPilotState"}, - {2, nullptr, "UnsetDebugPadAutoPilotState"}, - {10, nullptr, "DeactivateTouchScreen"}, - {11, nullptr, "SetTouchScreenAutoPilotState"}, - {12, nullptr, "UnsetTouchScreenAutoPilotState"}, - {13, nullptr, "GetTouchScreenConfiguration"}, - {14, nullptr, "ProcessTouchScreenAutoTune"}, - {15, nullptr, "ForceStopTouchScreenManagement"}, - {16, nullptr, "ForceRestartTouchScreenManagement"}, - {17, nullptr, "IsTouchScreenManaged"}, - {20, nullptr, "DeactivateMouse"}, - {21, nullptr, "SetMouseAutoPilotState"}, - {22, nullptr, "UnsetMouseAutoPilotState"}, - {25, nullptr, "SetDebugMouseAutoPilotState"}, - {26, nullptr, "UnsetDebugMouseAutoPilotState"}, - {30, nullptr, "DeactivateKeyboard"}, - {31, nullptr, "SetKeyboardAutoPilotState"}, - {32, nullptr, "UnsetKeyboardAutoPilotState"}, - {50, nullptr, "DeactivateXpad"}, - {51, nullptr, "SetXpadAutoPilotState"}, - {52, nullptr, "UnsetXpadAutoPilotState"}, - {53, nullptr, "DeactivateJoyXpad"}, - {60, nullptr, "ClearNpadSystemCommonPolicy"}, - {61, nullptr, "DeactivateNpad"}, - {62, nullptr, "ForceDisconnectNpad"}, - {91, nullptr, "DeactivateGesture"}, - {110, nullptr, "DeactivateHomeButton"}, - {111, nullptr, "SetHomeButtonAutoPilotState"}, - {112, nullptr, "UnsetHomeButtonAutoPilotState"}, - {120, nullptr, "DeactivateSleepButton"}, - {121, nullptr, "SetSleepButtonAutoPilotState"}, - {122, nullptr, "UnsetSleepButtonAutoPilotState"}, - {123, nullptr, "DeactivateInputDetector"}, - {130, nullptr, "DeactivateCaptureButton"}, - {131, nullptr, "SetCaptureButtonAutoPilotState"}, - {132, nullptr, "UnsetCaptureButtonAutoPilotState"}, - {133, nullptr, "SetShiftAccelerometerCalibrationValue"}, - {134, nullptr, "GetShiftAccelerometerCalibrationValue"}, - {135, nullptr, "SetShiftGyroscopeCalibrationValue"}, - {136, nullptr, "GetShiftGyroscopeCalibrationValue"}, - {140, nullptr, "DeactivateConsoleSixAxisSensor"}, - {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, - {142, nullptr, "DeactivateSevenSixAxisSensor"}, - {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, - {144, nullptr, "GetAccelerometerFsr"}, - {145, nullptr, "SetAccelerometerFsr"}, - {146, nullptr, "GetAccelerometerOdr"}, - {147, nullptr, "SetAccelerometerOdr"}, - {148, nullptr, "GetGyroscopeFsr"}, - {149, nullptr, "SetGyroscopeFsr"}, - {150, nullptr, "GetGyroscopeOdr"}, - {151, nullptr, "SetGyroscopeOdr"}, - {152, nullptr, "GetWhoAmI"}, - {201, nullptr, "ActivateFirmwareUpdate"}, - {202, nullptr, "DeactivateFirmwareUpdate"}, - {203, nullptr, "StartFirmwareUpdate"}, - {204, nullptr, "GetFirmwareUpdateStage"}, - {205, nullptr, "GetFirmwareVersion"}, - {206, nullptr, "GetDestinationFirmwareVersion"}, - {207, nullptr, "DiscardFirmwareInfoCacheForRevert"}, - {208, nullptr, "StartFirmwareUpdateForRevert"}, - {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, - {210, nullptr, "IsFirmwareUpdatingDevice"}, - {211, nullptr, "StartFirmwareUpdateIndividual"}, - {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, - {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, - {221, nullptr, "UpdateControllerColor"}, - {222, nullptr, "ConnectUsbPadsAsync"}, - {223, nullptr, "DisconnectUsbPadsAsync"}, - {224, nullptr, "UpdateDesignInfo"}, - {225, nullptr, "GetUniquePadDriverState"}, - {226, nullptr, "GetSixAxisSensorDriverStates"}, - {227, nullptr, "GetRxPacketHistory"}, - {228, nullptr, "AcquireOperationEventHandle"}, - {229, nullptr, "ReadSerialFlash"}, - {230, nullptr, "WriteSerialFlash"}, - {231, nullptr, "GetOperationResult"}, - {232, nullptr, "EnableShipmentMode"}, - {233, nullptr, "ClearPairingInfo"}, - {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, - {235, nullptr, "EnableAnalogStickPower"}, - {236, nullptr, "RequestKuinaUartClockCal"}, - {237, nullptr, "GetKuinaUartClockCal"}, - {238, nullptr, "SetKuinaUartClockTrim"}, - {239, nullptr, "KuinaLoopbackTest"}, - {240, nullptr, "RequestBatteryVoltage"}, - {241, nullptr, "GetBatteryVoltage"}, - {242, nullptr, "GetUniquePadPowerInfo"}, - {243, nullptr, "RebootUniquePad"}, - {244, nullptr, "RequestKuinaFirmwareVersion"}, - {245, nullptr, "GetKuinaFirmwareVersion"}, - {246, nullptr, "GetVidPid"}, - {247, nullptr, "GetAnalogStickCalibrationValue"}, - {248, nullptr, "GetUniquePadIdsFull"}, - {249, nullptr, "ConnectUniquePad"}, - {250, nullptr, "IsVirtual"}, - {251, nullptr, "GetAnalogStickModuleParam"}, - {301, nullptr, "GetAbstractedPadHandles"}, - {302, nullptr, "GetAbstractedPadState"}, - {303, nullptr, "GetAbstractedPadsState"}, - {321, nullptr, "SetAutoPilotVirtualPadState"}, - {322, nullptr, "UnsetAutoPilotVirtualPadState"}, - {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, - {324, nullptr, "AttachHdlsWorkBuffer"}, - {325, nullptr, "ReleaseHdlsWorkBuffer"}, - {326, nullptr, "DumpHdlsNpadAssignmentState"}, - {327, nullptr, "DumpHdlsStates"}, - {328, nullptr, "ApplyHdlsNpadAssignmentState"}, - {329, nullptr, "ApplyHdlsStateList"}, - {330, nullptr, "AttachHdlsVirtualDevice"}, - {331, nullptr, "DetachHdlsVirtualDevice"}, - {332, nullptr, "SetHdlsState"}, - {350, nullptr, "AddRegisteredDevice"}, - {400, nullptr, "DisableExternalMcuOnNxDevice"}, - {401, nullptr, "DisableRailDeviceFiltering"}, - {402, nullptr, "EnableWiredPairing"}, - {403, nullptr, "EnableShipmentModeAutoClear"}, - {404, nullptr, "SetRailEnabled"}, - {500, nullptr, "SetFactoryInt"}, - {501, nullptr, "IsFactoryBootEnabled"}, - {550, nullptr, "SetAnalogStickModelDataTemporarily"}, - {551, nullptr, "GetAnalogStickModelData"}, - {552, nullptr, "ResetAnalogStickModelData"}, - {600, nullptr, "ConvertPadState"}, - {650, nullptr, "AddButtonPlayData"}, - {651, nullptr, "StartButtonPlayData"}, - {652, nullptr, "StopButtonPlayData"}, - {2000, nullptr, "DeactivateDigitizer"}, - {2001, nullptr, "SetDigitizerAutoPilotState"}, - {2002, nullptr, "UnsetDigitizerAutoPilotState"}, - {2002, nullptr, "ReloadFirmwareDebugSettings"}, - }; - // clang-format on - - RegisterHandlers(functions); - } -}; - -class HidSys final : public ServiceFramework { -public: - explicit HidSys(Core::System& system_, std::shared_ptr applet_resource_) - : ServiceFramework{system_, "hid:sys"}, service_context{system_, "hid:sys"}, - applet_resource{applet_resource_} { - // clang-format off - static const FunctionInfo functions[] = { - {31, nullptr, "SendKeyboardLockKeyEvent"}, - {101, nullptr, "AcquireHomeButtonEventHandle"}, - {111, nullptr, "ActivateHomeButton"}, - {121, nullptr, "AcquireSleepButtonEventHandle"}, - {131, nullptr, "ActivateSleepButton"}, - {141, nullptr, "AcquireCaptureButtonEventHandle"}, - {151, nullptr, "ActivateCaptureButton"}, - {161, nullptr, "GetPlatformConfig"}, - {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, - {211, nullptr, "GetNpadsWithNfc"}, - {212, nullptr, "AcquireNfcActivateEventHandle"}, - {213, nullptr, "ActivateNfc"}, - {214, nullptr, "GetXcdHandleForNpadWithNfc"}, - {215, nullptr, "IsNfcActivated"}, - {230, nullptr, "AcquireIrSensorEventHandle"}, - {231, nullptr, "ActivateIrSensor"}, - {232, nullptr, "GetIrSensorState"}, - {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, - {301, nullptr, "ActivateNpadSystem"}, - {303, &HidSys::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, - {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, - {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, - {306, &HidSys::GetLastActiveNpad, "GetLastActiveNpad"}, - {307, nullptr, "GetNpadSystemExtStyle"}, - {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, - {309, nullptr, "GetNpadFullKeyGripColor"}, - {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, - {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, - {312, nullptr, "SetSupportedNpadStyleSetAll"}, - {313, nullptr, "GetNpadCaptureButtonAssignment"}, - {314, nullptr, "GetAppletFooterUiType"}, - {315, nullptr, "GetAppletDetailedUiType"}, - {316, nullptr, "GetNpadInterfaceType"}, - {317, nullptr, "GetNpadLeftRightInterfaceType"}, - {318, nullptr, "HasBattery"}, - {319, nullptr, "HasLeftRightBattery"}, - {321, &HidSys::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, - {322, nullptr, "GetIrSensorState"}, - {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, - {324, nullptr, "GetUniquePadButtonSet"}, - {325, nullptr, "GetUniquePadColor"}, - {326, nullptr, "GetUniquePadAppletDetailedUiType"}, - {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, - {328, nullptr, "AttachAbstractedPadToNpad"}, - {329, nullptr, "DetachAbstractedPadAll"}, - {330, nullptr, "CheckAbstractedPadConnection"}, - {500, nullptr, "SetAppletResourceUserId"}, - {501, nullptr, "RegisterAppletResourceUserId"}, - {502, nullptr, "UnregisterAppletResourceUserId"}, - {503, nullptr, "EnableAppletToGetInput"}, - {504, nullptr, "SetAruidValidForVibration"}, - {505, nullptr, "EnableAppletToGetSixAxisSensor"}, - {506, nullptr, "EnableAppletToGetPadInput"}, - {507, nullptr, "EnableAppletToGetTouchScreen"}, - {510, nullptr, "SetVibrationMasterVolume"}, - {511, nullptr, "GetVibrationMasterVolume"}, - {512, nullptr, "BeginPermitVibrationSession"}, - {513, nullptr, "EndPermitVibrationSession"}, - {514, nullptr, "Unknown514"}, - {520, nullptr, "EnableHandheldHids"}, - {521, nullptr, "DisableHandheldHids"}, - {522, nullptr, "SetJoyConRailEnabled"}, - {523, nullptr, "IsJoyConRailEnabled"}, - {524, nullptr, "IsHandheldHidsEnabled"}, - {525, nullptr, "IsJoyConAttachedOnAllRail"}, - {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, - {541, nullptr, "GetPlayReportControllerUsages"}, - {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, - {543, nullptr, "GetRegisteredDevicesOld"}, - {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"}, - {545, nullptr, "SendConnectionTrigger"}, - {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, - {547, nullptr, "GetAllowedBluetoothLinksCount"}, - {548, nullptr, "GetRegisteredDevices"}, - {549, nullptr, "GetConnectableRegisteredDevices"}, - {700, nullptr, "ActivateUniquePad"}, - {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, - {703, nullptr, "GetUniquePadIds"}, - {751, &HidSys::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, - {800, nullptr, "ListSixAxisSensorHandles"}, - {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, - {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, - {803, nullptr, "StartSixAxisSensorUserCalibration"}, - {804, nullptr, "CancelSixAxisSensorUserCalibration"}, - {805, nullptr, "GetUniquePadBluetoothAddress"}, - {806, nullptr, "DisconnectUniquePad"}, - {807, nullptr, "GetUniquePadType"}, - {808, nullptr, "GetUniquePadInterface"}, - {809, nullptr, "GetUniquePadSerialNumber"}, - {810, nullptr, "GetUniquePadControllerNumber"}, - {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, - {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, - {821, nullptr, "StartAnalogStickManualCalibration"}, - {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, - {823, nullptr, "CancelAnalogStickManualCalibration"}, - {824, nullptr, "ResetAnalogStickManualCalibration"}, - {825, nullptr, "GetAnalogStickState"}, - {826, nullptr, "GetAnalogStickManualCalibrationStage"}, - {827, nullptr, "IsAnalogStickButtonPressed"}, - {828, nullptr, "IsAnalogStickInReleasePosition"}, - {829, nullptr, "IsAnalogStickInCircumference"}, - {830, nullptr, "SetNotificationLedPattern"}, - {831, nullptr, "SetNotificationLedPatternWithTimeout"}, - {832, nullptr, "PrepareHidsForNotificationWake"}, - {850, &HidSys::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, - {851, nullptr, "EnableUsbFullKeyController"}, - {852, nullptr, "IsUsbConnected"}, - {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, - {900, nullptr, "ActivateInputDetector"}, - {901, nullptr, "NotifyInputDetector"}, - {1000, nullptr, "InitializeFirmwareUpdate"}, - {1001, nullptr, "GetFirmwareVersion"}, - {1002, nullptr, "GetAvailableFirmwareVersion"}, - {1003, nullptr, "IsFirmwareUpdateAvailable"}, - {1004, nullptr, "CheckFirmwareUpdateRequired"}, - {1005, nullptr, "StartFirmwareUpdate"}, - {1006, nullptr, "AbortFirmwareUpdate"}, - {1007, nullptr, "GetFirmwareUpdateState"}, - {1008, nullptr, "ActivateAudioControl"}, - {1009, nullptr, "AcquireAudioControlEventHandle"}, - {1010, nullptr, "GetAudioControlStates"}, - {1011, nullptr, "DeactivateAudioControl"}, - {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, - {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, - {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, - {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, - {1100, nullptr, "GetHidbusSystemServiceObject"}, - {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, - {1130, nullptr, "InitializeUsbFirmwareUpdate"}, - {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, - {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, - {1133, nullptr, "StartUsbFirmwareUpdate"}, - {1134, nullptr, "GetUsbFirmwareUpdateState"}, - {1150, nullptr, "SetTouchScreenMagnification"}, - {1151, nullptr, "GetTouchScreenFirmwareVersion"}, - {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, - {1153, &HidSys::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, - {1154, nullptr, "IsFirmwareAvailableForNotification"}, - {1155, nullptr, "SetForceHandheldStyleVibration"}, - {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, - {1157, nullptr, "CancelConnectionTrigger"}, - {1200, nullptr, "IsButtonConfigSupported"}, - {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, - {1202, nullptr, "DeleteButtonConfig"}, - {1203, nullptr, "DeleteButtonConfigEmbedded"}, - {1204, nullptr, "SetButtonConfigEnabled"}, - {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, - {1206, nullptr, "IsButtonConfigEnabled"}, - {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, - {1208, nullptr, "SetButtonConfigEmbedded"}, - {1209, nullptr, "SetButtonConfigFull"}, - {1210, nullptr, "SetButtonConfigLeft"}, - {1211, nullptr, "SetButtonConfigRight"}, - {1212, nullptr, "GetButtonConfigEmbedded"}, - {1213, nullptr, "GetButtonConfigFull"}, - {1214, nullptr, "GetButtonConfigLeft"}, - {1215, nullptr, "GetButtonConfigRight"}, - {1250, nullptr, "IsCustomButtonConfigSupported"}, - {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, - {1252, nullptr, "IsDefaultButtonConfigFull"}, - {1253, nullptr, "IsDefaultButtonConfigLeft"}, - {1254, nullptr, "IsDefaultButtonConfigRight"}, - {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, - {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, - {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, - {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, - {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, - {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, - {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, - {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, - {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, - {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, - {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, - {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, - {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, - {1268, nullptr, "DeleteButtonConfigStorageFull"}, - {1269, nullptr, "DeleteButtonConfigStorageLeft"}, - {1270, nullptr, "DeleteButtonConfigStorageRight"}, - {1271, nullptr, "IsUsingCustomButtonConfig"}, - {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, - {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, - {1274, nullptr, "SetDefaultButtonConfig"}, - {1275, nullptr, "SetAllDefaultButtonConfig"}, - {1276, nullptr, "SetHidButtonConfigEmbedded"}, - {1277, nullptr, "SetHidButtonConfigFull"}, - {1278, nullptr, "SetHidButtonConfigLeft"}, - {1279, nullptr, "SetHidButtonConfigRight"}, - {1280, nullptr, "GetHidButtonConfigEmbedded"}, - {1281, nullptr, "GetHidButtonConfigFull"}, - {1282, nullptr, "GetHidButtonConfigLeft"}, - {1283, nullptr, "GetHidButtonConfigRight"}, - {1284, nullptr, "GetButtonConfigStorageEmbedded"}, - {1285, nullptr, "GetButtonConfigStorageFull"}, - {1286, nullptr, "GetButtonConfigStorageLeft"}, - {1287, nullptr, "GetButtonConfigStorageRight"}, - {1288, nullptr, "SetButtonConfigStorageEmbedded"}, - {1289, nullptr, "SetButtonConfigStorageFull"}, - {1290, nullptr, "DeleteButtonConfigStorageRight"}, - {1291, nullptr, "DeleteButtonConfigStorageRight"}, - }; - // clang-format on - - RegisterHandlers(functions); - - joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); - } - - ~HidSys() { - service_context.CloseEvent(joy_detach_event); - }; - -private: - void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "called"); - - GetAppletResource() - ->GetController(HidController::NPad) - .ApplyNpadSystemCommonPolicy(); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - } - - void GetLastActiveNpad(HLERequestContext& ctx) { - LOG_DEBUG(Service_HID, "(STUBBED) called"); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushEnum(system.HIDCore().GetLastActiveController()); - } - - void GetUniquePadsFromNpad(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id_type{rp.PopEnum()}; - - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); - - const std::vector unique_pads{}; - - ctx.WriteBuffer(unique_pads); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast(unique_pads.size())); - } - - void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { - LOG_INFO(Service_AM, "called"); - - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(joy_detach_event->GetReadableEvent()); - } - - void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { - const bool is_enabled = false; - - LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(is_enabled); - } - - void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); - - Core::HID::TouchScreenConfigurationForNx touchscreen_config{ - .mode = Core::HID::TouchScreenModeForNx::Finger, - }; - - if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && - touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { - touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; - } - - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(touchscreen_config); - } - - std::shared_ptr GetAppletResource() { - if (applet_resource == nullptr) { - applet_resource = std::make_shared(system, service_context); - } - - return applet_resource; - } - - Kernel::KEvent* joy_detach_event; - KernelHelpers::ServiceContext service_context; - std::shared_ptr applet_resource; -}; +namespace Service::HID { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique(system); - std::shared_ptr applet_resource; + std::shared_ptr resouce_manager = std::make_shared(system); + + server_manager->RegisterNamedService("hid", + std::make_shared(system, resouce_manager)); + server_manager->RegisterNamedService( + "hid:dbg", std::make_shared(system, resouce_manager)); + server_manager->RegisterNamedService( + "hid:sys", std::make_shared(system, resouce_manager)); - server_manager->RegisterNamedService("hid", std::make_shared(system, applet_resource)); server_manager->RegisterNamedService("hidbus", std::make_shared(system)); - server_manager->RegisterNamedService("hid:dbg", std::make_shared(system)); - server_manager->RegisterNamedService("hid:sys", - std::make_shared(system, applet_resource)); - server_manager->RegisterNamedService("irs", std::make_shared(system)); - server_manager->RegisterNamedService("irs:sys", - std::make_shared(system)); + server_manager->RegisterNamedService("irs", std::make_shared(system)); + server_manager->RegisterNamedService("irs:sys", std::make_shared(system)); server_manager->RegisterNamedService("xcd:sys", std::make_shared(system)); + system.RunServer(std::move(server_manager)); } diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 0ca43de93..ec5463f4e 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -3,220 +3,12 @@ #pragma once -#include - -#include "core/hle/service/hid/controllers/controller_base.h" -#include "core/hle/service/kernel_helpers.h" -#include "core/hle/service/service.h" - -namespace Core::Timing { -struct EventType; -} - -namespace Service::SM { -class ServiceManager; +namespace Core { +class System; } namespace Service::HID { -enum class HidController : std::size_t { - DebugPad, - Touchscreen, - Mouse, - Keyboard, - XPad, - HomeButton, - SleepButton, - CaptureButton, - InputDetector, - UniquePad, - NPad, - Gesture, - ConsoleSixAxisSensor, - DebugMouse, - Palma, - - MaxControllers, -}; - -class IAppletResource final : public ServiceFramework { -public: - explicit IAppletResource(Core::System& system_, - KernelHelpers::ServiceContext& service_context_); - ~IAppletResource() override; - - void ActivateController(HidController controller); - void DeactivateController(HidController controller); - - template - T& GetController(HidController controller) { - return static_cast(*controllers[static_cast(controller)]); - } - - template - const T& GetController(HidController controller) const { - return static_cast(*controllers[static_cast(controller)]); - } - -private: - template - void MakeController(HidController controller, u8* shared_memory) { - if constexpr (std::is_constructible_v) { - controllers[static_cast(controller)] = - std::make_unique(system, shared_memory); - } else { - controllers[static_cast(controller)] = - std::make_unique(system.HIDCore(), shared_memory); - } - } - - template - void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) { - controllers[static_cast(controller)] = - std::make_unique(system.HIDCore(), shared_memory, service_context); - } - - void GetSharedMemoryHandle(HLERequestContext& ctx); - void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); - - KernelHelpers::ServiceContext& service_context; - - std::shared_ptr npad_update_event; - std::shared_ptr default_update_event; - std::shared_ptr mouse_keyboard_update_event; - std::shared_ptr motion_update_event; - - std::array, static_cast(HidController::MaxControllers)> - controllers{}; -}; - -class Hid final : public ServiceFramework { -public: - explicit Hid(Core::System& system_, std::shared_ptr applet_resource_); - ~Hid() override; - - std::shared_ptr GetAppletResource(); - -private: - void CreateAppletResource(HLERequestContext& ctx); - void ActivateDebugPad(HLERequestContext& ctx); - void ActivateTouchScreen(HLERequestContext& ctx); - void ActivateMouse(HLERequestContext& ctx); - void ActivateKeyboard(HLERequestContext& ctx); - void SendKeyboardLockKeyEvent(HLERequestContext& ctx); - void ActivateXpad(HLERequestContext& ctx); - void GetXpadIDs(HLERequestContext& ctx); - void ActivateSixAxisSensor(HLERequestContext& ctx); - void DeactivateSixAxisSensor(HLERequestContext& ctx); - void StartSixAxisSensor(HLERequestContext& ctx); - void StopSixAxisSensor(HLERequestContext& ctx); - void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx); - void EnableSixAxisSensorFusion(HLERequestContext& ctx); - void SetSixAxisSensorFusionParameters(HLERequestContext& ctx); - void GetSixAxisSensorFusionParameters(HLERequestContext& ctx); - void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx); - void SetGyroscopeZeroDriftMode(HLERequestContext& ctx); - void GetGyroscopeZeroDriftMode(HLERequestContext& ctx); - void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx); - void IsSixAxisSensorAtRest(HLERequestContext& ctx); - void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx); - void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx); - void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx); - void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx); - void GetSixAxisSensorIcInformation(HLERequestContext& ctx); - void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx); - void ActivateGesture(HLERequestContext& ctx); - void SetSupportedNpadStyleSet(HLERequestContext& ctx); - void GetSupportedNpadStyleSet(HLERequestContext& ctx); - void SetSupportedNpadIdType(HLERequestContext& ctx); - void ActivateNpad(HLERequestContext& ctx); - void DeactivateNpad(HLERequestContext& ctx); - void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx); - void DisconnectNpad(HLERequestContext& ctx); - void GetPlayerLedPattern(HLERequestContext& ctx); - void ActivateNpadWithRevision(HLERequestContext& ctx); - void SetNpadJoyHoldType(HLERequestContext& ctx); - void GetNpadJoyHoldType(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx); - void MergeSingleJoyAsDualJoy(HLERequestContext& ctx); - void StartLrAssignmentMode(HLERequestContext& ctx); - void StopLrAssignmentMode(HLERequestContext& ctx); - void SetNpadHandheldActivationMode(HLERequestContext& ctx); - void GetNpadHandheldActivationMode(HLERequestContext& ctx); - void SwapNpadAssignment(HLERequestContext& ctx); - void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx); - void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx); - void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx); - void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx); - void SetNpadCaptureButtonAssignment(HLERequestContext& ctx); - void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx); - void GetVibrationDeviceInfo(HLERequestContext& ctx); - void SendVibrationValue(HLERequestContext& ctx); - void GetActualVibrationValue(HLERequestContext& ctx); - void CreateActiveVibrationDeviceList(HLERequestContext& ctx); - void PermitVibration(HLERequestContext& ctx); - void IsVibrationPermitted(HLERequestContext& ctx); - void SendVibrationValues(HLERequestContext& ctx); - void SendVibrationGcErmCommand(HLERequestContext& ctx); - void GetActualVibrationGcErmCommand(HLERequestContext& ctx); - void BeginPermitVibrationSession(HLERequestContext& ctx); - void EndPermitVibrationSession(HLERequestContext& ctx); - void IsVibrationDeviceMounted(HLERequestContext& ctx); - void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); - void StartConsoleSixAxisSensor(HLERequestContext& ctx); - void StopConsoleSixAxisSensor(HLERequestContext& ctx); - void ActivateSevenSixAxisSensor(HLERequestContext& ctx); - void StartSevenSixAxisSensor(HLERequestContext& ctx); - void StopSevenSixAxisSensor(HLERequestContext& ctx); - void InitializeSevenSixAxisSensor(HLERequestContext& ctx); - void FinalizeSevenSixAxisSensor(HLERequestContext& ctx); - void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx); - void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); - void GetPalmaConnectionHandle(HLERequestContext& ctx); - void InitializePalma(HLERequestContext& ctx); - void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx); - void GetPalmaOperationInfo(HLERequestContext& ctx); - void PlayPalmaActivity(HLERequestContext& ctx); - void SetPalmaFrModeType(HLERequestContext& ctx); - void ReadPalmaStep(HLERequestContext& ctx); - void EnablePalmaStep(HLERequestContext& ctx); - void ResetPalmaStep(HLERequestContext& ctx); - void ReadPalmaApplicationSection(HLERequestContext& ctx); - void WritePalmaApplicationSection(HLERequestContext& ctx); - void ReadPalmaUniqueCode(HLERequestContext& ctx); - void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx); - void WritePalmaActivityEntry(HLERequestContext& ctx); - void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx); - void WritePalmaWaveEntry(HLERequestContext& ctx); - void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); - void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); - void SuspendPalmaFeature(HLERequestContext& ctx); - void GetPalmaOperationResult(HLERequestContext& ctx); - void ReadPalmaPlayLog(HLERequestContext& ctx); - void ResetPalmaPlayLog(HLERequestContext& ctx); - void SetIsPalmaAllConnectable(HLERequestContext& ctx); - void SetIsPalmaPairedConnectable(HLERequestContext& ctx); - void PairPalma(HLERequestContext& ctx); - void SetPalmaBoostMode(HLERequestContext& ctx); - void CancelWritePalmaWaveEntry(HLERequestContext& ctx); - void EnablePalmaBoostMode(HLERequestContext& ctx); - void GetPalmaBluetoothAddress(HLERequestContext& ctx); - void SetDisallowedPalmaConnection(HLERequestContext& ctx); - void SetNpadCommunicationMode(HLERequestContext& ctx); - void GetNpadCommunicationMode(HLERequestContext& ctx); - void SetTouchScreenConfiguration(HLERequestContext& ctx); - void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); - - std::shared_ptr applet_resource; - - KernelHelpers::ServiceContext service_context; -}; - void LoopProcess(Core::System& system); } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp new file mode 100644 index 000000000..6294f3dfb --- /dev/null +++ b/src/core/hle/service/hid/hid_debug_server.cpp @@ -0,0 +1,159 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hle/service/hid/hid_debug_server.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::HID { + +IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr resource) + : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} { + // clang-format off + static const FunctionInfo functions[] = { + {0, nullptr, "DeactivateDebugPad"}, + {1, nullptr, "SetDebugPadAutoPilotState"}, + {2, nullptr, "UnsetDebugPadAutoPilotState"}, + {10, nullptr, "DeactivateTouchScreen"}, + {11, nullptr, "SetTouchScreenAutoPilotState"}, + {12, nullptr, "UnsetTouchScreenAutoPilotState"}, + {13, nullptr, "GetTouchScreenConfiguration"}, + {14, nullptr, "ProcessTouchScreenAutoTune"}, + {15, nullptr, "ForceStopTouchScreenManagement"}, + {16, nullptr, "ForceRestartTouchScreenManagement"}, + {17, nullptr, "IsTouchScreenManaged"}, + {20, nullptr, "DeactivateMouse"}, + {21, nullptr, "SetMouseAutoPilotState"}, + {22, nullptr, "UnsetMouseAutoPilotState"}, + {25, nullptr, "SetDebugMouseAutoPilotState"}, + {26, nullptr, "UnsetDebugMouseAutoPilotState"}, + {30, nullptr, "DeactivateKeyboard"}, + {31, nullptr, "SetKeyboardAutoPilotState"}, + {32, nullptr, "UnsetKeyboardAutoPilotState"}, + {50, nullptr, "DeactivateXpad"}, + {51, nullptr, "SetXpadAutoPilotState"}, + {52, nullptr, "UnsetXpadAutoPilotState"}, + {53, nullptr, "DeactivateJoyXpad"}, + {60, nullptr, "ClearNpadSystemCommonPolicy"}, + {61, nullptr, "DeactivateNpad"}, + {62, nullptr, "ForceDisconnectNpad"}, + {91, nullptr, "DeactivateGesture"}, + {110, nullptr, "DeactivateHomeButton"}, + {111, nullptr, "SetHomeButtonAutoPilotState"}, + {112, nullptr, "UnsetHomeButtonAutoPilotState"}, + {120, nullptr, "DeactivateSleepButton"}, + {121, nullptr, "SetSleepButtonAutoPilotState"}, + {122, nullptr, "UnsetSleepButtonAutoPilotState"}, + {123, nullptr, "DeactivateInputDetector"}, + {130, nullptr, "DeactivateCaptureButton"}, + {131, nullptr, "SetCaptureButtonAutoPilotState"}, + {132, nullptr, "UnsetCaptureButtonAutoPilotState"}, + {133, nullptr, "SetShiftAccelerometerCalibrationValue"}, + {134, nullptr, "GetShiftAccelerometerCalibrationValue"}, + {135, nullptr, "SetShiftGyroscopeCalibrationValue"}, + {136, nullptr, "GetShiftGyroscopeCalibrationValue"}, + {140, nullptr, "DeactivateConsoleSixAxisSensor"}, + {141, nullptr, "GetConsoleSixAxisSensorSamplingFrequency"}, + {142, nullptr, "DeactivateSevenSixAxisSensor"}, + {143, nullptr, "GetConsoleSixAxisSensorCountStates"}, + {144, nullptr, "GetAccelerometerFsr"}, + {145, nullptr, "SetAccelerometerFsr"}, + {146, nullptr, "GetAccelerometerOdr"}, + {147, nullptr, "SetAccelerometerOdr"}, + {148, nullptr, "GetGyroscopeFsr"}, + {149, nullptr, "SetGyroscopeFsr"}, + {150, nullptr, "GetGyroscopeOdr"}, + {151, nullptr, "SetGyroscopeOdr"}, + {152, nullptr, "GetWhoAmI"}, + {201, nullptr, "ActivateFirmwareUpdate"}, + {202, nullptr, "DeactivateFirmwareUpdate"}, + {203, nullptr, "StartFirmwareUpdate"}, + {204, nullptr, "GetFirmwareUpdateStage"}, + {205, nullptr, "GetFirmwareVersion"}, + {206, nullptr, "GetDestinationFirmwareVersion"}, + {207, nullptr, "DiscardFirmwareInfoCacheForRevert"}, + {208, nullptr, "StartFirmwareUpdateForRevert"}, + {209, nullptr, "GetAvailableFirmwareVersionForRevert"}, + {210, nullptr, "IsFirmwareUpdatingDevice"}, + {211, nullptr, "StartFirmwareUpdateIndividual"}, + {215, nullptr, "SetUsbFirmwareForceUpdateEnabled"}, + {216, nullptr, "SetAllKuinaDevicesToFirmwareUpdateMode"}, + {221, nullptr, "UpdateControllerColor"}, + {222, nullptr, "ConnectUsbPadsAsync"}, + {223, nullptr, "DisconnectUsbPadsAsync"}, + {224, nullptr, "UpdateDesignInfo"}, + {225, nullptr, "GetUniquePadDriverState"}, + {226, nullptr, "GetSixAxisSensorDriverStates"}, + {227, nullptr, "GetRxPacketHistory"}, + {228, nullptr, "AcquireOperationEventHandle"}, + {229, nullptr, "ReadSerialFlash"}, + {230, nullptr, "WriteSerialFlash"}, + {231, nullptr, "GetOperationResult"}, + {232, nullptr, "EnableShipmentMode"}, + {233, nullptr, "ClearPairingInfo"}, + {234, nullptr, "GetUniquePadDeviceTypeSetInternal"}, + {235, nullptr, "EnableAnalogStickPower"}, + {236, nullptr, "RequestKuinaUartClockCal"}, + {237, nullptr, "GetKuinaUartClockCal"}, + {238, nullptr, "SetKuinaUartClockTrim"}, + {239, nullptr, "KuinaLoopbackTest"}, + {240, nullptr, "RequestBatteryVoltage"}, + {241, nullptr, "GetBatteryVoltage"}, + {242, nullptr, "GetUniquePadPowerInfo"}, + {243, nullptr, "RebootUniquePad"}, + {244, nullptr, "RequestKuinaFirmwareVersion"}, + {245, nullptr, "GetKuinaFirmwareVersion"}, + {246, nullptr, "GetVidPid"}, + {247, nullptr, "GetAnalogStickCalibrationValue"}, + {248, nullptr, "GetUniquePadIdsFull"}, + {249, nullptr, "ConnectUniquePad"}, + {250, nullptr, "IsVirtual"}, + {251, nullptr, "GetAnalogStickModuleParam"}, + {301, nullptr, "GetAbstractedPadHandles"}, + {302, nullptr, "GetAbstractedPadState"}, + {303, nullptr, "GetAbstractedPadsState"}, + {321, nullptr, "SetAutoPilotVirtualPadState"}, + {322, nullptr, "UnsetAutoPilotVirtualPadState"}, + {323, nullptr, "UnsetAllAutoPilotVirtualPadState"}, + {324, nullptr, "AttachHdlsWorkBuffer"}, + {325, nullptr, "ReleaseHdlsWorkBuffer"}, + {326, nullptr, "DumpHdlsNpadAssignmentState"}, + {327, nullptr, "DumpHdlsStates"}, + {328, nullptr, "ApplyHdlsNpadAssignmentState"}, + {329, nullptr, "ApplyHdlsStateList"}, + {330, nullptr, "AttachHdlsVirtualDevice"}, + {331, nullptr, "DetachHdlsVirtualDevice"}, + {332, nullptr, "SetHdlsState"}, + {350, nullptr, "AddRegisteredDevice"}, + {400, nullptr, "DisableExternalMcuOnNxDevice"}, + {401, nullptr, "DisableRailDeviceFiltering"}, + {402, nullptr, "EnableWiredPairing"}, + {403, nullptr, "EnableShipmentModeAutoClear"}, + {404, nullptr, "SetRailEnabled"}, + {500, nullptr, "SetFactoryInt"}, + {501, nullptr, "IsFactoryBootEnabled"}, + {550, nullptr, "SetAnalogStickModelDataTemporarily"}, + {551, nullptr, "GetAnalogStickModelData"}, + {552, nullptr, "ResetAnalogStickModelData"}, + {600, nullptr, "ConvertPadState"}, + {650, nullptr, "AddButtonPlayData"}, + {651, nullptr, "StartButtonPlayData"}, + {652, nullptr, "StopButtonPlayData"}, + {2000, nullptr, "DeactivateDigitizer"}, + {2001, nullptr, "SetDigitizerAutoPilotState"}, + {2002, nullptr, "UnsetDigitizerAutoPilotState"}, + {2002, nullptr, "ReloadFirmwareDebugSettings"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IHidDebugServer::~IHidDebugServer() = default; + +std::shared_ptr IHidDebugServer::GetResourceManager() { + resource_manager->Initialize(); + return resource_manager; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h new file mode 100644 index 000000000..406db2211 --- /dev/null +++ b/src/core/hle/service/hid/hid_debug_server.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::HID { +class ResourceManager; + +class IHidDebugServer final : public ServiceFramework { +public: + explicit IHidDebugServer(Core::System& system_, std::shared_ptr resource); + ~IHidDebugServer() override; + +private: + std::shared_ptr GetResourceManager(); + + std::shared_ptr resource_manager; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp new file mode 100644 index 000000000..348dc98d9 --- /dev/null +++ b/src/core/hle/service/hid/hid_server.cpp @@ -0,0 +1,2269 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include +#include "common/common_types.h" +#include "common/logging/log.h" +#include "common/settings.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/hid/errors.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" +#include "core/memory.h" + +#include "core/hle/service/hid/controllers/console_sixaxis.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/controllers/debug_pad.h" +#include "core/hle/service/hid/controllers/gesture.h" +#include "core/hle/service/hid/controllers/keyboard.h" +#include "core/hle/service/hid/controllers/mouse.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/hid/controllers/stubbed.h" +#include "core/hle/service/hid/controllers/touchscreen.h" +#include "core/hle/service/hid/controllers/xpad.h" + +namespace Service::HID { + +class IActiveVibrationDeviceList final : public ServiceFramework { +public: + explicit IActiveVibrationDeviceList(Core::System& system_, + std::shared_ptr resource) + : ServiceFramework{system_, "IActiveVibrationDeviceList"}, resource_manager(resource) { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"}, + }; + // clang-format on + + RegisterHandlers(functions); + } + +private: + void InitializeVibrationDevice(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_device_handle{rp.PopRaw()}; + + if (resource_manager != nullptr) { + resource_manager->GetController(HidController::NPad) + .InitializeVibrationDevice(vibration_device_handle); + } + + LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}", + vibration_device_handle.npad_type, vibration_device_handle.npad_id, + vibration_device_handle.device_index); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + std::shared_ptr resource_manager; +}; + +IHidServer::IHidServer(Core::System& system_, std::shared_ptr resource) + : ServiceFramework{system_, "hid"}, resource_manager{resource} { + // clang-format off + static const FunctionInfo functions[] = { + {0, &IHidServer::CreateAppletResource, "CreateAppletResource"}, + {1, &IHidServer::ActivateDebugPad, "ActivateDebugPad"}, + {11, &IHidServer::ActivateTouchScreen, "ActivateTouchScreen"}, + {21, &IHidServer::ActivateMouse, "ActivateMouse"}, + {26, nullptr, "ActivateDebugMouse"}, + {31, &IHidServer::ActivateKeyboard, "ActivateKeyboard"}, + {32, &IHidServer::SendKeyboardLockKeyEvent, "SendKeyboardLockKeyEvent"}, + {40, nullptr, "AcquireXpadIdEventHandle"}, + {41, nullptr, "ReleaseXpadIdEventHandle"}, + {51, &IHidServer::ActivateXpad, "ActivateXpad"}, + {55, &IHidServer::GetXpadIds, "GetXpadIds"}, + {56, nullptr, "ActivateJoyXpad"}, + {58, nullptr, "GetJoyXpadLifoHandle"}, + {59, nullptr, "GetJoyXpadIds"}, + {60, &IHidServer::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, + {61, &IHidServer::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, + {62, nullptr, "GetSixAxisSensorLifoHandle"}, + {63, nullptr, "ActivateJoySixAxisSensor"}, + {64, nullptr, "DeactivateJoySixAxisSensor"}, + {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, + {66, &IHidServer::StartSixAxisSensor, "StartSixAxisSensor"}, + {67, &IHidServer::StopSixAxisSensor, "StopSixAxisSensor"}, + {68, &IHidServer::IsSixAxisSensorFusionEnabled, "IsSixAxisSensorFusionEnabled"}, + {69, &IHidServer::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, + {70, &IHidServer::SetSixAxisSensorFusionParameters, "SetSixAxisSensorFusionParameters"}, + {71, &IHidServer::GetSixAxisSensorFusionParameters, "GetSixAxisSensorFusionParameters"}, + {72, &IHidServer::ResetSixAxisSensorFusionParameters, "ResetSixAxisSensorFusionParameters"}, + {73, nullptr, "SetAccelerometerParameters"}, + {74, nullptr, "GetAccelerometerParameters"}, + {75, nullptr, "ResetAccelerometerParameters"}, + {76, nullptr, "SetAccelerometerPlayMode"}, + {77, nullptr, "GetAccelerometerPlayMode"}, + {78, nullptr, "ResetAccelerometerPlayMode"}, + {79, &IHidServer::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"}, + {80, &IHidServer::GetGyroscopeZeroDriftMode, "GetGyroscopeZeroDriftMode"}, + {81, &IHidServer::ResetGyroscopeZeroDriftMode, "ResetGyroscopeZeroDriftMode"}, + {82, &IHidServer::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, + {83, &IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor, "IsFirmwareUpdateAvailableForSixAxisSensor"}, + {84, &IHidServer::EnableSixAxisSensorUnalteredPassthrough, "EnableSixAxisSensorUnalteredPassthrough"}, + {85, &IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled, "IsSixAxisSensorUnalteredPassthroughEnabled"}, + {86, nullptr, "StoreSixAxisSensorCalibrationParameter"}, + {87, &IHidServer::LoadSixAxisSensorCalibrationParameter, "LoadSixAxisSensorCalibrationParameter"}, + {88, &IHidServer::GetSixAxisSensorIcInformation, "GetSixAxisSensorIcInformation"}, + {89, &IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned, "ResetIsSixAxisSensorDeviceNewlyAssigned"}, + {91, &IHidServer::ActivateGesture, "ActivateGesture"}, + {100, &IHidServer::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, + {101, &IHidServer::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, + {102, &IHidServer::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, + {103, &IHidServer::ActivateNpad, "ActivateNpad"}, + {104, &IHidServer::DeactivateNpad, "DeactivateNpad"}, + {106, &IHidServer::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, + {107, &IHidServer::DisconnectNpad, "DisconnectNpad"}, + {108, &IHidServer::GetPlayerLedPattern, "GetPlayerLedPattern"}, + {109, &IHidServer::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, + {120, &IHidServer::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, + {121, &IHidServer::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, + {122, &IHidServer::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, + {123, &IHidServer::SetNpadJoyAssignmentModeSingle, "SetNpadJoyAssignmentModeSingle"}, + {124, &IHidServer::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"}, + {125, &IHidServer::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"}, + {126, &IHidServer::StartLrAssignmentMode, "StartLrAssignmentMode"}, + {127, &IHidServer::StopLrAssignmentMode, "StopLrAssignmentMode"}, + {128, &IHidServer::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, + {129, &IHidServer::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, + {130, &IHidServer::SwapNpadAssignment, "SwapNpadAssignment"}, + {131, &IHidServer::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, + {132, &IHidServer::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, + {133, &IHidServer::SetNpadJoyAssignmentModeSingleWithDestination, "SetNpadJoyAssignmentModeSingleWithDestination"}, + {134, &IHidServer::SetNpadAnalogStickUseCenterClamp, "SetNpadAnalogStickUseCenterClamp"}, + {135, &IHidServer::SetNpadCaptureButtonAssignment, "SetNpadCaptureButtonAssignment"}, + {136, &IHidServer::ClearNpadCaptureButtonAssignment, "ClearNpadCaptureButtonAssignment"}, + {200, &IHidServer::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"}, + {201, &IHidServer::SendVibrationValue, "SendVibrationValue"}, + {202, &IHidServer::GetActualVibrationValue, "GetActualVibrationValue"}, + {203, &IHidServer::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"}, + {204, &IHidServer::PermitVibration, "PermitVibration"}, + {205, &IHidServer::IsVibrationPermitted, "IsVibrationPermitted"}, + {206, &IHidServer::SendVibrationValues, "SendVibrationValues"}, + {207, &IHidServer::SendVibrationGcErmCommand, "SendVibrationGcErmCommand"}, + {208, &IHidServer::GetActualVibrationGcErmCommand, "GetActualVibrationGcErmCommand"}, + {209, &IHidServer::BeginPermitVibrationSession, "BeginPermitVibrationSession"}, + {210, &IHidServer::EndPermitVibrationSession, "EndPermitVibrationSession"}, + {211, &IHidServer::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"}, + {212, nullptr, "SendVibrationValueInBool"}, + {300, &IHidServer::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, + {301, &IHidServer::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, + {302, &IHidServer::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"}, + {303, &IHidServer::ActivateSevenSixAxisSensor, "ActivateSevenSixAxisSensor"}, + {304, &IHidServer::StartSevenSixAxisSensor, "StartSevenSixAxisSensor"}, + {305, &IHidServer::StopSevenSixAxisSensor, "StopSevenSixAxisSensor"}, + {306, &IHidServer::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, + {307, &IHidServer::FinalizeSevenSixAxisSensor, "FinalizeSevenSixAxisSensor"}, + {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, + {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, + {310, &IHidServer::ResetSevenSixAxisSensorTimestamp, "ResetSevenSixAxisSensorTimestamp"}, + {400, &IHidServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, + {401, nullptr, "EnableUsbFullKeyController"}, + {402, nullptr, "IsUsbFullKeyControllerConnected"}, + {403, nullptr, "HasBattery"}, + {404, nullptr, "HasLeftRightBattery"}, + {405, nullptr, "GetNpadInterfaceType"}, + {406, nullptr, "GetNpadLeftRightInterfaceType"}, + {407, nullptr, "GetNpadOfHighestBatteryLevel"}, + {408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"}, + {500, &IHidServer::GetPalmaConnectionHandle, "GetPalmaConnectionHandle"}, + {501, &IHidServer::InitializePalma, "InitializePalma"}, + {502, &IHidServer::AcquirePalmaOperationCompleteEvent, "AcquirePalmaOperationCompleteEvent"}, + {503, &IHidServer::GetPalmaOperationInfo, "GetPalmaOperationInfo"}, + {504, &IHidServer::PlayPalmaActivity, "PlayPalmaActivity"}, + {505, &IHidServer::SetPalmaFrModeType, "SetPalmaFrModeType"}, + {506, &IHidServer::ReadPalmaStep, "ReadPalmaStep"}, + {507, &IHidServer::EnablePalmaStep, "EnablePalmaStep"}, + {508, &IHidServer::ResetPalmaStep, "ResetPalmaStep"}, + {509, &IHidServer::ReadPalmaApplicationSection, "ReadPalmaApplicationSection"}, + {510, &IHidServer::WritePalmaApplicationSection, "WritePalmaApplicationSection"}, + {511, &IHidServer::ReadPalmaUniqueCode, "ReadPalmaUniqueCode"}, + {512, &IHidServer::SetPalmaUniqueCodeInvalid, "SetPalmaUniqueCodeInvalid"}, + {513, &IHidServer::WritePalmaActivityEntry, "WritePalmaActivityEntry"}, + {514, &IHidServer::WritePalmaRgbLedPatternEntry, "WritePalmaRgbLedPatternEntry"}, + {515, &IHidServer::WritePalmaWaveEntry, "WritePalmaWaveEntry"}, + {516, &IHidServer::SetPalmaDataBaseIdentificationVersion, "SetPalmaDataBaseIdentificationVersion"}, + {517, &IHidServer::GetPalmaDataBaseIdentificationVersion, "GetPalmaDataBaseIdentificationVersion"}, + {518, &IHidServer::SuspendPalmaFeature, "SuspendPalmaFeature"}, + {519, &IHidServer::GetPalmaOperationResult, "GetPalmaOperationResult"}, + {520, &IHidServer::ReadPalmaPlayLog, "ReadPalmaPlayLog"}, + {521, &IHidServer::ResetPalmaPlayLog, "ResetPalmaPlayLog"}, + {522, &IHidServer::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"}, + {523, &IHidServer::SetIsPalmaPairedConnectable, "SetIsPalmaPairedConnectable"}, + {524, &IHidServer::PairPalma, "PairPalma"}, + {525, &IHidServer::SetPalmaBoostMode, "SetPalmaBoostMode"}, + {526, &IHidServer::CancelWritePalmaWaveEntry, "CancelWritePalmaWaveEntry"}, + {527, &IHidServer::EnablePalmaBoostMode, "EnablePalmaBoostMode"}, + {528, &IHidServer::GetPalmaBluetoothAddress, "GetPalmaBluetoothAddress"}, + {529, &IHidServer::SetDisallowedPalmaConnection, "SetDisallowedPalmaConnection"}, + {1000, &IHidServer::SetNpadCommunicationMode, "SetNpadCommunicationMode"}, + {1001, &IHidServer::GetNpadCommunicationMode, "GetNpadCommunicationMode"}, + {1002, &IHidServer::SetTouchScreenConfiguration, "SetTouchScreenConfiguration"}, + {1003, &IHidServer::IsFirmwareUpdateNeededForNotification, "IsFirmwareUpdateNeededForNotification"}, + {2000, nullptr, "ActivateDigitizer"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +IHidServer::~IHidServer() = default; + +void IHidServer::CreateAppletResource(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, resource_manager); +} + +void IHidServer::ActivateDebugPad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->ActivateController(HidController::DebugPad); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateTouchScreen(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->ActivateController(HidController::Touchscreen); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateMouse(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->ActivateController(HidController::Mouse); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateKeyboard(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->ActivateController(HidController::Keyboard); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SendKeyboardLockKeyEvent(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto flags{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateXpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 basic_xpad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + GetResourceManager()->ActivateController(HidController::XPad); + + LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", + parameters.basic_xpad_id, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetXpadIds(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} + +void IHidServer::ActivateSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 basic_xpad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + // This function does nothing on 10.0.0+ + + LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", + parameters.basic_xpad_id, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::DeactivateSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 basic_xpad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + // This function does nothing on 10.0.0+ + + LOG_WARNING(Service_HID, "(STUBBED) called, basic_xpad_id={}, applet_resource_user_id={}", + parameters.basic_xpad_id, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StartSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, true); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::StopSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.SetSixAxisEnabled(parameters.sixaxis_handle, false); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsSixAxisSensorFusionEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + bool is_enabled{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = + controller.IsSixAxisSensorFusionEnabled(parameters.sixaxis_handle, is_enabled); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_enabled); +} + +void IHidServer::EnableSixAxisSensorFusion(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enable_sixaxis_sensor_fusion; + INSERT_PADDING_BYTES_NOINIT(3); + Core::HID::SixAxisSensorHandle sixaxis_handle; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, + parameters.enable_sixaxis_sensor_fusion); + + LOG_DEBUG(Service_HID, + "called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, " + "device_index={}, applet_resource_user_id={}", + parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetSixAxisSensorFusionParameters(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + Core::HID::SixAxisSensorFusionParameters sixaxis_fusion; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.sixaxis_fusion); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " + "parameter2={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.sixaxis_fusion.parameter1, + parameters.sixaxis_fusion.parameter2, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::GetSixAxisSensorFusionParameters(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + Core::HID::SixAxisSensorFusionParameters fusion_parameters{}; + const auto& controller = + GetResourceManager()->GetController(HidController::NPad); + const auto result = + controller.GetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(fusion_parameters); +} + +void IHidServer::ResetSixAxisSensorFusionParameters(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + // Since these parameters are unknown just use what HW outputs + const Core::HID::SixAxisSensorFusionParameters fusion_parameters{ + .parameter1 = 0.03f, + .parameter2 = 0.4f, + }; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result1 = + controller.SetSixAxisFusionParameters(parameters.sixaxis_handle, fusion_parameters); + const auto result2 = controller.SetSixAxisFusionEnabled(parameters.sixaxis_handle, true); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + if (result1.IsError()) { + rb.Push(result1); + return; + } + rb.Push(result2); +} + +void IHidServer::SetGyroscopeZeroDriftMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto sixaxis_handle{rp.PopRaw()}; + const auto drift_mode{rp.PopEnum()}; + const auto applet_resource_user_id{rp.Pop()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(sixaxis_handle, drift_mode); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, " + "applet_resource_user_id={}", + sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index, + drift_mode, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::GetGyroscopeZeroDriftMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.GetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.PushEnum(drift_mode); +} + +void IHidServer::ResetGyroscopeZeroDriftMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + const auto drift_mode{Core::HID::GyroscopeZeroDriftMode::Standard}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.SetGyroscopeZeroDriftMode(parameters.sixaxis_handle, drift_mode); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsSixAxisSensorAtRest(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + bool is_at_rest{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + controller.IsSixAxisSensorAtRest(parameters.sixaxis_handle, is_at_rest); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_at_rest); +} + +void IHidServer::IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + bool is_firmware_available{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + controller.IsFirmwareUpdateAvailableForSixAxisSensor(parameters.sixaxis_handle, + is_firmware_available); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_firmware_available); +} + +void IHidServer::EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool enabled; + Core::HID::SixAxisSensorHandle sixaxis_handle; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.EnableSixAxisSensorUnalteredPassthrough( + parameters.sixaxis_handle, parameters.enabled); + + LOG_DEBUG(Service_HID, + "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, " + "applet_resource_user_id={}", + parameters.enabled, parameters.sixaxis_handle.npad_type, + parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + bool is_unaltered_sisxaxis_enabled{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled( + parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled); + + LOG_DEBUG( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_unaltered_sisxaxis_enabled); +} + +void IHidServer::LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + Core::HID::SixAxisSensorCalibrationParameter calibration{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = + controller.LoadSixAxisSensorCalibrationParameter(parameters.sixaxis_handle, calibration); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(calibration); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::GetSixAxisSensorIcInformation(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + Core::HID::SixAxisSensorIcInformation ic_information{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = + controller.GetSixAxisSensorIcInformation(parameters.sixaxis_handle, ic_information); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + if (result.IsSuccess()) { + ctx.WriteBuffer(ic_information); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::SixAxisSensorHandle sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = + controller.ResetIsSixAxisSensorDeviceNewlyAssigned(parameters.sixaxis_handle); + + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ActivateGesture(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + u32 unknown; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + GetResourceManager()->ActivateController(HidController::Gesture); + + LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", + parameters.unknown, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetSupportedNpadStyleSet(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadStyleSet supported_styleset; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .SetSupportedStyleSet({parameters.supported_styleset}); + + LOG_DEBUG(Service_HID, "called, supported_styleset={}, applet_resource_user_id={}", + parameters.supported_styleset, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetSupportedNpadStyleSet(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(GetResourceManager() + ->GetController(HidController::NPad) + .GetSupportedStyleSet() + .raw); +} + +void IHidServer::SetSupportedNpadIdType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + const auto result = GetResourceManager() + ->GetController(HidController::NPad) + .SetSupportedNpadIdTypes(ctx.ReadBuffer()); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ActivateNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->ActivateController(HidController::NPad); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::DeactivateNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->DeactivateController(HidController::NPad); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + u64 unknown; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", + parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown); + + // Games expect this event to be signaled after calling this function + GetResourceManager() + ->GetController(HidController::NPad) + .SignalStyleSetChangedEvent(parameters.npad_id); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(GetResourceManager() + ->GetController(HidController::NPad) + .GetStyleSetChangedEvent(parameters.npad_id)); +} + +void IHidServer::DisconnectNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + controller.DisconnectNpad(parameters.npad_id); + + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPlayerLedPattern(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id{rp.PopEnum()}; + + Core::HID::LedPattern pattern{0, 0, 0, 0}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.GetLedPattern(npad_id, pattern); + + LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(pattern.raw); +} + +void IHidServer::ActivateNpadWithRevision(HLERequestContext& ctx) { + // Should have no effect with how our npad sets up the data + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 revision; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + GetResourceManager()->ActivateController(HidController::NPad); + + LOG_DEBUG(Service_HID, "called, revision={}, applet_resource_user_id={}", parameters.revision, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadJoyHoldType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + const auto hold_type{rp.PopEnum()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .SetHoldType(hold_type); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}", + applet_resource_user_id, hold_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetNpadJoyHoldType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum( + GetResourceManager()->GetController(HidController::NPad).GetHoldType()); +} + +void IHidServer::SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + Core::HID::NpadIdType new_npad_id{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + controller.SetNpadMode(new_npad_id, parameters.npad_id, + Controller_NPad::NpadJoyDeviceType::Left, + Controller_NPad::NpadJoyAssignmentMode::Single); + + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Controller_NPad::NpadJoyDeviceType npad_joy_device_type; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + Core::HID::NpadIdType new_npad_id{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, + Controller_NPad::NpadJoyAssignmentMode::Single); + + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", + parameters.npad_id, parameters.applet_resource_user_id, + parameters.npad_joy_device_type); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + Core::HID::NpadIdType new_npad_id{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + controller.SetNpadMode(new_npad_id, parameters.npad_id, {}, + Controller_NPad::NpadJoyAssignmentMode::Dual); + + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::MergeSingleJoyAsDualJoy(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_1{rp.PopEnum()}; + const auto npad_id_2{rp.PopEnum()}; + const auto applet_resource_user_id{rp.Pop()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); + + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", + npad_id_1, npad_id_2, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::StartLrAssignmentMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .StartLRAssignmentMode(); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StopLrAssignmentMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .StopLRAssignmentMode(); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadHandheldActivationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + const auto activation_mode{rp.PopEnum()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .SetNpadHandheldActivationMode(activation_mode); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}", + applet_resource_user_id, activation_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetNpadHandheldActivationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(GetResourceManager() + ->GetController(HidController::NPad) + .GetNpadHandheldActivationMode()); +} + +void IHidServer::SwapNpadAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_1{rp.PopEnum()}; + const auto npad_id_2{rp.PopEnum()}; + const auto applet_resource_user_id{rp.Pop()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.SwapNpadAssignment(npad_id_1, npad_id_2); + + LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", + npad_id_1, npad_id_2, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + bool is_enabled = false; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = + controller.IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id, is_enabled); + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(result); + rb.Push(is_enabled); +} + +void IHidServer::EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_BYTES_NOINIT(3); + Core::HID::NpadIdType npad_id; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto result = controller.SetUnintendedHomeButtonInputProtectionEnabled( + parameters.is_enabled, parameters.npad_id); + + LOG_DEBUG(Service_HID, + "(STUBBED) called, is_enabled={}, npad_id={}, applet_resource_user_id={}", + parameters.is_enabled, parameters.npad_id, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Controller_NPad::NpadJoyDeviceType npad_joy_device_type; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + Core::HID::NpadIdType new_npad_id{}; + auto& controller = GetResourceManager()->GetController(HidController::NPad); + const auto is_reassigned = + controller.SetNpadMode(new_npad_id, parameters.npad_id, parameters.npad_joy_device_type, + Controller_NPad::NpadJoyAssignmentMode::Single); + + LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}", + parameters.npad_id, parameters.applet_resource_user_id, + parameters.npad_joy_device_type); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(is_reassigned); + rb.PushEnum(new_npad_id); +} + +void IHidServer::SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool analog_stick_use_center_clamp; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .SetAnalogStickUseCenterClamp(parameters.analog_stick_use_center_clamp); + + LOG_WARNING(Service_HID, + "(STUBBED) called, analog_stick_use_center_clamp={}, applet_resource_user_id={}", + parameters.analog_stick_use_center_clamp, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadCaptureButtonAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadStyleSet npad_styleset; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::HID::NpadButton button; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, npad_styleset={}, applet_resource_user_id={}, button={}", + parameters.npad_styleset, parameters.applet_resource_user_id, parameters.button); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ClearNpadCaptureButtonAssignment(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetVibrationDeviceInfo(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto vibration_device_handle{rp.PopRaw()}; + const auto& controller = + GetResourceManager()->GetController(HidController::NPad); + + Core::HID::VibrationDeviceInfo vibration_device_info; + bool check_device_index = false; + + switch (vibration_device_handle.npad_type) { + case Core::HID::NpadStyleIndex::ProController: + case Core::HID::NpadStyleIndex::Handheld: + case Core::HID::NpadStyleIndex::JoyconDual: + case Core::HID::NpadStyleIndex::JoyconLeft: + case Core::HID::NpadStyleIndex::JoyconRight: + vibration_device_info.type = Core::HID::VibrationDeviceType::LinearResonantActuator; + check_device_index = true; + break; + case Core::HID::NpadStyleIndex::GameCube: + vibration_device_info.type = Core::HID::VibrationDeviceType::GcErm; + break; + case Core::HID::NpadStyleIndex::N64: + vibration_device_info.type = Core::HID::VibrationDeviceType::N64; + break; + default: + vibration_device_info.type = Core::HID::VibrationDeviceType::Unknown; + break; + } + + vibration_device_info.position = Core::HID::VibrationDevicePosition::None; + if (check_device_index) { + switch (vibration_device_handle.device_index) { + case Core::HID::DeviceIndex::Left: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Left; + break; + case Core::HID::DeviceIndex::Right: + vibration_device_info.position = Core::HID::VibrationDevicePosition::Right; + break; + case Core::HID::DeviceIndex::None: + default: + ASSERT_MSG(false, "DeviceIndex should never be None!"); + break; + } + } + + LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}", + vibration_device_info.type, vibration_device_info.position); + + const auto result = controller.IsDeviceHandleValid(vibration_device_handle); + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + return; + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(vibration_device_info); +} + +void IHidServer::SendVibrationValue(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + Core::HID::VibrationValue vibration_value; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .VibrateController(parameters.vibration_device_handle, parameters.vibration_value); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetActualVibrationValue(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(GetResourceManager() + ->GetController(HidController::NPad) + .GetLastVibration(parameters.vibration_device_handle)); +} + +void IHidServer::CreateActiveVibrationDeviceList(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(ResultSuccess); + rb.PushIpcInterface(system, resource_manager); +} + +void IHidServer::PermitVibration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto can_vibrate{rp.Pop()}; + + // nnSDK saves this value as a float. Since it can only be 1.0f or 0.0f we simplify this value + // by converting it to a bool + Settings::values.vibration_enabled.SetValue(can_vibrate); + + LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsVibrationPermitted(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + // nnSDK checks if a float is greater than zero. We return the bool we stored earlier + const auto is_enabled = Settings::values.vibration_enabled.GetValue(); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_enabled); +} + +void IHidServer::SendVibrationValues(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + const auto handle_data = ctx.ReadBuffer(0); + const auto handle_count = ctx.GetReadBufferNumElements(0); + const auto vibration_data = ctx.ReadBuffer(1); + const auto vibration_count = ctx.GetReadBufferNumElements(1); + + auto vibration_device_handles = + std::span(reinterpret_cast(handle_data.data()), + handle_count); + auto vibration_values = std::span( + reinterpret_cast(vibration_data.data()), vibration_count); + + GetResourceManager() + ->GetController(HidController::NPad) + .VibrateControllers(vibration_device_handles, vibration_values); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SendVibrationGcErmCommand(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + Core::HID::VibrationGcErmCommand gc_erm_command; + }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + /** + * Note: This uses yuzu-specific behavior such that the StopHard command produces + * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined below, + * in order to differentiate between Stop and StopHard commands. + * This is done to reuse the controller vibration functions made for regular controllers. + */ + const auto vibration_value = [parameters] { + switch (parameters.gc_erm_command) { + case Core::HID::VibrationGcErmCommand::Stop: + return Core::HID::VibrationValue{ + .low_amplitude = 0.0f, + .low_frequency = 160.0f, + .high_amplitude = 0.0f, + .high_frequency = 320.0f, + }; + case Core::HID::VibrationGcErmCommand::Start: + return Core::HID::VibrationValue{ + .low_amplitude = 1.0f, + .low_frequency = 160.0f, + .high_amplitude = 1.0f, + .high_frequency = 320.0f, + }; + case Core::HID::VibrationGcErmCommand::StopHard: + return Core::HID::VibrationValue{ + .low_amplitude = 0.0f, + .low_frequency = 0.0f, + .high_amplitude = 0.0f, + .high_frequency = 0.0f, + }; + default: + return Core::HID::DEFAULT_VIBRATION_VALUE; + } + }(); + + GetResourceManager() + ->GetController(HidController::NPad) + .VibrateController(parameters.vibration_device_handle, vibration_value); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}, " + "gc_erm_command={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id, + parameters.gc_erm_command); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetActualVibrationGcErmCommand(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + + const auto parameters{rp.PopRaw()}; + + const auto last_vibration = GetResourceManager() + ->GetController(HidController::NPad) + .GetLastVibration(parameters.vibration_device_handle); + + const auto gc_erm_command = [last_vibration] { + if (last_vibration.low_amplitude != 0.0f || last_vibration.high_amplitude != 0.0f) { + return Core::HID::VibrationGcErmCommand::Start; + } + + /** + * Note: This uses yuzu-specific behavior such that the StopHard command produces + * vibrations where freq_low == 0.0f and freq_high == 0.0f, as defined in the HID function + * SendVibrationGcErmCommand, in order to differentiate between Stop and StopHard commands. + * This is done to reuse the controller vibration functions made for regular controllers. + */ + if (last_vibration.low_frequency == 0.0f && last_vibration.high_frequency == 0.0f) { + return Core::HID::VibrationGcErmCommand::StopHard; + } + + return Core::HID::VibrationGcErmCommand::Stop; + }(); + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(gc_erm_command); +} + +void IHidServer::BeginPermitVibrationSession(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .SetPermitVibrationSession(true); + + LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::EndPermitVibrationSession(HLERequestContext& ctx) { + GetResourceManager() + ->GetController(HidController::NPad) + .SetPermitVibrationSession(false); + + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsVibrationDeviceMounted(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::VibrationDeviceHandle vibration_device_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_DEBUG(Service_HID, + "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.vibration_device_handle.npad_type, + parameters.vibration_device_handle.npad_id, + parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(GetResourceManager() + ->GetController(HidController::NPad) + .IsVibrationDeviceMounted(parameters.vibration_device_handle)); +} + +void IHidServer::ActivateConsoleSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->ActivateController(HidController::ConsoleSixAxisSensor); + + LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StartConsoleSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", + parameters.console_sixaxis_handle.unknown_1, + parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StopConsoleSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::ConsoleSixAxisSensorHandle console_sixaxis_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", + parameters.console_sixaxis_handle.unknown_1, + parameters.console_sixaxis_handle.unknown_2, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ActivateSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager()->ActivateController(HidController::ConsoleSixAxisSensor); + + LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StartSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::StopSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::InitializeSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + const auto t_mem_1_size{rp.Pop()}; + const auto t_mem_2_size{rp.Pop()}; + const auto t_mem_1_handle{ctx.GetCopyHandle(0)}; + const auto t_mem_2_handle{ctx.GetCopyHandle(1)}; + + ASSERT_MSG(t_mem_1_size == 0x1000, "t_mem_1_size is not 0x1000 bytes"); + ASSERT_MSG(t_mem_2_size == 0x7F000, "t_mem_2_size is not 0x7F000 bytes"); + + auto t_mem_1 = system.ApplicationProcess()->GetHandleTable().GetObject( + t_mem_1_handle); + + if (t_mem_1.IsNull()) { + LOG_ERROR(Service_HID, "t_mem_1 is a nullptr for handle=0x{:08X}", t_mem_1_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + auto t_mem_2 = system.ApplicationProcess()->GetHandleTable().GetObject( + t_mem_2_handle); + + if (t_mem_2.IsNull()) { + LOG_ERROR(Service_HID, "t_mem_2 is a nullptr for handle=0x{:08X}", t_mem_2_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem_1->GetSize() == 0x1000, "t_mem_1 has incorrect size"); + ASSERT_MSG(t_mem_2->GetSize() == 0x7F000, "t_mem_2 has incorrect size"); + + // Activate console six axis controller + GetResourceManager() + ->GetController(HidController::ConsoleSixAxisSensor) + .ActivateController(); + + GetResourceManager() + ->GetController(HidController::ConsoleSixAxisSensor) + .SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); + + LOG_WARNING(Service_HID, + "called, t_mem_1_handle=0x{:08X}, t_mem_2_handle=0x{:08X}, " + "applet_resource_user_id={}", + t_mem_1_handle, t_mem_2_handle, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::FinalizeSevenSixAxisSensor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + + GetResourceManager() + ->GetController(HidController::ConsoleSixAxisSensor) + .ResetTimestamp(); + + LOG_WARNING(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +void IHidServer::GetPalmaConnectionHandle(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + Core::HID::NpadIdType npad_id; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", + parameters.npad_id, parameters.applet_resource_user_id); + + Controller_Palma::PalmaConnectionHandle handle; + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = controller.GetPalmaConnectionHandle(parameters.npad_id, handle); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.PushRaw(handle); +} + +void IHidServer::InitializePalma(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = controller.InitializePalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetResourceManager()->GetController(HidController::Palma); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(controller.AcquirePalmaOperationCompleteEvent(connection_handle)); +} + +void IHidServer::GetPalmaOperationInfo(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + Controller_Palma::PalmaOperationType operation_type; + Controller_Palma::PalmaOperationData data; + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = controller.GetPalmaOperationInfo(connection_handle, operation_type, data); + + if (result.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); + } + + ctx.WriteBuffer(data); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(result); + rb.Push(static_cast(operation_type)); +} + +void IHidServer::PlayPalmaActivity(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto palma_activity{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, palma_activity={}", + connection_handle.npad_id, palma_activity); + + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = controller.PlayPalmaActivity(connection_handle, palma_activity); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::SetPalmaFrModeType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto fr_mode{rp.PopEnum()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, fr_mode={}", + connection_handle.npad_id, fr_mode); + + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = controller.SetPalmaFrModeType(connection_handle, fr_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ReadPalmaStep(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = controller.ReadPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::EnablePalmaStep(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_enabled; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, is_enabled={}", + parameters.connection_handle.npad_id, parameters.is_enabled); + + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = + controller.EnablePalmaStep(parameters.connection_handle, parameters.is_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ResetPalmaStep(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + auto& controller = GetResourceManager()->GetController(HidController::Palma); + const auto result = controller.ResetPalmaStep(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ReadPalmaApplicationSection(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaApplicationSection(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ReadPalmaUniqueCode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager() + ->GetController(HidController::Palma) + .ReadPalmaUniqueCode(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetPalmaUniqueCodeInvalid(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager() + ->GetController(HidController::Palma) + .SetPalmaUniqueCodeInvalid(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaActivityEntry(HLERequestContext& ctx) { + LOG_CRITICAL(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaRgbLedPatternEntry(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto unknown{rp.Pop()}; + + [[maybe_unused]] const auto buffer = ctx.ReadBuffer(); + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, unknown={}", + connection_handle.npad_id, unknown); + + GetResourceManager() + ->GetController(HidController::Palma) + .WritePalmaRgbLedPatternEntry(connection_handle, unknown); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::WritePalmaWaveEntry(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + const auto wave_set{rp.PopEnum()}; + const auto unknown{rp.Pop()}; + const auto t_mem_size{rp.Pop()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + const auto size{rp.Pop()}; + + ASSERT_MSG(t_mem_size == 0x3000, "t_mem_size is not 0x3000 bytes"); + + auto t_mem = system.ApplicationProcess()->GetHandleTable().GetObject( + t_mem_handle); + + if (t_mem.IsNull()) { + LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultUnknown); + return; + } + + ASSERT_MSG(t_mem->GetSize() == 0x3000, "t_mem has incorrect size"); + + LOG_WARNING(Service_HID, + "(STUBBED) called, connection_handle={}, wave_set={}, unknown={}, " + "t_mem_handle=0x{:08X}, t_mem_size={}, size={}", + connection_handle.npad_id, wave_set, unknown, t_mem_handle, t_mem_size, size); + + GetResourceManager() + ->GetController(HidController::Palma) + .WritePalmaWaveEntry(connection_handle, wave_set, t_mem->GetSourceAddress(), t_mem_size); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 database_id_version; + INSERT_PADDING_WORDS_NOINIT(1); + Controller_Palma::PalmaConnectionHandle connection_handle; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}, database_id_version={}", + parameters.connection_handle.npad_id, parameters.database_id_version); + + GetResourceManager() + ->GetController(HidController::Palma) + .SetPalmaDataBaseIdentificationVersion(parameters.connection_handle, + parameters.database_id_version); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager() + ->GetController(HidController::Palma) + .GetPalmaDataBaseIdentificationVersion(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SuspendPalmaFeature(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPalmaOperationResult(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + const auto result = GetResourceManager() + ->GetController(HidController::Palma) + .GetPalmaOperationResult(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidServer::ReadPalmaPlayLog(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::ResetPalmaPlayLog(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetIsPalmaAllConnectable(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + bool is_palma_all_connectable; + INSERT_PADDING_BYTES_NOINIT(7); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, + "(STUBBED) called, is_palma_all_connectable={},applet_resource_user_id={}", + parameters.is_palma_all_connectable, parameters.applet_resource_user_id); + + GetResourceManager() + ->GetController(HidController::Palma) + .SetIsPalmaAllConnectable(parameters.is_palma_all_connectable); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetIsPalmaPairedConnectable(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::PairPalma(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto connection_handle{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, connection_handle={}", connection_handle.npad_id); + + GetResourceManager() + ->GetController(HidController::Palma) + .PairPalma(connection_handle); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetPalmaBoostMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto palma_boost_mode{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, palma_boost_mode={}", palma_boost_mode); + + GetResourceManager() + ->GetController(HidController::Palma) + .SetPalmaBoostMode(palma_boost_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::CancelWritePalmaWaveEntry(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::EnablePalmaBoostMode(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetPalmaBluetoothAddress(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetDisallowedPalmaConnection(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::SetNpadCommunicationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop()}; + const auto communication_mode{rp.PopEnum()}; + + GetResourceManager() + ->GetController(HidController::NPad) + .SetNpadCommunicationMode(communication_mode); + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, communication_mode={}", + applet_resource_user_id, communication_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(GetResourceManager() + ->GetController(HidController::NPad) + .GetNpadCommunicationMode()); +} + +void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto touchscreen_mode{rp.PopRaw()}; + const auto applet_resource_user_id{rp.Pop()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", + touchscreen_mode.mode, applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + struct Parameters { + s32 unknown; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}, applet_resource_user_id={}", + parameters.unknown, parameters.applet_resource_user_id); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +std::shared_ptr IHidServer::GetResourceManager() { + resource_manager->Initialize(); + return resource_manager; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_server.h b/src/core/hle/service/hid/hid_server.h new file mode 100644 index 000000000..3d25ea1bc --- /dev/null +++ b/src/core/hle/service/hid/hid_server.h @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Service::HID { +class ResourceManager; + +class IHidServer final : public ServiceFramework { +public: + explicit IHidServer(Core::System& system_, std::shared_ptr resource); + ~IHidServer() override; + + std::shared_ptr GetResourceManager(); + +private: + void CreateAppletResource(HLERequestContext& ctx); + void ActivateDebugPad(HLERequestContext& ctx); + void ActivateTouchScreen(HLERequestContext& ctx); + void ActivateMouse(HLERequestContext& ctx); + void ActivateKeyboard(HLERequestContext& ctx); + void SendKeyboardLockKeyEvent(HLERequestContext& ctx); + void ActivateXpad(HLERequestContext& ctx); + void GetXpadIds(HLERequestContext& ctx); + void GetJoyXpadIds(HLERequestContext& ctx); + void ActivateSixAxisSensor(HLERequestContext& ctx); + void DeactivateSixAxisSensor(HLERequestContext& ctx); + void StartSixAxisSensor(HLERequestContext& ctx); + void StopSixAxisSensor(HLERequestContext& ctx); + void IsSixAxisSensorFusionEnabled(HLERequestContext& ctx); + void EnableSixAxisSensorFusion(HLERequestContext& ctx); + void SetSixAxisSensorFusionParameters(HLERequestContext& ctx); + void GetSixAxisSensorFusionParameters(HLERequestContext& ctx); + void ResetSixAxisSensorFusionParameters(HLERequestContext& ctx); + void SetGyroscopeZeroDriftMode(HLERequestContext& ctx); + void GetGyroscopeZeroDriftMode(HLERequestContext& ctx); + void ResetGyroscopeZeroDriftMode(HLERequestContext& ctx); + void IsSixAxisSensorAtRest(HLERequestContext& ctx); + void IsFirmwareUpdateAvailableForSixAxisSensor(HLERequestContext& ctx); + void EnableSixAxisSensorUnalteredPassthrough(HLERequestContext& ctx); + void IsSixAxisSensorUnalteredPassthroughEnabled(HLERequestContext& ctx); + void LoadSixAxisSensorCalibrationParameter(HLERequestContext& ctx); + void GetSixAxisSensorIcInformation(HLERequestContext& ctx); + void ResetIsSixAxisSensorDeviceNewlyAssigned(HLERequestContext& ctx); + void ActivateGesture(HLERequestContext& ctx); + void SetSupportedNpadStyleSet(HLERequestContext& ctx); + void GetSupportedNpadStyleSet(HLERequestContext& ctx); + void SetSupportedNpadIdType(HLERequestContext& ctx); + void ActivateNpad(HLERequestContext& ctx); + void DeactivateNpad(HLERequestContext& ctx); + void AcquireNpadStyleSetUpdateEventHandle(HLERequestContext& ctx); + void DisconnectNpad(HLERequestContext& ctx); + void GetPlayerLedPattern(HLERequestContext& ctx); + void ActivateNpadWithRevision(HLERequestContext& ctx); + void SetNpadJoyHoldType(HLERequestContext& ctx); + void GetNpadJoyHoldType(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeSingleByDefault(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeSingle(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeDual(HLERequestContext& ctx); + void MergeSingleJoyAsDualJoy(HLERequestContext& ctx); + void StartLrAssignmentMode(HLERequestContext& ctx); + void StopLrAssignmentMode(HLERequestContext& ctx); + void SetNpadHandheldActivationMode(HLERequestContext& ctx); + void GetNpadHandheldActivationMode(HLERequestContext& ctx); + void SwapNpadAssignment(HLERequestContext& ctx); + void IsUnintendedHomeButtonInputProtectionEnabled(HLERequestContext& ctx); + void EnableUnintendedHomeButtonInputProtection(HLERequestContext& ctx); + void SetNpadJoyAssignmentModeSingleWithDestination(HLERequestContext& ctx); + void SetNpadAnalogStickUseCenterClamp(HLERequestContext& ctx); + void SetNpadCaptureButtonAssignment(HLERequestContext& ctx); + void ClearNpadCaptureButtonAssignment(HLERequestContext& ctx); + void GetVibrationDeviceInfo(HLERequestContext& ctx); + void SendVibrationValue(HLERequestContext& ctx); + void GetActualVibrationValue(HLERequestContext& ctx); + void CreateActiveVibrationDeviceList(HLERequestContext& ctx); + void PermitVibration(HLERequestContext& ctx); + void IsVibrationPermitted(HLERequestContext& ctx); + void SendVibrationValues(HLERequestContext& ctx); + void SendVibrationGcErmCommand(HLERequestContext& ctx); + void GetActualVibrationGcErmCommand(HLERequestContext& ctx); + void BeginPermitVibrationSession(HLERequestContext& ctx); + void EndPermitVibrationSession(HLERequestContext& ctx); + void IsVibrationDeviceMounted(HLERequestContext& ctx); + void ActivateConsoleSixAxisSensor(HLERequestContext& ctx); + void StartConsoleSixAxisSensor(HLERequestContext& ctx); + void StopConsoleSixAxisSensor(HLERequestContext& ctx); + void ActivateSevenSixAxisSensor(HLERequestContext& ctx); + void StartSevenSixAxisSensor(HLERequestContext& ctx); + void StopSevenSixAxisSensor(HLERequestContext& ctx); + void InitializeSevenSixAxisSensor(HLERequestContext& ctx); + void FinalizeSevenSixAxisSensor(HLERequestContext& ctx); + void ResetSevenSixAxisSensorTimestamp(HLERequestContext& ctx); + void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void GetPalmaConnectionHandle(HLERequestContext& ctx); + void InitializePalma(HLERequestContext& ctx); + void AcquirePalmaOperationCompleteEvent(HLERequestContext& ctx); + void GetPalmaOperationInfo(HLERequestContext& ctx); + void PlayPalmaActivity(HLERequestContext& ctx); + void SetPalmaFrModeType(HLERequestContext& ctx); + void ReadPalmaStep(HLERequestContext& ctx); + void EnablePalmaStep(HLERequestContext& ctx); + void ResetPalmaStep(HLERequestContext& ctx); + void ReadPalmaApplicationSection(HLERequestContext& ctx); + void WritePalmaApplicationSection(HLERequestContext& ctx); + void ReadPalmaUniqueCode(HLERequestContext& ctx); + void SetPalmaUniqueCodeInvalid(HLERequestContext& ctx); + void WritePalmaActivityEntry(HLERequestContext& ctx); + void WritePalmaRgbLedPatternEntry(HLERequestContext& ctx); + void WritePalmaWaveEntry(HLERequestContext& ctx); + void SetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); + void GetPalmaDataBaseIdentificationVersion(HLERequestContext& ctx); + void SuspendPalmaFeature(HLERequestContext& ctx); + void GetPalmaOperationResult(HLERequestContext& ctx); + void ReadPalmaPlayLog(HLERequestContext& ctx); + void ResetPalmaPlayLog(HLERequestContext& ctx); + void SetIsPalmaAllConnectable(HLERequestContext& ctx); + void SetIsPalmaPairedConnectable(HLERequestContext& ctx); + void PairPalma(HLERequestContext& ctx); + void SetPalmaBoostMode(HLERequestContext& ctx); + void CancelWritePalmaWaveEntry(HLERequestContext& ctx); + void EnablePalmaBoostMode(HLERequestContext& ctx); + void GetPalmaBluetoothAddress(HLERequestContext& ctx); + void SetDisallowedPalmaConnection(HLERequestContext& ctx); + void SetNpadCommunicationMode(HLERequestContext& ctx); + void GetNpadCommunicationMode(HLERequestContext& ctx); + void SetTouchScreenConfiguration(HLERequestContext& ctx); + void IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx); + + std::shared_ptr resource_manager; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp new file mode 100644 index 000000000..83cfadada --- /dev/null +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -0,0 +1,304 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "core/hid/hid_core.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/touchscreen.h" +#include "core/hle/service/hid/errors.h" +#include "core/hle/service/hid/hid_system_server.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" + +namespace Service::HID { + +IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr resource) + : ServiceFramework{system_, "hid:sys"}, service_context{system_, service_name}, + resource_manager{resource} { + // clang-format off + static const FunctionInfo functions[] = { + {31, nullptr, "SendKeyboardLockKeyEvent"}, + {101, nullptr, "AcquireHomeButtonEventHandle"}, + {111, nullptr, "ActivateHomeButton"}, + {121, nullptr, "AcquireSleepButtonEventHandle"}, + {131, nullptr, "ActivateSleepButton"}, + {141, nullptr, "AcquireCaptureButtonEventHandle"}, + {151, nullptr, "ActivateCaptureButton"}, + {161, nullptr, "GetPlatformConfig"}, + {210, nullptr, "AcquireNfcDeviceUpdateEventHandle"}, + {211, nullptr, "GetNpadsWithNfc"}, + {212, nullptr, "AcquireNfcActivateEventHandle"}, + {213, nullptr, "ActivateNfc"}, + {214, nullptr, "GetXcdHandleForNpadWithNfc"}, + {215, nullptr, "IsNfcActivated"}, + {230, nullptr, "AcquireIrSensorEventHandle"}, + {231, nullptr, "ActivateIrSensor"}, + {232, nullptr, "GetIrSensorState"}, + {233, nullptr, "GetXcdHandleForNpadWithIrSensor"}, + {301, nullptr, "ActivateNpadSystem"}, + {303, &IHidSystemServer::ApplyNpadSystemCommonPolicy, "ApplyNpadSystemCommonPolicy"}, + {304, nullptr, "EnableAssigningSingleOnSlSrPress"}, + {305, nullptr, "DisableAssigningSingleOnSlSrPress"}, + {306, &IHidSystemServer::GetLastActiveNpad, "GetLastActiveNpad"}, + {307, nullptr, "GetNpadSystemExtStyle"}, + {308, nullptr, "ApplyNpadSystemCommonPolicyFull"}, + {309, nullptr, "GetNpadFullKeyGripColor"}, + {310, nullptr, "GetMaskedSupportedNpadStyleSet"}, + {311, nullptr, "SetNpadPlayerLedBlinkingDevice"}, + {312, nullptr, "SetSupportedNpadStyleSetAll"}, + {313, nullptr, "GetNpadCaptureButtonAssignment"}, + {314, nullptr, "GetAppletFooterUiType"}, + {315, nullptr, "GetAppletDetailedUiType"}, + {316, nullptr, "GetNpadInterfaceType"}, + {317, nullptr, "GetNpadLeftRightInterfaceType"}, + {318, nullptr, "HasBattery"}, + {319, nullptr, "HasLeftRightBattery"}, + {321, &IHidSystemServer::GetUniquePadsFromNpad, "GetUniquePadsFromNpad"}, + {322, nullptr, "GetIrSensorState"}, + {323, nullptr, "GetXcdHandleForNpadWithIrSensor"}, + {324, nullptr, "GetUniquePadButtonSet"}, + {325, nullptr, "GetUniquePadColor"}, + {326, nullptr, "GetUniquePadAppletDetailedUiType"}, + {327, nullptr, "GetAbstractedPadIdDataFromNpad"}, + {328, nullptr, "AttachAbstractedPadToNpad"}, + {329, nullptr, "DetachAbstractedPadAll"}, + {330, nullptr, "CheckAbstractedPadConnection"}, + {500, nullptr, "SetAppletResourceUserId"}, + {501, nullptr, "RegisterAppletResourceUserId"}, + {502, nullptr, "UnregisterAppletResourceUserId"}, + {503, nullptr, "EnableAppletToGetInput"}, + {504, nullptr, "SetAruidValidForVibration"}, + {505, nullptr, "EnableAppletToGetSixAxisSensor"}, + {506, nullptr, "EnableAppletToGetPadInput"}, + {507, nullptr, "EnableAppletToGetTouchScreen"}, + {510, nullptr, "SetVibrationMasterVolume"}, + {511, nullptr, "GetVibrationMasterVolume"}, + {512, nullptr, "BeginPermitVibrationSession"}, + {513, nullptr, "EndPermitVibrationSession"}, + {514, nullptr, "Unknown514"}, + {520, nullptr, "EnableHandheldHids"}, + {521, nullptr, "DisableHandheldHids"}, + {522, nullptr, "SetJoyConRailEnabled"}, + {523, nullptr, "IsJoyConRailEnabled"}, + {524, nullptr, "IsHandheldHidsEnabled"}, + {525, nullptr, "IsJoyConAttachedOnAllRail"}, + {540, nullptr, "AcquirePlayReportControllerUsageUpdateEvent"}, + {541, nullptr, "GetPlayReportControllerUsages"}, + {542, nullptr, "AcquirePlayReportRegisteredDeviceUpdateEvent"}, + {543, nullptr, "GetRegisteredDevicesOld"}, + {544, nullptr, "AcquireConnectionTriggerTimeoutEvent"}, + {545, nullptr, "SendConnectionTrigger"}, + {546, nullptr, "AcquireDeviceRegisteredEventForControllerSupport"}, + {547, nullptr, "GetAllowedBluetoothLinksCount"}, + {548, nullptr, "GetRegisteredDevices"}, + {549, nullptr, "GetConnectableRegisteredDevices"}, + {700, nullptr, "ActivateUniquePad"}, + {702, nullptr, "AcquireUniquePadConnectionEventHandle"}, + {703, nullptr, "GetUniquePadIds"}, + {751, &IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle, "AcquireJoyDetachOnBluetoothOffEventHandle"}, + {800, nullptr, "ListSixAxisSensorHandles"}, + {801, nullptr, "IsSixAxisSensorUserCalibrationSupported"}, + {802, nullptr, "ResetSixAxisSensorCalibrationValues"}, + {803, nullptr, "StartSixAxisSensorUserCalibration"}, + {804, nullptr, "CancelSixAxisSensorUserCalibration"}, + {805, nullptr, "GetUniquePadBluetoothAddress"}, + {806, nullptr, "DisconnectUniquePad"}, + {807, nullptr, "GetUniquePadType"}, + {808, nullptr, "GetUniquePadInterface"}, + {809, nullptr, "GetUniquePadSerialNumber"}, + {810, nullptr, "GetUniquePadControllerNumber"}, + {811, nullptr, "GetSixAxisSensorUserCalibrationStage"}, + {812, nullptr, "GetConsoleUniqueSixAxisSensorHandle"}, + {821, nullptr, "StartAnalogStickManualCalibration"}, + {822, nullptr, "RetryCurrentAnalogStickManualCalibrationStage"}, + {823, nullptr, "CancelAnalogStickManualCalibration"}, + {824, nullptr, "ResetAnalogStickManualCalibration"}, + {825, nullptr, "GetAnalogStickState"}, + {826, nullptr, "GetAnalogStickManualCalibrationStage"}, + {827, nullptr, "IsAnalogStickButtonPressed"}, + {828, nullptr, "IsAnalogStickInReleasePosition"}, + {829, nullptr, "IsAnalogStickInCircumference"}, + {830, nullptr, "SetNotificationLedPattern"}, + {831, nullptr, "SetNotificationLedPatternWithTimeout"}, + {832, nullptr, "PrepareHidsForNotificationWake"}, + {850, &IHidSystemServer::IsUsbFullKeyControllerEnabled, "IsUsbFullKeyControllerEnabled"}, + {851, nullptr, "EnableUsbFullKeyController"}, + {852, nullptr, "IsUsbConnected"}, + {870, nullptr, "IsHandheldButtonPressedOnConsoleMode"}, + {900, nullptr, "ActivateInputDetector"}, + {901, nullptr, "NotifyInputDetector"}, + {1000, nullptr, "InitializeFirmwareUpdate"}, + {1001, nullptr, "GetFirmwareVersion"}, + {1002, nullptr, "GetAvailableFirmwareVersion"}, + {1003, nullptr, "IsFirmwareUpdateAvailable"}, + {1004, nullptr, "CheckFirmwareUpdateRequired"}, + {1005, nullptr, "StartFirmwareUpdate"}, + {1006, nullptr, "AbortFirmwareUpdate"}, + {1007, nullptr, "GetFirmwareUpdateState"}, + {1008, nullptr, "ActivateAudioControl"}, + {1009, nullptr, "AcquireAudioControlEventHandle"}, + {1010, nullptr, "GetAudioControlStates"}, + {1011, nullptr, "DeactivateAudioControl"}, + {1050, nullptr, "IsSixAxisSensorAccurateUserCalibrationSupported"}, + {1051, nullptr, "StartSixAxisSensorAccurateUserCalibration"}, + {1052, nullptr, "CancelSixAxisSensorAccurateUserCalibration"}, + {1053, nullptr, "GetSixAxisSensorAccurateUserCalibrationState"}, + {1100, nullptr, "GetHidbusSystemServiceObject"}, + {1120, nullptr, "SetFirmwareHotfixUpdateSkipEnabled"}, + {1130, nullptr, "InitializeUsbFirmwareUpdate"}, + {1131, nullptr, "FinalizeUsbFirmwareUpdate"}, + {1132, nullptr, "CheckUsbFirmwareUpdateRequired"}, + {1133, nullptr, "StartUsbFirmwareUpdate"}, + {1134, nullptr, "GetUsbFirmwareUpdateState"}, + {1150, nullptr, "SetTouchScreenMagnification"}, + {1151, nullptr, "GetTouchScreenFirmwareVersion"}, + {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, + {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, + {1154, nullptr, "IsFirmwareAvailableForNotification"}, + {1155, nullptr, "SetForceHandheldStyleVibration"}, + {1156, nullptr, "SendConnectionTriggerWithoutTimeoutEvent"}, + {1157, nullptr, "CancelConnectionTrigger"}, + {1200, nullptr, "IsButtonConfigSupported"}, + {1201, nullptr, "IsButtonConfigEmbeddedSupported"}, + {1202, nullptr, "DeleteButtonConfig"}, + {1203, nullptr, "DeleteButtonConfigEmbedded"}, + {1204, nullptr, "SetButtonConfigEnabled"}, + {1205, nullptr, "SetButtonConfigEmbeddedEnabled"}, + {1206, nullptr, "IsButtonConfigEnabled"}, + {1207, nullptr, "IsButtonConfigEmbeddedEnabled"}, + {1208, nullptr, "SetButtonConfigEmbedded"}, + {1209, nullptr, "SetButtonConfigFull"}, + {1210, nullptr, "SetButtonConfigLeft"}, + {1211, nullptr, "SetButtonConfigRight"}, + {1212, nullptr, "GetButtonConfigEmbedded"}, + {1213, nullptr, "GetButtonConfigFull"}, + {1214, nullptr, "GetButtonConfigLeft"}, + {1215, nullptr, "GetButtonConfigRight"}, + {1250, nullptr, "IsCustomButtonConfigSupported"}, + {1251, nullptr, "IsDefaultButtonConfigEmbedded"}, + {1252, nullptr, "IsDefaultButtonConfigFull"}, + {1253, nullptr, "IsDefaultButtonConfigLeft"}, + {1254, nullptr, "IsDefaultButtonConfigRight"}, + {1255, nullptr, "IsButtonConfigStorageEmbeddedEmpty"}, + {1256, nullptr, "IsButtonConfigStorageFullEmpty"}, + {1257, nullptr, "IsButtonConfigStorageLeftEmpty"}, + {1258, nullptr, "IsButtonConfigStorageRightEmpty"}, + {1259, nullptr, "GetButtonConfigStorageEmbeddedDeprecated"}, + {1260, nullptr, "GetButtonConfigStorageFullDeprecated"}, + {1261, nullptr, "GetButtonConfigStorageLeftDeprecated"}, + {1262, nullptr, "GetButtonConfigStorageRightDeprecated"}, + {1263, nullptr, "SetButtonConfigStorageEmbeddedDeprecated"}, + {1264, nullptr, "SetButtonConfigStorageFullDeprecated"}, + {1265, nullptr, "SetButtonConfigStorageLeftDeprecated"}, + {1266, nullptr, "SetButtonConfigStorageRightDeprecated"}, + {1267, nullptr, "DeleteButtonConfigStorageEmbedded"}, + {1268, nullptr, "DeleteButtonConfigStorageFull"}, + {1269, nullptr, "DeleteButtonConfigStorageLeft"}, + {1270, nullptr, "DeleteButtonConfigStorageRight"}, + {1271, nullptr, "IsUsingCustomButtonConfig"}, + {1272, nullptr, "IsAnyCustomButtonConfigEnabled"}, + {1273, nullptr, "SetAllCustomButtonConfigEnabled"}, + {1274, nullptr, "SetDefaultButtonConfig"}, + {1275, nullptr, "SetAllDefaultButtonConfig"}, + {1276, nullptr, "SetHidButtonConfigEmbedded"}, + {1277, nullptr, "SetHidButtonConfigFull"}, + {1278, nullptr, "SetHidButtonConfigLeft"}, + {1279, nullptr, "SetHidButtonConfigRight"}, + {1280, nullptr, "GetHidButtonConfigEmbedded"}, + {1281, nullptr, "GetHidButtonConfigFull"}, + {1282, nullptr, "GetHidButtonConfigLeft"}, + {1283, nullptr, "GetHidButtonConfigRight"}, + {1284, nullptr, "GetButtonConfigStorageEmbedded"}, + {1285, nullptr, "GetButtonConfigStorageFull"}, + {1286, nullptr, "GetButtonConfigStorageLeft"}, + {1287, nullptr, "GetButtonConfigStorageRight"}, + {1288, nullptr, "SetButtonConfigStorageEmbedded"}, + {1289, nullptr, "SetButtonConfigStorageFull"}, + {1290, nullptr, "DeleteButtonConfigStorageRight"}, + {1291, nullptr, "DeleteButtonConfigStorageRight"}, + }; + // clang-format on + + RegisterHandlers(functions); + + joy_detach_event = service_context.CreateEvent("HidSys::JoyDetachEvent"); +} + +IHidSystemServer::~IHidSystemServer() { + service_context.CloseEvent(joy_detach_event); +}; + +void IHidSystemServer::ApplyNpadSystemCommonPolicy(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "called"); + + GetResourceManager() + ->GetController(HidController::NPad) + .ApplyNpadSystemCommonPolicy(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::GetLastActiveNpad(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(system.HIDCore().GetLastActiveController()); +} + +void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); + + const std::vector unique_pads{}; + + ctx.WriteBuffer(unique_pads); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast(unique_pads.size())); +} + +void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(joy_detach_event->GetReadableEvent()); +} + +void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { + const bool is_enabled = false; + + LOG_WARNING(Service_HID, "(STUBBED) called, is_enabled={}", is_enabled); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(is_enabled); +} + +void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + Core::HID::TouchScreenConfigurationForNx touchscreen_config{ + .mode = Core::HID::TouchScreenModeForNx::Finger, + }; + + if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && + touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { + touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; + } + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(touchscreen_config); +} + +std::shared_ptr IHidSystemServer::GetResourceManager() { + resource_manager->Initialize(); + return resource_manager; +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h new file mode 100644 index 000000000..d4b3910fa --- /dev/null +++ b/src/core/hle/service/hid/hid_system_server.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Core { +class System; +} + +namespace Kernel { +class KEvent; +} + +namespace Service::HID { +class ResourceManager; + +class IHidSystemServer final : public ServiceFramework { +public: + explicit IHidSystemServer(Core::System& system_, std::shared_ptr resource); + ~IHidSystemServer() override; + +private: + void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); + void GetLastActiveNpad(HLERequestContext& ctx); + void GetUniquePadsFromNpad(HLERequestContext& ctx); + void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); + void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); + + std::shared_ptr GetResourceManager(); + + Kernel::KEvent* joy_detach_event; + KernelHelpers::ServiceContext service_context; + std::shared_ptr resource_manager; +}; + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index a8fa19025..c8e6dab17 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -3,15 +3,12 @@ #pragma once +#include "core/core.h" #include "core/hid/hid_types.h" #include "core/hid/irs_types.h" #include "core/hle/service/hid/irsensor/processor_base.h" #include "core/hle/service/service.h" -namespace Core { -class System; -} - namespace Core::HID { class EmulatedController; } // namespace Core::HID diff --git a/src/core/hle/service/hid/resource_manager.cpp b/src/core/hle/service/hid/resource_manager.cpp new file mode 100644 index 000000000..4e462f3b3 --- /dev/null +++ b/src/core/hle/service/hid/resource_manager.cpp @@ -0,0 +1,192 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/core_timing.h" +#include "core/hid/hid_core.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/hid/resource_manager.h" +#include "core/hle/service/ipc_helpers.h" + +#include "core/hle/service/hid/controllers/console_sixaxis.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/hid/controllers/debug_pad.h" +#include "core/hle/service/hid/controllers/gesture.h" +#include "core/hle/service/hid/controllers/keyboard.h" +#include "core/hle/service/hid/controllers/mouse.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "core/hle/service/hid/controllers/palma.h" +#include "core/hle/service/hid/controllers/stubbed.h" +#include "core/hle/service/hid/controllers/touchscreen.h" +#include "core/hle/service/hid/controllers/xpad.h" + +namespace Service::HID { + +// Updating period for each HID device. +// Period time is obtained by measuring the number of samples in a second on HW using a homebrew +// Correct npad_update_ns is 4ms this is overclocked to lower input lag +constexpr auto npad_update_ns = std::chrono::nanoseconds{1 * 1000 * 1000}; // (1ms, 1000Hz) +constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) +constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) +constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) + +ResourceManager::ResourceManager(Core::System& system_) + : system{system_}, service_context{system_, "hid"} {} + +ResourceManager::~ResourceManager() = default; + +void ResourceManager::Initialize() { + if (is_initialized) { + return; + } + + u8* shared_memory = system.Kernel().GetHidSharedMem().GetPointer(); + MakeController(HidController::DebugPad, shared_memory); + MakeController(HidController::Touchscreen, shared_memory); + MakeController(HidController::Mouse, shared_memory); + MakeController(HidController::Keyboard, shared_memory); + MakeController(HidController::XPad, shared_memory); + MakeController(HidController::HomeButton, shared_memory); + MakeController(HidController::SleepButton, shared_memory); + MakeController(HidController::CaptureButton, shared_memory); + MakeController(HidController::InputDetector, shared_memory); + MakeController(HidController::UniquePad, shared_memory); + MakeControllerWithServiceContext(HidController::NPad, shared_memory); + MakeController(HidController::Gesture, shared_memory); + MakeController(HidController::ConsoleSixAxisSensor, shared_memory); + MakeController(HidController::DebugMouse, shared_memory); + MakeControllerWithServiceContext(HidController::Palma, shared_memory); + + // Homebrew doesn't try to activate some controllers, so we activate them by default + GetController(HidController::NPad).ActivateController(); + GetController(HidController::Touchscreen).ActivateController(); + + GetController(HidController::HomeButton).SetCommonHeaderOffset(0x4C00); + GetController(HidController::SleepButton).SetCommonHeaderOffset(0x4E00); + GetController(HidController::CaptureButton).SetCommonHeaderOffset(0x5000); + GetController(HidController::InputDetector).SetCommonHeaderOffset(0x5200); + GetController(HidController::UniquePad).SetCommonHeaderOffset(0x5A00); + GetController(HidController::DebugMouse).SetCommonHeaderOffset(0x3DC00); + + system.HIDCore().ReloadInputDevices(); + is_initialized = true; +} + +void ResourceManager::ActivateController(HidController controller) { + controllers[static_cast(controller)]->ActivateController(); +} + +void ResourceManager::DeactivateController(HidController controller) { + controllers[static_cast(controller)]->DeactivateController(); +} + +void ResourceManager::UpdateControllers(std::uintptr_t user_data, + std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + + for (const auto& controller : controllers) { + // Keyboard has it's own update event + if (controller == controllers[static_cast(HidController::Keyboard)]) { + continue; + } + // Mouse has it's own update event + if (controller == controllers[static_cast(HidController::Mouse)]) { + continue; + } + // Npad has it's own update event + if (controller == controllers[static_cast(HidController::NPad)]) { + continue; + } + controller->OnUpdate(core_timing); + } +} + +void ResourceManager::UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + + controllers[static_cast(HidController::NPad)]->OnUpdate(core_timing); +} + +void ResourceManager::UpdateMouseKeyboard(std::uintptr_t user_data, + std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + + controllers[static_cast(HidController::Mouse)]->OnUpdate(core_timing); + controllers[static_cast(HidController::Keyboard)]->OnUpdate(core_timing); +} + +void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { + auto& core_timing = system.CoreTiming(); + + controllers[static_cast(HidController::NPad)]->OnMotionUpdate(core_timing); +} + +IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr resource) + : ServiceFramework{system_, "IAppletResource"} { + static const FunctionInfo functions[] = { + {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, + }; + RegisterHandlers(functions); + + resource->Initialize(); + + // Register update callbacks + npad_update_event = Core::Timing::CreateEvent( + "HID::UpdatePadCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional { + const auto guard = LockService(); + resource->UpdateNpad(user_data, ns_late); + return std::nullopt; + }); + default_update_event = Core::Timing::CreateEvent( + "HID::UpdateDefaultCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional { + const auto guard = LockService(); + resource->UpdateControllers(user_data, ns_late); + return std::nullopt; + }); + mouse_keyboard_update_event = Core::Timing::CreateEvent( + "HID::UpdateMouseKeyboardCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional { + const auto guard = LockService(); + resource->UpdateMouseKeyboard(user_data, ns_late); + return std::nullopt; + }); + motion_update_event = Core::Timing::CreateEvent( + "HID::UpdateMotionCallback", + [this, resource](std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late) + -> std::optional { + const auto guard = LockService(); + resource->UpdateMotion(user_data, ns_late); + return std::nullopt; + }); + + system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); + system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, + default_update_event); + system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, + mouse_keyboard_update_event); + system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, + motion_update_event); +} + +IAppletResource::~IAppletResource() { + system.CoreTiming().UnscheduleEvent(npad_update_event, 0); + system.CoreTiming().UnscheduleEvent(default_update_event, 0); + system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0); + system.CoreTiming().UnscheduleEvent(motion_update_event, 0); +} + +void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) { + LOG_DEBUG(Service_HID, "called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(&system.Kernel().GetHidSharedMem()); +} + +} // namespace Service::HID diff --git a/src/core/hle/service/hid/resource_manager.h b/src/core/hle/service/hid/resource_manager.h new file mode 100644 index 000000000..81b17f9a9 --- /dev/null +++ b/src/core/hle/service/hid/resource_manager.h @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include "core/core.h" +#include "core/hle/service/hid/controllers/controller_base.h" +#include "core/hle/service/kernel_helpers.h" +#include "core/hle/service/service.h" + +namespace Core::Timing { +struct EventType; +} + +namespace Core::HID { +class HIDCore; +} + +namespace Service::HID { + +enum class HidController : std::size_t { + DebugPad, + Touchscreen, + Mouse, + Keyboard, + XPad, + HomeButton, + SleepButton, + CaptureButton, + InputDetector, + UniquePad, + NPad, + Gesture, + ConsoleSixAxisSensor, + DebugMouse, + Palma, + + MaxControllers, +}; +class ResourceManager { +public: + explicit ResourceManager(Core::System& system_); + ~ResourceManager(); + + template + T& GetController(HidController controller) { + return static_cast(*controllers[static_cast(controller)]); + } + + template + const T& GetController(HidController controller) const { + return static_cast(*controllers[static_cast(controller)]); + } + + void Initialize(); + void ActivateController(HidController controller); + void DeactivateController(HidController controller); + + void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateNpad(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateMouseKeyboard(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); + +private: + template + void MakeController(HidController controller, u8* shared_memory) { + if constexpr (std::is_constructible_v) { + controllers[static_cast(controller)] = + std::make_unique(system, shared_memory); + } else { + controllers[static_cast(controller)] = + std::make_unique(system.HIDCore(), shared_memory); + } + } + + template + void MakeControllerWithServiceContext(HidController controller, u8* shared_memory) { + controllers[static_cast(controller)] = + std::make_unique(system.HIDCore(), shared_memory, service_context); + } + + bool is_initialized{false}; + std::array, static_cast(HidController::MaxControllers)> + controllers{}; + + Core::System& system; + KernelHelpers::ServiceContext service_context; +}; + +class IAppletResource final : public ServiceFramework { +public: + explicit IAppletResource(Core::System& system_, std::shared_ptr resource); + ~IAppletResource() override; + +private: + void GetSharedMemoryHandle(HLERequestContext& ctx); + + std::shared_ptr npad_update_event; + std::shared_ptr default_update_event; + std::shared_ptr mouse_keyboard_update_event; + std::shared_ptr motion_update_event; +}; + +} // namespace Service::HID diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 53a89cc8f..da140c01c 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -10,7 +10,8 @@ #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" #include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid.h" +#include "core/hle/service/hid/hid_server.h" +#include "core/hle/service/hid/resource_manager.h" #include "core/hle/service/sm/sm.h" #include "core/memory.h" #include "core/memory/cheat_engine.h" @@ -54,13 +55,13 @@ void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) } u64 StandardVmCallbacks::HidKeysDown() { - const auto hid = system.ServiceManager().GetService("hid"); + const auto hid = system.ServiceManager().GetService("hid"); if (hid == nullptr) { LOG_WARNING(CheatEngine, "Attempted to read input state, but hid is not initialized!"); return 0; } - const auto applet_resource = hid->GetAppletResource(); + const auto applet_resource = hid->GetResourceManager(); if (applet_resource == nullptr) { LOG_WARNING(CheatEngine, "Attempted to read input state, but applet resource is not initialized!"); diff --git a/src/yuzu/applets/qt_controller.cpp b/src/yuzu/applets/qt_controller.cpp index 515cb7ce6..9e5319716 100644 --- a/src/yuzu/applets/qt_controller.cpp +++ b/src/yuzu/applets/qt_controller.cpp @@ -13,7 +13,6 @@ #include "core/hid/hid_core.h" #include "core/hid/hid_types.h" #include "core/hle/service/hid/controllers/npad.h" -#include "core/hle/service/hid/hid.h" #include "core/hle/service/sm/sm.h" #include "ui_qt_controller.h" #include "yuzu/applets/qt_controller.h" -- cgit v1.2.3 From 20a17607ae86321fc188c9a71ed72f8eb15cfadb Mon Sep 17 00:00:00 2001 From: Liam Date: Sun, 19 Nov 2023 11:49:51 -0500 Subject: qt: fix linux build --- src/yuzu/util/util.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/yuzu') diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 7b2a47496..e22cf84bf 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp @@ -4,7 +4,10 @@ #include #include #include + +#include "common/logging/log.h" #include "yuzu/util/util.h" + #ifdef _WIN32 #include #include "common/fs/file.h" -- cgit v1.2.3 From cb004d1ba1bde740ba10418ac3f3cfd2db8ffac7 Mon Sep 17 00:00:00 2001 From: FrozenAra Date: Wed, 1 Nov 2023 01:09:33 +0100 Subject: Implemented qlaunch version of the controller applet --- src/core/hid/hid_types.h | 7 + src/core/hle/service/am/am.cpp | 91 ++++++- src/core/hle/service/am/am.h | 3 + .../hle/service/am/applets/applet_controller.h | 2 +- src/core/hle/service/btm/btm.cpp | 56 +++- src/core/hle/service/hid/controllers/npad.cpp | 16 ++ src/core/hle/service/hid/controllers/npad.h | 72 ++--- src/core/hle/service/hid/hid_server.cpp | 4 +- src/core/hle/service/hid/hid_system_server.cpp | 293 +++++++++++++++++++-- src/core/hle/service/hid/hid_system_server.h | 23 ++ src/core/hle/service/ldn/ldn.cpp | 10 +- src/core/hle/service/set/set_sys.cpp | 30 ++- src/core/hle/service/set/set_sys.h | 12 + src/yuzu/main.cpp | 41 ++- src/yuzu/main.h | 1 + src/yuzu/main.ui | 22 +- 16 files changed, 594 insertions(+), 89 deletions(-) (limited to 'src/yuzu') diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h index 9d48cd90e..70fcc6b69 100644 --- a/src/core/hid/hid_types.h +++ b/src/core/hid/hid_types.h @@ -218,6 +218,13 @@ enum class NpadIdType : u32 { Invalid = 0xFFFFFFFF, }; +enum class NpadInterfaceType : u8 { + Bluetooth = 1, + Rail = 2, + Usb = 3, + Embedded = 4, +}; + // This is nn::hid::NpadStyleIndex enum class NpadStyleIndex : u8 { None = 0, diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index cc643ea09..a266d7c21 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -13,6 +13,7 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/savedata_factory.h" +#include "core/hid/hid_types.h" #include "core/hle/kernel/k_event.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/result.h" @@ -21,6 +22,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applet_cabinet.h" +#include "core/hle/service/am/applets/applet_controller.h" #include "core/hle/service/am/applets/applet_mii_edit_types.h" #include "core/hle/service/am/applets/applet_profile_select.h" #include "core/hle/service/am/applets/applet_software_keyboard_types.h" @@ -35,6 +37,7 @@ #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/hid/controllers/npad.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ns/ns.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" @@ -73,7 +76,7 @@ IWindowController::IWindowController(Core::System& system_) static const FunctionInfo functions[] = { {0, nullptr, "CreateWindow"}, {1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"}, - {2, nullptr, "GetAppletResourceUserIdOfCallerApplet"}, + {2, &IWindowController::GetAppletResourceUserIdOfCallerApplet, "GetAppletResourceUserIdOfCallerApplet"}, {10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"}, {11, nullptr, "ReleaseForegroundRights"}, {12, nullptr, "RejectToChangeIntoBackground"}, @@ -97,6 +100,16 @@ void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) { rb.Push(process_id); } +void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) { + const u64 process_id = 0; + + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(process_id); +} + void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); IPC::ResponseBuilder rb{ctx, 2}; @@ -1565,7 +1578,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) {6, nullptr, "GetPopInteractiveInDataEvent"}, {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"}, {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"}, - {12, nullptr, "GetMainAppletIdentityInfo"}, + {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"}, {13, nullptr, "CanUseApplicationCore"}, {14, &ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo, "GetCallerAppletIdentityInfo"}, {15, nullptr, "GetMainAppletApplicationControlProperty"}, @@ -1609,6 +1622,9 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_) case Applets::AppletId::SoftwareKeyboard: PushInShowSoftwareKeyboard(); break; + case Applets::AppletId::Controller: + PushInShowController(); + break; default: break; } @@ -1666,13 +1682,33 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) { rb.PushRaw(applet_info); } -void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { +void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) { struct AppletIdentityInfo { Applets::AppletId applet_id; INSERT_PADDING_BYTES(0x4); u64 application_id; }; + static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); + + LOG_WARNING(Service_AM, "(STUBBED) called"); + + const AppletIdentityInfo applet_info{ + .applet_id = Applets::AppletId::QLaunch, + .application_id = 0x0100000000001000ull, + }; + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(applet_info); +} +void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) { + struct AppletIdentityInfo { + Applets::AppletId applet_id; + INSERT_PADDING_BYTES(0x4); + u64 application_id; + }; + static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size."); LOG_WARNING(Service_AM, "(STUBBED) called"); const AppletIdentityInfo applet_info{ @@ -1737,6 +1773,55 @@ void ILibraryAppletSelfAccessor::PushInShowAlbum() { queue_data.emplace_back(std::move(settings_data)); } +void ILibraryAppletSelfAccessor::PushInShowController() { + const Applets::CommonArguments common_args = { + .arguments_version = Applets::CommonArgumentVersion::Version3, + .size = Applets::CommonArgumentSize::Version3, + .library_version = static_cast(Applets::ControllerAppletVersion::Version8), + .theme_color = Applets::ThemeColor::BasicBlack, + .play_startup_sound = true, + .system_tick = system.CoreTiming().GetClockTicks(), + }; + + Applets::ControllerSupportArgNew user_args = { + .header = {.player_count_min = 1, + .player_count_max = 4, + .enable_take_over_connection = true, + .enable_left_justify = false, + .enable_permit_joy_dual = true, + .enable_single_mode = false, + .enable_identification_color = false}, + .identification_colors = {}, + .enable_explain_text = false, + .explain_text = {}, + }; + + Applets::ControllerSupportArgPrivate private_args = { + .arg_private_size = sizeof(Applets::ControllerSupportArgPrivate), + .arg_size = sizeof(Applets::ControllerSupportArgNew), + .is_home_menu = true, + .flag_1 = true, + .mode = Applets::ControllerSupportMode::ShowControllerSupport, + .caller = Applets::ControllerSupportCaller:: + Application, // switchbrew: Always zero except with + // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem, + // which sets this to the input param + .style_set = Core::HID::NpadStyleSet::None, + .joy_hold_type = 0, + }; + std::vector common_args_data(sizeof(common_args)); + std::vector private_args_data(sizeof(private_args)); + std::vector user_args_data(sizeof(user_args)); + + std::memcpy(common_args_data.data(), &common_args, sizeof(common_args)); + std::memcpy(private_args_data.data(), &private_args, sizeof(private_args)); + std::memcpy(user_args_data.data(), &user_args, sizeof(user_args)); + + queue_data.emplace_back(std::move(common_args_data)); + queue_data.emplace_back(std::move(private_args_data)); + queue_data.emplace_back(std::move(user_args_data)); +} + void ILibraryAppletSelfAccessor::PushInShowCabinetData() { const Applets::CommonArguments arguments{ .arguments_version = Applets::CommonArgumentVersion::Version3, diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 8f8cb8a9e..905a71b9f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -87,6 +87,7 @@ public: private: void GetAppletResourceUserId(HLERequestContext& ctx); + void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx); void AcquireForegroundRights(HLERequestContext& ctx); }; @@ -345,6 +346,7 @@ private: void PopInData(HLERequestContext& ctx); void PushOutData(HLERequestContext& ctx); void GetLibraryAppletInfo(HLERequestContext& ctx); + void GetMainAppletIdentityInfo(HLERequestContext& ctx); void ExitProcessAndReturn(HLERequestContext& ctx); void GetCallerAppletIdentityInfo(HLERequestContext& ctx); void GetDesirableKeyboardLayout(HLERequestContext& ctx); @@ -355,6 +357,7 @@ private: void PushInShowCabinetData(); void PushInShowMiiEditData(); void PushInShowSoftwareKeyboard(); + void PushInShowController(); std::deque> queue_data; }; diff --git a/src/core/hle/service/am/applets/applet_controller.h b/src/core/hle/service/am/applets/applet_controller.h index f6c64f633..9f839f3d7 100644 --- a/src/core/hle/service/am/applets/applet_controller.h +++ b/src/core/hle/service/am/applets/applet_controller.h @@ -56,7 +56,7 @@ enum class ControllerSupportResult : u32 { struct ControllerSupportArgPrivate { u32 arg_private_size{}; u32 arg_size{}; - bool flag_0{}; + bool is_home_menu{}; bool flag_1{}; ControllerSupportMode mode{}; ControllerSupportCaller caller{}; diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp index 8069f75b7..c65e32489 100644 --- a/src/core/hle/service/btm/btm.cpp +++ b/src/core/hle/service/btm/btm.cpp @@ -127,7 +127,7 @@ public: private: void GetCore(HLERequestContext& ctx) { - LOG_DEBUG(Service_BTM, "called"); + LOG_WARNING(Service_BTM, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); @@ -263,13 +263,13 @@ public: explicit IBtmSystemCore(Core::System& system_) : ServiceFramework{system_, "IBtmSystemCore"} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "StartGamepadPairing"}, - {1, nullptr, "CancelGamepadPairing"}, + {0, &IBtmSystemCore::StartGamepadPairing, "StartGamepadPairing"}, + {1, &IBtmSystemCore::CancelGamepadPairing, "CancelGamepadPairing"}, {2, nullptr, "ClearGamepadPairingDatabase"}, {3, nullptr, "GetPairedGamepadCount"}, {4, nullptr, "EnableRadio"}, {5, nullptr, "DisableRadio"}, - {6, nullptr, "GetRadioOnOff"}, + {6, &IBtmSystemCore::IsRadioEnabled, "IsRadioEnabled"}, {7, nullptr, "AcquireRadioEvent"}, {8, nullptr, "AcquireGamepadPairingEvent"}, {9, nullptr, "IsGamepadPairingStarted"}, @@ -280,18 +280,58 @@ public: {14, nullptr, "AcquireAudioDeviceConnectionEvent"}, {15, nullptr, "ConnectAudioDevice"}, {16, nullptr, "IsConnectingAudioDevice"}, - {17, nullptr, "GetConnectedAudioDevices"}, + {17, &IBtmSystemCore::GetConnectedAudioDevices, "GetConnectedAudioDevices"}, {18, nullptr, "DisconnectAudioDevice"}, {19, nullptr, "AcquirePairedAudioDeviceInfoChangedEvent"}, {20, nullptr, "GetPairedAudioDevices"}, {21, nullptr, "RemoveAudioDevicePairing"}, - {22, nullptr, "RequestAudioDeviceConnectionRejection"}, - {23, nullptr, "CancelAudioDeviceConnectionRejection"} + {22, &IBtmSystemCore::RequestAudioDeviceConnectionRejection, "RequestAudioDeviceConnectionRejection"}, + {23, &IBtmSystemCore::CancelAudioDeviceConnectionRejection, "CancelAudioDeviceConnectionRejection"} }; // clang-format on RegisterHandlers(functions); } + +private: + void IsRadioEnabled(HLERequestContext& ctx) { + LOG_DEBUG(Service_BTM, "(STUBBED) called"); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(true); + } + + void StartGamepadPairing(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CancelGamepadPairing(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void CancelAudioDeviceConnectionRejection(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } + + void GetConnectedAudioDevices(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); + } + + void RequestAudioDeviceConnectionRejection(HLERequestContext& ctx) { + LOG_WARNING(Service_BTM, "(STUBBED) called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } }; class BTM_SYS final : public ServiceFramework { @@ -308,7 +348,7 @@ public: private: void GetCore(HLERequestContext& ctx) { - LOG_DEBUG(Service_BTM, "called"); + LOG_WARNING(Service_BTM, "called"); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index d46bf917e..127af2b82 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -344,6 +344,7 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) { controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices, Common::Input::PollingMode::Active); } + SignalStyleSetChangedEvent(npad_id); WriteEmptyEntry(controller.shared_memory); hid_core.SetLastActiveController(npad_id); @@ -1726,4 +1727,19 @@ const Controller_NPad::SixaxisParameters& Controller_NPad::GetSixaxisState( } } +Controller_NPad::AppletDetailedUiType Controller_NPad::GetAppletDetailedUiType( + Core::HID::NpadIdType npad_id) { + + auto controller = GetControllerFromNpadIdType(npad_id); + auto shared_memory = controller.shared_memory; + Service::HID::Controller_NPad::AppletFooterUiType applet_footer_type = + shared_memory->applet_footer_type; + + Controller_NPad::AppletDetailedUiType detailed_ui_type{ + .ui_variant = 0, + .footer = applet_footer_type, + }; + return detailed_ui_type; +} + } // namespace Service::HID diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index e23b4986c..cd93abdd1 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -78,6 +78,46 @@ public: MaxActivationMode = 3, }; + // This is nn::hid::system::AppletFooterUiAttributesSet + struct AppletFooterUiAttributes { + INSERT_PADDING_BYTES(0x4); + }; + + // This is nn::hid::system::AppletFooterUiType + enum class AppletFooterUiType : u8 { + None = 0, + HandheldNone = 1, + HandheldJoyConLeftOnly = 2, + HandheldJoyConRightOnly = 3, + HandheldJoyConLeftJoyConRight = 4, + JoyDual = 5, + JoyDualLeftOnly = 6, + JoyDualRightOnly = 7, + JoyLeftHorizontal = 8, + JoyLeftVertical = 9, + JoyRightHorizontal = 10, + JoyRightVertical = 11, + SwitchProController = 12, + CompatibleProController = 13, + CompatibleJoyCon = 14, + LarkHvc1 = 15, + LarkHvc2 = 16, + LarkNesLeft = 17, + LarkNesRight = 18, + Lucia = 19, + Verification = 20, + Lagon = 21, + }; + + using AppletFooterUiVariant = u8; + + // This is "nn::hid::system::AppletDetailedUiType". + struct AppletDetailedUiType { + AppletFooterUiVariant ui_variant; + INSERT_PADDING_BYTES(0x2); + AppletFooterUiType footer; + }; + static_assert(sizeof(AppletDetailedUiType) == 0x4, "AppletDetailedUiType is an invalid size"); // This is nn::hid::NpadCommunicationMode enum class NpadCommunicationMode : u64 { Mode_5ms = 0, @@ -203,6 +243,7 @@ public: static Result IsDeviceHandleValid(const Core::HID::VibrationDeviceHandle& device_handle); static Result VerifyValidSixAxisSensorHandle( const Core::HID::SixAxisSensorHandle& device_handle); + AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); private: static constexpr std::size_t NPAD_COUNT = 10; @@ -360,37 +401,6 @@ private: static_assert(sizeof(NfcXcdDeviceHandleStateImpl) == 0x18, "NfcXcdDeviceHandleStateImpl is an invalid size"); - // This is nn::hid::system::AppletFooterUiAttributesSet - struct AppletFooterUiAttributes { - INSERT_PADDING_BYTES(0x4); - }; - - // This is nn::hid::system::AppletFooterUiType - enum class AppletFooterUiType : u8 { - None = 0, - HandheldNone = 1, - HandheldJoyConLeftOnly = 2, - HandheldJoyConRightOnly = 3, - HandheldJoyConLeftJoyConRight = 4, - JoyDual = 5, - JoyDualLeftOnly = 6, - JoyDualRightOnly = 7, - JoyLeftHorizontal = 8, - JoyLeftVertical = 9, - JoyRightHorizontal = 10, - JoyRightVertical = 11, - SwitchProController = 12, - CompatibleProController = 13, - CompatibleJoyCon = 14, - LarkHvc1 = 15, - LarkHvc2 = 16, - LarkNesLeft = 17, - LarkNesRight = 18, - Lucia = 19, - Verification = 20, - Lagon = 21, - }; - // This is nn::hid::NpadLarkType enum class NpadLarkType : u32 { Invalid, diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 0be6a7186..56f26a14a 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -1222,8 +1222,8 @@ void IHidServer::SetNpadJoyAssignmentModeDual(HLERequestContext& ctx) { controller.SetNpadMode(new_npad_id, parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual); - LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, - parameters.applet_resource_user_id); + LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id, + parameters.applet_resource_user_id); // Spams a lot when controller applet is open IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index 83cfadada..6f1902ee5 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -36,24 +36,24 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptrGetController(HidController::NPad) + .ApplyNpadSystemCommonPolicy(); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::GetNpadFullKeyGripColor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + Core::HID::NpadColor left_color{}; + Core::HID::NpadColor right_color{}; + // TODO: Get colors from Npad + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(left_color); + rb.PushRaw(right_color); +} + +void IHidSystemServer::GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_INFO(Service_HID, "(STUBBED) called"); + + Core::HID::NpadStyleSet supported_styleset = + GetResourceManager() + ->GetController(HidController::NPad) + .GetSupportedStyleSet() + .raw; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(supported_styleset); +} + +void IHidSystemServer::SetSupportedNpadStyleSetAll(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_INFO(Service_HID, "(STUBBED) called"); + + Core::HID::NpadStyleSet supported_styleset = + GetResourceManager() + ->GetController(HidController::NPad) + .GetSupportedStyleSet() + .raw; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(supported_styleset); +} + +void IHidSystemServer::GetAppletDetailedUiType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum()}; + + LOG_DEBUG(Service_HID, "called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + const Service::HID::Controller_NPad::AppletDetailedUiType detailed_ui_type = + GetResourceManager() + ->GetController(HidController::NPad) + .GetAppletDetailedUiType(npad_id_type); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(detailed_ui_type); +} + +void IHidSystemServer::GetNpadInterfaceType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); +} + +void IHidSystemServer::GetNpadLeftRightInterfaceType(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); + rb.PushEnum(Core::HID::NpadInterfaceType::Bluetooth); +} + +void IHidSystemServer::HasBattery(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(false); +} + +void IHidSystemServer::HasLeftRightBattery(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto npad_id_type{rp.PopEnum()}; + + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running + + struct LeftRightBattery { + bool left; + bool right; + }; + + LeftRightBattery left_right_battery{ + .left = false, + .right = false, + }; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.PushRaw(left_right_battery); +} + void IHidSystemServer::GetUniquePadsFromNpad(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto npad_id_type{rp.PopEnum()}; - LOG_WARNING(Service_HID, "(STUBBED) called, npad_id_type={}", npad_id_type); + LOG_DEBUG(Service_HID, "(STUBBED) called, npad_id_type={}", + npad_id_type); // Spams a lot when controller applet is running const std::vector unique_pads{}; - ctx.WriteBuffer(unique_pads); + if (!unique_pads.empty()) { + ctx.WriteBuffer(unique_pads); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); rb.Push(static_cast(unique_pads.size())); } +void IHidSystemServer::GetIrSensorState(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx) { + LOG_INFO(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent()); +} + +void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) { + LOG_INFO(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.Push(ResultSuccess); + rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent()); +} + +void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + struct RegisterData { + std::array data; + }; + static_assert(sizeof(RegisterData) == 0x68, "RegisterData is an invalid size"); + std::vector registered_devices{}; + + if (!registered_devices.empty()) { + ctx.WriteBuffer(registered_devices); + } + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(registered_devices.size()); +} + +void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2, 1}; + rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent()); + rb.Push(ResultSuccess); +} + +void IHidSystemServer::GetUniquePadIds(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(0); +} + void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx) { LOG_INFO(Service_AM, "called"); @@ -279,6 +501,31 @@ void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { rb.Push(is_enabled); } +void IHidSystemServer::IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx) { + const bool button_pressed = false; + + LOG_DEBUG(Service_HID, "(STUBBED) called, is_enabled={}", + button_pressed); // Spams a lot when controller applet is open + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(button_pressed); +} + +void IHidSystemServer::InitializeFirmwareUpdate(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + +void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { LOG_WARNING(Service_HID, "(STUBBED) called"); diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index d4b3910fa..822d5e5b9 100644 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h @@ -24,15 +24,38 @@ public: private: void ApplyNpadSystemCommonPolicy(HLERequestContext& ctx); + void EnableAssigningSingleOnSlSrPress(HLERequestContext& ctx); + void DisableAssigningSingleOnSlSrPress(HLERequestContext& ctx); void GetLastActiveNpad(HLERequestContext& ctx); + void ApplyNpadSystemCommonPolicyFull(HLERequestContext& ctx); + void GetNpadFullKeyGripColor(HLERequestContext& ctx); + void GetMaskedSupportedNpadStyleSet(HLERequestContext& ctx); + void SetSupportedNpadStyleSetAll(HLERequestContext& ctx); + void GetAppletDetailedUiType(HLERequestContext& ctx); + void GetNpadInterfaceType(HLERequestContext& ctx); + void GetNpadLeftRightInterfaceType(HLERequestContext& ctx); + void HasBattery(HLERequestContext& ctx); + void HasLeftRightBattery(HLERequestContext& ctx); void GetUniquePadsFromNpad(HLERequestContext& ctx); + void GetIrSensorState(HLERequestContext& ctx); + void AcquireConnectionTriggerTimeoutEvent(HLERequestContext& ctx); + void AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx); + void GetRegisteredDevices(HLERequestContext& ctx); + void AcquireUniquePadConnectionEventHandle(HLERequestContext& ctx); + void GetUniquePadIds(HLERequestContext& ctx); void AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestContext& ctx); void IsUsbFullKeyControllerEnabled(HLERequestContext& ctx); + void IsHandheldButtonPressedOnConsoleMode(HLERequestContext& ctx); + void InitializeFirmwareUpdate(HLERequestContext& ctx); + void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); std::shared_ptr GetResourceManager(); + Kernel::KEvent* acquire_connection_trigger_timeout_event; + Kernel::KEvent* acquire_device_registered_event; Kernel::KEvent* joy_detach_event; + Kernel::KEvent* unique_pad_connection_event; KernelHelpers::ServiceContext service_context; std::shared_ptr resource_manager; }; diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp index 7927f8264..961f89a14 100644 --- a/src/core/hle/service/ldn/ldn.cpp +++ b/src/core/hle/service/ldn/ldn.cpp @@ -115,12 +115,20 @@ public: {400, nullptr, "InitializeSystem"}, {401, nullptr, "FinalizeSystem"}, {402, nullptr, "SetOperationMode"}, - {403, nullptr, "InitializeSystem2"}, + {403, &ISystemLocalCommunicationService::InitializeSystem2, "InitializeSystem2"}, }; // clang-format on RegisterHandlers(functions); } + +private: + void InitializeSystem2(HLERequestContext& ctx) { + LOG_WARNING(Service_LDN, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } }; class IUserLocalCommunicationService final diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index ec3af80af..19c667b42 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -431,8 +431,7 @@ void SET_SYS::GetAutoUpdateEnableFlag(HLERequestContext& ctx) { void SET_SYS::GetBatteryPercentageFlag(HLERequestContext& ctx) { u8 battery_percentage_flag{1}; - LOG_WARNING(Service_SET, "(STUBBED) called, battery_percentage_flag={}", - battery_percentage_flag); + LOG_DEBUG(Service_SET, "(STUBBED) called, battery_percentage_flag={}", battery_percentage_flag); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); @@ -492,6 +491,29 @@ void SET_SYS::GetChineseTraditionalInputMethod(HLERequestContext& ctx) { rb.PushEnum(ChineseTraditionalInputMethod::Unknown0); } +void SET_SYS::GetHomeMenuScheme(HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "(STUBBED) called"); + + const HomeMenuScheme default_color = { + .main = 0xFF323232, + .back = 0xFF323232, + .sub = 0xFFFFFFFF, + .bezel = 0xFFFFFFFF, + .extra = 0xFF000000, + }; + + IPC::ResponseBuilder rb{ctx, 7}; + rb.Push(ResultSuccess); + rb.PushRaw(default_color); +} + +void SET_SYS::GetHomeMenuSchemeModel(HLERequestContext& ctx) { + LOG_WARNING(Service_SET, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(0); +} void SET_SYS::GetFieldTestingFlag(HLERequestContext& ctx) { LOG_WARNING(Service_SET, "(STUBBED) called"); @@ -674,7 +696,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {171, nullptr, "SetChineseTraditionalInputMethod"}, {172, nullptr, "GetPtmCycleCountReliability"}, {173, nullptr, "SetPtmCycleCountReliability"}, - {174, nullptr, "GetHomeMenuScheme"}, + {174, &SET_SYS::GetHomeMenuScheme, "GetHomeMenuScheme"}, {175, nullptr, "GetThemeSettings"}, {176, nullptr, "SetThemeSettings"}, {177, nullptr, "GetThemeKey"}, @@ -685,7 +707,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {182, nullptr, "SetT"}, {183, nullptr, "GetPlatformRegion"}, {184, nullptr, "SetPlatformRegion"}, - {185, nullptr, "GetHomeMenuSchemeModel"}, + {185, &SET_SYS::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, {186, nullptr, "GetMemoryUsageRateFlag"}, {187, nullptr, "GetTouchScreenMode"}, {188, nullptr, "SetTouchScreenMode"}, diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index c7dba2a9e..93023c6dd 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -269,6 +269,16 @@ private: }; static_assert(sizeof(EulaVersion) == 0x30, "EulaVersion is incorrect size"); + /// This is nn::settings::system::HomeMenuScheme + struct HomeMenuScheme { + u32 main; + u32 back; + u32 sub; + u32 bezel; + u32 extra; + }; + static_assert(sizeof(HomeMenuScheme) == 0x14, "HomeMenuScheme is incorrect size"); + void SetLanguageCode(HLERequestContext& ctx); void GetFirmwareVersion(HLERequestContext& ctx); void GetFirmwareVersion2(HLERequestContext& ctx); @@ -305,6 +315,8 @@ private: void GetKeyboardLayout(HLERequestContext& ctx); void GetChineseTraditionalInputMethod(HLERequestContext& ctx); void GetFieldTestingFlag(HLERequestContext& ctx); + void GetHomeMenuScheme(HLERequestContext& ctx); + void GetHomeMenuSchemeModel(HLERequestContext& ctx); AccountSettings account_settings{ .flags = {}, diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f077d7f9c..b0e995c52 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1574,6 +1574,7 @@ void GMainWindow::ConnectMenuEvents() { connect_menu(ui->action_Load_Cabinet_Formatter, [this]() { OnCabinet(Service::NFP::CabinetMode::StartFormatter); }); connect_menu(ui->action_Load_Mii_Edit, &GMainWindow::OnMiiEdit); + connect_menu(ui->action_Open_Controller_Menu, &GMainWindow::OnOpenControllerMenu); connect_menu(ui->action_Capture_Screenshot, &GMainWindow::OnCaptureScreenshot); // TAS @@ -1601,14 +1602,13 @@ void GMainWindow::UpdateMenuState() { ui->action_Pause, }; - const std::array applet_actions{ - ui->action_Load_Album, - ui->action_Load_Cabinet_Nickname_Owner, - ui->action_Load_Cabinet_Eraser, - ui->action_Load_Cabinet_Restorer, - ui->action_Load_Cabinet_Formatter, - ui->action_Load_Mii_Edit, - }; + const std::array applet_actions{ui->action_Load_Album, + ui->action_Load_Cabinet_Nickname_Owner, + ui->action_Load_Cabinet_Eraser, + ui->action_Load_Cabinet_Restorer, + ui->action_Load_Cabinet_Formatter, + ui->action_Load_Mii_Edit, + ui->action_Open_Controller_Menu}; for (QAction* action : running_actions) { action->setEnabled(emulation_running); @@ -4346,6 +4346,31 @@ void GMainWindow::OnMiiEdit() { BootGame(filename, MiiEditId); } +void GMainWindow::OnOpenControllerMenu() { + constexpr u64 ControllerAppletId = + static_cast(Service::AM::Applets::AppletProgramId::Controller); + auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); + if (!bis_system) { + QMessageBox::warning(this, tr("No firmware available"), + tr("Please install the firmware to use the Controller Menu.")); + return; + } + + auto controller_applet_nca = + bis_system->GetEntry(ControllerAppletId, FileSys::ContentRecordType::Program); + if (!controller_applet_nca) { + QMessageBox::warning(this, tr("Controller Applet"), + tr("Controller Menu is not available. Please reinstall firmware.")); + return; + } + + system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller); + + const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); + UISettings::values.roms_path = QFileInfo(filename).path(); + BootGame(filename, ControllerAppletId); +} + void GMainWindow::OnCaptureScreenshot() { if (emu_thread == nullptr || !emu_thread->IsRunning()) { return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index f9c6efe4f..19740cc52 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -402,6 +402,7 @@ private slots: void OnAlbum(); void OnCabinet(Service::NFP::CabinetMode mode); void OnMiiEdit(); + void OnOpenControllerMenu(); void OnCaptureScreenshot(); void OnReinitializeKeys(ReinitializeKeyBehavior behavior); void OnLanguageChanged(const QString& locale); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index 88684ffb5..e53f9951e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -25,7 +25,7 @@ - + 0 @@ -36,7 +36,7 @@ 0 0 1280 - 26 + 21 @@ -162,6 +162,7 @@ + @@ -382,9 +383,9 @@ - - Open &Album - + + Open &Album + @@ -407,9 +408,9 @@ - - Open &Mii Editor - + + Open &Mii Editor + @@ -454,6 +455,11 @@ R&ecord + + + Open &Controller Menu + + -- cgit v1.2.3 From da14c7b8e47fcd5456d88a033a1fb154a0dcfa39 Mon Sep 17 00:00:00 2001 From: t895 Date: Sun, 12 Nov 2023 02:03:01 -0500 Subject: config: Unify config handling under frontend_common Replaces every way of handling config for each frontend with SimpleIni. frontend_common's Config class is at the center where it saves and loads all of the cross-platform settings and provides a set of pure virtual functions for platform specific settings. As a result of making config handling platform specific, several parts had to be moved to each platform's own config class or to other parts. Default keys were put in platform specific config classes and translatable strings for Qt were moved to shared_translation. Default hotkeys, default_theme, window geometry, and qt metatypes were moved to uisettings. Additionally, to reduce dependence on Qt, QStrings were converted to std::strings where applicable. --- .gitmodules | 6 +- CMakeLists.txt | 1 - externals/CMakeLists.txt | 3 + externals/simpleini | 1 + src/CMakeLists.txt | 1 + src/android/app/src/main/jni/CMakeLists.txt | 9 +- src/android/app/src/main/jni/android_config.cpp | 70 ++ src/android/app/src/main/jni/android_config.h | 41 + src/android/app/src/main/jni/android_settings.cpp | 10 + src/android/app/src/main/jni/android_settings.h | 29 + src/android/app/src/main/jni/config.cpp | 330 ----- src/android/app/src/main/jni/config.h | 47 - src/android/app/src/main/jni/default_ini.h | 511 -------- src/android/app/src/main/jni/native.cpp | 15 +- src/android/app/src/main/jni/native_config.cpp | 23 +- src/android/app/src/main/jni/uisettings.cpp | 10 - src/android/app/src/main/jni/uisettings.h | 29 - src/common/settings.cpp | 4 +- src/frontend_common/CMakeLists.txt | 10 + src/frontend_common/config.cpp | 931 ++++++++++++++ src/frontend_common/config.h | 206 +++ src/yuzu/CMakeLists.txt | 6 +- src/yuzu/configuration/config.cpp | 1309 -------------------- src/yuzu/configuration/config.h | 179 --- src/yuzu/configuration/configure_camera.cpp | 2 +- src/yuzu/configuration/configure_dialog.cpp | 1 - src/yuzu/configuration/configure_hotkeys.cpp | 36 +- .../configuration/configure_input_per_game.cpp | 6 +- src/yuzu/configuration/configure_input_per_game.h | 5 +- src/yuzu/configuration/configure_input_player.cpp | 11 +- src/yuzu/configuration/configure_per_game.cpp | 7 +- src/yuzu/configuration/configure_per_game.h | 5 +- .../configuration/configure_per_game_addons.cpp | 1 - src/yuzu/configuration/configure_ringcon.cpp | 4 +- src/yuzu/configuration/configure_system.cpp | 1 - .../configure_touchscreen_advanced.cpp | 2 +- src/yuzu/configuration/configure_ui.cpp | 7 +- src/yuzu/configuration/input_profiles.cpp | 10 +- src/yuzu/configuration/input_profiles.h | 4 +- src/yuzu/configuration/qt_config.cpp | 548 ++++++++ src/yuzu/configuration/qt_config.h | 55 + src/yuzu/configuration/shared_translation.h | 43 + src/yuzu/debugger/wait_tree.cpp | 6 +- src/yuzu/game_list.cpp | 7 +- src/yuzu/game_list_p.h | 8 +- src/yuzu/game_list_worker.cpp | 16 +- src/yuzu/hotkeys.cpp | 28 +- src/yuzu/hotkeys.h | 16 +- src/yuzu/main.cpp | 112 +- src/yuzu/main.h | 7 +- src/yuzu/uisettings.cpp | 65 +- src/yuzu/uisettings.h | 79 +- src/yuzu_cmd/CMakeLists.txt | 7 +- src/yuzu_cmd/config.cpp | 279 ----- src/yuzu_cmd/config.h | 38 - src/yuzu_cmd/default_ini.h | 553 --------- src/yuzu_cmd/sdl_config.cpp | 257 ++++ src/yuzu_cmd/sdl_config.h | 49 + src/yuzu_cmd/yuzu.cpp | 5 +- 59 files changed, 2584 insertions(+), 3477 deletions(-) create mode 160000 externals/simpleini create mode 100644 src/android/app/src/main/jni/android_config.cpp create mode 100644 src/android/app/src/main/jni/android_config.h create mode 100644 src/android/app/src/main/jni/android_settings.cpp create mode 100644 src/android/app/src/main/jni/android_settings.h delete mode 100644 src/android/app/src/main/jni/config.cpp delete mode 100644 src/android/app/src/main/jni/config.h delete mode 100644 src/android/app/src/main/jni/default_ini.h delete mode 100644 src/android/app/src/main/jni/uisettings.cpp delete mode 100644 src/android/app/src/main/jni/uisettings.h create mode 100644 src/frontend_common/CMakeLists.txt create mode 100644 src/frontend_common/config.cpp create mode 100644 src/frontend_common/config.h delete mode 100644 src/yuzu/configuration/config.cpp delete mode 100644 src/yuzu/configuration/config.h create mode 100644 src/yuzu/configuration/qt_config.cpp create mode 100644 src/yuzu/configuration/qt_config.h delete mode 100644 src/yuzu_cmd/config.cpp delete mode 100644 src/yuzu_cmd/config.h delete mode 100644 src/yuzu_cmd/default_ini.h create mode 100644 src/yuzu_cmd/sdl_config.cpp create mode 100644 src/yuzu_cmd/sdl_config.h (limited to 'src/yuzu') diff --git a/.gitmodules b/.gitmodules index b72a2ec8c..45dd0d259 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "enet"] path = externals/enet url = https://github.com/lsalzman/enet.git -[submodule "inih"] - path = externals/inih/inih - url = https://github.com/benhoyt/inih.git [submodule "cubeb"] path = externals/cubeb url = https://github.com/mozilla/cubeb.git @@ -61,3 +58,6 @@ [submodule "breakpad"] path = externals/breakpad url = https://github.com/yuzu-emu/breakpad.git +[submodule "simpleini"] + path = externals/simpleini + url = https://github.com/brofield/simpleini.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c35e0946..e5cac8fe9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,7 +285,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) find_package(Boost 1.79.0 REQUIRED context) find_package(enet 1.3 MODULE) find_package(fmt 9 REQUIRED) -find_package(inih 52 MODULE COMPONENTS INIReader) find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle) find_package(lz4 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED) diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index be8b0b5e8..d0d4926bb 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -295,3 +295,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client) target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB) endif() endif() + +# SimpleIni +add_subdirectory(simpleini) diff --git a/externals/simpleini b/externals/simpleini new file mode 160000 index 000000000..382ddbb4b --- /dev/null +++ b/externals/simpleini @@ -0,0 +1 @@ +Subproject commit 382ddbb4b92c0b26aa1b32cefba2002119a5b1f2 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2ca4904a..e04d2418b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -187,6 +187,7 @@ add_subdirectory(audio_core) add_subdirectory(video_core) add_subdirectory(network) add_subdirectory(input_common) +add_subdirectory(frontend_common) add_subdirectory(shader_recompiler) if (YUZU_ROOM) diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt index 88a570f68..49ad029aa 100644 --- a/src/android/app/src/main/jni/CMakeLists.txt +++ b/src/android/app/src/main/jni/CMakeLists.txt @@ -6,9 +6,6 @@ add_library(yuzu-android SHARED android_common/android_common.h applets/software_keyboard.cpp applets/software_keyboard.h - config.cpp - config.h - default_ini.h emu_window/emu_window.cpp emu_window/emu_window.h id_cache.cpp @@ -16,15 +13,17 @@ add_library(yuzu-android SHARED native.cpp native.h native_config.cpp - uisettings.cpp + android_settings.cpp game_metadata.cpp native_log.cpp + android_config.cpp + android_config.h ) set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR}) -target_link_libraries(yuzu-android PRIVATE audio_core common core input_common) target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad inih jnigraphics log) +target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common) if (ARCHITECTURE_arm64) target_link_libraries(yuzu-android PRIVATE adrenotools) endif() diff --git a/src/android/app/src/main/jni/android_config.cpp b/src/android/app/src/main/jni/android_config.cpp new file mode 100644 index 000000000..3041c25c9 --- /dev/null +++ b/src/android/app/src/main/jni/android_config.cpp @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "android_config.h" +#include "android_settings.h" +#include "common/settings_setting.h" + +AndroidConfig::AndroidConfig(const std::string& config_name, ConfigType config_type) + : Config(config_type) { + Initialize(config_name); + if (config_type != ConfigType::InputProfile) { + ReadAndroidValues(); + SaveAndroidValues(); + } +} + +AndroidConfig::~AndroidConfig() { + if (global) { + AndroidConfig::SaveAllValues(); + } +} + +void AndroidConfig::ReloadAllValues() { + Reload(); + ReadAndroidValues(); + SaveAndroidValues(); +} + +void AndroidConfig::SaveAllValues() { + Save(); + SaveAndroidValues(); +} + +void AndroidConfig::ReadAndroidValues() { + if (global) { + ReadAndroidUIValues(); + } +} + +void AndroidConfig::ReadAndroidUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); + + ReadCategory(Settings::Category::Android); + + EndGroup(); +} + +void AndroidConfig::SaveAndroidValues() { + if (global) { + SaveAndroidUIValues(); + } + + WriteToIni(); +} + +void AndroidConfig::SaveAndroidUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Android)); + + WriteCategory(Settings::Category::Android); + + EndGroup(); +} + +std::vector& AndroidConfig::FindRelevantList(Settings::Category category) { + auto& map = Settings::values.linkage.by_category; + if (map.contains(category)) { + return Settings::values.linkage.by_category[category]; + } + return AndroidSettings::values.linkage.by_category[category]; +} diff --git a/src/android/app/src/main/jni/android_config.h b/src/android/app/src/main/jni/android_config.h new file mode 100644 index 000000000..e679392fd --- /dev/null +++ b/src/android/app/src/main/jni/android_config.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "frontend_common/config.h" + +class AndroidConfig final : public Config { +public: + explicit AndroidConfig(const std::string& config_name = "config", + ConfigType config_type = ConfigType::GlobalConfig); + ~AndroidConfig() override; + + void ReloadAllValues() override; + void SaveAllValues() override; + +protected: + void ReadAndroidValues(); + void ReadAndroidUIValues(); + void ReadHidbusValues() override {} + void ReadDebugControlValues() override {} + void ReadPathValues() override {} + void ReadShortcutValues() override {} + void ReadUIValues() override {} + void ReadUIGamelistValues() override {} + void ReadUILayoutValues() override {} + void ReadMultiplayerValues() override {} + + void SaveAndroidValues(); + void SaveAndroidUIValues(); + void SaveHidbusValues() override {} + void SaveDebugControlValues() override {} + void SavePathValues() override {} + void SaveShortcutValues() override {} + void SaveUIValues() override {} + void SaveUIGamelistValues() override {} + void SaveUILayoutValues() override {} + void SaveMultiplayerValues() override {} + + std::vector& FindRelevantList(Settings::Category category) override; +}; diff --git a/src/android/app/src/main/jni/android_settings.cpp b/src/android/app/src/main/jni/android_settings.cpp new file mode 100644 index 000000000..16023a6b0 --- /dev/null +++ b/src/android/app/src/main/jni/android_settings.cpp @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "android_settings.h" + +namespace AndroidSettings { + +Values values; + +} // namespace AndroidSettings diff --git a/src/android/app/src/main/jni/android_settings.h b/src/android/app/src/main/jni/android_settings.h new file mode 100644 index 000000000..37bc33918 --- /dev/null +++ b/src/android/app/src/main/jni/android_settings.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/common_types.h" +#include "common/settings_setting.h" + +namespace AndroidSettings { + +struct Values { + Settings::Linkage linkage; + + // Android + Settings::Setting picture_in_picture{linkage, false, "picture_in_picture", + Settings::Category::Android}; + Settings::Setting screen_layout{linkage, + 5, + "screen_layout", + Settings::Category::Android, + Settings::Specialization::Default, + true, + true}; +}; + +extern Values values; + +} // namespace AndroidSettings diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp deleted file mode 100644 index 81120ab0f..000000000 --- a/src/android/app/src/main/jni/config.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include - -#include -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "common/settings_enums.h" -#include "core/hle/service/acc/profile_manager.h" -#include "input_common/main.h" -#include "jni/config.h" -#include "jni/default_ini.h" -#include "uisettings.h" - -namespace FS = Common::FS; - -Config::Config(const std::string& config_name, ConfigType config_type) - : type(config_type), global{config_type == ConfigType::GlobalConfig} { - Initialize(config_name); -} - -Config::~Config() = default; - -bool Config::LoadINI(const std::string& default_contents, bool retry) { - void(FS::CreateParentDir(config_loc)); - config = std::make_unique(FS::PathToUTF8String(config_loc)); - const auto config_loc_str = FS::PathToUTF8String(config_loc); - if (config->ParseError() < 0) { - if (retry) { - LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", - config_loc_str); - - void(FS::CreateParentDir(config_loc)); - void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents)); - - config = std::make_unique(config_loc_str); - - return LoadINI(default_contents, false); - } - LOG_ERROR(Config, "Failed."); - return false; - } - LOG_INFO(Config, "Successfully loaded {}", config_loc_str); - return true; -} - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { - std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault()); - if (setting_value.empty()) { - setting_value = setting.GetDefault(); - } - setting = std::move(setting_value); -} - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { - setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); -} - -template -void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { - setting = static_cast( - config->GetInteger(group, setting.GetLabel(), static_cast(setting.GetDefault()))); -} - -void Config::ReadValues() { - ReadSetting("ControlsGeneral", Settings::values.mouse_enabled); - ReadSetting("ControlsGeneral", Settings::values.touch_device); - ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled); - ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled); - ReadSetting("ControlsGeneral", Settings::values.vibration_enabled); - ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations); - ReadSetting("ControlsGeneral", Settings::values.motion_enabled); - Settings::values.touchscreen.enabled = - config->GetBoolean("ControlsGeneral", "touch_enabled", true); - Settings::values.touchscreen.rotation_angle = - config->GetInteger("ControlsGeneral", "touch_angle", 0); - Settings::values.touchscreen.diameter_x = - config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); - Settings::values.touchscreen.diameter_y = - config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); - - int num_touch_from_button_maps = - config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); - if (num_touch_from_button_maps > 0) { - for (int i = 0; i < num_touch_from_button_maps; ++i) { - Settings::TouchFromButtonMap map; - map.name = config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_name"), - "default"); - const int num_touch_maps = config->GetInteger( - "ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), - 0); - map.buttons.reserve(num_touch_maps); - - for (int j = 0; j < num_touch_maps; ++j) { - std::string touch_mapping = - config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_bind_") + std::to_string(j), - ""); - map.buttons.emplace_back(std::move(touch_mapping)); - } - - Settings::values.touch_from_button_maps.emplace_back(std::move(map)); - } - } else { - Settings::values.touch_from_button_maps.emplace_back( - Settings::TouchFromButtonMap{"default", {}}); - num_touch_from_button_maps = 1; - } - Settings::values.touch_from_button_map_index = std::clamp( - Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); - - ReadSetting("ControlsGeneral", Settings::values.udp_input_servers); - - // Data Storage - ReadSetting("Data Storage", Settings::values.use_virtual_sd); - FS::SetYuzuPath(FS::YuzuPath::NANDDir, - config->Get("Data Storage", "nand_directory", - FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); - FS::SetYuzuPath(FS::YuzuPath::SDMCDir, - config->Get("Data Storage", "sdmc_directory", - FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); - FS::SetYuzuPath(FS::YuzuPath::LoadDir, - config->Get("Data Storage", "load_directory", - FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); - FS::SetYuzuPath(FS::YuzuPath::DumpDir, - config->Get("Data Storage", "dump_directory", - FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - ReadSetting("Data Storage", Settings::values.gamecard_inserted); - ReadSetting("Data Storage", Settings::values.gamecard_current_game); - ReadSetting("Data Storage", Settings::values.gamecard_path); - - // System - ReadSetting("System", Settings::values.current_user); - Settings::values.current_user = std::clamp(Settings::values.current_user.GetValue(), 0, - Service::Account::MAX_USERS - 1); - - // Disable docked mode by default on Android - Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false) - ? Settings::ConsoleMode::Docked - : Settings::ConsoleMode::Handheld); - - const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); - if (rng_seed_enabled) { - Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0)); - } else { - Settings::values.rng_seed.SetValue(0); - } - Settings::values.rng_seed_enabled.SetValue(rng_seed_enabled); - - const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false); - if (custom_rtc_enabled) { - Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0); - } else { - Settings::values.custom_rtc = 0; - } - Settings::values.custom_rtc_enabled = custom_rtc_enabled; - - ReadSetting("System", Settings::values.language_index); - ReadSetting("System", Settings::values.region_index); - ReadSetting("System", Settings::values.time_zone_index); - ReadSetting("System", Settings::values.sound_index); - - // Core - ReadSetting("Core", Settings::values.use_multi_core); - ReadSetting("Core", Settings::values.memory_layout_mode); - - // Cpu - ReadSetting("Cpu", Settings::values.cpu_accuracy); - ReadSetting("Cpu", Settings::values.cpu_debug_mode); - ReadSetting("Cpu", Settings::values.cpuopt_page_tables); - ReadSetting("Cpu", Settings::values.cpuopt_block_linking); - ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer); - ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher); - ReadSetting("Cpu", Settings::values.cpuopt_context_elimination); - ReadSetting("Cpu", Settings::values.cpuopt_const_prop); - ReadSetting("Cpu", Settings::values.cpuopt_misc_ir); - ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks); - ReadSetting("Cpu", Settings::values.cpuopt_fastmem); - ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives); - ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives); - ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check); - ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor); - - // Renderer - ReadSetting("Renderer", Settings::values.renderer_backend); - ReadSetting("Renderer", Settings::values.renderer_debug); - ReadSetting("Renderer", Settings::values.renderer_shader_feedback); - ReadSetting("Renderer", Settings::values.enable_nsight_aftermath); - ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks); - ReadSetting("Renderer", Settings::values.vulkan_device); - - ReadSetting("Renderer", Settings::values.resolution_setup); - ReadSetting("Renderer", Settings::values.scaling_filter); - ReadSetting("Renderer", Settings::values.fsr_sharpening_slider); - ReadSetting("Renderer", Settings::values.anti_aliasing); - ReadSetting("Renderer", Settings::values.fullscreen_mode); - ReadSetting("Renderer", Settings::values.aspect_ratio); - ReadSetting("Renderer", Settings::values.max_anisotropy); - ReadSetting("Renderer", Settings::values.use_speed_limit); - ReadSetting("Renderer", Settings::values.speed_limit); - ReadSetting("Renderer", Settings::values.use_disk_shader_cache); - ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation); - ReadSetting("Renderer", Settings::values.vsync_mode); - ReadSetting("Renderer", Settings::values.shader_backend); - ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); - ReadSetting("Renderer", Settings::values.nvdec_emulation); - ReadSetting("Renderer", Settings::values.use_fast_gpu_time); - ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache); - - ReadSetting("Renderer", Settings::values.bg_red); - ReadSetting("Renderer", Settings::values.bg_green); - ReadSetting("Renderer", Settings::values.bg_blue); - - // Use GPU accuracy normal by default on Android - Settings::values.gpu_accuracy = static_cast(config->GetInteger( - "Renderer", "gpu_accuracy", static_cast(Settings::GpuAccuracy::Normal))); - - // Use GPU default anisotropic filtering on Android - Settings::values.max_anisotropy = - static_cast(config->GetInteger("Renderer", "max_anisotropy", 1)); - - // Disable ASTC compute by default on Android - Settings::values.accelerate_astc.SetValue( - config->GetBoolean("Renderer", "accelerate_astc", false) ? Settings::AstcDecodeMode::Gpu - : Settings::AstcDecodeMode::Cpu); - - // Enable asynchronous presentation by default on Android - Settings::values.async_presentation = - config->GetBoolean("Renderer", "async_presentation", true); - - // Disable force_max_clock by default on Android - Settings::values.renderer_force_max_clock = - config->GetBoolean("Renderer", "force_max_clock", false); - - // Disable use_reactive_flushing by default on Android - Settings::values.use_reactive_flushing = - config->GetBoolean("Renderer", "use_reactive_flushing", false); - - // Audio - ReadSetting("Audio", Settings::values.sink_id); - ReadSetting("Audio", Settings::values.audio_output_device_id); - ReadSetting("Audio", Settings::values.volume); - - // Miscellaneous - // log_filter has a different default here than from common - Settings::values.log_filter = "*:Info"; - ReadSetting("Miscellaneous", Settings::values.use_dev_keys); - - // Debugging - Settings::values.record_frame_times = - config->GetBoolean("Debugging", "record_frame_times", false); - ReadSetting("Debugging", Settings::values.dump_exefs); - ReadSetting("Debugging", Settings::values.dump_nso); - ReadSetting("Debugging", Settings::values.enable_fs_access_log); - ReadSetting("Debugging", Settings::values.reporting_services); - ReadSetting("Debugging", Settings::values.quest_flag); - ReadSetting("Debugging", Settings::values.use_debug_asserts); - ReadSetting("Debugging", Settings::values.use_auto_stub); - ReadSetting("Debugging", Settings::values.disable_macro_jit); - ReadSetting("Debugging", Settings::values.disable_macro_hle); - ReadSetting("Debugging", Settings::values.use_gdbstub); - ReadSetting("Debugging", Settings::values.gdbstub_port); - - const auto title_list = config->Get("AddOns", "title_ids", ""); - std::stringstream ss(title_list); - std::string line; - while (std::getline(ss, line, '|')) { - const auto title_id = std::strtoul(line.c_str(), nullptr, 16); - const auto disabled_list = config->Get("AddOns", "disabled_" + line, ""); - - std::stringstream inner_ss(disabled_list); - std::string inner_line; - std::vector out; - while (std::getline(inner_ss, inner_line, '|')) { - out.push_back(inner_line); - } - - Settings::values.disabled_addons.insert_or_assign(title_id, out); - } - - // Web Service - ReadSetting("WebService", Settings::values.enable_telemetry); - ReadSetting("WebService", Settings::values.web_api_url); - ReadSetting("WebService", Settings::values.yuzu_username); - ReadSetting("WebService", Settings::values.yuzu_token); - - // Network - ReadSetting("Network", Settings::values.network_interface); - - // Android - ReadSetting("Android", AndroidSettings::values.picture_in_picture); - ReadSetting("Android", AndroidSettings::values.screen_layout); -} - -void Config::Initialize(const std::string& config_name) { - const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); - const auto config_file = fmt::format("{}.ini", config_name); - - switch (type) { - case ConfigType::GlobalConfig: - config_loc = FS::PathToUTF8String(fs_config_loc / config_file); - break; - case ConfigType::PerGameConfig: - config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); - break; - case ConfigType::InputProfile: - config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); - LoadINI(DefaultINI::android_config_file); - return; - } - LoadINI(DefaultINI::android_config_file); - ReadValues(); -} diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h deleted file mode 100644 index e1e8f47ed..000000000 --- a/src/android/app/src/main/jni/config.h +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include - -#include "common/settings.h" - -class INIReader; - -class Config { - bool LoadINI(const std::string& default_contents = "", bool retry = true); - -public: - enum class ConfigType { - GlobalConfig, - PerGameConfig, - InputProfile, - }; - - explicit Config(const std::string& config_name = "config", - ConfigType config_type = ConfigType::GlobalConfig); - ~Config(); - - void Initialize(const std::string& config_name); - -private: - /** - * Applies a value read from the config to a Setting. - * - * @param group The name of the INI group - * @param setting The yuzu setting to modify - */ - template - void ReadSetting(const std::string& group, Settings::Setting& setting); - - void ReadValues(); - - const ConfigType type; - std::unique_ptr config; - std::string config_loc; - const bool global; -}; diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h deleted file mode 100644 index d81422a74..000000000 --- a/src/android/app/src/main/jni/default_ini.h +++ /dev/null @@ -1,511 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace DefaultINI { - -const char* android_config_file = R"( - -[ControlsP0] -# The input devices and parameters for each Switch native input -# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... -# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." -# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values - -# Indicates if this player should be connected at boot -connected= - -# for button input, the following devices are available: -# - "keyboard" (default) for keyboard input. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "button"(optional): the index of the button to bind -# - "hat"(optional): the index of the hat to bind as direction buttons -# - "axis"(optional): the index of the axis to bind -# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" -# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is -# triggered if the axis value crosses -# - "direction"(only used for axis): "+" means the button is triggered when the axis value -# is greater than the threshold; "-" means the button is triggered when the axis value -# is smaller than the threshold -button_a= -button_b= -button_x= -button_y= -button_lstick= -button_rstick= -button_l= -button_r= -button_zl= -button_zr= -button_plus= -button_minus= -button_dleft= -button_dup= -button_dright= -button_ddown= -button_lstick_left= -button_lstick_up= -button_lstick_right= -button_lstick_down= -button_sl= -button_sr= -button_home= -button_screenshot= - -# for analog input, the following devices are available: -# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: -# - "up", "down", "left", "right": sub-devices for each direction. -# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" -# - "modifier": sub-devices as a modifier. -# - "modifier_scale": a float number representing the applied modifier scale to the analog input. -# Must be in range of 0.0-1.0. Defaults to 0.5 -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "axis_x": the index of the axis to bind as x-axis (default to 0) -# - "axis_y": the index of the axis to bind as y-axis (default to 1) -lstick= -rstick= - -# for motion input, the following devices are available: -# - "keyboard" (default) for emulating random motion input from buttons. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for motion input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "motion": the index of the motion sensor to bind -# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: -# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" -# - "port": the port of the cemu hook server -# - "pad": the index of the joystick -# - "motion": the index of the motion sensor of the joystick to bind -motionleft= -motionright= - -[ControlsGeneral] -# To use the debug_pad, prepend `debug_pad_` before each button setting above. -# i.e. debug_pad_button_a= - -# Enable debug pad inputs to the guest -# 0 (default): Disabled, 1: Enabled -debug_pad_enabled = - -# Whether to enable or disable vibration -# 0: Disabled, 1 (default): Enabled -vibration_enabled= - -# Whether to enable or disable accurate vibrations -# 0 (default): Disabled, 1: Enabled -enable_accurate_vibrations= - -# Enables controller motion inputs -# 0: Disabled, 1 (default): Enabled -motion_enabled = - -# Defines the udp device's touch screen coordinate system for cemuhookudp devices -# - "min_x", "min_y", "max_x", "max_y" -touch_device= - -# for mapping buttons to touch inputs. -#touch_from_button_map=1 -#touch_from_button_maps_0_name=default -#touch_from_button_maps_0_count=2 -#touch_from_button_maps_0_bind_0=foo -#touch_from_button_maps_0_bind_1=bar -# etc. - -# List of Cemuhook UDP servers, delimited by ','. -# Default: 127.0.0.1:26760 -# Example: 127.0.0.1:26760,123.4.5.67:26761 -udp_input_servers = - -# Enable controlling an axis via a mouse input. -# 0 (default): Off, 1: On -mouse_panning = - -# Set mouse sensitivity. -# Default: 1.0 -mouse_panning_sensitivity = - -# Emulate an analog control stick from keyboard inputs. -# 0 (default): Disabled, 1: Enabled -emulate_analog_keyboard = - -# Enable mouse inputs to the guest -# 0 (default): Disabled, 1: Enabled -mouse_enabled = - -# Enable keyboard inputs to the guest -# 0 (default): Disabled, 1: Enabled -keyboard_enabled = - -[Core] -# Whether to use multi-core for CPU emulation -# 0: Disabled, 1 (default): Enabled -use_multi_core = - -# Enable unsafe extended guest system memory layout (8GB DRAM) -# 0 (default): Disabled, 1: Enabled -use_unsafe_extended_memory_layout = - -[Cpu] -# Adjusts various optimizations. -# Auto-select mode enables choice unsafe optimizations. -# Accurate enables only safe optimizations. -# Unsafe allows any unsafe optimizations. -# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations -cpu_accuracy = - -# Allow disabling safe optimizations. -# 0 (default): Disabled, 1: Enabled -cpu_debug_mode = - -# Enable inline page tables optimization (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_page_tables = - -# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) -# 0: Disabled, 1 (default): Enabled -cpuopt_block_linking = - -# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) -# 0: Disabled, 1 (default): Enabled -cpuopt_return_stack_buffer = - -# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) -# 0: Disabled, 1 (default): Enabled -cpuopt_fast_dispatcher = - -# Enable context elimination CPU Optimization (reduce host memory use for guest context) -# 0: Disabled, 1 (default): Enabled -cpuopt_context_elimination = - -# Enable constant propagation CPU optimization (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_const_prop = - -# Enable miscellaneous CPU optimizations (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_misc_ir = - -# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) -# 0: Disabled, 1 (default): Enabled -cpuopt_reduce_misalign_checks = - -# Enable Host MMU Emulation (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem = - -# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem_exclusives = - -# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_recompile_exclusives = - -# Enable optimization to ignore invalid memory accesses (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_ignore_memory_aborts = - -# Enable unfuse FMA (improve performance on CPUs without FMA) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_unfuse_fma = - -# Enable faster FRSQRTE and FRECPE -# Only enabled if cpu_accuracy is set to Unsafe. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_reduce_fp_error = - -# Enable faster ASIMD instructions (32 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_standard_fpcr = - -# Enable inaccurate NaN handling -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_inaccurate_nan = - -# Disable address space checks (64 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_fastmem_check = - -# Enable faster exclusive instructions -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_global_monitor = - -[Renderer] -# Which backend API to use. -# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null -backend = - -# Whether to enable asynchronous presentation (Vulkan only) -# 0: Off, 1 (default): On -async_presentation = - -# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied). -# 0 (default): Disabled, 1: Enabled -force_max_clock = - -# Enable graphics API debugging mode. -# 0 (default): Disabled, 1: Enabled -debug = - -# Enable shader feedback. -# 0 (default): Disabled, 1: Enabled -renderer_shader_feedback = - -# Enable Nsight Aftermath crash dumps -# 0 (default): Disabled, 1: Enabled -nsight_aftermath = - -# Disable shader loop safety checks, executing the shader without loop logic changes -# 0 (default): Disabled, 1: Enabled -disable_shader_loop_safety_checks = - -# Which Vulkan physical device to use (defaults to 0) -vulkan_device = - -# 0: 0.5x (360p/540p) [EXPERIMENTAL] -# 1: 0.75x (540p/810p) [EXPERIMENTAL] -# 2 (default): 1x (720p/1080p) -# 3: 2x (1440p/2160p) -# 4: 3x (2160p/3240p) -# 5: 4x (2880p/4320p) -# 6: 5x (3600p/5400p) -# 7: 6x (4320p/6480p) -resolution_setup = - -# Pixel filter to use when up- or down-sampling rendered frames. -# 0: Nearest Neighbor -# 1 (default): Bilinear -# 2: Bicubic -# 3: Gaussian -# 4: ScaleForce -# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only] -scaling_filter = - -# Anti-Aliasing (AA) -# 0 (default): None, 1: FXAA -anti_aliasing = - -# Whether to use fullscreen or borderless window mode -# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen -fullscreen_mode = - -# Aspect ratio -# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window -aspect_ratio = - -# Anisotropic filtering -# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x -max_anisotropy = - -# Whether to enable VSync or not. -# OpenGL: Values other than 0 enable VSync -# Vulkan: FIFO is selected if the requested mode is not supported by the driver. -# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. -# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. -# Mailbox can have lower latency than FIFO and does not tear but may drop frames. -# Immediate (no synchronization) just presents whatever is available and can exhibit tearing. -# 0: Immediate (Off), 1 (Default): Mailbox (On), 2: FIFO, 3: FIFO Relaxed -use_vsync = - -# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is -# not available and GLASM is selected, GLSL will be used. -# 0: GLSL, 1 (default): GLASM, 2: SPIR-V -shader_backend = - -# Whether to allow asynchronous shader building. -# 0 (default): Off, 1: On -use_asynchronous_shaders = - -# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory. -# 0 (default): Off, 1: On -use_reactive_flushing = - -# NVDEC emulation. -# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding -nvdec_emulation = - -# Accelerate ASTC texture decoding. -# 0 (default): Off, 1: On -accelerate_astc = - -# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value -# 0: Off, 1: On (default) -use_speed_limit = - -# Limits the speed of the game to run no faster than this value as a percentage of target speed -# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) -speed_limit = - -# Whether to use disk based shader cache -# 0: Off, 1 (default): On -use_disk_shader_cache = - -# Which gpu accuracy level to use -# 0 (default): Normal, 1: High, 2: Extreme (Very slow) -gpu_accuracy = - -# Whether to use asynchronous GPU emulation -# 0 : Off (slow), 1 (default): On (fast) -use_asynchronous_gpu_emulation = - -# Inform the guest that GPU operations completed more quickly than they did. -# 0: Off, 1 (default): On -use_fast_gpu_time = - -# Force unmodified buffers to be flushed, which can cost performance. -# 0: Off (default), 1: On -use_pessimistic_flushes = - -# Whether to use garbage collection or not for GPU caches. -# 0 (default): Off, 1: On -use_caches_gc = - -# The clear color for the renderer. What shows up on the sides of the bottom screen. -# Must be in range of 0-255. Defaults to 0 for all. -bg_red = -bg_blue = -bg_green = - -[Audio] -# Which audio output engine to use. -# auto (default): Auto-select -# cubeb: Cubeb audio engine (if available) -# sdl2: SDL2 audio engine (if available) -# null: No audio output -output_engine = - -# Which audio device to use. -# auto (default): Auto-select -output_device = - -# Output volume. -# 100 (default): 100%, 0; mute -volume = - -[Data Storage] -# Whether to create a virtual SD card. -# 1: Yes, 0 (default): No -use_virtual_sd = - -# Whether or not to enable gamecard emulation -# 1: Yes, 0 (default): No -gamecard_inserted = - -# Whether or not the gamecard should be emulated as the current game -# If 'gamecard_inserted' is 0 this setting is irrelevant -# 1: Yes, 0 (default): No -gamecard_current_game = - -# Path to an XCI file to use as the gamecard -# If 'gamecard_inserted' is 0 this setting is irrelevant -# If 'gamecard_current_game' is 1 this setting is irrelevant -gamecard_path = - -[System] -# Whether the system is docked -# 1 (default): Yes, 0: No -use_docked_mode = - -# Sets the seed for the RNG generator built into the switch -# rng_seed will be ignored and randomly generated if rng_seed_enabled is false -rng_seed_enabled = -rng_seed = - -# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service -# This will auto-increment, with the time set being the time the game is started -# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used -custom_rtc_enabled = -custom_rtc = - -# Sets the systems language index -# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, -# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, -# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese -language_index = - -# The system region that yuzu will use during emulation -# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan -region_index = - -# The system time zone that yuzu will use during emulation -# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone -time_zone_index = - -# Sets the sound output mode. -# 0: Mono, 1 (default): Stereo, 2: Surround -sound_index = - -[Miscellaneous] -# A filter which removes logs below a certain logging level. -# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical -log_filter = *:Trace - -# Use developer keys -# 0 (default): Disabled, 1: Enabled -use_dev_keys = - -[Debugging] -# Record frame time data, can be found in the log directory. Boolean value -record_frame_times = -# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them -dump_exefs=false -# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them -dump_nso=false -# Determines whether or not yuzu will save the filesystem access log. -enable_fs_access_log=false -# Enables verbose reporting services -reporting_services = -# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode -# false: Retail/Normal Mode (default), true: Kiosk Mode -quest_flag = -# Determines whether debug asserts should be enabled, which will throw an exception on asserts. -# false: Disabled (default), true: Enabled -use_debug_asserts = -# Determines whether unimplemented HLE service calls should be automatically stubbed. -# false: Disabled (default), true: Enabled -use_auto_stub = -# Enables/Disables the macro JIT compiler -disable_macro_jit=false -# Determines whether to enable the GDB stub and wait for the debugger to attach before running. -# false: Disabled (default), true: Enabled -use_gdbstub=false -# The port to use for the GDB server, if it is enabled. -gdbstub_port=6543 - -[WebService] -# Whether or not to enable telemetry -# 0: No, 1 (default): Yes -enable_telemetry = -# URL for Web API -web_api_url = https://api.yuzu-emu.org -# Username and token for yuzu Web Service -# See https://profile.yuzu-emu.org/ for more info -yuzu_username = -yuzu_token = - -[Network] -# Name of the network interface device to use with yuzu LAN play. -# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo' -# e.g. On Windows: 'Ethernet', 'Wi-Fi' -network_interface = - -[AddOns] -# Used to disable add-ons -# List of title IDs of games that will have add-ons disabled (separated by '|'): -title_ids = -# For each title ID, have a key/value pair called `disabled_` equal to the names of the add-ons to disable (sep. by '|') -# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey -)"; -} // namespace DefaultINI diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 64663b084..617288ae4 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -52,8 +52,8 @@ #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" +#include "frontend_common/config.h" #include "jni/android_common/android_common.h" -#include "jni/config.h" #include "jni/id_cache.h" #include "jni/native.h" #include "video_core/renderer_base.h" @@ -664,8 +664,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass c void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeSystem(JNIEnv* env, jclass clazz, jboolean reload) { - // Create the default config.ini. - Config{}; // Initialize the emulated system. if (!reload) { EmulationSession::GetInstance().System().Initialize(); @@ -680,17 +678,6 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass cl void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z( JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {} -void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) { - Config{}; -} - -void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz, - jstring j_game_id) { - std::string_view game_id = env->GetStringUTFChars(j_game_id, 0); - - env->ReleaseStringUTFChars(j_game_id, game_id.data()); -} - jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) { jdoubleArray j_stats = env->NewDoubleArray(4); diff --git a/src/android/app/src/main/jni/native_config.cpp b/src/android/app/src/main/jni/native_config.cpp index 8a704960c..8e81816e5 100644 --- a/src/android/app/src/main/jni/native_config.cpp +++ b/src/android/app/src/main/jni/native_config.cpp @@ -5,11 +5,14 @@ #include +#include "android_config.h" +#include "android_settings.h" #include "common/logging/log.h" #include "common/settings.h" +#include "frontend_common/config.h" #include "jni/android_common/android_common.h" -#include "jni/config.h" -#include "uisettings.h" + +std::unique_ptr config; template Settings::Setting* getSetting(JNIEnv* env, jstring jkey) { @@ -28,6 +31,22 @@ Settings::Setting* getSetting(JNIEnv* env, jstring jkey) { extern "C" { +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeConfig(JNIEnv* env, jobject obj) { + config = std::make_unique(); +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadConfig(JNIEnv* env, jobject obj) { + config.reset(); +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadSettings(JNIEnv* env, jobject obj) { + config->AndroidConfig::ReloadAllValues(); +} + +void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveSettings(JNIEnv* env, jobject obj) { + config->AndroidConfig::SaveAllValues(); +} + jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getBoolean(JNIEnv* env, jobject obj, jstring jkey, jboolean getDefault) { auto setting = getSetting(env, jkey); diff --git a/src/android/app/src/main/jni/uisettings.cpp b/src/android/app/src/main/jni/uisettings.cpp deleted file mode 100644 index f2f0bad50..000000000 --- a/src/android/app/src/main/jni/uisettings.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "uisettings.h" - -namespace AndroidSettings { - -Values values; - -} // namespace AndroidSettings diff --git a/src/android/app/src/main/jni/uisettings.h b/src/android/app/src/main/jni/uisettings.h deleted file mode 100644 index 37bc33918..000000000 --- a/src/android/app/src/main/jni/uisettings.h +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-FileCopyrightText: 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include "common/common_types.h" -#include "common/settings_setting.h" - -namespace AndroidSettings { - -struct Values { - Settings::Linkage linkage; - - // Android - Settings::Setting picture_in_picture{linkage, false, "picture_in_picture", - Settings::Category::Android}; - Settings::Setting screen_layout{linkage, - 5, - "screen_layout", - Settings::Category::Android, - Settings::Specialization::Default, - true, - true}; -}; - -extern Values values; - -} // namespace AndroidSettings diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 51717be06..a10131eb2 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -206,9 +206,9 @@ const char* TranslateCategory(Category category) { case Category::UiAudio: return "UiAudio"; case Category::UiLayout: - return "UiLayout"; + return "UILayout"; case Category::UiGameList: - return "UiGameList"; + return "UIGameList"; case Category::Screenshots: return "Screenshots"; case Category::Shortcuts: diff --git a/src/frontend_common/CMakeLists.txt b/src/frontend_common/CMakeLists.txt new file mode 100644 index 000000000..1537271fc --- /dev/null +++ b/src/frontend_common/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-2.0-or-later + +add_library(frontend_common STATIC + config.cpp + config.h +) + +create_target_directory_groups(frontend_common) +target_link_libraries(frontend_common PUBLIC core SimpleIni PRIVATE common Boost::headers) diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp new file mode 100644 index 000000000..d317d81fd --- /dev/null +++ b/src/frontend_common/config.cpp @@ -0,0 +1,931 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/settings.h" +#include "common/settings_common.h" +#include "common/settings_enums.h" +#include "config.h" +#include "core/core.h" +#include "core/hle/service/acc/profile_manager.h" +#include "core/hle/service/hid/controllers/npad.h" +#include "network/network.h" + +#include + +namespace FS = Common::FS; + +Config::Config(const ConfigType config_type) + : type(config_type), global{config_type == ConfigType::GlobalConfig} {} + +void Config::Initialize(const std::string& config_name) { + const std::filesystem::path fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); + const auto config_file = fmt::format("{}.ini", config_name); + + switch (type) { + case ConfigType::GlobalConfig: + config_loc = FS::PathToUTF8String(fs_config_loc / config_file); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + Reload(); + break; + case ConfigType::PerGameConfig: + config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + Reload(); + break; + case ConfigType::InputProfile: + config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + break; + } +} + +void Config::Initialize(const std::optional config_path) { + const std::filesystem::path default_sdl_config_path = + FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; + config_loc = config_path.value_or(FS::PathToUTF8String(default_sdl_config_path)); + void(FS::CreateParentDir(config_loc)); + SetUpIni(); + Reload(); +} + +void Config::WriteToIni() const { + if (const SI_Error rc = config->SaveFile(config_loc.c_str()); rc < 0) { + LOG_ERROR(Frontend, "Config file could not be saved!"); + } +} + +void Config::SetUpIni() { + config = std::make_unique(); + config->SetUnicode(true); + config->SetSpaces(false); + config->LoadFile(config_loc.c_str()); +} + +bool Config::IsCustomConfig() const { + return type == ConfigType::PerGameConfig; +} + +void Config::ReadPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix.append("player_").append(ToString(player_index)).append("_"); + } + + auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + ReadStringSetting(std::string(player_prefix).append("profile_name")); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + player.profile_name = profile_name; + } + + if (player_prefix.empty() && Settings::IsConfiguringGlobal()) { + const auto controller = static_cast( + ReadIntegerSetting(std::string(player_prefix).append("type"), + static_cast(Settings::ControllerType::ProController))); + + if (controller == Settings::ControllerType::LeftJoycon || + controller == Settings::ControllerType::RightJoycon) { + player.controller_type = controller; + } + } else { + std::string connected_key = player_prefix; + player.connected = ReadBooleanSetting(connected_key.append("connected"), + std::make_optional(player_index == 0)); + + player.controller_type = static_cast( + ReadIntegerSetting(std::string(player_prefix).append("type"), + static_cast(Settings::ControllerType::ProController))); + + player.vibration_enabled = ReadBooleanSetting( + std::string(player_prefix).append("vibration_enabled"), std::make_optional(true)); + + player.vibration_strength = static_cast( + ReadIntegerSetting(std::string(player_prefix).append("vibration_strength"), 100)); + + player.body_color_left = static_cast(ReadIntegerSetting( + std::string(player_prefix).append("body_color_left"), Settings::JOYCON_BODY_NEON_BLUE)); + player.body_color_right = static_cast(ReadIntegerSetting( + std::string(player_prefix).append("body_color_right"), Settings::JOYCON_BODY_NEON_RED)); + player.button_color_left = static_cast( + ReadIntegerSetting(std::string(player_prefix).append("button_color_left"), + Settings::JOYCON_BUTTONS_NEON_BLUE)); + player.button_color_right = static_cast( + ReadIntegerSetting(std::string(player_prefix).append("button_color_right"), + Settings::JOYCON_BUTTONS_NEON_RED)); + } +} + +void Config::ReadTouchscreenValues() { + Settings::values.touchscreen.enabled = + ReadBooleanSetting(std::string("touchscreen_enabled"), std::make_optional(true)); + Settings::values.touchscreen.rotation_angle = + static_cast(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); + Settings::values.touchscreen.diameter_x = + static_cast(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15)); + Settings::values.touchscreen.diameter_y = + static_cast(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15)); +} + +void Config::ReadAudioValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); + + ReadCategory(Settings::Category::Audio); + ReadCategory(Settings::Category::UiAudio); + + EndGroup(); +} + +void Config::ReadControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + ReadCategory(Settings::Category::Controls); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadPlayerValues(p); + } + + // Disable docked mode if handheld is selected + const auto controller_type = Settings::values.players.GetValue()[0].controller_type; + if (controller_type == Settings::ControllerType::Handheld) { + Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); + Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); + } + + if (IsCustomConfig()) { + EndGroup(); + return; + } + ReadTouchscreenValues(); + ReadMotionTouchValues(); + + EndGroup(); +} + +void Config::ReadMotionTouchValues() { + int num_touch_from_button_maps = BeginArray(std::string("touch_from_button_maps")); + + if (num_touch_from_button_maps > 0) { + for (int i = 0; i < num_touch_from_button_maps; ++i) { + SetArrayIndex(i); + + Settings::TouchFromButtonMap map; + map.name = ReadStringSetting(std::string("name"), std::string("default")); + + const int num_touch_maps = BeginArray(std::string("entries")); + map.buttons.reserve(num_touch_maps); + for (int j = 0; j < num_touch_maps; j++) { + SetArrayIndex(j); + std::string touch_mapping = ReadStringSetting(std::string("bind")); + map.buttons.emplace_back(std::move(touch_mapping)); + } + EndArray(); // entries + Settings::values.touch_from_button_maps.emplace_back(std::move(map)); + } + } else { + Settings::values.touch_from_button_maps.emplace_back( + Settings::TouchFromButtonMap{"default", {}}); + num_touch_from_button_maps = 1; + } + EndArray(); // touch_from_button_maps + + Settings::values.touch_from_button_map_index = std::clamp( + Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); +} + +void Config::ReadCoreValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); + + ReadCategory(Settings::Category::Core); + + EndGroup(); +} + +void Config::ReadDataStorageValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); + + FS::SetYuzuPath(FS::YuzuPath::NANDDir, ReadStringSetting(std::string("nand_directory"))); + FS::SetYuzuPath(FS::YuzuPath::SDMCDir, ReadStringSetting(std::string("sdmc_directory"))); + FS::SetYuzuPath(FS::YuzuPath::LoadDir, ReadStringSetting(std::string("load_directory"))); + FS::SetYuzuPath(FS::YuzuPath::DumpDir, ReadStringSetting(std::string("dump_directory"))); + FS::SetYuzuPath(FS::YuzuPath::TASDir, ReadStringSetting(std::string("tas_directory"))); + + ReadCategory(Settings::Category::DataStorage); + + EndGroup(); +} + +void Config::ReadDebuggingValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); + + // Intentionally not using the QT default setting as this is intended to be changed in the ini + Settings::values.record_frame_times = + ReadBooleanSetting(std::string("record_frame_times"), std::make_optional(false)); + + ReadCategory(Settings::Category::Debugging); + ReadCategory(Settings::Category::DebuggingGraphics); + + EndGroup(); +} + +void Config::ReadServiceValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); + + ReadCategory(Settings::Category::Services); + + EndGroup(); +} + +void Config::ReadDisabledAddOnValues() { + // Custom config section + BeginGroup(std::string("DisabledAddOns")); + + const int size = BeginArray(std::string("")); + for (int i = 0; i < size; ++i) { + SetArrayIndex(i); + const auto title_id = ReadIntegerSetting(std::string("title_id"), 0); + std::vector out; + const int d_size = BeginArray("disabled"); + for (int j = 0; j < d_size; ++j) { + SetArrayIndex(j); + out.push_back(ReadStringSetting(std::string("d"), std::string(""))); + } + EndArray(); // d + Settings::values.disabled_addons.insert_or_assign(title_id, out); + } + EndArray(); // Base disabled addons array - Has no base key + + EndGroup(); +} + +void Config::ReadMiscellaneousValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); + + ReadCategory(Settings::Category::Miscellaneous); + + EndGroup(); +} + +void Config::ReadCpuValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); + + ReadCategory(Settings::Category::Cpu); + ReadCategory(Settings::Category::CpuDebug); + ReadCategory(Settings::Category::CpuUnsafe); + + EndGroup(); +} + +void Config::ReadRendererValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); + + ReadCategory(Settings::Category::Renderer); + ReadCategory(Settings::Category::RendererAdvanced); + ReadCategory(Settings::Category::RendererDebug); + + EndGroup(); +} + +void Config::ReadScreenshotValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); + + ReadCategory(Settings::Category::Screenshots); + FS::SetYuzuPath(FS::YuzuPath::ScreenshotsDir, + ReadStringSetting(std::string("screenshot_path"), + FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); + + EndGroup(); +} + +void Config::ReadSystemValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::System)); + + ReadCategory(Settings::Category::System); + ReadCategory(Settings::Category::SystemAudio); + + EndGroup(); +} + +void Config::ReadWebServiceValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); + + ReadCategory(Settings::Category::WebService); + + EndGroup(); +} + +void Config::ReadNetworkValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); + + ReadCategory(Settings::Category::Network); + + EndGroup(); +} + +void Config::ReadValues() { + if (global) { + ReadDataStorageValues(); + ReadDebuggingValues(); + ReadDisabledAddOnValues(); + ReadNetworkValues(); + ReadServiceValues(); + ReadWebServiceValues(); + ReadMiscellaneousValues(); + } + ReadControlValues(); + ReadCoreValues(); + ReadCpuValues(); + ReadRendererValues(); + ReadAudioValues(); + ReadSystemValues(); +} + +void Config::SavePlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix = std::string("player_").append(ToString(player_index)).append("_"); + } + + const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + if (player.profile_name.empty()) { + // No custom profile selected + return; + } + WriteSetting(std::string(player_prefix).append("profile_name"), player.profile_name, + std::make_optional(std::string(""))); + } + + WriteSetting(std::string(player_prefix).append("type"), static_cast(player.controller_type), + std::make_optional(static_cast(Settings::ControllerType::ProController))); + + if (!player_prefix.empty() || !Settings::IsConfiguringGlobal()) { + WriteSetting(std::string(player_prefix).append("connected"), player.connected, + std::make_optional(player_index == 0)); + WriteSetting(std::string(player_prefix).append("vibration_enabled"), + player.vibration_enabled, std::make_optional(true)); + WriteSetting(std::string(player_prefix).append("vibration_strength"), + player.vibration_strength, std::make_optional(100)); + WriteSetting(std::string(player_prefix).append("body_color_left"), player.body_color_left, + std::make_optional(Settings::JOYCON_BODY_NEON_BLUE)); + WriteSetting(std::string(player_prefix).append("body_color_right"), player.body_color_right, + std::make_optional(Settings::JOYCON_BODY_NEON_RED)); + WriteSetting(std::string(player_prefix).append("button_color_left"), + player.button_color_left, + std::make_optional(Settings::JOYCON_BUTTONS_NEON_BLUE)); + WriteSetting(std::string(player_prefix).append("button_color_right"), + player.button_color_right, + std::make_optional(Settings::JOYCON_BUTTONS_NEON_RED)); + } +} + +void Config::SaveTouchscreenValues() { + const auto& touchscreen = Settings::values.touchscreen; + + WriteSetting(std::string("touchscreen_enabled"), touchscreen.enabled, std::make_optional(true)); + + WriteSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, + std::make_optional(static_cast(0))); + WriteSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, + std::make_optional(static_cast(15))); + WriteSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, + std::make_optional(static_cast(15))); +} + +void Config::SaveMotionTouchValues() { + BeginArray(std::string("touch_from_button_maps")); + for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { + SetArrayIndex(static_cast(p)); + WriteSetting(std::string("name"), Settings::values.touch_from_button_maps[p].name, + std::make_optional(std::string("default"))); + + BeginArray(std::string("entries")); + for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); + ++q) { + SetArrayIndex(static_cast(q)); + WriteSetting(std::string("bind"), + Settings::values.touch_from_button_maps[p].buttons[q]); + } + EndArray(); // entries + } + EndArray(); // touch_from_button_maps +} + +void Config::SaveValues() { + if (global) { + SaveDataStorageValues(); + SaveDebuggingValues(); + SaveDisabledAddOnValues(); + SaveNetworkValues(); + SaveWebServiceValues(); + SaveMiscellaneousValues(); + } + SaveControlValues(); + SaveCoreValues(); + SaveCpuValues(); + SaveRendererValues(); + SaveAudioValues(); + SaveSystemValues(); + + WriteToIni(); +} + +void Config::SaveAudioValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Audio)); + + WriteCategory(Settings::Category::Audio); + WriteCategory(Settings::Category::UiAudio); + + EndGroup(); +} + +void Config::SaveControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + WriteCategory(Settings::Category::Controls); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SavePlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + SaveTouchscreenValues(); + SaveMotionTouchValues(); + + EndGroup(); +} + +void Config::SaveCoreValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Core)); + + WriteCategory(Settings::Category::Core); + + EndGroup(); +} + +void Config::SaveDataStorageValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::DataStorage)); + + WriteSetting(std::string("nand_directory"), FS::GetYuzuPathString(FS::YuzuPath::NANDDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); + WriteSetting(std::string("sdmc_directory"), FS::GetYuzuPathString(FS::YuzuPath::SDMCDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); + WriteSetting(std::string("load_directory"), FS::GetYuzuPathString(FS::YuzuPath::LoadDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); + WriteSetting(std::string("dump_directory"), FS::GetYuzuPathString(FS::YuzuPath::DumpDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); + WriteSetting(std::string("tas_directory"), FS::GetYuzuPathString(FS::YuzuPath::TASDir), + std::make_optional(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); + + WriteCategory(Settings::Category::DataStorage); + + EndGroup(); +} + +void Config::SaveDebuggingValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Debugging)); + + // Intentionally not using the QT default setting as this is intended to be changed in the ini + WriteSetting(std::string("record_frame_times"), Settings::values.record_frame_times); + + WriteCategory(Settings::Category::Debugging); + WriteCategory(Settings::Category::DebuggingGraphics); + + EndGroup(); +} + +void Config::SaveNetworkValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Services)); + + WriteCategory(Settings::Category::Network); + + EndGroup(); +} + +void Config::SaveDisabledAddOnValues() { + // Custom config section + BeginGroup(std::string("DisabledAddOns")); + + int i = 0; + BeginArray(std::string("")); + for (const auto& elem : Settings::values.disabled_addons) { + SetArrayIndex(i); + WriteSetting(std::string("title_id"), elem.first, std::make_optional(static_cast(0))); + BeginArray(std::string("disabled")); + for (std::size_t j = 0; j < elem.second.size(); ++j) { + SetArrayIndex(static_cast(j)); + WriteSetting(std::string("d"), elem.second[j], std::make_optional(std::string(""))); + } + EndArray(); // disabled + ++i; + } + EndArray(); // Base disabled addons array - Has no base key + + EndGroup(); +} + +void Config::SaveMiscellaneousValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Miscellaneous)); + + WriteCategory(Settings::Category::Miscellaneous); + + EndGroup(); +} + +void Config::SaveCpuValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Cpu)); + + WriteCategory(Settings::Category::Cpu); + WriteCategory(Settings::Category::CpuDebug); + WriteCategory(Settings::Category::CpuUnsafe); + + EndGroup(); +} + +void Config::SaveRendererValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Renderer)); + + WriteCategory(Settings::Category::Renderer); + WriteCategory(Settings::Category::RendererAdvanced); + WriteCategory(Settings::Category::RendererDebug); + + EndGroup(); +} + +void Config::SaveScreenshotValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Screenshots)); + + WriteSetting(std::string("screenshot_path"), + FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir)); + WriteCategory(Settings::Category::Screenshots); + + EndGroup(); +} + +void Config::SaveSystemValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::System)); + + WriteCategory(Settings::Category::System); + WriteCategory(Settings::Category::SystemAudio); + + EndGroup(); +} + +void Config::SaveWebServiceValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::WebService)); + + WriteCategory(Settings::Category::WebService); + + EndGroup(); +} + +bool Config::ReadBooleanSetting(const std::string& key, const std::optional default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), false); + } + + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), false)) { + return static_cast(default_value.value()); + } else { + return config->GetBoolValue(GetSection().c_str(), full_key.c_str(), + static_cast(default_value.value())); + } +} + +s64 Config::ReadIntegerSetting(const std::string& key, const std::optional default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + try { + return std::stoll( + std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); + } catch (...) { + return 0; + } + } + + s64 result = 0; + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + try { + result = std::stoll(std::string(config->GetValue( + GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); + } catch (...) { + result = default_value.value(); + } + } + return result; +} + +double Config::ReadDoubleSetting(const std::string& key, + const std::optional default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + return config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), 0); + } + + double result; + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + result = + config->GetDoubleValue(GetSection().c_str(), full_key.c_str(), default_value.value()); + } + return result; +} + +std::string Config::ReadStringSetting(const std::string& key, + const std::optional default_value) { + std::string result; + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + result = config->GetValue(GetSection().c_str(), full_key.c_str(), ""); + boost::replace_all(result, "\"", ""); + return result; + } + + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + result = + config->GetValue(GetSection().c_str(), full_key.c_str(), default_value.value().c_str()); + } + boost::replace_all(result, "\"", ""); + boost::replace_all(result, "//", "/"); + return result; +} + +bool Config::Exists(const std::string& section, const std::string& key) const { + const std::string value = config->GetValue(section.c_str(), key.c_str(), ""); + return !value.empty(); +} + +template +void Config::WriteSetting(const std::string& key, const Type& value, + const std::optional& default_value, + const std::optional& use_global) { + std::string full_key = GetFullKey(key, false); + + std::string saved_value; + std::string string_default; + if constexpr (std::is_same_v) { + saved_value.append(AdjustOutputString(value)); + if (default_value.has_value()) { + string_default.append(AdjustOutputString(default_value.value())); + } + } else { + saved_value.append(AdjustOutputString(ToString(value))); + if (default_value.has_value()) { + string_default.append(ToString(default_value.value())); + } + } + + if (default_value.has_value() && use_global.has_value()) { + if (!global) { + WriteSettingInternal(std::string(full_key).append("\\global"), + ToString(use_global.value())); + } + if (global || use_global.value() == false) { + WriteSettingInternal(std::string(full_key).append("\\default"), + ToString(string_default == saved_value)); + WriteSettingInternal(full_key, saved_value); + } + } else if (default_value.has_value() && !use_global.has_value()) { + WriteSettingInternal(std::string(full_key).append("\\default"), + ToString(string_default == saved_value)); + WriteSettingInternal(full_key, saved_value); + } else { + WriteSettingInternal(full_key, saved_value); + } +} + +void Config::WriteSettingInternal(const std::string& key, const std::string& value) { + config->SetValue(GetSection().c_str(), key.c_str(), value.c_str()); +} + +void Config::Reload() { + ReadValues(); + // To apply default value changes + SaveValues(); +} + +void Config::Save() { + SaveValues(); +} + +void Config::ClearControlPlayerValues() const { + // If key is an empty string, all keys in the current group() are removed. + const char* section = Settings::TranslateCategory(Settings::Category::Controls); + CSimpleIniA::TNamesDepend keys; + config->GetAllKeys(section, keys); + for (const auto& key : keys) { + if (std::string(config->GetValue(section, key.pItem)).empty()) { + config->Delete(section, key.pItem); + } + } +} + +const std::string& Config::GetConfigFilePath() const { + return config_loc; +} + +void Config::ReadCategory(const Settings::Category category) { + const auto& settings = FindRelevantList(category); + std::ranges::for_each(settings, [&](const auto& setting) { ReadSettingGeneric(setting); }); +} + +void Config::WriteCategory(const Settings::Category category) { + const auto& settings = FindRelevantList(category); + std::ranges::for_each(settings, [&](const auto& setting) { WriteSettingGeneric(setting); }); +} + +void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { + if (!setting->Save() || (!setting->Switchable() && !global)) { + return; + } + + const std::string key = AdjustKey(setting->GetLabel()); + const std::string default_value(setting->DefaultToString()); + + bool use_global = true; + if (setting->Switchable() && !global) { + use_global = + ReadBooleanSetting(std::string(key).append("\\use_global"), std::make_optional(true)); + setting->SetGlobal(use_global); + } + + if (global || !use_global) { + const bool is_default = + ReadBooleanSetting(std::string(key).append("\\default"), std::make_optional(true)); + if (!is_default) { + const std::string setting_string = ReadStringSetting(key, default_value); + setting->LoadString(setting_string); + } else { + // Empty string resets the Setting to default + setting->LoadString(""); + } + } +} + +void Config::WriteSettingGeneric(const Settings::BasicSetting* const setting) { + if (!setting->Save()) { + return; + } + + std::string key = AdjustKey(setting->GetLabel()); + if (setting->Switchable()) { + if (!global) { + WriteSetting(std::string(key).append("\\use_global"), setting->UsingGlobal()); + } + if (global || !setting->UsingGlobal()) { + WriteSetting(std::string(key).append("\\default"), + setting->ToString() == setting->DefaultToString()); + WriteSetting(key, setting->ToString()); + } + } else if (global) { + WriteSetting(std::string(key).append("\\default"), + setting->ToString() == setting->DefaultToString()); + WriteSetting(key, setting->ToString()); + } +} + +void Config::BeginGroup(const std::string& group) { + // You can't begin a group while reading/writing from a config array + ASSERT(array_stack.empty()); + + key_stack.push_back(AdjustKey(group)); +} + +void Config::EndGroup() { + // You can't end a group if you haven't started one yet + ASSERT(!key_stack.empty()); + + // You can't end a group when reading/writing from a config array + ASSERT(array_stack.empty()); + + key_stack.pop_back(); +} + +std::string Config::GetSection() { + if (key_stack.empty()) { + return std::string{""}; + } + + return key_stack.front(); +} + +std::string Config::GetGroup() const { + if (key_stack.size() <= 1) { + return std::string{""}; + } + + std::string key; + for (size_t i = 1; i < key_stack.size(); ++i) { + key.append(key_stack[i]).append("\\"); + } + return key; +} + +std::string Config::AdjustKey(const std::string& key) { + std::string adjusted_key(key); + boost::replace_all(adjusted_key, "/", "\\"); + boost::replace_all(adjusted_key, " ", "%20"); + return adjusted_key; +} + +std::string Config::AdjustOutputString(const std::string& string) { + std::string adjusted_string(string); + boost::replace_all(adjusted_string, "\\", "/"); + boost::replace_all(adjusted_string, "//", "/"); + + // Needed for backwards compatibility with QSettings deserialization + for (const auto& special_character : special_characters) { + if (adjusted_string.find(special_character) != std::string::npos) { + adjusted_string.insert(0, "\""); + adjusted_string.append("\""); + break; + } + } + return adjusted_string; +} + +std::string Config::GetFullKey(const std::string& key, bool skipArrayIndex) { + if (array_stack.empty()) { + return std::string(GetGroup()).append(AdjustKey(key)); + } + + std::string array_key; + for (size_t i = 0; i < array_stack.size(); ++i) { + if (!array_stack[i].name.empty()) { + array_key.append(array_stack[i].name).append("\\"); + } + + if (!skipArrayIndex || (array_stack.size() - 1 != i && array_stack.size() > 1)) { + array_key.append(ToString(array_stack[i].index)).append("\\"); + } + } + std::string final_key = std::string(GetGroup()).append(array_key).append(AdjustKey(key)); + return final_key; +} + +int Config::BeginArray(const std::string& array) { + array_stack.push_back(ConfigArray{AdjustKey(array), 0, 0}); + const int size = config->GetLongValue(GetSection().c_str(), + GetFullKey(std::string("size"), true).c_str(), 0); + array_stack.back().size = size; + return size; +} + +void Config::EndArray() { + // You can't end a config array before starting one + ASSERT(!array_stack.empty()); + + // Write out the size to config + if (key_stack.size() == 1 && array_stack.back().name.empty()) { + // Edge-case where the first array created doesn't have a name + config->SetValue(GetSection().c_str(), std::string("size").c_str(), + ToString(array_stack.back().size).c_str()); + } else { + const auto key = GetFullKey(std::string("size"), true); + config->SetValue(GetSection().c_str(), key.c_str(), + ToString(array_stack.back().size).c_str()); + } + + array_stack.pop_back(); +} + +void Config::SetArrayIndex(const int index) { + // You can't set the array index if you haven't started one yet + ASSERT(!array_stack.empty()); + + const int array_index = index + 1; + + // You can't exceed the known max size of the array by more than 1 + ASSERT(array_stack.front().size + 1 >= array_index); + + // Change the config array size to the current index since you may want + // to reduce the number of elements that you read back from the config + // in the future. + array_stack.back().size = array_index; + array_stack.back().index = array_index; +} diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h new file mode 100644 index 000000000..f741aa8bb --- /dev/null +++ b/src/frontend_common/config.h @@ -0,0 +1,206 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/settings.h" + +#include +#include + +// Workaround for conflicting definition in libloaderapi.h caused by SimpleIni +#undef LoadString +#undef CreateFile +#undef DeleteFile +#undef CopyFile +#undef CreateDirectory +#undef MoveFile + +namespace Core { +class System; +} + +class Config { +public: + enum class ConfigType { + GlobalConfig, + PerGameConfig, + InputProfile, + }; + + virtual ~Config() = default; + + void ClearControlPlayerValues() const; + + [[nodiscard]] const std::string& GetConfigFilePath() const; + + [[nodiscard]] bool Exists(const std::string& section, const std::string& key) const; + +protected: + explicit Config(ConfigType config_type = ConfigType::GlobalConfig); + + void Initialize(const std::string& config_name = "config"); + void Initialize(std::optional config_path); + + void WriteToIni() const; + + void SetUpIni(); + [[nodiscard]] bool IsCustomConfig() const; + + void Reload(); + void Save(); + + /** + * Derived config classes must implement this so they can reload all platform-specific + * values and global ones. + */ + virtual void ReloadAllValues() = 0; + + /** + * Derived config classes must implement this so they can save all platform-specific + * and global values. + */ + virtual void SaveAllValues() = 0; + + void ReadValues(); + void ReadPlayerValues(std::size_t player_index); + + void ReadTouchscreenValues(); + void ReadMotionTouchValues(); + + // Read functions bases off the respective config section names. + void ReadAudioValues(); + void ReadControlValues(); + void ReadCoreValues(); + void ReadDataStorageValues(); + void ReadDebuggingValues(); + void ReadServiceValues(); + void ReadDisabledAddOnValues(); + void ReadMiscellaneousValues(); + void ReadCpuValues(); + void ReadRendererValues(); + void ReadScreenshotValues(); + void ReadSystemValues(); + void ReadWebServiceValues(); + void ReadNetworkValues(); + + // Read platform specific sections + virtual void ReadHidbusValues() = 0; + virtual void ReadDebugControlValues() = 0; + virtual void ReadPathValues() = 0; + virtual void ReadShortcutValues() = 0; + virtual void ReadUIValues() = 0; + virtual void ReadUIGamelistValues() = 0; + virtual void ReadUILayoutValues() = 0; + virtual void ReadMultiplayerValues() = 0; + + void SaveValues(); + void SavePlayerValues(std::size_t player_index); + void SaveTouchscreenValues(); + void SaveMotionTouchValues(); + + // Save functions based off the respective config section names. + void SaveAudioValues(); + void SaveControlValues(); + void SaveCoreValues(); + void SaveDataStorageValues(); + void SaveDebuggingValues(); + void SaveNetworkValues(); + void SaveDisabledAddOnValues(); + void SaveMiscellaneousValues(); + void SaveCpuValues(); + void SaveRendererValues(); + void SaveScreenshotValues(); + void SaveSystemValues(); + void SaveWebServiceValues(); + + // Save platform specific sections + virtual void SaveHidbusValues() = 0; + virtual void SaveDebugControlValues() = 0; + virtual void SavePathValues() = 0; + virtual void SaveShortcutValues() = 0; + virtual void SaveUIValues() = 0; + virtual void SaveUIGamelistValues() = 0; + virtual void SaveUILayoutValues() = 0; + virtual void SaveMultiplayerValues() = 0; + + virtual std::vector& FindRelevantList(Settings::Category category) = 0; + + /** + * Reads a setting from the qt_config. + * + * @param key The setting's identifier + * @param default_value The value to use when the setting is not already present in the config + */ + bool ReadBooleanSetting(const std::string& key, + std::optional default_value = std::nullopt); + s64 ReadIntegerSetting(const std::string& key, std::optional default_value = std::nullopt); + double ReadDoubleSetting(const std::string& key, + std::optional default_value = std::nullopt); + std::string ReadStringSetting(const std::string& key, + std::optional default_value = std::nullopt); + + /** + * Writes a setting to the qt_config. + * + * @param key The setting's idetentifier + * @param value Value of the setting + * @param default_value Default of the setting if not present in config + * @param use_global Specifies if the custom or global config should be in use, for custom + * configs + */ + template + void WriteSetting(const std::string& key, const Type& value, + const std::optional& default_value = std::nullopt, + const std::optional& use_global = std::nullopt); + void WriteSettingInternal(const std::string& key, const std::string& value); + + void ReadCategory(Settings::Category category); + void WriteCategory(Settings::Category category); + void ReadSettingGeneric(Settings::BasicSetting* setting); + void WriteSettingGeneric(const Settings::BasicSetting* setting); + + template + [[nodiscard]] std::string ToString(const T& value_) { + if constexpr (std::is_same_v) { + return value_; + } else if constexpr (std::is_same_v>) { + return value_.has_value() ? std::to_string(*value_) : "none"; + } else if constexpr (std::is_same_v) { + return value_ ? "true" : "false"; + } else { + return std::to_string(static_cast(value_)); + } + } + + void BeginGroup(const std::string& group); + void EndGroup(); + std::string GetSection(); + [[nodiscard]] std::string GetGroup() const; + static std::string AdjustKey(const std::string& key); + static std::string AdjustOutputString(const std::string& string); + std::string GetFullKey(const std::string& key, bool skipArrayIndex); + int BeginArray(const std::string& array); + void EndArray(); + void SetArrayIndex(int index); + + const ConfigType type; + std::unique_ptr config; + std::string config_loc; + const bool global; + +private: + inline static std::array special_characters = {'!', '#', '$', '%', '^', '&', '*', + '|', ';', '\'', '\"', ',', '<', '.', + '>', '?', '`', '~', '='}; + + struct ConfigArray { + std::string name; + int size; + int index; + }; + std::vector array_stack; + std::vector key_stack; +}; diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 33e1fb663..b251c9045 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -38,8 +38,6 @@ add_executable(yuzu compatdb.ui compatibility_list.cpp compatibility_list.h - configuration/config.cpp - configuration/config.h configuration/configuration_shared.cpp configuration/configuration_shared.h configuration/configure.ui @@ -147,6 +145,8 @@ add_executable(yuzu configuration/shared_translation.h configuration/shared_widget.cpp configuration/shared_widget.h + configuration/qt_config.cpp + configuration/qt_config.h debugger/console.cpp debugger/console.h debugger/controller.cpp @@ -344,7 +344,7 @@ endif() create_target_directory_groups(yuzu) -target_link_libraries(yuzu PRIVATE common core input_common network video_core) +target_link_libraries(yuzu PRIVATE common core input_common frontend_common network video_core) target_link_libraries(yuzu PRIVATE Boost::headers glad Qt${QT_MAJOR_VERSION}::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp deleted file mode 100644 index c0ae6468b..000000000 --- a/src/yuzu/configuration/config.cpp +++ /dev/null @@ -1,1309 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/settings.h" -#include "common/settings_common.h" -#include "common/settings_enums.h" -#include "core/core.h" -#include "core/hle/service/acc/profile_manager.h" -#include "core/hle/service/hid/controllers/npad.h" -#include "input_common/main.h" -#include "network/network.h" -#include "yuzu/configuration/config.h" - -namespace FS = Common::FS; - -Config::Config(const std::string& config_name, ConfigType config_type) - : type(config_type), global{config_type == ConfigType::GlobalConfig} { - Initialize(config_name); -} - -Config::~Config() { - if (global) { - Save(); - } -} - -const std::array Config::default_buttons = { - Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F, - Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, - Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, - Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, - Qt::Key_Q, Qt::Key_E, -}; - -const std::array Config::default_motions = { - Qt::Key_7, - Qt::Key_8, -}; - -const std::array, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{ - { - Qt::Key_W, - Qt::Key_S, - Qt::Key_A, - Qt::Key_D, - }, - { - Qt::Key_I, - Qt::Key_K, - Qt::Key_J, - Qt::Key_L, - }, -}}; - -const std::array Config::default_stick_mod = { - Qt::Key_Shift, - 0, -}; - -const std::array Config::default_ringcon_analogs{{ - Qt::Key_A, - Qt::Key_D, -}}; - -const std::map Config::anti_aliasing_texts_map = { - {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, - {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, - {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, -}; - -const std::map Config::scaling_filter_texts_map = { - {Settings::ScalingFilter::NearestNeighbor, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, - {Settings::ScalingFilter::Bilinear, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, - {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, - {Settings::ScalingFilter::Gaussian, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, - {Settings::ScalingFilter::ScaleForce, - QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, - {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, -}; - -const std::map Config::use_docked_mode_texts_map = { - {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, - {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, -}; - -const std::map Config::gpu_accuracy_texts_map = { - {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, - {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, - {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, -}; - -const std::map Config::renderer_backend_texts_map = { - {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, - {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, - {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, -}; - -const std::map Config::shader_backend_texts_map = { - {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, - {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, - {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, -}; - -// This shouldn't have anything except static initializers (no functions). So -// QKeySequence(...).toString() is NOT ALLOWED HERE. -// This must be in alphabetical order according to action name as it must have the same order as -// UISetting::values.shortcuts, which is alphabetically ordered. -// clang-format off -const std::array Config::default_hotkeys{{ - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("="), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral(""), QStringLiteral(""), Qt::ApplicationShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut, false}}, -}}; -// clang-format on - -void Config::Initialize(const std::string& config_name) { - const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); - const auto config_file = fmt::format("{}.ini", config_name); - - switch (type) { - case ConfigType::GlobalConfig: - qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file); - void(FS::CreateParentDir(qt_config_loc)); - qt_config = std::make_unique(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - Reload(); - break; - case ConfigType::PerGameConfig: - qt_config_loc = - FS::PathToUTF8String(fs_config_loc / "custom" / FS::ToU8String(config_file)); - void(FS::CreateParentDir(qt_config_loc)); - qt_config = std::make_unique(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - Reload(); - break; - case ConfigType::InputProfile: - qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); - void(FS::CreateParentDir(qt_config_loc)); - qt_config = std::make_unique(QString::fromStdString(qt_config_loc), - QSettings::IniFormat); - break; - } -} - -bool Config::IsCustomConfig() { - return type == ConfigType::PerGameConfig; -} - -void Config::ReadPlayerValue(std::size_t player_index) { - const QString player_prefix = [this, player_index] { - if (type == ConfigType::InputProfile) { - return QString{}; - } else { - return QStringLiteral("player_%1_").arg(player_index); - } - }(); - - auto& player = Settings::values.players.GetValue()[player_index]; - if (IsCustomConfig()) { - const auto profile_name = - qt_config->value(QStringLiteral("%1profile_name").arg(player_prefix), QString{}) - .toString() - .toStdString(); - if (profile_name.empty()) { - // Use the global input config - player = Settings::values.players.GetValue(true)[player_index]; - return; - } - player.profile_name = profile_name; - } - - if (player_prefix.isEmpty() && Settings::IsConfiguringGlobal()) { - const auto controller = static_cast( - qt_config - ->value(QStringLiteral("%1type").arg(player_prefix), - static_cast(Settings::ControllerType::ProController)) - .toUInt()); - - if (controller == Settings::ControllerType::LeftJoycon || - controller == Settings::ControllerType::RightJoycon) { - player.controller_type = controller; - } - } else { - player.connected = - ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0) - .toBool(); - - player.controller_type = static_cast( - qt_config - ->value(QStringLiteral("%1type").arg(player_prefix), - static_cast(Settings::ControllerType::ProController)) - .toUInt()); - - player.vibration_enabled = - qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true) - .toBool(); - - player.vibration_strength = - qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100) - .toInt(); - - player.body_color_left = qt_config - ->value(QStringLiteral("%1body_color_left").arg(player_prefix), - Settings::JOYCON_BODY_NEON_BLUE) - .toUInt(); - player.body_color_right = - qt_config - ->value(QStringLiteral("%1body_color_right").arg(player_prefix), - Settings::JOYCON_BODY_NEON_RED) - .toUInt(); - player.button_color_left = - qt_config - ->value(QStringLiteral("%1button_color_left").arg(player_prefix), - Settings::JOYCON_BUTTONS_NEON_BLUE) - .toUInt(); - player.button_color_right = - qt_config - ->value(QStringLiteral("%1button_color_right").arg(player_prefix), - Settings::JOYCON_BUTTONS_NEON_RED) - .toUInt(); - } - - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - auto& player_buttons = player.buttons[i]; - - player_buttons = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeButton::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_buttons.empty()) { - player_buttons = default_param; - } - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - auto& player_analogs = player.analogs[i]; - - player_analogs = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_analogs.empty()) { - player_analogs = default_param; - } - } - - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; - - player_motions = qt_config - ->value(QStringLiteral("%1").arg(player_prefix) + - QString::fromUtf8(Settings::NativeMotion::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (player_motions.empty()) { - player_motions = default_param; - } - } -} - -void Config::ReadDebugValues() { - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; - - debug_pad_buttons = qt_config - ->value(QStringLiteral("debug_pad_") + - QString::fromUtf8(Settings::NativeButton::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (debug_pad_buttons.empty()) { - debug_pad_buttons = default_param; - } - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; - - debug_pad_analogs = qt_config - ->value(QStringLiteral("debug_pad_") + - QString::fromUtf8(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (debug_pad_analogs.empty()) { - debug_pad_analogs = default_param; - } - } -} - -void Config::ReadTouchscreenValues() { - Settings::values.touchscreen.enabled = - ReadSetting(QStringLiteral("touchscreen_enabled"), true).toBool(); - - Settings::values.touchscreen.rotation_angle = - ReadSetting(QStringLiteral("touchscreen_angle"), 0).toUInt(); - Settings::values.touchscreen.diameter_x = - ReadSetting(QStringLiteral("touchscreen_diameter_x"), 15).toUInt(); - Settings::values.touchscreen.diameter_y = - ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt(); -} - -void Config::ReadHidbusValues() { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); - auto& ringcon_analogs = Settings::values.ringcon_analogs; - - ringcon_analogs = - qt_config->value(QStringLiteral("ring_controller"), QString::fromStdString(default_param)) - .toString() - .toStdString(); - if (ringcon_analogs.empty()) { - ringcon_analogs = default_param; - } -} - -void Config::ReadAudioValues() { - qt_config->beginGroup(QStringLiteral("Audio")); - - ReadCategory(Settings::Category::Audio); - ReadCategory(Settings::Category::UiAudio); - - qt_config->endGroup(); -} - -void Config::ReadControlValues() { - qt_config->beginGroup(QStringLiteral("Controls")); - - ReadCategory(Settings::Category::Controls); - - Settings::values.players.SetGlobal(!IsCustomConfig()); - for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { - ReadPlayerValue(p); - } - - // Disable docked mode if handheld is selected - const auto controller_type = Settings::values.players.GetValue()[0].controller_type; - if (controller_type == Settings::ControllerType::Handheld) { - Settings::values.use_docked_mode.SetGlobal(!IsCustomConfig()); - Settings::values.use_docked_mode.SetValue(Settings::ConsoleMode::Handheld); - } - - if (IsCustomConfig()) { - qt_config->endGroup(); - return; - } - ReadDebugValues(); - ReadTouchscreenValues(); - ReadMotionTouchValues(); - ReadHidbusValues(); - - qt_config->endGroup(); -} - -void Config::ReadMotionTouchValues() { - int num_touch_from_button_maps = - qt_config->beginReadArray(QStringLiteral("touch_from_button_maps")); - - if (num_touch_from_button_maps > 0) { - const auto append_touch_from_button_map = [this] { - Settings::TouchFromButtonMap map; - map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default")) - .toString() - .toStdString(); - const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries")); - map.buttons.reserve(num_touch_maps); - for (int i = 0; i < num_touch_maps; i++) { - qt_config->setArrayIndex(i); - std::string touch_mapping = - ReadSetting(QStringLiteral("bind")).toString().toStdString(); - map.buttons.emplace_back(std::move(touch_mapping)); - } - qt_config->endArray(); // entries - Settings::values.touch_from_button_maps.emplace_back(std::move(map)); - }; - - for (int i = 0; i < num_touch_from_button_maps; ++i) { - qt_config->setArrayIndex(i); - append_touch_from_button_map(); - } - } else { - Settings::values.touch_from_button_maps.emplace_back( - Settings::TouchFromButtonMap{"default", {}}); - num_touch_from_button_maps = 1; - } - qt_config->endArray(); - - Settings::values.touch_from_button_map_index = std::clamp( - Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); -} - -void Config::ReadCoreValues() { - qt_config->beginGroup(QStringLiteral("Core")); - - ReadCategory(Settings::Category::Core); - - qt_config->endGroup(); -} - -void Config::ReadDataStorageValues() { - qt_config->beginGroup(QStringLiteral("Data Storage")); - - FS::SetYuzuPath( - FS::YuzuPath::NANDDir, - qt_config - ->value(QStringLiteral("nand_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))) - .toString() - .toStdString()); - FS::SetYuzuPath( - FS::YuzuPath::SDMCDir, - qt_config - ->value(QStringLiteral("sdmc_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))) - .toString() - .toStdString()); - FS::SetYuzuPath( - FS::YuzuPath::LoadDir, - qt_config - ->value(QStringLiteral("load_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))) - .toString() - .toStdString()); - FS::SetYuzuPath( - FS::YuzuPath::DumpDir, - qt_config - ->value(QStringLiteral("dump_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) - .toString() - .toStdString()); - FS::SetYuzuPath(FS::YuzuPath::TASDir, - qt_config - ->value(QStringLiteral("tas_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))) - .toString() - .toStdString()); - - ReadCategory(Settings::Category::DataStorage); - - qt_config->endGroup(); -} - -void Config::ReadDebuggingValues() { - qt_config->beginGroup(QStringLiteral("Debugging")); - - // Intentionally not using the QT default setting as this is intended to be changed in the ini - Settings::values.record_frame_times = - qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); - - ReadCategory(Settings::Category::Debugging); - ReadCategory(Settings::Category::DebuggingGraphics); - - qt_config->endGroup(); -} - -void Config::ReadServiceValues() { - qt_config->beginGroup(QStringLiteral("Services")); - - ReadCategory(Settings::Category::Services); - - qt_config->endGroup(); -} - -void Config::ReadDisabledAddOnValues() { - const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); - - for (int i = 0; i < size; ++i) { - qt_config->setArrayIndex(i); - const auto title_id = ReadSetting(QStringLiteral("title_id"), 0).toULongLong(); - std::vector out; - const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled")); - for (int j = 0; j < d_size; ++j) { - qt_config->setArrayIndex(j); - out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString()); - } - qt_config->endArray(); - Settings::values.disabled_addons.insert_or_assign(title_id, out); - } - - qt_config->endArray(); -} - -void Config::ReadMiscellaneousValues() { - qt_config->beginGroup(QStringLiteral("Miscellaneous")); - - ReadCategory(Settings::Category::Miscellaneous); - - qt_config->endGroup(); -} - -void Config::ReadPathValues() { - qt_config->beginGroup(QStringLiteral("Paths")); - - UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); - UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); - UISettings::values.game_dir_deprecated = - ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); - UISettings::values.game_dir_deprecated_deepscan = - ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); - const int gamedirs_size = qt_config->beginReadArray(QStringLiteral("gamedirs")); - for (int i = 0; i < gamedirs_size; ++i) { - qt_config->setArrayIndex(i); - UISettings::GameDir game_dir; - game_dir.path = ReadSetting(QStringLiteral("path")).toString(); - game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool(); - game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool(); - UISettings::values.game_dirs.append(game_dir); - } - qt_config->endArray(); - // create NAND and SD card directories if empty, these are not removable through the UI, - // also carries over old game list settings if present - if (UISettings::values.game_dirs.isEmpty()) { - UISettings::GameDir game_dir; - game_dir.path = QStringLiteral("SDMC"); - game_dir.expanded = true; - UISettings::values.game_dirs.append(game_dir); - game_dir.path = QStringLiteral("UserNAND"); - UISettings::values.game_dirs.append(game_dir); - game_dir.path = QStringLiteral("SysNAND"); - UISettings::values.game_dirs.append(game_dir); - if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { - game_dir.path = UISettings::values.game_dir_deprecated; - game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; - UISettings::values.game_dirs.append(game_dir); - } - } - UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); - UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); - - qt_config->endGroup(); -} - -void Config::ReadCpuValues() { - qt_config->beginGroup(QStringLiteral("Cpu")); - - ReadCategory(Settings::Category::Cpu); - ReadCategory(Settings::Category::CpuDebug); - ReadCategory(Settings::Category::CpuUnsafe); - - qt_config->endGroup(); -} - -void Config::ReadRendererValues() { - qt_config->beginGroup(QStringLiteral("Renderer")); - - ReadCategory(Settings::Category::Renderer); - ReadCategory(Settings::Category::RendererAdvanced); - ReadCategory(Settings::Category::RendererDebug); - - qt_config->endGroup(); -} - -void Config::ReadScreenshotValues() { - qt_config->beginGroup(QStringLiteral("Screenshots")); - - ReadCategory(Settings::Category::Screenshots); - FS::SetYuzuPath( - FS::YuzuPath::ScreenshotsDir, - qt_config - ->value(QStringLiteral("screenshot_path"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))) - .toString() - .toStdString()); - - qt_config->endGroup(); -} - -void Config::ReadShortcutValues() { - qt_config->beginGroup(QStringLiteral("Shortcuts")); - - for (const auto& [name, group, shortcut] : default_hotkeys) { - qt_config->beginGroup(group); - qt_config->beginGroup(name); - // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 - // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open - // a file dialog in windowed mode - UISettings::values.shortcuts.push_back( - {name, - group, - {ReadSetting(QStringLiteral("KeySeq"), shortcut.keyseq).toString(), - ReadSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq) - .toString(), - shortcut.context, ReadSetting(QStringLiteral("Repeat"), shortcut.repeat).toBool()}}); - qt_config->endGroup(); - qt_config->endGroup(); - } - - qt_config->endGroup(); -} - -void Config::ReadSystemValues() { - qt_config->beginGroup(QStringLiteral("System")); - - ReadCategory(Settings::Category::System); - ReadCategory(Settings::Category::SystemAudio); - - qt_config->endGroup(); -} - -void Config::ReadUIValues() { - qt_config->beginGroup(QStringLiteral("UI")); - - UISettings::values.theme = - ReadSetting( - QStringLiteral("theme"), - QString::fromUtf8(UISettings::themes[static_cast(default_theme)].second)) - .toString(); - - ReadUIGamelistValues(); - ReadUILayoutValues(); - ReadPathValues(); - ReadScreenshotValues(); - ReadShortcutValues(); - ReadMultiplayerValues(); - - ReadCategory(Settings::Category::Ui); - ReadCategory(Settings::Category::UiGeneral); - - qt_config->endGroup(); -} - -void Config::ReadUIGamelistValues() { - qt_config->beginGroup(QStringLiteral("UIGameList")); - - ReadCategory(Settings::Category::UiGameList); - - const int favorites_size = qt_config->beginReadArray(QStringLiteral("favorites")); - for (int i = 0; i < favorites_size; i++) { - qt_config->setArrayIndex(i); - UISettings::values.favorited_ids.append( - ReadSetting(QStringLiteral("program_id")).toULongLong()); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -void Config::ReadUILayoutValues() { - qt_config->beginGroup(QStringLiteral("UILayout")); - - UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray(); - UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray(); - UISettings::values.renderwindow_geometry = - ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray(); - UISettings::values.gamelist_header_state = - ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); - UISettings::values.microprofile_geometry = - ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); - - ReadCategory(Settings::Category::UiLayout); - - qt_config->endGroup(); -} - -void Config::ReadWebServiceValues() { - qt_config->beginGroup(QStringLiteral("WebService")); - - ReadCategory(Settings::Category::WebService); - - qt_config->endGroup(); -} - -void Config::ReadMultiplayerValues() { - qt_config->beginGroup(QStringLiteral("Multiplayer")); - - ReadCategory(Settings::Category::Multiplayer); - - // Read ban list back - int size = qt_config->beginReadArray(QStringLiteral("username_ban_list")); - UISettings::values.multiplayer_ban_list.first.resize(size); - for (int i = 0; i < size; ++i) { - qt_config->setArrayIndex(i); - UISettings::values.multiplayer_ban_list.first[i] = - ReadSetting(QStringLiteral("username")).toString().toStdString(); - } - qt_config->endArray(); - size = qt_config->beginReadArray(QStringLiteral("ip_ban_list")); - UISettings::values.multiplayer_ban_list.second.resize(size); - for (int i = 0; i < size; ++i) { - qt_config->setArrayIndex(i); - UISettings::values.multiplayer_ban_list.second[i] = - ReadSetting(QStringLiteral("ip")).toString().toStdString(); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -void Config::ReadNetworkValues() { - qt_config->beginGroup(QString::fromStdString("Services")); - - ReadCategory(Settings::Category::Network); - - qt_config->endGroup(); -} - -void Config::ReadValues() { - if (global) { - ReadDataStorageValues(); - ReadDebuggingValues(); - ReadDisabledAddOnValues(); - ReadNetworkValues(); - ReadServiceValues(); - ReadUIValues(); - ReadWebServiceValues(); - ReadMiscellaneousValues(); - } - ReadControlValues(); - ReadCoreValues(); - ReadCpuValues(); - ReadRendererValues(); - ReadAudioValues(); - ReadSystemValues(); -} - -void Config::SavePlayerValue(std::size_t player_index) { - const QString player_prefix = [this, player_index] { - if (type == ConfigType::InputProfile) { - return QString{}; - } else { - return QStringLiteral("player_%1_").arg(player_index); - } - }(); - - const auto& player = Settings::values.players.GetValue()[player_index]; - if (IsCustomConfig()) { - if (player.profile_name.empty()) { - // No custom profile selected - return; - } - WriteSetting(QStringLiteral("%1profile_name").arg(player_prefix), - QString::fromStdString(player.profile_name), QString{}); - } - - WriteSetting(QStringLiteral("%1type").arg(player_prefix), - static_cast(player.controller_type), - static_cast(Settings::ControllerType::ProController)); - - if (!player_prefix.isEmpty() || !Settings::IsConfiguringGlobal()) { - WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, - player_index == 0); - WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix), - player.vibration_enabled, true); - WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix), - player.vibration_strength, 100); - WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left, - Settings::JOYCON_BODY_NEON_BLUE); - WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix), - player.body_color_right, Settings::JOYCON_BODY_NEON_RED); - WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix), - player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE); - WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix), - player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED); - } - - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(player.buttons[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(player.analogs[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); - WriteSetting(QStringLiteral("%1").arg(player_prefix) + - QString::fromStdString(Settings::NativeMotion::mapping[i]), - QString::fromStdString(player.motions[i]), - QString::fromStdString(default_param)); - } -} - -void Config::SaveDebugValues() { - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - WriteSetting(QStringLiteral("debug_pad_") + - QString::fromStdString(Settings::NativeButton::mapping[i]), - QString::fromStdString(Settings::values.debug_pad_buttons[i]), - QString::fromStdString(default_param)); - } - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_stick_mod[i], 0.5f); - WriteSetting(QStringLiteral("debug_pad_") + - QString::fromStdString(Settings::NativeAnalog::mapping[i]), - QString::fromStdString(Settings::values.debug_pad_analogs[i]), - QString::fromStdString(default_param)); - } -} - -void Config::SaveTouchscreenValues() { - const auto& touchscreen = Settings::values.touchscreen; - - WriteSetting(QStringLiteral("touchscreen_enabled"), touchscreen.enabled, true); - - WriteSetting(QStringLiteral("touchscreen_angle"), touchscreen.rotation_angle, 0); - WriteSetting(QStringLiteral("touchscreen_diameter_x"), touchscreen.diameter_x, 15); - WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15); -} - -void Config::SaveMotionTouchValues() { - qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps")); - for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) { - qt_config->setArrayIndex(static_cast(p)); - WriteSetting(QStringLiteral("name"), - QString::fromStdString(Settings::values.touch_from_button_maps[p].name), - QStringLiteral("default")); - qt_config->beginWriteArray(QStringLiteral("entries")); - for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size(); - ++q) { - qt_config->setArrayIndex(static_cast(q)); - WriteSetting( - QStringLiteral("bind"), - QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q])); - } - qt_config->endArray(); - } - qt_config->endArray(); -} - -void Config::SaveHidbusValues() { - const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); - WriteSetting(QStringLiteral("ring_controller"), - QString::fromStdString(Settings::values.ringcon_analogs), - QString::fromStdString(default_param)); -} - -void Config::SaveValues() { - if (global) { - SaveDataStorageValues(); - SaveDebuggingValues(); - SaveDisabledAddOnValues(); - SaveNetworkValues(); - SaveUIValues(); - SaveWebServiceValues(); - SaveMiscellaneousValues(); - } - SaveControlValues(); - SaveCoreValues(); - SaveCpuValues(); - SaveRendererValues(); - SaveAudioValues(); - SaveSystemValues(); - - qt_config->sync(); -} - -void Config::SaveAudioValues() { - qt_config->beginGroup(QStringLiteral("Audio")); - - WriteCategory(Settings::Category::Audio); - WriteCategory(Settings::Category::UiAudio); - - qt_config->endGroup(); -} - -void Config::SaveControlValues() { - qt_config->beginGroup(QStringLiteral("Controls")); - - WriteCategory(Settings::Category::Controls); - - Settings::values.players.SetGlobal(!IsCustomConfig()); - for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { - SavePlayerValue(p); - } - if (IsCustomConfig()) { - qt_config->endGroup(); - return; - } - SaveDebugValues(); - SaveTouchscreenValues(); - SaveMotionTouchValues(); - SaveHidbusValues(); - - qt_config->endGroup(); -} - -void Config::SaveCoreValues() { - qt_config->beginGroup(QStringLiteral("Core")); - - WriteCategory(Settings::Category::Core); - - qt_config->endGroup(); -} - -void Config::SaveDataStorageValues() { - qt_config->beginGroup(QStringLiteral("Data Storage")); - - WriteSetting(QStringLiteral("nand_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); - WriteSetting(QStringLiteral("sdmc_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); - WriteSetting(QStringLiteral("load_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); - WriteSetting(QStringLiteral("dump_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - WriteSetting(QStringLiteral("tas_directory"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir)), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::TASDir))); - - WriteCategory(Settings::Category::DataStorage); - - qt_config->endGroup(); -} - -void Config::SaveDebuggingValues() { - qt_config->beginGroup(QStringLiteral("Debugging")); - - // Intentionally not using the QT default setting as this is intended to be changed in the ini - qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); - - WriteCategory(Settings::Category::Debugging); - WriteCategory(Settings::Category::DebuggingGraphics); - - qt_config->endGroup(); -} - -void Config::SaveNetworkValues() { - qt_config->beginGroup(QStringLiteral("Services")); - - WriteCategory(Settings::Category::Network); - - qt_config->endGroup(); -} - -void Config::SaveDisabledAddOnValues() { - qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); - - int i = 0; - for (const auto& elem : Settings::values.disabled_addons) { - qt_config->setArrayIndex(i); - WriteSetting(QStringLiteral("title_id"), QVariant::fromValue(elem.first), 0); - qt_config->beginWriteArray(QStringLiteral("disabled")); - for (std::size_t j = 0; j < elem.second.size(); ++j) { - qt_config->setArrayIndex(static_cast(j)); - WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{}); - } - qt_config->endArray(); - ++i; - } - - qt_config->endArray(); -} - -void Config::SaveMiscellaneousValues() { - qt_config->beginGroup(QStringLiteral("Miscellaneous")); - - WriteCategory(Settings::Category::Miscellaneous); - - qt_config->endGroup(); -} - -void Config::SavePathValues() { - qt_config->beginGroup(QStringLiteral("Paths")); - - WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); - WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); - qt_config->beginWriteArray(QStringLiteral("gamedirs")); - for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { - qt_config->setArrayIndex(i); - const auto& game_dir = UISettings::values.game_dirs[i]; - WriteSetting(QStringLiteral("path"), game_dir.path); - WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); - WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); - } - qt_config->endArray(); - WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); - WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); - - qt_config->endGroup(); -} - -void Config::SaveCpuValues() { - qt_config->beginGroup(QStringLiteral("Cpu")); - - WriteCategory(Settings::Category::Cpu); - WriteCategory(Settings::Category::CpuDebug); - WriteCategory(Settings::Category::CpuUnsafe); - - qt_config->endGroup(); -} - -void Config::SaveRendererValues() { - qt_config->beginGroup(QStringLiteral("Renderer")); - - WriteCategory(Settings::Category::Renderer); - WriteCategory(Settings::Category::RendererAdvanced); - WriteCategory(Settings::Category::RendererDebug); - - qt_config->endGroup(); -} - -void Config::SaveScreenshotValues() { - qt_config->beginGroup(QStringLiteral("Screenshots")); - - WriteSetting(QStringLiteral("screenshot_path"), - QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); - WriteCategory(Settings::Category::Screenshots); - - qt_config->endGroup(); -} - -void Config::SaveShortcutValues() { - qt_config->beginGroup(QStringLiteral("Shortcuts")); - - // Lengths of UISettings::values.shortcuts & default_hotkeys are same. - // However, their ordering must also be the same. - for (std::size_t i = 0; i < default_hotkeys.size(); i++) { - const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; - const auto& default_hotkey = default_hotkeys[i].shortcut; - - qt_config->beginGroup(group); - qt_config->beginGroup(name); - WriteSetting(QStringLiteral("KeySeq"), shortcut.keyseq, default_hotkey.keyseq); - WriteSetting(QStringLiteral("Controller_KeySeq"), shortcut.controller_keyseq, - default_hotkey.controller_keyseq); - WriteSetting(QStringLiteral("Context"), shortcut.context, default_hotkey.context); - WriteSetting(QStringLiteral("Repeat"), shortcut.repeat, default_hotkey.repeat); - qt_config->endGroup(); - qt_config->endGroup(); - } - - qt_config->endGroup(); -} - -void Config::SaveSystemValues() { - qt_config->beginGroup(QStringLiteral("System")); - - WriteCategory(Settings::Category::System); - WriteCategory(Settings::Category::SystemAudio); - - qt_config->endGroup(); -} - -void Config::SaveUIValues() { - qt_config->beginGroup(QStringLiteral("UI")); - - WriteCategory(Settings::Category::Ui); - WriteCategory(Settings::Category::UiGeneral); - - WriteSetting(QStringLiteral("theme"), UISettings::values.theme, - QString::fromUtf8(UISettings::themes[static_cast(default_theme)].second)); - - SaveUIGamelistValues(); - SaveUILayoutValues(); - SavePathValues(); - SaveScreenshotValues(); - SaveShortcutValues(); - SaveMultiplayerValues(); - - qt_config->endGroup(); -} - -void Config::SaveUIGamelistValues() { - qt_config->beginGroup(QStringLiteral("UIGameList")); - - WriteCategory(Settings::Category::UiGameList); - - qt_config->beginWriteArray(QStringLiteral("favorites")); - for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { - qt_config->setArrayIndex(i); - WriteSetting(QStringLiteral("program_id"), - QVariant::fromValue(UISettings::values.favorited_ids[i])); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -void Config::SaveUILayoutValues() { - qt_config->beginGroup(QStringLiteral("UILayout")); - - WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry); - WriteSetting(QStringLiteral("state"), UISettings::values.state); - WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry); - WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); - WriteSetting(QStringLiteral("microProfileDialogGeometry"), - UISettings::values.microprofile_geometry); - - WriteCategory(Settings::Category::UiLayout); - - qt_config->endGroup(); -} - -void Config::SaveWebServiceValues() { - qt_config->beginGroup(QStringLiteral("WebService")); - - WriteCategory(Settings::Category::WebService); - - qt_config->endGroup(); -} - -void Config::SaveMultiplayerValues() { - qt_config->beginGroup(QStringLiteral("Multiplayer")); - - WriteCategory(Settings::Category::Multiplayer); - - // Write ban list - qt_config->beginWriteArray(QStringLiteral("username_ban_list")); - for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { - qt_config->setArrayIndex(static_cast(i)); - WriteSetting(QStringLiteral("username"), - QString::fromStdString(UISettings::values.multiplayer_ban_list.first[i])); - } - qt_config->endArray(); - qt_config->beginWriteArray(QStringLiteral("ip_ban_list")); - for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { - qt_config->setArrayIndex(static_cast(i)); - WriteSetting(QStringLiteral("ip"), - QString::fromStdString(UISettings::values.multiplayer_ban_list.second[i])); - } - qt_config->endArray(); - - qt_config->endGroup(); -} - -QVariant Config::ReadSetting(const QString& name) const { - return qt_config->value(name); -} - -QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const { - QVariant result; - if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { - result = default_value; - } else { - result = qt_config->value(name, default_value); - } - return result; -} - -void Config::WriteSetting(const QString& name, const QVariant& value) { - qt_config->setValue(name, value); -} - -void Config::WriteSetting(const QString& name, const QVariant& value, - const QVariant& default_value) { - qt_config->setValue(name + QStringLiteral("/default"), value == default_value); - qt_config->setValue(name, value); -} - -void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, - bool use_global) { - if (!global) { - qt_config->setValue(name + QStringLiteral("/use_global"), use_global); - } - if (global || !use_global) { - qt_config->setValue(name + QStringLiteral("/default"), value == default_value); - qt_config->setValue(name, value); - } -} - -void Config::Reload() { - ReadValues(); - // To apply default value changes - SaveValues(); -} - -void Config::Save() { - SaveValues(); -} - -void Config::ReadControlPlayerValue(std::size_t player_index) { - qt_config->beginGroup(QStringLiteral("Controls")); - ReadPlayerValue(player_index); - qt_config->endGroup(); -} - -void Config::SaveControlPlayerValue(std::size_t player_index) { - qt_config->beginGroup(QStringLiteral("Controls")); - SavePlayerValue(player_index); - qt_config->endGroup(); -} - -void Config::ClearControlPlayerValues() { - qt_config->beginGroup(QStringLiteral("Controls")); - // If key is an empty string, all keys in the current group() are removed. - qt_config->remove(QString{}); - qt_config->endGroup(); -} - -const std::string& Config::GetConfigFilePath() const { - return qt_config_loc; -} - -static auto FindRelevantList(Settings::Category category) { - auto& map = Settings::values.linkage.by_category; - if (map.contains(category)) { - return Settings::values.linkage.by_category[category]; - } - return UISettings::values.linkage.by_category[category]; -} - -void Config::ReadCategory(Settings::Category category) { - const auto& settings = FindRelevantList(category); - std::for_each(settings.begin(), settings.end(), - [&](const auto& setting) { ReadSettingGeneric(setting); }); -} - -void Config::WriteCategory(Settings::Category category) { - const auto& settings = FindRelevantList(category); - std::for_each(settings.begin(), settings.end(), - [&](const auto& setting) { WriteSettingGeneric(setting); }); -} - -void Config::ReadSettingGeneric(Settings::BasicSetting* const setting) { - if (!setting->Save() || (!setting->Switchable() && !global)) { - return; - } - const QString name = QString::fromStdString(setting->GetLabel()); - const auto default_value = - QVariant::fromValue(QString::fromStdString(setting->DefaultToString())); - - bool use_global = true; - if (setting->Switchable() && !global) { - use_global = qt_config->value(name + QStringLiteral("/use_global"), true).value(); - setting->SetGlobal(use_global); - } - - if (global || !use_global) { - const bool is_default = - qt_config->value(name + QStringLiteral("/default"), true).value(); - if (!is_default) { - setting->LoadString( - qt_config->value(name, default_value).value().toStdString()); - } else { - // Empty string resets the Setting to default - setting->LoadString(""); - } - } -} - -void Config::WriteSettingGeneric(Settings::BasicSetting* const setting) const { - if (!setting->Save()) { - return; - } - const QVariant value = QVariant::fromValue(QString::fromStdString(setting->ToString())); - const QVariant default_value = - QVariant::fromValue(QString::fromStdString(setting->DefaultToString())); - const QString label = QString::fromStdString(setting->GetLabel()); - if (setting->Switchable()) { - if (!global) { - qt_config->setValue(label + QStringLiteral("/use_global"), setting->UsingGlobal()); - } - if (global || !setting->UsingGlobal()) { - qt_config->setValue(label + QStringLiteral("/default"), value == default_value); - qt_config->setValue(label, value); - } - } else if (global) { - qt_config->setValue(label + QStringLiteral("/default"), value == default_value); - qt_config->setValue(label, value); - } -} diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h deleted file mode 100644 index 1589ba057..000000000 --- a/src/yuzu/configuration/config.h +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include -#include -#include "common/settings.h" -#include "common/settings_enums.h" -#include "yuzu/uisettings.h" - -class QSettings; - -namespace Core { -class System; -} - -class Config { -public: - enum class ConfigType { - GlobalConfig, - PerGameConfig, - InputProfile, - }; - - explicit Config(const std::string& config_name = "qt-config", - ConfigType config_type = ConfigType::GlobalConfig); - ~Config(); - - void Reload(); - void Save(); - - void ReadControlPlayerValue(std::size_t player_index); - void SaveControlPlayerValue(std::size_t player_index); - void ClearControlPlayerValues(); - - const std::string& GetConfigFilePath() const; - - static const std::array default_buttons; - static const std::array default_motions; - static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; - static const std::array default_stick_mod; - static const std::array default_ringcon_analogs; - static const std::array - default_mouse_buttons; - static const std::array default_keyboard_keys; - static const std::array default_keyboard_mods; - static const std::array default_hotkeys; - - static const std::map anti_aliasing_texts_map; - static const std::map scaling_filter_texts_map; - static const std::map use_docked_mode_texts_map; - static const std::map gpu_accuracy_texts_map; - static const std::map renderer_backend_texts_map; - static const std::map shader_backend_texts_map; - - static constexpr UISettings::Theme default_theme{ -#ifdef _WIN32 - UISettings::Theme::DarkColorful -#else - UISettings::Theme::DefaultColorful -#endif - }; - -private: - void Initialize(const std::string& config_name); - bool IsCustomConfig(); - - void ReadValues(); - void ReadPlayerValue(std::size_t player_index); - void ReadDebugValues(); - void ReadKeyboardValues(); - void ReadMouseValues(); - void ReadTouchscreenValues(); - void ReadMotionTouchValues(); - void ReadHidbusValues(); - void ReadIrCameraValues(); - - // Read functions bases off the respective config section names. - void ReadAudioValues(); - void ReadControlValues(); - void ReadCoreValues(); - void ReadDataStorageValues(); - void ReadDebuggingValues(); - void ReadServiceValues(); - void ReadDisabledAddOnValues(); - void ReadMiscellaneousValues(); - void ReadPathValues(); - void ReadCpuValues(); - void ReadRendererValues(); - void ReadScreenshotValues(); - void ReadShortcutValues(); - void ReadSystemValues(); - void ReadUIValues(); - void ReadUIGamelistValues(); - void ReadUILayoutValues(); - void ReadWebServiceValues(); - void ReadMultiplayerValues(); - void ReadNetworkValues(); - - void SaveValues(); - void SavePlayerValue(std::size_t player_index); - void SaveDebugValues(); - void SaveMouseValues(); - void SaveTouchscreenValues(); - void SaveMotionTouchValues(); - void SaveHidbusValues(); - void SaveIrCameraValues(); - - // Save functions based off the respective config section names. - void SaveAudioValues(); - void SaveControlValues(); - void SaveCoreValues(); - void SaveDataStorageValues(); - void SaveDebuggingValues(); - void SaveNetworkValues(); - void SaveDisabledAddOnValues(); - void SaveMiscellaneousValues(); - void SavePathValues(); - void SaveCpuValues(); - void SaveRendererValues(); - void SaveScreenshotValues(); - void SaveShortcutValues(); - void SaveSystemValues(); - void SaveUIValues(); - void SaveUIGamelistValues(); - void SaveUILayoutValues(); - void SaveWebServiceValues(); - void SaveMultiplayerValues(); - - /** - * Reads a setting from the qt_config. - * - * @param name The setting's identifier - * @param default_value The value to use when the setting is not already present in the config - */ - QVariant ReadSetting(const QString& name) const; - QVariant ReadSetting(const QString& name, const QVariant& default_value) const; - - /** - * Writes a setting to the qt_config. - * - * @param name The setting's idetentifier - * @param value Value of the setting - * @param default_value Default of the setting if not present in qt_config - * @param use_global Specifies if the custom or global config should be in use, for custom - * configs - */ - void WriteSetting(const QString& name, const QVariant& value); - void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); - void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, - bool use_global); - - void ReadCategory(Settings::Category category); - void WriteCategory(Settings::Category category); - void ReadSettingGeneric(Settings::BasicSetting* const setting); - void WriteSettingGeneric(Settings::BasicSetting* const setting) const; - - const ConfigType type; - std::unique_ptr qt_config; - std::string qt_config_loc; - const bool global; -}; - -// These metatype declarations cannot be in common/settings.h because core is devoid of QT -Q_DECLARE_METATYPE(Settings::CpuAccuracy); -Q_DECLARE_METATYPE(Settings::GpuAccuracy); -Q_DECLARE_METATYPE(Settings::FullscreenMode); -Q_DECLARE_METATYPE(Settings::NvdecEmulation); -Q_DECLARE_METATYPE(Settings::ResolutionSetup); -Q_DECLARE_METATYPE(Settings::ScalingFilter); -Q_DECLARE_METATYPE(Settings::AntiAliasing); -Q_DECLARE_METATYPE(Settings::RendererBackend); -Q_DECLARE_METATYPE(Settings::ShaderBackend); -Q_DECLARE_METATYPE(Settings::AstcRecompression); -Q_DECLARE_METATYPE(Settings::AstcDecodeMode); diff --git a/src/yuzu/configuration/configure_camera.cpp b/src/yuzu/configuration/configure_camera.cpp index d95e96696..3368f53f3 100644 --- a/src/yuzu/configuration/configure_camera.cpp +++ b/src/yuzu/configuration/configure_camera.cpp @@ -10,10 +10,10 @@ #include #include +#include "common/settings.h" #include "input_common/drivers/camera.h" #include "input_common/main.h" #include "ui_configure_camera.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_camera.h" ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_) diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 0ad95cc02..aab54a1cc 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -8,7 +8,6 @@ #include "core/core.h" #include "ui_configure.h" #include "vk_device_info.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" #include "yuzu/configuration/configure_debug_tab.h" diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 68e21cd84..76fc33e49 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -9,10 +9,11 @@ #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" +#include "frontend_common/config.h" #include "ui_configure_hotkeys.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_hotkeys.h" #include "yuzu/hotkeys.h" +#include "yuzu/uisettings.h" #include "yuzu/util/sequence_dialog/sequence_dialog.h" constexpr int name_column = 0; @@ -62,18 +63,21 @@ ConfigureHotkeys::~ConfigureHotkeys() = default; void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { for (const auto& group : registry.hotkey_groups) { + QString parent_item_data = QString::fromStdString(group.first); auto* parent_item = - new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first))); + new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(parent_item_data))); parent_item->setEditable(false); - parent_item->setData(group.first); + parent_item->setData(parent_item_data); for (const auto& hotkey : group.second) { - auto* action = - new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first))); + QString hotkey_action_data = QString::fromStdString(hotkey.first); + auto* action = new QStandardItem( + QCoreApplication::translate("Hotkeys", qPrintable(hotkey_action_data))); auto* keyseq = new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); - auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); + auto* controller_keyseq = + new QStandardItem(QString::fromStdString(hotkey.second.controller_keyseq)); action->setEditable(false); - action->setData(hotkey.first); + action->setData(hotkey_action_data); keyseq->setEditable(false); controller_keyseq->setEditable(false); parent_item->appendRow({action, keyseq, controller_keyseq}); @@ -301,13 +305,13 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { const QStandardItem* controller_keyseq = parent->child(key_column_id, controller_column); for (auto& [group, sub_actions] : registry.hotkey_groups) { - if (group != parent->data()) + if (group != parent->data().toString().toStdString()) continue; for (auto& [action_name, hotkey] : sub_actions) { - if (action_name != action->data()) + if (action_name != action->data().toString().toStdString()) continue; hotkey.keyseq = QKeySequence(keyseq->text()); - hotkey.controller_keyseq = controller_keyseq->text(); + hotkey.controller_keyseq = controller_keyseq->text().toStdString(); } } } @@ -319,7 +323,7 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { void ConfigureHotkeys::RestoreDefaults() { for (int r = 0; r < model->rowCount(); ++r) { const QStandardItem* parent = model->item(r, 0); - const int hotkey_size = static_cast(Config::default_hotkeys.size()); + const int hotkey_size = static_cast(UISettings::default_hotkeys.size()); if (hotkey_size != parent->rowCount()) { QMessageBox::warning(this, tr("Invalid hotkey settings"), @@ -330,10 +334,11 @@ void ConfigureHotkeys::RestoreDefaults() { for (int r2 = 0; r2 < parent->rowCount(); ++r2) { model->item(r, 0) ->child(r2, hotkey_column) - ->setText(Config::default_hotkeys[r2].shortcut.keyseq); + ->setText(QString::fromStdString(UISettings::default_hotkeys[r2].shortcut.keyseq)); model->item(r, 0) ->child(r2, controller_column) - ->setText(Config::default_hotkeys[r2].shortcut.controller_keyseq); + ->setText(QString::fromStdString( + UISettings::default_hotkeys[r2].shortcut.controller_keyseq)); } } } @@ -379,7 +384,7 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) { void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { const QString& default_key_sequence = - Config::default_hotkeys[index.row()].shortcut.controller_keyseq; + QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.controller_keyseq); const auto [key_sequence_used, used_action] = IsUsedControllerKey(default_key_sequence); if (key_sequence_used && default_key_sequence != model->data(index).toString()) { @@ -393,7 +398,8 @@ void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { const QKeySequence& default_key_sequence = QKeySequence::fromString( - Config::default_hotkeys[index.row()].shortcut.keyseq, QKeySequence::NativeText); + QString::fromStdString(UISettings::default_hotkeys[index.row()].shortcut.keyseq), + QKeySequence::NativeText); const auto [key_sequence_used, used_action] = IsUsedKey(default_key_sequence); if (key_sequence_used && default_key_sequence != QKeySequence(model->data(index).toString())) { diff --git a/src/yuzu/configuration/configure_input_per_game.cpp b/src/yuzu/configuration/configure_input_per_game.cpp index 78e65d468..8d9f65a05 100644 --- a/src/yuzu/configuration/configure_input_per_game.cpp +++ b/src/yuzu/configuration/configure_input_per_game.cpp @@ -5,12 +5,12 @@ #include "core/core.h" #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" +#include "frontend_common/config.h" #include "ui_configure_input_per_game.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_per_game.h" #include "yuzu/configuration/input_profiles.h" -ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, Config* config_, +ConfigureInputPerGame::ConfigureInputPerGame(Core::System& system_, QtConfig* config_, QWidget* parent) : QWidget(parent), ui(std::make_unique()), profiles(std::make_unique()), system{system_}, config{config_} { @@ -110,6 +110,6 @@ void ConfigureInputPerGame::SaveConfiguration() { // Clear all controls from the config in case the user reverted back to globals config->ClearControlPlayerValues(); for (size_t index = 0; index < Settings::values.players.GetValue().size(); ++index) { - config->SaveControlPlayerValue(index); + config->SaveQtControlPlayerValues(index); } } diff --git a/src/yuzu/configuration/configure_input_per_game.h b/src/yuzu/configuration/configure_input_per_game.h index 660faf574..4420e856c 100644 --- a/src/yuzu/configuration/configure_input_per_game.h +++ b/src/yuzu/configuration/configure_input_per_game.h @@ -9,6 +9,7 @@ #include "ui_configure_input_per_game.h" #include "yuzu/configuration/input_profiles.h" +#include "yuzu/configuration/qt_config.h" class QComboBox; @@ -22,7 +23,7 @@ class ConfigureInputPerGame : public QWidget { Q_OBJECT public: - explicit ConfigureInputPerGame(Core::System& system_, Config* config_, + explicit ConfigureInputPerGame(Core::System& system_, QtConfig* config_, QWidget* parent = nullptr); /// Load and Save configurations to settings file. @@ -41,5 +42,5 @@ private: std::array profile_comboboxes; Core::System& system; - Config* config; + QtConfig* config; }; diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 9259e2a5d..0f7b3714e 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -12,15 +12,16 @@ #include #include "common/assert.h" #include "common/param_package.h" +#include "configuration/qt_config.h" #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" #include "core/hid/hid_types.h" +#include "frontend_common/config.h" #include "input_common/drivers/keyboard.h" #include "input_common/drivers/mouse.h" #include "input_common/main.h" #include "ui_configure_input_player.h" #include "yuzu/bootmanager.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input_player.h" #include "yuzu/configuration/configure_input_player_widget.h" #include "yuzu/configuration/configure_mouse_panning.h" @@ -1397,25 +1398,25 @@ void ConfigureInputPlayer::UpdateMappingWithDefaults() { for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) { emulated_controller->SetButtonParam( button_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( - Config::default_buttons[button_id])}); + QtConfig::default_buttons[button_id])}); } for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) { Common::ParamPackage analog_param{}; for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) { Common::ParamPackage params{InputCommon::GenerateKeyboardParam( - Config::default_analogs[analog_id][sub_button_id])}; + QtConfig::default_analogs[analog_id][sub_button_id])}; SetAnalogParam(params, analog_param, analog_sub_buttons[sub_button_id]); } analog_param.Set("modifier", InputCommon::GenerateKeyboardParam( - Config::default_stick_mod[analog_id])); + QtConfig::default_stick_mod[analog_id])); emulated_controller->SetStickParam(analog_id, analog_param); } for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) { emulated_controller->SetMotionParam( motion_id, Common::ParamPackage{InputCommon::GenerateKeyboardParam( - Config::default_motions[motion_id])}); + QtConfig::default_motions[motion_id])}); } // If mouse is selected we want to override with mappings from the driver diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index b91d6ad4a..b274a3321 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -25,8 +25,8 @@ #include "core/file_sys/patch_manager.h" #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" +#include "frontend_common/config.h" #include "ui_configure_per_game.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_audio.h" #include "yuzu/configuration/configure_cpu.h" @@ -50,8 +50,7 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st const auto file_path = std::filesystem::path(Common::FS::ToU8String(file_name)); const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); - game_config = std::make_unique(config_file_name, Config::ConfigType::PerGameConfig); - + game_config = std::make_unique(config_file_name, Config::ConfigType::PerGameConfig); addons_tab = std::make_unique(system_, this); audio_tab = std::make_unique(system_, tab_group, *builder, this); cpu_tab = std::make_unique(system_, tab_group, *builder, this); @@ -108,7 +107,7 @@ void ConfigurePerGame::ApplyConfiguration() { system.ApplySettings(); Settings::LogSettings(); - game_config->Save(); + game_config->SaveAllValues(); } void ConfigurePerGame::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h index cc2513001..c8ee46c04 100644 --- a/src/yuzu/configuration/configure_per_game.h +++ b/src/yuzu/configuration/configure_per_game.h @@ -12,9 +12,10 @@ #include "configuration/shared_widget.h" #include "core/file_sys/vfs_types.h" +#include "frontend_common/config.h" #include "vk_device_info.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" +#include "yuzu/configuration/qt_config.h" #include "yuzu/configuration/shared_translation.h" namespace Core { @@ -72,7 +73,7 @@ private: QGraphicsScene* scene; - std::unique_ptr game_config; + std::unique_ptr game_config; Core::System& system; std::unique_ptr builder; diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index 674a75a62..140a7fe5d 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp @@ -19,7 +19,6 @@ #include "core/file_sys/xts_archive.h" #include "core/loader/loader.h" #include "ui_configure_per_game_addons.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_input.h" #include "yuzu/configuration/configure_per_game_addons.h" #include "yuzu/uisettings.h" diff --git a/src/yuzu/configuration/configure_ringcon.cpp b/src/yuzu/configuration/configure_ringcon.cpp index f83705544..9572ff43c 100644 --- a/src/yuzu/configuration/configure_ringcon.cpp +++ b/src/yuzu/configuration/configure_ringcon.cpp @@ -8,6 +8,7 @@ #include #include +#include "configuration/qt_config.h" #include "core/hid/emulated_controller.h" #include "core/hid/hid_core.h" #include "input_common/drivers/keyboard.h" @@ -15,7 +16,6 @@ #include "input_common/main.h" #include "ui_configure_ringcon.h" #include "yuzu/bootmanager.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_ringcon.h" const std::array @@ -270,7 +270,7 @@ void ConfigureRingController::LoadConfiguration() { void ConfigureRingController::RestoreDefaults() { const std::string default_ring_string = InputCommon::GenerateAnalogParamFromKeys( - 0, 0, Config::default_ringcon_analogs[0], Config::default_ringcon_analogs[1], 0, 0.05f); + 0, 0, QtConfig::default_ringcon_analogs[0], QtConfig::default_ringcon_analogs[1], 0, 0.05f); emulated_controller->SetRingParam(Common::ParamPackage(default_ring_string)); UpdateUI(); } diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 0c8e5c8b4..7cbf43775 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -16,7 +16,6 @@ #include "core/core.h" #include "core/hle/service/time/time_manager.h" #include "ui_configure_system.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_system.h" #include "yuzu/configuration/shared_widget.h" diff --git a/src/yuzu/configuration/configure_touchscreen_advanced.cpp b/src/yuzu/configuration/configure_touchscreen_advanced.cpp index 5a03e48df..94df6d9d3 100644 --- a/src/yuzu/configuration/configure_touchscreen_advanced.cpp +++ b/src/yuzu/configuration/configure_touchscreen_advanced.cpp @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include "common/settings.h" #include "ui_configure_touchscreen_advanced.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_touchscreen_advanced.h" ConfigureTouchscreenAdvanced::ConfigureTouchscreenAdvanced(QWidget* parent) diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 82f3b6e78..dd43f0a0e 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -164,7 +164,7 @@ ConfigureUi::~ConfigureUi() = default; void ConfigureUi::ApplyConfiguration() { UISettings::values.theme = - ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); + ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString().toStdString(); UISettings::values.show_add_ons = ui->show_add_ons->isChecked(); UISettings::values.show_compat = ui->show_compat->isChecked(); UISettings::values.show_size = ui->show_size->isChecked(); @@ -191,9 +191,10 @@ void ConfigureUi::RequestGameListUpdate() { } void ConfigureUi::SetConfiguration() { - ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); + ui->theme_combobox->setCurrentIndex( + ui->theme_combobox->findData(QString::fromStdString(UISettings::values.theme))); ui->language_combobox->setCurrentIndex( - ui->language_combobox->findData(UISettings::values.language)); + ui->language_combobox->findData(QString::fromStdString(UISettings::values.language))); ui->show_add_ons->setChecked(UISettings::values.show_add_ons.GetValue()); ui->show_compat->setChecked(UISettings::values.show_compat.GetValue()); ui->show_size->setChecked(UISettings::values.show_size.GetValue()); diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index 41ef4250a..716efbccd 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp @@ -5,7 +5,7 @@ #include "common/fs/fs.h" #include "common/fs/path_util.h" -#include "yuzu/configuration/config.h" +#include "frontend_common/config.h" #include "yuzu/configuration/input_profiles.h" namespace FS = Common::FS; @@ -44,7 +44,7 @@ InputProfiles::InputProfiles() { if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { map_profiles.insert_or_assign( name_without_ext, - std::make_unique(name_without_ext, Config::ConfigType::InputProfile)); + std::make_unique(name_without_ext, Config::ConfigType::InputProfile)); } return true; @@ -85,7 +85,7 @@ bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t p } map_profiles.insert_or_assign( - profile_name, std::make_unique(profile_name, Config::ConfigType::InputProfile)); + profile_name, std::make_unique(profile_name, Config::ConfigType::InputProfile)); return SaveProfile(profile_name, player_index); } @@ -113,7 +113,7 @@ bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t pla return false; } - map_profiles[profile_name]->ReadControlPlayerValue(player_index); + map_profiles[profile_name]->ReadQtControlPlayerValues(player_index); return true; } @@ -122,7 +122,7 @@ bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t pla return false; } - map_profiles[profile_name]->SaveControlPlayerValue(player_index); + map_profiles[profile_name]->SaveQtControlPlayerValues(player_index); return true; } diff --git a/src/yuzu/configuration/input_profiles.h b/src/yuzu/configuration/input_profiles.h index 2bf3e4250..023ec74a6 100644 --- a/src/yuzu/configuration/input_profiles.h +++ b/src/yuzu/configuration/input_profiles.h @@ -6,6 +6,8 @@ #include #include +#include "configuration/qt_config.h" + namespace Core { class System; } @@ -30,5 +32,5 @@ public: private: bool ProfileExistsInMap(const std::string& profile_name) const; - std::unordered_map> map_profiles; + std::unordered_map> map_profiles; }; diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp new file mode 100644 index 000000000..82402ec70 --- /dev/null +++ b/src/yuzu/configuration/qt_config.cpp @@ -0,0 +1,548 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "input_common/main.h" +#include "qt_config.h" +#include "uisettings.h" + +const std::array QtConfig::default_buttons = { + Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F, + Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, + Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, + Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, + Qt::Key_Q, Qt::Key_E, +}; + +const std::array QtConfig::default_motions = { + Qt::Key_7, + Qt::Key_8, +}; + +const std::array, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{ + { + Qt::Key_W, + Qt::Key_S, + Qt::Key_A, + Qt::Key_D, + }, + { + Qt::Key_I, + Qt::Key_K, + Qt::Key_J, + Qt::Key_L, + }, +}}; + +const std::array QtConfig::default_stick_mod = { + Qt::Key_Shift, + 0, +}; + +const std::array QtConfig::default_ringcon_analogs{{ + Qt::Key_A, + Qt::Key_D, +}}; + +QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type) + : Config(config_type) { + Initialize(config_name); + if (config_type != ConfigType::InputProfile) { + ReadQtValues(); + SaveQtValues(); + } +} + +QtConfig::~QtConfig() { + if (global) { + QtConfig::SaveAllValues(); + } +} + +void QtConfig::ReloadAllValues() { + Reload(); + ReadQtValues(); + SaveQtValues(); +} + +void QtConfig::SaveAllValues() { + Save(); + SaveQtValues(); +} + +void QtConfig::ReadQtValues() { + if (global) { + ReadUIValues(); + } + ReadQtControlValues(); +} + +void QtConfig::ReadQtPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix.append("player_").append(ToString(player_index)).append("_"); + } + + auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + ReadStringSetting(std::string(player_prefix).append("profile_name")); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; + + player_buttons = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); + if (player_buttons.empty()) { + player_buttons = default_param; + } + } + + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& player_analogs = player.analogs[i]; + + player_analogs = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); + if (player_analogs.empty()) { + player_analogs = default_param; + } + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); + if (player_motions.empty()) { + player_motions = default_param; + } + } +} + +void QtConfig::ReadHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + auto& ringcon_analogs = Settings::values.ringcon_analogs; + + ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param); + if (ringcon_analogs.empty()) { + ringcon_analogs = default_param; + } +} + +void QtConfig::ReadDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; + + debug_pad_buttons = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param); + if (debug_pad_buttons.empty()) { + debug_pad_buttons = default_param; + } + } + + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; + + debug_pad_analogs = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param); + if (debug_pad_analogs.empty()) { + debug_pad_analogs = default_param; + } + } +} + +void QtConfig::ReadQtControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadQtPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + ReadDebugControlValues(); + ReadHidbusValues(); + + EndGroup(); +} + +void QtConfig::ReadPathValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); + + UISettings::values.roms_path = ReadStringSetting(std::string("romsPath")); + UISettings::values.symbols_path = ReadStringSetting(std::string("symbolsPath")); + UISettings::values.game_dir_deprecated = + ReadStringSetting(std::string("gameListRootDir"), std::string(".")); + UISettings::values.game_dir_deprecated_deepscan = + ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false)); + + const int gamedirs_size = BeginArray(std::string("gamedirs")); + for (int i = 0; i < gamedirs_size; ++i) { + SetArrayIndex(i); + UISettings::GameDir game_dir; + game_dir.path = ReadStringSetting(std::string("path")); + game_dir.deep_scan = + ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false)); + game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true)); + UISettings::values.game_dirs.append(game_dir); + } + EndArray(); + + // Create NAND and SD card directories if empty, these are not removable through the UI, + // also carries over old game list settings if present + if (UISettings::values.game_dirs.empty()) { + UISettings::GameDir game_dir; + game_dir.path = std::string("SDMC"); + game_dir.expanded = true; + UISettings::values.game_dirs.append(game_dir); + game_dir.path = std::string("UserNAND"); + UISettings::values.game_dirs.append(game_dir); + game_dir.path = std::string("SysNAND"); + UISettings::values.game_dirs.append(game_dir); + if (UISettings::values.game_dir_deprecated != std::string(".")) { + game_dir.path = UISettings::values.game_dir_deprecated; + game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; + UISettings::values.game_dirs.append(game_dir); + } + } + UISettings::values.recent_files = + QString::fromStdString(ReadStringSetting(std::string("recentFiles"))) + .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive); + UISettings::values.language = ReadStringSetting(std::string("language"), std::string("")); + + EndGroup(); +} + +void QtConfig::ReadShortcutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); + + for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) { + BeginGroup(group); + BeginGroup(name); + + // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 + // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open + // a file dialog in windowed mode + UISettings::values.shortcuts.push_back( + {name, + group, + {ReadStringSetting(std::string("KeySeq"), shortcut.keyseq), + ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq), + shortcut.context, + ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}}); + + EndGroup(); // name + EndGroup(); // group + } + + EndGroup(); +} + +void QtConfig::ReadUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); + + UISettings::values.theme = ReadStringSetting( + std::string("theme"), + std::string(UISettings::themes[static_cast(UISettings::default_theme)].second)); + + ReadUIGamelistValues(); + ReadUILayoutValues(); + ReadPathValues(); + ReadScreenshotValues(); + ReadShortcutValues(); + ReadMultiplayerValues(); + + ReadCategory(Settings::Category::Ui); + ReadCategory(Settings::Category::UiGeneral); + + EndGroup(); +} + +void QtConfig::ReadUIGamelistValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); + + ReadCategory(Settings::Category::UiGameList); + + const int favorites_size = BeginArray("favorites"); + for (int i = 0; i < favorites_size; i++) { + SetArrayIndex(i); + UISettings::values.favorited_ids.append(ReadIntegerSetting(std::string("program_id"))); + } + EndArray(); + + EndGroup(); +} + +void QtConfig::ReadUILayoutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); + + ReadCategory(Settings::Category::UiLayout); + + EndGroup(); +} + +void QtConfig::ReadMultiplayerValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer)); + + ReadCategory(Settings::Category::Multiplayer); + + // Read ban list back + int size = BeginArray(std::string("username_ban_list")); + UISettings::values.multiplayer_ban_list.first.resize(size); + for (int i = 0; i < size; ++i) { + SetArrayIndex(i); + UISettings::values.multiplayer_ban_list.first[i] = + ReadStringSetting(std::string("username"), std::string("")); + } + EndArray(); + + size = BeginArray(std::string("ip_ban_list")); + UISettings::values.multiplayer_ban_list.second.resize(size); + for (int i = 0; i < size; ++i) { + UISettings::values.multiplayer_ban_list.second[i] = + ReadStringSetting("username", std::string("")); + } + EndArray(); + + EndGroup(); +} + +void QtConfig::SaveQtValues() { + if (global) { + SaveUIValues(); + } + SaveQtControlValues(); + + WriteToIni(); +} + +void QtConfig::SaveQtPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix = std::string("player_").append(ToString(player_index)).append("_"); + } + + const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig() && player.profile_name.empty()) { + // No custom profile selected + return; + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), + player.buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), + player.analogs[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), + player.motions[i], std::make_optional(default_param)); + } +} + +void QtConfig::SaveDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), + Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), + Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); + } +} + +void QtConfig::SaveHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, + std::make_optional(default_param)); +} + +void QtConfig::SaveQtControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SaveQtPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + SaveDebugControlValues(); + SaveHidbusValues(); + + EndGroup(); +} + +void QtConfig::SavePathValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); + + WriteSetting(std::string("romsPath"), UISettings::values.roms_path); + WriteSetting(std::string("symbolsPath"), UISettings::values.symbols_path); + BeginArray(std::string("gamedirs")); + for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { + SetArrayIndex(i); + const auto& game_dir = UISettings::values.game_dirs[i]; + WriteSetting(std::string("path"), game_dir.path); + WriteSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); + WriteSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true)); + } + EndArray(); + + WriteSetting(std::string("recentFiles"), + UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString()); + WriteSetting(std::string("language"), UISettings::values.language); + + EndGroup(); +} + +void QtConfig::SaveShortcutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); + + // Lengths of UISettings::values.shortcuts & default_hotkeys are same. + // However, their ordering must also be the same. + for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) { + const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; + const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut; + + BeginGroup(group); + BeginGroup(name); + + WriteSetting(std::string("KeySeq"), shortcut.keyseq, + std::make_optional(default_hotkey.keyseq)); + WriteSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq, + std::make_optional(default_hotkey.controller_keyseq)); + WriteSetting(std::string("Context"), shortcut.context, + std::make_optional(default_hotkey.context)); + WriteSetting(std::string("Repeat"), shortcut.repeat, + std::make_optional(default_hotkey.repeat)); + + EndGroup(); // name + EndGroup(); // group + } + + EndGroup(); +} + +void QtConfig::SaveUIValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); + + WriteCategory(Settings::Category::Ui); + WriteCategory(Settings::Category::UiGeneral); + + WriteSetting(std::string("theme"), UISettings::values.theme, + std::make_optional(std::string( + UISettings::themes[static_cast(UISettings::default_theme)].second))); + + SaveUIGamelistValues(); + SaveUILayoutValues(); + SavePathValues(); + SaveScreenshotValues(); + SaveShortcutValues(); + SaveMultiplayerValues(); + + EndGroup(); +} + +void QtConfig::SaveUIGamelistValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); + + WriteCategory(Settings::Category::UiGameList); + + BeginArray(std::string("favorites")); + for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { + SetArrayIndex(i); + WriteSetting(std::string("program_id"), UISettings::values.favorited_ids[i]); + } + EndArray(); // favorites + + EndGroup(); +} + +void QtConfig::SaveUILayoutValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout)); + + WriteCategory(Settings::Category::UiLayout); + + EndGroup(); +} + +void QtConfig::SaveMultiplayerValues() { + BeginGroup(std::string("Multiplayer")); + + WriteCategory(Settings::Category::Multiplayer); + + // Write ban list + BeginArray(std::string("username_ban_list")); + for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { + SetArrayIndex(static_cast(i)); + WriteSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]); + } + EndArray(); // username_ban_list + + BeginArray(std::string("ip_ban_list")); + for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { + SetArrayIndex(static_cast(i)); + WriteSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]); + } + EndArray(); // ip_ban_list + + EndGroup(); +} + +std::vector& QtConfig::FindRelevantList(Settings::Category category) { + auto& map = Settings::values.linkage.by_category; + if (map.contains(category)) { + return Settings::values.linkage.by_category[category]; + } + return UISettings::values.linkage.by_category[category]; +} + +void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + ReadPlayerValues(player_index); + ReadQtPlayerValues(player_index); + + EndGroup(); +} + +void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + SavePlayerValues(player_index); + SaveQtPlayerValues(player_index); + + EndGroup(); + + WriteToIni(); +} diff --git a/src/yuzu/configuration/qt_config.h b/src/yuzu/configuration/qt_config.h new file mode 100644 index 000000000..dc2dceb4d --- /dev/null +++ b/src/yuzu/configuration/qt_config.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "frontend_common/config.h" + +class QtConfig final : public Config { +public: + explicit QtConfig(const std::string& config_name = "qt-config", + ConfigType config_type = ConfigType::GlobalConfig); + ~QtConfig() override; + + void ReloadAllValues() override; + void SaveAllValues() override; + + void ReadQtControlPlayerValues(std::size_t player_index); + void SaveQtControlPlayerValues(std::size_t player_index); + +protected: + void ReadQtValues(); + void ReadQtPlayerValues(std::size_t player_index); + void ReadQtControlValues(); + void ReadHidbusValues() override; + void ReadDebugControlValues() override; + void ReadPathValues() override; + void ReadShortcutValues() override; + void ReadUIValues() override; + void ReadUIGamelistValues() override; + void ReadUILayoutValues() override; + void ReadMultiplayerValues() override; + + void SaveQtValues(); + void SaveQtPlayerValues(std::size_t player_index); + void SaveQtControlValues(); + void SaveHidbusValues() override; + void SaveDebugControlValues() override; + void SavePathValues() override; + void SaveShortcutValues() override; + void SaveUIValues() override; + void SaveUIGamelistValues() override; + void SaveUILayoutValues() override; + void SaveMultiplayerValues() override; + + std::vector& FindRelevantList(Settings::Category category) override; + +public: + static const std::array default_buttons; + static const std::array default_motions; + static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; + static const std::array default_stick_mod; + static const std::array default_ringcon_analogs; +}; diff --git a/src/yuzu/configuration/shared_translation.h b/src/yuzu/configuration/shared_translation.h index 99a0e808c..d5fc3b8de 100644 --- a/src/yuzu/configuration/shared_translation.h +++ b/src/yuzu/configuration/shared_translation.h @@ -10,6 +10,7 @@ #include #include #include "common/common_types.h" +#include "common/settings.h" class QWidget; @@ -22,4 +23,46 @@ std::unique_ptr InitializeTranslations(QWidget* parent); std::unique_ptr ComboboxEnumeration(QWidget* parent); +static const std::map anti_aliasing_texts_map = { + {Settings::AntiAliasing::None, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "None"))}, + {Settings::AntiAliasing::Fxaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FXAA"))}, + {Settings::AntiAliasing::Smaa, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SMAA"))}, +}; + +static const std::map scaling_filter_texts_map = { + {Settings::ScalingFilter::NearestNeighbor, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Nearest"))}, + {Settings::ScalingFilter::Bilinear, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bilinear"))}, + {Settings::ScalingFilter::Bicubic, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Bicubic"))}, + {Settings::ScalingFilter::Gaussian, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Gaussian"))}, + {Settings::ScalingFilter::ScaleForce, + QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "ScaleForce"))}, + {Settings::ScalingFilter::Fsr, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "FSR"))}, +}; + +static const std::map use_docked_mode_texts_map = { + {Settings::ConsoleMode::Docked, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Docked"))}, + {Settings::ConsoleMode::Handheld, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Handheld"))}, +}; + +static const std::map gpu_accuracy_texts_map = { + {Settings::GpuAccuracy::Normal, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Normal"))}, + {Settings::GpuAccuracy::High, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "High"))}, + {Settings::GpuAccuracy::Extreme, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Extreme"))}, +}; + +static const std::map renderer_backend_texts_map = { + {Settings::RendererBackend::Vulkan, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Vulkan"))}, + {Settings::RendererBackend::OpenGL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "OpenGL"))}, + {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, +}; + +static const std::map shader_backend_texts_map = { + {Settings::ShaderBackend::Glsl, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, + {Settings::ShaderBackend::Glasm, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, + {Settings::ShaderBackend::SpirV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, +}; + } // namespace ConfigurationShared diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 7049c57b6..6d227ef8d 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -36,10 +36,8 @@ constexpr std::array, 10> WaitTreeColors{{ bool IsDarkTheme() { const auto& theme = UISettings::values.theme; - return theme == QStringLiteral("qdarkstyle") || - theme == QStringLiteral("qdarkstyle_midnight_blue") || - theme == QStringLiteral("colorful_dark") || - theme == QStringLiteral("colorful_midnight_blue"); + return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") || + theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue"); } } // namespace diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index f294dc23d..59b317135 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -278,7 +278,7 @@ void GameList::OnUpdateThemedIcons() { case GameListItemType::CustomDir: { const UISettings::GameDir& game_dir = UISettings::values.game_dirs[child->data(GameListDir::GameDirRole).toInt()]; - const QString icon_name = QFileInfo::exists(game_dir.path) + const QString icon_name = QFileInfo::exists(QString::fromStdString(game_dir.path)) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); child->setData( @@ -727,7 +727,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { }); connect(open_directory_location, &QAction::triggered, [this, game_dir_index] { - emit OpenDirectory(UISettings::values.game_dirs[game_dir_index].path); + emit OpenDirectory( + QString::fromStdString(UISettings::values.game_dirs[game_dir_index].path)); }); } @@ -869,7 +870,7 @@ const QStringList GameList::supported_file_extensions = { QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")}; void GameList::RefreshGameDirectory() { - if (!UISettings::values.game_dirs.isEmpty() && current_worker != nullptr) { + if (!UISettings::values.game_dirs.empty() && current_worker != nullptr) { LOG_INFO(Frontend, "Change detected in the games directory. Reloading game list."); PopulateAsync(UISettings::values.game_dirs); } diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index 86a0c41d9..c330b574f 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h @@ -286,13 +286,13 @@ public: setData(QObject::tr("System Titles"), Qt::DisplayRole); break; case GameListItemType::CustomDir: { - const QString icon_name = QFileInfo::exists(game_dir->path) - ? QStringLiteral("folder") - : QStringLiteral("bad_folder"); + const QString path = QString::fromStdString(game_dir->path); + const QString icon_name = + QFileInfo::exists(path) ? QStringLiteral("folder") : QStringLiteral("bad_folder"); setData(QIcon::fromTheme(icon_name).pixmap(icon_size).scaled( icon_size, icon_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), Qt::DecorationRole); - setData(game_dir->path, Qt::DisplayRole); + setData(path, Qt::DisplayRole); break; } default: diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 69be21027..ad3eada94 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -456,26 +456,26 @@ void GameListWorker::run() { break; } - if (game_dir.path == QStringLiteral("SDMC")) { + if (game_dir.path == std::string("SDMC")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir); DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); - } else if (game_dir.path == QStringLiteral("UserNAND")) { + } else if (game_dir.path == std::string("UserNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir); DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); - } else if (game_dir.path == QStringLiteral("SysNAND")) { + } else if (game_dir.path == std::string("SysNAND")) { auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir); DirEntryReady(game_list_dir); AddTitlesToGameList(game_list_dir); } else { - watch_list.append(game_dir.path); + watch_list.append(QString::fromStdString(game_dir.path)); auto* const game_list_dir = new GameListDir(game_dir); DirEntryReady(game_list_dir); - ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), - game_dir.deep_scan, game_list_dir); - ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), - game_dir.deep_scan, game_list_dir); + ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path, game_dir.deep_scan, + game_list_dir); + ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path, game_dir.deep_scan, + game_list_dir); } } diff --git a/src/yuzu/hotkeys.cpp b/src/yuzu/hotkeys.cpp index 6530186c1..eebfbf155 100644 --- a/src/yuzu/hotkeys.cpp +++ b/src/yuzu/hotkeys.cpp @@ -19,7 +19,7 @@ void HotkeyRegistry::SaveHotkeys() { for (const auto& hotkey : group.second) { UISettings::values.shortcuts.push_back( {hotkey.first, group.first, - UISettings::ContextualShortcut({hotkey.second.keyseq.toString(), + UISettings::ContextualShortcut({hotkey.second.keyseq.toString().toStdString(), hotkey.second.controller_keyseq, hotkey.second.context, hotkey.second.repeat})}); } @@ -31,12 +31,12 @@ void HotkeyRegistry::LoadHotkeys() { // beginGroup() for (auto shortcut : UISettings::values.shortcuts) { Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; - if (!shortcut.shortcut.keyseq.isEmpty()) { - hk.keyseq = - QKeySequence::fromString(shortcut.shortcut.keyseq, QKeySequence::NativeText); + if (!shortcut.shortcut.keyseq.empty()) { + hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq), + QKeySequence::NativeText); hk.context = static_cast(shortcut.shortcut.context); } - if (!shortcut.shortcut.controller_keyseq.isEmpty()) { + if (!shortcut.shortcut.controller_keyseq.empty()) { hk.controller_keyseq = shortcut.shortcut.controller_keyseq; } if (hk.shortcut) { @@ -51,7 +51,8 @@ void HotkeyRegistry::LoadHotkeys() { } } -QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action, QWidget* widget) { +QShortcut* HotkeyRegistry::GetHotkey(const std::string& group, const std::string& action, + QWidget* widget) { Hotkey& hk = hotkey_groups[group][action]; if (!hk.shortcut) { @@ -62,7 +63,8 @@ QShortcut* HotkeyRegistry::GetHotkey(const QString& group, const QString& action return hk.shortcut; } -ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, const QString& action, +ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group, + const std::string& action, Core::HID::EmulatedController* controller) { Hotkey& hk = hotkey_groups[group][action]; @@ -74,12 +76,12 @@ ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const QString& group, co return hk.controller_shortcut; } -QKeySequence HotkeyRegistry::GetKeySequence(const QString& group, const QString& action) { +QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group, const std::string& action) { return hotkey_groups[group][action].keyseq; } -Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const QString& group, - const QString& action) { +Qt::ShortcutContext HotkeyRegistry::GetShortcutContext(const std::string& group, + const std::string& action) { return hotkey_groups[group][action].context; } @@ -101,10 +103,10 @@ void ControllerShortcut::SetKey(const ControllerButtonSequence& buttons) { button_sequence = buttons; } -void ControllerShortcut::SetKey(const QString& buttons_shortcut) { +void ControllerShortcut::SetKey(const std::string& buttons_shortcut) { ControllerButtonSequence sequence{}; - name = buttons_shortcut.toStdString(); - std::istringstream command_line(buttons_shortcut.toStdString()); + name = buttons_shortcut; + std::istringstream command_line(buttons_shortcut); std::string line; while (std::getline(command_line, line, '+')) { if (line.empty()) { diff --git a/src/yuzu/hotkeys.h b/src/yuzu/hotkeys.h index 56eee8d82..e11332d2e 100644 --- a/src/yuzu/hotkeys.h +++ b/src/yuzu/hotkeys.h @@ -33,7 +33,7 @@ public: ~ControllerShortcut(); void SetKey(const ControllerButtonSequence& buttons); - void SetKey(const QString& buttons_shortcut); + void SetKey(const std::string& buttons_shortcut); ControllerButtonSequence ButtonSequence() const; @@ -88,8 +88,8 @@ public: * will be the same. Thus, you shouldn't rely on the caller really being the * QShortcut's parent. */ - QShortcut* GetHotkey(const QString& group, const QString& action, QWidget* widget); - ControllerShortcut* GetControllerHotkey(const QString& group, const QString& action, + QShortcut* GetHotkey(const std::string& group, const std::string& action, QWidget* widget); + ControllerShortcut* GetControllerHotkey(const std::string& group, const std::string& action, Core::HID::EmulatedController* controller); /** @@ -98,7 +98,7 @@ public: * @param group General group this hotkey belongs to (e.g. "Main Window", "Debugger"). * @param action Name of the action (e.g. "Start Emulation", "Load Image"). */ - QKeySequence GetKeySequence(const QString& group, const QString& action); + QKeySequence GetKeySequence(const std::string& group, const std::string& action); /** * Returns a Qt::ShortcutContext object who can be connected to other @@ -108,20 +108,20 @@ public: * "Debugger"). * @param action Name of the action (e.g. "Start Emulation", "Load Image"). */ - Qt::ShortcutContext GetShortcutContext(const QString& group, const QString& action); + Qt::ShortcutContext GetShortcutContext(const std::string& group, const std::string& action); private: struct Hotkey { QKeySequence keyseq; - QString controller_keyseq; + std::string controller_keyseq; QShortcut* shortcut = nullptr; ControllerShortcut* controller_shortcut = nullptr; Qt::ShortcutContext context = Qt::WindowShortcut; bool repeat; }; - using HotkeyMap = std::map; - using HotkeyGroupMap = std::map; + using HotkeyMap = std::map; + using HotkeyGroupMap = std::map; HotkeyGroupMap hotkey_groups; }; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f22db233b..defe45198 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -128,6 +128,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/loader/loader.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" +#include "frontend_common/config.h" #include "input_common/drivers/tas_input.h" #include "input_common/drivers/virtual_amiibo.h" #include "input_common/main.h" @@ -140,9 +141,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "yuzu/bootmanager.h" #include "yuzu/compatdb.h" #include "yuzu/compatibility_list.h" -#include "yuzu/configuration/config.h" #include "yuzu/configuration/configure_dialog.h" #include "yuzu/configuration/configure_input_per_game.h" +#include "yuzu/configuration/qt_config.h" #include "yuzu/debugger/console.h" #include "yuzu/debugger/controller.h" #include "yuzu/debugger/profiler.h" @@ -311,7 +312,7 @@ bool GMainWindow::CheckDarkMode() { #endif // __unix__ } -GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan) +GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulkan) : ui{std::make_unique()}, system{std::make_unique()}, input_subsystem{std::make_shared()}, config{std::move(config_)}, vfs{std::make_shared()}, @@ -676,7 +677,7 @@ void GMainWindow::ControllerSelectorReconfigureControllers( // Don't forget to apply settings. system->HIDCore().DisableAllControllerConfiguration(); system->ApplySettings(); - config->Save(); + config->SaveAllValues(); UpdateStatusButtons(); @@ -1129,7 +1130,7 @@ void GMainWindow::InitializeWidgets() { connect(aa_status_button, &QPushButton::customContextMenuRequested, [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& aa_text_pair : Config::anti_aliasing_texts_map) { + for (auto const& aa_text_pair : ConfigurationShared::anti_aliasing_texts_map) { context_menu.addAction(aa_text_pair.second, [this, aa_text_pair] { Settings::values.anti_aliasing.SetValue(aa_text_pair.first); UpdateAAText(); @@ -1153,7 +1154,7 @@ void GMainWindow::InitializeWidgets() { connect(filter_status_button, &QPushButton::customContextMenuRequested, [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& filter_text_pair : Config::scaling_filter_texts_map) { + for (auto const& filter_text_pair : ConfigurationShared::scaling_filter_texts_map) { context_menu.addAction(filter_text_pair.second, [this, filter_text_pair] { Settings::values.scaling_filter.SetValue(filter_text_pair.first); UpdateFilterText(); @@ -1176,7 +1177,7 @@ void GMainWindow::InitializeWidgets() { [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& pair : Config::use_docked_mode_texts_map) { + for (auto const& pair : ConfigurationShared::use_docked_mode_texts_map) { context_menu.addAction(pair.second, [this, &pair] { if (pair.first != Settings::values.use_docked_mode.GetValue()) { OnToggleDockedMode(); @@ -1200,7 +1201,7 @@ void GMainWindow::InitializeWidgets() { [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& gpu_accuracy_pair : Config::gpu_accuracy_texts_map) { + for (auto const& gpu_accuracy_pair : ConfigurationShared::gpu_accuracy_texts_map) { if (gpu_accuracy_pair.first == Settings::GpuAccuracy::Extreme) { continue; } @@ -1229,7 +1230,8 @@ void GMainWindow::InitializeWidgets() { [this](const QPoint& menu_location) { QMenu context_menu; - for (auto const& renderer_backend_pair : Config::renderer_backend_texts_map) { + for (auto const& renderer_backend_pair : + ConfigurationShared::renderer_backend_texts_map) { if (renderer_backend_pair.first == Settings::RendererBackend::Null) { continue; } @@ -1294,16 +1296,17 @@ void GMainWindow::InitializeRecentFileMenuActions() { void GMainWindow::LinkActionShortcut(QAction* action, const QString& action_name, const bool tas_allowed) { - static const QString main_window = QStringLiteral("Main Window"); - action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name)); - action->setShortcutContext(hotkey_registry.GetShortcutContext(main_window, action_name)); + static const auto main_window = std::string("Main Window"); + action->setShortcut(hotkey_registry.GetKeySequence(main_window, action_name.toStdString())); + action->setShortcutContext( + hotkey_registry.GetShortcutContext(main_window, action_name.toStdString())); action->setAutoRepeat(false); this->addAction(action); auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); const auto* controller_hotkey = - hotkey_registry.GetControllerHotkey(main_window, action_name, controller); + hotkey_registry.GetControllerHotkey(main_window, action_name.toStdString(), controller); connect( controller_hotkey, &ControllerShortcut::Activated, this, [action, tas_allowed, this] { @@ -1335,10 +1338,11 @@ void GMainWindow::InitializeHotkeys() { static const QString main_window = QStringLiteral("Main Window"); const auto connect_shortcut = [&](const QString& action_name, const Fn& function) { - const auto* hotkey = hotkey_registry.GetHotkey(main_window, action_name, this); + const auto* hotkey = + hotkey_registry.GetHotkey(main_window.toStdString(), action_name.toStdString(), this); auto* controller = system->HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1); - const auto* controller_hotkey = - hotkey_registry.GetControllerHotkey(main_window, action_name, controller); + const auto* controller_hotkey = hotkey_registry.GetControllerHotkey( + main_window.toStdString(), action_name.toStdString(), controller); connect(hotkey, &QShortcut::activated, this, function); connect(controller_hotkey, &ControllerShortcut::Activated, this, function, Qt::QueuedConnection); @@ -1918,7 +1922,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t // Save configurations UpdateUISettings(); game_list->SaveInterfaceLayout(); - config->Save(); + config->SaveAllValues(); u64 title_id{0}; @@ -1936,7 +1940,7 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t const auto config_file_name = title_id == 0 ? Common::FS::PathToUTF8String(file_path.filename()) : fmt::format("{:016X}", title_id); - Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); + QtConfig per_game_config(config_file_name, Config::ConfigType::PerGameConfig); system->HIDCore().ReloadInputDevices(); system->ApplySettings(); } @@ -3135,7 +3139,7 @@ void GMainWindow::OnGameListAddDirectory() { return; } - UISettings::GameDir game_dir{dir_path, false, true}; + UISettings::GameDir game_dir{dir_path.toStdString(), false, true}; if (!UISettings::values.game_dirs.contains(game_dir)) { UISettings::values.game_dirs.append(game_dir); game_list->PopulateAsync(UISettings::values.game_dirs); @@ -3181,14 +3185,14 @@ void GMainWindow::OnMenuLoadFile() { "%1 is an identifier for the Switch executable file extensions.") .arg(extensions); const QString filename = QFileDialog::getOpenFileName( - this, tr("Load File"), UISettings::values.roms_path, file_filter); + this, tr("Load File"), QString::fromStdString(UISettings::values.roms_path), file_filter); is_load_file_select_active = false; if (filename.isEmpty()) { return; } - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename); } @@ -3221,7 +3225,8 @@ void GMainWindow::OnMenuInstallToNAND() { "Image (*.xci)"); QStringList filenames = QFileDialog::getOpenFileNames( - this, tr("Install Files"), UISettings::values.roms_path, file_filter); + this, tr("Install Files"), QString::fromStdString(UISettings::values.roms_path), + file_filter); if (filenames.isEmpty()) { return; @@ -3239,7 +3244,7 @@ void GMainWindow::OnMenuInstallToNAND() { } // Save folder location of the first selected file - UISettings::values.roms_path = QFileInfo(filenames[0]).path(); + UISettings::values.roms_path = QFileInfo(filenames[0]).path().toStdString(); int remaining = filenames.size(); @@ -3584,7 +3589,7 @@ void GMainWindow::OnExit() { void GMainWindow::OnSaveConfig() { system->ApplySettings(); - config->Save(); + config->SaveAllValues(); } void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { @@ -3840,7 +3845,7 @@ void GMainWindow::OnConfigure() { Settings::values.disabled_addons.clear(); - config = std::make_unique(); + config = std::make_unique(); UISettings::values.reset_to_defaults = false; UISettings::values.game_dirs = std::move(old_game_dirs); @@ -3875,7 +3880,7 @@ void GMainWindow::OnConfigure() { UISettings::values.configuration_applied = false; - config->Save(); + config->SaveAllValues(); if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { render_window->installEventFilter(render_window); @@ -4091,7 +4096,7 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file UISettings::values.configuration_applied = false; if (!is_powered_on) { - config->Save(); + config->SaveAllValues(); } } @@ -4324,7 +4329,7 @@ void GMainWindow::OnAlbum() { system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::PhotoViewer); const auto filename = QString::fromStdString(album_nca->GetFullPath()); - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename, AlbumId); } @@ -4348,7 +4353,7 @@ void GMainWindow::OnCabinet(Service::NFP::CabinetMode mode) { system->GetAppletManager().SetCabinetMode(mode); const auto filename = QString::fromStdString(cabinet_nca->GetFullPath()); - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename, CabinetId); } @@ -4371,7 +4376,7 @@ void GMainWindow::OnMiiEdit() { system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::MiiEdit); const auto filename = QString::fromStdString((mii_applet_nca->GetFullPath())); - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename, MiiEditId); } @@ -4396,7 +4401,7 @@ void GMainWindow::OnOpenControllerMenu() { system->GetAppletManager().SetCurrentAppletId(Service::AM::Applets::AppletId::Controller); const auto filename = QString::fromStdString((controller_applet_nca->GetFullPath())); - UISettings::values.roms_path = QFileInfo(filename).path(); + UISettings::values.roms_path = QFileInfo(filename).path().toStdString(); BootGame(filename, ControllerAppletId); } @@ -4590,7 +4595,8 @@ void GMainWindow::UpdateStatusBar() { void GMainWindow::UpdateGPUAccuracyButton() { const auto gpu_accuracy = Settings::values.gpu_accuracy.GetValue(); - const auto gpu_accuracy_text = Config::gpu_accuracy_texts_map.find(gpu_accuracy)->second; + const auto gpu_accuracy_text = + ConfigurationShared::gpu_accuracy_texts_map.find(gpu_accuracy)->second; gpu_accuracy_button->setText(gpu_accuracy_text.toUpper()); gpu_accuracy_button->setChecked(gpu_accuracy != Settings::GpuAccuracy::Normal); } @@ -4599,31 +4605,32 @@ void GMainWindow::UpdateDockedButton() { const auto console_mode = Settings::values.use_docked_mode.GetValue(); dock_status_button->setChecked(Settings::IsDockedMode()); dock_status_button->setText( - Config::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); + ConfigurationShared::use_docked_mode_texts_map.find(console_mode)->second.toUpper()); } void GMainWindow::UpdateAPIText() { const auto api = Settings::values.renderer_backend.GetValue(); - const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; + const auto renderer_status_text = + ConfigurationShared::renderer_backend_texts_map.find(api)->second; renderer_status_button->setText( api == Settings::RendererBackend::OpenGL - ? tr("%1 %2").arg( - renderer_status_text.toUpper(), - Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) - ->second) + ? tr("%1 %2").arg(renderer_status_text.toUpper(), + ConfigurationShared::shader_backend_texts_map + .find(Settings::values.shader_backend.GetValue()) + ->second) : renderer_status_text.toUpper()); } void GMainWindow::UpdateFilterText() { const auto filter = Settings::values.scaling_filter.GetValue(); - const auto filter_text = Config::scaling_filter_texts_map.find(filter)->second; + const auto filter_text = ConfigurationShared::scaling_filter_texts_map.find(filter)->second; filter_status_button->setText(filter == Settings::ScalingFilter::Fsr ? tr("FSR") : filter_text.toUpper()); } void GMainWindow::UpdateAAText() { const auto aa_mode = Settings::values.anti_aliasing.GetValue(); - const auto aa_text = Config::anti_aliasing_texts_map.find(aa_mode)->second; + const auto aa_text = ConfigurationShared::anti_aliasing_texts_map.find(aa_mode)->second; aa_status_button->setText(aa_mode == Settings::AntiAliasing::None ? QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "NO AA")) : aa_text.toUpper()); @@ -4926,6 +4933,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) { UpdateUISettings(); game_list->SaveInterfaceLayout(); + UISettings::SaveWindowState(); hotkey_registry.SaveHotkeys(); // Unload controllers early @@ -5080,9 +5088,9 @@ static void AdjustLinkColor() { } void GMainWindow::UpdateUITheme() { - const QString default_theme = - QString::fromUtf8(UISettings::themes[static_cast(Config::default_theme)].second); - QString current_theme = UISettings::values.theme; + const QString default_theme = QString::fromUtf8( + UISettings::themes[static_cast(UISettings::default_theme)].second); + QString current_theme = QString::fromStdString(UISettings::values.theme); if (current_theme.isEmpty()) { current_theme = default_theme; @@ -5110,7 +5118,7 @@ void GMainWindow::UpdateUITheme() { QFile f(theme_uri); if (!f.open(QFile::ReadOnly | QFile::Text)) { LOG_ERROR(Frontend, "Unable to open style \"{}\", fallback to the default theme", - UISettings::values.theme.toStdString()); + UISettings::values.theme); current_theme = default_theme; } } @@ -5123,7 +5131,7 @@ void GMainWindow::UpdateUITheme() { setStyleSheet(ts.readAll()); } else { LOG_ERROR(Frontend, "Unable to set style \"{}\", stylesheet file not found", - UISettings::values.theme.toStdString()); + UISettings::values.theme); qApp->setStyleSheet({}); setStyleSheet({}); } @@ -5132,27 +5140,28 @@ void GMainWindow::UpdateUITheme() { void GMainWindow::LoadTranslation() { bool loaded; - if (UISettings::values.language.isEmpty()) { + if (UISettings::values.language.empty()) { // If the selected language is empty, use system locale loaded = translator.load(QLocale(), {}, {}, QStringLiteral(":/languages/")); } else { // Otherwise load from the specified file - loaded = translator.load(UISettings::values.language, QStringLiteral(":/languages/")); + loaded = translator.load(QString::fromStdString(UISettings::values.language), + QStringLiteral(":/languages/")); } if (loaded) { qApp->installTranslator(&translator); } else { - UISettings::values.language = QStringLiteral("en"); + UISettings::values.language = std::string("en"); } } void GMainWindow::OnLanguageChanged(const QString& locale) { - if (UISettings::values.language != QStringLiteral("en")) { + if (UISettings::values.language != std::string("en")) { qApp->removeTranslator(&translator); } - UISettings::values.language = locale; + UISettings::values.language = locale.toStdString(); LoadTranslation(); ui->retranslateUi(this); multiplayer_state->retranslateUi(); @@ -5178,7 +5187,7 @@ void GMainWindow::changeEvent(QEvent* event) { // UpdateUITheme is a decent work around if (event->type() == QEvent::PaletteChange) { const QPalette test_palette(qApp->palette()); - const QString current_theme = UISettings::values.theme; + const QString current_theme = QString::fromStdString(UISettings::values.theme); // Keeping eye on QPalette::Window to avoid looping. QPalette::Text might be useful too static QColor last_window_color; const QColor window_color = test_palette.color(QPalette::Active, QPalette::Window); @@ -5272,7 +5281,8 @@ static void SetHighDPIAttributes() { } int main(int argc, char* argv[]) { - std::unique_ptr config = std::make_unique(); + std::unique_ptr config = std::make_unique(); + UISettings::RestoreWindowState(config); bool has_broken_vulkan = false; bool is_child = false; if (CheckEnvVars(&is_child)) { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 49ee1e1d2..c989c079d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -15,6 +15,7 @@ #include "common/announce_multiplayer_room.h" #include "common/common_types.h" +#include "configuration/qt_config.h" #include "input_common/drivers/tas_input.h" #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" @@ -26,7 +27,7 @@ #include #endif -class Config; +class QtConfig; class ClickableLabel; class EmuThread; class GameList; @@ -185,7 +186,7 @@ class GMainWindow : public QMainWindow { public: void filterBarSetChecked(bool state); void UpdateUITheme(); - explicit GMainWindow(std::unique_ptr config_, bool has_broken_vulkan); + explicit GMainWindow(std::unique_ptr config_, bool has_broken_vulkan); ~GMainWindow() override; bool DropAction(QDropEvent* event); @@ -521,7 +522,7 @@ private: QSlider* volume_slider = nullptr; QTimer status_bar_update_timer; - std::unique_ptr config; + std::unique_ptr config; // Whether emulation is currently running in yuzu. bool emulation_running = false; diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp index 1c833767b..7bb7e95af 100644 --- a/src/yuzu/uisettings.cpp +++ b/src/yuzu/uisettings.cpp @@ -1,6 +1,9 @@ // SPDX-FileCopyrightText: 2016 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include "common/fs/fs.h" +#include "common/fs/path_util.h" #include "yuzu/uisettings.h" #ifndef CANNOT_EXPLICITLY_INSTANTIATE @@ -15,6 +18,8 @@ template class Setting; } // namespace Settings #endif +namespace FS = Common::FS; + namespace UISettings { const Themes themes{{ @@ -28,10 +33,8 @@ const Themes themes{{ bool IsDarkTheme() { const auto& theme = UISettings::values.theme; - return theme == QStringLiteral("qdarkstyle") || - theme == QStringLiteral("qdarkstyle_midnight_blue") || - theme == QStringLiteral("colorful_dark") || - theme == QStringLiteral("colorful_midnight_blue"); + return theme == std::string("qdarkstyle") || theme == std::string("qdarkstyle_midnight_blue") || + theme == std::string("colorful_dark") || theme == std::string("colorful_midnight_blue"); } Values values = {}; @@ -52,4 +55,58 @@ u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) { return height * 16 / 9; } +void SaveWindowState() { + const auto window_state_config_loc = + FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini"); + + void(FS::CreateParentDir(window_state_config_loc)); + QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat); + + config.setValue(QStringLiteral("geometry"), values.geometry); + config.setValue(QStringLiteral("state"), values.state); + config.setValue(QStringLiteral("geometryRenderWindow"), values.renderwindow_geometry); + config.setValue(QStringLiteral("gameListHeaderState"), values.gamelist_header_state); + config.setValue(QStringLiteral("microProfileDialogGeometry"), values.microprofile_geometry); + + config.sync(); +} + +void RestoreWindowState(std::unique_ptr& qtConfig) { + const auto window_state_config_loc = + FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "window_state.ini"); + + // Migrate window state from old location + if (!FS::Exists(window_state_config_loc) && qtConfig->Exists("UI", "UILayout\\geometry")) { + const auto config_loc = + FS::PathToUTF8String(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "qt-config.ini"); + QSettings config(QString::fromStdString(config_loc), QSettings::IniFormat); + + config.beginGroup(QStringLiteral("UI")); + config.beginGroup(QStringLiteral("UILayout")); + values.geometry = config.value(QStringLiteral("geometry")).toByteArray(); + values.state = config.value(QStringLiteral("state")).toByteArray(); + values.renderwindow_geometry = + config.value(QStringLiteral("geometryRenderWindow")).toByteArray(); + values.gamelist_header_state = + config.value(QStringLiteral("gameListHeaderState")).toByteArray(); + values.microprofile_geometry = + config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray(); + config.endGroup(); + config.endGroup(); + return; + } + + void(FS::CreateParentDir(window_state_config_loc)); + const QSettings config(QString::fromStdString(window_state_config_loc), QSettings::IniFormat); + + values.geometry = config.value(QStringLiteral("geometry")).toByteArray(); + values.state = config.value(QStringLiteral("state")).toByteArray(); + values.renderwindow_geometry = + config.value(QStringLiteral("geometryRenderWindow")).toByteArray(); + values.gamelist_header_state = + config.value(QStringLiteral("gameListHeaderState")).toByteArray(); + values.microprofile_geometry = + config.value(QStringLiteral("microProfileDialogGeometry")).toByteArray(); +} + } // namespace UISettings diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 3485a6347..549a39e1b 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -14,6 +14,7 @@ #include "common/common_types.h" #include "common/settings.h" #include "common/settings_enums.h" +#include "configuration/qt_config.h" using Settings::Category; using Settings::ConfirmStop; @@ -37,15 +38,15 @@ namespace UISettings { bool IsDarkTheme(); struct ContextualShortcut { - QString keyseq; - QString controller_keyseq; + std::string keyseq; + std::string controller_keyseq; int context; bool repeat; }; struct Shortcut { - QString name; - QString group; + std::string name; + std::string group; ContextualShortcut shortcut; }; @@ -58,11 +59,19 @@ enum class Theme { MidnightBlueColorful, }; +static constexpr Theme default_theme{ +#ifdef _WIN32 + Theme::DarkColorful +#else + Theme::DefaultColorful +#endif +}; + using Themes = std::array, 6>; extern const Themes themes; struct GameDir { - QString path; + std::string path; bool deep_scan = false; bool expanded = false; bool operator==(const GameDir& rhs) const { @@ -144,15 +153,15 @@ struct Values { Category::Screenshots}; Setting screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots}; - QString roms_path; - QString symbols_path; - QString game_dir_deprecated; + std::string roms_path; + std::string symbols_path; + std::string game_dir_deprecated; bool game_dir_deprecated_deepscan; - QVector game_dirs; + QVector game_dirs; QStringList recent_files; - QString language; + std::string language; - QString theme; + std::string theme; // Shortcut name std::vector shortcuts; @@ -206,6 +215,54 @@ extern Values values; u32 CalculateWidth(u32 height, Settings::AspectRatio ratio); +void SaveWindowState(); +void RestoreWindowState(std::unique_ptr& qtConfig); + +// This shouldn't have anything except static initializers (no functions). So +// QKeySequence(...).toString() is NOT ALLOWED HERE. +// This must be in alphabetical order according to action name as it must have the same order as +// UISetting::values.shortcuts, which is alphabetically ordered. +// clang-format off +const std::array default_hotkeys{{ + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+P"), std::string("Screenshot"), Qt::WidgetWithChildrenShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F8"), std::string("Home+L"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F10"), std::string("Home+X"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F9"), std::string("Home+R"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F4"), std::string("Home+Plus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Esc"), std::string(""), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+Q"), std::string("Home+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F6"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F5"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F"), std::string(""), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+U"), std::string("Home+Y"), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F9"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Renderdoc Capture")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string(""), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+S"), std::string(""), Qt::WindowShortcut, false}}, +}}; +// clang-format on + } // namespace UISettings Q_DECLARE_METATYPE(UISettings::GameDir*); + +// These metatype declarations cannot be in common/settings.h because core is devoid of QT +Q_DECLARE_METATYPE(Settings::CpuAccuracy); +Q_DECLARE_METATYPE(Settings::GpuAccuracy); +Q_DECLARE_METATYPE(Settings::FullscreenMode); +Q_DECLARE_METATYPE(Settings::NvdecEmulation); +Q_DECLARE_METATYPE(Settings::ResolutionSetup); +Q_DECLARE_METATYPE(Settings::ScalingFilter); +Q_DECLARE_METATYPE(Settings::AntiAliasing); +Q_DECLARE_METATYPE(Settings::RendererBackend); +Q_DECLARE_METATYPE(Settings::ShaderBackend); +Q_DECLARE_METATYPE(Settings::AstcRecompression); +Q_DECLARE_METATYPE(Settings::AstcDecodeMode); diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt index 46eddf423..281e0658e 100644 --- a/src/yuzu_cmd/CMakeLists.txt +++ b/src/yuzu_cmd/CMakeLists.txt @@ -13,9 +13,6 @@ function(create_resource file output filename) endfunction() add_executable(yuzu-cmd - config.cpp - config.h - default_ini.h emu_window/emu_window_sdl2.cpp emu_window/emu_window_sdl2.h emu_window/emu_window_sdl2_gl.cpp @@ -25,14 +22,16 @@ add_executable(yuzu-cmd emu_window/emu_window_sdl2_vk.cpp emu_window/emu_window_sdl2_vk.h precompiled_headers.h + sdl_config.cpp + sdl_config.h yuzu.cpp yuzu.rc ) create_target_directory_groups(yuzu-cmd) -target_link_libraries(yuzu-cmd PRIVATE common core input_common) target_link_libraries(yuzu-cmd PRIVATE inih::INIReader glad) +target_link_libraries(yuzu-cmd PRIVATE common core input_common frontend_common) if (MSVC) target_link_libraries(yuzu-cmd PRIVATE getopt) endif() diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp deleted file mode 100644 index 0d25ff400..000000000 --- a/src/yuzu_cmd/config.cpp +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include "common/fs/file.h" -#include "common/fs/fs.h" -#include "common/fs/path_util.h" -#include "common/logging/log.h" -#include "common/settings.h" -#include "core/hle/service/acc/profile_manager.h" -#include "input_common/main.h" -#include "yuzu_cmd/config.h" -#include "yuzu_cmd/default_ini.h" - -namespace FS = Common::FS; - -const std::filesystem::path default_config_path = - FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; - -Config::Config(std::optional config_path) - : sdl2_config_loc{config_path.value_or(default_config_path)}, - sdl2_config{std::make_unique(FS::PathToUTF8String(sdl2_config_loc))} { - Reload(); -} - -Config::~Config() = default; - -bool Config::LoadINI(const std::string& default_contents, bool retry) { - const auto config_loc_str = FS::PathToUTF8String(sdl2_config_loc); - if (sdl2_config->ParseError() < 0) { - if (retry) { - LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", - config_loc_str); - - void(FS::CreateParentDir(sdl2_config_loc)); - void(FS::WriteStringToFile(sdl2_config_loc, FS::FileType::TextFile, default_contents)); - - sdl2_config = std::make_unique(config_loc_str); - - return LoadINI(default_contents, false); - } - LOG_ERROR(Config, "Failed."); - return false; - } - LOG_INFO(Config, "Successfully loaded {}", config_loc_str); - return true; -} - -static const std::array default_buttons = { - SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, - SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, - SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, -}; - -static const std::array default_motions = { - SDL_SCANCODE_7, - SDL_SCANCODE_8, -}; - -static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs{{ - { - SDL_SCANCODE_UP, - SDL_SCANCODE_DOWN, - SDL_SCANCODE_LEFT, - SDL_SCANCODE_RIGHT, - SDL_SCANCODE_D, - }, - { - SDL_SCANCODE_I, - SDL_SCANCODE_K, - SDL_SCANCODE_J, - SDL_SCANCODE_L, - SDL_SCANCODE_D, - }, -}}; - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { - std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); - if (setting_value.empty()) { - setting_value = setting.GetDefault(); - } - setting = std::move(setting_value); -} - -template <> -void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { - setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); -} - -template -void Config::ReadSetting(const std::string& group, Settings::Setting& setting) { - setting = static_cast(sdl2_config->GetInteger(group, setting.GetLabel(), - static_cast(setting.GetDefault()))); -} - -void Config::ReadCategory(Settings::Category category) { - for (const auto setting : Settings::values.linkage.by_category[category]) { - const char* category_name = [&]() { - if (category == Settings::Category::Controls) { - // For compatibility with older configs - return "ControlsGeneral"; - } else { - return Settings::TranslateCategory(category); - } - }(); - std::string setting_value = - sdl2_config->Get(category_name, setting->GetLabel(), setting->DefaultToString()); - setting->LoadString(setting_value); - } -} - -void Config::ReadValues() { - // Controls - ReadCategory(Settings::Category::Controls); - - for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { - auto& player = Settings::values.players.GetValue()[p]; - - const auto group = fmt::format("ControlsP{}", p); - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - player.buttons[i] = - sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param); - if (player.buttons[i].empty()) { - player.buttons[i] = default_param; - } - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); - player.analogs[i] = - sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param); - if (player.analogs[i].empty()) { - player.analogs[i] = default_param; - } - } - - for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { - const std::string default_param = - InputCommon::GenerateKeyboardParam(default_motions[i]); - auto& player_motions = player.motions[i]; - - player_motions = - sdl2_config->Get(group, Settings::NativeMotion::mapping[i], default_param); - if (player_motions.empty()) { - player_motions = default_param; - } - } - - player.connected = sdl2_config->GetBoolean(group, "connected", false); - } - - for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { - std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); - Settings::values.debug_pad_buttons[i] = sdl2_config->Get( - "ControlsGeneral", std::string("debug_pad_") + Settings::NativeButton::mapping[i], - default_param); - if (Settings::values.debug_pad_buttons[i].empty()) - Settings::values.debug_pad_buttons[i] = default_param; - } - - for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { - std::string default_param = InputCommon::GenerateAnalogParamFromKeys( - default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], - default_analogs[i][3], default_analogs[i][4], 0.5f); - Settings::values.debug_pad_analogs[i] = sdl2_config->Get( - "ControlsGeneral", std::string("debug_pad_") + Settings::NativeAnalog::mapping[i], - default_param); - if (Settings::values.debug_pad_analogs[i].empty()) - Settings::values.debug_pad_analogs[i] = default_param; - } - - Settings::values.touchscreen.enabled = - sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true); - Settings::values.touchscreen.rotation_angle = - sdl2_config->GetInteger("ControlsGeneral", "touch_angle", 0); - Settings::values.touchscreen.diameter_x = - sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_x", 15); - Settings::values.touchscreen.diameter_y = - sdl2_config->GetInteger("ControlsGeneral", "touch_diameter_y", 15); - - int num_touch_from_button_maps = - sdl2_config->GetInteger("ControlsGeneral", "touch_from_button_map", 0); - if (num_touch_from_button_maps > 0) { - for (int i = 0; i < num_touch_from_button_maps; ++i) { - Settings::TouchFromButtonMap map; - map.name = sdl2_config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_name"), - "default"); - const int num_touch_maps = sdl2_config->GetInteger( - "ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"), - 0); - map.buttons.reserve(num_touch_maps); - - for (int j = 0; j < num_touch_maps; ++j) { - std::string touch_mapping = - sdl2_config->Get("ControlsGeneral", - std::string("touch_from_button_maps_") + std::to_string(i) + - std::string("_bind_") + std::to_string(j), - ""); - map.buttons.emplace_back(std::move(touch_mapping)); - } - - Settings::values.touch_from_button_maps.emplace_back(std::move(map)); - } - } else { - Settings::values.touch_from_button_maps.emplace_back( - Settings::TouchFromButtonMap{"default", {}}); - num_touch_from_button_maps = 1; - } - Settings::values.touch_from_button_map_index = std::clamp( - Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1); - - ReadCategory(Settings::Category::Audio); - ReadCategory(Settings::Category::Core); - ReadCategory(Settings::Category::Cpu); - ReadCategory(Settings::Category::CpuDebug); - ReadCategory(Settings::Category::CpuUnsafe); - ReadCategory(Settings::Category::Renderer); - ReadCategory(Settings::Category::RendererAdvanced); - ReadCategory(Settings::Category::RendererDebug); - ReadCategory(Settings::Category::System); - ReadCategory(Settings::Category::SystemAudio); - ReadCategory(Settings::Category::DataStorage); - ReadCategory(Settings::Category::Debugging); - ReadCategory(Settings::Category::DebuggingGraphics); - ReadCategory(Settings::Category::Miscellaneous); - ReadCategory(Settings::Category::Network); - ReadCategory(Settings::Category::WebService); - - // Data Storage - FS::SetYuzuPath(FS::YuzuPath::NANDDir, - sdl2_config->Get("Data Storage", "nand_directory", - FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); - FS::SetYuzuPath(FS::YuzuPath::SDMCDir, - sdl2_config->Get("Data Storage", "sdmc_directory", - FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); - FS::SetYuzuPath(FS::YuzuPath::LoadDir, - sdl2_config->Get("Data Storage", "load_directory", - FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); - FS::SetYuzuPath(FS::YuzuPath::DumpDir, - sdl2_config->Get("Data Storage", "dump_directory", - FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); - - // Debugging - Settings::values.record_frame_times = - sdl2_config->GetBoolean("Debugging", "record_frame_times", false); - - const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); - std::stringstream ss(title_list); - std::string line; - while (std::getline(ss, line, '|')) { - const auto title_id = std::strtoul(line.c_str(), nullptr, 16); - const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); - - std::stringstream inner_ss(disabled_list); - std::string inner_line; - std::vector out; - while (std::getline(inner_ss, inner_line, '|')) { - out.push_back(inner_line); - } - - Settings::values.disabled_addons.insert_or_assign(title_id, out); - } -} - -void Config::Reload() { - LoadINI(DefaultINI::sdl2_config_file); - ReadValues(); -} diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h deleted file mode 100644 index 512591a39..000000000 --- a/src/yuzu_cmd/config.h +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include - -#include "common/settings.h" - -class INIReader; - -class Config { - std::filesystem::path sdl2_config_loc; - std::unique_ptr sdl2_config; - - bool LoadINI(const std::string& default_contents = "", bool retry = true); - void ReadValues(); - -public: - explicit Config(std::optional config_path); - ~Config(); - - void Reload(); - -private: - /** - * Applies a value read from the sdl2_config to a Setting. - * - * @param group The name of the INI group - * @param setting The yuzu setting to modify - */ - template - void ReadSetting(const std::string& group, Settings::Setting& setting); - void ReadCategory(Settings::Category category); -}; diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h deleted file mode 100644 index 119e22183..000000000 --- a/src/yuzu_cmd/default_ini.h +++ /dev/null @@ -1,553 +0,0 @@ -// SPDX-FileCopyrightText: 2014 Citra Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -namespace DefaultINI { - -const char* sdl2_config_file = - R"( -[ControlsP0] -# The input devices and parameters for each Switch native input -# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... -# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..." -# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values - -# Indicates if this player should be connected at boot -# 0 (default): Disabled, 1: Enabled -connected= - -# for button input, the following devices are available: -# - "keyboard" (default) for keyboard input. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "button"(optional): the index of the button to bind -# - "hat"(optional): the index of the hat to bind as direction buttons -# - "axis"(optional): the index of the axis to bind -# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right" -# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is -# triggered if the axis value crosses -# - "direction"(only used for axis): "+" means the button is triggered when the axis value -# is greater than the threshold; "-" means the button is triggered when the axis value -# is smaller than the threshold -button_a= -button_b= -button_x= -button_y= -button_lstick= -button_rstick= -button_l= -button_r= -button_zl= -button_zr= -button_plus= -button_minus= -button_dleft= -button_dup= -button_dright= -button_ddown= -button_lstick_left= -button_lstick_up= -button_lstick_right= -button_lstick_down= -button_sl= -button_sr= -button_home= -button_screenshot= - -# for analog input, the following devices are available: -# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters: -# - "up", "down", "left", "right": sub-devices for each direction. -# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00" -# - "modifier": sub-devices as a modifier. -# - "modifier_scale": a float number representing the applied modifier scale to the analog input. -# Must be in range of 0.0-1.0. Defaults to 0.5 -# - "sdl" for joystick input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "axis_x": the index of the axis to bind as x-axis (default to 0) -# - "axis_y": the index of the axis to bind as y-axis (default to 1) -lstick= -rstick= - -# for motion input, the following devices are available: -# - "keyboard" (default) for emulating random motion input from buttons. Required parameters: -# - "code": the code of the key to bind -# - "sdl" for motion input using SDL. Required parameters: -# - "guid": SDL identification GUID of the joystick -# - "port": the index of the joystick to bind -# - "motion": the index of the motion sensor to bind -# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters: -# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001" -# - "port": the port of the cemu hook server -# - "pad": the index of the joystick -# - "motion": the index of the motion sensor of the joystick to bind -motionleft= -motionright= - -[ControlsGeneral] -# To use the debug_pad, prepend `debug_pad_` before each button setting above. -# i.e. debug_pad_button_a= - -# Enable debug pad inputs to the guest -# 0 (default): Disabled, 1: Enabled -debug_pad_enabled = - -# Enable sdl raw input. Allows to configure up to 8 xinput controllers. -# 0 (default): Disabled, 1: Enabled -enable_raw_input = - -# Enable yuzu joycon driver instead of SDL drive. -# 0: Disabled, 1 (default): Enabled -enable_joycon_driver = - -# Emulates an analog input from buttons. Allowing to dial any angle. -# 0 (default): Disabled, 1: Enabled -emulate_analog_keyboard = - -# Whether to enable or disable vibration -# 0: Disabled, 1 (default): Enabled -vibration_enabled= - -# Whether to enable or disable accurate vibrations -# 0 (default): Disabled, 1: Enabled -enable_accurate_vibrations= - -# Enables controller motion inputs -# 0: Disabled, 1 (default): Enabled -motion_enabled = - -# Defines the udp device's touch screen coordinate system for cemuhookudp devices -# - "min_x", "min_y", "max_x", "max_y" -touch_device= - -# for mapping buttons to touch inputs. -#touch_from_button_map=1 -#touch_from_button_maps_0_name=default -#touch_from_button_maps_0_count=2 -#touch_from_button_maps_0_bind_0=foo -#touch_from_button_maps_0_bind_1=bar -# etc. - -# List of Cemuhook UDP servers, delimited by ','. -# Default: 127.0.0.1:26760 -# Example: 127.0.0.1:26760,123.4.5.67:26761 -udp_input_servers = - -# Enable controlling an axis via a mouse input. -# 0 (default): Off, 1: On -mouse_panning = - -# Set mouse panning horizontal sensitivity. -# Default: 50.0 -mouse_panning_x_sensitivity = - -# Set mouse panning vertical sensitivity. -# Default: 50.0 -mouse_panning_y_sensitivity = - -# Set mouse panning deadzone horizontal counterweight. -# Default: 0.0 -mouse_panning_deadzone_x_counterweight = - -# Set mouse panning deadzone vertical counterweight. -# Default: 0.0 -mouse_panning_deadzone_y_counterweight = - -# Set mouse panning stick decay strength. -# Default: 22.0 -mouse_panning_decay_strength = - -# Set mouse panning stick minimum decay. -# Default: 5.0 -mouse_panning_minimum_decay = - -# Emulate an analog control stick from keyboard inputs. -# 0 (default): Disabled, 1: Enabled -emulate_analog_keyboard = - -# Enable mouse inputs to the guest -# 0 (default): Disabled, 1: Enabled -mouse_enabled = - -# Enable keyboard inputs to the guest -# 0 (default): Disabled, 1: Enabled -keyboard_enabled = - -)" - R"( -[Core] -# Whether to use multi-core for CPU emulation -# 0: Disabled, 1 (default): Enabled -use_multi_core = - -# Enable unsafe extended guest system memory layout (8GB DRAM) -# 0 (default): Disabled, 1: Enabled -use_unsafe_extended_memory_layout = - -[Cpu] -# Adjusts various optimizations. -# Auto-select mode enables choice unsafe optimizations. -# Accurate enables only safe optimizations. -# Unsafe allows any unsafe optimizations. -# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations -cpu_accuracy = - -# Allow disabling safe optimizations. -# 0 (default): Disabled, 1: Enabled -cpu_debug_mode = - -# Enable inline page tables optimization (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_page_tables = - -# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps) -# 0: Disabled, 1 (default): Enabled -cpuopt_block_linking = - -# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns) -# 0: Disabled, 1 (default): Enabled -cpuopt_return_stack_buffer = - -# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture) -# 0: Disabled, 1 (default): Enabled -cpuopt_fast_dispatcher = - -# Enable context elimination CPU Optimization (reduce host memory use for guest context) -# 0: Disabled, 1 (default): Enabled -cpuopt_context_elimination = - -# Enable constant propagation CPU optimization (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_const_prop = - -# Enable miscellaneous CPU optimizations (basic IR optimization) -# 0: Disabled, 1 (default): Enabled -cpuopt_misc_ir = - -# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access) -# 0: Disabled, 1 (default): Enabled -cpuopt_reduce_misalign_checks = - -# Enable Host MMU Emulation (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem = - -# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_fastmem_exclusives = - -# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_recompile_exclusives = - -# Enable optimization to ignore invalid memory accesses (faster guest memory access) -# 0: Disabled, 1 (default): Enabled -cpuopt_ignore_memory_aborts = - -# Enable unfuse FMA (improve performance on CPUs without FMA) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_unfuse_fma = - -# Enable faster FRSQRTE and FRECPE -# Only enabled if cpu_accuracy is set to Unsafe. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_reduce_fp_error = - -# Enable faster ASIMD instructions (32 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_standard_fpcr = - -# Enable inaccurate NaN handling -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_inaccurate_nan = - -# Disable address space checks (64 bits only) -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_fastmem_check = - -# Enable faster exclusive instructions -# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select. -# 0: Disabled, 1 (default): Enabled -cpuopt_unsafe_ignore_global_monitor = - -)" - R"( -[Renderer] -# Which backend API to use. -# 0: OpenGL, 1 (default): Vulkan -backend = - -# Whether to enable asynchronous presentation (Vulkan only) -# 0 (default): Off, 1: On -async_presentation = - -# Enable graphics API debugging mode. -# 0 (default): Disabled, 1: Enabled -debug = - -# Enable shader feedback. -# 0 (default): Disabled, 1: Enabled -renderer_shader_feedback = - -# Enable Nsight Aftermath crash dumps -# 0 (default): Disabled, 1: Enabled -nsight_aftermath = - -# Disable shader loop safety checks, executing the shader without loop logic changes -# 0 (default): Disabled, 1: Enabled -disable_shader_loop_safety_checks = - -# Which Vulkan physical device to use (defaults to 0) -vulkan_device = - -# 0: 0.5x (360p/540p) [EXPERIMENTAL] -# 1: 0.75x (540p/810p) [EXPERIMENTAL] -# 2 (default): 1x (720p/1080p) -# 3: 1.5x (1080p/1620p) [EXPERIMENTAL] -# 4: 2x (1440p/2160p) -# 5: 3x (2160p/3240p) -# 6: 4x (2880p/4320p) -# 7: 5x (3600p/5400p) -# 8: 6x (4320p/6480p) -# 9: 7x (5040p/7560p) -# 10: 8x (5760/8640p) -resolution_setup = - -# Pixel filter to use when up- or down-sampling rendered frames. -# 0: Nearest Neighbor -# 1 (default): Bilinear -# 2: Bicubic -# 3: Gaussian -# 4: ScaleForce -# 5: AMD FidelityFX™️ Super Resolution -scaling_filter = - -# Anti-Aliasing (AA) -# 0 (default): None, 1: FXAA, 2: SMAA -anti_aliasing = - -# Whether to use fullscreen or borderless window mode -# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen -fullscreen_mode = - -# Aspect ratio -# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Force 16:10, 4: Stretch to Window -aspect_ratio = - -# Anisotropic filtering -# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x -max_anisotropy = - -# Whether to enable VSync or not. -# OpenGL: Values other than 0 enable VSync -# Vulkan: FIFO is selected if the requested mode is not supported by the driver. -# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate. -# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down. -# Mailbox can have lower latency than FIFO and does not tear but may drop frames. -# Immediate (no synchronization) just presents whatever is available and can exhibit tearing. -# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed -use_vsync = - -# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is -# not available and GLASM is selected, GLSL will be used. -# 0: GLSL, 1 (default): GLASM, 2: SPIR-V -shader_backend = - -# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory. -# 0: Off, 1 (default): On -use_reactive_flushing = - -# Whether to allow asynchronous shader building. -# 0 (default): Off, 1: On -use_asynchronous_shaders = - -# NVDEC emulation. -# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding -nvdec_emulation = - -# Accelerate ASTC texture decoding. -# 0: Off, 1 (default): On -accelerate_astc = - -# Decode ASTC textures asynchronously. -# 0 (default): Off, 1: On -async_astc = - -# Recompress ASTC textures to a different format. -# 0 (default): Uncompressed, 1: BC1 (Low quality), 2: BC3: (Medium quality) -async_astc = - -# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value -# 0: Off, 1: On (default) -use_speed_limit = - -# Limits the speed of the game to run no faster than this value as a percentage of target speed -# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) -speed_limit = - -# Whether to use disk based shader cache -# 0: Off, 1 (default): On -use_disk_shader_cache = - -# Which gpu accuracy level to use -# 0: Normal, 1 (default): High, 2: Extreme (Very slow) -gpu_accuracy = - -# Whether to use asynchronous GPU emulation -# 0 : Off (slow), 1 (default): On (fast) -use_asynchronous_gpu_emulation = - -# Inform the guest that GPU operations completed more quickly than they did. -# 0: Off, 1 (default): On -use_fast_gpu_time = - -# Whether to use garbage collection or not for GPU caches. -# 0 (default): Off, 1: On -use_caches_gc = - -# The clear color for the renderer. What shows up on the sides of the bottom screen. -# Must be in range of 0-255. Defaults to 0 for all. -bg_red = -bg_blue = -bg_green = - -)" - R"( -[Audio] -# Which audio output engine to use. -# auto (default): Auto-select -# cubeb: Cubeb audio engine (if available) -# sdl2: SDL2 audio engine (if available) -# null: No audio output -output_engine = - -# Which audio device to use. -# auto (default): Auto-select -output_device = - -# Output volume. -# 100 (default): 100%, 0; mute -volume = - -[Data Storage] -# Whether to create a virtual SD card. -# 1 (default): Yes, 0: No -use_virtual_sd = - -# Whether or not to enable gamecard emulation -# 1: Yes, 0 (default): No -gamecard_inserted = - -# Whether or not the gamecard should be emulated as the current game -# If 'gamecard_inserted' is 0 this setting is irrelevant -# 1: Yes, 0 (default): No -gamecard_current_game = - -# Path to an XCI file to use as the gamecard -# If 'gamecard_inserted' is 0 this setting is irrelevant -# If 'gamecard_current_game' is 1 this setting is irrelevant -gamecard_path = - -[System] -# Whether the system is docked -# 1 (default): Yes, 0: No -use_docked_mode = - -# Sets the seed for the RNG generator built into the switch -# rng_seed will be ignored and randomly generated if rng_seed_enabled is false -rng_seed_enabled = -rng_seed = - -# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service -# This will auto-increment, with the time set being the time the game is started -# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used -custom_rtc_enabled = -custom_rtc = - -# Sets the systems language index -# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, -# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French, -# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese -language_index = - -# The system region that yuzu will use during emulation -# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan -region_index = - -# The system time zone that yuzu will use during emulation -# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone -time_zone_index = - -# Sets the sound output mode. -# 0: Mono, 1 (default): Stereo, 2: Surround -sound_index = - -[Miscellaneous] -# A filter which removes logs below a certain logging level. -# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical -log_filter = *:Trace - -# Use developer keys -# 0 (default): Disabled, 1: Enabled -use_dev_keys = - -[Debugging] -# Record frame time data, can be found in the log directory. Boolean value -record_frame_times = -# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them -dump_exefs=false -# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them -dump_nso=false -# Determines whether or not yuzu will save the filesystem access log. -enable_fs_access_log=false -# Enables verbose reporting services -reporting_services = -# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode -# false: Retail/Normal Mode (default), true: Kiosk Mode -quest_flag = -# Determines whether debug asserts should be enabled, which will throw an exception on asserts. -# false: Disabled (default), true: Enabled -use_debug_asserts = -# Determines whether unimplemented HLE service calls should be automatically stubbed. -# false: Disabled (default), true: Enabled -use_auto_stub = -# Enables/Disables the macro JIT compiler -disable_macro_jit=false -# Determines whether to enable the GDB stub and wait for the debugger to attach before running. -# false: Disabled (default), true: Enabled -use_gdbstub=false -# The port to use for the GDB server, if it is enabled. -gdbstub_port=6543 - -[WebService] -# Whether or not to enable telemetry -# 0: No, 1 (default): Yes -enable_telemetry = -# URL for Web API -web_api_url = https://api.yuzu-emu.org -# Username and token for yuzu Web Service -# See https://profile.yuzu-emu.org/ for more info -yuzu_username = -yuzu_token = - -[Network] -# Name of the network interface device to use with yuzu LAN play. -# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo' -# e.g. On Windows: 'Ethernet', 'Wi-Fi' -network_interface = - -[AddOns] -# Used to disable add-ons -# List of title IDs of games that will have add-ons disabled (separated by '|'): -title_ids = -# For each title ID, have a key/value pair called `disabled_` equal to the names of the add-ons to disable (sep. by '|') -# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey -)"; -} // namespace DefaultINI diff --git a/src/yuzu_cmd/sdl_config.cpp b/src/yuzu_cmd/sdl_config.cpp new file mode 100644 index 000000000..39fd8050c --- /dev/null +++ b/src/yuzu_cmd/sdl_config.cpp @@ -0,0 +1,257 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// SDL will break our main function in yuzu-cmd if we don't define this before adding SDL.h +#define SDL_MAIN_HANDLED +#include + +#include "input_common/main.h" +#include "sdl_config.h" + +const std::array SdlConfig::default_buttons = { + SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, + SDL_SCANCODE_G, SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, + SDL_SCANCODE_M, SDL_SCANCODE_N, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, +}; + +const std::array SdlConfig::default_motions = { + SDL_SCANCODE_7, + SDL_SCANCODE_8, +}; + +const std::array, Settings::NativeAnalog::NumAnalogs> SdlConfig::default_analogs{ + { + { + SDL_SCANCODE_UP, + SDL_SCANCODE_DOWN, + SDL_SCANCODE_LEFT, + SDL_SCANCODE_RIGHT, + }, + { + SDL_SCANCODE_I, + SDL_SCANCODE_K, + SDL_SCANCODE_J, + SDL_SCANCODE_L, + }, + }}; + +const std::array SdlConfig::default_stick_mod = { + SDL_SCANCODE_D, + 0, +}; + +const std::array SdlConfig::default_ringcon_analogs{{ + 0, + 0, +}}; + +SdlConfig::SdlConfig(const std::optional config_path) { + Initialize(config_path); + ReadSdlValues(); + SaveSdlValues(); +} + +SdlConfig::~SdlConfig() { + if (global) { + SdlConfig::SaveAllValues(); + } +} + +void SdlConfig::ReloadAllValues() { + Reload(); + ReadSdlValues(); + SaveSdlValues(); +} + +void SdlConfig::SaveAllValues() { + Save(); + SaveSdlValues(); +} + +void SdlConfig::ReadSdlValues() { + ReadSdlControlValues(); +} + +void SdlConfig::ReadSdlControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + ReadSdlPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + ReadDebugControlValues(); + ReadHidbusValues(); + + EndGroup(); +} + +void SdlConfig::ReadSdlPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix.append("player_").append(ToString(player_index)).append("_"); + } + + auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig()) { + const auto profile_name = + ReadStringSetting(std::string(player_prefix).append("profile_name")); + if (profile_name.empty()) { + // Use the global input config + player = Settings::values.players.GetValue(true)[player_index]; + return; + } + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& player_buttons = player.buttons[i]; + + player_buttons = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); + if (player_buttons.empty()) { + player_buttons = default_param; + } + } + + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& player_analogs = player.analogs[i]; + + player_analogs = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); + if (player_analogs.empty()) { + player_analogs = default_param; + } + } + + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + auto& player_motions = player.motions[i]; + + player_motions = ReadStringSetting( + std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); + if (player_motions.empty()) { + player_motions = default_param; + } + } +} + +void SdlConfig::ReadDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; + debug_pad_buttons = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param); + if (debug_pad_buttons.empty()) { + debug_pad_buttons = default_param; + } + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; + debug_pad_analogs = ReadStringSetting( + std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param); + if (debug_pad_analogs.empty()) { + debug_pad_analogs = default_param; + } + } +} + +void SdlConfig::ReadHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + auto& ringcon_analogs = Settings::values.ringcon_analogs; + + ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param); + if (ringcon_analogs.empty()) { + ringcon_analogs = default_param; + } +} + +void SdlConfig::SaveSdlValues() { + SaveSdlControlValues(); + + WriteToIni(); +} + +void SdlConfig::SaveSdlControlValues() { + BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); + + Settings::values.players.SetGlobal(!IsCustomConfig()); + for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { + SaveSdlPlayerValues(p); + } + if (IsCustomConfig()) { + EndGroup(); + return; + } + SaveDebugControlValues(); + SaveHidbusValues(); + + EndGroup(); +} + +void SdlConfig::SaveSdlPlayerValues(const std::size_t player_index) { + std::string player_prefix; + if (type != ConfigType::InputProfile) { + player_prefix = std::string("player_").append(ToString(player_index)).append("_"); + } + + const auto& player = Settings::values.players.GetValue()[player_index]; + if (IsCustomConfig() && player.profile_name.empty()) { + // No custom profile selected + return; + } + + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), + player.buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), + player.analogs[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); + WriteSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), + player.motions[i], std::make_optional(default_param)); + } +} + +void SdlConfig::SaveDebugControlValues() { + for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { + const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); + WriteSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), + Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); + } + for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], + default_analogs[i][3], default_stick_mod[i], 0.5f); + WriteSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), + Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); + } +} + +void SdlConfig::SaveHidbusValues() { + const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( + 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); + WriteSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, + std::make_optional(default_param)); +} + +std::vector& SdlConfig::FindRelevantList(Settings::Category category) { + return Settings::values.linkage.by_category[category]; +} diff --git a/src/yuzu_cmd/sdl_config.h b/src/yuzu_cmd/sdl_config.h new file mode 100644 index 000000000..1fd1c692d --- /dev/null +++ b/src/yuzu_cmd/sdl_config.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "frontend_common/config.h" + +class SdlConfig final : public Config { +public: + explicit SdlConfig(std::optional config_path); + ~SdlConfig() override; + + void ReloadAllValues() override; + void SaveAllValues() override; + +protected: + void ReadSdlValues(); + void ReadSdlPlayerValues(std::size_t player_index); + void ReadSdlControlValues(); + void ReadHidbusValues() override; + void ReadDebugControlValues() override; + void ReadPathValues() override {} + void ReadShortcutValues() override {} + void ReadUIValues() override {} + void ReadUIGamelistValues() override {} + void ReadUILayoutValues() override {} + void ReadMultiplayerValues() override {} + + void SaveSdlValues(); + void SaveSdlPlayerValues(std::size_t player_index); + void SaveSdlControlValues(); + void SaveHidbusValues() override; + void SaveDebugControlValues() override; + void SavePathValues() override {} + void SaveShortcutValues() override {} + void SaveUIValues() override {} + void SaveUIGamelistValues() override {} + void SaveUILayoutValues() override {} + void SaveMultiplayerValues() override {} + + std::vector& FindRelevantList(Settings::Category category) override; + +public: + static const std::array default_buttons; + static const std::array default_motions; + static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; + static const std::array default_stick_mod; + static const std::array default_ringcon_analogs; +}; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 087cfaa26..0416d5951 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -29,10 +29,11 @@ #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "core/telemetry_session.h" +#include "frontend_common/config.h" #include "input_common/main.h" #include "network/network.h" +#include "sdl_config.h" #include "video_core/renderer_base.h" -#include "yuzu_cmd/config.h" #include "yuzu_cmd/emu_window/emu_window_sdl2.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h" #include "yuzu_cmd/emu_window/emu_window_sdl2_null.h" @@ -300,7 +301,7 @@ int main(int argc, char** argv) { } } - Config config{config_path}; + SdlConfig config{config_path}; // apply the log_filter setting // the logger was initialized before and doesn't pick up the filter on its own -- cgit v1.2.3 From c4f6c3b00be9e735444a8067b652f35a70268f4a Mon Sep 17 00:00:00 2001 From: Merry Date: Tue, 21 Nov 2023 22:57:09 +0000 Subject: shared_widget: Explicit capture of 'this' --- src/yuzu/configuration/shared_widget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/yuzu') diff --git a/src/yuzu/configuration/shared_widget.cpp b/src/yuzu/configuration/shared_widget.cpp index ea8d7add4..941683a43 100644 --- a/src/yuzu/configuration/shared_widget.cpp +++ b/src/yuzu/configuration/shared_widget.cpp @@ -194,7 +194,7 @@ QWidget* Widget::CreateRadioGroup(std::function& serializer, return group; } - const auto get_selected = [=]() -> int { + const auto get_selected = [this]() -> int { for (const auto& [id, button] : radio_buttons) { if (button->isChecked()) { return id; @@ -203,7 +203,7 @@ QWidget* Widget::CreateRadioGroup(std::function& serializer, return -1; }; - const auto set_index = [=](u32 value) { + const auto set_index = [this](u32 value) { for (const auto& [id, button] : radio_buttons) { button->setChecked(id == value); } -- cgit v1.2.3 From b088a448cdc6ab66ea3a95fe40b158ab6030c2c2 Mon Sep 17 00:00:00 2001 From: Merry Date: Tue, 21 Nov 2023 22:57:47 +0000 Subject: game_list_worker: Explicit caputure of 'this' --- src/yuzu/game_list_worker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/yuzu') diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 69be21027..307eac02d 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -479,6 +479,6 @@ void GameListWorker::run() { } } - RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); }); + RecordEvent([this](GameList* game_list) { game_list->DonePopulating(watch_list); }); processing_completed.Set(); } -- cgit v1.2.3 From d8f380961e2c7165ba296c246e002d16598942d4 Mon Sep 17 00:00:00 2001 From: t895 Date: Tue, 21 Nov 2023 20:46:27 -0500 Subject: frontend_common: Add option to read unsigned integers --- src/frontend_common/config.cpp | 29 ++++++++++++++++++++++++++++- src/frontend_common/config.h | 4 ++++ src/yuzu/configuration/qt_config.cpp | 3 ++- 3 files changed, 34 insertions(+), 2 deletions(-) (limited to 'src/yuzu') diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp index 40a44ae12..a68a9cb4b 100644 --- a/src/frontend_common/config.cpp +++ b/src/frontend_common/config.cpp @@ -284,7 +284,7 @@ void Config::ReadDisabledAddOnValues() { const int size = BeginArray(std::string("")); for (int i = 0; i < size; ++i) { SetArrayIndex(i); - const auto title_id = ReadIntegerSetting(std::string("title_id"), 0); + const auto title_id = ReadUnsignedIntegerSetting(std::string("title_id"), 0); std::vector out; const int d_size = BeginArray("disabled"); for (int j = 0; j < d_size; ++j) { @@ -664,6 +664,33 @@ s64 Config::ReadIntegerSetting(const std::string& key, const std::optional return result; } +u64 Config::ReadUnsignedIntegerSetting(const std::string& key, + const std::optional default_value) { + std::string full_key = GetFullKey(key, false); + if (!default_value.has_value()) { + try { + return std::stoull( + std::string(config->GetValue(GetSection().c_str(), full_key.c_str(), "0"))); + } catch (...) { + return 0; + } + } + + u64 result = 0; + if (config->GetBoolValue(GetSection().c_str(), + std::string(full_key).append("\\default").c_str(), true)) { + result = default_value.value(); + } else { + try { + result = std::stoull(std::string(config->GetValue( + GetSection().c_str(), full_key.c_str(), ToString(default_value.value()).c_str()))); + } catch (...) { + result = default_value.value(); + } + } + return result; +} + double Config::ReadDoubleSetting(const std::string& key, const std::optional default_value) { std::string full_key = GetFullKey(key, false); diff --git a/src/frontend_common/config.h b/src/frontend_common/config.h index f741aa8bb..20a1a8056 100644 --- a/src/frontend_common/config.h +++ b/src/frontend_common/config.h @@ -137,6 +137,8 @@ protected: bool ReadBooleanSetting(const std::string& key, std::optional default_value = std::nullopt); s64 ReadIntegerSetting(const std::string& key, std::optional default_value = std::nullopt); + u64 ReadUnsignedIntegerSetting(const std::string& key, + std::optional default_value = std::nullopt); double ReadDoubleSetting(const std::string& key, std::optional default_value = std::nullopt); std::string ReadStringSetting(const std::string& key, @@ -170,6 +172,8 @@ protected: return value_.has_value() ? std::to_string(*value_) : "none"; } else if constexpr (std::is_same_v) { return value_ ? "true" : "false"; + } else if constexpr (std::is_same_v) { + return std::to_string(static_cast(value_)); } else { return std::to_string(static_cast(value_)); } diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp index 82402ec70..5a8e69aa9 100644 --- a/src/yuzu/configuration/qt_config.cpp +++ b/src/yuzu/configuration/qt_config.cpp @@ -283,7 +283,8 @@ void QtConfig::ReadUIGamelistValues() { const int favorites_size = BeginArray("favorites"); for (int i = 0; i < favorites_size; i++) { SetArrayIndex(i); - UISettings::values.favorited_ids.append(ReadIntegerSetting(std::string("program_id"))); + UISettings::values.favorited_ids.append( + ReadUnsignedIntegerSetting(std::string("program_id"))); } EndArray(); -- cgit v1.2.3 From 2d4e7c826422fdbfa7e0ba837ab65f58801e3251 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Fri, 24 Nov 2023 11:53:31 -0600 Subject: yuzu: Display firmware version --- src/core/hle/service/set/set_sys.cpp | 66 +++++++++++++++++++----------------- src/core/hle/service/set/set_sys.h | 24 +++++++++++++ src/yuzu/main.cpp | 39 +++++++++++++++++++-- src/yuzu/main.h | 2 ++ 4 files changed, 98 insertions(+), 33 deletions(-) (limited to 'src/yuzu') diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index 19c667b42..f5edfdc8b 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -19,19 +19,8 @@ namespace Service::Set { -namespace { -constexpr u64 SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET = 0x05; - -enum class GetFirmwareVersionType { - Version1, - Version2, -}; - -void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, - GetFirmwareVersionType type) { - ASSERT_MSG(ctx.GetWriteBufferSize() == 0x100, - "FirmwareVersion output buffer must be 0x100 bytes in size!"); - +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type) { constexpr u64 FirmwareVersionSystemDataId = 0x0100000000000809; auto& fsc = system.GetFileSystemController(); @@ -52,39 +41,34 @@ void GetFirmwareVersionImpl(Core::System& system, HLERequestContext& ctx, FileSys::SystemArchive::SynthesizeSystemArchive(FirmwareVersionSystemDataId)); } - const auto early_exit_failure = [&ctx](std::string_view desc, Result code) { + const auto early_exit_failure = [](std::string_view desc, Result code) { LOG_ERROR(Service_SET, "General failure while attempting to resolve firmware version ({}).", desc); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(code); + return code; }; const auto ver_file = romfs->GetFile("file"); if (ver_file == nullptr) { - early_exit_failure("The system version archive didn't contain the file 'file'.", - FileSys::ERROR_INVALID_ARGUMENT); - return; + return early_exit_failure("The system version archive didn't contain the file 'file'.", + FileSys::ERROR_INVALID_ARGUMENT); } auto data = ver_file->ReadAllBytes(); - if (data.size() != 0x100) { - early_exit_failure("The system version file 'file' was not the correct size.", - FileSys::ERROR_OUT_OF_BOUNDS); - return; + if (data.size() != sizeof(FirmwareVersionFormat)) { + return early_exit_failure("The system version file 'file' was not the correct size.", + FileSys::ERROR_OUT_OF_BOUNDS); } + std::memcpy(&out_firmware, data.data(), sizeof(FirmwareVersionFormat)); + // If the command is GetFirmwareVersion (as opposed to GetFirmwareVersion2), hardware will // zero out the REVISION_MINOR field. if (type == GetFirmwareVersionType::Version1) { - data[SYSTEM_VERSION_FILE_MINOR_REVISION_OFFSET] = 0; + out_firmware.revision_minor = 0; } - ctx.WriteBuffer(data); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + return ResultSuccess; } -} // Anonymous namespace void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; @@ -98,12 +82,32 @@ void SET_SYS::SetLanguageCode(HLERequestContext& ctx) { void SET_SYS::GetFirmwareVersion(HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); - GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version1); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version1); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void SET_SYS::GetFirmwareVersion2(HLERequestContext& ctx) { LOG_DEBUG(Service_SET, "called"); - GetFirmwareVersionImpl(system, ctx, GetFirmwareVersionType::Version2); + + FirmwareVersionFormat firmware_data{}; + const auto result = + GetFirmwareVersionImpl(firmware_data, system, GetFirmwareVersionType::Version2); + + if (result.IsSuccess()) { + ctx.WriteBuffer(firmware_data); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); } void SET_SYS::GetAccountSettings(HLERequestContext& ctx) { diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 93023c6dd..5f770fd32 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -4,6 +4,7 @@ #pragma once #include "common/uuid.h" +#include "core/hle/result.h" #include "core/hle/service/service.h" #include "core/hle/service/time/clock_types.h" @@ -12,6 +13,29 @@ class System; } namespace Service::Set { +enum class LanguageCode : u64; +enum class GetFirmwareVersionType { + Version1, + Version2, +}; + +struct FirmwareVersionFormat { + u8 major; + u8 minor; + u8 micro; + INSERT_PADDING_BYTES(1); + u8 revision_major; + u8 revision_minor; + INSERT_PADDING_BYTES(2); + std::array platform; + std::array version_hash; + std::array display_version; + std::array display_title; +}; +static_assert(sizeof(FirmwareVersionFormat) == 0x100, "FirmwareVersionFormat is an invalid size"); + +Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, + GetFirmwareVersionType type); class SET_SYS final : public ServiceFramework { public: diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f22db233b..1848ac89f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -47,6 +47,7 @@ #include "core/hle/service/am/applet_ae.h" #include "core/hle/service/am/applet_oe.h" #include "core/hle/service/am/applets/applets.h" +#include "core/hle/service/set/set_sys.h" #include "yuzu/multiplayer/state.h" #include "yuzu/util/controller_navigation.h" @@ -1047,7 +1048,12 @@ void GMainWindow::InitializeWidgets() { statusBar()->addPermanentWidget(label); } - // TODO (flTobi): Add the widget when multiplayer is fully implemented + firmware_label = new QLabel(); + firmware_label->setObjectName(QStringLiteral("FirmwareLabel")); + firmware_label->setVisible(false); + firmware_label->setFocusPolicy(Qt::NoFocus); + statusBar()->addPermanentWidget(firmware_label); + statusBar()->addPermanentWidget(multiplayer_state->GetStatusText(), 0); statusBar()->addPermanentWidget(multiplayer_state->GetStatusIcon(), 0); @@ -2161,6 +2167,10 @@ void GMainWindow::OnEmulationStopped() { emu_frametime_label->setVisible(false); renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); + if (!firmware_label->text().isEmpty()) { + firmware_label->setVisible(true); + } + current_game_path.clear(); // When closing the game, destroy the GLWindow to clear the context after the game is closed @@ -4586,6 +4596,7 @@ void GMainWindow::UpdateStatusBar() { emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); game_fps_label->setVisible(true); emu_frametime_label->setVisible(true); + firmware_label->setVisible(false); } void GMainWindow::UpdateGPUAccuracyButton() { @@ -4803,6 +4814,8 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { "games.")); } + SetFirmwareVersion(); + if (behavior == ReinitializeKeyBehavior::Warning) { game_list->PopulateAsync(UISettings::values.game_dirs); } @@ -4830,7 +4843,7 @@ bool GMainWindow::CheckSystemArchiveDecryption() { } bool GMainWindow::CheckFirmwarePresence() { - constexpr u64 MiiEditId = 0x0100000000001009ull; + constexpr u64 MiiEditId = static_cast(Service::AM::Applets::AppletProgramId::MiiEdit); auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); if (!bis_system) { @@ -4845,6 +4858,28 @@ bool GMainWindow::CheckFirmwarePresence() { return true; } +void GMainWindow::SetFirmwareVersion() { + Service::Set::FirmwareVersionFormat firmware_data{}; + const auto result = Service::Set::GetFirmwareVersionImpl( + firmware_data, *system, Service::Set::GetFirmwareVersionType::Version2); + + if (result.IsError() || !CheckFirmwarePresence()) { + LOG_INFO(Frontend, "Installed firmware: No firmware available"); + firmware_label->setVisible(false); + return; + } + + firmware_label->setVisible(true); + + const std::string display_version(firmware_data.display_version.data()); + const std::string display_title(firmware_data.display_title.data()); + + LOG_INFO(Frontend, "Installed firmware: {}", display_title); + + firmware_label->setText(QString::fromStdString(display_version)); + firmware_label->setToolTip(QString::fromStdString(display_title)); +} + bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, u64* selected_title_id, u8* selected_content_record_type) { using ContentInfo = std::tuple; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 49ee1e1d2..0c8b7606d 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -457,6 +457,7 @@ private: bool CheckDarkMode(); bool CheckSystemArchiveDecryption(); bool CheckFirmwarePresence(); + void SetFirmwareVersion(); void ConfigureFilesystemProvider(const std::string& filepath); /** * Open (or not) the right confirm dialog based on current setting and game exit lock @@ -511,6 +512,7 @@ private: QLabel* game_fps_label = nullptr; QLabel* emu_frametime_label = nullptr; QLabel* tas_label = nullptr; + QLabel* firmware_label = nullptr; QPushButton* gpu_accuracy_button = nullptr; QPushButton* renderer_status_button = nullptr; QPushButton* dock_status_button = nullptr; -- cgit v1.2.3