summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/core.cpp13
-rw-r--r--src/core/core.h2
-rw-r--r--src/core/file_sys/card_image.cpp18
-rw-r--r--src/core/file_sys/card_image.h3
-rw-r--r--src/core/file_sys/submission_package.cpp71
-rw-r--r--src/core/file_sys/submission_package.h11
-rw-r--r--src/core/loader/loader.cpp13
-rw-r--r--src/core/loader/loader.h13
-rw-r--r--src/core/loader/nsp.cpp34
-rw-r--r--src/core/loader/nsp.h4
-rw-r--r--src/core/loader/xci.cpp14
-rw-r--r--src/core/loader/xci.h3
-rw-r--r--src/yuzu/game_list.cpp8
-rw-r--r--src/yuzu/game_list.h5
-rw-r--r--src/yuzu/game_list_worker.cpp44
-rw-r--r--src/yuzu/main.cpp17
-rw-r--r--src/yuzu/main.h6
17 files changed, 171 insertions, 108 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 406320ed6..15226cf41 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -216,9 +216,9 @@ struct System::Impl {
216 } 216 }
217 217
218 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, 218 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
219 std::size_t program_index) { 219 u64 program_id, std::size_t program_index) {
220 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath), 220 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
221 program_index); 221 program_id, program_index);
222 222
223 if (!app_loader) { 223 if (!app_loader) {
224 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 224 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -269,11 +269,10 @@ struct System::Impl {
269 } 269 }
270 } 270 }
271 271
272 u64 title_id{0}; 272 if (app_loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) {
273 if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
274 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result); 273 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result);
275 } 274 }
276 perf_stats = std::make_unique<PerfStats>(title_id); 275 perf_stats = std::make_unique<PerfStats>(program_id);
277 // Reset counters and set time origin to current frame 276 // Reset counters and set time origin to current frame
278 GetAndResetPerfStats(); 277 GetAndResetPerfStats();
279 perf_stats->BeginSystemFrame(); 278 perf_stats->BeginSystemFrame();
@@ -459,8 +458,8 @@ void System::Shutdown() {
459} 458}
460 459
461System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath, 460System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
462 std::size_t program_index) { 461 u64 program_id, std::size_t program_index) {
463 return impl->Load(*this, emu_window, filepath, program_index); 462 return impl->Load(*this, emu_window, filepath, program_id, program_index);
464} 463}
465 464
466bool System::IsPoweredOn() const { 465bool System::IsPoweredOn() const {
diff --git a/src/core/core.h b/src/core/core.h
index 8b93ba998..b93c32e60 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -175,7 +175,7 @@ public:
175 * @returns ResultStatus code, indicating if the operation succeeded. 175 * @returns ResultStatus code, indicating if the operation succeeded.
176 */ 176 */
177 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath, 177 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
178 std::size_t program_index = 0); 178 u64 program_id = 0, std::size_t program_index = 0);
179 179
180 /** 180 /**
181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index db2f6a955..755d3303e 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
29 "logo", 29 "logo",
30}; 30};
31 31
32XCI::XCI(VirtualFile file_, std::size_t program_index) 32XCI::XCI(VirtualFile file_, u64 program_id, size_t program_index)
33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, 33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
34 partitions(partition_names.size()), 34 partitions(partition_names.size()),
35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { 35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -63,12 +63,12 @@ XCI::XCI(VirtualFile file_, std::size_t program_index)
63 63
64 secure_partition = std::make_shared<NSP>( 64 secure_partition = std::make_shared<NSP>(
65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]), 65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
66 program_index); 66 program_id, program_index);
67 67
68 ncas = secure_partition->GetNCAsCollapsed(); 68 ncas = secure_partition->GetNCAsCollapsed();
69 program = 69 program =
70 secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); 70 secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
71 program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID()); 71 program_nca_status = secure_partition->GetProgramStatus();
72 if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) { 72 if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
73 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; 73 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
74 } 74 }
@@ -174,6 +174,10 @@ u64 XCI::GetProgramTitleID() const {
174 return secure_partition->GetProgramTitleID(); 174 return secure_partition->GetProgramTitleID();
175} 175}
176 176
177std::vector<u64> XCI::GetProgramTitleIDs() const {
178 return secure_partition->GetProgramTitleIDs();
179}
180
177u32 XCI::GetSystemUpdateVersion() { 181u32 XCI::GetSystemUpdateVersion() {
178 const auto update = GetPartition(XCIPartition::Update); 182 const auto update = GetPartition(XCIPartition::Update);
179 if (update == nullptr) { 183 if (update == nullptr) {
@@ -229,9 +233,11 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
229} 233}
230 234
231std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { 235std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
232 const auto iter = 236 const auto program_id = secure_partition->GetProgramTitleID();
233 std::find_if(ncas.begin(), ncas.end(), 237 const auto iter = std::find_if(
234 [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; }); 238 ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) {
239 return nca->GetType() == type && nca->GetTitleId() == program_id;
240 });
235 return iter == ncas.end() ? nullptr : *iter; 241 return iter == ncas.end() ? nullptr : *iter;
236} 242}
237 243
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 4960e90fe..0fd9fa87c 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
78 78
79class XCI : public ReadOnlyVfsDirectory { 79class XCI : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit XCI(VirtualFile file, std::size_t program_index = 0); 81 explicit XCI(VirtualFile file, u64 program_id = 0, size_t program_index = 0);
82 ~XCI() override; 82 ~XCI() override;
83 83
84 Loader::ResultStatus GetStatus() const; 84 Loader::ResultStatus GetStatus() const;
@@ -104,6 +104,7 @@ public:
104 VirtualFile GetLogoPartitionRaw() const; 104 VirtualFile GetLogoPartitionRaw() const;
105 105
106 u64 GetProgramTitleID() const; 106 u64 GetProgramTitleID() const;
107 std::vector<u64> GetProgramTitleIDs() const;
107 u32 GetSystemUpdateVersion(); 108 u32 GetSystemUpdateVersion();
108 u64 GetSystemUpdateTitleID() const; 109 u64 GetSystemUpdateTitleID() const;
109 110
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index d51d469e3..f192dffa5 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,9 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_, std::size_t program_index_) 23NSP::NSP(VirtualFile file_, u64 title_id_, std::size_t program_index_)
24 : file(std::move(file_)), program_index(program_index_), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), expected_program_id(title_id_),
25 program_index(program_index_), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { 26 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 27 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 28 status = pfs->GetStatus();
@@ -46,60 +47,59 @@ Loader::ResultStatus NSP::GetStatus() const {
46 return status; 47 return status;
47} 48}
48 49
49Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { 50Loader::ResultStatus NSP::GetProgramStatus() const {
50 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) { 51 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
51 return Loader::ResultStatus::Success; 52 return Loader::ResultStatus::Success;
52 } 53 }
53 54
54 const auto iter = program_status.find(title_id); 55 const auto iter = program_status.find(GetProgramTitleID());
55 if (iter == program_status.end()) 56 if (iter == program_status.end())
56 return Loader::ResultStatus::ErrorNSPMissingProgramNCA; 57 return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
57 return iter->second; 58 return iter->second;
58} 59}
59 60
60u64 NSP::GetFirstTitleID() const {
61 if (IsExtractedType()) {
62 return GetProgramTitleID();
63 }
64
65 if (program_status.empty())
66 return 0;
67 return program_status.begin()->first;
68}
69
70u64 NSP::GetProgramTitleID() const { 61u64 NSP::GetProgramTitleID() const {
71 if (IsExtractedType()) { 62 if (IsExtractedType()) {
72 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { 63 return GetExtractedTitleID() + program_index;
73 return 0; 64 }
74 }
75 65
76 ProgramMetadata meta; 66 auto program_id = expected_program_id;
77 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) { 67 if (program_id == 0) {
78 return meta.GetTitleID(); 68 if (!program_status.empty()) {
79 } else { 69 program_id = program_status.begin()->first;
80 return 0;
81 } 70 }
82 } 71 }
83 72
84 const auto out = GetFirstTitleID(); 73 program_id = program_id + program_index;
85 if ((out & 0x800) == 0) 74 if (program_status.find(program_id) != program_status.end()) {
86 return out; 75 return program_id;
76 }
87 77
88 const auto ids = GetTitleIDs(); 78 const auto ids = GetProgramTitleIDs();
89 const auto iter = 79 const auto iter =
90 std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; }); 80 std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
91 return iter == ids.end() ? out : *iter; 81 return iter == ids.end() ? 0 : *iter;
82}
83
84u64 NSP::GetExtractedTitleID() const {
85 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
86 return 0;
87 }
88
89 ProgramMetadata meta;
90 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
91 return meta.GetTitleID();
92 } else {
93 return 0;
94 }
92} 95}
93 96
94std::vector<u64> NSP::GetTitleIDs() const { 97std::vector<u64> NSP::GetProgramTitleIDs() const {
95 if (IsExtractedType()) { 98 if (IsExtractedType()) {
96 return {GetProgramTitleID()}; 99 return {GetExtractedTitleID()};
97 } 100 }
98 101
99 std::vector<u64> out; 102 std::vector<u64> out{program_ids.cbegin(), program_ids.cend()};
100 out.reserve(ncas.size());
101 for (const auto& kv : ncas)
102 out.push_back(kv.first);
103 return out; 103 return out;
104} 104}
105 105
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
146 if (extracted) 146 if (extracted)
147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
148 148
149 const auto title_id_iter = ncas.find(title_id + program_index); 149 const auto title_id_iter = ncas.find(title_id);
150 if (title_id_iter == ncas.end()) 150 if (title_id_iter == ncas.end())
151 return nullptr; 151 return nullptr;
152 152
@@ -160,7 +160,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
160VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const { 160VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
161 if (extracted) 161 if (extracted)
162 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 162 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
163 const auto nca = GetNCA(title_id, type); 163 const auto nca = GetNCA(title_id, type, title_type);
164 if (nca != nullptr) 164 if (nca != nullptr)
165 return nca->GetBaseFile(); 165 return nca->GetBaseFile();
166 return nullptr; 166 return nullptr;
@@ -286,6 +286,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
286 286
287 if (next_nca->GetType() == NCAContentType::Program) { 287 if (next_nca->GetType() == NCAContentType::Program) {
288 program_status[next_nca->GetTitleId()] = next_nca->GetStatus(); 288 program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
289 program_ids.insert(next_nca->GetTitleId() & 0xFFFFFFFFFFFFF000);
289 } 290 }
290 291
291 if (next_nca->GetStatus() != Loader::ResultStatus::Success && 292 if (next_nca->GetStatus() != Loader::ResultStatus::Success &&
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index ecb3b6f15..030f36c09 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -6,6 +6,7 @@
6 6
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9#include <set>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
@@ -27,15 +28,15 @@ enum class ContentRecordType : u8;
27 28
28class NSP : public ReadOnlyVfsDirectory { 29class NSP : public ReadOnlyVfsDirectory {
29public: 30public:
30 explicit NSP(VirtualFile file_, std::size_t program_index_ = 0); 31 explicit NSP(VirtualFile file_, u64 title_id = 0, std::size_t program_index_ = 0);
31 ~NSP() override; 32 ~NSP() override;
32 33
33 Loader::ResultStatus GetStatus() const; 34 Loader::ResultStatus GetStatus() const;
34 Loader::ResultStatus GetProgramStatus(u64 title_id) const; 35 Loader::ResultStatus GetProgramStatus() const;
35 // Should only be used when one title id can be assured. 36 // Should only be used when one title id can be assured.
36 u64 GetFirstTitleID() const;
37 u64 GetProgramTitleID() const; 37 u64 GetProgramTitleID() const;
38 std::vector<u64> GetTitleIDs() const; 38 u64 GetExtractedTitleID() const;
39 std::vector<u64> GetProgramTitleIDs() const;
39 40
40 bool IsExtractedType() const; 41 bool IsExtractedType() const;
41 42
@@ -69,6 +70,7 @@ private:
69 70
70 VirtualFile file; 71 VirtualFile file;
71 72
73 const u64 expected_program_id;
72 const std::size_t program_index; 74 const std::size_t program_index;
73 75
74 bool extracted = false; 76 bool extracted = false;
@@ -78,6 +80,7 @@ private:
78 std::shared_ptr<PartitionFilesystem> pfs; 80 std::shared_ptr<PartitionFilesystem> pfs;
79 // Map title id -> {map type -> NCA} 81 // Map title id -> {map type -> NCA}
80 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; 82 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
83 std::set<u64> program_ids;
81 std::vector<VirtualFile> ticket_files; 84 std::vector<VirtualFile> ticket_files;
82 85
83 Core::Crypto::KeyManager& keys; 86 Core::Crypto::KeyManager& keys;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 228dc6389..199e69e89 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -206,7 +206,8 @@ AppLoader::~AppLoader() = default;
206 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type 206 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
207 */ 207 */
208static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file, 208static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
209 FileType type, std::size_t program_index) { 209 FileType type, u64 program_id,
210 std::size_t program_index) {
210 switch (type) { 211 switch (type) {
211 // Standard ELF file format. 212 // Standard ELF file format.
212 case FileType::ELF: 213 case FileType::ELF:
@@ -227,7 +228,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
227 // NX XCI (nX Card Image) file format. 228 // NX XCI (nX Card Image) file format.
228 case FileType::XCI: 229 case FileType::XCI:
229 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(), 230 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
230 system.GetContentProvider(), program_index); 231 system.GetContentProvider(), program_id,
232 program_index);
231 233
232 // NX NAX (NintendoAesXts) file format. 234 // NX NAX (NintendoAesXts) file format.
233 case FileType::NAX: 235 case FileType::NAX:
@@ -236,7 +238,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
236 // NX NSP (Nintendo Submission Package) file format 238 // NX NSP (Nintendo Submission Package) file format
237 case FileType::NSP: 239 case FileType::NSP:
238 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(), 240 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
239 system.GetContentProvider(), program_index); 241 system.GetContentProvider(), program_id,
242 program_index);
240 243
241 // NX KIP (Kernel Internal Process) file format 244 // NX KIP (Kernel Internal Process) file format
242 case FileType::KIP: 245 case FileType::KIP:
@@ -252,7 +255,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
252} 255}
253 256
254std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, 257std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
255 std::size_t program_index) { 258 u64 program_id, std::size_t program_index) {
256 FileType type = IdentifyFile(file); 259 FileType type = IdentifyFile(file);
257 const FileType filename_type = GuessFromFilename(file->GetName()); 260 const FileType filename_type = GuessFromFilename(file->GetName());
258 261
@@ -266,7 +269,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
266 269
267 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); 270 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
268 271
269 return GetFileLoader(system, std::move(file), type, program_index); 272 return GetFileLoader(system, std::move(file), type, program_id, program_index);
270} 273}
271 274
272} // namespace Loader 275} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index edc8bb257..7b1bac3f7 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -227,6 +227,17 @@ public:
227 } 227 }
228 228
229 /** 229 /**
230 * Get the program ids of the application
231 *
232 * @param[out] out_program_ids Reference to store program ids into
233 *
234 * @return ResultStatus result of function
235 */
236 virtual ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) {
237 return ResultStatus::ErrorNotImplemented;
238 }
239
240 /**
230 * Get the RomFS of the application 241 * Get the RomFS of the application
231 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer 242 * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
232 * 243 *
@@ -324,6 +335,6 @@ protected:
324 * @return the best loader for this file. 335 * @return the best loader for this file.
325 */ 336 */
326std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file, 337std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
327 std::size_t program_index = 0); 338 u64 program_id = 0, std::size_t program_index = 0);
328 339
329} // namespace Loader 340} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index d815a7cd3..8b167ad3c 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -23,10 +23,9 @@ namespace Loader {
23 23
24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, 24AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
25 const Service::FileSystem::FileSystemController& fsc, 25 const Service::FileSystem::FileSystemController& fsc,
26 const FileSys::ContentProvider& content_provider, 26 const FileSys::ContentProvider& content_provider, u64 program_id,
27 std::size_t program_index) 27 std::size_t program_index)
28 : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_index)), 28 : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_id, program_index)) {
29 title_id(nsp->GetProgramTitleID()) {
30 29
31 if (nsp->GetStatus() != ResultStatus::Success) { 30 if (nsp->GetStatus() != ResultStatus::Success) {
32 return; 31 return;
@@ -46,12 +45,8 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
46 return pm.ParseControlNCA(*control_nca); 45 return pm.ParseControlNCA(*control_nca);
47 }(); 46 }();
48 47
49 if (title_id == 0) {
50 return;
51 }
52
53 secondary_loader = std::make_unique<AppLoader_NCA>( 48 secondary_loader = std::make_unique<AppLoader_NCA>(
54 nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); 49 nsp->GetNCAFile(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Program));
55 } 50 }
56} 51}
57 52
@@ -68,10 +63,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) {
68 } 63 }
69 64
70 // Non-Extracted Type case 65 // Non-Extracted Type case
66 const auto program_id = nsp.GetProgramTitleID();
71 if (!nsp.IsExtractedType() && 67 if (!nsp.IsExtractedType() &&
72 nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && 68 nsp.GetNCA(program_id, FileSys::ContentRecordType::Program) != nullptr &&
73 AppLoader_NCA::IdentifyType(nsp.GetNCAFile( 69 AppLoader_NCA::IdentifyType(
74 nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) { 70 nsp.GetNCAFile(program_id, FileSys::ContentRecordType::Program)) == FileType::NCA) {
75 return FileType::NSP; 71 return FileType::NSP;
76 } 72 }
77 } 73 }
@@ -84,6 +80,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
84 return {ResultStatus::ErrorAlreadyLoaded, {}}; 80 return {ResultStatus::ErrorAlreadyLoaded, {}};
85 } 81 }
86 82
83 const auto title_id = nsp->GetProgramTitleID();
84
87 if (!nsp->IsExtractedType() && title_id == 0) { 85 if (!nsp->IsExtractedType() && title_id == 0) {
88 return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; 86 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
89 } 87 }
@@ -93,7 +91,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
93 return {nsp_status, {}}; 91 return {nsp_status, {}};
94 } 92 }
95 93
96 const auto nsp_program_status = nsp->GetProgramStatus(title_id); 94 const auto nsp_program_status = nsp->GetProgramStatus();
97 if (nsp_program_status != ResultStatus::Success) { 95 if (nsp_program_status != ResultStatus::Success) {
98 return {nsp_program_status, {}}; 96 return {nsp_program_status, {}};
99 } 97 }
@@ -134,8 +132,8 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
134 return ResultStatus::ErrorNoPackedUpdate; 132 return ResultStatus::ErrorNoPackedUpdate;
135 } 133 }
136 134
137 const auto read = 135 const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()),
138 nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program); 136 FileSys::ContentRecordType::Program);
139 137
140 if (read == nullptr) { 138 if (read == nullptr) {
141 return ResultStatus::ErrorNoPackedUpdate; 139 return ResultStatus::ErrorNoPackedUpdate;
@@ -151,11 +149,15 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
151} 149}
152 150
153ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { 151ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
154 if (title_id == 0) { 152 out_program_id = nsp->GetProgramTitleID();
153 if (out_program_id == 0) {
155 return ResultStatus::ErrorNotInitialized; 154 return ResultStatus::ErrorNotInitialized;
156 } 155 }
156 return ResultStatus::Success;
157}
157 158
158 out_program_id = title_id; 159ResultStatus AppLoader_NSP::ReadProgramIds(std::vector<u64>& out_program_ids) {
160 out_program_ids = nsp->GetProgramTitleIDs();
159 return ResultStatus::Success; 161 return ResultStatus::Success;
160} 162}
161 163
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 644c0ff58..50406a92e 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -28,7 +28,7 @@ class AppLoader_NSP final : public AppLoader {
28public: 28public:
29 explicit AppLoader_NSP(FileSys::VirtualFile file_, 29 explicit AppLoader_NSP(FileSys::VirtualFile file_,
30 const Service::FileSystem::FileSystemController& fsc, 30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider, 31 const FileSys::ContentProvider& content_provider, u64 program_id,
32 std::size_t program_index); 32 std::size_t program_index);
33 ~AppLoader_NSP() override; 33 ~AppLoader_NSP() override;
34 34
@@ -51,6 +51,7 @@ public:
51 u64 ReadRomFSIVFCOffset() const override; 51 u64 ReadRomFSIVFCOffset() const override;
52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; 52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
53 ResultStatus ReadProgramId(u64& out_program_id) override; 53 ResultStatus ReadProgramId(u64& out_program_id) override;
54 ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
54 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 55 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
55 ResultStatus ReadTitle(std::string& title) override; 56 ResultStatus ReadTitle(std::string& title) override;
56 ResultStatus ReadControlData(FileSys::NACP& nacp) override; 57 ResultStatus ReadControlData(FileSys::NACP& nacp) override;
@@ -67,7 +68,6 @@ private:
67 68
68 FileSys::VirtualFile icon_file; 69 FileSys::VirtualFile icon_file;
69 std::unique_ptr<FileSys::NACP> nacp_file; 70 std::unique_ptr<FileSys::NACP> nacp_file;
70 u64 title_id;
71}; 71};
72 72
73} // namespace Loader 73} // namespace Loader
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 635d6ae15..269603eef 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -22,9 +22,9 @@ namespace Loader {
22 22
23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_, 23AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_,
24 const Service::FileSystem::FileSystemController& fsc, 24 const Service::FileSystem::FileSystemController& fsc,
25 const FileSys::ContentProvider& content_provider, 25 const FileSys::ContentProvider& content_provider, u64 program_id,
26 std::size_t program_index) 26 std::size_t program_index)
27 : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_index)), 27 : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_id, program_index)),
28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { 28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
29 if (xci->GetStatus() != ResultStatus::Success) { 29 if (xci->GetStatus() != ResultStatus::Success) {
30 return; 30 return;
@@ -121,6 +121,11 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
121 return nca_loader->ReadProgramId(out_program_id); 121 return nca_loader->ReadProgramId(out_program_id);
122} 122}
123 123
124ResultStatus AppLoader_XCI::ReadProgramIds(std::vector<u64>& out_program_ids) {
125 out_program_ids = xci->GetProgramTitleIDs();
126 return ResultStatus::Success;
127}
128
124ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) { 129ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
125 if (icon_file == nullptr) { 130 if (icon_file == nullptr) {
126 return ResultStatus::ErrorNoControl; 131 return ResultStatus::ErrorNoControl;
@@ -149,8 +154,9 @@ ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
149} 154}
150 155
151ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) { 156ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) {
152 const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(), 157 const auto nca =
153 FileSys::ContentRecordType::HtmlDocument); 158 xci->GetSecurePartitionNSP()->GetNCA(xci->GetSecurePartitionNSP()->GetProgramTitleID(),
159 FileSys::ContentRecordType::HtmlDocument);
154 if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) { 160 if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) {
155 return ResultStatus::ErrorXCIMissingPartition; 161 return ResultStatus::ErrorXCIMissingPartition;
156 } 162 }
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 708155c30..30caaf90e 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -28,7 +28,7 @@ class AppLoader_XCI final : public AppLoader {
28public: 28public:
29 explicit AppLoader_XCI(FileSys::VirtualFile file_, 29 explicit AppLoader_XCI(FileSys::VirtualFile file_,
30 const Service::FileSystem::FileSystemController& fsc, 30 const Service::FileSystem::FileSystemController& fsc,
31 const FileSys::ContentProvider& content_provider, 31 const FileSys::ContentProvider& content_provider, u64 program_id,
32 std::size_t program_index); 32 std::size_t program_index);
33 ~AppLoader_XCI() override; 33 ~AppLoader_XCI() override;
34 34
@@ -51,6 +51,7 @@ public:
51 u64 ReadRomFSIVFCOffset() const override; 51 u64 ReadRomFSIVFCOffset() const override;
52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override; 52 ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
53 ResultStatus ReadProgramId(u64& out_program_id) override; 53 ResultStatus ReadProgramId(u64& out_program_id) override;
54 ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
54 ResultStatus ReadIcon(std::vector<u8>& buffer) override; 55 ResultStatus ReadIcon(std::vector<u8>& buffer) override;
55 ResultStatus ReadTitle(std::string& title) override; 56 ResultStatus ReadTitle(std::string& title) override;
56 ResultStatus ReadControlData(FileSys::NACP& control) override; 57 ResultStatus ReadControlData(FileSys::NACP& control) override;
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 218b4782b..76c063c97 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -404,9 +404,11 @@ void GameList::ValidateEntry(const QModelIndex& item) {
404 return; 404 return;
405 } 405 }
406 406
407 const auto title_id = selected.data(GameListItemPath::ProgramIdRole).toULongLong();
408
407 // Users usually want to run a different game after closing one 409 // Users usually want to run a different game after closing one
408 search_field->clear(); 410 search_field->clear();
409 emit GameChosen(file_path); 411 emit GameChosen(file_path, title_id);
410 break; 412 break;
411 } 413 }
412 case GameListItemType::AddDir: 414 case GameListItemType::AddDir:
@@ -548,10 +550,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
548 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path); 550 emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
549 }); 551 });
550 connect(start_game, &QAction::triggered, [this, path]() { 552 connect(start_game, &QAction::triggered, [this, path]() {
551 emit BootGame(QString::fromStdString(path), 0, StartGameType::Normal); 553 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal);
552 }); 554 });
553 connect(start_game_global, &QAction::triggered, [this, path]() { 555 connect(start_game_global, &QAction::triggered, [this, path]() {
554 emit BootGame(QString::fromStdString(path), 0, StartGameType::Global); 556 emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global);
555 }); 557 });
556 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { 558 connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
557 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path); 559 emit OpenFolderRequested(program_id, GameListOpenTarget::ModData, path);
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 50402da51..c9a9f4654 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -88,8 +88,9 @@ public:
88 static const QStringList supported_file_extensions; 88 static const QStringList supported_file_extensions;
89 89
90signals: 90signals:
91 void BootGame(const QString& game_path, std::size_t program_index, StartGameType type); 91 void BootGame(const QString& game_path, u64 program_id, std::size_t program_index,
92 void GameChosen(const QString& game_path); 92 StartGameType type);
93 void GameChosen(const QString& game_path, const u64 title_id = 0);
93 void ShouldCancelWorker(); 94 void ShouldCancelWorker();
94 void OpenFolderRequested(u64 program_id, GameListOpenTarget target, 95 void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
95 const std::string& game_path); 96 const std::string& game_path);
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 33cc90d5a..2d5492157 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -336,18 +336,44 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
336 } 336 }
337 } 337 }
338 } else { 338 } else {
339 std::vector<u8> icon; 339 std::vector<u64> program_ids;
340 [[maybe_unused]] const auto res1 = loader->ReadIcon(icon); 340 loader->ReadProgramIds(program_ids);
341
342 if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
343 (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
344 for (const auto id : program_ids) {
345 loader = Loader::GetLoader(system, file, id);
346 if (!loader) {
347 continue;
348 }
349
350 std::vector<u8> icon;
351 [[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
341 352
342 std::string name = " "; 353 std::string name = " ";
343 [[maybe_unused]] const auto res3 = loader->ReadTitle(name); 354 [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
344 355
345 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(), 356 const FileSys::PatchManager patch{id, system.GetFileSystemController(),
346 system.GetContentProvider()}; 357 system.GetContentProvider()};
358
359 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, id,
360 compatibility_list, patch),
361 parent_dir);
362 }
363 } else {
364 std::vector<u8> icon;
365 [[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
347 366
348 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id, 367 std::string name = " ";
349 compatibility_list, patch), 368 [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
350 parent_dir); 369
370 const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
371 system.GetContentProvider()};
372
373 emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader,
374 program_id, compatibility_list, patch),
375 parent_dir);
376 }
351 } 377 }
352 } else if (is_dir) { 378 } else if (is_dir) {
353 watch_list.append(QString::fromStdString(physical_name)); 379 watch_list.append(QString::fromStdString(physical_name));
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b7fd33ae7..03a909d17 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1221,7 +1221,7 @@ void GMainWindow::AllowOSSleep() {
1221#endif 1221#endif
1222} 1222}
1223 1223
1224bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { 1224bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
1225 // Shutdown previous session if the emu thread is still active... 1225 // Shutdown previous session if the emu thread is still active...
1226 if (emu_thread != nullptr) 1226 if (emu_thread != nullptr)
1227 ShutdownGame(); 1227 ShutdownGame();
@@ -1244,7 +1244,7 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
1244 }); 1244 });
1245 1245
1246 const Core::System::ResultStatus result{ 1246 const Core::System::ResultStatus result{
1247 system.Load(*render_window, filename.toStdString(), program_index)}; 1247 system.Load(*render_window, filename.toStdString(), program_id, program_index)};
1248 1248
1249 const auto drd_callout = (UISettings::values.callout_flags.GetValue() & 1249 const auto drd_callout = (UISettings::values.callout_flags.GetValue() &
1250 static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; 1250 static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1331,7 +1331,8 @@ void GMainWindow::SelectAndSetCurrentUser() {
1331 Settings::values.current_user = dialog.GetIndex(); 1331 Settings::values.current_user = dialog.GetIndex();
1332} 1332}
1333 1333
1334void GMainWindow::BootGame(const QString& filename, std::size_t program_index, StartGameType type) { 1334void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
1335 StartGameType type) {
1335 LOG_INFO(Frontend, "yuzu starting..."); 1336 LOG_INFO(Frontend, "yuzu starting...");
1336 StoreRecentFile(filename); // Put the filename on top of the list 1337 StoreRecentFile(filename); // Put the filename on top of the list
1337 1338
@@ -1341,7 +1342,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1341 1342
1342 auto& system = Core::System::GetInstance(); 1343 auto& system = Core::System::GetInstance();
1343 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1344 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1344 const auto loader = Loader::GetLoader(system, v_file, program_index); 1345 const auto loader = Loader::GetLoader(system, v_file, program_id, program_index);
1345 1346
1346 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success && 1347 if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
1347 type == StartGameType::Normal) { 1348 type == StartGameType::Normal) {
@@ -1369,7 +1370,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
1369 SelectAndSetCurrentUser(); 1370 SelectAndSetCurrentUser();
1370 } 1371 }
1371 1372
1372 if (!LoadROM(filename, program_index)) 1373 if (!LoadROM(filename, program_id, program_index))
1373 return; 1374 return;
1374 1375
1375 // Create and start the emulation thread 1376 // Create and start the emulation thread
@@ -1548,8 +1549,8 @@ void GMainWindow::UpdateRecentFiles() {
1548 ui.menu_recent_files->setEnabled(num_recent_files != 0); 1549 ui.menu_recent_files->setEnabled(num_recent_files != 0);
1549} 1550}
1550 1551
1551void GMainWindow::OnGameListLoadFile(QString game_path) { 1552void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) {
1552 BootGame(game_path); 1553 BootGame(game_path, program_id);
1553} 1554}
1554 1555
1555void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, 1556void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
@@ -2450,7 +2451,7 @@ void GMainWindow::OnLoadComplete() {
2450 2451
2451void GMainWindow::OnExecuteProgram(std::size_t program_index) { 2452void GMainWindow::OnExecuteProgram(std::size_t program_index) {
2452 ShutdownGame(); 2453 ShutdownGame();
2453 BootGame(last_filename_booted, program_index); 2454 BootGame(last_filename_booted, 0, program_index);
2454} 2455}
2455 2456
2456void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) { 2457void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 45c8310e1..a50e5b9fe 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -186,8 +186,8 @@ private:
186 void PreventOSSleep(); 186 void PreventOSSleep();
187 void AllowOSSleep(); 187 void AllowOSSleep();
188 188
189 bool LoadROM(const QString& filename, std::size_t program_index); 189 bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index);
190 void BootGame(const QString& filename, std::size_t program_index = 0, 190 void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0,
191 StartGameType with_config = StartGameType::Normal); 191 StartGameType with_config = StartGameType::Normal);
192 void ShutdownGame(); 192 void ShutdownGame();
193 193
@@ -238,7 +238,7 @@ private slots:
238 void OnOpenQuickstartGuide(); 238 void OnOpenQuickstartGuide();
239 void OnOpenFAQ(); 239 void OnOpenFAQ();
240 /// Called whenever a user selects a game in the game list widget. 240 /// Called whenever a user selects a game in the game list widget.
241 void OnGameListLoadFile(QString game_path); 241 void OnGameListLoadFile(QString game_path, u64 program_id);
242 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, 242 void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
243 const std::string& game_path); 243 const std::string& game_path);
244 void OnTransferableShaderCacheOpenFile(u64 program_id); 244 void OnTransferableShaderCacheOpenFile(u64 program_id);