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