aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xREADME.md2
-rwxr-xr-xsrc/core/CMakeLists.txt6
-rwxr-xr-xsrc/core/hle/service/aoc/addon_content_manager.cpp223
-rwxr-xr-xsrc/core/hle/service/aoc/addon_content_manager.h51
-rwxr-xr-xsrc/core/hle/service/aoc/purchase_event_manager.cpp67
-rwxr-xr-xsrc/core/hle/service/aoc/purchase_event_manager.h30
-rwxr-xr-xsrc/core/hle/service/services.cpp2
-rwxr-xr-xsrc/yuzu/main.cpp84
8 files changed, 419 insertions, 46 deletions
diff --git a/README.md b/README.md
index 72587fb6c..18f973c4c 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
1yuzu emulator early access 1yuzu emulator early access
2============= 2=============
3 3
4This is the source code for early-access 4161. 4This is the source code for early-access 4162.
5 5
6## Legal Notice 6## Legal Notice
7 7
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index fc07115ed..6a55ec03b 100755
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -486,8 +486,10 @@ add_library(core STATIC
486 hle/service/am/service/system_applet_proxy.h 486 hle/service/am/service/system_applet_proxy.h
487 hle/service/am/service/window_controller.cpp 487 hle/service/am/service/window_controller.cpp
488 hle/service/am/service/window_controller.h 488 hle/service/am/service/window_controller.h
489 hle/service/aoc/aoc_u.cpp 489 hle/service/aoc/addon_content_manager.cpp
490 hle/service/aoc/aoc_u.h 490 hle/service/aoc/addon_content_manager.h
491 hle/service/aoc/purchase_event_manager.cpp
492 hle/service/aoc/purchase_event_manager.h
491 hle/service/apm/apm.cpp 493 hle/service/apm/apm.cpp
492 hle/service/apm/apm.h 494 hle/service/apm/apm.h
493 hle/service/apm/apm_controller.cpp 495 hle/service/apm/apm_controller.cpp
diff --git a/src/core/hle/service/aoc/addon_content_manager.cpp b/src/core/hle/service/aoc/addon_content_manager.cpp
new file mode 100755
index 000000000..d47f57d64
--- /dev/null
+++ b/src/core/hle/service/aoc/addon_content_manager.cpp
@@ -0,0 +1,223 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <numeric>
6#include <vector>
7
8#include "common/logging/log.h"
9#include "common/settings.h"
10#include "core/core.h"
11#include "core/file_sys/common_funcs.h"
12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/control_metadata.h"
14#include "core/file_sys/nca_metadata.h"
15#include "core/file_sys/patch_manager.h"
16#include "core/file_sys/registered_cache.h"
17#include "core/hle/kernel/k_event.h"
18#include "core/hle/service/aoc/addon_content_manager.h"
19#include "core/hle/service/aoc/purchase_event_manager.h"
20#include "core/hle/service/cmif_serialization.h"
21#include "core/hle/service/ipc_helpers.h"
22#include "core/hle/service/server_manager.h"
23#include "core/loader/loader.h"
24
25namespace Service::AOC {
26
27static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
28 return FileSys::GetBaseTitleID(title_id) == base;
29}
30
31static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
32 std::vector<u64> add_on_content;
33 const auto& rcu = system.GetContentProvider();
34 const auto list =
35 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
36 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
37 [](const FileSys::ContentProviderEntry& rce) { return rce.title_id; });
38 add_on_content.erase(
39 std::remove_if(
40 add_on_content.begin(), add_on_content.end(),
41 [&rcu](u64 tid) {
42 return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
43 Loader::ResultStatus::Success;
44 }),
45 add_on_content.end());
46 return add_on_content;
47}
48
49IAddOnContentManager::IAddOnContentManager(Core::System& system_)
50 : ServiceFramework{system_, "aoc:u"}, add_on_content{AccumulateAOCTitleIDs(system)},
51 service_context{system_, "aoc:u"} {
52 // clang-format off
53 static const FunctionInfo functions[] = {
54 {0, nullptr, "CountAddOnContentByApplicationId"},
55 {1, nullptr, "ListAddOnContentByApplicationId"},
56 {2, D<&IAddOnContentManager::CountAddOnContent>, "CountAddOnContent"},
57 {3, D<&IAddOnContentManager::ListAddOnContent>, "ListAddOnContent"},
58 {4, nullptr, "GetAddOnContentBaseIdByApplicationId"},
59 {5, D<&IAddOnContentManager::GetAddOnContentBaseId>, "GetAddOnContentBaseId"},
60 {6, nullptr, "PrepareAddOnContentByApplicationId"},
61 {7, D<&IAddOnContentManager::PrepareAddOnContent>, "PrepareAddOnContent"},
62 {8, D<&IAddOnContentManager::GetAddOnContentListChangedEvent>, "GetAddOnContentListChangedEvent"},
63 {9, nullptr, "GetAddOnContentLostErrorCode"},
64 {10, D<&IAddOnContentManager::GetAddOnContentListChangedEventWithProcessId>, "GetAddOnContentListChangedEventWithProcessId"},
65 {11, D<&IAddOnContentManager::NotifyMountAddOnContent>, "NotifyMountAddOnContent"},
66 {12, D<&IAddOnContentManager::NotifyUnmountAddOnContent>, "NotifyUnmountAddOnContent"},
67 {13, nullptr, "IsAddOnContentMountedForDebug"},
68 {50, D<&IAddOnContentManager::CheckAddOnContentMountStatus>, "CheckAddOnContentMountStatus"},
69 {100, D<&IAddOnContentManager::CreateEcPurchasedEventManager>, "CreateEcPurchasedEventManager"},
70 {101, D<&IAddOnContentManager::CreatePermanentEcPurchasedEventManager>, "CreatePermanentEcPurchasedEventManager"},
71 {110, nullptr, "CreateContentsServiceManager"},
72 {200, nullptr, "SetRequiredAddOnContentsOnContentsAvailabilityTransition"},
73 {300, nullptr, "SetupHostAddOnContent"},
74 {301, nullptr, "GetRegisteredAddOnContentPath"},
75 {302, nullptr, "UpdateCachedList"},
76 };
77 // clang-format on
78
79 RegisterHandlers(functions);
80
81 aoc_change_event = service_context.CreateEvent("GetAddOnContentListChanged:Event");
82}
83
84IAddOnContentManager::~IAddOnContentManager() {
85 service_context.CloseEvent(aoc_change_event);
86}
87
88Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
89 LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
90
91 const auto current = system.GetApplicationProcessProgramID();
92
93 const auto& disabled = Settings::values.disabled_addons[current];
94 if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
95 *out_count = 0;
96 R_SUCCEED();
97 }
98
99 *out_count = static_cast<u32>(
100 std::count_if(add_on_content.begin(), add_on_content.end(),
101 [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }));
102
103 R_SUCCEED();
104}
105
106Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
107 OutBuffer<BufferAttr_HipcMapAlias> out_addons,
108 u32 offset, u32 count, ClientProcessId process_id) {
109 LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
110 process_id.pid);
111
112 const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
113
114 std::vector<u32> out;
115 const auto& disabled = Settings::values.disabled_addons[current];
116 if (std::find(disabled.begin(), disabled.end(), "DLC") == disabled.end()) {
117 for (u64 content_id : add_on_content) {
118 if (FileSys::GetBaseTitleID(content_id) != current) {
119 continue;
120 }
121
122 out.push_back(static_cast<u32>(FileSys::GetAOCID(content_id)));
123 }
124 }
125
126 // TODO(DarkLordZach): Find the correct error code.
127 R_UNLESS(out.size() >= offset, ResultUnknown);
128
129 *out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
130 std::rotate(out.begin(), out.begin() + offset, out.end());
131
132 std::memcpy(out_addons.data(), out.data(), *out_count * sizeof(u32));
133
134 R_SUCCEED();
135}
136
137Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
138 ClientProcessId process_id) {
139 LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
140
141 const auto title_id = system.GetApplicationProcessProgramID();
142 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
143 system.GetContentProvider()};
144
145 const auto res = pm.GetControlMetadata();
146 if (res.first == nullptr) {
147 *out_title_id = FileSys::GetAOCBaseTitleID(title_id);
148 R_SUCCEED();
149 }
150
151 *out_title_id = res.first->GetDLCBaseTitleId();
152
153 R_SUCCEED();
154}
155
156Result IAddOnContentManager::PrepareAddOnContent(s32 addon_index, ClientProcessId process_id) {
157 LOG_WARNING(Service_AOC, "(STUBBED) called with addon_index={}, process_id={}", addon_index,
158 process_id.pid);
159
160 R_SUCCEED();
161}
162
163Result IAddOnContentManager::GetAddOnContentListChangedEvent(
164 OutCopyHandle<Kernel::KReadableEvent> out_event) {
165 LOG_WARNING(Service_AOC, "(STUBBED) called");
166
167 *out_event = &aoc_change_event->GetReadableEvent();
168
169 R_SUCCEED();
170}
171
172Result IAddOnContentManager::GetAddOnContentListChangedEventWithProcessId(
173 OutCopyHandle<Kernel::KReadableEvent> out_event, ClientProcessId process_id) {
174 LOG_WARNING(Service_AOC, "(STUBBED) called");
175
176 *out_event = &aoc_change_event->GetReadableEvent();
177
178 R_SUCCEED();
179}
180
181Result IAddOnContentManager::NotifyMountAddOnContent() {
182 LOG_WARNING(Service_AOC, "(STUBBED) called");
183
184 R_SUCCEED();
185}
186
187Result IAddOnContentManager::NotifyUnmountAddOnContent() {
188 LOG_WARNING(Service_AOC, "(STUBBED) called");
189
190 R_SUCCEED();
191}
192
193Result IAddOnContentManager::CheckAddOnContentMountStatus() {
194 LOG_WARNING(Service_AOC, "(STUBBED) called");
195
196 R_SUCCEED();
197}
198
199Result IAddOnContentManager::CreateEcPurchasedEventManager(
200 OutInterface<IPurchaseEventManager> out_interface) {
201 LOG_WARNING(Service_AOC, "(STUBBED) called");
202
203 *out_interface = std::make_shared<IPurchaseEventManager>(system);
204
205 R_SUCCEED();
206}
207
208Result IAddOnContentManager::CreatePermanentEcPurchasedEventManager(
209 OutInterface<IPurchaseEventManager> out_interface) {
210 LOG_WARNING(Service_AOC, "(STUBBED) called");
211
212 *out_interface = std::make_shared<IPurchaseEventManager>(system);
213
214 R_SUCCEED();
215}
216
217void LoopProcess(Core::System& system) {
218 auto server_manager = std::make_unique<ServerManager>(system);
219 server_manager->RegisterNamedService("aoc:u", std::make_shared<IAddOnContentManager>(system));
220 ServerManager::RunServer(std::move(server_manager));
221}
222
223} // namespace Service::AOC
diff --git a/src/core/hle/service/aoc/addon_content_manager.h b/src/core/hle/service/aoc/addon_content_manager.h
new file mode 100755
index 000000000..91857df4c
--- /dev/null
+++ b/src/core/hle/service/aoc/addon_content_manager.h
@@ -0,0 +1,51 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/service.h"
9
10namespace Core {
11class System;
12}
13
14namespace Kernel {
15class KEvent;
16}
17
18namespace Service::AOC {
19
20class IPurchaseEventManager;
21
22class IAddOnContentManager final : public ServiceFramework<IAddOnContentManager> {
23public:
24 explicit IAddOnContentManager(Core::System& system);
25 ~IAddOnContentManager() override;
26
27 Result CountAddOnContent(Out<u32> out_count, ClientProcessId process_id);
28 Result ListAddOnContent(Out<u32> out_count, OutBuffer<BufferAttr_HipcMapAlias> out_addons,
29 u32 offset, u32 count, ClientProcessId process_id);
30 Result GetAddOnContentBaseId(Out<u64> out_title_id, ClientProcessId process_id);
31 Result PrepareAddOnContent(s32 addon_index, ClientProcessId process_id);
32 Result GetAddOnContentListChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
33 Result GetAddOnContentListChangedEventWithProcessId(
34 OutCopyHandle<Kernel::KReadableEvent> out_event, ClientProcessId process_id);
35 Result NotifyMountAddOnContent();
36 Result NotifyUnmountAddOnContent();
37 Result CheckAddOnContentMountStatus();
38 Result CreateEcPurchasedEventManager(OutInterface<IPurchaseEventManager> out_interface);
39 Result CreatePermanentEcPurchasedEventManager(
40 OutInterface<IPurchaseEventManager> out_interface);
41
42private:
43 std::vector<u64> add_on_content;
44 KernelHelpers::ServiceContext service_context;
45
46 Kernel::KEvent* aoc_change_event;
47};
48
49void LoopProcess(Core::System& system);
50
51} // namespace Service::AOC
diff --git a/src/core/hle/service/aoc/purchase_event_manager.cpp b/src/core/hle/service/aoc/purchase_event_manager.cpp
new file mode 100755
index 000000000..9e718510b
--- /dev/null
+++ b/src/core/hle/service/aoc/purchase_event_manager.cpp
@@ -0,0 +1,67 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/aoc/purchase_event_manager.h"
5#include "core/hle/service/cmif_serialization.h"
6
7namespace Service::AOC {
8
9constexpr Result ResultNoPurchasedProductInfoAvailable{ErrorModule::NIMShop, 400};
10
11IPurchaseEventManager::IPurchaseEventManager(Core::System& system_)
12 : ServiceFramework{system_, "IPurchaseEventManager"}, service_context{system,
13 "IPurchaseEventManager"} {
14 // clang-format off
15 static const FunctionInfo functions[] = {
16 {0, D<&IPurchaseEventManager::SetDefaultDeliveryTarget>, "SetDefaultDeliveryTarget"},
17 {1, D<&IPurchaseEventManager::SetDeliveryTarget>, "SetDeliveryTarget"},
18 {2, D<&IPurchaseEventManager::GetPurchasedEvent>, "GetPurchasedEvent"},
19 {3, D<&IPurchaseEventManager::PopPurchasedProductInfo>, "PopPurchasedProductInfo"},
20 {4, D<&IPurchaseEventManager::PopPurchasedProductInfoWithUid>, "PopPurchasedProductInfoWithUid"},
21 };
22 // clang-format on
23
24 RegisterHandlers(functions);
25
26 purchased_event = service_context.CreateEvent("IPurchaseEventManager:PurchasedEvent");
27}
28
29IPurchaseEventManager::~IPurchaseEventManager() {
30 service_context.CloseEvent(purchased_event);
31}
32
33Result IPurchaseEventManager::SetDefaultDeliveryTarget(
34 ClientProcessId process_id, InBuffer<BufferAttr_HipcMapAlias> in_buffer) {
35 LOG_WARNING(Service_AOC, "(STUBBED) called, process_id={}", process_id.pid);
36
37 R_SUCCEED();
38}
39
40Result IPurchaseEventManager::SetDeliveryTarget(u64 unknown,
41 InBuffer<BufferAttr_HipcMapAlias> in_buffer) {
42 LOG_WARNING(Service_AOC, "(STUBBED) called, unknown={}", unknown);
43
44 R_SUCCEED();
45}
46
47Result IPurchaseEventManager::GetPurchasedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event) {
48 LOG_WARNING(Service_AOC, "called");
49
50 *out_event = &purchased_event->GetReadableEvent();
51
52 R_SUCCEED();
53}
54
55Result IPurchaseEventManager::PopPurchasedProductInfo() {
56 LOG_DEBUG(Service_AOC, "(STUBBED) called");
57
58 R_RETURN(ResultNoPurchasedProductInfoAvailable);
59}
60
61Result IPurchaseEventManager::PopPurchasedProductInfoWithUid() {
62 LOG_DEBUG(Service_AOC, "(STUBBED) called");
63
64 R_RETURN(ResultNoPurchasedProductInfoAvailable);
65}
66
67} // namespace Service::AOC
diff --git a/src/core/hle/service/aoc/purchase_event_manager.h b/src/core/hle/service/aoc/purchase_event_manager.h
new file mode 100755
index 000000000..ea3836bc9
--- /dev/null
+++ b/src/core/hle/service/aoc/purchase_event_manager.h
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/kernel_helpers.h"
8#include "core/hle/service/os/event.h"
9#include "core/hle/service/service.h"
10
11namespace Service::AOC {
12
13class IPurchaseEventManager final : public ServiceFramework<IPurchaseEventManager> {
14public:
15 explicit IPurchaseEventManager(Core::System& system_);
16 ~IPurchaseEventManager() override;
17
18 Result SetDefaultDeliveryTarget(ClientProcessId process_id,
19 InBuffer<BufferAttr_HipcMapAlias> in_buffer);
20 Result SetDeliveryTarget(u64 unknown, InBuffer<BufferAttr_HipcMapAlias> in_buffer);
21 Result GetPurchasedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
22 Result PopPurchasedProductInfo();
23 Result PopPurchasedProductInfoWithUid();
24
25private:
26 KernelHelpers::ServiceContext service_context;
27 Kernel::KEvent* purchased_event;
28};
29
30} // namespace Service::AOC
diff --git a/src/core/hle/service/services.cpp b/src/core/hle/service/services.cpp
index 1aa85ea54..3defa4b31 100755
--- a/src/core/hle/service/services.cpp
+++ b/src/core/hle/service/services.cpp
@@ -5,7 +5,7 @@
5 5
6#include "core/hle/service/acc/acc.h" 6#include "core/hle/service/acc/acc.h"
7#include "core/hle/service/am/am.h" 7#include "core/hle/service/am/am.h"
8#include "core/hle/service/aoc/aoc_u.h" 8#include "core/hle/service/aoc/addon_content_manager.h"
9#include "core/hle/service/apm/apm.h" 9#include "core/hle/service/apm/apm.h"
10#include "core/hle/service/audio/audio.h" 10#include "core/hle/service/audio/audio.h"
11#include "core/hle/service/bcat/bcat.h" 11#include "core/hle/service/bcat/bcat.h"
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index ed820399b..6c8540186 100755
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -3010,9 +3010,6 @@ bool GMainWindow::MakeShortcutIcoPath(const u64 program_id, const std::string_vi
3010 3010
3011void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, 3011void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path,
3012 GameListShortcutTarget target) { 3012 GameListShortcutTarget target) {
3013 std::string game_title;
3014 QString qt_game_title;
3015 std::filesystem::path out_icon_path;
3016 // Get path to yuzu executable 3013 // Get path to yuzu executable
3017 const QStringList args = QApplication::arguments(); 3014 const QStringList args = QApplication::arguments();
3018 std::filesystem::path yuzu_command = args[0].toStdString(); 3015 std::filesystem::path yuzu_command = args[0].toStdString();
@@ -3029,48 +3026,51 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga
3029 shortcut_path = 3026 shortcut_path =
3030 QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString(); 3027 QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation).toStdString();
3031 } 3028 }
3032 // Icon path and title 3029
3033 if (std::filesystem::exists(shortcut_path)) { 3030 if (!std::filesystem::exists(shortcut_path)) {
3034 // Get title from game file 3031 GMainWindow::CreateShortcutMessagesGUI(
3035 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), 3032 this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
3036 system->GetContentProvider()}; 3033 QString::fromStdString(shortcut_path.generic_string()));
3037 const auto control = pm.GetControlMetadata(); 3034 LOG_ERROR(Frontend, "Invalid shortcut target {}", shortcut_path.generic_string());
3038 const auto loader = 3035 return;
3039 Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read)); 3036 }
3040 game_title = fmt::format("{:016X}", program_id); 3037
3041 if (control.first != nullptr) { 3038 // Get title from game file
3042 game_title = control.first->GetApplicationName(); 3039 const FileSys::PatchManager pm{program_id, system->GetFileSystemController(),
3043 } else { 3040 system->GetContentProvider()};
3044 loader->ReadTitle(game_title); 3041 const auto control = pm.GetControlMetadata();
3045 } 3042 const auto loader =
3046 // Delete illegal characters from title 3043 Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::OpenMode::Read));
3047 const std::string illegal_chars = "<>:\"/\\|?*."; 3044 std::string game_title = fmt::format("{:016X}", program_id);
3048 for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) { 3045 if (control.first != nullptr) {
3049 if (illegal_chars.find(*it) != std::string::npos) { 3046 game_title = control.first->GetApplicationName();
3050 game_title.erase(it.base() - 1); 3047 } else {
3051 } 3048 loader->ReadTitle(game_title);
3052 } 3049 }
3053 qt_game_title = QString::fromStdString(game_title); 3050 // Delete illegal characters from title
3054 // Get icon from game file 3051 const std::string illegal_chars = "<>:\"/\\|?*.";
3055 std::vector<u8> icon_image_file{}; 3052 for (auto it = game_title.rbegin(); it != game_title.rend(); ++it) {
3056 if (control.second != nullptr) { 3053 if (illegal_chars.find(*it) != std::string::npos) {
3057 icon_image_file = control.second->ReadAllBytes(); 3054 game_title.erase(it.base() - 1);
3058 } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
3059 LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
3060 } 3055 }
3061 QImage icon_data = 3056 }
3062 QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); 3057 const QString qt_game_title = QString::fromStdString(game_title);
3063 if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) { 3058 // Get icon from game file
3064 if (!SaveIconToFile(out_icon_path, icon_data)) { 3059 std::vector<u8> icon_image_file{};
3065 LOG_ERROR(Frontend, "Could not write icon to file"); 3060 if (control.second != nullptr) {
3066 } 3061 icon_image_file = control.second->ReadAllBytes();
3062 } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) {
3063 LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path);
3064 }
3065 QImage icon_data =
3066 QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size()));
3067 std::filesystem::path out_icon_path;
3068 if (GMainWindow::MakeShortcutIcoPath(program_id, game_title, out_icon_path)) {
3069 if (!SaveIconToFile(out_icon_path, icon_data)) {
3070 LOG_ERROR(Frontend, "Could not write icon to file");
3067 } 3071 }
3068 } else {
3069 GMainWindow::CreateShortcutMessagesGUI(this, GMainWindow::CREATE_SHORTCUT_MSGBOX_ERROR,
3070 qt_game_title);
3071 LOG_ERROR(Frontend, "Invalid shortcut target");
3072 return;
3073 } 3072 }
3073
3074#if defined(__linux__) 3074#if defined(__linux__)
3075 // Special case for AppImages 3075 // Special case for AppImages
3076 // Warn once if we are making a shortcut to a volatile AppImage 3076 // Warn once if we are making a shortcut to a volatile AppImage