mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #2280 from lioncash/nso
loader/nso: Minor refactoring
This commit is contained in:
		
						commit
						6af322a347
					
				@ -20,6 +20,7 @@
 | 
				
			|||||||
#include "core/file_sys/vfs_vector.h"
 | 
					#include "core/file_sys/vfs_vector.h"
 | 
				
			||||||
#include "core/hle/service/filesystem/filesystem.h"
 | 
					#include "core/hle/service/filesystem/filesystem.h"
 | 
				
			||||||
#include "core/loader/loader.h"
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
 | 
					#include "core/loader/nso.h"
 | 
				
			||||||
#include "core/settings.h"
 | 
					#include "core/settings.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FileSys {
 | 
					namespace FileSys {
 | 
				
			||||||
@ -32,14 +33,6 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
 | 
				
			|||||||
    "subsdk3", "subsdk4",   "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
 | 
					    "subsdk3", "subsdk4",   "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
 | 
				
			||||||
    std::array<u8, sizeof(u32)> bytes{};
 | 
					    std::array<u8, sizeof(u32)> bytes{};
 | 
				
			||||||
    bytes[0] = version % SINGLE_BYTE_MODULUS;
 | 
					    bytes[0] = version % SINGLE_BYTE_MODULUS;
 | 
				
			||||||
@ -163,14 +156,16 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
 | 
					std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
 | 
				
			||||||
    if (nso.size() < 0x100)
 | 
					    if (nso.size() < sizeof(Loader::NSOHeader)) {
 | 
				
			||||||
        return nso;
 | 
					        return nso;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    NSOBuildHeader header;
 | 
					    Loader::NSOHeader header;
 | 
				
			||||||
    std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader));
 | 
					    std::memcpy(&header, nso.data(), sizeof(header));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
 | 
					    if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
 | 
				
			||||||
        return nso;
 | 
					        return nso;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto build_id_raw = Common::HexArrayToString(header.build_id);
 | 
					    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);
 | 
					    const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
 | 
				
			||||||
@ -213,9 +208,11 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (out.size() < 0x100)
 | 
					    if (out.size() < sizeof(Loader::NSOHeader)) {
 | 
				
			||||||
        return nso;
 | 
					        return nso;
 | 
				
			||||||
    std::memcpy(out.data(), &header, sizeof(NSOBuildHeader));
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::memcpy(out.data(), &header, sizeof(header));
 | 
				
			||||||
    return out;
 | 
					    return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -21,36 +21,8 @@
 | 
				
			|||||||
#include "core/settings.h"
 | 
					#include "core/settings.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Loader {
 | 
					namespace Loader {
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
struct NsoSegmentHeader {
 | 
					struct MODHeader {
 | 
				
			||||||
    u32_le offset;
 | 
					 | 
				
			||||||
    u32_le location;
 | 
					 | 
				
			||||||
    u32_le size;
 | 
					 | 
				
			||||||
    union {
 | 
					 | 
				
			||||||
        u32_le alignment;
 | 
					 | 
				
			||||||
        u32_le bss_size;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct NsoHeader {
 | 
					 | 
				
			||||||
    u32_le magic;
 | 
					 | 
				
			||||||
    u32_le version;
 | 
					 | 
				
			||||||
    INSERT_PADDING_WORDS(1);
 | 
					 | 
				
			||||||
    u8 flags;
 | 
					 | 
				
			||||||
    std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
 | 
					 | 
				
			||||||
    std::array<u8, 0x20> build_id;
 | 
					 | 
				
			||||||
    std::array<u32_le, 3> segments_compressed_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    bool IsSegmentCompressed(size_t segment_num) const {
 | 
					 | 
				
			||||||
        ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
 | 
					 | 
				
			||||||
        return ((flags >> segment_num) & 1);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
 | 
					 | 
				
			||||||
static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ModHeader {
 | 
					 | 
				
			||||||
    u32_le magic;
 | 
					    u32_le magic;
 | 
				
			||||||
    u32_le dynamic_offset;
 | 
					    u32_le dynamic_offset;
 | 
				
			||||||
    u32_le bss_start_offset;
 | 
					    u32_le bss_start_offset;
 | 
				
			||||||
@ -59,7 +31,32 @@ struct ModHeader {
 | 
				
			|||||||
    u32_le eh_frame_hdr_end_offset;
 | 
					    u32_le eh_frame_hdr_end_offset;
 | 
				
			||||||
    u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
 | 
					    u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
 | 
					static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
 | 
				
			||||||
 | 
					                                  const NSOSegmentHeader& header) {
 | 
				
			||||||
 | 
					    std::vector<u8> uncompressed_data(header.size);
 | 
				
			||||||
 | 
					    const int bytes_uncompressed =
 | 
				
			||||||
 | 
					        LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
 | 
				
			||||||
 | 
					                            reinterpret_cast<char*>(uncompressed_data.data()),
 | 
				
			||||||
 | 
					                            static_cast<int>(compressed_data.size()), header.size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
 | 
				
			||||||
 | 
					                   bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
 | 
				
			||||||
 | 
					               "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return uncompressed_data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr u32 PageAlignSize(u32 size) {
 | 
				
			||||||
 | 
					    return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // Anonymous namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
 | 
				
			||||||
 | 
					    ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
 | 
				
			||||||
 | 
					    return ((flags >> segment_num) & 1) != 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
 | 
					AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -76,38 +73,22 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
 | 
				
			|||||||
    return FileType::NSO;
 | 
					    return FileType::NSO;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
 | 
					 | 
				
			||||||
                                         const NsoSegmentHeader& header) {
 | 
					 | 
				
			||||||
    std::vector<u8> uncompressed_data(header.size);
 | 
					 | 
				
			||||||
    const int bytes_uncompressed =
 | 
					 | 
				
			||||||
        LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
 | 
					 | 
				
			||||||
                            reinterpret_cast<char*>(uncompressed_data.data()),
 | 
					 | 
				
			||||||
                            static_cast<int>(compressed_data.size()), header.size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
 | 
					 | 
				
			||||||
                   bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
 | 
					 | 
				
			||||||
               "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return uncompressed_data;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static constexpr u32 PageAlignSize(u32 size) {
 | 
					 | 
				
			||||||
    return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
 | 
					std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
 | 
				
			||||||
                                               const FileSys::VfsFile& file, VAddr load_base,
 | 
					                                               const FileSys::VfsFile& file, VAddr load_base,
 | 
				
			||||||
                                               bool should_pass_arguments,
 | 
					                                               bool should_pass_arguments,
 | 
				
			||||||
                                               std::optional<FileSys::PatchManager> pm) {
 | 
					                                               std::optional<FileSys::PatchManager> pm) {
 | 
				
			||||||
    if (file.GetSize() < sizeof(NsoHeader))
 | 
					    if (file.GetSize() < sizeof(NSOHeader)) {
 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    NsoHeader nso_header{};
 | 
					    NSOHeader nso_header{};
 | 
				
			||||||
    if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
 | 
					    if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
 | 
					    if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
 | 
				
			||||||
        return {};
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Build program image
 | 
					    // Build program image
 | 
				
			||||||
    Kernel::CodeSet codeset;
 | 
					    Kernel::CodeSet codeset;
 | 
				
			||||||
@ -143,10 +124,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
 | 
				
			|||||||
    std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
 | 
					    std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Read MOD header
 | 
					    // Read MOD header
 | 
				
			||||||
    ModHeader mod_header{};
 | 
					    MODHeader mod_header{};
 | 
				
			||||||
    // Default .bss to size in segment header if MOD0 section doesn't exist
 | 
					    // Default .bss to size in segment header if MOD0 section doesn't exist
 | 
				
			||||||
    u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
 | 
					    u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
 | 
				
			||||||
    std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
 | 
					    std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
 | 
				
			||||||
    const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
 | 
					    const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
 | 
				
			||||||
    if (has_mod_header) {
 | 
					    if (has_mod_header) {
 | 
				
			||||||
        // Resize program image to include .bss section and page align each section
 | 
					        // Resize program image to include .bss section and page align each section
 | 
				
			||||||
@ -158,13 +139,15 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Apply patches if necessary
 | 
					    // Apply patches if necessary
 | 
				
			||||||
    if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
 | 
					    if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
 | 
				
			||||||
        std::vector<u8> pi_header(program_image.size() + 0x100);
 | 
					        std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size());
 | 
				
			||||||
        std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
 | 
					        pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
 | 
				
			||||||
        std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
 | 
					                         reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
 | 
				
			||||||
 | 
					        pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
 | 
				
			||||||
 | 
					                         program_image.end());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pi_header = pm->PatchNSO(pi_header);
 | 
					        pi_header = pm->PatchNSO(pi_header);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size());
 | 
					        std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Apply cheats if they exist and the program has a valid title ID
 | 
					    // Apply cheats if they exist and the program has a valid title ID
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
#include <optional>
 | 
					#include <optional>
 | 
				
			||||||
 | 
					#include <type_traits>
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
#include "common/swap.h"
 | 
					#include "common/swap.h"
 | 
				
			||||||
#include "core/file_sys/patch_manager.h"
 | 
					#include "core/file_sys/patch_manager.h"
 | 
				
			||||||
@ -16,6 +18,43 @@ class Process;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Loader {
 | 
					namespace Loader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct NSOSegmentHeader {
 | 
				
			||||||
 | 
					    u32_le offset;
 | 
				
			||||||
 | 
					    u32_le location;
 | 
				
			||||||
 | 
					    u32_le size;
 | 
				
			||||||
 | 
					    union {
 | 
				
			||||||
 | 
					        u32_le alignment;
 | 
				
			||||||
 | 
					        u32_le bss_size;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct NSOHeader {
 | 
				
			||||||
 | 
					    using SHA256Hash = std::array<u8, 0x20>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct RODataRelativeExtent {
 | 
				
			||||||
 | 
					        u32_le data_offset;
 | 
				
			||||||
 | 
					        u32_le size;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u32_le magic;
 | 
				
			||||||
 | 
					    u32_le version;
 | 
				
			||||||
 | 
					    u32 reserved;
 | 
				
			||||||
 | 
					    u32_le flags;
 | 
				
			||||||
 | 
					    std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
 | 
				
			||||||
 | 
					    std::array<u8, 0x20> build_id;
 | 
				
			||||||
 | 
					    std::array<u32_le, 3> segments_compressed_size;
 | 
				
			||||||
 | 
					    std::array<u8, 0x1C> padding;
 | 
				
			||||||
 | 
					    RODataRelativeExtent api_info_extent;
 | 
				
			||||||
 | 
					    RODataRelativeExtent dynstr_extent;
 | 
				
			||||||
 | 
					    RODataRelativeExtent dynsyn_extent;
 | 
				
			||||||
 | 
					    std::array<SHA256Hash, 3> segment_hashes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool IsSegmentCompressed(size_t segment_num) const;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
 | 
				
			||||||
 | 
					static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
 | 
					constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct NSOArgumentHeader {
 | 
					struct NSOArgumentHeader {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user