summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Rodrigo Locatti2020-09-15 01:57:13 +0000
committerGravatar GitHub2020-09-15 01:57:13 +0000
commitb5f4221c3dba29dad11897ac8d7860d773f359f1 (patch)
tree94cb81437733d78d17dab14902a0e5db7fe62e2c /src
parentMerge pull request #4652 from lioncash/crypto (diff)
parentpatch_manager: Resolve implicit truncations in FormatTitleVersion() (diff)
downloadyuzu-b5f4221c3dba29dad11897ac8d7860d773f359f1.tar.gz
yuzu-b5f4221c3dba29dad11897ac8d7860d773f359f1.tar.xz
yuzu-b5f4221c3dba29dad11897ac8d7860d773f359f1.zip
Merge pull request #4655 from lioncash/internal2
patch_manager: Minor cleanup
Diffstat (limited to 'src')
-rw-r--r--src/core/file_sys/patch_manager.cpp134
-rw-r--r--src/core/file_sys/patch_manager.h48
2 files changed, 95 insertions, 87 deletions
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index c228d253e..87c354a43 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -27,6 +27,7 @@
27#include "core/settings.h" 27#include "core/settings.h"
28 28
29namespace FileSys { 29namespace FileSys {
30namespace {
30 31
31constexpr u64 SINGLE_BYTE_MODULUS = 0x100; 32constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
32constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 33constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
@@ -36,19 +37,28 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
36 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", 37 "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
37}; 38};
38 39
39std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { 40enum class TitleVersionFormat : u8 {
41 ThreeElements, ///< vX.Y.Z
42 FourElements, ///< vX.Y.Z.W
43};
44
45std::string FormatTitleVersion(u32 version,
46 TitleVersionFormat format = TitleVersionFormat::ThreeElements) {
40 std::array<u8, sizeof(u32)> bytes{}; 47 std::array<u8, sizeof(u32)> bytes{};
41 bytes[0] = version % SINGLE_BYTE_MODULUS; 48 bytes[0] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
42 for (std::size_t i = 1; i < bytes.size(); ++i) { 49 for (std::size_t i = 1; i < bytes.size(); ++i) {
43 version /= SINGLE_BYTE_MODULUS; 50 version /= SINGLE_BYTE_MODULUS;
44 bytes[i] = version % SINGLE_BYTE_MODULUS; 51 bytes[i] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
45 } 52 }
46 53
47 if (format == TitleVersionFormat::FourElements) 54 if (format == TitleVersionFormat::FourElements) {
48 return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); 55 return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
56 }
49 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); 57 return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
50} 58}
51 59
60// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
61// doesn't have a directory with name.
52VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) { 62VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
53#ifdef _WIN32 63#ifdef _WIN32
54 return dir->GetSubdirectory(name); 64 return dir->GetSubdirectory(name);
@@ -65,6 +75,45 @@ VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name)
65#endif 75#endif
66} 76}
67 77
78std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
79 const Core::System& system, u64 title_id, const PatchManager::BuildID& build_id_,
80 const VirtualDir& base_path, bool upper) {
81 const auto build_id_raw = Common::HexToString(build_id_, upper);
82 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
83 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
84
85 if (file == nullptr) {
86 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
87 title_id, build_id);
88 return std::nullopt;
89 }
90
91 std::vector<u8> data(file->GetSize());
92 if (file->Read(data.data(), data.size()) != data.size()) {
93 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
94 title_id, build_id);
95 return std::nullopt;
96 }
97
98 Core::Memory::TextCheatParser parser;
99 return parser.Parse(system,
100 std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
101}
102
103void AppendCommaIfNotEmpty(std::string& to, std::string_view with) {
104 if (to.empty()) {
105 to += with;
106 } else {
107 to += ", ";
108 to += with;
109 }
110}
111
112bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
113 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
114}
115} // Anonymous namespace
116
68PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 117PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
69 118
70PatchManager::~PatchManager() = default; 119PatchManager::~PatchManager() = default;
@@ -245,7 +294,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
245 return out; 294 return out;
246} 295}
247 296
248bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { 297bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
249 const auto build_id_raw = Common::HexToString(build_id_); 298 const auto build_id_raw = Common::HexToString(build_id_);
250 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); 299 const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
251 300
@@ -265,36 +314,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
265 return !CollectPatches(patch_dirs, build_id).empty(); 314 return !CollectPatches(patch_dirs, build_id).empty();
266} 315}
267 316
268namespace {
269std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
270 const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
271 const VirtualDir& base_path, bool upper) {
272 const auto build_id_raw = Common::HexToString(build_id_, upper);
273 const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
274 const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
275
276 if (file == nullptr) {
277 LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
278 title_id, build_id);
279 return std::nullopt;
280 }
281
282 std::vector<u8> data(file->GetSize());
283 if (file->Read(data.data(), data.size()) != data.size()) {
284 LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
285 title_id, build_id);
286 return std::nullopt;
287 }
288
289 Core::Memory::TextCheatParser parser;
290 return parser.Parse(system,
291 std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
292}
293
294} // Anonymous namespace
295
296std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( 317std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
297 const Core::System& system, const std::array<u8, 32>& build_id_) const { 318 const Core::System& system, const BuildID& build_id_) const {
298 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); 319 const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
299 if (load_dir == nullptr) { 320 if (load_dir == nullptr) {
300 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); 321 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
@@ -435,21 +456,11 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
435 return romfs; 456 return romfs;
436} 457}
437 458
438static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { 459PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const {
439 if (to.empty()) 460 if (title_id == 0) {
440 to += with;
441 else
442 to += ", " + with;
443}
444
445static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
446 return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
447}
448
449std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
450 VirtualFile update_raw) const {
451 if (title_id == 0)
452 return {}; 461 return {};
462 }
463
453 std::map<std::string, std::string, std::less<>> out; 464 std::map<std::string, std::string, std::less<>> out;
454 const auto& installed = Core::System::GetInstance().GetContentProvider(); 465 const auto& installed = Core::System::GetInstance().GetContentProvider();
455 const auto& disabled = Settings::values.disabled_addons[title_id]; 466 const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -472,8 +483,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
472 if (meta_ver.value_or(0) == 0) { 483 if (meta_ver.value_or(0) == 0) {
473 out.insert_or_assign(update_label, ""); 484 out.insert_or_assign(update_label, "");
474 } else { 485 } else {
475 out.insert_or_assign( 486 out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver));
476 update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
477 } 487 }
478 } else if (update_raw != nullptr) { 488 } else if (update_raw != nullptr) {
479 out.insert_or_assign(update_label, "PACKED"); 489 out.insert_or_assign(update_label, "PACKED");
@@ -562,40 +572,46 @@ std::optional<u32> PatchManager::GetGameVersion() const {
562 return installed.GetEntryVersion(title_id); 572 return installed.GetEntryVersion(title_id);
563} 573}
564 574
565std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 575PatchManager::Metadata PatchManager::GetControlMetadata() const {
566 const auto& installed = Core::System::GetInstance().GetContentProvider(); 576 const auto& installed = Core::System::GetInstance().GetContentProvider();
567 577
568 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); 578 const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
569 if (base_control_nca == nullptr) 579 if (base_control_nca == nullptr) {
570 return {}; 580 return {};
581 }
571 582
572 return ParseControlNCA(*base_control_nca); 583 return ParseControlNCA(*base_control_nca);
573} 584}
574 585
575std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { 586PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
576 const auto base_romfs = nca.GetRomFS(); 587 const auto base_romfs = nca.GetRomFS();
577 if (base_romfs == nullptr) 588 if (base_romfs == nullptr) {
578 return {}; 589 return {};
590 }
579 591
580 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); 592 const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
581 if (romfs == nullptr) 593 if (romfs == nullptr) {
582 return {}; 594 return {};
595 }
583 596
584 const auto extracted = ExtractRomFS(romfs); 597 const auto extracted = ExtractRomFS(romfs);
585 if (extracted == nullptr) 598 if (extracted == nullptr) {
586 return {}; 599 return {};
600 }
587 601
588 auto nacp_file = extracted->GetFile("control.nacp"); 602 auto nacp_file = extracted->GetFile("control.nacp");
589 if (nacp_file == nullptr) 603 if (nacp_file == nullptr) {
590 nacp_file = extracted->GetFile("Control.nacp"); 604 nacp_file = extracted->GetFile("Control.nacp");
605 }
591 606
592 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); 607 auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
593 608
594 VirtualFile icon_file; 609 VirtualFile icon_file;
595 for (const auto& language : FileSys::LANGUAGE_NAMES) { 610 for (const auto& language : FileSys::LANGUAGE_NAMES) {
596 icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); 611 icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat"));
597 if (icon_file != nullptr) 612 if (icon_file != nullptr) {
598 break; 613 break;
614 }
599 } 615 }
600 616
601 return {std::move(nacp), icon_file}; 617 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 532f4995f..1f28c6241 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -22,70 +22,62 @@ namespace FileSys {
22class NCA; 22class NCA;
23class NACP; 23class NACP;
24 24
25enum class TitleVersionFormat : u8 {
26 ThreeElements, ///< vX.Y.Z
27 FourElements, ///< vX.Y.Z.W
28};
29
30std::string FormatTitleVersion(u32 version,
31 TitleVersionFormat format = TitleVersionFormat::ThreeElements);
32
33// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
34// doesn't have a directory with name.
35VirtualDir FindSubdirectoryCaseless(VirtualDir dir, std::string_view name);
36
37// A centralized class to manage patches to games. 25// A centralized class to manage patches to games.
38class PatchManager { 26class PatchManager {
39public: 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
40 explicit PatchManager(u64 title_id); 32 explicit PatchManager(u64 title_id);
41 ~PatchManager(); 33 ~PatchManager();
42 34
43 u64 GetTitleID() const; 35 [[nodiscard]] u64 GetTitleID() const;
44 36
45 // Currently tracked ExeFS patches: 37 // Currently tracked ExeFS patches:
46 // - Game Updates 38 // - Game Updates
47 VirtualDir PatchExeFS(VirtualDir exefs) const; 39 [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const;
48 40
49 // Currently tracked NSO patches: 41 // Currently tracked NSO patches:
50 // - IPS 42 // - IPS
51 // - IPSwitch 43 // - IPSwitch
52 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;
53 46
54 // 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.
55 // Used to prevent expensive copies in NSO loader. 48 // Used to prevent expensive copies in NSO loader.
56 bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; 49 [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const;
57 50
58 // Creates a CheatList object with all 51 // Creates a CheatList object with all
59 std::vector<Core::Memory::CheatEntry> CreateCheatList( 52 [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
60 const Core::System& system, const std::array<u8, 0x20>& build_id) const; 53 const Core::System& system, const BuildID& build_id) const;
61 54
62 // Currently tracked RomFS patches: 55 // Currently tracked RomFS patches:
63 // - Game Updates 56 // - Game Updates
64 // - LayeredFS 57 // - LayeredFS
65 VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, 58 [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
66 ContentRecordType type = ContentRecordType::Program, 59 ContentRecordType type = ContentRecordType::Program,
67 VirtualFile update_raw = nullptr) const; 60 VirtualFile update_raw = nullptr) const;
68 61
69 // Returns a vector of pairs between patch names and patch versions. 62 // Returns a vector of pairs between patch names and patch versions.
70 // 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"}
71 std::map<std::string, std::string, std::less<>> GetPatchVersionNames( 64 [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const;
72 VirtualFile update_raw = nullptr) const;
73 65
74 // 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,
75 // 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
76 // std::nullopt 68 // std::nullopt
77 std::optional<u32> GetGameVersion() const; 69 [[nodiscard]] std::optional<u32> GetGameVersion() const;
78 70
79 // 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
80 // it, falling back to the base control data. 72 // it, falling back to the base control data.
81 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; 73 [[nodiscard]] Metadata GetControlMetadata() const;
82 74
83 // Version of GetControlMetadata that takes an arbitrary NCA 75 // Version of GetControlMetadata that takes an arbitrary NCA
84 std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; 76 [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const;
85 77
86private: 78private:
87 std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, 79 [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
88 const std::string& build_id) const; 80 const std::string& build_id) const;
89 81
90 u64 title_id; 82 u64 title_id;
91}; 83};