diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/file_sys/patch_manager.cpp | 112 | ||||
| -rw-r--r-- | 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) | |||
| 73 | return nullptr; | 73 | return nullptr; |
| 74 | #endif | 74 | #endif |
| 75 | } | 75 | } |
| 76 | |||
| 77 | std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( | ||
| 78 | const Core::System& system, u64 title_id, const PatchManager::BuildID& build_id_, | ||
| 79 | const VirtualDir& base_path, bool upper) { | ||
| 80 | const auto build_id_raw = Common::HexToString(build_id_, upper); | ||
| 81 | const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); | ||
| 82 | const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); | ||
| 83 | |||
| 84 | if (file == nullptr) { | ||
| 85 | LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", | ||
| 86 | title_id, build_id); | ||
| 87 | return std::nullopt; | ||
| 88 | } | ||
| 89 | |||
| 90 | std::vector<u8> data(file->GetSize()); | ||
| 91 | if (file->Read(data.data(), data.size()) != data.size()) { | ||
| 92 | LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", | ||
| 93 | title_id, build_id); | ||
| 94 | return std::nullopt; | ||
| 95 | } | ||
| 96 | |||
| 97 | Core::Memory::TextCheatParser parser; | ||
| 98 | return parser.Parse(system, | ||
| 99 | std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); | ||
| 100 | } | ||
| 101 | |||
| 102 | void AppendCommaIfNotEmpty(std::string& to, std::string_view with) { | ||
| 103 | if (to.empty()) { | ||
| 104 | to += with; | ||
| 105 | } else { | ||
| 106 | to += ", "; | ||
| 107 | to += with; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||
| 112 | return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | ||
| 113 | } | ||
| 76 | } // Anonymous namespace | 114 | } // Anonymous namespace |
| 77 | 115 | ||
| 78 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | 116 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} |
| @@ -255,7 +293,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | |||
| 255 | return out; | 293 | return out; |
| 256 | } | 294 | } |
| 257 | 295 | ||
| 258 | bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | 296 | bool PatchManager::HasNSOPatch(const BuildID& build_id_) const { |
| 259 | const auto build_id_raw = Common::HexToString(build_id_); | 297 | const auto build_id_raw = Common::HexToString(build_id_); |
| 260 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | 298 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); |
| 261 | 299 | ||
| @@ -275,36 +313,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 275 | return !CollectPatches(patch_dirs, build_id).empty(); | 313 | return !CollectPatches(patch_dirs, build_id).empty(); |
| 276 | } | 314 | } |
| 277 | 315 | ||
| 278 | namespace { | ||
| 279 | std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( | ||
| 280 | const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_, | ||
| 281 | const VirtualDir& base_path, bool upper) { | ||
| 282 | const auto build_id_raw = Common::HexToString(build_id_, upper); | ||
| 283 | const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); | ||
| 284 | const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); | ||
| 285 | |||
| 286 | if (file == nullptr) { | ||
| 287 | LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", | ||
| 288 | title_id, build_id); | ||
| 289 | return std::nullopt; | ||
| 290 | } | ||
| 291 | |||
| 292 | std::vector<u8> data(file->GetSize()); | ||
| 293 | if (file->Read(data.data(), data.size()) != data.size()) { | ||
| 294 | LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", | ||
| 295 | title_id, build_id); | ||
| 296 | return std::nullopt; | ||
| 297 | } | ||
| 298 | |||
| 299 | Core::Memory::TextCheatParser parser; | ||
| 300 | return parser.Parse(system, | ||
| 301 | std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); | ||
| 302 | } | ||
| 303 | |||
| 304 | } // Anonymous namespace | ||
| 305 | |||
| 306 | std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( | 316 | std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( |
| 307 | const Core::System& system, const std::array<u8, 32>& build_id_) const { | 317 | const Core::System& system, const BuildID& build_id_) const { |
| 308 | const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); | 318 | const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); |
| 309 | if (load_dir == nullptr) { | 319 | if (load_dir == nullptr) { |
| 310 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | 320 | 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 | |||
| 445 | return romfs; | 455 | return romfs; |
| 446 | } | 456 | } |
| 447 | 457 | ||
| 448 | static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { | 458 | PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const { |
| 449 | if (to.empty()) | 459 | if (title_id == 0) { |
| 450 | to += with; | ||
| 451 | else | ||
| 452 | to += ", " + with; | ||
| 453 | } | ||
| 454 | |||
| 455 | static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||
| 456 | return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | ||
| 457 | } | ||
| 458 | |||
| 459 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( | ||
| 460 | VirtualFile update_raw) const { | ||
| 461 | if (title_id == 0) | ||
| 462 | return {}; | 460 | return {}; |
| 461 | } | ||
| 462 | |||
| 463 | std::map<std::string, std::string, std::less<>> out; | 463 | std::map<std::string, std::string, std::less<>> out; |
| 464 | const auto& installed = Core::System::GetInstance().GetContentProvider(); | 464 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 465 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 465 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| @@ -571,40 +571,46 @@ std::optional<u32> PatchManager::GetGameVersion() const { | |||
| 571 | return installed.GetEntryVersion(title_id); | 571 | return installed.GetEntryVersion(title_id); |
| 572 | } | 572 | } |
| 573 | 573 | ||
| 574 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 574 | PatchManager::Metadata PatchManager::GetControlMetadata() const { |
| 575 | const auto& installed = Core::System::GetInstance().GetContentProvider(); | 575 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 576 | 576 | ||
| 577 | const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); | 577 | const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); |
| 578 | if (base_control_nca == nullptr) | 578 | if (base_control_nca == nullptr) { |
| 579 | return {}; | 579 | return {}; |
| 580 | } | ||
| 580 | 581 | ||
| 581 | return ParseControlNCA(*base_control_nca); | 582 | return ParseControlNCA(*base_control_nca); |
| 582 | } | 583 | } |
| 583 | 584 | ||
| 584 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { | 585 | PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const { |
| 585 | const auto base_romfs = nca.GetRomFS(); | 586 | const auto base_romfs = nca.GetRomFS(); |
| 586 | if (base_romfs == nullptr) | 587 | if (base_romfs == nullptr) { |
| 587 | return {}; | 588 | return {}; |
| 589 | } | ||
| 588 | 590 | ||
| 589 | const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); | 591 | const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); |
| 590 | if (romfs == nullptr) | 592 | if (romfs == nullptr) { |
| 591 | return {}; | 593 | return {}; |
| 594 | } | ||
| 592 | 595 | ||
| 593 | const auto extracted = ExtractRomFS(romfs); | 596 | const auto extracted = ExtractRomFS(romfs); |
| 594 | if (extracted == nullptr) | 597 | if (extracted == nullptr) { |
| 595 | return {}; | 598 | return {}; |
| 599 | } | ||
| 596 | 600 | ||
| 597 | auto nacp_file = extracted->GetFile("control.nacp"); | 601 | auto nacp_file = extracted->GetFile("control.nacp"); |
| 598 | if (nacp_file == nullptr) | 602 | if (nacp_file == nullptr) { |
| 599 | nacp_file = extracted->GetFile("Control.nacp"); | 603 | nacp_file = extracted->GetFile("Control.nacp"); |
| 604 | } | ||
| 600 | 605 | ||
| 601 | auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); | 606 | auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); |
| 602 | 607 | ||
| 603 | VirtualFile icon_file; | 608 | VirtualFile icon_file; |
| 604 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | 609 | for (const auto& language : FileSys::LANGUAGE_NAMES) { |
| 605 | icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); | 610 | icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat")); |
| 606 | if (icon_file != nullptr) | 611 | if (icon_file != nullptr) { |
| 607 | break; | 612 | break; |
| 613 | } | ||
| 608 | } | 614 | } |
| 609 | 615 | ||
| 610 | return {std::move(nacp), icon_file}; | 616 | 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; | |||
| 25 | // A centralized class to manage patches to games. | 25 | // A centralized class to manage patches to games. |
| 26 | class PatchManager { | 26 | class PatchManager { |
| 27 | public: | 27 | public: |
| 28 | using BuildID = std::array<u8, 0x20>; | ||
| 29 | using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; | ||
| 30 | using PatchVersionNames = std::map<std::string, std::string, std::less<>>; | ||
| 31 | |||
| 28 | explicit PatchManager(u64 title_id); | 32 | explicit PatchManager(u64 title_id); |
| 29 | ~PatchManager(); | 33 | ~PatchManager(); |
| 30 | 34 | ||
| 31 | u64 GetTitleID() const; | 35 | [[nodiscard]] u64 GetTitleID() const; |
| 32 | 36 | ||
| 33 | // Currently tracked ExeFS patches: | 37 | // Currently tracked ExeFS patches: |
| 34 | // - Game Updates | 38 | // - Game Updates |
| 35 | VirtualDir PatchExeFS(VirtualDir exefs) const; | 39 | [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const; |
| 36 | 40 | ||
| 37 | // Currently tracked NSO patches: | 41 | // Currently tracked NSO patches: |
| 38 | // - IPS | 42 | // - IPS |
| 39 | // - IPSwitch | 43 | // - IPSwitch |
| 40 | std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const; | 44 | [[nodiscard]] std::vector<u8> PatchNSO(const std::vector<u8>& nso, |
| 45 | const std::string& name) const; | ||
| 41 | 46 | ||
| 42 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. | 47 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. |
| 43 | // Used to prevent expensive copies in NSO loader. | 48 | // Used to prevent expensive copies in NSO loader. |
| 44 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; | 49 | [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const; |
| 45 | 50 | ||
| 46 | // Creates a CheatList object with all | 51 | // Creates a CheatList object with all |
| 47 | std::vector<Core::Memory::CheatEntry> CreateCheatList( | 52 | [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList( |
| 48 | const Core::System& system, const std::array<u8, 0x20>& build_id) const; | 53 | const Core::System& system, const BuildID& build_id) const; |
| 49 | 54 | ||
| 50 | // Currently tracked RomFS patches: | 55 | // Currently tracked RomFS patches: |
| 51 | // - Game Updates | 56 | // - Game Updates |
| 52 | // - LayeredFS | 57 | // - LayeredFS |
| 53 | VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, | 58 | [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, |
| 54 | ContentRecordType type = ContentRecordType::Program, | 59 | ContentRecordType type = ContentRecordType::Program, |
| 55 | VirtualFile update_raw = nullptr) const; | 60 | VirtualFile update_raw = nullptr) const; |
| 56 | 61 | ||
| 57 | // Returns a vector of pairs between patch names and patch versions. | 62 | // Returns a vector of pairs between patch names and patch versions. |
| 58 | // i.e. Update 3.2.2 will return {"Update", "3.2.2"} | 63 | // i.e. Update 3.2.2 will return {"Update", "3.2.2"} |
| 59 | std::map<std::string, std::string, std::less<>> GetPatchVersionNames( | 64 | [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const; |
| 60 | VirtualFile update_raw = nullptr) const; | ||
| 61 | 65 | ||
| 62 | // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, | 66 | // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, |
| 63 | // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be | 67 | // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be |
| 64 | // std::nullopt | 68 | // std::nullopt |
| 65 | std::optional<u32> GetGameVersion() const; | 69 | [[nodiscard]] std::optional<u32> GetGameVersion() const; |
| 66 | 70 | ||
| 67 | // Given title_id of the program, attempts to get the control data of the update and parse | 71 | // Given title_id of the program, attempts to get the control data of the update and parse |
| 68 | // it, falling back to the base control data. | 72 | // it, falling back to the base control data. |
| 69 | std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; | 73 | [[nodiscard]] Metadata GetControlMetadata() const; |
| 70 | 74 | ||
| 71 | // Version of GetControlMetadata that takes an arbitrary NCA | 75 | // Version of GetControlMetadata that takes an arbitrary NCA |
| 72 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; | 76 | [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const; |
| 73 | 77 | ||
| 74 | private: | 78 | private: |
| 75 | std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, | 79 | [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, |
| 76 | const std::string& build_id) const; | 80 | const std::string& build_id) const; |
| 77 | 81 | ||
| 78 | u64 title_id; | 82 | u64 title_id; |
| 79 | }; | 83 | }; |