diff options
| author | 2023-10-08 17:11:52 -0400 | |
|---|---|---|
| committer | 2023-10-08 17:11:52 -0400 | |
| commit | c0d152affa3e1c118247a2cb36c8a26dee9d6372 (patch) | |
| tree | 4eba3e56e85575c4498567f97627b93a8b199ac2 | |
| parent | Merge pull request #10519 from mdmrk/master (diff) | |
| parent | yuzu: Add desktop shortcut support for Windows (diff) | |
| download | yuzu-c0d152affa3e1c118247a2cb36c8a26dee9d6372.tar.gz yuzu-c0d152affa3e1c118247a2cb36c8a26dee9d6372.tar.xz yuzu-c0d152affa3e1c118247a2cb36c8a26dee9d6372.zip | |
Merge pull request #11705 from FearlessTobi/windows-sc
yuzu: Add desktop shortcut support for Windows (continuation of #11344)
| -rw-r--r-- | src/common/fs/fs_paths.h | 1 | ||||
| -rw-r--r-- | src/common/fs/path_util.cpp | 1 | ||||
| -rw-r--r-- | src/common/fs/path_util.h | 1 | ||||
| -rw-r--r-- | src/yuzu/game_list.cpp | 4 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 85 | ||||
| -rw-r--r-- | src/yuzu/util/util.cpp | 77 | ||||
| -rw-r--r-- | src/yuzu/util/util.h | 14 |
7 files changed, 157 insertions, 26 deletions
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h index 59d66f71e..441c8af97 100644 --- a/src/common/fs/fs_paths.h +++ b/src/common/fs/fs_paths.h | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #define SDMC_DIR "sdmc" | 23 | #define SDMC_DIR "sdmc" |
| 24 | #define SHADER_DIR "shader" | 24 | #define SHADER_DIR "shader" |
| 25 | #define TAS_DIR "tas" | 25 | #define TAS_DIR "tas" |
| 26 | #define ICONS_DIR "icons" | ||
| 26 | 27 | ||
| 27 | // yuzu-specific files | 28 | // yuzu-specific files |
| 28 | 29 | ||
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp index 84ed0ad10..0abd81a45 100644 --- a/src/common/fs/path_util.cpp +++ b/src/common/fs/path_util.cpp | |||
| @@ -129,6 +129,7 @@ public: | |||
| 129 | GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR); | 129 | GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR); |
| 130 | GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR); | 130 | GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR); |
| 131 | GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); | 131 | GenerateYuzuPath(YuzuPath::TASDir, yuzu_path / TAS_DIR); |
| 132 | GenerateYuzuPath(YuzuPath::IconsDir, yuzu_path / ICONS_DIR); | ||
| 132 | } | 133 | } |
| 133 | 134 | ||
| 134 | private: | 135 | private: |
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h index 289974e32..63801c924 100644 --- a/src/common/fs/path_util.h +++ b/src/common/fs/path_util.h | |||
| @@ -25,6 +25,7 @@ enum class YuzuPath { | |||
| 25 | SDMCDir, // Where the emulated SDMC is stored. | 25 | SDMCDir, // Where the emulated SDMC is stored. |
| 26 | ShaderDir, // Where shaders are stored. | 26 | ShaderDir, // Where shaders are stored. |
| 27 | TASDir, // Where TAS scripts are stored. | 27 | TASDir, // Where TAS scripts are stored. |
| 28 | IconsDir, // Where Icons for Windows shortcuts are stored. | ||
| 28 | }; | 29 | }; |
| 29 | 30 | ||
| 30 | /** | 31 | /** |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 3f04fcb66..74f48031a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -564,9 +564,9 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 564 | QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); | 564 | QAction* verify_integrity = context_menu.addAction(tr("Verify Integrity")); |
| 565 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 565 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 566 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 566 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 567 | #ifndef WIN32 | ||
| 568 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); | 567 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); |
| 569 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); | 568 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); |
| 569 | #ifndef WIN32 | ||
| 570 | QAction* create_applications_menu_shortcut = | 570 | QAction* create_applications_menu_shortcut = |
| 571 | shortcut_menu->addAction(tr("Add to Applications Menu")); | 571 | shortcut_menu->addAction(tr("Add to Applications Menu")); |
| 572 | #endif | 572 | #endif |
| @@ -644,10 +644,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 644 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { | 644 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { |
| 645 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); | 645 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); |
| 646 | }); | 646 | }); |
| 647 | #ifndef WIN32 | ||
| 648 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { | 647 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { |
| 649 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); | 648 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); |
| 650 | }); | 649 | }); |
| 650 | #ifndef WIN32 | ||
| 651 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { | 651 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { |
| 652 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); | 652 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); |
| 653 | }); | 653 | }); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e0bfa7185..89361fa3f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -98,6 +98,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 98 | #include "common/scm_rev.h" | 98 | #include "common/scm_rev.h" |
| 99 | #include "common/scope_exit.h" | 99 | #include "common/scope_exit.h" |
| 100 | #ifdef _WIN32 | 100 | #ifdef _WIN32 |
| 101 | #include <shlobj.h> | ||
| 101 | #include "common/windows/timer_resolution.h" | 102 | #include "common/windows/timer_resolution.h" |
| 102 | #endif | 103 | #endif |
| 103 | #ifdef ARCHITECTURE_x86_64 | 104 | #ifdef ARCHITECTURE_x86_64 |
| @@ -2842,7 +2843,6 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2842 | const QStringList args = QApplication::arguments(); | 2843 | const QStringList args = QApplication::arguments(); |
| 2843 | std::filesystem::path yuzu_command = args[0].toStdString(); | 2844 | std::filesystem::path yuzu_command = args[0].toStdString(); |
| 2844 | 2845 | ||
| 2845 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2846 | // If relative path, make it an absolute path | 2846 | // If relative path, make it an absolute path |
| 2847 | if (yuzu_command.c_str()[0] == '.') { | 2847 | if (yuzu_command.c_str()[0] == '.') { |
| 2848 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; | 2848 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; |
| @@ -2865,12 +2865,14 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2865 | UISettings::values.shortcut_already_warned = true; | 2865 | UISettings::values.shortcut_already_warned = true; |
| 2866 | } | 2866 | } |
| 2867 | #endif // __linux__ | 2867 | #endif // __linux__ |
| 2868 | #endif // __linux__ || __FreeBSD__ | ||
| 2869 | 2868 | ||
| 2870 | std::filesystem::path target_directory{}; | 2869 | std::filesystem::path target_directory{}; |
| 2871 | // Determine target directory for shortcut | 2870 | // Determine target directory for shortcut |
| 2872 | #if defined(__linux__) || defined(__FreeBSD__) | 2871 | #if defined(WIN32) |
| 2872 | const char* home = std::getenv("USERPROFILE"); | ||
| 2873 | #else | ||
| 2873 | const char* home = std::getenv("HOME"); | 2874 | const char* home = std::getenv("HOME"); |
| 2875 | #endif | ||
| 2874 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); | 2876 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); |
| 2875 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); | 2877 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); |
| 2876 | 2878 | ||
| @@ -2880,7 +2882,7 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2880 | QMessageBox::critical( | 2882 | QMessageBox::critical( |
| 2881 | this, tr("Create Shortcut"), | 2883 | this, tr("Create Shortcut"), |
| 2882 | tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") | 2884 | tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") |
| 2883 | .arg(QString::fromStdString(target_directory)), | 2885 | .arg(QString::fromStdString(target_directory.generic_string())), |
| 2884 | QMessageBox::StandardButton::Ok); | 2886 | QMessageBox::StandardButton::Ok); |
| 2885 | return; | 2887 | return; |
| 2886 | } | 2888 | } |
| @@ -2888,15 +2890,15 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2888 | target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / | 2890 | target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / |
| 2889 | "applications"; | 2891 | "applications"; |
| 2890 | if (!Common::FS::CreateDirs(target_directory)) { | 2892 | if (!Common::FS::CreateDirs(target_directory)) { |
| 2891 | QMessageBox::critical(this, tr("Create Shortcut"), | 2893 | QMessageBox::critical( |
| 2892 | tr("Cannot create shortcut in applications menu. Path \"%1\" " | 2894 | this, tr("Create Shortcut"), |
| 2893 | "does not exist and cannot be created.") | 2895 | tr("Cannot create shortcut in applications menu. Path \"%1\" " |
| 2894 | .arg(QString::fromStdString(target_directory)), | 2896 | "does not exist and cannot be created.") |
| 2895 | QMessageBox::StandardButton::Ok); | 2897 | .arg(QString::fromStdString(target_directory.generic_string())), |
| 2898 | QMessageBox::StandardButton::Ok); | ||
| 2896 | return; | 2899 | return; |
| 2897 | } | 2900 | } |
| 2898 | } | 2901 | } |
| 2899 | #endif | ||
| 2900 | 2902 | ||
| 2901 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); | 2903 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); |
| 2902 | // Determine full paths for icon and shortcut | 2904 | // Determine full paths for icon and shortcut |
| @@ -2918,9 +2920,14 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2918 | const std::filesystem::path shortcut_path = | 2920 | const std::filesystem::path shortcut_path = |
| 2919 | target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) | 2921 | target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) |
| 2920 | : fmt::format("yuzu-{:016X}.desktop", program_id)); | 2922 | : fmt::format("yuzu-{:016X}.desktop", program_id)); |
| 2923 | #elif defined(WIN32) | ||
| 2924 | std::filesystem::path icons_path = | ||
| 2925 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::IconsDir); | ||
| 2926 | std::filesystem::path icon_path = | ||
| 2927 | icons_path / ((program_id == 0 ? fmt::format("yuzu-{}.ico", game_file_name) | ||
| 2928 | : fmt::format("yuzu-{:016X}.ico", program_id))); | ||
| 2921 | #else | 2929 | #else |
| 2922 | const std::filesystem::path icon_path{}; | 2930 | std::string icon_extension; |
| 2923 | const std::filesystem::path shortcut_path{}; | ||
| 2924 | #endif | 2931 | #endif |
| 2925 | 2932 | ||
| 2926 | // Get title from game file | 2933 | // Get title from game file |
| @@ -2945,29 +2952,37 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga | |||
| 2945 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); | 2952 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); |
| 2946 | } | 2953 | } |
| 2947 | 2954 | ||
| 2948 | QImage icon_jpeg = | 2955 | QImage icon_data = |
| 2949 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); | 2956 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); |
| 2950 | #if defined(__linux__) || defined(__FreeBSD__) | 2957 | #if defined(__linux__) || defined(__FreeBSD__) |
| 2951 | // Convert and write the icon as a PNG | 2958 | // Convert and write the icon as a PNG |
| 2952 | if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { | 2959 | if (!icon_data.save(QString::fromStdString(icon_path.string()))) { |
| 2953 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); | 2960 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); |
| 2954 | } else { | 2961 | } else { |
| 2955 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); | 2962 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); |
| 2956 | } | 2963 | } |
| 2964 | #elif defined(WIN32) | ||
| 2965 | if (!SaveIconToFile(icon_path.string(), icon_data)) { | ||
| 2966 | LOG_ERROR(Frontend, "Could not write icon to file"); | ||
| 2967 | return; | ||
| 2968 | } | ||
| 2957 | #endif // __linux__ | 2969 | #endif // __linux__ |
| 2958 | 2970 | ||
| 2959 | #if defined(__linux__) || defined(__FreeBSD__) | 2971 | #ifdef _WIN32 |
| 2972 | // Replace characters that are illegal in Windows filenames by a dash | ||
| 2973 | const std::string illegal_chars = "<>:\"/\\|?*"; | ||
| 2974 | for (char c : illegal_chars) { | ||
| 2975 | std::replace(title.begin(), title.end(), c, '_'); | ||
| 2976 | } | ||
| 2977 | const std::filesystem::path shortcut_path = target_directory / (title + ".lnk").c_str(); | ||
| 2978 | #endif | ||
| 2979 | |||
| 2960 | const std::string comment = | 2980 | const std::string comment = |
| 2961 | tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); | 2981 | tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); |
| 2962 | const std::string arguments = fmt::format("-g \"{:s}\"", game_path); | 2982 | const std::string arguments = fmt::format("-g \"{:s}\"", game_path); |
| 2963 | const std::string categories = "Game;Emulator;Qt;"; | 2983 | const std::string categories = "Game;Emulator;Qt;"; |
| 2964 | const std::string keywords = "Switch;Nintendo;"; | 2984 | const std::string keywords = "Switch;Nintendo;"; |
| 2965 | #else | 2985 | |
| 2966 | const std::string comment{}; | ||
| 2967 | const std::string arguments{}; | ||
| 2968 | const std::string categories{}; | ||
| 2969 | const std::string keywords{}; | ||
| 2970 | #endif | ||
| 2971 | if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), | 2986 | if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), |
| 2972 | yuzu_command.string(), arguments, categories, keywords)) { | 2987 | yuzu_command.string(), arguments, categories, keywords)) { |
| 2973 | QMessageBox::critical(this, tr("Create Shortcut"), | 2988 | QMessageBox::critical(this, tr("Create Shortcut"), |
| @@ -3989,6 +4004,34 @@ bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::st | |||
| 3989 | shortcut_stream.close(); | 4004 | shortcut_stream.close(); |
| 3990 | 4005 | ||
| 3991 | return true; | 4006 | return true; |
| 4007 | #elif defined(WIN32) | ||
| 4008 | IShellLinkW* shell_link; | ||
| 4009 | auto hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, | ||
| 4010 | (void**)&shell_link); | ||
| 4011 | if (FAILED(hres)) { | ||
| 4012 | return false; | ||
| 4013 | } | ||
| 4014 | shell_link->SetPath( | ||
| 4015 | Common::UTF8ToUTF16W(command).data()); // Path to the object we are referring to | ||
| 4016 | shell_link->SetArguments(Common::UTF8ToUTF16W(arguments).data()); | ||
| 4017 | shell_link->SetDescription(Common::UTF8ToUTF16W(comment).data()); | ||
| 4018 | shell_link->SetIconLocation(Common::UTF8ToUTF16W(icon_path).data(), 0); | ||
| 4019 | |||
| 4020 | IPersistFile* persist_file; | ||
| 4021 | hres = shell_link->QueryInterface(IID_IPersistFile, (void**)&persist_file); | ||
| 4022 | if (FAILED(hres)) { | ||
| 4023 | return false; | ||
| 4024 | } | ||
| 4025 | |||
| 4026 | hres = persist_file->Save(Common::UTF8ToUTF16W(shortcut_path).data(), TRUE); | ||
| 4027 | if (FAILED(hres)) { | ||
| 4028 | return false; | ||
| 4029 | } | ||
| 4030 | |||
| 4031 | persist_file->Release(); | ||
| 4032 | shell_link->Release(); | ||
| 4033 | |||
| 4034 | return true; | ||
| 3992 | #endif | 4035 | #endif |
| 3993 | return false; | 4036 | return false; |
| 3994 | } | 4037 | } |
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index 5c3e4589e..61cf00176 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp | |||
| @@ -5,6 +5,10 @@ | |||
| 5 | #include <cmath> | 5 | #include <cmath> |
| 6 | #include <QPainter> | 6 | #include <QPainter> |
| 7 | #include "yuzu/util/util.h" | 7 | #include "yuzu/util/util.h" |
| 8 | #ifdef _WIN32 | ||
| 9 | #include <windows.h> | ||
| 10 | #include "common/fs/file.h" | ||
| 11 | #endif | ||
| 8 | 12 | ||
| 9 | QFont GetMonospaceFont() { | 13 | QFont GetMonospaceFont() { |
| 10 | QFont font(QStringLiteral("monospace")); | 14 | QFont font(QStringLiteral("monospace")); |
| @@ -37,3 +41,76 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { | |||
| 37 | painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); | 41 | painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); |
| 38 | return circle_pixmap; | 42 | return circle_pixmap; |
| 39 | } | 43 | } |
| 44 | |||
| 45 | bool SaveIconToFile(const std::string_view path, const QImage& image) { | ||
| 46 | #if defined(WIN32) | ||
| 47 | #pragma pack(push, 2) | ||
| 48 | struct IconDir { | ||
| 49 | WORD id_reserved; | ||
| 50 | WORD id_type; | ||
| 51 | WORD id_count; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct IconDirEntry { | ||
| 55 | BYTE width; | ||
| 56 | BYTE height; | ||
| 57 | BYTE color_count; | ||
| 58 | BYTE reserved; | ||
| 59 | WORD planes; | ||
| 60 | WORD bit_count; | ||
| 61 | DWORD bytes_in_res; | ||
| 62 | DWORD image_offset; | ||
| 63 | }; | ||
| 64 | #pragma pack(pop) | ||
| 65 | |||
| 66 | QImage source_image = image.convertToFormat(QImage::Format_RGB32); | ||
| 67 | constexpr int bytes_per_pixel = 4; | ||
| 68 | const int image_size = source_image.width() * source_image.height() * bytes_per_pixel; | ||
| 69 | |||
| 70 | BITMAPINFOHEADER info_header{}; | ||
| 71 | info_header.biSize = sizeof(BITMAPINFOHEADER), info_header.biWidth = source_image.width(), | ||
| 72 | info_header.biHeight = source_image.height() * 2, info_header.biPlanes = 1, | ||
| 73 | info_header.biBitCount = bytes_per_pixel * 8, info_header.biCompression = BI_RGB; | ||
| 74 | |||
| 75 | const IconDir icon_dir{.id_reserved = 0, .id_type = 1, .id_count = 1}; | ||
| 76 | const IconDirEntry icon_entry{.width = static_cast<BYTE>(source_image.width()), | ||
| 77 | .height = static_cast<BYTE>(source_image.height() * 2), | ||
| 78 | .color_count = 0, | ||
| 79 | .reserved = 0, | ||
| 80 | .planes = 1, | ||
| 81 | .bit_count = bytes_per_pixel * 8, | ||
| 82 | .bytes_in_res = | ||
| 83 | static_cast<DWORD>(sizeof(BITMAPINFOHEADER) + image_size), | ||
| 84 | .image_offset = sizeof(IconDir) + sizeof(IconDirEntry)}; | ||
| 85 | |||
| 86 | Common::FS::IOFile icon_file(path, Common::FS::FileAccessMode::Write, | ||
| 87 | Common::FS::FileType::BinaryFile); | ||
| 88 | if (!icon_file.IsOpen()) { | ||
| 89 | return false; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (!icon_file.Write(icon_dir)) { | ||
| 93 | return false; | ||
| 94 | } | ||
| 95 | if (!icon_file.Write(icon_entry)) { | ||
| 96 | return false; | ||
| 97 | } | ||
| 98 | if (!icon_file.Write(info_header)) { | ||
| 99 | return false; | ||
| 100 | } | ||
| 101 | |||
| 102 | for (int y = 0; y < image.height(); y++) { | ||
| 103 | const auto* line = source_image.scanLine(source_image.height() - 1 - y); | ||
| 104 | std::vector<u8> line_data(source_image.width() * bytes_per_pixel); | ||
| 105 | std::memcpy(line_data.data(), line, line_data.size()); | ||
| 106 | if (!icon_file.Write(line_data)) { | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | icon_file.Close(); | ||
| 111 | |||
| 112 | return true; | ||
| 113 | #else | ||
| 114 | return false; | ||
| 115 | #endif | ||
| 116 | } | ||
diff --git a/src/yuzu/util/util.h b/src/yuzu/util/util.h index 39dd2d895..09c14ce3f 100644 --- a/src/yuzu/util/util.h +++ b/src/yuzu/util/util.h | |||
| @@ -7,14 +7,22 @@ | |||
| 7 | #include <QString> | 7 | #include <QString> |
| 8 | 8 | ||
| 9 | /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. | 9 | /// Returns a QFont object appropriate to use as a monospace font for debugging widgets, etc. |
| 10 | QFont GetMonospaceFont(); | 10 | [[nodiscard]] QFont GetMonospaceFont(); |
| 11 | 11 | ||
| 12 | /// Convert a size in bytes into a readable format (KiB, MiB, etc.) | 12 | /// Convert a size in bytes into a readable format (KiB, MiB, etc.) |
| 13 | QString ReadableByteSize(qulonglong size); | 13 | [[nodiscard]] QString ReadableByteSize(qulonglong size); |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * Creates a circle pixmap from a specified color | 16 | * Creates a circle pixmap from a specified color |
| 17 | * @param color The color the pixmap shall have | 17 | * @param color The color the pixmap shall have |
| 18 | * @return QPixmap circle pixmap | 18 | * @return QPixmap circle pixmap |
| 19 | */ | 19 | */ |
| 20 | QPixmap CreateCirclePixmapFromColor(const QColor& color); | 20 | [[nodiscard]] QPixmap CreateCirclePixmapFromColor(const QColor& color); |
| 21 | |||
| 22 | /** | ||
| 23 | * Saves a windows icon to a file | ||
| 24 | * @param path The icons path | ||
| 25 | * @param image The image to save | ||
| 26 | * @return bool If the operation succeeded | ||
| 27 | */ | ||
| 28 | [[nodiscard]] bool SaveIconToFile(const std::string_view path, const QImage& image); | ||