diff options
60 files changed, 973 insertions, 497 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e977e8a8..54dca3302 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -60,6 +60,7 @@ else() -Wmissing-declarations -Wno-attributes -Wno-unused-parameter + -fconcepts ) if (ARCHITECTURE_x86_64) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index d120c8d3d..78c3bfb3b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -110,6 +110,7 @@ add_library(common STATIC common_funcs.h common_paths.h common_types.h + concepts.h dynamic_library.cpp dynamic_library.h fiber.cpp diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 076752d3b..3c593d5f6 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -35,6 +35,7 @@ #define KEYS_DIR "keys" #define LOAD_DIR "load" #define DUMP_DIR "dump" +#define SCREENSHOTS_DIR "screenshots" #define SHADER_DIR "shader" #define LOG_DIR "log" diff --git a/src/common/concepts.h b/src/common/concepts.h new file mode 100644 index 000000000..db5fb373d --- /dev/null +++ b/src/common/concepts.h @@ -0,0 +1,32 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +namespace Common { + +#include <type_traits> + +// Check if type is like an STL container +template <typename T> +concept IsSTLContainer = requires(T t) { + typename T::value_type; + typename T::iterator; + typename T::const_iterator; + // TODO(ogniK): Replace below is std::same_as<void> when MSVC supports it. + t.begin(); + t.end(); + t.cbegin(); + t.cend(); + t.data(); + t.size(); +}; + +// Check if type T is derived from T2 +template <typename T, typename T2> +concept IsBaseOf = requires { + std::is_base_of_v<T, T2>; +}; + +} // namespace Common diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 45b750e1e..4ede9f72c 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -695,6 +695,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); + paths.emplace(UserPath::ScreenshotsDir, user_path + SCREENSHOTS_DIR DIR_SEP); paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP); paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); diff --git a/src/common/file_util.h b/src/common/file_util.h index f7a0c33fa..187b93161 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h @@ -32,6 +32,7 @@ enum class UserPath { SDMCDir, LoadDir, DumpDir, + ScreenshotsDir, ShaderDir, SysDataDir, UserDir, diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 32afcf3ae..358943429 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -52,15 +52,15 @@ void CpuManager::Shutdown() { } std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { - return std::function<void(void*)>(GuestThreadFunction); + return GuestThreadFunction; } std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { - return std::function<void(void*)>(IdleThreadFunction); + return IdleThreadFunction; } std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { - return std::function<void(void*)>(SuspendThreadFunction); + return SuspendThreadFunction; } void CpuManager::GuestThreadFunction(void* cpu_manager_) { diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h index c95205668..2b4f21073 100644 --- a/src/core/file_sys/mode.h +++ b/src/core/file_sys/mode.h @@ -4,6 +4,7 @@ #pragma once +#include "common/common_funcs.h" #include "common/common_types.h" namespace FileSys { @@ -11,13 +12,11 @@ namespace FileSys { enum class Mode : u32 { Read = 1, Write = 2, - ReadWrite = 3, + ReadWrite = Read | Write, Append = 4, - WriteAppend = 6, + WriteAppend = Write | Append, }; -inline u32 operator&(Mode lhs, Mode rhs) { - return static_cast<u32>(lhs) & static_cast<u32>(rhs); -} +DECLARE_ENUM_FLAG_OPERATORS(Mode) } // namespace FileSys diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c47ff863e..729dbb5f4 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -288,8 +288,8 @@ std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( } Core::Memory::TextCheatParser parser; - return parser.Parse( - system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size())); + return parser.Parse(system, + std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); } } // Anonymous namespace diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 37351c561..f831487dd 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -344,15 +344,18 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { - if (map.find(title_id) == map.end()) - return {}; - - const auto& cnmt = map.at(title_id); + const auto cmnt_iter = map.find(title_id); + if (cmnt_iter == map.cend()) { + return std::nullopt; + } - const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(), + const auto& cnmt = cmnt_iter->second; + const auto& content_records = cnmt.GetContentRecords(); + const auto iter = std::find_if(content_records.cbegin(), content_records.cend(), [type](const ContentRecord& rec) { return rec.type == type; }); - if (iter == cnmt.GetContentRecords().end()) - return {}; + if (iter == content_records.cend()) { + return std::nullopt; + } return std::make_optional(iter->nca_id); } @@ -467,14 +470,16 @@ VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType ty std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { const auto meta_iter = meta.find(title_id); - if (meta_iter != meta.end()) + if (meta_iter != meta.cend()) { return meta_iter->second.GetTitleVersion(); + } const auto yuzu_meta_iter = yuzu_meta.find(title_id); - if (yuzu_meta_iter != yuzu_meta.end()) + if (yuzu_meta_iter != yuzu_meta.cend()) { return yuzu_meta_iter->second.GetTitleVersion(); + } - return {}; + return std::nullopt; } VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { @@ -547,56 +552,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); } -bool RegisteredCache::RemoveExistingEntry(u64 title_id) { - const auto delete_nca = [this](const NcaID& id) { - const auto path = GetRelativePathFromNcaID(id, false, true, false); - - if (dir->GetFileRelative(path) == nullptr) { - return false; - } - - Core::Crypto::SHA256Hash hash{}; - mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0); - const auto dirname = fmt::format("000000{:02X}", hash[0]); - - const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); - - const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); - - return res; - }; - - // If an entry exists in the registered cache, remove it - if (HasEntry(title_id, ContentRecordType::Meta)) { - LOG_INFO(Loader, - "Previously installed entry (v{}) for title_id={:016X} detected! " - "Attempting to remove...", - GetEntryVersion(title_id).value_or(0), title_id); - // Get all the ncas associated with the current CNMT and delete them - const auto meta_old_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); - const auto program_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); - const auto data_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); - const auto control_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); - const auto html_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); - const auto legal_id = - GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); - - delete_nca(meta_old_id); - delete_nca(program_id); - delete_nca(data_id); - delete_nca(control_id); - delete_nca(html_id); - delete_nca(legal_id); - return true; - } - return false; -} - InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, const VfsCopyFunction& copy) { const auto ncas = nsp.GetNCAsCollapsed(); @@ -692,6 +647,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); } +bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { + const auto delete_nca = [this](const NcaID& id) { + const auto path = GetRelativePathFromNcaID(id, false, true, false); + + const bool isFile = dir->GetFileRelative(path) != nullptr; + const bool isDir = dir->GetDirectoryRelative(path) != nullptr; + + if (isFile) { + return dir->DeleteFile(path); + } else if (isDir) { + return dir->DeleteSubdirectoryRecursive(path); + } + + return false; + }; + + // If an entry exists in the registered cache, remove it + if (HasEntry(title_id, ContentRecordType::Meta)) { + LOG_INFO(Loader, + "Previously installed entry (v{}) for title_id={:016X} detected! " + "Attempting to remove...", + GetEntryVersion(title_id).value_or(0), title_id); + + // Get all the ncas associated with the current CNMT and delete them + const auto meta_old_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); + const auto program_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); + const auto data_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); + const auto control_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); + const auto html_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); + const auto legal_id = + GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); + + const auto deleted_meta = delete_nca(meta_old_id); + const auto deleted_program = delete_nca(program_id); + const auto deleted_data = delete_nca(data_id); + const auto deleted_control = delete_nca(control_id); + const auto deleted_html = delete_nca(html_id); + const auto deleted_legal = delete_nca(legal_id); + + return deleted_meta && (deleted_meta || deleted_program || deleted_data || + deleted_control || deleted_html || deleted_legal); + } + + return false; +} + InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, bool overwrite_if_exists, std::optional<NcaID> override_id) { diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 29cf0d40c..ec1d54f27 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -155,9 +155,6 @@ public: std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, std::optional<u64> title_id = {}) const override; - // Removes an existing entry based on title id - bool RemoveExistingEntry(u64 title_id); - // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure // there is a meta NCA and all of them are accessible. InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, @@ -172,6 +169,9 @@ public: InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); + // Removes an existing entry based on title id + bool RemoveExistingEntry(u64 title_id) const; + private: template <typename T> void IterateAllMetadata(std::vector<T>& out, diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index adfd2c1a4..ba4efee3a 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp @@ -17,23 +17,23 @@ constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; namespace { -void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { +void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) { if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { if (meta.zero_1 != 0) { LOG_WARNING(Service_FS, - "Possibly incorrect SaveDataDescriptor, type is " + "Possibly incorrect SaveDataAttribute, type is " "SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).", meta.zero_1); } if (meta.zero_2 != 0) { LOG_WARNING(Service_FS, - "Possibly incorrect SaveDataDescriptor, type is " + "Possibly incorrect SaveDataAttribute, type is " "SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).", meta.zero_2); } if (meta.zero_3 != 0) { LOG_WARNING(Service_FS, - "Possibly incorrect SaveDataDescriptor, type is " + "Possibly incorrect SaveDataAttribute, type is " "SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).", meta.zero_3); } @@ -41,33 +41,32 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) { LOG_WARNING(Service_FS, - "Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is " + "Possibly incorrect SaveDataAttribute, type is SystemSaveData but title_id is " "non-zero ({:016X}).", meta.title_id); } if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { LOG_WARNING(Service_FS, - "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is " + "Possibly incorrect SaveDataAttribute, type is DeviceSaveData but user_id is " "non-zero ({:016X}{:016X})", meta.user_id[1], meta.user_id[0]); } } -bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) { - return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage || +bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) { + return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage || (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User - (desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) && - desc.title_id == 0 && desc.save_id == 0); + (attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) && + attr.title_id == 0 && attr.save_id == 0); } } // Anonymous namespace -std::string SaveDataDescriptor::DebugInfo() const { - return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, " - "save_id={:016X}, " +std::string SaveDataAttribute::DebugInfo() const { + return fmt::format("[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, " "rank={}, index={}]", - static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, + title_id, user_id[1], user_id[0], save_id, static_cast<u8>(type), static_cast<u8>(rank), index); } @@ -80,8 +79,8 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save SaveDataFactory::~SaveDataFactory() = default; ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, - const SaveDataDescriptor& meta) const { - PrintSaveDataDescriptorWarnings(meta); + const SaveDataAttribute& meta) const { + PrintSaveDataAttributeWarnings(meta); const auto save_directory = GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); @@ -98,7 +97,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, } ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, - const SaveDataDescriptor& meta) const { + const SaveDataAttribute& meta) const { const auto save_directory = GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 991e57aa1..6625bbbd8 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h @@ -21,6 +21,7 @@ enum class SaveDataSpaceId : u8 { TemporaryStorage = 3, SdCardUser = 4, ProperSystem = 100, + SafeMode = 101, }; enum class SaveDataType : u8 { @@ -30,28 +31,50 @@ enum class SaveDataType : u8 { DeviceSaveData = 3, TemporaryStorage = 4, CacheStorage = 5, + SystemBcat = 6, }; enum class SaveDataRank : u8 { - Primary, - Secondary, + Primary = 0, + Secondary = 1, }; -struct SaveDataDescriptor { - u64_le title_id; +enum class SaveDataFlags : u32 { + None = (0 << 0), + KeepAfterResettingSystemSaveData = (1 << 0), + KeepAfterRefurbishment = (1 << 1), + KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2), + NeedsSecureDelete = (1 << 3), +}; + +struct SaveDataAttribute { + u64 title_id; u128 user_id; - u64_le save_id; + u64 save_id; SaveDataType type; SaveDataRank rank; - u16_le index; + u16 index; INSERT_PADDING_BYTES(4); - u64_le zero_1; - u64_le zero_2; - u64_le zero_3; + u64 zero_1; + u64 zero_2; + u64 zero_3; std::string DebugInfo() const; }; -static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); +static_assert(sizeof(SaveDataAttribute) == 0x40, "SaveDataAttribute has incorrect size."); + +struct SaveDataExtraData { + SaveDataAttribute attr; + u64 owner_id; + s64 timestamp; + SaveDataFlags flags; + INSERT_PADDING_BYTES(4); + s64 available_size; + s64 journal_size; + s64 commit_id; + std::array<u8, 0x190> unused; +}; +static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has incorrect size."); struct SaveDataSize { u64 normal; @@ -64,8 +87,8 @@ public: explicit SaveDataFactory(VirtualDir dir); ~SaveDataFactory(); - ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; - ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; + ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; + ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const; VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 96ce5957c..0db0091f6 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -18,20 +18,22 @@ static std::string ModeFlagsToString(Mode mode) { std::string mode_str; // Calculate the correct open mode for the file. - if (mode & Mode::Read && mode & Mode::Write) { - if (mode & Mode::Append) + if (True(mode & Mode::Read) && True(mode & Mode::Write)) { + if (True(mode & Mode::Append)) { mode_str = "a+"; - else + } else { mode_str = "r+"; + } } else { - if (mode & Mode::Read) + if (True(mode & Mode::Read)) { mode_str = "r"; - else if (mode & Mode::Append) + } else if (True(mode & Mode::Append)) { mode_str = "a"; - else if (mode & Mode::Write) + } else if (True(mode & Mode::Write)) { mode_str = "w"; - else + } else { UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); + } } mode_str += "b"; @@ -73,8 +75,9 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { } } - if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0) + if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { FileUtil::CreateEmptyFile(path); + } auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); cache[path] = backing; @@ -247,11 +250,11 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { } bool RealVfsFile::IsWritable() const { - return (perms & Mode::WriteAppend) != 0; + return True(perms & Mode::WriteAppend); } bool RealVfsFile::IsReadable() const { - return (perms & Mode::ReadWrite) != 0; + return True(perms & Mode::ReadWrite); } std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { @@ -319,8 +322,9 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_components(FileUtil::SplitPathComponents(path)), parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), perms(perms_) { - if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) + if (!FileUtil::Exists(path) && True(perms & Mode::WriteAppend)) { FileUtil::CreateDir(path); + } } RealVfsDirectory::~RealVfsDirectory() = default; @@ -371,11 +375,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() } bool RealVfsDirectory::IsWritable() const { - return (perms & Mode::WriteAppend) != 0; + return True(perms & Mode::WriteAppend); } bool RealVfsDirectory::IsReadable() const { - return (perms & Mode::ReadWrite) != 0; + return True(perms & Mode::ReadWrite); } std::string RealVfsDirectory::GetName() const { diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 86e06ccb9..81413c684 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp @@ -70,14 +70,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) NAX::~NAX() = default; Loader::ResultStatus NAX::Parse(std::string_view path) { - if (file->ReadObject(header.get()) != sizeof(NAXHeader)) + if (file == nullptr) { + return Loader::ResultStatus::ErrorNullFile; + } + if (file->ReadObject(header.get()) != sizeof(NAXHeader)) { return Loader::ResultStatus::ErrorBadNAXHeader; - - if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) + } + if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) { return Loader::ResultStatus::ErrorBadNAXHeader; - - if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) + } + if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) { return Loader::ResultStatus::ErrorIncorrectNAXFileSize; + } keys.DeriveSDSeedLazy(); std::array<Core::Crypto::Key256, 2> sd_keys{}; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b31673928..f3277b766 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -13,6 +13,7 @@ #include <vector> #include <boost/container/small_vector.hpp> #include "common/common_types.h" +#include "common/concepts.h" #include "common/swap.h" #include "core/hle/ipc.h" #include "core/hle/kernel/object.h" @@ -193,23 +194,24 @@ public: /* Helper function to write a buffer using the appropriate buffer descriptor * - * @tparam ContiguousContainer an arbitrary container that satisfies the - * ContiguousContainer concept in the C++ standard library. + * @tparam T an arbitrary container that satisfies the + * ContiguousContainer concept in the C++ standard library or a trivially copyable type. * - * @param container The container to write the data of into a buffer. + * @param data The container/data to write into a buffer. * @param buffer_index The buffer in particular to write to. */ - template <typename ContiguousContainer, - typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> - std::size_t WriteBuffer(const ContiguousContainer& container, - std::size_t buffer_index = 0) const { - using ContiguousType = typename ContiguousContainer::value_type; - - static_assert(std::is_trivially_copyable_v<ContiguousType>, - "Container to WriteBuffer must contain trivially copyable objects"); - - return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType), - buffer_index); + template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> + std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { + if constexpr (Common::IsSTLContainer<T>) { + using ContiguousType = typename T::value_type; + static_assert(std::is_trivially_copyable_v<ContiguousType>, + "Container to WriteBuffer must contain trivially copyable objects"); + return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType), + buffer_index); + } else { + static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); + return WriteBuffer(&data, sizeof(T), buffer_index); + } } /// Helper function to get the size of the input buffer diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 8ac856ec3..63e4aeca0 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -286,9 +286,7 @@ protected: ProfileBase profile_base{}; ProfileData data{}; if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { - std::array<u8, sizeof(ProfileData)> raw_data; - std::memcpy(raw_data.data(), &data, sizeof(ProfileData)); - ctx.WriteBuffer(raw_data); + ctx.WriteBuffer(data); IPC::ResponseBuilder rb{ctx, 16}; rb.Push(RESULT_SUCCESS); rb.PushRaw(profile_base); @@ -333,7 +331,7 @@ protected: std::vector<u8> buffer(size); image.ReadBytes(buffer.data(), buffer.size()); - ctx.WriteBuffer(buffer.data(), buffer.size()); + ctx.WriteBuffer(buffer); rb.Push<u32>(size); } diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index eb8c81645..a98d57b5c 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp @@ -58,7 +58,7 @@ ProfileManager::~ProfileManager() { /// internal management of the users profiles std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { if (user_count >= MAX_USERS) { - return {}; + return std::nullopt; } profiles[user_count] = profile; return user_count++; @@ -101,13 +101,14 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) { return ERROR_USER_ALREADY_EXISTS; } - ProfileInfo profile; - profile.user_uuid = uuid; - profile.username = username; - profile.data = {}; - profile.creation_time = 0x0; - profile.is_open = false; - return AddUser(profile); + + return AddUser({ + .user_uuid = uuid, + .username = username, + .creation_time = 0, + .data = {}, + .is_open = false, + }); } /// Creates a new user on the system. This function allows a much simpler method of registration @@ -126,7 +127,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { if (index >= MAX_USERS) { - return {}; + return std::nullopt; } return profiles[index].user_uuid; @@ -135,13 +136,13 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { /// Returns a users profile index based on their user id. std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { if (!uuid) { - return {}; + return std::nullopt; } const auto iter = std::find_if(profiles.begin(), profiles.end(), [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); if (iter == profiles.end()) { - return {}; + return std::nullopt; } return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); @@ -339,7 +340,13 @@ void ProfileManager::ParseUserSaveFile() { continue; } - AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false}); + AddUser({ + .user_uuid = user.uuid, + .username = user.username, + .creation_time = user.timestamp, + .data = user.extra_data, + .is_open = false, + }); } std::stable_partition(profiles.begin(), profiles.end(), @@ -350,11 +357,13 @@ void ProfileManager::WriteUserSaveFile() { ProfileDataRaw raw{}; for (std::size_t i = 0; i < MAX_USERS; ++i) { - raw.users[i].username = profiles[i].username; - raw.users[i].uuid2 = profiles[i].user_uuid; - raw.users[i].uuid = profiles[i].user_uuid; - raw.users[i].timestamp = profiles[i].creation_time; - raw.users[i].extra_data = profiles[i].data; + raw.users[i] = { + .uuid = profiles[i].user_uuid, + .uuid2 = profiles[i].user_uuid, + .timestamp = profiles[i].creation_time, + .username = profiles[i].username, + .extra_data = profiles[i].data, + }; } const auto raw_path = diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ceed20609..55a1edf1a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1342,12 +1342,12 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); - FileSys::SaveDataDescriptor descriptor{}; - descriptor.title_id = system.CurrentProcess()->GetTitleID(); - descriptor.user_id = user_id; - descriptor.type = FileSys::SaveDataType::SaveData; + FileSys::SaveDataAttribute attribute{}; + attribute.title_id = system.CurrentProcess()->GetTitleID(); + attribute.user_id = user_id; + attribute.type = FileSys::SaveDataType::SaveData; const auto res = system.GetFileSystemController().CreateSaveData( - FileSys::SaveDataSpaceId::NandUser, descriptor); + FileSys::SaveDataSpaceId::NandUser, attribute); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(res.Code()); diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 106e89743..dd80dd1dc 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -71,7 +71,7 @@ public: stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, audio_params.channel_count, std::move(unique_name), - [=]() { buffer_event.writable->Signal(); }); + [this] { buffer_event.writable->Signal(); }); } private: diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index d19513cbb..f1d81602c 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -92,7 +92,7 @@ private: if (performance) { rb.Push<u64>(*performance); } - ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); + ctx.WriteBuffer(samples); } bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 603b64d4f..db0e06ca1 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp @@ -112,7 +112,7 @@ private: void GetImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_BCAT, "called"); - ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl)); + ctx.WriteBuffer(impl); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index a41c73c48..c2737a365 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp @@ -160,7 +160,7 @@ private: return; } - ctx.WriteBuffer(key.data(), key.size()); + ctx.WriteBuffer(key); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index c66124998..4490f8e4c 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -311,7 +311,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( } ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( - FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const { + FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const { LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", static_cast<u8>(space), save_struct.DebugInfo()); @@ -323,15 +323,15 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( } ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( - FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const { + FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const { LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", - static_cast<u8>(space), descriptor.DebugInfo()); + static_cast<u8>(space), attribute.DebugInfo()); if (save_data_factory == nullptr) { return FileSys::ERROR_ENTITY_NOT_FOUND; } - return save_data_factory->Open(space, descriptor); + return save_data_factory->Open(space, attribute); } ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 1b0a6a949..6dbbf0b2b 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h @@ -31,7 +31,7 @@ enum class SaveDataSpaceId : u8; enum class SaveDataType : u8; enum class StorageId : u8; -struct SaveDataDescriptor; +struct SaveDataAttribute; struct SaveDataSize; } // namespace FileSys @@ -69,9 +69,9 @@ public: ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const; ResultVal<FileSys::VirtualDir> CreateSaveData( - FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; + FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; ResultVal<FileSys::VirtualDir> OpenSaveData( - FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; + FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; ResultVal<FileSys::VirtualDir> OpenSDMC() const; ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 20c331b77..26fd87f58 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -696,8 +696,8 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) {67, nullptr, "FindSaveDataWithFilter"}, {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, - {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, - {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, + {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, + {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, {80, nullptr, "OpenSaveDataMetaFile"}, {81, nullptr, "OpenSaveDataTransferManager"}, {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, @@ -812,7 +812,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); + auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>(); [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); u128 uid = rp.PopRaw<u128>(); @@ -826,17 +826,18 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { } void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { - LOG_INFO(Service_FS, "called."); + IPC::RequestParser rp{ctx}; struct Parameters { - FileSys::SaveDataSpaceId save_data_space_id; - FileSys::SaveDataDescriptor descriptor; + FileSys::SaveDataSpaceId space_id; + FileSys::SaveDataAttribute attribute; }; - IPC::RequestParser rp{ctx}; const auto parameters = rp.PopRaw<Parameters>(); - auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor); + LOG_INFO(Service_FS, "called."); + + auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute); if (dir.Failed()) { IPC::ResponseBuilder rb{ctx, 2, 0, 0}; rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); @@ -844,13 +845,18 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { } FileSys::StorageId id; - if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) { + + switch (parameters.space_id) { + case FileSys::SaveDataSpaceId::NandUser: id = FileSys::StorageId::NandUser; - } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem || - parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) { + break; + case FileSys::SaveDataSpaceId::SdCardSystem: + case FileSys::SaveDataSpaceId::SdCardUser: id = FileSys::StorageId::SdCard; - } else { + break; + case FileSys::SaveDataSpaceId::NandSystem: id = FileSys::StorageId::NandSystem; + break; } auto filesystem = @@ -876,22 +882,31 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); } -void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - log_mode = rp.PopEnum<LogMode>(); - - LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); +void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called."); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } -void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_FS, "called"); +void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute( + Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + + struct Parameters { + FileSys::SaveDataSpaceId space_id; + FileSys::SaveDataAttribute attribute; + }; + + const auto parameters = rp.PopRaw<Parameters>(); + // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData + constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None); + + LOG_WARNING(Service_FS, "(STUBBED) called, flags={}", flags); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.PushEnum(log_mode); + rb.Push(flags); } void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { @@ -966,6 +981,24 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); } +void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + log_mode = rp.PopEnum<LogMode>(); + + LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(log_mode); +} + void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { const auto raw = ctx.ReadBuffer(); auto log = Common::StringFromFixedZeroTerminatedBuffer( diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index dfb3e395b..4964e874e 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -43,11 +43,13 @@ private: void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); - void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); - void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); + void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx); + void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(Kernel::HLERequestContext& ctx); void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); + void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); + void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 64a526b9e..d8cd10e31 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -310,7 +310,7 @@ public: ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, u64 size) const { - for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { + for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { auto& page_table{process->PageTable()}; const VAddr addr{GetRandomMapRegion(page_table, size)}; const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; @@ -331,8 +331,7 @@ public: ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, VAddr bss_addr, std::size_t bss_size, std::size_t size) const { - - for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { + for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { auto& page_table{process->PageTable()}; VAddr addr{}; diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 4b79eb81d..5e2d769a4 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -127,7 +127,7 @@ private: const u32 array_size = rp.Pop<u32>(); LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); - ctx.WriteBuffer(&device_handle, sizeof(device_handle)); + ctx.WriteBuffer(device_handle); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); @@ -220,7 +220,7 @@ private: tag_info.protocol = 1; // TODO(ogniK): Figure out actual values tag_info.tag_type = 2; - ctx.WriteBuffer(&tag_info, sizeof(TagInfo)); + ctx.WriteBuffer(tag_info); rb.Push(RESULT_SUCCESS); } @@ -237,7 +237,7 @@ private: IPC::ResponseBuilder rb{ctx, 2}; auto amiibo = nfp_interface.GetAmiiboBuffer(); - ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info)); + ctx.WriteBuffer(amiibo.model_info); rb.Push(RESULT_SUCCESS); } @@ -283,7 +283,7 @@ private: CommonInfo common_info{}; common_info.application_area_size = 0; - ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); + ctx.WriteBuffer(common_info); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index deaf0808b..88fbfa9b0 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -60,24 +60,24 @@ void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { if (ctrl.must_delay) { ctrl.fresh_call = false; - ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout, - [=](std::shared_ptr<Kernel::Thread> thread, - Kernel::HLERequestContext& ctx, - Kernel::ThreadWakeupReason reason) { - IoctlCtrl ctrl2{ctrl}; - std::vector<u8> tmp_output = output; - std::vector<u8> tmp_output2 = output2; - u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, - tmp_output2, ctrl2, version); - ctx.WriteBuffer(tmp_output, 0); - if (version == IoctlVersion::Version3) { - ctx.WriteBuffer(tmp_output2, 1); - } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(RESULT_SUCCESS); - rb.Push(result); - }, - nvdrv->GetEventWriteable(ctrl.event_id)); + ctx.SleepClientThread( + "NVServices::DelayedResponse", ctrl.timeout, + [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, + Kernel::ThreadWakeupReason reason) { + IoctlCtrl ctrl2{ctrl}; + std::vector<u8> tmp_output = output; + std::vector<u8> tmp_output2 = output2; + const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, + tmp_output2, ctrl2, version); + ctx_.WriteBuffer(tmp_output, 0); + if (version == IoctlVersion::Version3) { + ctx_.WriteBuffer(tmp_output2, 1); + } + IPC::ResponseBuilder rb{ctx_, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(ioctl_result); + }, + nvdrv->GetEventWriteable(ctrl.event_id)); } else { ctx.WriteBuffer(output); if (version == IoctlVersion::Version3) { diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 34fe2fd82..e64777668 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -106,7 +106,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); + ctx.WriteBuffer(layout); } } // Anonymous namespace diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index b06d2f103..b526a94fe 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -9,6 +9,7 @@ #include <type_traits> #include <unordered_map> +#include "common/concepts.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" @@ -56,10 +57,8 @@ public: ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); - template <typename T> + template <Common::IsBaseOf<Kernel::SessionRequestHandler> T> std::shared_ptr<T> GetService(const std::string& service_name) const { - static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, - "Not a base of ServiceFrameworkBase"); auto service = registered_services.find(service_name); if (service == registered_services.end()) { LOG_DEBUG(Service, "Can't find service: {}", service_name); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 13e4b3818..ee4fa4b48 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -290,7 +290,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); + ctx.WriteBuffer(clock_snapshot); } void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) { @@ -313,7 +313,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); - ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); + ctx.WriteBuffer(clock_snapshot); } void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index db57ae069..ff3a10b3e 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp @@ -142,7 +142,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u32>(1); // Number of times we're returning - ctx.WriteBuffer(&posix_time, sizeof(s64)); + ctx.WriteBuffer(posix_time); } void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { @@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.PushRaw<u32>(1); // Number of times we're returning - ctx.WriteBuffer(&posix_time, sizeof(s64)); + ctx.WriteBuffer(posix_time); } } // namespace Service::Time diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 825d11a3f..988d253f9 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -548,8 +548,8 @@ private: // Wait the current thread until a buffer becomes available ctx.SleepClientThread( "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, - [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, - Kernel::ThreadWakeupReason reason) { + [=, this](std::shared_ptr<Kernel::Thread> thread, + Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { // Repeat TransactParcel DequeueBuffer when a buffer is available const auto guard = nv_flinger->Lock(); auto& buffer_queue = nv_flinger->FindBufferQueue(id); diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index 2e7da23fe..48be80c12 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp @@ -313,30 +313,32 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { switch (opcode_type) { case CheatVmOpcodeType::StoreStatic: { - StoreStaticOpcode store_static{}; // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) // Read additional words. const u32 second_dword = GetNextDword(); - store_static.bit_width = (first_dword >> 24) & 0xF; - store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); - store_static.offset_register = ((first_dword >> 16) & 0xF); - store_static.rel_address = - (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); - store_static.value = GetNextVmInt(store_static.bit_width); - opcode.opcode = store_static; + const u32 bit_width = (first_dword >> 24) & 0xF; + + opcode.opcode = StoreStaticOpcode{ + .bit_width = bit_width, + .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), + .offset_register = (first_dword >> 16) & 0xF, + .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, + .value = GetNextVmInt(bit_width), + }; } break; case CheatVmOpcodeType::BeginConditionalBlock: { - BeginConditionalOpcode begin_cond{}; // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) // Read additional words. const u32 second_dword = GetNextDword(); - begin_cond.bit_width = (first_dword >> 24) & 0xF; - begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); - begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); - begin_cond.rel_address = - (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); - begin_cond.value = GetNextVmInt(begin_cond.bit_width); - opcode.opcode = begin_cond; + const u32 bit_width = (first_dword >> 24) & 0xF; + + opcode.opcode = BeginConditionalOpcode{ + .bit_width = bit_width, + .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), + .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF), + .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, + .value = GetNextVmInt(bit_width), + }; } break; case CheatVmOpcodeType::EndConditionalBlock: { // 20000000 @@ -344,12 +346,14 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { opcode.opcode = EndConditionalOpcode{}; } break; case CheatVmOpcodeType::ControlLoop: { - ControlLoopOpcode ctrl_loop{}; // 300R0000 VVVVVVVV // 310R0000 // Parse register, whether loop start or loop end. - ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; - ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); + ControlLoopOpcode ctrl_loop{ + .start_loop = ((first_dword >> 24) & 0xF) == 0, + .reg_index = (first_dword >> 20) & 0xF, + .num_iters = 0, + }; // Read number of iters if loop start. if (ctrl_loop.start_loop) { @@ -358,66 +362,65 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { opcode.opcode = ctrl_loop; } break; case CheatVmOpcodeType::LoadRegisterStatic: { - LoadRegisterStaticOpcode ldr_static{}; // 400R0000 VVVVVVVV VVVVVVVV // Read additional words. - ldr_static.reg_index = ((first_dword >> 16) & 0xF); - ldr_static.value = - (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); - opcode.opcode = ldr_static; + opcode.opcode = LoadRegisterStaticOpcode{ + .reg_index = (first_dword >> 16) & 0xF, + .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(), + }; } break; case CheatVmOpcodeType::LoadRegisterMemory: { - LoadRegisterMemoryOpcode ldr_memory{}; // 5TMRI0AA AAAAAAAA // Read additional words. const u32 second_dword = GetNextDword(); - ldr_memory.bit_width = (first_dword >> 24) & 0xF; - ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); - ldr_memory.reg_index = ((first_dword >> 16) & 0xF); - ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; - ldr_memory.rel_address = - (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); - opcode.opcode = ldr_memory; + opcode.opcode = LoadRegisterMemoryOpcode{ + .bit_width = (first_dword >> 24) & 0xF, + .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), + .reg_index = ((first_dword >> 16) & 0xF), + .load_from_reg = ((first_dword >> 12) & 0xF) != 0, + .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, + }; } break; case CheatVmOpcodeType::StoreStaticToAddress: { - StoreStaticToAddressOpcode str_static{}; // 6T0RIor0 VVVVVVVV VVVVVVVV // Read additional words. - str_static.bit_width = (first_dword >> 24) & 0xF; - str_static.reg_index = ((first_dword >> 16) & 0xF); - str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; - str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; - str_static.offset_reg_index = ((first_dword >> 4) & 0xF); - str_static.value = - (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); - opcode.opcode = str_static; + opcode.opcode = StoreStaticToAddressOpcode{ + .bit_width = (first_dword >> 24) & 0xF, + .reg_index = (first_dword >> 16) & 0xF, + .increment_reg = ((first_dword >> 12) & 0xF) != 0, + .add_offset_reg = ((first_dword >> 8) & 0xF) != 0, + .offset_reg_index = (first_dword >> 4) & 0xF, + .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(), + }; } break; case CheatVmOpcodeType::PerformArithmeticStatic: { - PerformArithmeticStaticOpcode perform_math_static{}; // 7T0RC000 VVVVVVVV // Read additional words. - perform_math_static.bit_width = (first_dword >> 24) & 0xF; - perform_math_static.reg_index = ((first_dword >> 16) & 0xF); - perform_math_static.math_type = - static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF); - perform_math_static.value = GetNextDword(); - opcode.opcode = perform_math_static; + opcode.opcode = PerformArithmeticStaticOpcode{ + .bit_width = (first_dword >> 24) & 0xF, + .reg_index = ((first_dword >> 16) & 0xF), + .math_type = static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF), + .value = GetNextDword(), + }; } break; case CheatVmOpcodeType::BeginKeypressConditionalBlock: { - BeginKeypressConditionalOpcode begin_keypress_cond{}; // 8kkkkkkk // Just parse the mask. - begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; - opcode.opcode = begin_keypress_cond; + opcode.opcode = BeginKeypressConditionalOpcode{ + .key_mask = first_dword & 0x0FFFFFFF, + }; } break; case CheatVmOpcodeType::PerformArithmeticRegister: { - PerformArithmeticRegisterOpcode perform_math_reg{}; // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) - perform_math_reg.bit_width = (first_dword >> 24) & 0xF; - perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF); - perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); - perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); - perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; + PerformArithmeticRegisterOpcode perform_math_reg{ + .bit_width = (first_dword >> 24) & 0xF, + .math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF), + .dst_reg_index = (first_dword >> 16) & 0xF, + .src_reg_1_index = (first_dword >> 12) & 0xF, + .src_reg_2_index = 0, + .has_immediate = ((first_dword >> 8) & 0xF) != 0, + .value = {}, + }; if (perform_math_reg.has_immediate) { perform_math_reg.src_reg_2_index = 0; perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); @@ -427,7 +430,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { opcode.opcode = perform_math_reg; } break; case CheatVmOpcodeType::StoreRegisterToAddress: { - StoreRegisterToAddressOpcode str_register{}; // ATSRIOxa (aaaaaaaa) // A = opcode 10 // T = bit width @@ -439,20 +441,23 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { // Relative Address // x = offset register (for offset type 1), memory type (for offset type 3) // a = relative address (for offset type 2+3) - str_register.bit_width = (first_dword >> 24) & 0xF; - str_register.str_reg_index = ((first_dword >> 20) & 0xF); - str_register.addr_reg_index = ((first_dword >> 16) & 0xF); - str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; - str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)); - str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); + StoreRegisterToAddressOpcode str_register{ + .bit_width = (first_dword >> 24) & 0xF, + .str_reg_index = (first_dword >> 20) & 0xF, + .addr_reg_index = (first_dword >> 16) & 0xF, + .increment_reg = ((first_dword >> 12) & 0xF) != 0, + .ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)), + .mem_type = MemoryAccessType::MainNso, + .ofs_reg_index = (first_dword >> 4) & 0xF, + .rel_address = 0, + }; switch (str_register.ofs_type) { case StoreRegisterOffsetType::None: case StoreRegisterOffsetType::Reg: // Nothing more to do break; case StoreRegisterOffsetType::Imm: - str_register.rel_address = - ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); break; case StoreRegisterOffsetType::MemReg: str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); @@ -460,8 +465,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { case StoreRegisterOffsetType::MemImm: case StoreRegisterOffsetType::MemImmReg: str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); - str_register.rel_address = - ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); break; default: str_register.ofs_type = StoreRegisterOffsetType::None; @@ -470,7 +474,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { opcode.opcode = str_register; } break; case CheatVmOpcodeType::BeginRegisterConditionalBlock: { - BeginRegisterConditionalOpcode begin_reg_cond{}; // C0TcSX## // C0TcS0Ma aaaaaaaa // C0TcS1Mr @@ -492,11 +495,19 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { // r = offset register. // X = other register. // V = value. - begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; - begin_reg_cond.cond_type = - static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); - begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); - begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF); + + BeginRegisterConditionalOpcode begin_reg_cond{ + .bit_width = (first_dword >> 20) & 0xF, + .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF), + .val_reg_index = (first_dword >> 12) & 0xF, + .comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF), + .mem_type = MemoryAccessType::MainNso, + .addr_reg_index = 0, + .other_reg_index = 0, + .ofs_reg_index = 0, + .rel_address = 0, + .value = {}, + }; switch (begin_reg_cond.comp_type) { case CompareRegisterValueType::StaticValue: @@ -508,26 +519,25 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { case CompareRegisterValueType::MemoryRelAddr: begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); begin_reg_cond.rel_address = - ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); break; case CompareRegisterValueType::MemoryOfsReg: begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); begin_reg_cond.ofs_reg_index = (first_dword & 0xF); break; case CompareRegisterValueType::RegisterRelAddr: - begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); + begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF; begin_reg_cond.rel_address = - ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); break; case CompareRegisterValueType::RegisterOfsReg: - begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); - begin_reg_cond.ofs_reg_index = (first_dword & 0xF); + begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF; + begin_reg_cond.ofs_reg_index = first_dword & 0xF; break; } opcode.opcode = begin_reg_cond; } break; case CheatVmOpcodeType::SaveRestoreRegister: { - SaveRestoreRegisterOpcode save_restore_reg{}; // C10D0Sx0 // C1 = opcode 0xC1 // D = destination index. @@ -535,36 +545,37 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring // a register. // NOTE: If we add more save slots later, current encoding is backwards compatible. - save_restore_reg.dst_index = (first_dword >> 16) & 0xF; - save_restore_reg.src_index = (first_dword >> 8) & 0xF; - save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF); - opcode.opcode = save_restore_reg; + opcode.opcode = SaveRestoreRegisterOpcode{ + .dst_index = (first_dword >> 16) & 0xF, + .src_index = (first_dword >> 8) & 0xF, + .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF), + }; } break; case CheatVmOpcodeType::SaveRestoreRegisterMask: { - SaveRestoreRegisterMaskOpcode save_restore_regmask{}; // C2x0XXXX // C2 = opcode 0xC2 // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. // X = 16-bit bitmask, bit i --> save or restore register i. - save_restore_regmask.op_type = - static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF); + SaveRestoreRegisterMaskOpcode save_restore_regmask{ + .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF), + .should_operate = {}, + }; for (std::size_t i = 0; i < NumRegisters; i++) { - save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; + save_restore_regmask.should_operate[i] = (first_dword & (1U << i)) != 0; } opcode.opcode = save_restore_regmask; } break; case CheatVmOpcodeType::ReadWriteStaticRegister: { - ReadWriteStaticRegisterOpcode rw_static_reg{}; // C3000XXx // C3 = opcode 0xC3. // XX = static register index. // x = register index. - rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF); - rw_static_reg.idx = (first_dword & 0xF); - opcode.opcode = rw_static_reg; + opcode.opcode = ReadWriteStaticRegisterOpcode{ + .static_idx = (first_dword >> 4) & 0xFF, + .idx = first_dword & 0xF, + }; } break; case CheatVmOpcodeType::DebugLog: { - DebugLogOpcode debug_log{}; // FFFTIX## // FFFTI0Ma aaaaaaaa // FFFTI1Mr @@ -583,31 +594,36 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { // a = relative address. // r = offset register. // X = value register. - debug_log.bit_width = (first_dword >> 16) & 0xF; - debug_log.log_id = ((first_dword >> 12) & 0xF); - debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF); + DebugLogOpcode debug_log{ + .bit_width = (first_dword >> 16) & 0xF, + .log_id = (first_dword >> 12) & 0xF, + .val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF), + .mem_type = MemoryAccessType::MainNso, + .addr_reg_index = 0, + .val_reg_index = 0, + .ofs_reg_index = 0, + .rel_address = 0, + }; switch (debug_log.val_type) { case DebugLogValueType::RegisterValue: - debug_log.val_reg_index = ((first_dword >> 4) & 0xF); + debug_log.val_reg_index = (first_dword >> 4) & 0xF; break; case DebugLogValueType::MemoryRelAddr: debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); - debug_log.rel_address = - ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); break; case DebugLogValueType::MemoryOfsReg: debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); - debug_log.ofs_reg_index = (first_dword & 0xF); + debug_log.ofs_reg_index = first_dword & 0xF; break; case DebugLogValueType::RegisterRelAddr: - debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); - debug_log.rel_address = - ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); + debug_log.addr_reg_index = (first_dword >> 4) & 0xF; + debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); break; case DebugLogValueType::RegisterOfsReg: - debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); - debug_log.ofs_reg_index = (first_dword & 0xF); + debug_log.addr_reg_index = (first_dword >> 4) & 0xF; + debug_log.ofs_reg_index = first_dword & 0xF; break; } opcode.opcode = debug_log; diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 29339ead7..b899ac884 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -74,15 +74,16 @@ void PerfStats::EndGameFrame() { game_frames += 1; } -double PerfStats::GetMeanFrametime() { +double PerfStats::GetMeanFrametime() const { std::lock_guard lock{object_mutex}; if (current_index <= IgnoreFrames) { return 0; } + const double sum = std::accumulate(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, 0.0); - return sum / (current_index - IgnoreFrames); + return sum / static_cast<double>(current_index - IgnoreFrames); } PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { @@ -94,12 +95,13 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; - PerfStatsResults results{}; - results.system_fps = static_cast<double>(system_frames) / interval; - results.game_fps = static_cast<double>(game_frames) / interval; - results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / - static_cast<double>(system_frames); - results.emulation_speed = system_us_per_second.count() / 1'000'000.0; + const PerfStatsResults results{ + .system_fps = static_cast<double>(system_frames) / interval, + .game_fps = static_cast<double>(game_frames) / interval, + .frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / + static_cast<double>(system_frames), + .emulation_speed = system_us_per_second.count() / 1'000'000.0, + }; // Reset counters reset_point = now; @@ -111,7 +113,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us return results; } -double PerfStats::GetLastFrameTimeScale() { +double PerfStats::GetLastFrameTimeScale() const { std::lock_guard lock{object_mutex}; constexpr double FRAME_LENGTH = 1.0 / 60; diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index d9a64f072..69256b960 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -30,7 +30,6 @@ struct PerfStatsResults { class PerfStats { public: explicit PerfStats(u64 title_id); - ~PerfStats(); using Clock = std::chrono::high_resolution_clock; @@ -42,18 +41,18 @@ public: PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); /** - * Returns the Arthimetic Mean of all frametime values stored in the performance history. + * Returns the arithmetic mean of all frametime values stored in the performance history. */ - double GetMeanFrametime(); + double GetMeanFrametime() const; /** * Gets the ratio between walltime and the emulated time of the previous system frame. This is * useful for scaling inputs or outputs moving between the two time domains. */ - double GetLastFrameTimeScale(); + double GetLastFrameTimeScale() const; private: - std::mutex object_mutex{}; + mutable std::mutex object_mutex; /// Title ID for the game that is running. 0 if there is no game running yet u64 title_id{0}; @@ -61,7 +60,7 @@ private: std::size_t current_index{0}; /// Stores an hour of historical frametime data useful for processing and tracking performance /// regressions with code changes. - std::array<double, 216000> perf_history = {}; + std::array<double, 216000> perf_history{}; /// Point when the cumulative counters were reset Clock::time_point reset_point = Clock::now(); diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index 022b26e6d..b35459152 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp @@ -46,20 +46,16 @@ struct ScopeInit final { Core::Timing::CoreTiming core_timing; }; -#pragma optimize("", off) - u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) { - u64 start = core_timing.GetGlobalTimeNs().count(); - u64 placebo = 0; + const u64 start = core_timing.GetGlobalTimeNs().count(); + volatile u64 placebo = 0; for (std::size_t i = 0; i < 1000; i++) { - placebo += core_timing.GetGlobalTimeNs().count(); + placebo = placebo + core_timing.GetGlobalTimeNs().count(); } - u64 end = core_timing.GetGlobalTimeNs().count(); - return (end - start); + const u64 end = core_timing.GetGlobalTimeNs().count(); + return end - start; } -#pragma optimize("", on) - } // Anonymous namespace TEST_CASE("CoreTiming[BasicOrder]", "[core]") { diff --git a/src/video_core/compatible_formats.h b/src/video_core/compatible_formats.h index d1082566d..51766349b 100644 --- a/src/video_core/compatible_formats.h +++ b/src/video_core/compatible_formats.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <array> #include <bitset> #include <cstddef> diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 8e19c3373..512578c8b 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -81,7 +81,7 @@ void GPU::WaitFence(u32 syncpoint_id, u32 value) { } MICROPROFILE_SCOPE(GPU_wait); std::unique_lock lock{sync_mutex}; - sync_cv.wait(lock, [=]() { return syncpoints[syncpoint_id].load() >= value; }); + sync_cv.wait(lock, [=, this] { return syncpoints[syncpoint_id].load() >= value; }); } void GPU::IncrementSyncPoint(const u32 syncpoint_id) { diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index ce53e5a6b..a551e3de8 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -696,6 +696,7 @@ void VKBlitScreen::CreateFramebuffers() { .flags = 0, .renderPass = *renderpass, .attachmentCount = 1, + .pAttachments = nullptr, .width = size.width, .height = size.height, .layers = 1, diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 6245e0d78..0c03e4d83 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp @@ -771,8 +771,9 @@ std::vector<VkDeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const .pNext = nullptr, .flags = 0, .queueFamilyIndex = queue_family, + .queueCount = 1, + .pQueuePriorities = nullptr, }); - ci.queueCount = 1; ci.pQueuePriorities = &QUEUE_PRIORITY; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 42b3a744c..418c62bc4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -261,8 +261,13 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach } const Specialization specialization{ + .base_binding = 0, .workgroup_size = key.workgroup_size, .shared_memory_size = key.shared_memory_size, + .point_size = std::nullopt, + .enabled_attributes = {}, + .attribute_types = {}, + .ndc_minus_one_to_one = false, }; const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute, shader->GetRegistry(), specialization), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 2ed2004f0..7500e8244 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -815,8 +815,13 @@ bool RasterizerVulkan::WalkAttachmentOverlaps(const CachedSurfaceView& attachmen std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers( VkRenderPass renderpass) { - FramebufferCacheKey key{renderpass, std::numeric_limits<u32>::max(), - std::numeric_limits<u32>::max(), std::numeric_limits<u32>::max()}; + FramebufferCacheKey key{ + .renderpass = renderpass, + .width = std::numeric_limits<u32>::max(), + .height = std::numeric_limits<u32>::max(), + .layers = std::numeric_limits<u32>::max(), + .views = {}, + }; const auto try_push = [&key](const View& view) { if (!view) { diff --git a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp index 2d5460776..b068888f9 100644 --- a/src/video_core/renderer_vulkan/vk_sampler_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_sampler_cache.cpp @@ -47,6 +47,7 @@ vk::Sampler VKSamplerCache::CreateSampler(const Tegra::Texture::TSCEntry& tsc) c VkSamplerCustomBorderColorCreateInfoEXT border{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, .pNext = nullptr, + .customBorderColor = {}, .format = VK_FORMAT_UNDEFINED, }; std::memcpy(&border.customBorderColor, color.data(), sizeof(color)); diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index efd4bb13b..2c6f54101 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -473,6 +473,8 @@ VkImageView CachedSurfaceView::GetAttachment() { .aspectMask = aspect_mask, .baseMipLevel = base_level, .levelCount = num_levels, + .baseArrayLayer = 0, + .layerCount = 0, }, }; if (image_view_type == VK_IMAGE_VIEW_TYPE_3D) { diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index 9e5fe2374..9a98f0e98 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp @@ -74,9 +74,9 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta SurfaceParams params; params.is_tiled = tic.IsTiled(); params.srgb_conversion = tic.IsSrgbConversionEnabled(); - params.block_width = params.is_tiled ? tic.BlockWidth() : 0, - params.block_height = params.is_tiled ? tic.BlockHeight() : 0, - params.block_depth = params.is_tiled ? tic.BlockDepth() : 0, + params.block_width = params.is_tiled ? tic.BlockWidth() : 0; + params.block_height = params.is_tiled ? tic.BlockHeight() : 0; + params.block_depth = params.is_tiled ? tic.BlockDepth() : 0; params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1; params.pixel_format = lookup_table.GetPixelFormat( tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type); @@ -130,14 +130,13 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl SurfaceParams params; params.is_tiled = tic.IsTiled(); params.srgb_conversion = tic.IsSrgbConversionEnabled(); - params.block_width = params.is_tiled ? tic.BlockWidth() : 0, - params.block_height = params.is_tiled ? tic.BlockHeight() : 0, - params.block_depth = params.is_tiled ? tic.BlockDepth() : 0, + params.block_width = params.is_tiled ? tic.BlockWidth() : 0; + params.block_height = params.is_tiled ? tic.BlockHeight() : 0; + params.block_depth = params.is_tiled ? tic.BlockDepth() : 0; params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1; params.pixel_format = lookup_table.GetPixelFormat( tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type); params.type = GetFormatType(params.pixel_format); - params.type = GetFormatType(params.pixel_format); params.target = ImageTypeToSurfaceTarget(entry.type); // TODO: on 1DBuffer we should use the tic info. if (tic.IsBuffer()) { @@ -167,27 +166,30 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) { const auto& regs = system.GPU().Maxwell3D().regs; - SurfaceParams params; - params.is_tiled = regs.zeta.memory_layout.type == - Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear; - params.srgb_conversion = false; - params.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U); - params.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U); - params.block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U); - params.tile_width_spacing = 1; - params.pixel_format = PixelFormatFromDepthFormat(regs.zeta.format); - params.type = GetFormatType(params.pixel_format); - params.width = regs.zeta_width; - params.height = regs.zeta_height; - params.pitch = 0; - params.num_levels = 1; - params.emulated_levels = 1; - const bool is_layered = regs.zeta_layers > 1 && params.block_depth == 0; - params.is_layered = is_layered; - params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D; - params.depth = is_layered ? regs.zeta_layers.Value() : 1U; - return params; + const auto block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U); + const bool is_layered = regs.zeta_layers > 1 && block_depth == 0; + const auto pixel_format = PixelFormatFromDepthFormat(regs.zeta.format); + + return { + .is_tiled = regs.zeta.memory_layout.type == + Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear, + .srgb_conversion = false, + .is_layered = is_layered, + .block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U), + .block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U), + .block_depth = block_depth, + .tile_width_spacing = 1, + .width = regs.zeta_width, + .height = regs.zeta_height, + .depth = is_layered ? regs.zeta_layers.Value() : 1U, + .pitch = 0, + .num_levels = 1, + .emulated_levels = 1, + .pixel_format = pixel_format, + .type = GetFormatType(pixel_format), + .target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D, + }; } SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) { @@ -233,24 +235,29 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz SurfaceParams SurfaceParams::CreateForFermiCopySurface( const Tegra::Engines::Fermi2D::Regs::Surface& config) { - SurfaceParams params{}; - params.is_tiled = !config.linear; - params.srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB || - config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB; - params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 5U) : 0, - params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 5U) : 0, - params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 5U) : 0, - params.tile_width_spacing = 1; - params.pixel_format = PixelFormatFromRenderTargetFormat(config.format); - params.type = GetFormatType(params.pixel_format); - params.width = config.width; - params.height = config.height; - params.pitch = config.pitch; - // TODO(Rodrigo): Try to guess texture arrays from parameters - params.target = SurfaceTarget::Texture2D; - params.depth = 1; - params.num_levels = 1; - params.emulated_levels = 1; + const bool is_tiled = !config.linear; + const auto pixel_format = PixelFormatFromRenderTargetFormat(config.format); + + SurfaceParams params{ + .is_tiled = is_tiled, + .srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB || + config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB, + .block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U, + .block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U, + .block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U, + .tile_width_spacing = 1, + .width = config.width, + .height = config.height, + .depth = 1, + .pitch = config.pitch, + .num_levels = 1, + .emulated_levels = 1, + .pixel_format = pixel_format, + .type = GetFormatType(pixel_format), + // TODO(Rodrigo): Try to guess texture arrays from parameters + .target = SurfaceTarget::Texture2D, + }; + params.is_layered = params.IsLayered(); return params; } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5738787ac..8fc322b30 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -567,7 +567,7 @@ void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_p screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32); renderer.RequestScreenshot( screenshot_image.bits(), - [=] { + [=, this] { const std::string std_screenshot_path = screenshot_path.toStdString(); if (screenshot_image.mirrored(false, true).save(screenshot_path)) { LOG_INFO(Frontend, "Screenshot saved to \"{}\"", std_screenshot_path); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 94b96afb4..cb71b8d11 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -578,7 +578,6 @@ void Config::ReadPathValues() { UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); - UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString(); UISettings::values.game_dir_deprecated = ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); UISettings::values.game_dir_deprecated_deepscan = @@ -673,6 +672,22 @@ void Config::ReadRendererValues() { qt_config->endGroup(); } +void Config::ReadScreenshotValues() { + qt_config->beginGroup(QStringLiteral("Screenshots")); + + UISettings::values.enable_screenshot_save_as = + ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); + FileUtil::GetUserPath( + FileUtil::UserPath::ScreenshotsDir, + qt_config + ->value(QStringLiteral("screenshot_path"), QString::fromStdString(FileUtil::GetUserPath( + FileUtil::UserPath::ScreenshotsDir))) + .toString() + .toStdString()); + + qt_config->endGroup(); +} + void Config::ReadShortcutValues() { qt_config->beginGroup(QStringLiteral("Shortcuts")); @@ -754,6 +769,7 @@ void Config::ReadUIValues() { ReadUIGamelistValues(); ReadUILayoutValues(); ReadPathValues(); + ReadScreenshotValues(); ReadShortcutValues(); UISettings::values.single_window_mode = @@ -1083,7 +1099,6 @@ void Config::SavePathValues() { WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); - WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path); qt_config->beginWriteArray(QStringLiteral("gamedirs")); for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { qt_config->setArrayIndex(i); @@ -1159,6 +1174,17 @@ void Config::SaveRendererValues() { qt_config->endGroup(); } +void Config::SaveScreenshotValues() { + qt_config->beginGroup(QStringLiteral("Screenshots")); + + WriteSetting(QStringLiteral("enable_screenshot_save_as"), + UISettings::values.enable_screenshot_save_as); + WriteSetting(QStringLiteral("screenshot_path"), + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); + + qt_config->endGroup(); +} + void Config::SaveShortcutValues() { qt_config->beginGroup(QStringLiteral("Shortcuts")); @@ -1221,6 +1247,7 @@ void Config::SaveUIValues() { SaveUIGamelistValues(); SaveUILayoutValues(); SavePathValues(); + SaveScreenshotValues(); SaveShortcutValues(); WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 8e815f829..e5f39b040 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -51,6 +51,7 @@ private: void ReadPathValues(); void ReadCpuValues(); void ReadRendererValues(); + void ReadScreenshotValues(); void ReadShortcutValues(); void ReadSystemValues(); void ReadUIValues(); @@ -76,6 +77,7 @@ private: void SavePathValues(); void SaveCpuValues(); void SaveRendererValues(); + void SaveScreenshotValues(); void SaveShortcutValues(); void SaveSystemValues(); void SaveUIValues(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 00433926d..b1850bc95 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -280,9 +280,9 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } button->setContextMenuPolicy(Qt::CustomContextMenu); - connect(button, &QPushButton::clicked, [=] { + connect(button, &QPushButton::clicked, [=, this] { HandleClick(button_map[button_id], - [=](Common::ParamPackage params) { + [=, this](Common::ParamPackage params) { // Workaround for ZL & ZR for analog triggers like on XBOX controllors. // Analog triggers (from controllers like the XBOX controller) would not // work due to a different range of their signals (from 0 to 255 on @@ -300,19 +300,20 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i }, InputCommon::Polling::DeviceType::Button); }); - connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { - QMenu context_menu; - context_menu.addAction(tr("Clear"), [&] { - buttons_param[button_id].Clear(); - button_map[button_id]->setText(tr("[not set]")); - }); - context_menu.addAction(tr("Restore Default"), [&] { - buttons_param[button_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; - button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); - }); - context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); - }); + connect(button, &QPushButton::customContextMenuRequested, + [=, this](const QPoint& menu_location) { + QMenu context_menu; + context_menu.addAction(tr("Clear"), [&] { + buttons_param[button_id].Clear(); + button_map[button_id]->setText(tr("[not set]")); + }); + context_menu.addAction(tr("Restore Default"), [&] { + buttons_param[button_id] = Common::ParamPackage{ + InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])}; + button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); + }); + context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); + }); } for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) { @@ -323,16 +324,16 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i } analog_button->setContextMenuPolicy(Qt::CustomContextMenu); - connect(analog_button, &QPushButton::clicked, [=]() { + connect(analog_button, &QPushButton::clicked, [=, this] { HandleClick(analog_map_buttons[analog_id][sub_button_id], - [=](const Common::ParamPackage& params) { + [=, this](const Common::ParamPackage& params) { SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]); }, InputCommon::Polling::DeviceType::Button); }); connect(analog_button, &QPushButton::customContextMenuRequested, - [=](const QPoint& menu_location) { + [=, this](const QPoint& menu_location) { QMenu context_menu; context_menu.addAction(tr("Clear"), [&] { analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]); @@ -350,32 +351,35 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i menu_location)); }); } - connect(analog_map_stick[analog_id], &QPushButton::clicked, [=] { + connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] { if (QMessageBox::information( this, tr("Information"), tr("After pressing OK, first move your joystick horizontally, " "and then vertically."), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { - HandleClick( - analog_map_stick[analog_id], - [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, - InputCommon::Polling::DeviceType::Analog); + HandleClick(analog_map_stick[analog_id], + [=, this](const Common::ParamPackage& params) { + analogs_param[analog_id] = params; + }, + InputCommon::Polling::DeviceType::Analog); } }); - connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, [=] { - const float slider_value = analog_map_deadzone_and_modifier_slider[analog_id]->value(); - if (analogs_param[analog_id].Get("engine", "") == "sdl" || - analogs_param[analog_id].Get("engine", "") == "gcpad") { - analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( - tr("Deadzone: %1%").arg(slider_value)); - analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); - } else { - analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( - tr("Modifier Scale: %1%").arg(slider_value)); - analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); - } - }); + connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged, + [=, this] { + const float slider_value = + analog_map_deadzone_and_modifier_slider[analog_id]->value(); + if (analogs_param[analog_id].Get("engine", "") == "sdl" || + analogs_param[analog_id].Get("engine", "") == "gcpad") { + analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( + tr("Deadzone: %1%").arg(slider_value)); + analogs_param[analog_id].Set("deadzone", slider_value / 100.0f); + } else { + analog_map_deadzone_and_modifier_slider_label[analog_id]->setText( + tr("Modifier Scale: %1%").arg(slider_value)); + analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f); + } + }); } connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp index e0647ea5b..ea2549363 100644 --- a/src/yuzu/configuration/configure_mouse_advanced.cpp +++ b/src/yuzu/configuration/configure_mouse_advanced.cpp @@ -83,25 +83,28 @@ ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent) } button->setContextMenuPolicy(Qt::CustomContextMenu); - connect(button, &QPushButton::clicked, [=] { - HandleClick( - button_map[button_id], - [=](const Common::ParamPackage& params) { buttons_param[button_id] = params; }, - InputCommon::Polling::DeviceType::Button); - }); - connect(button, &QPushButton::customContextMenuRequested, [=](const QPoint& menu_location) { - QMenu context_menu; - context_menu.addAction(tr("Clear"), [&] { - buttons_param[button_id].Clear(); - button_map[button_id]->setText(tr("[not set]")); - }); - context_menu.addAction(tr("Restore Default"), [&] { - buttons_param[button_id] = Common::ParamPackage{ - InputCommon::GenerateKeyboardParam(Config::default_mouse_buttons[button_id])}; - button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); - }); - context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); + connect(button, &QPushButton::clicked, [=, this] { + HandleClick(button_map[button_id], + [=, this](const Common::ParamPackage& params) { + buttons_param[button_id] = params; + }, + InputCommon::Polling::DeviceType::Button); }); + connect(button, &QPushButton::customContextMenuRequested, + [=, this](const QPoint& menu_location) { + QMenu context_menu; + context_menu.addAction(tr("Clear"), [&] { + buttons_param[button_id].Clear(); + button_map[button_id]->setText(tr("[not set]")); + }); + context_menu.addAction(tr("Restore Default"), [&] { + buttons_param[button_id] = + Common::ParamPackage{InputCommon::GenerateKeyboardParam( + Config::default_mouse_buttons[button_id])}; + button_map[button_id]->setText(ButtonToText(buttons_param[button_id])); + }); + context_menu.exec(button_map[button_id]->mapToGlobal(menu_location)); + }); } connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); }); diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 24b6c5b72..2c20b68d0 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp @@ -4,9 +4,11 @@ #include <array> #include <utility> +#include <QFileDialog> #include <QDirIterator> #include "common/common_types.h" +#include "common/file_util.h" #include "core/settings.h" #include "ui_configure_ui.h" #include "yuzu/configuration/configure_ui.h" @@ -52,9 +54,21 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur // Update text ComboBoxes after user interaction. connect(ui->row_1_text_combobox, QOverload<int>::of(&QComboBox::activated), - [=]() { ConfigureUi::UpdateSecondRowComboBox(); }); + [this] { ConfigureUi::UpdateSecondRowComboBox(); }); connect(ui->row_2_text_combobox, QOverload<int>::of(&QComboBox::activated), - [=]() { ConfigureUi::UpdateFirstRowComboBox(); }); + [this] { ConfigureUi::UpdateFirstRowComboBox(); }); + + // Set screenshot path to user specification. + connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { + const QString& filename = + QFileDialog::getExistingDirectory( + this, tr("Select Screenshots Path..."), + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))) + + QDir::separator(); + if (!filename.isEmpty()) { + ui->screenshot_path_edit->setText(filename); + } + }); } ConfigureUi::~ConfigureUi() = default; @@ -66,6 +80,10 @@ void ConfigureUi::ApplyConfiguration() { UISettings::values.icon_size = ui->icon_size_combobox->currentData().toUInt(); UISettings::values.row_1_text_id = ui->row_1_text_combobox->currentData().toUInt(); UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); + + UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); + FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir, + ui->screenshot_path_edit->text().toStdString()); Settings::Apply(); } @@ -80,6 +98,10 @@ void ConfigureUi::SetConfiguration() { ui->show_add_ons->setChecked(UISettings::values.show_add_ons); ui->icon_size_combobox->setCurrentIndex( ui->icon_size_combobox->findData(UISettings::values.icon_size)); + + ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); + ui->screenshot_path_edit->setText( + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir))); } void ConfigureUi::changeEvent(QEvent* event) { diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui index 0b81747d7..d895b799f 100644 --- a/src/yuzu/configuration/configure_ui.ui +++ b/src/yuzu/configuration/configure_ui.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>300</width> - <height>377</height> + <width>363</width> + <height>391</height> </rect> </property> <property name="windowTitle"> @@ -128,6 +128,47 @@ </widget> </item> <item> + <widget class="QGroupBox" name="screenshots_GroupBox"> + <property name="title"> + <string>Screenshots</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="enable_screenshot_save_as"> + <property name="text"> + <string>Ask Where To Save Screenshots (Windows Only)</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Screenshots Path: </string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="screenshot_path_edit"/> + </item> + <item> + <widget class="QToolButton" name="screenshot_path_button"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index ab7fc7a24..62acc3720 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -474,28 +474,56 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) { QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); - QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); + QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location")); QAction* open_transferable_shader_cache = context_menu.addAction(tr("Open Transferable Shader Cache")); context_menu.addSeparator(); + 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")); + remove_menu->addSeparator(); + QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents")); QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); context_menu.addSeparator(); QAction* properties = context_menu.addAction(tr("Properties")); - open_save_location->setEnabled(program_id != 0); + open_save_location->setVisible(program_id != 0); + open_mod_location->setVisible(program_id != 0); + open_transferable_shader_cache->setVisible(program_id != 0); + remove_update->setVisible(program_id != 0); + remove_dlc->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); navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0); connect(open_save_location, &QAction::triggered, [this, program_id, path]() { emit OpenFolderRequested(GameListOpenTarget::SaveData, path); }); - connect(open_lfs_location, &QAction::triggered, [this, program_id, path]() { + connect(open_mod_location, &QAction::triggered, [this, program_id, path]() { emit OpenFolderRequested(GameListOpenTarget::ModData, path); }); connect(open_transferable_shader_cache, &QAction::triggered, [this, program_id]() { emit OpenTransferableShaderCacheRequested(program_id); }); + connect(remove_all_content, &QAction::triggered, [this, program_id]() { + emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Game); + }); + connect(remove_update, &QAction::triggered, [this, program_id]() { + emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::Update); + }); + connect(remove_dlc, &QAction::triggered, [this, program_id]() { + emit RemoveInstalledEntryRequested(program_id, InstalledEntryType::AddOnContent); + }); + connect(remove_shader_cache, &QAction::triggered, [this, program_id]() { + emit RemoveFileRequested(program_id, GameListRemoveTarget::ShaderCache); + }); + connect(remove_custom_config, &QAction::triggered, [this, program_id]() { + emit RemoveFileRequested(program_id, GameListRemoveTarget::CustomConfiguration); + }); connect(dump_romfs, &QAction::triggered, [this, program_id, path]() { emit DumpRomFSRequested(program_id, path); }); connect(copy_tid, &QAction::triggered, diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index a38cb2fc3..483835cce 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -39,6 +39,17 @@ enum class GameListOpenTarget { ModData, }; +enum class GameListRemoveTarget { + ShaderCache, + CustomConfiguration, +}; + +enum class InstalledEntryType { + Game, + Update, + AddOnContent, +}; + class GameList : public QWidget { Q_OBJECT @@ -75,6 +86,8 @@ signals: void ShouldCancelWorker(); void OpenFolderRequested(GameListOpenTarget target, const std::string& game_path); void OpenTransferableShaderCacheRequested(u64 program_id); + void RemoveInstalledEntryRequested(u64 program_id, InstalledEntryType type); + void RemoveFileRequested(u64 program_id, GameListRemoveTarget target); void DumpRomFSRequested(u64 program_id, const std::string& game_path); void CopyTIDRequested(u64 program_id); void NavigateToGamedbEntryRequested(u64 program_id, @@ -117,8 +130,6 @@ private: friend class GameListSearchField; }; -Q_DECLARE_METATYPE(GameListOpenTarget); - class GameListPlaceholder : public QWidget { Q_OBJECT public: diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 31a635176..592993c36 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -583,7 +583,7 @@ void GMainWindow::InitializeWidgets() { renderer_status_button->setObjectName(QStringLiteral("RendererStatusBarButton")); renderer_status_button->setCheckable(true); renderer_status_button->setFocusPolicy(Qt::NoFocus); - connect(renderer_status_button, &QPushButton::toggled, [=](bool checked) { + connect(renderer_status_button, &QPushButton::toggled, [this](bool checked) { renderer_status_button->setText(checked ? tr("VULKAN") : tr("OPENGL")); }); renderer_status_button->toggle(); @@ -595,7 +595,7 @@ void GMainWindow::InitializeWidgets() { #else renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan); - connect(renderer_status_button, &QPushButton::clicked, [=] { + connect(renderer_status_button, &QPushButton::clicked, [this] { if (emulation_running) { return; } @@ -847,6 +847,9 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); connect(game_list, &GameList::OpenTransferableShaderCacheRequested, this, &GMainWindow::OnTransferableShaderCacheOpenFile); + connect(game_list, &GameList::RemoveInstalledEntryRequested, this, + &GMainWindow::OnGameListRemoveInstalledEntry); + connect(game_list, &GameList::RemoveFileRequested, this, &GMainWindow::OnGameListRemoveFile); connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, @@ -1257,7 +1260,6 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str case GameListOpenTarget::SaveData: { open_target = tr("Save Data"); const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); - ASSERT(program_id != 0); if (has_user_save) { // User save data @@ -1322,14 +1324,12 @@ void GMainWindow::OnGameListOpenFolder(GameListOpenTarget target, const std::str } void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { - ASSERT(program_id != 0); - const QString shader_dir = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); - const QString tranferable_shader_cache_folder_path = + const QString transferable_shader_cache_folder_path = shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); const QString transferable_shader_cache_file_path = - tranferable_shader_cache_folder_path + QDir::separator() + + transferable_shader_cache_folder_path + QDir::separator() + QString::fromStdString(fmt::format("{:016X}.bin", program_id)); if (!QFile::exists(transferable_shader_cache_file_path)) { @@ -1350,7 +1350,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { param << QDir::toNativeSeparators(transferable_shader_cache_file_path); QProcess::startDetached(explorer, param); #else - QDesktopServices::openUrl(QUrl::fromLocalFile(tranferable_shader_cache_folder_path)); + QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path)); #endif } @@ -1394,6 +1394,174 @@ static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src return true; } +void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type) { + const QString entry_type = [this, type] { + switch (type) { + case InstalledEntryType::Game: + return tr("Contents"); + case InstalledEntryType::Update: + return tr("Update"); + case InstalledEntryType::AddOnContent: + return tr("DLC"); + default: + return QString{}; + } + }(); + + if (QMessageBox::question( + this, tr("Remove Entry"), tr("Remove Installed Game %1?").arg(entry_type), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) { + return; + } + + switch (type) { + case InstalledEntryType::Game: + RemoveBaseContent(program_id, entry_type); + [[fallthrough]]; + case InstalledEntryType::Update: + RemoveUpdateContent(program_id, entry_type); + if (type != InstalledEntryType::Game) { + break; + } + [[fallthrough]]; + case InstalledEntryType::AddOnContent: + RemoveAddOnContent(program_id, entry_type); + break; + } + FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + + "game_list"); + game_list->PopulateAsync(UISettings::values.game_dirs); +} + +void GMainWindow::RemoveBaseContent(u64 program_id, const QString& entry_type) { + const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); + const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(program_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(program_id); + + if (res) { + QMessageBox::information(this, tr("Successfully Removed"), + tr("Successfully removed the installed base game.")); + } else { + QMessageBox::warning( + this, tr("Error Removing %1").arg(entry_type), + tr("The base game is not installed in the NAND and cannot be removed.")); + } +} + +void GMainWindow::RemoveUpdateContent(u64 program_id, const QString& entry_type) { + const auto update_id = program_id | 0x800; + const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); + const auto res = fs_controller.GetUserNANDContents()->RemoveExistingEntry(update_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(update_id); + + if (res) { + QMessageBox::information(this, tr("Successfully Removed"), + tr("Successfully removed the installed update.")); + } else { + QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), + tr("There is no update installed for this title.")); + } +} + +void GMainWindow::RemoveAddOnContent(u64 program_id, const QString& entry_type) { + u32 count{}; + const auto& fs_controller = Core::System::GetInstance().GetFileSystemController(); + const auto dlc_entries = Core::System::GetInstance().GetContentProvider().ListEntriesFilter( + FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); + + for (const auto& entry : dlc_entries) { + if ((entry.title_id & DLC_BASE_TITLE_ID_MASK) == program_id) { + const auto res = + fs_controller.GetUserNANDContents()->RemoveExistingEntry(entry.title_id) || + fs_controller.GetSDMCContents()->RemoveExistingEntry(entry.title_id); + if (res) { + ++count; + } + } + } + + if (count == 0) { + QMessageBox::warning(this, tr("Error Removing %1").arg(entry_type), + tr("There are no DLC installed for this title.")); + return; + } + + QMessageBox::information(this, tr("Successfully Removed"), + tr("Successfully removed %1 installed DLC.").arg(count)); +} + +void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target) { + const QString question = [this, target] { + switch (target) { + case GameListRemoveTarget::ShaderCache: + return tr("Delete Transferable Shader Cache?"); + case GameListRemoveTarget::CustomConfiguration: + return tr("Remove Custom Game Configuration?"); + default: + return QString{}; + } + }(); + + if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No, + QMessageBox::No) != QMessageBox::Yes) { + return; + } + + switch (target) { + case GameListRemoveTarget::ShaderCache: + RemoveTransferableShaderCache(program_id); + break; + case GameListRemoveTarget::CustomConfiguration: + RemoveCustomConfiguration(program_id); + break; + } +} + +void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { + const QString shader_dir = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)); + const QString transferable_shader_cache_folder_path = + shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); + const QString transferable_shader_cache_file_path = + transferable_shader_cache_folder_path + QDir::separator() + + QString::fromStdString(fmt::format("{:016X}.bin", program_id)); + + if (!QFile::exists(transferable_shader_cache_file_path)) { + QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), + tr("A shader cache for this title does not exist.")); + return; + } + + if (QFile::remove(transferable_shader_cache_file_path)) { + QMessageBox::information(this, tr("Successfully Removed"), + tr("Successfully removed the transferable shader cache.")); + } else { + QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), + tr("Failed to remove the transferable shader cache.")); + } +} + +void GMainWindow::RemoveCustomConfiguration(u64 program_id) { + const QString config_dir = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir)); + const QString custom_config_file_path = + config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id)); + + if (!QFile::exists(custom_config_file_path)) { + QMessageBox::warning(this, tr("Error Removing Custom Configuration"), + tr("A custom configuration for this title does not exist.")); + return; + } + + if (QFile::remove(custom_config_file_path)) { + QMessageBox::information(this, tr("Successfully Removed"), + tr("Successfully removed the custom game configuration.")); + } else { + QMessageBox::warning(this, tr("Error Removing Custom Configuration"), + tr("Failed to remove the custom game configuration.")); + } +} + void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { const auto failed = [this] { QMessageBox::warning(this, tr("RomFS Extraction Failed!"), @@ -1655,7 +1823,7 @@ void GMainWindow::OnMenuInstallToNAND() { ui.action_Install_File_NAND->setEnabled(false); - install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this); + install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & ~Qt::WindowMaximizeButtonHint); install_progress->setAttribute(Qt::WA_DeleteOnClose, true); @@ -1705,18 +1873,18 @@ void GMainWindow::OnMenuInstallToNAND() { install_progress->close(); const QString install_results = - (new_files.isEmpty() ? QStringLiteral("") + (new_files.isEmpty() ? QString{} : tr("%n file(s) were newly installed\n", "", new_files.size())) + (overwritten_files.isEmpty() - ? QStringLiteral("") + ? QString{} : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + - (failed_files.isEmpty() ? QStringLiteral("") + (failed_files.isEmpty() ? QString{} : tr("%n file(s) failed to install\n", "", failed_files.size())); QMessageBox::information(this, tr("Install Results"), install_results); - game_list->PopulateAsync(UISettings::values.game_dirs); FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + "game_list"); + game_list->PopulateAsync(UISettings::values.game_dirs); ui.action_Install_File_NAND->setEnabled(true); } @@ -2153,17 +2321,28 @@ void GMainWindow::OnToggleFilterBar() { void GMainWindow::OnCaptureScreenshot() { OnPauseGame(); - QFileDialog png_dialog(this, tr("Capture Screenshot"), UISettings::values.screenshot_path, - tr("PNG Image (*.png)")); - png_dialog.setAcceptMode(QFileDialog::AcceptSave); - png_dialog.setDefaultSuffix(QStringLiteral("png")); - if (png_dialog.exec()) { - const QString path = png_dialog.selectedFiles().first(); - if (!path.isEmpty()) { - UISettings::values.screenshot_path = QFileInfo(path).path(); - render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); + + const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); + const auto screenshot_path = + QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::ScreenshotsDir)); + const auto date = + QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); + QString filename = QStringLiteral("%1%2_%3.png") + .arg(screenshot_path) + .arg(title_id, 16, 16, QLatin1Char{'0'}) + .arg(date); + +#ifdef _WIN32 + if (UISettings::values.enable_screenshot_save_as) { + filename = QFileDialog::getSaveFileName(this, tr("Capture Screenshot"), filename, + tr("PNG Image (*.png)")); + if (filename.isEmpty()) { + OnStartGame(); + return; } } +#endif + render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, filename); OnStartGame(); } @@ -2202,8 +2381,7 @@ void GMainWindow::UpdateStatusBar() { if (shaders_building != 0) { shader_building_label->setText( - tr("Building: %1 shader").arg(shaders_building) + - (shaders_building != 1 ? QString::fromStdString("s") : QString::fromStdString(""))); + tr("Building: %n shader(s)", "", static_cast<int>(shaders_building))); shader_building_label->setVisible(true); } else { shader_building_label->setVisible(false); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index db573d606..73a44a3bf 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -32,6 +32,8 @@ class QPushButton; class QProgressDialog; class WaitTreeWidget; enum class GameListOpenTarget; +enum class GameListRemoveTarget; +enum class InstalledEntryType; class GameListPlaceholder; namespace Core::Frontend { @@ -198,6 +200,8 @@ private slots: void OnGameListLoadFile(QString game_path); void OnGameListOpenFolder(GameListOpenTarget target, const std::string& game_path); void OnTransferableShaderCacheOpenFile(u64 program_id); + void OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryType type); + void OnGameListRemoveFile(u64 program_id, GameListRemoveTarget target); void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, @@ -229,6 +233,11 @@ private slots: void OnLanguageChanged(const QString& locale); 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 RemoveCustomConfiguration(u64 program_id); std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); InstallResult InstallNSPXCI(const QString& filename); InstallResult InstallNCA(const QString& filename); diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index ac7b9aef6..bbfeafc55 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -66,11 +66,11 @@ struct Values { // Discord RPC bool enable_discord_presence; + bool enable_screenshot_save_as; u16 screenshot_resolution_factor; QString roms_path; QString symbols_path; - QString screenshot_path; QString game_dir_deprecated; bool game_dir_deprecated_deepscan; QVector<UISettings::GameDir> game_dirs; |