From 637ab14ae6e42d656ad0a920487014e11461d76a Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 14 Sep 2020 19:04:49 -0400 Subject: [PATCH 1/3] patch_manager: Make a few functions internally linked These functions are only used within this translation unit, so we can make them internally linked. --- src/core/file_sys/patch_manager.cpp | 15 ++++++++++++--- src/core/file_sys/patch_manager.h | 12 ------------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c228d253e..e91ff968c 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -27,6 +27,7 @@ #include "core/settings.h" namespace FileSys { +namespace { constexpr u64 SINGLE_BYTE_MODULUS = 0x100; constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; @@ -36,7 +37,13 @@ constexpr std::array EXEFS_FILE_NAMES{ "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", }; -std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { +enum class TitleVersionFormat : u8 { + ThreeElements, ///< vX.Y.Z + FourElements, ///< vX.Y.Z.W +}; + +std::string FormatTitleVersion(u32 version, + TitleVersionFormat format = TitleVersionFormat::ThreeElements) { std::array bytes{}; bytes[0] = version % SINGLE_BYTE_MODULUS; for (std::size_t i = 1; i < bytes.size(); ++i) { @@ -49,6 +56,8 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); } +// Returns a directory with name matching name case-insensitive. Returns nullptr if directory +// doesn't have a directory with name. VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) { #ifdef _WIN32 return dir->GetSubdirectory(name); @@ -64,6 +73,7 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) return nullptr; #endif } +} // Anonymous namespace PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} @@ -472,8 +482,7 @@ std::map> PatchManager::GetPatchVersionNam if (meta_ver.value_or(0) == 0) { out.insert_or_assign(update_label, ""); } else { - out.insert_or_assign( - update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); + out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver)); } } else if (update_raw != nullptr) { out.insert_or_assign(update_label, "PACKED"); diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 532f4995f..92a7c6e04 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -22,18 +22,6 @@ namespace FileSys { class NCA; class NACP; -enum class TitleVersionFormat : u8 { - ThreeElements, ///< vX.Y.Z - FourElements, ///< vX.Y.Z.W -}; - -std::string FormatTitleVersion(u32 version, - TitleVersionFormat format = TitleVersionFormat::ThreeElements); - -// Returns a directory with name matching name case-insensitive. Returns nullptr if directory -// doesn't have a directory with name. -VirtualDir FindSubdirectoryCaseless(VirtualDir dir, std::string_view name); - // A centralized class to manage patches to games. class PatchManager { public: From a4392c24cf67184183cdfa8a5b9b21dbf2686732 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 14 Sep 2020 19:09:49 -0400 Subject: [PATCH 2/3] patch_manager: Make use of type aliases We can use these to avoid typing the same type redundantly. This way, if these ever change, only a single location needs to be modified. --- src/core/file_sys/patch_manager.cpp | 112 +++++++++++++++------------- src/core/file_sys/patch_manager.h | 36 +++++---- 2 files changed, 79 insertions(+), 69 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index e91ff968c..2b7bd2832 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -73,6 +73,44 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) return nullptr; #endif } + +std::optional> ReadCheatFileFromFolder( + const Core::System& system, u64 title_id, const PatchManager::BuildID& build_id_, + const VirtualDir& base_path, bool upper) { + const auto build_id_raw = Common::HexToString(build_id_, upper); + const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); + const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); + + if (file == nullptr) { + LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", + title_id, build_id); + return std::nullopt; + } + + std::vector data(file->GetSize()); + if (file->Read(data.data(), data.size()) != data.size()) { + LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", + title_id, build_id); + return std::nullopt; + } + + Core::Memory::TextCheatParser parser; + return parser.Parse(system, + std::string_view(reinterpret_cast(data.data()), data.size())); +} + +void AppendCommaIfNotEmpty(std::string& to, std::string_view with) { + if (to.empty()) { + to += with; + } else { + to += ", "; + to += with; + } +} + +bool IsDirValidAndNonEmpty(const VirtualDir& dir) { + return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); +} } // Anonymous namespace PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} @@ -255,7 +293,7 @@ std::vector PatchManager::PatchNSO(const std::vector& nso, const std::st return out; } -bool PatchManager::HasNSOPatch(const std::array& build_id_) const { +bool PatchManager::HasNSOPatch(const BuildID& build_id_) const { const auto build_id_raw = Common::HexToString(build_id_); const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); @@ -275,36 +313,8 @@ bool PatchManager::HasNSOPatch(const std::array& build_id_) const { return !CollectPatches(patch_dirs, build_id).empty(); } -namespace { -std::optional> ReadCheatFileFromFolder( - const Core::System& system, u64 title_id, const std::array& build_id_, - const VirtualDir& base_path, bool upper) { - const auto build_id_raw = Common::HexToString(build_id_, upper); - const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); - const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); - - if (file == nullptr) { - LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", - title_id, build_id); - return std::nullopt; - } - - std::vector data(file->GetSize()); - if (file->Read(data.data(), data.size()) != data.size()) { - LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", - title_id, build_id); - return std::nullopt; - } - - Core::Memory::TextCheatParser parser; - return parser.Parse(system, - std::string_view(reinterpret_cast(data.data()), data.size())); -} - -} // Anonymous namespace - std::vector PatchManager::CreateCheatList( - const Core::System& system, const std::array& build_id_) const { + const Core::System& system, const BuildID& build_id_) const { const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); if (load_dir == nullptr) { LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); @@ -445,21 +455,11 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content return romfs; } -static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { - if (to.empty()) - to += with; - else - to += ", " + with; -} - -static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { - return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); -} - -std::map> PatchManager::GetPatchVersionNames( - VirtualFile update_raw) const { - if (title_id == 0) +PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const { + if (title_id == 0) { return {}; + } + std::map> out; const auto& installed = Core::System::GetInstance().GetContentProvider(); const auto& disabled = Settings::values.disabled_addons[title_id]; @@ -571,40 +571,46 @@ std::optional PatchManager::GetGameVersion() const { return installed.GetEntryVersion(title_id); } -std::pair, VirtualFile> PatchManager::GetControlMetadata() const { +PatchManager::Metadata PatchManager::GetControlMetadata() const { const auto& installed = Core::System::GetInstance().GetContentProvider(); const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); - if (base_control_nca == nullptr) + if (base_control_nca == nullptr) { return {}; + } return ParseControlNCA(*base_control_nca); } -std::pair, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { +PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const { const auto base_romfs = nca.GetRomFS(); - if (base_romfs == nullptr) + if (base_romfs == nullptr) { return {}; + } const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); - if (romfs == nullptr) + if (romfs == nullptr) { return {}; + } const auto extracted = ExtractRomFS(romfs); - if (extracted == nullptr) + if (extracted == nullptr) { return {}; + } auto nacp_file = extracted->GetFile("control.nacp"); - if (nacp_file == nullptr) + if (nacp_file == nullptr) { nacp_file = extracted->GetFile("Control.nacp"); + } auto nacp = nacp_file == nullptr ? nullptr : std::make_unique(nacp_file); VirtualFile icon_file; for (const auto& language : FileSys::LANGUAGE_NAMES) { - icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); - if (icon_file != nullptr) + icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat")); + if (icon_file != nullptr) { break; + } } return {std::move(nacp), icon_file}; diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 92a7c6e04..1f28c6241 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -25,55 +25,59 @@ class NACP; // A centralized class to manage patches to games. class PatchManager { public: + using BuildID = std::array; + using Metadata = std::pair, VirtualFile>; + using PatchVersionNames = std::map>; + explicit PatchManager(u64 title_id); ~PatchManager(); - u64 GetTitleID() const; + [[nodiscard]] u64 GetTitleID() const; // Currently tracked ExeFS patches: // - Game Updates - VirtualDir PatchExeFS(VirtualDir exefs) const; + [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const; // Currently tracked NSO patches: // - IPS // - IPSwitch - std::vector PatchNSO(const std::vector& nso, const std::string& name) const; + [[nodiscard]] std::vector PatchNSO(const std::vector& nso, + const std::string& name) const; // Checks to see if PatchNSO() will have any effect given the NSO's build ID. // Used to prevent expensive copies in NSO loader. - bool HasNSOPatch(const std::array& build_id) const; + [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const; // Creates a CheatList object with all - std::vector CreateCheatList( - const Core::System& system, const std::array& build_id) const; + [[nodiscard]] std::vector CreateCheatList( + const Core::System& system, const BuildID& build_id) const; // Currently tracked RomFS patches: // - Game Updates // - LayeredFS - VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, - ContentRecordType type = ContentRecordType::Program, - VirtualFile update_raw = nullptr) const; + [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, + ContentRecordType type = ContentRecordType::Program, + VirtualFile update_raw = nullptr) const; // Returns a vector of pairs between patch names and patch versions. // i.e. Update 3.2.2 will return {"Update", "3.2.2"} - std::map> GetPatchVersionNames( - VirtualFile update_raw = nullptr) const; + [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const; // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be // std::nullopt - std::optional GetGameVersion() const; + [[nodiscard]] std::optional GetGameVersion() const; // Given title_id of the program, attempts to get the control data of the update and parse // it, falling back to the base control data. - std::pair, VirtualFile> GetControlMetadata() const; + [[nodiscard]] Metadata GetControlMetadata() const; // Version of GetControlMetadata that takes an arbitrary NCA - std::pair, VirtualFile> ParseControlNCA(const NCA& nca) const; + [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const; private: - std::vector CollectPatches(const std::vector& patch_dirs, - const std::string& build_id) const; + [[nodiscard]] std::vector CollectPatches(const std::vector& patch_dirs, + const std::string& build_id) const; u64 title_id; }; From 33e4a0b6c180a5e866e3a45b42e9749506485558 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Mon, 14 Sep 2020 19:19:56 -0400 Subject: [PATCH 3/3] patch_manager: Resolve implicit truncations in FormatTitleVersion() We make it explicit that we're truncating arithmetic here to resolve compiler warnings (even if the sizes weren't u32/u64 arithmetic generally promotes to int :<) --- src/core/file_sys/patch_manager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 2b7bd2832..87c354a43 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -45,14 +45,15 @@ enum class TitleVersionFormat : u8 { std::string FormatTitleVersion(u32 version, TitleVersionFormat format = TitleVersionFormat::ThreeElements) { std::array bytes{}; - bytes[0] = version % SINGLE_BYTE_MODULUS; + bytes[0] = static_cast(version % SINGLE_BYTE_MODULUS); for (std::size_t i = 1; i < bytes.size(); ++i) { version /= SINGLE_BYTE_MODULUS; - bytes[i] = version % SINGLE_BYTE_MODULUS; + bytes[i] = static_cast(version % SINGLE_BYTE_MODULUS); } - if (format == TitleVersionFormat::FourElements) + if (format == TitleVersionFormat::FourElements) { return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); + } return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); }