diff options
| author | 2019-03-23 13:46:09 -0400 | |
|---|---|---|
| committer | 2019-03-23 13:46:09 -0400 | |
| commit | 6af322a347340953cb90098645ba6d1b26864c3b (patch) | |
| tree | ea15e581f4653eadc9eae05dd21e16920f04471d /src/core/loader/nso.cpp | |
| parent | Merge pull request #2279 from lioncash/cheat-global (diff) | |
| parent | loader/nso: Place translation unit specific functions into an anonymous names... (diff) | |
| download | yuzu-6af322a347340953cb90098645ba6d1b26864c3b.tar.gz yuzu-6af322a347340953cb90098645ba6d1b26864c3b.tar.xz yuzu-6af322a347340953cb90098645ba6d1b26864c3b.zip | |
Merge pull request #2280 from lioncash/nso
loader/nso: Minor refactoring
Diffstat (limited to 'src/core/loader/nso.cpp')
| -rw-r--r-- | src/core/loader/nso.cpp | 101 |
1 files changed, 42 insertions, 59 deletions
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 7494f8a28..714d85a59 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -21,36 +21,8 @@ | |||
| 21 | #include "core/settings.h" | 21 | #include "core/settings.h" |
| 22 | 22 | ||
| 23 | namespace Loader { | 23 | namespace Loader { |
| 24 | 24 | namespace { | |
| 25 | struct NsoSegmentHeader { | 25 | struct MODHeader { |
| 26 | u32_le offset; | ||
| 27 | u32_le location; | ||
| 28 | u32_le size; | ||
| 29 | union { | ||
| 30 | u32_le alignment; | ||
| 31 | u32_le bss_size; | ||
| 32 | }; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size."); | ||
| 35 | |||
| 36 | struct NsoHeader { | ||
| 37 | u32_le magic; | ||
| 38 | u32_le version; | ||
| 39 | INSERT_PADDING_WORDS(1); | ||
| 40 | u8 flags; | ||
| 41 | std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) | ||
| 42 | std::array<u8, 0x20> build_id; | ||
| 43 | std::array<u32_le, 3> segments_compressed_size; | ||
| 44 | |||
| 45 | bool IsSegmentCompressed(size_t segment_num) const { | ||
| 46 | ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num); | ||
| 47 | return ((flags >> segment_num) & 1); | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); | ||
| 51 | static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); | ||
| 52 | |||
| 53 | struct ModHeader { | ||
| 54 | u32_le magic; | 26 | u32_le magic; |
| 55 | u32_le dynamic_offset; | 27 | u32_le dynamic_offset; |
| 56 | u32_le bss_start_offset; | 28 | u32_le bss_start_offset; |
| @@ -59,25 +31,10 @@ struct ModHeader { | |||
| 59 | u32_le eh_frame_hdr_end_offset; | 31 | u32_le eh_frame_hdr_end_offset; |
| 60 | u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base | 32 | u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base |
| 61 | }; | 33 | }; |
| 62 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 34 | static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); |
| 63 | |||
| 64 | AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} | ||
| 65 | |||
| 66 | FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { | ||
| 67 | u32 magic = 0; | ||
| 68 | if (file->ReadObject(&magic) != sizeof(magic)) { | ||
| 69 | return FileType::Error; | ||
| 70 | } | ||
| 71 | |||
| 72 | if (Common::MakeMagic('N', 'S', 'O', '0') != magic) { | ||
| 73 | return FileType::Error; | ||
| 74 | } | ||
| 75 | |||
| 76 | return FileType::NSO; | ||
| 77 | } | ||
| 78 | 35 | ||
| 79 | static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, | 36 | std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, |
| 80 | const NsoSegmentHeader& header) { | 37 | const NSOSegmentHeader& header) { |
| 81 | std::vector<u8> uncompressed_data(header.size); | 38 | std::vector<u8> uncompressed_data(header.size); |
| 82 | const int bytes_uncompressed = | 39 | const int bytes_uncompressed = |
| 83 | LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), | 40 | LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()), |
| @@ -91,23 +48,47 @@ static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, | |||
| 91 | return uncompressed_data; | 48 | return uncompressed_data; |
| 92 | } | 49 | } |
| 93 | 50 | ||
| 94 | static constexpr u32 PageAlignSize(u32 size) { | 51 | constexpr u32 PageAlignSize(u32 size) { |
| 95 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 52 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 96 | } | 53 | } |
| 54 | } // Anonymous namespace | ||
| 55 | |||
| 56 | bool NSOHeader::IsSegmentCompressed(size_t segment_num) const { | ||
| 57 | ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num); | ||
| 58 | return ((flags >> segment_num) & 1) != 0; | ||
| 59 | } | ||
| 60 | |||
| 61 | AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} | ||
| 62 | |||
| 63 | FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { | ||
| 64 | u32 magic = 0; | ||
| 65 | if (file->ReadObject(&magic) != sizeof(magic)) { | ||
| 66 | return FileType::Error; | ||
| 67 | } | ||
| 68 | |||
| 69 | if (Common::MakeMagic('N', 'S', 'O', '0') != magic) { | ||
| 70 | return FileType::Error; | ||
| 71 | } | ||
| 72 | |||
| 73 | return FileType::NSO; | ||
| 74 | } | ||
| 97 | 75 | ||
| 98 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | 76 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, |
| 99 | const FileSys::VfsFile& file, VAddr load_base, | 77 | const FileSys::VfsFile& file, VAddr load_base, |
| 100 | bool should_pass_arguments, | 78 | bool should_pass_arguments, |
| 101 | std::optional<FileSys::PatchManager> pm) { | 79 | std::optional<FileSys::PatchManager> pm) { |
| 102 | if (file.GetSize() < sizeof(NsoHeader)) | 80 | if (file.GetSize() < sizeof(NSOHeader)) { |
| 103 | return {}; | 81 | return {}; |
| 82 | } | ||
| 104 | 83 | ||
| 105 | NsoHeader nso_header{}; | 84 | NSOHeader nso_header{}; |
| 106 | if (sizeof(NsoHeader) != file.ReadObject(&nso_header)) | 85 | if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { |
| 107 | return {}; | 86 | return {}; |
| 87 | } | ||
| 108 | 88 | ||
| 109 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | 89 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { |
| 110 | return {}; | 90 | return {}; |
| 91 | } | ||
| 111 | 92 | ||
| 112 | // Build program image | 93 | // Build program image |
| 113 | Kernel::CodeSet codeset; | 94 | Kernel::CodeSet codeset; |
| @@ -143,10 +124,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 143 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | 124 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); |
| 144 | 125 | ||
| 145 | // Read MOD header | 126 | // Read MOD header |
| 146 | ModHeader mod_header{}; | 127 | MODHeader mod_header{}; |
| 147 | // Default .bss to size in segment header if MOD0 section doesn't exist | 128 | // Default .bss to size in segment header if MOD0 section doesn't exist |
| 148 | u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; | 129 | u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; |
| 149 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); | 130 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader)); |
| 150 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; | 131 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; |
| 151 | if (has_mod_header) { | 132 | if (has_mod_header) { |
| 152 | // Resize program image to include .bss section and page align each section | 133 | // 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, | |||
| 158 | 139 | ||
| 159 | // Apply patches if necessary | 140 | // Apply patches if necessary |
| 160 | if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { | 141 | if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) { |
| 161 | std::vector<u8> pi_header(program_image.size() + 0x100); | 142 | std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); |
| 162 | std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); | 143 | pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header), |
| 163 | std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); | 144 | reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader)); |
| 145 | pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(), | ||
| 146 | program_image.end()); | ||
| 164 | 147 | ||
| 165 | pi_header = pm->PatchNSO(pi_header); | 148 | pi_header = pm->PatchNSO(pi_header); |
| 166 | 149 | ||
| 167 | std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); | 150 | std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin()); |
| 168 | } | 151 | } |
| 169 | 152 | ||
| 170 | // Apply cheats if they exist and the program has a valid title ID | 153 | // Apply cheats if they exist and the program has a valid title ID |