diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b1734f36..716256cd5 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,9 +47,10 @@ if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE) endif() if(EXISTS ${PROJECT_SOURCE_DIR}/hooks/pre-commit AND NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) - message(STATUS "Copying pre-commit hook") - file(COPY hooks/pre-commit - DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks) + if (EXISTS ${PROJECT_SOURCE_DIR}/.git/) + message(STATUS "Copying pre-commit hook") + file(COPY hooks/pre-commit DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks) + endif() endif() # Sanity check : Check that all submodules are present diff --git a/README.md b/README.md index 0b3914f04..906dbf41f 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1868. +This is the source code for early-access 1869. ## Legal Notice diff --git a/src/common/settings.h b/src/common/settings.h index 14c511033..3b748db0b 100755 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -339,7 +339,7 @@ struct Values { Setting use_nvdec_emulation{true, "use_nvdec_emulation"}; Setting accelerate_astc{true, "accelerate_astc"}; Setting use_vsync{true, "use_vsync"}; - Setting disable_fps_limit{false, "disable_fps_limit"}; + BasicSetting disable_fps_limit{false, "disable_fps_limit"}; Setting shader_backend{ShaderBackend::GLASM, "shader_backend"}; Setting use_asynchronous_shaders{false, "use_asynchronous_shaders"}; Setting use_fast_gpu_time{true, "use_fast_gpu_time"}; diff --git a/src/core/core.cpp b/src/core/core.cpp index 891f1cb49..a17b1ca19 100755 --- 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(title_id); + perf_stats = std::make_unique(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 100755 --- 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 100755 --- 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( main_hfs.GetFile(partition_names[static_cast(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 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>& XCI::GetNCAs() const { } std::shared_ptr XCI::GetNCAByType(NCAContentType type) const { - const auto iter = - std::find_if(ncas.begin(), ncas.end(), - [type](const std::shared_ptr& 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) { + 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 100755 --- 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 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..861edec36 100755 --- 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(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(); + } - 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 NSP::GetTitleIDs() const { - if (IsExtractedType()) { - return {GetProgramTitleID()}; +u64 NSP::GetExtractedTitleID() const { + if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { + return 0; } - std::vector 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 NSP::GetProgramTitleIDs() const { + if (IsExtractedType()) { + return {GetExtractedTitleID()}; + } + + std::vector out{program_ids.cbegin(), program_ids.cend()}; return out; } @@ -142,11 +142,12 @@ NSP::GetNCAs() const { return ncas; } -std::shared_ptr NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const { +std::shared_ptr NSP::GetNCA(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 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; @@ -157,10 +158,10 @@ std::shared_ptr NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType return type_iter->second; } -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) 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 +287,7 @@ void NSP::ReadNCAs(const std::vector& 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 100755 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -6,6 +6,7 @@ #include #include +#include #include #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 GetTitleIDs() const; + u64 GetExtractedTitleID() const; + std::vector 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 pfs; // Map title id -> {map type -> NCA} std::map, std::shared_ptr>> ncas; + std::set program_ids; std::vector ticket_files; Core::Crypto::KeyManager& keys; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 228dc6389..199e69e89 100755 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -206,7 +206,8 @@ AppLoader::~AppLoader() = default; * @return std::unique_ptr a pointer to a loader object; nullptr for unsupported type */ static std::unique_ptr 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 GetFileLoader(Core::System& system, FileSys::V // NX XCI (nX Card Image) file format. case FileType::XCI: return std::make_unique(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 GetFileLoader(Core::System& system, FileSys::V // NX NSP (Nintendo Submission Package) file format case FileType::NSP: return std::make_unique(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 GetFileLoader(Core::System& system, FileSys::V } std::unique_ptr 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 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 100755 --- 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& 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 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 100755 --- 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(file_, program_index)), - title_id(nsp->GetProgramTitleID()) { + : AppLoader(file_), nsp(std::make_unique(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( - 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& 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 100755 --- 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& out_program_ids) override; ResultStatus ReadIcon(std::vector& 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 nacp_file; - u64 title_id; }; } // namespace Loader diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 635d6ae15..269603eef 100755 --- 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(file_, program_index)), + : AppLoader(file_), xci(std::make_unique(file_, program_id, program_index)), nca_loader(std::make_unique(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& out_program_ids) { + out_program_ids = xci->GetProgramTitleIDs(); + return ResultStatus::Success; +} + ResultStatus AppLoader_XCI::ReadIcon(std::vector& 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 100755 --- 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& out_program_ids) override; ResultStatus ReadIcon(std::vector& buffer) override; ResultStatus ReadTitle(std::string& title) override; ResultStatus ReadControlData(FileSys::NACP& control) override; diff --git a/src/shader_recompiler/backend/glasm/reg_alloc.h b/src/shader_recompiler/backend/glasm/reg_alloc.h index 99dea02d6..5a703daf2 100755 --- a/src/shader_recompiler/backend/glasm/reg_alloc.h +++ b/src/shader_recompiler/backend/glasm/reg_alloc.h @@ -297,7 +297,7 @@ struct fmt::formatter { case Shader::Backend::GLASM::Type::U32: break; case Shader::Backend::GLASM::Type::U64: - return format_to(ctx.out(), "{}", Common::BitCast(value.imm_u64)); + return fmt::format_to(ctx.out(), "{}", Common::BitCast(value.imm_u64)); } throw Shader::InvalidArgument("Invalid value type {}", value.type); } diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/emit_context.h index e9b073a26..d9b639d29 100755 --- a/src/shader_recompiler/backend/glsl/emit_context.h +++ b/src/shader_recompiler/backend/glsl/emit_context.h @@ -6,6 +6,8 @@ #include #include +#include + #include #include "shader_recompiler/backend/glsl/var_alloc.h" diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index 2bb1d24a4..83c77967d 100755 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include -#include #include #include #include @@ -144,7 +143,9 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool>(mask); + for (size_t i = 0; i < program.info.passthrough.mask.size(); ++i) { + program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0; + } } break; } diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 4a41db25d..fc7ef8beb 100755 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -24,8 +24,8 @@ #include "video_core/host_shaders/opengl_present_frag.h" #include "video_core/host_shaders/opengl_present_vert.h" #include "video_core/renderer_opengl/gl_rasterizer.h" -#include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/textures/decoders.h" diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 6fda06a7e..a8d04dc61 100755 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -97,14 +97,19 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, std::unique_ptr context_) try - : RendererBase(emu_window, std::move(context_)), telemetry_session(telemetry_session_), - cpu_memory(cpu_memory_), gpu(gpu_), library(OpenLibrary()), + : RendererBase(emu_window, std::move(context_)), + telemetry_session(telemetry_session_), + cpu_memory(cpu_memory_), + gpu(gpu_), + library(OpenLibrary()), instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug.GetValue())), debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr), surface(CreateSurface(instance, render_window)), - device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false), - state_tracker(gpu), scheduler(device, state_tracker), + device(CreateDevice(instance, dld, *surface)), + memory_allocator(device, false), + state_tracker(gpu), + scheduler(device, state_tracker), swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width, render_window.GetFramebufferLayout().height, false), blit_screen(cpu_memory, render_window, device, memory_allocator, swapchain, scheduler, diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index f0ae0b0d6..18482e1d0 100755 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -503,9 +503,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { vertex_attributes.push_back({ .location = static_cast(index), .binding = 0, - .format = type == 1 ? VK_FORMAT_R32_SFLOAT - : type == 2 ? VK_FORMAT_R32_SINT - : VK_FORMAT_R32_UINT, + .format = type == 1 ? VK_FORMAT_R32_SFLOAT + : type == 2 ? VK_FORMAT_R32_SINT : VK_FORMAT_R32_UINT, .offset = 0, }); } diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 92a82e1cd..ab7204802 100755 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -839,7 +839,6 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.use_nvdec_emulation); ReadGlobalSetting(Settings::values.accelerate_astc); ReadGlobalSetting(Settings::values.use_vsync); - ReadGlobalSetting(Settings::values.disable_fps_limit); ReadGlobalSetting(Settings::values.shader_backend); ReadGlobalSetting(Settings::values.use_asynchronous_shaders); ReadGlobalSetting(Settings::values.use_fast_gpu_time); @@ -1371,7 +1370,6 @@ void Config::SaveRendererValues() { WriteGlobalSetting(Settings::values.use_nvdec_emulation); WriteGlobalSetting(Settings::values.accelerate_astc); WriteGlobalSetting(Settings::values.use_vsync); - WriteGlobalSetting(Settings::values.disable_fps_limit); WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()), static_cast(Settings::values.shader_backend.GetValue(global)), static_cast(Settings::values.shader_backend.GetDefault()), diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index e570fd2de..38276feb1 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -27,7 +27,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); - ui->disable_fps_limit->setChecked(Settings::values.disable_fps_limit.GetValue()); ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue()); ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); @@ -57,8 +56,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, ui->anisotropic_filtering_combobox); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync); - ConfigurationShared::ApplyPerGameSetting(&Settings::values.disable_fps_limit, - ui->disable_fps_limit, disable_fps_limit); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, ui->use_asynchronous_shaders, use_asynchronous_shaders); @@ -99,7 +96,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); - ui->disable_fps_limit->setEnabled(Settings::values.disable_fps_limit.UsingGlobal()); ui->use_asynchronous_shaders->setEnabled( Settings::values.use_asynchronous_shaders.UsingGlobal()); ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); @@ -111,8 +107,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { } ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync); - ConfigurationShared::SetColoredTristate(ui->disable_fps_limit, - Settings::values.disable_fps_limit, disable_fps_limit); ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders, Settings::values.use_asynchronous_shaders, use_asynchronous_shaders); diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index b4e5d7bd4..7356e6916 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -35,7 +35,6 @@ private: std::unique_ptr ui; ConfigurationShared::CheckState use_vsync; - ConfigurationShared::CheckState disable_fps_limit; ConfigurationShared::CheckState use_asynchronous_shaders; ConfigurationShared::CheckState use_fast_gpu_time; ConfigurationShared::CheckState use_caches_gc; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 38fd9b37e..772e5fed3 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -76,24 +76,6 @@ - - - - true - - - - <html><head/><body> - <p>Presents guest frames as they become available, disabling the FPS limit in most titles.</p> - <p>NOTE: Will cause instabilities.</p> - </body></html> - - - - Disable framerate limit (experimental) - - - diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 218b4782b..f746bd85d 100755 --- 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: @@ -518,9 +520,11 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QMenu* remove_menu = context_menu.addMenu(tr("Remove")); QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update")); QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC")); - QAction* remove_shader_cache = remove_menu->addAction(tr("Remove Shader Cache")); QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration")); + QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Shader Cache")); + QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Shader Cache")); remove_menu->addSeparator(); + QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Shader Caches")); QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS")); QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS")); @@ -538,6 +542,8 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri open_transferable_shader_cache->setVisible(program_id != 0); remove_update->setVisible(program_id != 0); remove_dlc->setVisible(program_id != 0); + remove_gl_shader_cache->setVisible(program_id != 0); + remove_vk_shader_cache->setVisible(program_id != 0); remove_shader_cache->setVisible(program_id != 0); remove_all_content->setVisible(program_id != 0); auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); @@ -548,10 +554,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); @@ -567,8 +573,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(remove_dlc, &QAction::triggered, [this, program_id]() { emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent); }); + connect(remove_gl_shader_cache, &QAction::triggered, [this, program_id, path]() { + emit RemoveFileRequested(program_id, GameListRemoveTarget::GlShaderCache, path); + }); + connect(remove_vk_shader_cache, &QAction::triggered, [this, program_id, path]() { + emit RemoveFileRequested(program_id, GameListRemoveTarget::VkShaderCache, path); + }); connect(remove_shader_cache, &QAction::triggered, [this, program_id, path]() { - emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache, path); + emit RemoveFileRequested(program_id, GameListRemoveTarget::AllShaderCache, path); }); connect(remove_custom_config, &QAction::triggered, [this, program_id, path]() { emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration, path); diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 50402da51..10339dcca 100755 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -41,7 +41,9 @@ enum class GameListOpenTarget { }; enum class GameListRemoveTarget { - ShaderCache, + GlShaderCache, + VkShaderCache, + AllShaderCache, CustomConfiguration, }; @@ -88,8 +90,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 100755 --- 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 icon; - [[maybe_unused]] const auto res1 = loader->ReadIcon(icon); + std::vector 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 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 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 6bf6643da..9cff0fdf9 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1208,7 +1208,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(); @@ -1231,7 +1231,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(CalloutFlag::DRDDeprecation)) == 0; @@ -1318,7 +1318,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 @@ -1328,7 +1329,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) { @@ -1342,6 +1343,9 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index, S ConfigureVibration::SetAllVibrationDevices(); + // Disable fps limit toggle when booting a new title + Settings::values.disable_fps_limit.SetValue(false); + // Save configurations UpdateUISettings(); game_list->SaveInterfaceLayout(); @@ -1353,7 +1357,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 @@ -1526,8 +1530,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, @@ -1632,9 +1636,9 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)}; - if (!Common::FS::Exists(shader_cache_folder_path)) { + if (!Common::FS::CreateDirs(shader_cache_folder_path)) { QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), - tr("A shader cache for this title does not exist.")); + tr("Filed to create the shader cache directory for this title.")); return; } const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)}; @@ -1782,8 +1786,12 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ const std::string& game_path) { const QString question = [this, target] { switch (target) { - case GameListRemoveTarget::ShaderCache: - return tr("Delete Transferable Shader Cache?"); + case GameListRemoveTarget::GlShaderCache: + return tr("Delete OpenGL Transferable Shader Cache?"); + case GameListRemoveTarget::VkShaderCache: + return tr("Delete Vulkan Transferable Shader Cache?"); + case GameListRemoveTarget::AllShaderCache: + return tr("Delete All Transferable Shader Caches?"); case GameListRemoveTarget::CustomConfiguration: return tr("Remove Custom Game Configuration?"); default: @@ -1797,8 +1805,12 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ } switch (target) { - case GameListRemoveTarget::ShaderCache: - RemoveTransferableShaderCache(program_id); + case GameListRemoveTarget::GlShaderCache: + case GameListRemoveTarget::VkShaderCache: + RemoveTransferableShaderCache(program_id, target); + break; + case GameListRemoveTarget::AllShaderCache: + RemoveAllTransferableShaderCaches(program_id); break; case GameListRemoveTarget::CustomConfiguration: RemoveCustomConfiguration(program_id, game_path); @@ -1806,18 +1818,27 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ } } -void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { +void GMainWindow::RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target) { + const auto target_file_name = [target] { + switch (target) { + case GameListRemoveTarget::GlShaderCache: + return "opengl.bin"; + case GameListRemoveTarget::VkShaderCache: + return "vulkan.bin"; + default: + return ""; + } + }(); const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); - const auto transferable_shader_cache_file_path = - shader_cache_dir / "opengl" / "transferable" / fmt::format("{:016X}.bin", program_id); + const auto shader_cache_folder_path = shader_cache_dir / fmt::format("{:016x}", program_id); + const auto target_file = shader_cache_folder_path / target_file_name; - if (!Common::FS::Exists(transferable_shader_cache_file_path)) { + if (!Common::FS::Exists(target_file)) { QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), tr("A shader cache for this title does not exist.")); return; } - - if (Common::FS::RemoveFile(transferable_shader_cache_file_path)) { + if (Common::FS::RemoveFile(target_file)) { QMessageBox::information(this, tr("Successfully Removed"), tr("Successfully removed the transferable shader cache.")); } else { @@ -1826,6 +1847,24 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { } } +void GMainWindow::RemoveAllTransferableShaderCaches(u64 program_id) { + const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); + const auto program_shader_cache_dir = shader_cache_dir / fmt::format("{:016x}", program_id); + + if (!Common::FS::Exists(program_shader_cache_dir)) { + QMessageBox::warning(this, tr("Error Removing Transferable Shader Caches"), + tr("A shader cache for this title does not exist.")); + return; + } + if (Common::FS::RemoveDirRecursively(program_shader_cache_dir)) { + QMessageBox::information(this, tr("Successfully Removed"), + tr("Successfully removed the transferable shader caches.")); + } else { + QMessageBox::warning(this, tr("Error Removing Transferable Shader Caches"), + tr("Failed to remove the transferable shader cache directory.")); + } +} + void GMainWindow::RemoveCustomConfiguration(u64 program_id, const std::string& game_path) { const auto file_path = std::filesystem::path(Common::FS::ToU8String(game_path)); const auto config_file_name = @@ -2408,7 +2447,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) { @@ -2876,7 +2915,12 @@ void GMainWindow::UpdateStatusBar() { } else { emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); } - game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); + if (Settings::values.disable_fps_limit) { + game_fps_label->setText( + tr("Game: %1 FPS (Limit off)").arg(results.average_game_fps, 0, 'f', 0)); + } else { + game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); + } emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); emu_speed_label->setVisible(!Settings::values.use_multi_core.GetValue()); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 15e4deab9..38e66ccd0 100755 --- 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); @@ -282,7 +282,8 @@ private: void RemoveBaseContent(u64 program_id, const QString& entry_type); void RemoveUpdateContent(u64 program_id, const QString& entry_type); void RemoveAddOnContent(u64 program_id, const QString& entry_type); - void RemoveTransferableShaderCache(u64 program_id); + void RemoveTransferableShaderCache(u64 program_id, GameListRemoveTarget target); + void RemoveAllTransferableShaderCaches(u64 program_id); void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); std::optional SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); InstallResult InstallNSPXCI(const QString& filename);