summaryrefslogtreecommitdiff
path: root/src/core/loader/ncch.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2017-10-09 23:56:20 -0400
committerGravatar bunnei2017-10-09 23:56:20 -0400
commitb1d5db1cf60344b6b081c9d03cb6ccc3264326cd (patch)
treefde377c4ba3c0f92c032e6f5ec8627aae37270ef /src/core/loader/ncch.cpp
parentloader: Various improvements for NSO/NRO loaders. (diff)
parentMerge pull request #2996 from MerryMage/split-travis (diff)
downloadyuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.gz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.tar.xz
yuzu-b1d5db1cf60344b6b081c9d03cb6ccc3264326cd.zip
Merge remote-tracking branch 'upstream/master' into nx
# Conflicts: # src/core/CMakeLists.txt # src/core/arm/dynarmic/arm_dynarmic.cpp # src/core/arm/dyncom/arm_dyncom.cpp # src/core/hle/kernel/process.cpp # src/core/hle/kernel/thread.cpp # src/core/hle/kernel/thread.h # src/core/hle/kernel/vm_manager.cpp # src/core/loader/3dsx.cpp # src/core/loader/elf.cpp # src/core/loader/ncch.cpp # src/core/memory.cpp # src/core/memory.h # src/core/memory_setup.h
Diffstat (limited to 'src/core/loader/ncch.cpp')
-rw-r--r--src/core/loader/ncch.cpp371
1 files changed, 105 insertions, 266 deletions
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 728886ea8..e33a37b2e 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -4,13 +4,17 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <cinttypes> 6#include <cinttypes>
7#include <codecvt>
7#include <cstring> 8#include <cstring>
9#include <locale>
8#include <memory> 10#include <memory>
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/string_util.h" 12#include "common/string_util.h"
11#include "common/swap.h" 13#include "common/swap.h"
12#include "core/core.h" 14#include "core/core.h"
13#include "core/file_sys/archive_selfncch.h" 15#include "core/file_sys/archive_selfncch.h"
16#include "core/file_sys/ncch_container.h"
17#include "core/file_sys/title_metadata.h"
14#include "core/hle/kernel/process.h" 18#include "core/hle/kernel/process.h"
15#include "core/hle/kernel/resource_limit.h" 19#include "core/hle/kernel/resource_limit.h"
16#include "core/hle/service/cfg/cfg.h" 20#include "core/hle/service/cfg/cfg.h"
@@ -18,93 +22,14 @@
18#include "core/loader/ncch.h" 22#include "core/loader/ncch.h"
19#include "core/loader/smdh.h" 23#include "core/loader/smdh.h"
20#include "core/memory.h" 24#include "core/memory.h"
25#include "network/network.h"
21 26
22//////////////////////////////////////////////////////////////////////////////////////////////////// 27////////////////////////////////////////////////////////////////////////////////////////////////////
23// Loader namespace 28// Loader namespace
24 29
25namespace Loader { 30namespace Loader {
26 31
27static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs 32static const u64 UPDATE_MASK = 0x0000000e00000000;
28static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes)
29
30/**
31 * Get the decompressed size of an LZSS compressed ExeFS file
32 * @param buffer Buffer of compressed file
33 * @param size Size of compressed buffer
34 * @return Size of decompressed buffer
35 */
36static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) {
37 u32 offset_size = *(u32*)(buffer + size - 4);
38 return offset_size + size;
39}
40
41/**
42 * Decompress ExeFS file (compressed with LZSS)
43 * @param compressed Compressed buffer
44 * @param compressed_size Size of compressed buffer
45 * @param decompressed Decompressed buffer
46 * @param decompressed_size Size of decompressed buffer
47 * @return True on success, otherwise false
48 */
49static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed,
50 u32 decompressed_size) {
51 const u8* footer = compressed + compressed_size - 8;
52 u32 buffer_top_and_bottom = *reinterpret_cast<const u32*>(footer);
53 u32 out = decompressed_size;
54 u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF);
55 u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF);
56
57 memset(decompressed, 0, decompressed_size);
58 memcpy(decompressed, compressed, compressed_size);
59
60 while (index > stop_index) {
61 u8 control = compressed[--index];
62
63 for (unsigned i = 0; i < 8; i++) {
64 if (index <= stop_index)
65 break;
66 if (index <= 0)
67 break;
68 if (out <= 0)
69 break;
70
71 if (control & 0x80) {
72 // Check if compression is out of bounds
73 if (index < 2)
74 return false;
75 index -= 2;
76
77 u32 segment_offset = compressed[index] | (compressed[index + 1] << 8);
78 u32 segment_size = ((segment_offset >> 12) & 15) + 3;
79 segment_offset &= 0x0FFF;
80 segment_offset += 2;
81
82 // Check if compression is out of bounds
83 if (out < segment_size)
84 return false;
85
86 for (unsigned j = 0; j < segment_size; j++) {
87 // Check if compression is out of bounds
88 if (out + segment_offset >= decompressed_size)
89 return false;
90
91 u8 data = decompressed[out + segment_offset];
92 decompressed[--out] = data;
93 }
94 } else {
95 // Check if compression is out of bounds
96 if (out < 1)
97 return false;
98 decompressed[--out] = compressed[--index];
99 }
100 control <<= 1;
101 }
102 }
103 return true;
104}
105
106////////////////////////////////////////////////////////////////////////////////////////////////////
107// AppLoader_NCCH class
108 33
109FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { 34FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
110 u32 magic; 35 u32 magic;
@@ -121,203 +46,105 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
121 return FileType::Error; 46 return FileType::Error;
122} 47}
123 48
49static std::string GetUpdateNCCHPath(u64_le program_id) {
50 u32 high = static_cast<u32>((program_id | UPDATE_MASK) >> 32);
51 u32 low = static_cast<u32>((program_id | UPDATE_MASK) & 0xFFFFFFFF);
52
53 // TODO(shinyquagsire23): Title database should be doing this path lookup
54 std::string content_path = Common::StringFromFormat(
55 "%sNintendo 3DS/%s/%s/title/%08x/%08x/content/", FileUtil::GetUserPath(D_SDMC_IDX).c_str(),
56 SYSTEM_ID, SDCARD_ID, high, low);
57 std::string tmd_path = content_path + "00000000.tmd";
58
59 u32 content_id = 0;
60 FileSys::TitleMetadata tmd(tmd_path);
61 if (tmd.Load() == ResultStatus::Success) {
62 content_id = tmd.GetBootContentID();
63 }
64
65 return Common::StringFromFormat("%s%08x.app", content_path.c_str(), content_id);
66}
67
124std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { 68std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() {
125 if (!is_loaded) { 69 if (!is_loaded) {
126 ResultStatus res = LoadExeFS(); 70 ResultStatus res = base_ncch.Load();
127 if (res != ResultStatus::Success) { 71 if (res != ResultStatus::Success) {
128 return std::make_pair(boost::none, res); 72 return std::make_pair(boost::none, res);
129 } 73 }
130 } 74 }
75
131 // Set the system mode as the one from the exheader. 76 // Set the system mode as the one from the exheader.
132 return std::make_pair(exheader_header.arm11_system_local_caps.system_mode.Value(), 77 return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.system_mode.Value(),
133 ResultStatus::Success); 78 ResultStatus::Success);
134} 79}
135 80
136ResultStatus AppLoader_NCCH::LoadExec() { 81ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr<Kernel::Process>& process) {
137 using Kernel::SharedPtr;
138 using Kernel::CodeSet; 82 using Kernel::CodeSet;
83 using Kernel::SharedPtr;
139 84
140 if (!is_loaded) 85 if (!is_loaded)
141 return ResultStatus::ErrorNotLoaded; 86 return ResultStatus::ErrorNotLoaded;
142 87
143 std::vector<u8> code; 88 std::vector<u8> code;
144 if (ResultStatus::Success == ReadCode(code)) { 89 u64_le program_id;
90 if (ResultStatus::Success == ReadCode(code) &&
91 ResultStatus::Success == ReadProgramId(program_id)) {
145 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( 92 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
146 (const char*)exheader_header.codeset_info.name, 8); 93 (const char*)overlay_ncch->exheader_header.codeset_info.name, 8);
147 94
148 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, ncch_header.program_id); 95 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id);
149 96
150 codeset->code.offset = 0; 97 codeset->code.offset = 0;
151 codeset->code.addr = exheader_header.codeset_info.text.address; 98 codeset->code.addr = overlay_ncch->exheader_header.codeset_info.text.address;
152 codeset->code.size = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; 99 codeset->code.size =
100 overlay_ncch->exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE;
153 101
154 codeset->rodata.offset = codeset->code.offset + codeset->code.size; 102 codeset->rodata.offset = codeset->code.offset + codeset->code.size;
155 codeset->rodata.addr = exheader_header.codeset_info.ro.address; 103 codeset->rodata.addr = overlay_ncch->exheader_header.codeset_info.ro.address;
156 codeset->rodata.size = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; 104 codeset->rodata.size =
105 overlay_ncch->exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE;
157 106
158 // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just 107 // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just
159 // to the regular size. Playing it safe for now. 108 // to the regular size. Playing it safe for now.
160 u32 bss_page_size = (exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; 109 u32 bss_page_size = (overlay_ncch->exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF;
161 code.resize(code.size() + bss_page_size, 0); 110 code.resize(code.size() + bss_page_size, 0);
162 111
163 codeset->data.offset = codeset->rodata.offset + codeset->rodata.size; 112 codeset->data.offset = codeset->rodata.offset + codeset->rodata.size;
164 codeset->data.addr = exheader_header.codeset_info.data.address; 113 codeset->data.addr = overlay_ncch->exheader_header.codeset_info.data.address;
165 codeset->data.size = 114 codeset->data.size =
166 exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size; 115 overlay_ncch->exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE +
116 bss_page_size;
167 117
168 codeset->entrypoint = codeset->code.addr; 118 codeset->entrypoint = codeset->code.addr;
169 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code)); 119 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
170 120
171 Kernel::g_current_process = Kernel::Process::Create("main"); 121 process = Kernel::Process::Create("main");
172 Kernel::g_current_process->LoadModule(codeset, codeset->entrypoint); 122 process->LoadModule(codeset, codeset->entrypoint);
173 123
174 // Attach a resource limit to the process based on the resource limit category 124 // Attach a resource limit to the process based on the resource limit category
175 Kernel::g_current_process->resource_limit = 125 process->resource_limit =
176 Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>( 126 Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>(
177 exheader_header.arm11_system_local_caps.resource_limit_category)); 127 overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category));
178 128
179 // Set the default CPU core for this process 129 // Set the default CPU core for this process
180 Kernel::g_current_process->ideal_processor = 130 process->ideal_processor =
181 exheader_header.arm11_system_local_caps.ideal_processor; 131 overlay_ncch->exheader_header.arm11_system_local_caps.ideal_processor;
182 132
183 // Copy data while converting endianness 133 // Copy data while converting endianness
184 std::array<u32, ARRAY_SIZE(exheader_header.arm11_kernel_caps.descriptors)> kernel_caps; 134 std::array<u32, ARRAY_SIZE(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors)>
185 std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), 135 kernel_caps;
136 std::copy_n(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(),
186 begin(kernel_caps)); 137 begin(kernel_caps));
187 Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); 138 process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
188 139
189 s32 priority = exheader_header.arm11_system_local_caps.priority; 140 s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority;
190 u32 stack_size = exheader_header.codeset_info.stack_size; 141 u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size;
191 Kernel::g_current_process->Run(codeset->entrypoint, priority, stack_size); 142 process->Run(codeset->entrypoint, priority, stack_size);
192 return ResultStatus::Success; 143 return ResultStatus::Success;
193 } 144 }
194 return ResultStatus::Error; 145 return ResultStatus::Error;
195} 146}
196 147
197ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector<u8>& buffer) {
198 if (!file.IsOpen())
199 return ResultStatus::Error;
200
201 ResultStatus result = LoadExeFS();
202 if (result != ResultStatus::Success)
203 return result;
204
205 LOG_DEBUG(Loader, "%d sections:", kMaxSections);
206 // Iterate through the ExeFs archive until we find a section with the specified name...
207 for (unsigned section_number = 0; section_number < kMaxSections; section_number++) {
208 const auto& section = exefs_header.section[section_number];
209
210 // Load the specified section...
211 if (strcmp(section.name, name) == 0) {
212 LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number,
213 section.offset, section.size, section.name);
214
215 s64 section_offset =
216 (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset);
217 file.Seek(section_offset, SEEK_SET);
218
219 if (strcmp(section.name, ".code") == 0 && is_compressed) {
220 // Section is compressed, read compressed .code section...
221 std::unique_ptr<u8[]> temp_buffer;
222 try {
223 temp_buffer.reset(new u8[section.size]);
224 } catch (std::bad_alloc&) {
225 return ResultStatus::ErrorMemoryAllocationFailed;
226 }
227
228 if (file.ReadBytes(&temp_buffer[0], section.size) != section.size)
229 return ResultStatus::Error;
230
231 // Decompress .code section...
232 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size);
233 buffer.resize(decompressed_size);
234 if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size))
235 return ResultStatus::ErrorInvalidFormat;
236 } else {
237 // Section is uncompressed...
238 buffer.resize(section.size);
239 if (file.ReadBytes(&buffer[0], section.size) != section.size)
240 return ResultStatus::Error;
241 }
242 return ResultStatus::Success;
243 }
244 }
245 return ResultStatus::ErrorNotUsed;
246}
247
248ResultStatus AppLoader_NCCH::LoadExeFS() {
249 if (is_exefs_loaded)
250 return ResultStatus::Success;
251
252 if (!file.IsOpen())
253 return ResultStatus::Error;
254
255 // Reset read pointer in case this file has been read before.
256 file.Seek(0, SEEK_SET);
257
258 if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header))
259 return ResultStatus::Error;
260
261 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
262 if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) {
263 LOG_DEBUG(Loader, "Only loading the first (bootable) NCCH within the NCSD file!");
264 ncch_offset = 0x4000;
265 file.Seek(ncch_offset, SEEK_SET);
266 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
267 }
268
269 // Verify we are loading the correct file type...
270 if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic)
271 return ResultStatus::ErrorInvalidFormat;
272
273 // Read ExHeader...
274
275 if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header))
276 return ResultStatus::Error;
277
278 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
279 entry_point = exheader_header.codeset_info.text.address;
280 code_size = exheader_header.codeset_info.text.code_size;
281 stack_size = exheader_header.codeset_info.stack_size;
282 bss_size = exheader_header.codeset_info.bss_size;
283 core_version = exheader_header.arm11_system_local_caps.core_version;
284 priority = exheader_header.arm11_system_local_caps.priority;
285 resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category;
286
287 LOG_DEBUG(Loader, "Name: %s", exheader_header.codeset_info.name);
288 LOG_DEBUG(Loader, "Program ID: %016" PRIX64, ncch_header.program_id);
289 LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no");
290 LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point);
291 LOG_DEBUG(Loader, "Code size: 0x%08X", code_size);
292 LOG_DEBUG(Loader, "Stack size: 0x%08X", stack_size);
293 LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size);
294 LOG_DEBUG(Loader, "Core version: %d", core_version);
295 LOG_DEBUG(Loader, "Thread priority: 0x%X", priority);
296 LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category);
297 LOG_DEBUG(Loader, "System Mode: %d",
298 static_cast<int>(exheader_header.arm11_system_local_caps.system_mode));
299
300 if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) {
301 LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted.");
302 return ResultStatus::ErrorEncrypted;
303 }
304
305 // Read ExeFS...
306
307 exefs_offset = ncch_header.exefs_offset * kBlockSize;
308 u32 exefs_size = ncch_header.exefs_size * kBlockSize;
309
310 LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset);
311 LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size);
312
313 file.Seek(exefs_offset + ncch_offset, SEEK_SET);
314 if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header))
315 return ResultStatus::Error;
316
317 is_exefs_loaded = true;
318 return ResultStatus::Success;
319}
320
321void AppLoader_NCCH::ParseRegionLockoutInfo() { 148void AppLoader_NCCH::ParseRegionLockoutInfo() {
322 std::vector<u8> smdh_buffer; 149 std::vector<u8> smdh_buffer;
323 if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) { 150 if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) {
@@ -335,28 +162,43 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() {
335 } 162 }
336} 163}
337 164
338ResultStatus AppLoader_NCCH::Load() { 165ResultStatus AppLoader_NCCH::Load(Kernel::SharedPtr<Kernel::Process>& process) {
166 u64_le ncch_program_id;
167
339 if (is_loaded) 168 if (is_loaded)
340 return ResultStatus::ErrorAlreadyLoaded; 169 return ResultStatus::ErrorAlreadyLoaded;
341 170
342 ResultStatus result = LoadExeFS(); 171 ResultStatus result = base_ncch.Load();
343 if (result != ResultStatus::Success) 172 if (result != ResultStatus::Success)
344 return result; 173 return result;
345 174
346 std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_header.program_id)}; 175 ReadProgramId(ncch_program_id);
176 std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_program_id)};
347 177
348 LOG_INFO(Loader, "Program ID: %s", program_id.c_str()); 178 LOG_INFO(Loader, "Program ID: %s", program_id.c_str());
349 179
180 update_ncch.OpenFile(GetUpdateNCCHPath(ncch_program_id));
181 result = update_ncch.Load();
182 if (result == ResultStatus::Success) {
183 overlay_ncch = &update_ncch;
184 }
185
350 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); 186 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
351 187
188 if (auto room_member = Network::GetRoomMember().lock()) {
189 Network::GameInfo game_info;
190 ReadTitle(game_info.name);
191 game_info.id = ncch_program_id;
192 room_member->SendGameInfo(game_info);
193 }
194
352 is_loaded = true; // Set state to loaded 195 is_loaded = true; // Set state to loaded
353 196
354 result = LoadExec(); // Load the executable into memory for booting 197 result = LoadExec(process); // Load the executable into memory for booting
355 if (ResultStatus::Success != result) 198 if (ResultStatus::Success != result)
356 return result; 199 return result;
357 200
358 Service::FS::RegisterArchiveType(std::make_unique<FileSys::ArchiveFactory_SelfNCCH>(*this), 201 Service::FS::RegisterSelfNCCH(*this);
359 Service::FS::ArchiveIdCode::SelfNCCH);
360 202
361 ParseRegionLockoutInfo(); 203 ParseRegionLockoutInfo();
362 204
@@ -364,61 +206,58 @@ ResultStatus AppLoader_NCCH::Load() {
364} 206}
365 207
366ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) { 208ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) {
367 return LoadSectionExeFS(".code", buffer); 209 return overlay_ncch->LoadSectionExeFS(".code", buffer);
368} 210}
369 211
370ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) { 212ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) {
371 return LoadSectionExeFS("icon", buffer); 213 return overlay_ncch->LoadSectionExeFS("icon", buffer);
372} 214}
373 215
374ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) { 216ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) {
375 return LoadSectionExeFS("banner", buffer); 217 return overlay_ncch->LoadSectionExeFS("banner", buffer);
376} 218}
377 219
378ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) { 220ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) {
379 return LoadSectionExeFS("logo", buffer); 221 return overlay_ncch->LoadSectionExeFS("logo", buffer);
380} 222}
381 223
382ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) { 224ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) {
383 if (!file.IsOpen()) 225 ResultStatus result = base_ncch.ReadProgramId(out_program_id);
384 return ResultStatus::Error;
385
386 ResultStatus result = LoadExeFS();
387 if (result != ResultStatus::Success) 226 if (result != ResultStatus::Success)
388 return result; 227 return result;
389 228
390 out_program_id = ncch_header.program_id;
391 return ResultStatus::Success; 229 return ResultStatus::Success;
392} 230}
393 231
394ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, 232ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
395 u64& size) { 233 u64& size) {
396 if (!file.IsOpen()) 234 return base_ncch.ReadRomFS(romfs_file, offset, size);
397 return ResultStatus::Error; 235}
398 236
399 // Check if the NCCH has a RomFS... 237ResultStatus AppLoader_NCCH::ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
400 if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { 238 u64& offset, u64& size) {
401 u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; 239 ResultStatus result = update_ncch.ReadRomFS(romfs_file, offset, size);
402 u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000;
403 240
404 LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); 241 if (result != ResultStatus::Success)
405 LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); 242 return base_ncch.ReadRomFS(romfs_file, offset, size);
243}
406 244
407 if (file.GetSize() < romfs_offset + romfs_size) 245ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) {
408 return ResultStatus::Error; 246 std::vector<u8> data;
247 Loader::SMDH smdh;
248 ReadIcon(data);
409 249
410 // We reopen the file, to allow its position to be independent from file's 250 if (!Loader::IsValidSMDH(data)) {
411 romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); 251 return ResultStatus::ErrorInvalidFormat;
412 if (!romfs_file->IsOpen()) 252 }
413 return ResultStatus::Error;
414 253
415 offset = romfs_offset; 254 memcpy(&smdh, data.data(), sizeof(Loader::SMDH));
416 size = romfs_size;
417 255
418 return ResultStatus::Success; 256 const auto& short_title = smdh.GetShortTitle(SMDH::TitleLanguage::English);
419 } 257 auto title_end = std::find(short_title.begin(), short_title.end(), u'\0');
420 LOG_DEBUG(Loader, "NCCH has no RomFS"); 258 title = Common::UTF16ToUTF8(std::u16string{short_title.begin(), title_end});
421 return ResultStatus::ErrorNotUsed; 259
260 return ResultStatus::Success;
422} 261}
423 262
424} // namespace Loader 263} // namespace Loader