summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/file_sys/patch_manager.cpp112
-rw-r--r--src/core/file_sys/patch_manager.h36
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
77std::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
102void 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
111bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
112 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
113}
76} // Anonymous namespace 114} // Anonymous namespace
77 115
78PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 116PatchManager::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
258bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { 296bool 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
278namespace {
279std::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
306std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( 316std::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
448static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { 458PatchManager::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
455static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
456 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
457}
458
459std::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
574std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 574PatchManager::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
584std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { 585PatchManager::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.
26class PatchManager { 26class PatchManager {
27public: 27public:
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
74private: 78private:
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};