summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/core.cpp29
-rw-r--r--src/core/core.h22
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h2
-rw-r--r--src/core/file_sys/submission_package.cpp6
-rw-r--r--src/core/file_sys/submission_package.h4
-rw-r--r--src/core/hle/service/am/am.cpp34
-rw-r--r--src/core/hle/service/am/am.h3
-rw-r--r--src/core/loader/loader.cpp12
-rw-r--r--src/core/loader/loader.h4
-rw-r--r--src/core/loader/nsp.cpp5
-rw-r--r--src/core/loader/nsp.h3
-rw-r--r--src/core/loader/xci.cpp5
-rw-r--r--src/core/loader/xci.h3
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/main.cpp24
-rw-r--r--src/yuzu/main.h8
18 files changed, 146 insertions, 36 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 1aa477a29..7ca3652af 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -145,7 +145,7 @@ struct System::Impl {
145 } 145 }
146 146
147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { 147 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
148 LOG_DEBUG(HW_Memory, "initialized OK"); 148 LOG_DEBUG(Core, "initialized OK");
149 149
150 device_memory = std::make_unique<Core::DeviceMemory>(); 150 device_memory = std::make_unique<Core::DeviceMemory>();
151 151
@@ -208,9 +208,11 @@ struct System::Impl {
208 return ResultStatus::Success; 208 return ResultStatus::Success;
209 } 209 }
210 210
211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, 211 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
212 const std::string& filepath) { 212 std::size_t program_index) {
213 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath)); 213 app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
214 program_index);
215
214 if (!app_loader) { 216 if (!app_loader) {
215 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); 217 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
216 return ResultStatus::ErrorGetLoader; 218 return ResultStatus::ErrorGetLoader;
@@ -416,6 +418,8 @@ struct System::Impl {
416 bool is_multicore{}; 418 bool is_multicore{};
417 bool is_async_gpu{}; 419 bool is_async_gpu{};
418 420
421 ExecuteProgramCallback execute_program_callback;
422
419 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{}; 423 std::array<u64, Core::Hardware::NUM_CPU_CORES> dynarmic_ticks{};
420 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{}; 424 std::array<MicroProfileToken, Core::Hardware::NUM_CPU_CORES> microprofile_dynarmic{};
421}; 425};
@@ -451,8 +455,9 @@ void System::Shutdown() {
451 impl->Shutdown(); 455 impl->Shutdown();
452} 456}
453 457
454System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 458System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
455 return impl->Load(*this, emu_window, filepath); 459 std::size_t program_index) {
460 return impl->Load(*this, emu_window, filepath, program_index);
456} 461}
457 462
458bool System::IsPoweredOn() const { 463bool System::IsPoweredOn() const {
@@ -789,4 +794,16 @@ bool System::IsMulticore() const {
789 return impl->is_multicore; 794 return impl->is_multicore;
790} 795}
791 796
797void System::RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback) {
798 impl->execute_program_callback = std::move(callback);
799}
800
801void System::ExecuteProgram(std::size_t program_index) {
802 if (impl->execute_program_callback) {
803 impl->execute_program_callback(program_index);
804 } else {
805 LOG_CRITICAL(Core, "execute_program_callback must be initialized by the frontend");
806 }
807}
808
792} // namespace Core 809} // namespace Core
diff --git a/src/core/core.h b/src/core/core.h
index cd155625c..f642befc0 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8#include <functional>
8#include <memory> 9#include <memory>
9#include <string> 10#include <string>
10#include <vector> 11#include <vector>
@@ -173,9 +174,11 @@ public:
173 * @param emu_window Reference to the host-system window used for video output and keyboard 174 * @param emu_window Reference to the host-system window used for video output and keyboard
174 * input. 175 * input.
175 * @param filepath String path to the executable application to load on the host file system. 176 * @param filepath String path to the executable application to load on the host file system.
177 * @param program_index Specifies the index within the container of the program to launch.
176 * @returns ResultStatus code, indicating if the operation succeeded. 178 * @returns ResultStatus code, indicating if the operation succeeded.
177 */ 179 */
178 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath); 180 [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
181 std::size_t program_index = 0);
179 182
180 /** 183 /**
181 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an 184 * Indicates if the emulated system is powered on (all subsystems initialized and able to run an
@@ -385,6 +388,23 @@ public:
385 /// Tells if system is running on multicore. 388 /// Tells if system is running on multicore.
386 [[nodiscard]] bool IsMulticore() const; 389 [[nodiscard]] bool IsMulticore() const;
387 390
391 /// Type used for the frontend to designate a callback for System to re-launch the application
392 /// using a specified program index.
393 using ExecuteProgramCallback = std::function<void(std::size_t)>;
394
395 /**
396 * Registers a callback from the frontend for System to re-launch the application using a
397 * specified program index.
398 * @param callback Callback from the frontend to relaunch the application.
399 */
400 void RegisterExecuteProgramCallback(ExecuteProgramCallback&& callback);
401
402 /**
403 * Instructs the frontend to re-launch the application using the specified program_index.
404 * @param program_index Specifies the index within the application of the program to launch.
405 */
406 void ExecuteProgram(std::size_t program_index);
407
388private: 408private:
389 System(); 409 System();
390 410
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 956da68f7..8dee5590b 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_) 32XCI::XCI(VirtualFile file_, std::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()} {
@@ -62,7 +62,8 @@ XCI::XCI(VirtualFile file_)
62 } 62 }
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 67
67 ncas = secure_partition->GetNCAsCollapsed(); 68 ncas = secure_partition->GetNCAsCollapsed();
68 program = 69 program =
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 2d0a0f285..4960e90fe 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); 81 explicit XCI(VirtualFile file, std::size_t program_index = 0);
82 ~XCI() override; 82 ~XCI() override;
83 83
84 Loader::ResultStatus GetStatus() const; 84 Loader::ResultStatus GetStatus() const;
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 90641d23b..c05735ddd 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,8 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_) 23NSP::NSP(VirtualFile file_, std::size_t program_index)
24 : file(std::move(file_)), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { 25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 26 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 27 status = pfs->GetStatus();
@@ -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); 149 const auto title_id_iter = ncas.find(title_id + program_index);
150 if (title_id_iter == ncas.end()) 150 if (title_id_iter == ncas.end())
151 return nullptr; 151 return nullptr;
152 152
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index c70a11b5b..54581a6f3 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -27,7 +27,7 @@ enum class ContentRecordType : u8;
27 27
28class NSP : public ReadOnlyVfsDirectory { 28class NSP : public ReadOnlyVfsDirectory {
29public: 29public:
30 explicit NSP(VirtualFile file); 30 explicit NSP(VirtualFile file, std::size_t program_index = 0);
31 ~NSP() override; 31 ~NSP() override;
32 32
33 Loader::ResultStatus GetStatus() const; 33 Loader::ResultStatus GetStatus() const;
@@ -69,6 +69,8 @@ private:
69 69
70 VirtualFile file; 70 VirtualFile file;
71 71
72 const std::size_t program_index;
73
72 bool extracted = false; 74 bool extracted = false;
73 Loader::ResultStatus status; 75 Loader::ResultStatus status;
74 std::map<u64, Loader::ResultStatus> program_status; 76 std::map<u64, Loader::ResultStatus> program_status;
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 55e428456..703a9b234 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1188,9 +1188,9 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1188 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"}, 1188 {102, &IApplicationFunctions::SetApplicationCopyrightVisibility, "SetApplicationCopyrightVisibility"},
1189 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"}, 1189 {110, &IApplicationFunctions::QueryApplicationPlayStatistics, "QueryApplicationPlayStatistics"},
1190 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"}, 1190 {111, &IApplicationFunctions::QueryApplicationPlayStatisticsByUid, "QueryApplicationPlayStatisticsByUid"},
1191 {120, nullptr, "ExecuteProgram"}, 1191 {120, &IApplicationFunctions::ExecuteProgram, "ExecuteProgram"},
1192 {121, nullptr, "ClearUserChannel"}, 1192 {121, &IApplicationFunctions::ClearUserChannel, "ClearUserChannel"},
1193 {122, nullptr, "UnpopToUserChannel"}, 1193 {122, &IApplicationFunctions::UnpopToUserChannel, "UnpopToUserChannel"},
1194 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, 1194 {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
1195 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, 1195 {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
1196 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, 1196 {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
@@ -1561,6 +1561,34 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
1561 rb.Push<u32>(0); 1561 rb.Push<u32>(0);
1562} 1562}
1563 1563
1564void IApplicationFunctions::ExecuteProgram(Kernel::HLERequestContext& ctx) {
1565 LOG_WARNING(Service_AM, "(STUBBED) called");
1566
1567 IPC::RequestParser rp{ctx};
1568 [[maybe_unused]] const auto unk_1 = rp.Pop<u32>();
1569 [[maybe_unused]] const auto unk_2 = rp.Pop<u32>();
1570 const auto program_index = rp.Pop<u64>();
1571
1572 IPC::ResponseBuilder rb{ctx, 2};
1573 rb.Push(RESULT_SUCCESS);
1574
1575 system.ExecuteProgram(program_index);
1576}
1577
1578void IApplicationFunctions::ClearUserChannel(Kernel::HLERequestContext& ctx) {
1579 LOG_WARNING(Service_AM, "(STUBBED) called");
1580
1581 IPC::ResponseBuilder rb{ctx, 2};
1582 rb.Push(RESULT_SUCCESS);
1583}
1584
1585void IApplicationFunctions::UnpopToUserChannel(Kernel::HLERequestContext& ctx) {
1586 LOG_WARNING(Service_AM, "(STUBBED) called");
1587
1588 IPC::ResponseBuilder rb{ctx, 2};
1589 rb.Push(RESULT_SUCCESS);
1590}
1591
1564void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) { 1592void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
1565 LOG_WARNING(Service_AM, "(STUBBED) called"); 1593 LOG_WARNING(Service_AM, "(STUBBED) called");
1566 1594
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 09c2d05bc..af97c303a 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -287,6 +287,9 @@ private:
287 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); 287 void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
288 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); 288 void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
289 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); 289 void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
290 void ExecuteProgram(Kernel::HLERequestContext& ctx);
291 void ClearUserChannel(Kernel::HLERequestContext& ctx);
292 void UnpopToUserChannel(Kernel::HLERequestContext& ctx);
290 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); 293 void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
291 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 294 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
292 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); 295 void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index deffe7379..d91c15561 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -198,10 +198,11 @@ AppLoader::~AppLoader() = default;
198 * @param system The system context to use. 198 * @param system The system context to use.
199 * @param file The file to retrieve the loader for 199 * @param file The file to retrieve the loader for
200 * @param type The file type 200 * @param type The file type
201 * @param program_index Specifies the index within the container of the program to launch.
201 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type 202 * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
202 */ 203 */
203static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file, 204static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
204 FileType type) { 205 FileType type, std::size_t program_index) {
205 switch (type) { 206 switch (type) {
206 // Standard ELF file format. 207 // Standard ELF file format.
207 case FileType::ELF: 208 case FileType::ELF:
@@ -222,7 +223,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
222 // NX XCI (nX Card Image) file format. 223 // NX XCI (nX Card Image) file format.
223 case FileType::XCI: 224 case FileType::XCI:
224 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(), 225 return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
225 system.GetContentProvider()); 226 system.GetContentProvider(), program_index);
226 227
227 // NX NAX (NintendoAesXts) file format. 228 // NX NAX (NintendoAesXts) file format.
228 case FileType::NAX: 229 case FileType::NAX:
@@ -231,7 +232,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
231 // NX NSP (Nintendo Submission Package) file format 232 // NX NSP (Nintendo Submission Package) file format
232 case FileType::NSP: 233 case FileType::NSP:
233 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(), 234 return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
234 system.GetContentProvider()); 235 system.GetContentProvider(), program_index);
235 236
236 // NX KIP (Kernel Internal Process) file format 237 // NX KIP (Kernel Internal Process) file format
237 case FileType::KIP: 238 case FileType::KIP:
@@ -246,7 +247,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
246 } 247 }
247} 248}
248 249
249std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file) { 250std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
251 std::size_t program_index) {
250 FileType type = IdentifyFile(file); 252 FileType type = IdentifyFile(file);
251 const FileType filename_type = GuessFromFilename(file->GetName()); 253 const FileType filename_type = GuessFromFilename(file->GetName());
252 254
@@ -260,7 +262,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
260 262
261 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); 263 LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
262 264
263 return GetFileLoader(system, std::move(file), type); 265 return GetFileLoader(system, std::move(file), type, program_index);
264} 266}
265 267
266} // namespace Loader 268} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 8dc2d7615..36e79e71d 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -293,9 +293,11 @@ protected:
293 * 293 *
294 * @param system The system context. 294 * @param system The system context.
295 * @param file The bootable file. 295 * @param file The bootable file.
296 * @param program_index Specifies the index within the container of the program to launch.
296 * 297 *
297 * @return the best loader for this file. 298 * @return the best loader for this file.
298 */ 299 */
299std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file); 300std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
301 std::size_t program_index = 0);
300 302
301} // namespace Loader 303} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index e821937fd..928f64c8c 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -23,8 +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,
27 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)), 27 std::size_t program_index)
28 : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file, program_index)),
28 title_id(nsp->GetProgramTitleID()) { 29 title_id(nsp->GetProgramTitleID()) {
29 30
30 if (nsp->GetStatus() != ResultStatus::Success) { 31 if (nsp->GetStatus() != ResultStatus::Success) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 36e8e3533..f0518ac47 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -28,7 +28,8 @@ 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,
32 std::size_t program_index);
32 ~AppLoader_NSP() override; 33 ~AppLoader_NSP() override;
33 34
34 /** 35 /**
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 536e721fc..aaa250cea 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -22,8 +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,
26 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)), 26 std::size_t program_index)
27 : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file, program_index)),
27 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) { 28 nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
28 if (xci->GetStatus() != ResultStatus::Success) { 29 if (xci->GetStatus() != ResultStatus::Success) {
29 return; 30 return;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 6dc1f9243..764dc8328 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -28,7 +28,8 @@ 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,
32 std::size_t program_index);
32 ~AppLoader_XCI() override; 33 ~AppLoader_XCI() override;
33 34
34 /** 35 /**
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index d62b0efc2..f0338cf7a 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -302,6 +302,12 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
302 this->setMouseTracking(true); 302 this->setMouseTracking(true);
303 303
304 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); 304 connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
305 connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram,
306 Qt::QueuedConnection);
307}
308
309void GRenderWindow::ExecuteProgram(std::size_t program_index) {
310 emit ExecuteProgramSignal(program_index);
305} 311}
306 312
307GRenderWindow::~GRenderWindow() { 313GRenderWindow::~GRenderWindow() {
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index ca35cf831..503b4f89e 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -166,6 +166,12 @@ public:
166 166
167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; 167 std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
168 168
169 /**
170 * Instructs the window to re-launch the application using the specified program_index.
171 * @param program_index Specifies the index within the application of the program to launch.
172 */
173 void ExecuteProgram(std::size_t program_index);
174
169public slots: 175public slots:
170 void OnEmulationStarting(EmuThread* emu_thread); 176 void OnEmulationStarting(EmuThread* emu_thread);
171 void OnEmulationStopping(); 177 void OnEmulationStopping();
@@ -175,6 +181,7 @@ signals:
175 /// Emitted when the window is closed 181 /// Emitted when the window is closed
176 void Closed(); 182 void Closed();
177 void FirstFrameDisplayed(); 183 void FirstFrameDisplayed();
184 void ExecuteProgramSignal(std::size_t program_index);
178 185
179private: 186private:
180 void TouchBeginEvent(const QTouchEvent* event); 187 void TouchBeginEvent(const QTouchEvent* event);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index e704cc656..805619ccf 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -978,7 +978,7 @@ void GMainWindow::AllowOSSleep() {
978#endif 978#endif
979} 979}
980 980
981bool GMainWindow::LoadROM(const QString& filename) { 981bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
982 // Shutdown previous session if the emu thread is still active... 982 // Shutdown previous session if the emu thread is still active...
983 if (emu_thread != nullptr) 983 if (emu_thread != nullptr)
984 ShutdownGame(); 984 ShutdownGame();
@@ -1003,7 +1003,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
1003 1003
1004 system.RegisterHostThread(); 1004 system.RegisterHostThread();
1005 1005
1006 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 1006 const Core::System::ResultStatus result{
1007 system.Load(*render_window, filename.toStdString(), program_index)};
1007 1008
1008 const auto drd_callout = 1009 const auto drd_callout =
1009 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0; 1010 (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1085,14 +1086,18 @@ void GMainWindow::SelectAndSetCurrentUser() {
1085 Settings::values.current_user = dialog.GetIndex(); 1086 Settings::values.current_user = dialog.GetIndex();
1086} 1087}
1087 1088
1088void GMainWindow::BootGame(const QString& filename) { 1089void GMainWindow::BootGame(const QString& filename, std::size_t program_index) {
1089 LOG_INFO(Frontend, "yuzu starting..."); 1090 LOG_INFO(Frontend, "yuzu starting...");
1090 StoreRecentFile(filename); // Put the filename on top of the list 1091 StoreRecentFile(filename); // Put the filename on top of the list
1091 1092
1092 u64 title_id{0}; 1093 u64 title_id{0};
1094
1095 last_filename_booted = filename;
1096
1093 auto& system = Core::System::GetInstance(); 1097 auto& system = Core::System::GetInstance();
1094 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData()); 1098 const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
1095 const auto loader = Loader::GetLoader(system, v_file); 1099 const auto loader = Loader::GetLoader(system, v_file, program_index);
1100
1096 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) { 1101 if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
1097 // Load per game settings 1102 // Load per game settings
1098 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig); 1103 Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
@@ -1106,7 +1111,7 @@ void GMainWindow::BootGame(const QString& filename) {
1106 SelectAndSetCurrentUser(); 1111 SelectAndSetCurrentUser();
1107 } 1112 }
1108 1113
1109 if (!LoadROM(filename)) 1114 if (!LoadROM(filename, program_index))
1110 return; 1115 return;
1111 1116
1112 // Create and start the emulation thread 1117 // Create and start the emulation thread
@@ -1114,6 +1119,10 @@ void GMainWindow::BootGame(const QString& filename) {
1114 emit EmulationStarting(emu_thread.get()); 1119 emit EmulationStarting(emu_thread.get());
1115 emu_thread->start(); 1120 emu_thread->start();
1116 1121
1122 // Register an ExecuteProgram callback such that Core can execute a sub-program
1123 system.RegisterExecuteProgramCallback(
1124 [this](std::size_t program_index) { render_window->ExecuteProgram(program_index); });
1125
1117 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); 1126 connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
1118 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views 1127 // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
1119 // before the CPU continues 1128 // before the CPU continues
@@ -2136,6 +2145,11 @@ void GMainWindow::OnLoadComplete() {
2136 loading_screen->OnLoadComplete(); 2145 loading_screen->OnLoadComplete();
2137} 2146}
2138 2147
2148void GMainWindow::OnExecuteProgram(std::size_t program_index) {
2149 ShutdownGame();
2150 BootGame(last_filename_booted, program_index);
2151}
2152
2139void GMainWindow::ErrorDisplayDisplayError(QString body) { 2153void GMainWindow::ErrorDisplayDisplayError(QString body) {
2140 QMessageBox::critical(this, tr("Error Display"), body); 2154 QMessageBox::critical(this, tr("Error Display"), body);
2141 emit ErrorDisplayFinished(); 2155 emit ErrorDisplayFinished();
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index b380a66f3..6242341d1 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -131,6 +131,7 @@ signals:
131 131
132public slots: 132public slots:
133 void OnLoadComplete(); 133 void OnLoadComplete();
134 void OnExecuteProgram(std::size_t program_index);
134 void ControllerSelectorReconfigureControllers( 135 void ControllerSelectorReconfigureControllers(
135 const Core::Frontend::ControllerParameters& parameters); 136 const Core::Frontend::ControllerParameters& parameters);
136 void ErrorDisplayDisplayError(QString body); 137 void ErrorDisplayDisplayError(QString body);
@@ -154,8 +155,8 @@ private:
154 void PreventOSSleep(); 155 void PreventOSSleep();
155 void AllowOSSleep(); 156 void AllowOSSleep();
156 157
157 bool LoadROM(const QString& filename); 158 bool LoadROM(const QString& filename, std::size_t program_index);
158 void BootGame(const QString& filename); 159 void BootGame(const QString& filename, std::size_t program_index = 0);
159 void ShutdownGame(); 160 void ShutdownGame();
160 161
161 void ShowTelemetryCallout(); 162 void ShowTelemetryCallout();
@@ -317,6 +318,9 @@ private:
317 // Install progress dialog 318 // Install progress dialog
318 QProgressDialog* install_progress; 319 QProgressDialog* install_progress;
319 320
321 // Last game booted, used for multi-process apps
322 QString last_filename_booted;
323
320protected: 324protected:
321 void dropEvent(QDropEvent* event) override; 325 void dropEvent(QDropEvent* event) override;
322 void dragEnterEvent(QDragEnterEvent* event) override; 326 void dragEnterEvent(QDragEnterEvent* event) override;