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 {
     }
 
     ResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath,
-                      std::size_t program_index) {
+                      u64 program_id, std::size_t program_index) {
         app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath),
-                                       program_index);
+                                       program_id, program_index);
 
         if (!app_loader) {
             LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -269,11 +269,10 @@ struct System::Impl {
             }
         }
 
-        u64 title_id{0};
-        if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
+        if (app_loader->ReadProgramId(program_id) != Loader::ResultStatus::Success) {
             LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", load_result);
         }
-        perf_stats = std::make_unique<PerfStats>(title_id);
+        perf_stats = std::make_unique<PerfStats>(program_id);
         // Reset counters and set time origin to current frame
         GetAndResetPerfStats();
         perf_stats->BeginSystemFrame();
@@ -459,8 +458,8 @@ void System::Shutdown() {
 }
 
 System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
-                                  std::size_t program_index) {
-    return impl->Load(*this, emu_window, filepath, program_index);
+                                  u64 program_id, std::size_t program_index) {
+    return impl->Load(*this, emu_window, filepath, program_id, program_index);
 }
 
 bool 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:
      * @returns ResultStatus code, indicating if the operation succeeded.
      */
     [[nodiscard]] ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
-                                    std::size_t program_index = 0);
+                                    u64 program_id = 0, std::size_t program_index = 0);
 
     /**
      * 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{
     "logo",
 };
 
-XCI::XCI(VirtualFile file_, std::size_t program_index)
+XCI::XCI(VirtualFile file_, u64 program_id, size_t program_index)
     : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
       partitions(partition_names.size()),
       partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -63,12 +63,12 @@ XCI::XCI(VirtualFile file_, std::size_t program_index)
 
     secure_partition = std::make_shared<NSP>(
         main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
-        program_index);
+        program_id, program_index);
 
     ncas = secure_partition->GetNCAsCollapsed();
     program =
         secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
-    program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
+    program_nca_status = secure_partition->GetProgramStatus();
     if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
         program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
     }
@@ -174,6 +174,10 @@ u64 XCI::GetProgramTitleID() const {
     return secure_partition->GetProgramTitleID();
 }
 
+std::vector<u64> XCI::GetProgramTitleIDs() const {
+    return secure_partition->GetProgramTitleIDs();
+}
+
 u32 XCI::GetSystemUpdateVersion() {
     const auto update = GetPartition(XCIPartition::Update);
     if (update == nullptr) {
@@ -229,9 +233,11 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
 }
 
 std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
-    const auto iter =
-        std::find_if(ncas.begin(), ncas.end(),
-                     [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; });
+    const auto program_id = secure_partition->GetProgramTitleID();
+    const auto iter = std::find_if(
+        ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) {
+            return nca->GetType() == type && nca->GetTitleId() == program_id;
+        });
     return iter == ncas.end() ? nullptr : *iter;
 }
 
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 };
 
 class XCI : public ReadOnlyVfsDirectory {
 public:
-    explicit XCI(VirtualFile file, std::size_t program_index = 0);
+    explicit XCI(VirtualFile file, u64 program_id = 0, size_t program_index = 0);
     ~XCI() override;
 
     Loader::ResultStatus GetStatus() const;
@@ -104,6 +104,7 @@ public:
     VirtualFile GetLogoPartitionRaw() const;
 
     u64 GetProgramTitleID() const;
+    std::vector<u64> GetProgramTitleIDs() const;
     u32 GetSystemUpdateVersion();
     u64 GetSystemUpdateTitleID() const;
 
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 @@
 
 namespace FileSys {
 
-NSP::NSP(VirtualFile file_, std::size_t program_index_)
-    : file(std::move(file_)), program_index(program_index_), status{Loader::ResultStatus::Success},
+NSP::NSP(VirtualFile file_, u64 title_id_, std::size_t program_index_)
+    : file(std::move(file_)), expected_program_id(title_id_),
+      program_index(program_index_), status{Loader::ResultStatus::Success},
       pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
     if (pfs->GetStatus() != Loader::ResultStatus::Success) {
         status = pfs->GetStatus();
@@ -46,60 +47,59 @@ Loader::ResultStatus NSP::GetStatus() const {
     return status;
 }
 
-Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
+Loader::ResultStatus NSP::GetProgramStatus() const {
     if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
         return Loader::ResultStatus::Success;
     }
 
-    const auto iter = program_status.find(title_id);
+    const auto iter = program_status.find(GetProgramTitleID());
     if (iter == program_status.end())
         return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
     return iter->second;
 }
 
-u64 NSP::GetFirstTitleID() const {
-    if (IsExtractedType()) {
-        return GetProgramTitleID();
-    }
-
-    if (program_status.empty())
-        return 0;
-    return program_status.begin()->first;
-}
-
 u64 NSP::GetProgramTitleID() const {
     if (IsExtractedType()) {
-        if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
-            return 0;
-        }
+        return GetExtractedTitleID() + program_index;
+    }
 
-        ProgramMetadata meta;
-        if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
-            return meta.GetTitleID();
-        } else {
-            return 0;
+    auto program_id = expected_program_id;
+    if (program_id == 0) {
+        if (!program_status.empty()) {
+            program_id = program_status.begin()->first;
         }
     }
 
-    const auto out = GetFirstTitleID();
-    if ((out & 0x800) == 0)
-        return out;
+    program_id = program_id + program_index;
+    if (program_status.find(program_id) != program_status.end()) {
+        return program_id;
+    }
 
-    const auto ids = GetTitleIDs();
+    const auto ids = GetProgramTitleIDs();
     const auto iter =
         std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
-    return iter == ids.end() ? out : *iter;
+    return iter == ids.end() ? 0 : *iter;
 }
 
-std::vector<u64> NSP::GetTitleIDs() const {
-    if (IsExtractedType()) {
-        return {GetProgramTitleID()};
+u64 NSP::GetExtractedTitleID() const {
+    if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
+        return 0;
     }
 
-    std::vector<u64> out;
-    out.reserve(ncas.size());
-    for (const auto& kv : ncas)
-        out.push_back(kv.first);
+    ProgramMetadata meta;
+    if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
+        return meta.GetTitleID();
+    } else {
+        return 0;
+    }
+}
+
+std::vector<u64> NSP::GetProgramTitleIDs() const {
+    if (IsExtractedType()) {
+        return {GetExtractedTitleID()};
+    }
+
+    std::vector<u64> out{program_ids.cbegin(), program_ids.cend()};
     return out;
 }
 
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
     if (extracted)
         LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
 
-    const auto title_id_iter = ncas.find(title_id + program_index);
+    const auto title_id_iter = ncas.find(title_id);
     if (title_id_iter == ncas.end())
         return nullptr;
 
@@ -160,7 +160,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
 VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
     if (extracted)
         LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
-    const auto nca = GetNCA(title_id, type);
+    const auto nca = GetNCA(title_id, type, title_type);
     if (nca != nullptr)
         return nca->GetBaseFile();
     return nullptr;
@@ -286,6 +286,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
 
                 if (next_nca->GetType() == NCAContentType::Program) {
                     program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
+                    program_ids.insert(next_nca->GetTitleId() & 0xFFFFFFFFFFFFF000);
                 }
 
                 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 @@
 
 #include <map>
 #include <memory>
+#include <set>
 #include <vector>
 #include "common/common_types.h"
 #include "core/file_sys/vfs.h"
@@ -27,15 +28,15 @@ enum class ContentRecordType : u8;
 
 class NSP : public ReadOnlyVfsDirectory {
 public:
-    explicit NSP(VirtualFile file_, std::size_t program_index_ = 0);
+    explicit NSP(VirtualFile file_, u64 title_id = 0, std::size_t program_index_ = 0);
     ~NSP() override;
 
     Loader::ResultStatus GetStatus() const;
-    Loader::ResultStatus GetProgramStatus(u64 title_id) const;
+    Loader::ResultStatus GetProgramStatus() const;
     // Should only be used when one title id can be assured.
-    u64 GetFirstTitleID() const;
     u64 GetProgramTitleID() const;
-    std::vector<u64> GetTitleIDs() const;
+    u64 GetExtractedTitleID() const;
+    std::vector<u64> GetProgramTitleIDs() const;
 
     bool IsExtractedType() const;
 
@@ -69,6 +70,7 @@ private:
 
     VirtualFile file;
 
+    const u64 expected_program_id;
     const std::size_t program_index;
 
     bool extracted = false;
@@ -78,6 +80,7 @@ private:
     std::shared_ptr<PartitionFilesystem> pfs;
     // Map title id -> {map type -> NCA}
     std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
+    std::set<u64> program_ids;
     std::vector<VirtualFile> ticket_files;
 
     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;
  * @return std::unique_ptr<AppLoader> a pointer to a loader object;  nullptr for unsupported type
  */
 static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
-                                                FileType type, std::size_t program_index) {
+                                                FileType type, u64 program_id,
+                                                std::size_t program_index) {
     switch (type) {
     // Standard ELF file format.
     case FileType::ELF:
@@ -227,7 +228,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
     // NX XCI (nX Card Image) file format.
     case FileType::XCI:
         return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
-                                               system.GetContentProvider(), program_index);
+                                               system.GetContentProvider(), program_id,
+                                               program_index);
 
     // NX NAX (NintendoAesXts) file format.
     case FileType::NAX:
@@ -236,7 +238,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
     // NX NSP (Nintendo Submission Package) file format
     case FileType::NSP:
         return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
-                                               system.GetContentProvider(), program_index);
+                                               system.GetContentProvider(), program_id,
+                                               program_index);
 
     // NX KIP (Kernel Internal Process) file format
     case FileType::KIP:
@@ -252,7 +255,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
 }
 
 std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
-                                     std::size_t program_index) {
+                                     u64 program_id, std::size_t program_index) {
     FileType type = IdentifyFile(file);
     const FileType filename_type = GuessFromFilename(file->GetName());
 
@@ -266,7 +269,7 @@ std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile
 
     LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
 
-    return GetFileLoader(system, std::move(file), type, program_index);
+    return GetFileLoader(system, std::move(file), type, program_id, program_index);
 }
 
 } // 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
@@ -226,6 +226,17 @@ public:
         return ResultStatus::ErrorNotImplemented;
     }
 
+    /**
+     * Get the program ids of the application
+     *
+     * @param[out] out_program_ids Reference to store program ids into
+     *
+     * @return ResultStatus result of function
+     */
+    virtual ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) {
+        return ResultStatus::ErrorNotImplemented;
+    }
+
     /**
      * Get the RomFS of the application
      * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
@@ -324,6 +335,6 @@ protected:
  * @return the best loader for this file.
  */
 std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file,
-                                     std::size_t program_index = 0);
+                                     u64 program_id = 0, std::size_t program_index = 0);
 
 } // 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 {
 
 AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
                              const Service::FileSystem::FileSystemController& fsc,
-                             const FileSys::ContentProvider& content_provider,
+                             const FileSys::ContentProvider& content_provider, u64 program_id,
                              std::size_t program_index)
-    : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_index)),
-      title_id(nsp->GetProgramTitleID()) {
+    : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_id, program_index)) {
 
     if (nsp->GetStatus() != ResultStatus::Success) {
         return;
@@ -46,12 +45,8 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_,
             return pm.ParseControlNCA(*control_nca);
         }();
 
-        if (title_id == 0) {
-            return;
-        }
-
         secondary_loader = std::make_unique<AppLoader_NCA>(
-            nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
+            nsp->GetNCAFile(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Program));
     }
 }
 
@@ -68,10 +63,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) {
         }
 
         // Non-Extracted Type case
+        const auto program_id = nsp.GetProgramTitleID();
         if (!nsp.IsExtractedType() &&
-            nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
-            AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
-                nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) {
+            nsp.GetNCA(program_id, FileSys::ContentRecordType::Program) != nullptr &&
+            AppLoader_NCA::IdentifyType(
+                nsp.GetNCAFile(program_id, FileSys::ContentRecordType::Program)) == FileType::NCA) {
             return FileType::NSP;
         }
     }
@@ -84,6 +80,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
         return {ResultStatus::ErrorAlreadyLoaded, {}};
     }
 
+    const auto title_id = nsp->GetProgramTitleID();
+
     if (!nsp->IsExtractedType() && title_id == 0) {
         return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
     }
@@ -93,7 +91,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::KProcess& process, Core::S
         return {nsp_status, {}};
     }
 
-    const auto nsp_program_status = nsp->GetProgramStatus(title_id);
+    const auto nsp_program_status = nsp->GetProgramStatus();
     if (nsp_program_status != ResultStatus::Success) {
         return {nsp_program_status, {}};
     }
@@ -134,8 +132,8 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
         return ResultStatus::ErrorNoPackedUpdate;
     }
 
-    const auto read =
-        nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program);
+    const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()),
+                                      FileSys::ContentRecordType::Program);
 
     if (read == nullptr) {
         return ResultStatus::ErrorNoPackedUpdate;
@@ -151,11 +149,15 @@ ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) {
 }
 
 ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
-    if (title_id == 0) {
+    out_program_id = nsp->GetProgramTitleID();
+    if (out_program_id == 0) {
         return ResultStatus::ErrorNotInitialized;
     }
+    return ResultStatus::Success;
+}
 
-    out_program_id = title_id;
+ResultStatus AppLoader_NSP::ReadProgramIds(std::vector<u64>& out_program_ids) {
+    out_program_ids = nsp->GetProgramTitleIDs();
     return ResultStatus::Success;
 }
 
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 {
 public:
     explicit AppLoader_NSP(FileSys::VirtualFile file_,
                            const Service::FileSystem::FileSystemController& fsc,
-                           const FileSys::ContentProvider& content_provider,
+                           const FileSys::ContentProvider& content_provider, u64 program_id,
                            std::size_t program_index);
     ~AppLoader_NSP() override;
 
@@ -51,6 +51,7 @@ public:
     u64 ReadRomFSIVFCOffset() const override;
     ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
     ResultStatus ReadProgramId(u64& out_program_id) override;
+    ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadTitle(std::string& title) override;
     ResultStatus ReadControlData(FileSys::NACP& nacp) override;
@@ -67,7 +68,6 @@ private:
 
     FileSys::VirtualFile icon_file;
     std::unique_ptr<FileSys::NACP> nacp_file;
-    u64 title_id;
 };
 
 } // 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 {
 
 AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file_,
                              const Service::FileSystem::FileSystemController& fsc,
-                             const FileSys::ContentProvider& content_provider,
+                             const FileSys::ContentProvider& content_provider, u64 program_id,
                              std::size_t program_index)
-    : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_index)),
+    : AppLoader(file_), xci(std::make_unique<FileSys::XCI>(file_, program_id, program_index)),
       nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
     if (xci->GetStatus() != ResultStatus::Success) {
         return;
@@ -121,6 +121,11 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {
     return nca_loader->ReadProgramId(out_program_id);
 }
 
+ResultStatus AppLoader_XCI::ReadProgramIds(std::vector<u64>& out_program_ids) {
+    out_program_ids = xci->GetProgramTitleIDs();
+    return ResultStatus::Success;
+}
+
 ResultStatus AppLoader_XCI::ReadIcon(std::vector<u8>& buffer) {
     if (icon_file == nullptr) {
         return ResultStatus::ErrorNoControl;
@@ -149,8 +154,9 @@ ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
 }
 
 ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& out_file) {
-    const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(),
-                                                          FileSys::ContentRecordType::HtmlDocument);
+    const auto nca =
+        xci->GetSecurePartitionNSP()->GetNCA(xci->GetSecurePartitionNSP()->GetProgramTitleID(),
+                                             FileSys::ContentRecordType::HtmlDocument);
     if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) {
         return ResultStatus::ErrorXCIMissingPartition;
     }
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 {
 public:
     explicit AppLoader_XCI(FileSys::VirtualFile file_,
                            const Service::FileSystem::FileSystemController& fsc,
-                           const FileSys::ContentProvider& content_provider,
+                           const FileSys::ContentProvider& content_provider, u64 program_id,
                            std::size_t program_index);
     ~AppLoader_XCI() override;
 
@@ -51,6 +51,7 @@ public:
     u64 ReadRomFSIVFCOffset() const override;
     ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) override;
     ResultStatus ReadProgramId(u64& out_program_id) override;
+    ResultStatus ReadProgramIds(std::vector<u64>& out_program_ids) override;
     ResultStatus ReadIcon(std::vector<u8>& buffer) override;
     ResultStatus ReadTitle(std::string& title) override;
     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) {
             return;
         }
 
+        const auto title_id = selected.data(GameListItemPath::ProgramIdRole).toULongLong();
+
         // Users usually want to run a different game after closing one
         search_field->clear();
-        emit GameChosen(file_path);
+        emit GameChosen(file_path, title_id);
         break;
     }
     case GameListItemType::AddDir:
@@ -548,10 +550,10 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
         emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData, path);
     });
     connect(start_game, &QAction::triggered, [this, path]() {
-        emit BootGame(QString::fromStdString(path), 0, StartGameType::Normal);
+        emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Normal);
     });
     connect(start_game_global, &QAction::triggered, [this, path]() {
-        emit BootGame(QString::fromStdString(path), 0, StartGameType::Global);
+        emit BootGame(QString::fromStdString(path), 0, 0, StartGameType::Global);
     });
     connect(open_mod_location, &QAction::triggered, [this, program_id, path]() {
         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:
     static const QStringList supported_file_extensions;
 
 signals:
-    void BootGame(const QString& game_path, std::size_t program_index, StartGameType type);
-    void GameChosen(const QString& game_path);
+    void BootGame(const QString& game_path, u64 program_id, std::size_t program_index,
+                  StartGameType type);
+    void GameChosen(const QString& game_path, const u64 title_id = 0);
     void ShouldCancelWorker();
     void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
                              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
                     }
                 }
             } else {
-                std::vector<u8> icon;
-                [[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
+                std::vector<u64> program_ids;
+                loader->ReadProgramIds(program_ids);
 
-                std::string name = " ";
-                [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
+                if (res2 == Loader::ResultStatus::Success && program_ids.size() > 1 &&
+                    (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) {
+                    for (const auto id : program_ids) {
+                        loader = Loader::GetLoader(system, file, id);
+                        if (!loader) {
+                            continue;
+                        }
 
-                const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
-                                                  system.GetContentProvider()};
+                        std::vector<u8> icon;
+                        [[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
 
-                emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
-                                                  compatibility_list, patch),
-                                parent_dir);
+                        std::string name = " ";
+                        [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
+
+                        const FileSys::PatchManager patch{id, system.GetFileSystemController(),
+                                                          system.GetContentProvider()};
+
+                        emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, id,
+                                                          compatibility_list, patch),
+                                        parent_dir);
+                    }
+                } else {
+                    std::vector<u8> icon;
+                    [[maybe_unused]] const auto res1 = loader->ReadIcon(icon);
+
+                    std::string name = " ";
+                    [[maybe_unused]] const auto res3 = loader->ReadTitle(name);
+
+                    const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
+                                                      system.GetContentProvider()};
+
+                    emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader,
+                                                      program_id, compatibility_list, patch),
+                                    parent_dir);
+                }
             }
         } else if (is_dir) {
             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() {
 #endif
 }
 
-bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
+bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t program_index) {
     // Shutdown previous session if the emu thread is still active...
     if (emu_thread != nullptr)
         ShutdownGame();
@@ -1244,7 +1244,7 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
     });
 
     const Core::System::ResultStatus result{
-        system.Load(*render_window, filename.toStdString(), program_index)};
+        system.Load(*render_window, filename.toStdString(), program_id, program_index)};
 
     const auto drd_callout = (UISettings::values.callout_flags.GetValue() &
                               static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
@@ -1331,7 +1331,8 @@ void GMainWindow::SelectAndSetCurrentUser() {
     Settings::values.current_user = dialog.GetIndex();
 }
 
-void GMainWindow::BootGame(const QString& filename, std::size_t program_index, StartGameType type) {
+void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t program_index,
+                           StartGameType type) {
     LOG_INFO(Frontend, "yuzu starting...");
     StoreRecentFile(filename); // Put the filename on top of the list
 
@@ -1341,7 +1342,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
 
     auto& system = Core::System::GetInstance();
     const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
-    const auto loader = Loader::GetLoader(system, v_file, program_index);
+    const auto loader = Loader::GetLoader(system, v_file, program_id, program_index);
 
     if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success &&
         type == StartGameType::Normal) {
@@ -1369,7 +1370,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S
         SelectAndSetCurrentUser();
     }
 
-    if (!LoadROM(filename, program_index))
+    if (!LoadROM(filename, program_id, program_index))
         return;
 
     // Create and start the emulation thread
@@ -1548,8 +1549,8 @@ void GMainWindow::UpdateRecentFiles() {
     ui.menu_recent_files->setEnabled(num_recent_files != 0);
 }
 
-void GMainWindow::OnGameListLoadFile(QString game_path) {
-    BootGame(game_path);
+void GMainWindow::OnGameListLoadFile(QString game_path, u64 program_id) {
+    BootGame(game_path, program_id);
 }
 
 void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
@@ -2450,7 +2451,7 @@ void GMainWindow::OnLoadComplete() {
 
 void GMainWindow::OnExecuteProgram(std::size_t program_index) {
     ShutdownGame();
-    BootGame(last_filename_booted, program_index);
+    BootGame(last_filename_booted, 0, program_index);
 }
 
 void 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:
     void PreventOSSleep();
     void AllowOSSleep();
 
-    bool LoadROM(const QString& filename, std::size_t program_index);
-    void BootGame(const QString& filename, std::size_t program_index = 0,
+    bool LoadROM(const QString& filename, u64 program_id, std::size_t program_index);
+    void BootGame(const QString& filename, u64 program_id = 0, std::size_t program_index = 0,
                   StartGameType with_config = StartGameType::Normal);
     void ShutdownGame();
 
@@ -238,7 +238,7 @@ private slots:
     void OnOpenQuickstartGuide();
     void OnOpenFAQ();
     /// Called whenever a user selects a game in the game list widget.
-    void OnGameListLoadFile(QString game_path);
+    void OnGameListLoadFile(QString game_path, u64 program_id);
     void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target,
                               const std::string& game_path);
     void OnTransferableShaderCacheOpenFile(u64 program_id);