mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	patch_manager: Add PatchNSO function
While PatchExeFS operated on the entire directory, this function operates on the uncompressed NSO. Avoids copying decompression code to PatchManager.
This commit is contained in:
		
							parent
							
								
									4c2a94fa94
								
							
						
					
					
						commit
						42fb4e82d3
					
				@ -34,6 +34,8 @@ add_library(core STATIC
 | 
			
		||||
    file_sys/errors.h
 | 
			
		||||
    file_sys/fsmitm_romfsbuild.cpp
 | 
			
		||||
    file_sys/fsmitm_romfsbuild.h
 | 
			
		||||
    file_sys/ips_layer.cpp
 | 
			
		||||
    file_sys/ips_layer.h
 | 
			
		||||
    file_sys/mode.h
 | 
			
		||||
    file_sys/nca_metadata.cpp
 | 
			
		||||
    file_sys/nca_metadata.h
 | 
			
		||||
 | 
			
		||||
@ -6,13 +6,16 @@
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
 | 
			
		||||
#include "common/hex_util.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/file_sys/content_archive.h"
 | 
			
		||||
#include "core/file_sys/control_metadata.h"
 | 
			
		||||
#include "core/file_sys/ips_layer.h"
 | 
			
		||||
#include "core/file_sys/patch_manager.h"
 | 
			
		||||
#include "core/file_sys/registered_cache.h"
 | 
			
		||||
#include "core/file_sys/romfs.h"
 | 
			
		||||
#include "core/file_sys/vfs_layered.h"
 | 
			
		||||
#include "core/file_sys/vfs_vector.h"
 | 
			
		||||
#include "core/hle/service/filesystem/filesystem.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
 | 
			
		||||
@ -21,6 +24,14 @@ namespace FileSys {
 | 
			
		||||
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
 | 
			
		||||
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
 | 
			
		||||
 | 
			
		||||
struct NSOBuildHeader {
 | 
			
		||||
    u32_le magic;
 | 
			
		||||
    INSERT_PADDING_BYTES(0x3C);
 | 
			
		||||
    std::array<u8, 0x20> build_id;
 | 
			
		||||
    INSERT_PADDING_BYTES(0xA0);
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
 | 
			
		||||
 | 
			
		||||
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
 | 
			
		||||
    std::array<u8, sizeof(u32)> bytes{};
 | 
			
		||||
    bytes[0] = version % SINGLE_BYTE_MODULUS;
 | 
			
		||||
@ -61,6 +72,89 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
 | 
			
		||||
    return exefs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
 | 
			
		||||
    if (nso.size() < 0x100)
 | 
			
		||||
        return nso;
 | 
			
		||||
 | 
			
		||||
    NSOBuildHeader header{};
 | 
			
		||||
    std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader));
 | 
			
		||||
 | 
			
		||||
    if (header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
 | 
			
		||||
        return nso;
 | 
			
		||||
 | 
			
		||||
    const auto build_id_raw = Common::HexArrayToString(header.build_id);
 | 
			
		||||
    const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
 | 
			
		||||
 | 
			
		||||
    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
 | 
			
		||||
    auto patch_dirs = load_dir->GetSubdirectories();
 | 
			
		||||
    std::sort(patch_dirs.begin(), patch_dirs.end(),
 | 
			
		||||
              [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
 | 
			
		||||
 | 
			
		||||
    std::vector<VirtualFile> ips;
 | 
			
		||||
    ips.reserve(patch_dirs.size() - 1);
 | 
			
		||||
    for (const auto& subdir : patch_dirs) {
 | 
			
		||||
        auto exefs_dir = subdir->GetSubdirectory("exefs");
 | 
			
		||||
        if (exefs_dir != nullptr) {
 | 
			
		||||
            for (const auto& file : exefs_dir->GetFiles()) {
 | 
			
		||||
                if (file->GetExtension() != "ips")
 | 
			
		||||
                    continue;
 | 
			
		||||
                auto name = file->GetName();
 | 
			
		||||
                const auto p1 = name.substr(0, name.find_first_of('.'));
 | 
			
		||||
                const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
 | 
			
		||||
 | 
			
		||||
                if (build_id == this_build_id)
 | 
			
		||||
                    ips.push_back(file);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto out = nso;
 | 
			
		||||
    for (const auto& ips_file : ips) {
 | 
			
		||||
        LOG_INFO(Loader, "    - Appling IPS patch from mod \"{}\"",
 | 
			
		||||
                 ips_file->GetContainingDirectory()->GetParentDirectory()->GetName());
 | 
			
		||||
        const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file);
 | 
			
		||||
        if (patched != nullptr)
 | 
			
		||||
            out = patched->ReadAllBytes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (out.size() < 0x100)
 | 
			
		||||
        return nso;
 | 
			
		||||
    std::memcpy(out.data(), &header, sizeof(NSOBuildHeader));
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
 | 
			
		||||
    const auto build_id_raw = Common::HexArrayToString(build_id_);
 | 
			
		||||
    const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
 | 
			
		||||
 | 
			
		||||
    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
 | 
			
		||||
    auto patch_dirs = load_dir->GetSubdirectories();
 | 
			
		||||
    std::sort(patch_dirs.begin(), patch_dirs.end(),
 | 
			
		||||
              [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
 | 
			
		||||
 | 
			
		||||
    for (const auto& subdir : patch_dirs) {
 | 
			
		||||
        auto exefs_dir = subdir->GetSubdirectory("exefs");
 | 
			
		||||
        if (exefs_dir != nullptr) {
 | 
			
		||||
            for (const auto& file : exefs_dir->GetFiles()) {
 | 
			
		||||
                if (file->GetExtension() != "ips")
 | 
			
		||||
                    continue;
 | 
			
		||||
                auto name = file->GetName();
 | 
			
		||||
                const auto p1 = name.substr(0, name.find_first_of('.'));
 | 
			
		||||
                const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
 | 
			
		||||
 | 
			
		||||
                if (build_id == this_build_id)
 | 
			
		||||
                    return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
 | 
			
		||||
    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
 | 
			
		||||
    if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
 | 
			
		||||
 | 
			
		||||
@ -34,6 +34,14 @@ public:
 | 
			
		||||
    // - Game Updates
 | 
			
		||||
    VirtualDir PatchExeFS(VirtualDir exefs) const;
 | 
			
		||||
 | 
			
		||||
    // Currently tracked NSO patches:
 | 
			
		||||
    // - IPS
 | 
			
		||||
    std::vector<u8> PatchNSO(const std::vector<u8>& nso) 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<u8, 0x20>& build_id) const;
 | 
			
		||||
 | 
			
		||||
    // Currently tracked RomFS patches:
 | 
			
		||||
    // - Game Updates
 | 
			
		||||
    // - LayeredFS
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user