summaryrefslogtreecommitdiff
path: root/src/core/loader/ncch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/loader/ncch.cpp')
-rw-r--r--src/core/loader/ncch.cpp263
1 files changed, 0 insertions, 263 deletions
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
deleted file mode 100644
index e33a37b2e..000000000
--- a/src/core/loader/ncch.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cinttypes>
7#include <codecvt>
8#include <cstring>
9#include <locale>
10#include <memory>
11#include "common/logging/log.h"
12#include "common/string_util.h"
13#include "common/swap.h"
14#include "core/core.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"
18#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/resource_limit.h"
20#include "core/hle/service/cfg/cfg.h"
21#include "core/hle/service/fs/archive.h"
22#include "core/loader/ncch.h"
23#include "core/loader/smdh.h"
24#include "core/memory.h"
25#include "network/network.h"
26
27////////////////////////////////////////////////////////////////////////////////////////////////////
28// Loader namespace
29
30namespace Loader {
31
32static const u64 UPDATE_MASK = 0x0000000e00000000;
33
34FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
35 u32 magic;
36 file.Seek(0x100, SEEK_SET);
37 if (1 != file.ReadArray<u32>(&magic, 1))
38 return FileType::Error;
39
40 if (MakeMagic('N', 'C', 'S', 'D') == magic)
41 return FileType::CCI;
42
43 if (MakeMagic('N', 'C', 'C', 'H') == magic)
44 return FileType::CXI;
45
46 return FileType::Error;
47}
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
68std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() {
69 if (!is_loaded) {
70 ResultStatus res = base_ncch.Load();
71 if (res != ResultStatus::Success) {
72 return std::make_pair(boost::none, res);
73 }
74 }
75
76 // Set the system mode as the one from the exheader.
77 return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.system_mode.Value(),
78 ResultStatus::Success);
79}
80
81ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr<Kernel::Process>& process) {
82 using Kernel::CodeSet;
83 using Kernel::SharedPtr;
84
85 if (!is_loaded)
86 return ResultStatus::ErrorNotLoaded;
87
88 std::vector<u8> code;
89 u64_le program_id;
90 if (ResultStatus::Success == ReadCode(code) &&
91 ResultStatus::Success == ReadProgramId(program_id)) {
92 std::string process_name = Common::StringFromFixedZeroTerminatedBuffer(
93 (const char*)overlay_ncch->exheader_header.codeset_info.name, 8);
94
95 SharedPtr<CodeSet> codeset = CodeSet::Create(process_name, program_id);
96
97 codeset->code.offset = 0;
98 codeset->code.addr = overlay_ncch->exheader_header.codeset_info.text.address;
99 codeset->code.size =
100 overlay_ncch->exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE;
101
102 codeset->rodata.offset = codeset->code.offset + codeset->code.size;
103 codeset->rodata.addr = overlay_ncch->exheader_header.codeset_info.ro.address;
104 codeset->rodata.size =
105 overlay_ncch->exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE;
106
107 // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just
108 // to the regular size. Playing it safe for now.
109 u32 bss_page_size = (overlay_ncch->exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF;
110 code.resize(code.size() + bss_page_size, 0);
111
112 codeset->data.offset = codeset->rodata.offset + codeset->rodata.size;
113 codeset->data.addr = overlay_ncch->exheader_header.codeset_info.data.address;
114 codeset->data.size =
115 overlay_ncch->exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE +
116 bss_page_size;
117
118 codeset->entrypoint = codeset->code.addr;
119 codeset->memory = std::make_shared<std::vector<u8>>(std::move(code));
120
121 process = Kernel::Process::Create("main");
122 process->LoadModule(codeset, codeset->entrypoint);
123
124 // Attach a resource limit to the process based on the resource limit category
125 process->resource_limit =
126 Kernel::ResourceLimit::GetForCategory(static_cast<Kernel::ResourceLimitCategory>(
127 overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category));
128
129 // Set the default CPU core for this process
130 process->ideal_processor =
131 overlay_ncch->exheader_header.arm11_system_local_caps.ideal_processor;
132
133 // Copy data while converting endianness
134 std::array<u32, ARRAY_SIZE(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors)>
135 kernel_caps;
136 std::copy_n(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(),
137 begin(kernel_caps));
138 process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size());
139
140 s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority;
141 u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size;
142 process->Run(codeset->entrypoint, priority, stack_size);
143 return ResultStatus::Success;
144 }
145 return ResultStatus::Error;
146}
147
148void AppLoader_NCCH::ParseRegionLockoutInfo() {
149 std::vector<u8> smdh_buffer;
150 if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) {
151 SMDH smdh;
152 memcpy(&smdh, smdh_buffer.data(), sizeof(SMDH));
153 u32 region_lockout = smdh.region_lockout;
154 constexpr u32 REGION_COUNT = 7;
155 for (u32 region = 0; region < REGION_COUNT; ++region) {
156 if (region_lockout & 1) {
157 Service::CFG::SetPreferredRegionCode(region);
158 break;
159 }
160 region_lockout >>= 1;
161 }
162 }
163}
164
165ResultStatus AppLoader_NCCH::Load(Kernel::SharedPtr<Kernel::Process>& process) {
166 u64_le ncch_program_id;
167
168 if (is_loaded)
169 return ResultStatus::ErrorAlreadyLoaded;
170
171 ResultStatus result = base_ncch.Load();
172 if (result != ResultStatus::Success)
173 return result;
174
175 ReadProgramId(ncch_program_id);
176 std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_program_id)};
177
178 LOG_INFO(Loader, "Program ID: %s", program_id.c_str());
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
186 Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
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
195 is_loaded = true; // Set state to loaded
196
197 result = LoadExec(process); // Load the executable into memory for booting
198 if (ResultStatus::Success != result)
199 return result;
200
201 Service::FS::RegisterSelfNCCH(*this);
202
203 ParseRegionLockoutInfo();
204
205 return ResultStatus::Success;
206}
207
208ResultStatus AppLoader_NCCH::ReadCode(std::vector<u8>& buffer) {
209 return overlay_ncch->LoadSectionExeFS(".code", buffer);
210}
211
212ResultStatus AppLoader_NCCH::ReadIcon(std::vector<u8>& buffer) {
213 return overlay_ncch->LoadSectionExeFS("icon", buffer);
214}
215
216ResultStatus AppLoader_NCCH::ReadBanner(std::vector<u8>& buffer) {
217 return overlay_ncch->LoadSectionExeFS("banner", buffer);
218}
219
220ResultStatus AppLoader_NCCH::ReadLogo(std::vector<u8>& buffer) {
221 return overlay_ncch->LoadSectionExeFS("logo", buffer);
222}
223
224ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) {
225 ResultStatus result = base_ncch.ReadProgramId(out_program_id);
226 if (result != ResultStatus::Success)
227 return result;
228
229 return ResultStatus::Success;
230}
231
232ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset,
233 u64& size) {
234 return base_ncch.ReadRomFS(romfs_file, offset, size);
235}
236
237ResultStatus AppLoader_NCCH::ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file,
238 u64& offset, u64& size) {
239 ResultStatus result = update_ncch.ReadRomFS(romfs_file, offset, size);
240
241 if (result != ResultStatus::Success)
242 return base_ncch.ReadRomFS(romfs_file, offset, size);
243}
244
245ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) {
246 std::vector<u8> data;
247 Loader::SMDH smdh;
248 ReadIcon(data);
249
250 if (!Loader::IsValidSMDH(data)) {
251 return ResultStatus::ErrorInvalidFormat;
252 }
253
254 memcpy(&smdh, data.data(), sizeof(Loader::SMDH));
255
256 const auto& short_title = smdh.GetShortTitle(SMDH::TitleLanguage::English);
257 auto title_end = std::find(short_title.begin(), short_title.end(), u'\0');
258 title = Common::UTF16ToUTF8(std::u16string{short_title.begin(), title_end});
259
260 return ResultStatus::Success;
261}
262
263} // namespace Loader