summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/audio_core/stream.cpp2
-rw-r--r--src/core/CMakeLists.txt25
-rw-r--r--src/core/arm/arm_interface.cpp201
-rw-r--r--src/core/arm/arm_interface.h11
-rw-r--r--src/core/core.cpp80
-rw-r--r--src/core/core.h12
-rw-r--r--src/core/core_cpu.cpp4
-rw-r--r--src/core/core_timing.cpp34
-rw-r--r--src/core/core_timing.h23
-rw-r--r--src/core/crypto/partition_data_manager.cpp131
-rw-r--r--src/core/file_sys/kernel_executable.cpp228
-rw-r--r--src/core/file_sys/kernel_executable.h99
-rw-r--r--src/core/file_sys/nca_metadata.h6
-rw-r--r--src/core/file_sys/patch_manager.cpp10
-rw-r--r--src/core/file_sys/patch_manager.h9
-rw-r--r--src/core/file_sys/program_metadata.cpp15
-rw-r--r--src/core/file_sys/program_metadata.h5
-rw-r--r--src/core/file_sys/registered_cache.cpp23
-rw-r--r--src/core/file_sys/registered_cache.h3
-rw-r--r--src/core/file_sys/submission_package.cpp11
-rw-r--r--src/core/frontend/applets/general_frontend.cpp99
-rw-r--r--src/core/frontend/applets/general_frontend.h84
-rw-r--r--src/core/frontend/applets/web_browser.cpp6
-rw-r--r--src/core/frontend/applets/web_browser.h8
-rw-r--r--src/core/hle/kernel/kernel.cpp14
-rw-r--r--src/core/hle/kernel/kernel.h5
-rw-r--r--src/core/hle/kernel/process.cpp137
-rw-r--r--src/core/hle/kernel/process.h20
-rw-r--r--src/core/hle/kernel/svc.cpp8
-rw-r--r--src/core/hle/kernel/thread.cpp10
-rw-r--r--src/core/hle/kernel/thread.h16
-rw-r--r--src/core/hle/kernel/vm_manager.cpp31
-rw-r--r--src/core/hle/kernel/vm_manager.h31
-rw-r--r--src/core/hle/service/acc/acc.cpp79
-rw-r--r--src/core/hle/service/acc/acc.h24
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp4
-rw-r--r--src/core/hle/service/acc/errors.h14
-rw-r--r--src/core/hle/service/am/am.cpp12
-rw-r--r--src/core/hle/service/am/am.h6
-rw-r--r--src/core/hle/service/am/applet_ae.cpp26
-rw-r--r--src/core/hle/service/am/applet_ae.h3
-rw-r--r--src/core/hle/service/am/applet_oe.cpp14
-rw-r--r--src/core/hle/service/am/applet_oe.h3
-rw-r--r--src/core/hle/service/am/applets/applets.cpp77
-rw-r--r--src/core/hle/service/am/applets/applets.h30
-rw-r--r--src/core/hle/service/am/applets/error.cpp19
-rw-r--r--src/core/hle/service/am/applets/general_backend.cpp126
-rw-r--r--src/core/hle/service/am/applets/general_backend.h35
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp496
-rw-r--r--src/core/hle/service/am/applets/web_browser.h37
-rw-r--r--src/core/hle/service/arp/arp.cpp75
-rw-r--r--src/core/hle/service/arp/arp.h16
-rw-r--r--src/core/hle/service/audio/audren_u.cpp19
-rw-r--r--src/core/hle/service/fatal/fatal.cpp26
-rw-r--r--src/core/hle/service/friend/errors.h12
-rw-r--r--src/core/hle/service/friend/friend.cpp115
-rw-r--r--src/core/hle/service/friend/friend.h1
-rw-r--r--src/core/hle/service/friend/interface.cpp2
-rw-r--r--src/core/hle/service/glue/arp.cpp297
-rw-r--r--src/core/hle/service/glue/arp.h43
-rw-r--r--src/core/hle/service/glue/bgtc.cpp50
-rw-r--r--src/core/hle/service/glue/bgtc.h23
-rw-r--r--src/core/hle/service/glue/errors.h16
-rw-r--r--src/core/hle/service/glue/glue.cpp25
-rw-r--r--src/core/hle/service/glue/glue.h16
-rw-r--r--src/core/hle/service/glue/manager.cpp78
-rw-r--r--src/core/hle/service/glue/manager.h63
-rw-r--r--src/core/hle/service/prepo/prepo.cpp25
-rw-r--r--src/core/hle/service/service.cpp11
-rw-r--r--src/core/hle/service/set/set.cpp10
-rw-r--r--src/core/hle/service/set/set.h1
-rw-r--r--src/core/hle/service/time/interface.cpp11
-rw-r--r--src/core/hle/service/time/interface.h5
-rw-r--r--src/core/hle/service/time/time.cpp115
-rw-r--r--src/core/hle/service/time/time.h11
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp68
-rw-r--r--src/core/hle/service/time/time_sharedmemory.h74
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp11
-rw-r--r--src/core/loader/deconstructed_rom_directory.h4
-rw-r--r--src/core/loader/kip.cpp102
-rw-r--r--src/core/loader/kip.h35
-rw-r--r--src/core/loader/loader.cpp16
-rw-r--r--src/core/loader/loader.h11
-rw-r--r--src/core/loader/nax.cpp4
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.cpp9
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nso.cpp9
-rw-r--r--src/core/loader/nso.h5
-rw-r--r--src/core/loader/nsp.cpp7
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp6
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/reporter.cpp353
-rw-r--r--src/core/reporter.h56
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h4
-rw-r--r--src/core/telemetry_session.cpp2
-rw-r--r--src/core/tools/freezer.cpp188
-rw-r--r--src/core/tools/freezer.h82
-rw-r--r--src/tests/core/arm/arm_test_common.cpp3
-rw-r--r--src/tests/core/core_timing.cpp20
-rw-r--r--src/video_core/rasterizer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_global_cache.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp76
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h29
-rw-r--r--src/yuzu/applets/web_browser.cpp4
-rw-r--r--src/yuzu/applets/web_browser.h4
-rw-r--r--src/yuzu/configuration/config.cpp11
-rw-r--r--src/yuzu/configuration/configure_debug.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.ui40
-rw-r--r--src/yuzu/configuration/configure_general.cpp5
-rw-r--r--src/yuzu/configuration/configure_general.ui20
-rw-r--r--src/yuzu/game_list.cpp3
-rw-r--r--src/yuzu/main.cpp12
-rw-r--r--src/yuzu_cmd/config.cpp5
-rw-r--r--src/yuzu_cmd/default_ini.h5
-rw-r--r--src/yuzu_tester/CMakeLists.txt34
-rw-r--r--src/yuzu_tester/config.cpp184
-rw-r--r--src/yuzu_tester/config.h24
-rw-r--r--src/yuzu_tester/default_ini.h150
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp122
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h41
-rw-r--r--src/yuzu_tester/resource.h16
-rw-r--r--src/yuzu_tester/service/yuzutest.cpp112
-rw-r--r--src/yuzu_tester/service/yuzutest.h25
-rw-r--r--src/yuzu_tester/yuzu.cpp267
-rw-r--r--src/yuzu_tester/yuzu.rc17
131 files changed, 5078 insertions, 693 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 04018233f..f18239edb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -88,6 +88,7 @@ add_subdirectory(tests)
88 88
89if (ENABLE_SDL2) 89if (ENABLE_SDL2)
90 add_subdirectory(yuzu_cmd) 90 add_subdirectory(yuzu_cmd)
91 add_subdirectory(yuzu_tester)
91endif() 92endif()
92 93
93if (ENABLE_QT) 94if (ENABLE_QT)
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 982c7af2f..6a5f53a57 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -105,7 +105,7 @@ void Stream::PlayNextBuffer() {
105 105
106 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); 106 sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
107 107
108 core_timing.ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); 108 core_timing.ScheduleEvent(GetBufferReleaseCycles(*active_buffer), release_event, {});
109} 109}
110 110
111void Stream::ReleaseActiveBuffer() { 111void Stream::ReleaseActiveBuffer() {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4204ace2b..30eb9d82e 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -45,6 +45,8 @@ add_library(core STATIC
45 file_sys/fsmitm_romfsbuild.h 45 file_sys/fsmitm_romfsbuild.h
46 file_sys/ips_layer.cpp 46 file_sys/ips_layer.cpp
47 file_sys/ips_layer.h 47 file_sys/ips_layer.h
48 file_sys/kernel_executable.cpp
49 file_sys/kernel_executable.h
48 file_sys/mode.h 50 file_sys/mode.h
49 file_sys/nca_metadata.cpp 51 file_sys/nca_metadata.cpp
50 file_sys/nca_metadata.h 52 file_sys/nca_metadata.h
@@ -173,6 +175,7 @@ add_library(core STATIC
173 hle/service/acc/acc_u0.h 175 hle/service/acc/acc_u0.h
174 hle/service/acc/acc_u1.cpp 176 hle/service/acc/acc_u1.cpp
175 hle/service/acc/acc_u1.h 177 hle/service/acc/acc_u1.h
178 hle/service/acc/errors.h
176 hle/service/acc/profile_manager.cpp 179 hle/service/acc/profile_manager.cpp
177 hle/service/acc/profile_manager.h 180 hle/service/acc/profile_manager.h
178 hle/service/am/am.cpp 181 hle/service/am/am.cpp
@@ -207,8 +210,6 @@ add_library(core STATIC
207 hle/service/apm/apm.h 210 hle/service/apm/apm.h
208 hle/service/apm/interface.cpp 211 hle/service/apm/interface.cpp
209 hle/service/apm/interface.h 212 hle/service/apm/interface.h
210 hle/service/arp/arp.cpp
211 hle/service/arp/arp.h
212 hle/service/audio/audctl.cpp 213 hle/service/audio/audctl.cpp
213 hle/service/audio/audctl.h 214 hle/service/audio/audctl.h
214 hle/service/audio/auddbg.cpp 215 hle/service/audio/auddbg.cpp
@@ -270,10 +271,20 @@ add_library(core STATIC
270 hle/service/filesystem/fsp_srv.h 271 hle/service/filesystem/fsp_srv.h
271 hle/service/fgm/fgm.cpp 272 hle/service/fgm/fgm.cpp
272 hle/service/fgm/fgm.h 273 hle/service/fgm/fgm.h
274 hle/service/friend/errors.h
273 hle/service/friend/friend.cpp 275 hle/service/friend/friend.cpp
274 hle/service/friend/friend.h 276 hle/service/friend/friend.h
275 hle/service/friend/interface.cpp 277 hle/service/friend/interface.cpp
276 hle/service/friend/interface.h 278 hle/service/friend/interface.h
279 hle/service/glue/arp.cpp
280 hle/service/glue/arp.h
281 hle/service/glue/bgtc.cpp
282 hle/service/glue/bgtc.h
283 hle/service/glue/errors.h
284 hle/service/glue/glue.cpp
285 hle/service/glue/glue.h
286 hle/service/glue/manager.cpp
287 hle/service/glue/manager.h
277 hle/service/grc/grc.cpp 288 hle/service/grc/grc.cpp
278 hle/service/grc/grc.h 289 hle/service/grc/grc.h
279 hle/service/hid/hid.cpp 290 hle/service/hid/hid.cpp
@@ -420,6 +431,8 @@ add_library(core STATIC
420 hle/service/time/interface.h 431 hle/service/time/interface.h
421 hle/service/time/time.cpp 432 hle/service/time/time.cpp
422 hle/service/time/time.h 433 hle/service/time/time.h
434 hle/service/time/time_sharedmemory.cpp
435 hle/service/time/time_sharedmemory.h
423 hle/service/usb/usb.cpp 436 hle/service/usb/usb.cpp
424 hle/service/usb/usb.h 437 hle/service/usb/usb.h
425 hle/service/vi/display/vi_display.cpp 438 hle/service/vi/display/vi_display.cpp
@@ -440,6 +453,8 @@ add_library(core STATIC
440 loader/deconstructed_rom_directory.h 453 loader/deconstructed_rom_directory.h
441 loader/elf.cpp 454 loader/elf.cpp
442 loader/elf.h 455 loader/elf.h
456 loader/kip.cpp
457 loader/kip.h
443 loader/loader.cpp 458 loader/loader.cpp
444 loader/loader.h 459 loader/loader.h
445 loader/nax.cpp 460 loader/nax.cpp
@@ -459,16 +474,20 @@ add_library(core STATIC
459 memory_setup.h 474 memory_setup.h
460 perf_stats.cpp 475 perf_stats.cpp
461 perf_stats.h 476 perf_stats.h
477 reporter.cpp
478 reporter.h
462 settings.cpp 479 settings.cpp
463 settings.h 480 settings.h
464 telemetry_session.cpp 481 telemetry_session.cpp
465 telemetry_session.h 482 telemetry_session.h
483 tools/freezer.cpp
484 tools/freezer.h
466) 485)
467 486
468create_target_directory_groups(core) 487create_target_directory_groups(core)
469 488
470target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 489target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
471target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives) 490target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
472if (ENABLE_WEB_SERVICE) 491if (ENABLE_WEB_SERVICE)
473 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 492 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
474 target_link_libraries(core PRIVATE web_service) 493 target_link_libraries(core PRIVATE web_service)
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index 2223cbeed..372612c9b 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -2,26 +2,213 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <map>
6#include <optional>
7#include "common/bit_field.h"
5#include "common/common_types.h" 8#include "common/common_types.h"
6#include "common/logging/log.h" 9#include "common/logging/log.h"
7#include "core/arm/arm_interface.h" 10#include "core/arm/arm_interface.h"
11#include "core/core.h"
12#include "core/loader/loader.h"
8#include "core/memory.h" 13#include "core/memory.h"
9 14
10namespace Core { 15namespace Core {
11void ARM_Interface::LogBacktrace() const {
12 VAddr fp = GetReg(29);
13 VAddr lr = GetReg(30);
14 const VAddr sp = GetReg(13);
15 const VAddr pc = GetPC();
16 16
17 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc); 17namespace {
18
19constexpr u64 ELF_DYNAMIC_TAG_NULL = 0;
20constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5;
21constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6;
22constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11;
23
24enum class ELFSymbolType : u8 {
25 None = 0,
26 Object = 1,
27 Function = 2,
28 Section = 3,
29 File = 4,
30 Common = 5,
31 TLS = 6,
32};
33
34enum class ELFSymbolBinding : u8 {
35 Local = 0,
36 Global = 1,
37 Weak = 2,
38};
39
40enum class ELFSymbolVisibility : u8 {
41 Default = 0,
42 Internal = 1,
43 Hidden = 2,
44 Protected = 3,
45};
46
47struct ELFSymbol {
48 u32 name_index;
49 union {
50 u8 info;
51
52 BitField<0, 4, ELFSymbolType> type;
53 BitField<4, 4, ELFSymbolBinding> binding;
54 };
55 ELFSymbolVisibility visibility;
56 u16 sh_index;
57 u64 value;
58 u64 size;
59};
60static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size.");
61
62using Symbols = std::vector<std::pair<ELFSymbol, std::string>>;
63
64Symbols GetSymbols(VAddr text_offset) {
65 const auto mod_offset = text_offset + Memory::Read32(text_offset + 4);
66
67 if (mod_offset < text_offset || (mod_offset & 0b11) != 0 ||
68 Memory::Read32(mod_offset) != Common::MakeMagic('M', 'O', 'D', '0')) {
69 return {};
70 }
71
72 const auto dynamic_offset = Memory::Read32(mod_offset + 0x4) + mod_offset;
73
74 VAddr string_table_offset{};
75 VAddr symbol_table_offset{};
76 u64 symbol_entry_size{};
77
78 VAddr dynamic_index = dynamic_offset;
79 while (true) {
80 const auto tag = Memory::Read64(dynamic_index);
81 const auto value = Memory::Read64(dynamic_index + 0x8);
82 dynamic_index += 0x10;
83
84 if (tag == ELF_DYNAMIC_TAG_NULL) {
85 break;
86 }
87
88 if (tag == ELF_DYNAMIC_TAG_STRTAB) {
89 string_table_offset = value;
90 } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) {
91 symbol_table_offset = value;
92 } else if (tag == ELF_DYNAMIC_TAG_SYMENT) {
93 symbol_entry_size = value;
94 }
95 }
96
97 if (string_table_offset == 0 || symbol_table_offset == 0 || symbol_entry_size == 0) {
98 return {};
99 }
100
101 const auto string_table_address = text_offset + string_table_offset;
102 const auto symbol_table_address = text_offset + symbol_table_offset;
103
104 Symbols out;
105
106 VAddr symbol_index = symbol_table_address;
107 while (symbol_index < string_table_address) {
108 ELFSymbol symbol{};
109 Memory::ReadBlock(symbol_index, &symbol, sizeof(ELFSymbol));
110
111 VAddr string_offset = string_table_address + symbol.name_index;
112 std::string name;
113 for (u8 c = Memory::Read8(string_offset); c != 0; c = Memory::Read8(++string_offset)) {
114 name += static_cast<char>(c);
115 }
116
117 symbol_index += symbol_entry_size;
118 out.push_back({symbol, name});
119 }
120
121 return out;
122}
123
124std::optional<std::string> GetSymbolName(const Symbols& symbols, VAddr func_address) {
125 const auto iter =
126 std::find_if(symbols.begin(), symbols.end(), [func_address](const auto& pair) {
127 const auto& [symbol, name] = pair;
128 const auto end_address = symbol.value + symbol.size;
129 return func_address >= symbol.value && func_address < end_address;
130 });
131
132 if (iter == symbols.end()) {
133 return std::nullopt;
134 }
135
136 return iter->second;
137}
138
139} // Anonymous namespace
140
141constexpr u64 SEGMENT_BASE = 0x7100000000ull;
142
143std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktrace() const {
144 std::vector<BacktraceEntry> out;
145
146 auto fp = GetReg(29);
147 auto lr = GetReg(30);
148
18 while (true) { 149 while (true) {
19 LOG_ERROR(Core_ARM, "{:016X}", lr); 150 out.push_back({"", 0, lr, 0});
20 if (!fp) { 151 if (!fp) {
21 break; 152 break;
22 } 153 }
23 lr = Memory::Read64(fp + 8) - 4; 154 lr = Memory::Read64(fp + 8) - 4;
24 fp = Memory::Read64(fp); 155 fp = Memory::Read64(fp);
25 } 156 }
157
158 std::map<VAddr, std::string> modules;
159 auto& loader{System::GetInstance().GetAppLoader()};
160 if (loader.ReadNSOModules(modules) != Loader::ResultStatus::Success) {
161 return {};
162 }
163
164 std::map<std::string, Symbols> symbols;
165 for (const auto& module : modules) {
166 symbols.insert_or_assign(module.second, GetSymbols(module.first));
167 }
168
169 for (auto& entry : out) {
170 VAddr base = 0;
171 for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
172 const auto& module{*iter};
173 if (entry.original_address >= module.first) {
174 entry.module = module.second;
175 base = module.first;
176 break;
177 }
178 }
179
180 entry.offset = entry.original_address - base;
181 entry.address = SEGMENT_BASE + entry.offset;
182
183 if (entry.module.empty())
184 entry.module = "unknown";
185
186 const auto symbol_set = symbols.find(entry.module);
187 if (symbol_set != symbols.end()) {
188 const auto symbol = GetSymbolName(symbol_set->second, entry.offset);
189 if (symbol.has_value()) {
190 // TODO(DarkLordZach): Add demangling of symbol names.
191 entry.name = *symbol;
192 }
193 }
194 }
195
196 return out;
197}
198
199void ARM_Interface::LogBacktrace() const {
200 const VAddr sp = GetReg(13);
201 const VAddr pc = GetPC();
202 LOG_ERROR(Core_ARM, "Backtrace, sp={:016X}, pc={:016X}", sp, pc);
203 LOG_ERROR(Core_ARM, "{:20}{:20}{:20}{:20}{}", "Module Name", "Address", "Original Address",
204 "Offset", "Symbol");
205 LOG_ERROR(Core_ARM, "");
206
207 const auto backtrace = GetBacktrace();
208 for (const auto& entry : backtrace) {
209 LOG_ERROR(Core_ARM, "{:20}{:016X} {:016X} {:016X} {}", entry.module, entry.address,
210 entry.original_address, entry.offset, entry.name);
211 }
26} 212}
213
27} // namespace Core 214} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 978b1518f..c6691a8e1 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <vector>
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
10namespace Common { 11namespace Common {
@@ -152,6 +153,16 @@ public:
152 /// Prepare core for thread reschedule (if needed to correctly handle state) 153 /// Prepare core for thread reschedule (if needed to correctly handle state)
153 virtual void PrepareReschedule() = 0; 154 virtual void PrepareReschedule() = 0;
154 155
156 struct BacktraceEntry {
157 std::string module;
158 u64 address;
159 u64 original_address;
160 u64 offset;
161 std::string name;
162 };
163
164 std::vector<BacktraceEntry> GetBacktrace() const;
165
155 /// fp (= r29) points to the last frame record. 166 /// fp (= r29) points to the last frame record.
156 /// Note that this is the frame record for the *previous* frame, not the current one. 167 /// Note that this is the frame record for the *previous* frame, not the current one.
157 /// Note we need to subtract 4 from our last read to get the proper address 168 /// Note we need to subtract 4 from our last read to get the proper address
diff --git a/src/core/core.cpp b/src/core/core.cpp
index ff0721079..262411db8 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -25,19 +25,47 @@
25#include "core/hle/kernel/scheduler.h" 25#include "core/hle/kernel/scheduler.h"
26#include "core/hle/kernel/thread.h" 26#include "core/hle/kernel/thread.h"
27#include "core/hle/service/am/applets/applets.h" 27#include "core/hle/service/am/applets/applets.h"
28#include "core/hle/service/glue/manager.h"
28#include "core/hle/service/service.h" 29#include "core/hle/service/service.h"
29#include "core/hle/service/sm/sm.h" 30#include "core/hle/service/sm/sm.h"
30#include "core/loader/loader.h" 31#include "core/loader/loader.h"
31#include "core/perf_stats.h" 32#include "core/perf_stats.h"
33#include "core/reporter.h"
32#include "core/settings.h" 34#include "core/settings.h"
33#include "core/telemetry_session.h" 35#include "core/telemetry_session.h"
36#include "core/tools/freezer.h"
34#include "file_sys/cheat_engine.h" 37#include "file_sys/cheat_engine.h"
38#include "file_sys/patch_manager.h"
35#include "video_core/debug_utils/debug_utils.h" 39#include "video_core/debug_utils/debug_utils.h"
36#include "video_core/renderer_base.h" 40#include "video_core/renderer_base.h"
37#include "video_core/video_core.h" 41#include "video_core/video_core.h"
38 42
39namespace Core { 43namespace Core {
40 44
45namespace {
46
47FileSys::StorageId GetStorageIdForFrontendSlot(
48 std::optional<FileSys::ContentProviderUnionSlot> slot) {
49 if (!slot.has_value()) {
50 return FileSys::StorageId::None;
51 }
52
53 switch (*slot) {
54 case FileSys::ContentProviderUnionSlot::UserNAND:
55 return FileSys::StorageId::NandUser;
56 case FileSys::ContentProviderUnionSlot::SysNAND:
57 return FileSys::StorageId::NandSystem;
58 case FileSys::ContentProviderUnionSlot::SDMC:
59 return FileSys::StorageId::SdCard;
60 case FileSys::ContentProviderUnionSlot::FrontendManual:
61 return FileSys::StorageId::Host;
62 default:
63 return FileSys::StorageId::None;
64 }
65}
66
67} // Anonymous namespace
68
41/*static*/ System System::s_instance; 69/*static*/ System System::s_instance;
42 70
43FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, 71FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
@@ -74,7 +102,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
74 return vfs->OpenFile(path, FileSys::Mode::Read); 102 return vfs->OpenFile(path, FileSys::Mode::Read);
75} 103}
76struct System::Impl { 104struct System::Impl {
77 explicit Impl(System& system) : kernel{system}, cpu_core_manager{system} {} 105 explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
78 106
79 Cpu& CurrentCpuCore() { 107 Cpu& CurrentCpuCore() {
80 return cpu_core_manager.GetCurrentCore(); 108 return cpu_core_manager.GetCurrentCore();
@@ -109,6 +137,9 @@ struct System::Impl {
109 /// Create default implementations of applets if one is not provided. 137 /// Create default implementations of applets if one is not provided.
110 applet_manager.SetDefaultAppletsIfMissing(); 138 applet_manager.SetDefaultAppletsIfMissing();
111 139
140 /// Reset all glue registrations
141 arp_manager.ResetAll();
142
112 telemetry_session = std::make_unique<Core::TelemetrySession>(); 143 telemetry_session = std::make_unique<Core::TelemetrySession>();
113 service_manager = std::make_shared<Service::SM::ServiceManager>(); 144 service_manager = std::make_shared<Service::SM::ServiceManager>();
114 145
@@ -150,7 +181,8 @@ struct System::Impl {
150 } 181 }
151 182
152 telemetry_session->AddInitialInfo(*app_loader); 183 telemetry_session->AddInitialInfo(*app_loader);
153 auto main_process = Kernel::Process::Create(system, "main"); 184 auto main_process =
185 Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
154 const auto [load_result, load_parameters] = app_loader->Load(*main_process); 186 const auto [load_result, load_parameters] = app_loader->Load(*main_process);
155 if (load_result != Loader::ResultStatus::Success) { 187 if (load_result != Loader::ResultStatus::Success) {
156 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 188 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
@@ -159,6 +191,7 @@ struct System::Impl {
159 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + 191 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
160 static_cast<u32>(load_result)); 192 static_cast<u32>(load_result));
161 } 193 }
194 AddGlueRegistrationForProcess(*app_loader, *main_process);
162 kernel.MakeCurrentProcess(main_process.get()); 195 kernel.MakeCurrentProcess(main_process.get());
163 196
164 // Main process has been loaded and been made current. 197 // Main process has been loaded and been made current.
@@ -217,6 +250,31 @@ struct System::Impl {
217 return app_loader->ReadTitle(out); 250 return app_loader->ReadTitle(out);
218 } 251 }
219 252
253 void AddGlueRegistrationForProcess(Loader::AppLoader& loader, Kernel::Process& process) {
254 std::vector<u8> nacp_data;
255 FileSys::NACP nacp;
256 if (loader.ReadControlData(nacp) == Loader::ResultStatus::Success) {
257 nacp_data = nacp.GetRawBytes();
258 } else {
259 nacp_data.resize(sizeof(FileSys::RawNACP));
260 }
261
262 Service::Glue::ApplicationLaunchProperty launch{};
263 launch.title_id = process.GetTitleID();
264
265 FileSys::PatchManager pm{launch.title_id};
266 launch.version = pm.GetGameVersion().value_or(0);
267
268 // TODO(DarkLordZach): When FSController/Game Card Support is added, if
269 // current_process_game_card use correct StorageId
270 launch.base_game_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
271 launch.title_id, FileSys::ContentRecordType::Program));
272 launch.update_storage_id = GetStorageIdForFrontendSlot(content_provider->GetSlotForEntry(
273 FileSys::GetUpdateTitleID(launch.title_id), FileSys::ContentRecordType::Program));
274
275 arp_manager.Register(launch.title_id, launch, std::move(nacp_data));
276 }
277
220 void SetStatus(ResultStatus new_status, const char* details = nullptr) { 278 void SetStatus(ResultStatus new_status, const char* details = nullptr) {
221 status = new_status; 279 status = new_status;
222 if (details) { 280 if (details) {
@@ -243,16 +301,22 @@ struct System::Impl {
243 bool is_powered_on = false; 301 bool is_powered_on = false;
244 302
245 std::unique_ptr<FileSys::CheatEngine> cheat_engine; 303 std::unique_ptr<FileSys::CheatEngine> cheat_engine;
304 std::unique_ptr<Tools::Freezer> memory_freezer;
246 305
247 /// Frontend applets 306 /// Frontend applets
248 Service::AM::Applets::AppletManager applet_manager; 307 Service::AM::Applets::AppletManager applet_manager;
249 308
309 /// Glue services
310 Service::Glue::ARPManager arp_manager;
311
250 /// Service manager 312 /// Service manager
251 std::shared_ptr<Service::SM::ServiceManager> service_manager; 313 std::shared_ptr<Service::SM::ServiceManager> service_manager;
252 314
253 /// Telemetry session for this emulation session 315 /// Telemetry session for this emulation session
254 std::unique_ptr<Core::TelemetrySession> telemetry_session; 316 std::unique_ptr<Core::TelemetrySession> telemetry_session;
255 317
318 Reporter reporter;
319
256 ResultStatus status = ResultStatus::Success; 320 ResultStatus status = ResultStatus::Success;
257 std::string status_details = ""; 321 std::string status_details = "";
258 322
@@ -492,6 +556,18 @@ void System::ClearContentProvider(FileSys::ContentProviderUnionSlot slot) {
492 impl->content_provider->ClearSlot(slot); 556 impl->content_provider->ClearSlot(slot);
493} 557}
494 558
559const Reporter& System::GetReporter() const {
560 return impl->reporter;
561}
562
563Service::Glue::ARPManager& System::GetARPManager() {
564 return impl->arp_manager;
565}
566
567const Service::Glue::ARPManager& System::GetARPManager() const {
568 return impl->arp_manager;
569}
570
495System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 571System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
496 return impl->Init(*this, emu_window); 572 return impl->Init(*this, emu_window);
497} 573}
diff --git a/src/core/core.h b/src/core/core.h
index 20959de54..70adb7af9 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <string> 9#include <string>
10 10
11#include <map>
11#include "common/common_types.h" 12#include "common/common_types.h"
12#include "core/file_sys/vfs_types.h" 13#include "core/file_sys/vfs_types.h"
13#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
@@ -42,6 +43,10 @@ struct AppletFrontendSet;
42class AppletManager; 43class AppletManager;
43} // namespace AM::Applets 44} // namespace AM::Applets
44 45
46namespace Glue {
47class ARPManager;
48}
49
45namespace SM { 50namespace SM {
46class ServiceManager; 51class ServiceManager;
47} // namespace SM 52} // namespace SM
@@ -68,6 +73,7 @@ class Cpu;
68class ExclusiveMonitor; 73class ExclusiveMonitor;
69class FrameLimiter; 74class FrameLimiter;
70class PerfStats; 75class PerfStats;
76class Reporter;
71class TelemetrySession; 77class TelemetrySession;
72 78
73struct PerfStatsResults; 79struct PerfStatsResults;
@@ -284,6 +290,12 @@ public:
284 290
285 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot); 291 void ClearContentProvider(FileSys::ContentProviderUnionSlot slot);
286 292
293 const Reporter& GetReporter() const;
294
295 Service::Glue::ARPManager& GetARPManager();
296
297 const Service::Glue::ARPManager& GetARPManager() const;
298
287private: 299private:
288 System(); 300 System();
289 301
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp
index ba63c3e61..99b7d387d 100644
--- a/src/core/core_cpu.cpp
+++ b/src/core/core_cpu.cpp
@@ -53,7 +53,7 @@ bool CpuBarrier::Rendezvous() {
53Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, 53Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
54 std::size_t core_index) 54 std::size_t core_index)
55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} { 55 : cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
56 if (Settings::values.use_cpu_jit) { 56 if (Settings::values.cpu_jit_enabled) {
57#ifdef ARCHITECTURE_x86_64 57#ifdef ARCHITECTURE_x86_64
58 arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index); 58 arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
59#else 59#else
@@ -70,7 +70,7 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
70Cpu::~Cpu() = default; 70Cpu::~Cpu() = default;
71 71
72std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { 72std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
73 if (Settings::values.use_cpu_jit) { 73 if (Settings::values.cpu_jit_enabled) {
74#ifdef ARCHITECTURE_x86_64 74#ifdef ARCHITECTURE_x86_64
75 return std::make_unique<DynarmicExclusiveMonitor>(num_cores); 75 return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
76#else 76#else
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 41adb2302..a58f7b131 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -56,12 +56,12 @@ void CoreTiming::Initialize() {
56} 56}
57 57
58void CoreTiming::Shutdown() { 58void CoreTiming::Shutdown() {
59 MoveEvents();
60 ClearPendingEvents(); 59 ClearPendingEvents();
61 UnregisterAllEvents(); 60 UnregisterAllEvents();
62} 61}
63 62
64EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { 63EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) {
64 std::lock_guard guard{inner_mutex};
65 // check for existing type with same name. 65 // check for existing type with same name.
66 // we want event type names to remain unique so that we can use them for serialization. 66 // we want event type names to remain unique so that we can use them for serialization.
67 ASSERT_MSG(event_types.find(name) == event_types.end(), 67 ASSERT_MSG(event_types.find(name) == event_types.end(),
@@ -82,6 +82,7 @@ void CoreTiming::UnregisterAllEvents() {
82 82
83void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { 83void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) {
84 ASSERT(event_type != nullptr); 84 ASSERT(event_type != nullptr);
85 std::lock_guard guard{inner_mutex};
85 const s64 timeout = GetTicks() + cycles_into_future; 86 const s64 timeout = GetTicks() + cycles_into_future;
86 87
87 // If this event needs to be scheduled before the next advance(), force one early 88 // If this event needs to be scheduled before the next advance(), force one early
@@ -93,12 +94,8 @@ void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_ty
93 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 94 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
94} 95}
95 96
96void CoreTiming::ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
97 u64 userdata) {
98 ts_queue.Push(Event{global_timer + cycles_into_future, 0, userdata, event_type});
99}
100
101void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { 97void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
98 std::lock_guard guard{inner_mutex};
102 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 99 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
103 return e.type == event_type && e.userdata == userdata; 100 return e.type == event_type && e.userdata == userdata;
104 }); 101 });
@@ -110,10 +107,6 @@ void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) {
110 } 107 }
111} 108}
112 109
113void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) {
114 unschedule_queue.Push(std::make_pair(event_type, userdata));
115}
116
117u64 CoreTiming::GetTicks() const { 110u64 CoreTiming::GetTicks() const {
118 u64 ticks = static_cast<u64>(global_timer); 111 u64 ticks = static_cast<u64>(global_timer);
119 if (!is_global_timer_sane) { 112 if (!is_global_timer_sane) {
@@ -135,6 +128,7 @@ void CoreTiming::ClearPendingEvents() {
135} 128}
136 129
137void CoreTiming::RemoveEvent(const EventType* event_type) { 130void CoreTiming::RemoveEvent(const EventType* event_type) {
131 std::lock_guard guard{inner_mutex};
138 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), 132 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(),
139 [&](const Event& e) { return e.type == event_type; }); 133 [&](const Event& e) { return e.type == event_type; });
140 134
@@ -145,11 +139,6 @@ void CoreTiming::RemoveEvent(const EventType* event_type) {
145 } 139 }
146} 140}
147 141
148void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) {
149 MoveEvents();
150 RemoveEvent(event_type);
151}
152
153void CoreTiming::ForceExceptionCheck(s64 cycles) { 142void CoreTiming::ForceExceptionCheck(s64 cycles) {
154 cycles = std::max<s64>(0, cycles); 143 cycles = std::max<s64>(0, cycles);
155 if (downcount <= cycles) { 144 if (downcount <= cycles) {
@@ -162,19 +151,8 @@ void CoreTiming::ForceExceptionCheck(s64 cycles) {
162 downcount = static_cast<int>(cycles); 151 downcount = static_cast<int>(cycles);
163} 152}
164 153
165void CoreTiming::MoveEvents() {
166 for (Event ev; ts_queue.Pop(ev);) {
167 ev.fifo_order = event_fifo_id++;
168 event_queue.emplace_back(std::move(ev));
169 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
170 }
171}
172
173void CoreTiming::Advance() { 154void CoreTiming::Advance() {
174 MoveEvents(); 155 std::unique_lock<std::mutex> guard(inner_mutex);
175 for (std::pair<const EventType*, u64> ev; unschedule_queue.Pop(ev);) {
176 UnscheduleEvent(ev.first, ev.second);
177 }
178 156
179 const int cycles_executed = slice_length - downcount; 157 const int cycles_executed = slice_length - downcount;
180 global_timer += cycles_executed; 158 global_timer += cycles_executed;
@@ -186,7 +164,9 @@ void CoreTiming::Advance() {
186 Event evt = std::move(event_queue.front()); 164 Event evt = std::move(event_queue.front());
187 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 165 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
188 event_queue.pop_back(); 166 event_queue.pop_back();
167 inner_mutex.unlock();
189 evt.type->callback(evt.userdata, global_timer - evt.time); 168 evt.type->callback(evt.userdata, global_timer - evt.time);
169 inner_mutex.lock();
190 } 170 }
191 171
192 is_global_timer_sane = false; 172 is_global_timer_sane = false;
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 9d2efde37..161c7007d 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -6,6 +6,7 @@
6 6
7#include <chrono> 7#include <chrono>
8#include <functional> 8#include <functional>
9#include <mutex>
9#include <string> 10#include <string>
10#include <unordered_map> 11#include <unordered_map>
11#include <vector> 12#include <vector>
@@ -67,7 +68,7 @@ public:
67 /// 68 ///
68 EventType* RegisterEvent(const std::string& name, TimedCallback callback); 69 EventType* RegisterEvent(const std::string& name, TimedCallback callback);
69 70
70 /// Unregisters all registered events thus far. 71 /// Unregisters all registered events thus far. Note: not thread unsafe
71 void UnregisterAllEvents(); 72 void UnregisterAllEvents();
72 73
73 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an 74 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an
@@ -76,20 +77,10 @@ public:
76 /// Scheduling from a callback will not update the downcount until the Advance() completes. 77 /// Scheduling from a callback will not update the downcount until the Advance() completes.
77 void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0); 78 void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
78 79
79 /// This is to be called when outside of hle threads, such as the graphics thread, wants to
80 /// schedule things to be executed on the main thread.
81 ///
82 /// @note This doesn't change slice_length and thus events scheduled by this might be
83 /// called with a delay of up to MAX_SLICE_LENGTH
84 void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
85 u64 userdata = 0);
86
87 void UnscheduleEvent(const EventType* event_type, u64 userdata); 80 void UnscheduleEvent(const EventType* event_type, u64 userdata);
88 void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
89 81
90 /// We only permit one event of each type in the queue at a time. 82 /// We only permit one event of each type in the queue at a time.
91 void RemoveEvent(const EventType* event_type); 83 void RemoveEvent(const EventType* event_type);
92 void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
93 84
94 void ForceExceptionCheck(s64 cycles); 85 void ForceExceptionCheck(s64 cycles);
95 86
@@ -120,7 +111,6 @@ private:
120 111
121 /// Clear all pending events. This should ONLY be done on exit. 112 /// Clear all pending events. This should ONLY be done on exit.
122 void ClearPendingEvents(); 113 void ClearPendingEvents();
123 void MoveEvents();
124 114
125 s64 global_timer = 0; 115 s64 global_timer = 0;
126 s64 idled_cycles = 0; 116 s64 idled_cycles = 0;
@@ -143,14 +133,9 @@ private:
143 // remain stable regardless of rehashes/resizing. 133 // remain stable regardless of rehashes/resizing.
144 std::unordered_map<std::string, EventType> event_types; 134 std::unordered_map<std::string, EventType> event_types;
145 135
146 // The queue for storing the events from other threads threadsafe until they will be added
147 // to the event_queue by the emu thread
148 Common::MPSCQueue<Event> ts_queue;
149
150 // The queue for unscheduling the events from other threads threadsafe
151 Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
152
153 EventType* ev_lost = nullptr; 136 EventType* ev_lost = nullptr;
137
138 std::mutex inner_mutex;
154}; 139};
155 140
156} // namespace Core::Timing 141} // namespace Core::Timing
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index ed0775444..01a969be9 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -22,8 +22,10 @@
22#include "core/crypto/key_manager.h" 22#include "core/crypto/key_manager.h"
23#include "core/crypto/partition_data_manager.h" 23#include "core/crypto/partition_data_manager.h"
24#include "core/crypto/xts_encryption_layer.h" 24#include "core/crypto/xts_encryption_layer.h"
25#include "core/file_sys/kernel_executable.h"
25#include "core/file_sys/vfs.h" 26#include "core/file_sys/vfs.h"
26#include "core/file_sys/vfs_offset.h" 27#include "core/file_sys/vfs_offset.h"
28#include "core/file_sys/vfs_vector.h"
27 29
28using namespace Common; 30using namespace Common;
29 31
@@ -45,36 +47,6 @@ struct Package2Header {
45}; 47};
46static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); 48static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
47 49
48struct INIHeader {
49 u32_le magic;
50 u32_le size;
51 u32_le process_count;
52 INSERT_PADDING_BYTES(4);
53};
54static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
55
56struct SectionHeader {
57 u32_le offset;
58 u32_le size_decompressed;
59 u32_le size_compressed;
60 u32_le attribute;
61};
62static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
63
64struct KIPHeader {
65 u32_le magic;
66 std::array<char, 12> name;
67 u64_le title_id;
68 u32_le category;
69 u8 priority;
70 u8 core;
71 INSERT_PADDING_BYTES(1);
72 u8 flags;
73 std::array<SectionHeader, 6> sections;
74 std::array<u32, 0x20> capabilities;
75};
76static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
77
78const std::array<SHA256Hash, 0x10> source_hashes{ 50const std::array<SHA256Hash, 0x10> source_hashes{
79 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source 51 "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
80 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source 52 "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
@@ -170,65 +142,6 @@ const std::array<SHA256Hash, 0x20> master_key_hashes{
170 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F 142 "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
171}; 143};
172 144
173static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
174 const auto data_size = in.size() - 0xC;
175
176 u32 compressed_size{};
177 u32 init_index{};
178 u32 additional_size{};
179 std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
180 std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
181 std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
182
183 std::vector<u8> out(in.size() + additional_size);
184
185 if (compressed_size == in.size())
186 std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
187 else
188 std::memcpy(out.data(), in.data(), compressed_size);
189
190 auto index = in.size() - init_index;
191 auto out_index = out.size();
192
193 while (out_index > 0) {
194 --index;
195 auto control = in[index];
196 for (size_t i = 0; i < 8; ++i) {
197 if ((control & 0x80) > 0) {
198 ASSERT(index >= 2);
199 index -= 2;
200 u64 segment_offset = in[index] | in[index + 1] << 8;
201 u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
202 segment_offset &= 0xFFF;
203 segment_offset += 3;
204
205 if (out_index < segment_size)
206 segment_size = out_index;
207
208 ASSERT(out_index >= segment_size);
209
210 out_index -= segment_size;
211
212 for (size_t j = 0; j < segment_size; ++j) {
213 ASSERT(out_index + j + segment_offset < out.size());
214 out[out_index + j] = out[out_index + j + segment_offset];
215 }
216 } else {
217 ASSERT(out_index >= 1);
218 --out_index;
219 --index;
220 out[out_index] = in[index];
221 }
222
223 control <<= 1;
224 if (out_index == 0)
225 return out;
226 }
227 }
228
229 return out;
230}
231
232static u8 CalculateMaxKeyblobSourceHash() { 145static u8 CalculateMaxKeyblobSourceHash() {
233 for (s8 i = 0x1F; i >= 0; --i) { 146 for (s8 i = 0x1F; i >= 0; --i) {
234 if (keyblob_source_hashes[i] != SHA256Hash{}) 147 if (keyblob_source_hashes[i] != SHA256Hash{})
@@ -478,37 +391,22 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
478 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); 391 cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
479 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); 392 cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
480 393
481 INIHeader ini; 394 const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c);
482 std::memcpy(&ini, c.data(), sizeof(INIHeader)); 395 const FileSys::INI ini{ini_file};
483 if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) 396 if (ini.GetStatus() != Loader::ResultStatus::Success)
484 return; 397 return;
485 398
486 u64 offset = sizeof(INIHeader); 399 for (const auto& kip : ini.GetKIPs()) {
487 for (size_t i = 0; i < ini.process_count; ++i) { 400 if (kip.GetStatus() != Loader::ResultStatus::Success)
488 KIPHeader kip;
489 std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
490 if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
491 return; 401 return;
492 402
493 const auto name = 403 if (kip.GetName() != "FS" && kip.GetName() != "spl") {
494 Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
495
496 if (name != "FS" && name != "spl") {
497 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
498 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
499 continue; 404 continue;
500 } 405 }
501 406
502 const u64 initial_offset = sizeof(KIPHeader) + offset; 407 const auto& text = kip.GetTextSection();
503 const auto text_begin = c.cbegin() + initial_offset; 408 const auto& rodata = kip.GetRODataSection();
504 const auto text_end = text_begin + kip.sections[0].size_compressed; 409 const auto& data = kip.GetDataSection();
505 const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
506
507 const auto rodata_end = text_end + kip.sections[1].size_compressed;
508 const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
509
510 const auto data_end = rodata_end + kip.sections[2].size_compressed;
511 const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
512 410
513 std::vector<u8> out; 411 std::vector<u8> out;
514 out.reserve(text.size() + rodata.size() + data.size()); 412 out.reserve(text.size() + rodata.size() + data.size());
@@ -516,12 +414,9 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa
516 out.insert(out.end(), rodata.begin(), rodata.end()); 414 out.insert(out.end(), rodata.begin(), rodata.end());
517 out.insert(out.end(), data.begin(), data.end()); 415 out.insert(out.end(), data.begin(), data.end());
518 416
519 offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + 417 if (kip.GetName() == "FS")
520 kip.sections[1].size_compressed + kip.sections[2].size_compressed;
521
522 if (name == "FS")
523 package2_fs[static_cast<size_t>(type)] = std::move(out); 418 package2_fs[static_cast<size_t>(type)] = std::move(out);
524 else if (name == "spl") 419 else if (kip.GetName() == "spl")
525 package2_spl[static_cast<size_t>(type)] = std::move(out); 420 package2_spl[static_cast<size_t>(type)] = std::move(out);
526 } 421 }
527} 422}
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
new file mode 100644
index 000000000..371300684
--- /dev/null
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -0,0 +1,228 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/string_util.h"
6#include "core/file_sys/kernel_executable.h"
7#include "core/file_sys/vfs_offset.h"
8
9namespace FileSys {
10
11constexpr u32 INI_MAX_KIPS = 0x50;
12
13namespace {
14bool DecompressBLZ(std::vector<u8>& data) {
15 if (data.size() < 0xC)
16 return {};
17
18 const auto data_size = data.size() - 0xC;
19
20 u32 compressed_size{};
21 u32 init_index{};
22 u32 additional_size{};
23 std::memcpy(&compressed_size, data.data() + data_size, sizeof(u32));
24 std::memcpy(&init_index, data.data() + data_size + 0x4, sizeof(u32));
25 std::memcpy(&additional_size, data.data() + data_size + 0x8, sizeof(u32));
26
27 const auto start_offset = data.size() - compressed_size;
28 data.resize(compressed_size + additional_size + start_offset);
29
30 std::size_t index = compressed_size - init_index;
31 std::size_t out_index = compressed_size + additional_size;
32
33 while (out_index > 0) {
34 --index;
35 auto control = data[index + start_offset];
36 for (size_t i = 0; i < 8; ++i) {
37 if (((control << i) & 0x80) > 0) {
38 if (index < 2) {
39 return false;
40 }
41 index -= 2;
42 std::size_t segment_offset =
43 data[index + start_offset] | data[index + start_offset + 1] << 8;
44 std::size_t segment_size = ((segment_offset >> 12) & 0xF) + 3;
45 segment_offset &= 0xFFF;
46 segment_offset += 3;
47
48 if (out_index < segment_size)
49 segment_size = out_index;
50
51 if (out_index < segment_size) {
52 return false;
53 }
54
55 out_index -= segment_size;
56
57 for (size_t j = 0; j < segment_size; ++j) {
58 if (out_index + j + segment_offset + start_offset >= data.size()) {
59 return false;
60 }
61 data[out_index + j + start_offset] =
62 data[out_index + j + segment_offset + start_offset];
63 }
64 } else {
65 if (out_index < 1) {
66 return false;
67 }
68 --out_index;
69 --index;
70 data[out_index + start_offset] = data[index + start_offset];
71 }
72
73 if (out_index == 0)
74 break;
75 }
76 }
77
78 return true;
79}
80} // Anonymous namespace
81
82KIP::KIP(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
83 if (file == nullptr) {
84 status = Loader::ResultStatus::ErrorNullFile;
85 return;
86 }
87
88 if (file->GetSize() < sizeof(KIPHeader) || file->ReadObject(&header) != sizeof(KIPHeader)) {
89 status = Loader::ResultStatus::ErrorBadKIPHeader;
90 return;
91 }
92
93 if (header.magic != Common::MakeMagic('K', 'I', 'P', '1')) {
94 status = Loader::ResultStatus::ErrorBadKIPHeader;
95 return;
96 }
97
98 u64 offset = sizeof(KIPHeader);
99 for (std::size_t i = 0; i < header.sections.size(); ++i) {
100 auto compressed = file->ReadBytes(header.sections[i].compressed_size, offset);
101 offset += header.sections[i].compressed_size;
102
103 if (header.sections[i].compressed_size == 0 && header.sections[i].decompressed_size != 0) {
104 decompressed_sections[i] = std::vector<u8>(header.sections[i].decompressed_size);
105 } else if (header.sections[i].compressed_size == header.sections[i].decompressed_size) {
106 decompressed_sections[i] = std::move(compressed);
107 } else {
108 decompressed_sections[i] = compressed;
109 if (!DecompressBLZ(decompressed_sections[i])) {
110 status = Loader::ResultStatus::ErrorBLZDecompressionFailed;
111 return;
112 }
113 }
114 }
115}
116
117Loader::ResultStatus KIP::GetStatus() const {
118 return status;
119}
120
121std::string KIP::GetName() const {
122 return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size());
123}
124
125u64 KIP::GetTitleID() const {
126 return header.title_id;
127}
128
129std::vector<u8> KIP::GetSectionDecompressed(u8 index) const {
130 return decompressed_sections[index];
131}
132
133bool KIP::Is64Bit() const {
134 return (header.flags & 0x8) != 0;
135}
136
137bool KIP::Is39BitAddressSpace() const {
138 return (header.flags & 0x10) != 0;
139}
140
141bool KIP::IsService() const {
142 return (header.flags & 0x20) != 0;
143}
144
145std::vector<u32> KIP::GetKernelCapabilities() const {
146 return std::vector<u32>(header.capabilities.begin(), header.capabilities.end());
147}
148
149s32 KIP::GetMainThreadPriority() const {
150 return header.main_thread_priority;
151}
152
153u32 KIP::GetMainThreadStackSize() const {
154 return header.sections[1].attribute;
155}
156
157u32 KIP::GetMainThreadCpuCore() const {
158 return header.default_core;
159}
160
161const std::vector<u8>& KIP::GetTextSection() const {
162 return decompressed_sections[0];
163}
164
165const std::vector<u8>& KIP::GetRODataSection() const {
166 return decompressed_sections[1];
167}
168
169const std::vector<u8>& KIP::GetDataSection() const {
170 return decompressed_sections[2];
171}
172
173u32 KIP::GetTextOffset() const {
174 return header.sections[0].offset;
175}
176
177u32 KIP::GetRODataOffset() const {
178 return header.sections[1].offset;
179}
180
181u32 KIP::GetDataOffset() const {
182 return header.sections[2].offset;
183}
184
185u32 KIP::GetBSSSize() const {
186 return header.sections[3].decompressed_size;
187}
188
189u32 KIP::GetBSSOffset() const {
190 return header.sections[3].offset;
191}
192
193INI::INI(const VirtualFile& file) : status(Loader::ResultStatus::Success) {
194 if (file->GetSize() < sizeof(INIHeader) || file->ReadObject(&header) != sizeof(INIHeader)) {
195 status = Loader::ResultStatus::ErrorBadINIHeader;
196 return;
197 }
198
199 if (header.magic != Common::MakeMagic('I', 'N', 'I', '1')) {
200 status = Loader::ResultStatus::ErrorBadINIHeader;
201 return;
202 }
203
204 if (header.kip_count > INI_MAX_KIPS) {
205 status = Loader::ResultStatus::ErrorINITooManyKIPs;
206 return;
207 }
208
209 u64 offset = sizeof(INIHeader);
210 for (std::size_t i = 0; i < header.kip_count; ++i) {
211 const auto kip_file =
212 std::make_shared<OffsetVfsFile>(file, file->GetSize() - offset, offset);
213 KIP kip(kip_file);
214 if (kip.GetStatus() == Loader::ResultStatus::Success) {
215 kips.push_back(std::move(kip));
216 }
217 }
218}
219
220Loader::ResultStatus INI::GetStatus() const {
221 return status;
222}
223
224const std::vector<KIP>& INI::GetKIPs() const {
225 return kips;
226}
227
228} // namespace FileSys
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
new file mode 100644
index 000000000..324a57384
--- /dev/null
+++ b/src/core/file_sys/kernel_executable.h
@@ -0,0 +1,99 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_funcs.h"
8#include "common/swap.h"
9#include "core/file_sys/vfs_types.h"
10#include "core/loader/loader.h"
11
12namespace FileSys {
13
14struct KIPSectionHeader {
15 u32_le offset;
16 u32_le decompressed_size;
17 u32_le compressed_size;
18 u32_le attribute;
19};
20static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size.");
21
22struct KIPHeader {
23 u32_le magic;
24 std::array<char, 0xC> name;
25 u64_le title_id;
26 u32_le process_category;
27 u8 main_thread_priority;
28 u8 default_core;
29 INSERT_PADDING_BYTES(1);
30 u8 flags;
31 std::array<KIPSectionHeader, 6> sections;
32 std::array<u32, 0x20> capabilities;
33};
34static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
35
36struct INIHeader {
37 u32_le magic;
38 u32_le size;
39 u32_le kip_count;
40 INSERT_PADDING_BYTES(0x4);
41};
42static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
43
44// Kernel Internal Process
45class KIP {
46public:
47 explicit KIP(const VirtualFile& file);
48
49 Loader::ResultStatus GetStatus() const;
50
51 std::string GetName() const;
52 u64 GetTitleID() const;
53 std::vector<u8> GetSectionDecompressed(u8 index) const;
54
55 // Executable Flags
56 bool Is64Bit() const;
57 bool Is39BitAddressSpace() const;
58 bool IsService() const;
59
60 std::vector<u32> GetKernelCapabilities() const;
61
62 s32 GetMainThreadPriority() const;
63 u32 GetMainThreadStackSize() const;
64 u32 GetMainThreadCpuCore() const;
65
66 const std::vector<u8>& GetTextSection() const;
67 const std::vector<u8>& GetRODataSection() const;
68 const std::vector<u8>& GetDataSection() const;
69
70 u32 GetTextOffset() const;
71 u32 GetRODataOffset() const;
72 u32 GetDataOffset() const;
73
74 u32 GetBSSSize() const;
75 u32 GetBSSOffset() const;
76
77private:
78 Loader::ResultStatus status;
79
80 KIPHeader header{};
81 std::array<std::vector<u8>, 6> decompressed_sections;
82};
83
84class INI {
85public:
86 explicit INI(const VirtualFile& file);
87
88 Loader::ResultStatus GetStatus() const;
89
90 const std::vector<KIP>& GetKIPs() const;
91
92private:
93 Loader::ResultStatus status;
94
95 INIHeader header{};
96 std::vector<KIP> kips;
97};
98
99} // namespace FileSys
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 84d5cd1e0..1f82fff0a 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -35,9 +35,9 @@ enum class ContentRecordType : u8 {
35 Program = 1, 35 Program = 1,
36 Data = 2, 36 Data = 2,
37 Control = 3, 37 Control = 3,
38 Manual = 4, 38 HtmlDocument = 4,
39 Legal = 5, 39 LegalInformation = 5,
40 Patch = 6, 40 DeltaFragment = 6,
41}; 41};
42 42
43struct ContentRecord { 43struct ContentRecord {
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index da823c37b..a8f80e2c6 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -493,6 +493,16 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
493 return out; 493 return out;
494} 494}
495 495
496std::optional<u32> PatchManager::GetGameVersion() const {
497 const auto& installed = Core::System::GetInstance().GetContentProvider();
498 const auto update_tid = GetUpdateTitleID(title_id);
499 if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
500 return installed.GetEntryVersion(update_tid);
501 }
502
503 return installed.GetEntryVersion(title_id);
504}
505
496std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 506std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
497 const auto& installed = Core::System::GetInstance().GetContentProvider(); 507 const auto& installed = Core::System::GetInstance().GetContentProvider();
498 508
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index 769f8c6f0..a363c6577 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -66,8 +66,13 @@ public:
66 std::map<std::string, std::string, std::less<>> GetPatchVersionNames( 66 std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
67 VirtualFile update_raw = nullptr) const; 67 VirtualFile update_raw = nullptr) const;
68 68
69 // Given title_id of the program, attempts to get the control data of the update and parse it, 69 // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
70 // falling back to the base control data. 70 // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
71 // std::nullopt
72 std::optional<u32> GetGameVersion() const;
73
74 // Given title_id of the program, attempts to get the control data of the update and parse
75 // it, falling back to the base control data.
71 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; 76 std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
72 77
73 // Version of GetControlMetadata that takes an arbitrary NCA 78 // Version of GetControlMetadata that takes an arbitrary NCA
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index d863253f8..eb76174c5 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -51,6 +51,21 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
51 return Loader::ResultStatus::Success; 51 return Loader::ResultStatus::Success;
52} 52}
53 53
54void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
55 u8 main_thread_prio, u8 main_thread_core,
56 u32 main_thread_stack_size, u64 title_id,
57 u64 filesystem_permissions,
58 KernelCapabilityDescriptors capabilities) {
59 npdm_header.has_64_bit_instructions.Assign(is_64_bit);
60 npdm_header.address_space_type.Assign(address_space);
61 npdm_header.main_thread_priority = main_thread_prio;
62 npdm_header.main_thread_cpu = main_thread_core;
63 npdm_header.main_stack_size = main_thread_stack_size;
64 aci_header.title_id = title_id;
65 aci_file_access.permissions = filesystem_permissions;
66 aci_kernel_capabilities = std ::move(capabilities);
67}
68
54bool ProgramMetadata::Is64BitProgram() const { 69bool ProgramMetadata::Is64BitProgram() const {
55 return npdm_header.has_64_bit_instructions; 70 return npdm_header.has_64_bit_instructions;
56} 71}
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 7de5b9cf9..43bf2820a 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -46,6 +46,11 @@ public:
46 46
47 Loader::ResultStatus Load(VirtualFile file); 47 Loader::ResultStatus Load(VirtualFile file);
48 48
49 // Load from parameters instead of NPDM file, used for KIP
50 void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, u8 main_thread_prio,
51 u8 main_thread_core, u32 main_thread_stack_size, u64 title_id,
52 u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
53
49 bool Is64BitProgram() const; 54 bool Is64BitProgram() const;
50 ProgramAddressSpaceType GetAddressSpaceType() const; 55 ProgramAddressSpaceType GetAddressSpaceType() const;
51 u8 GetMainThreadPriority() const; 56 u8 GetMainThreadPriority() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 58917e094..3725b10f7 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -99,7 +99,7 @@ ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
99 return ContentRecordType::Data; 99 return ContentRecordType::Data;
100 case NCAContentType::Manual: 100 case NCAContentType::Manual:
101 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal. 101 // TODO(DarkLordZach): Peek at NCA contents to differentiate Manual and Legal.
102 return ContentRecordType::Manual; 102 return ContentRecordType::HtmlDocument;
103 default: 103 default:
104 UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type)); 104 UNREACHABLE_MSG("Invalid NCAContentType={:02X}", static_cast<u8>(type));
105 } 105 }
@@ -397,8 +397,8 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
397 }); 397 });
398 398
399 if (meta_iter == ncas.end()) { 399 if (meta_iter == ncas.end()) {
400 LOG_ERROR(Loader, "The XCI you are attempting to install does not have a metadata NCA and " 400 LOG_ERROR(Loader, "The file you are attempting to install does not have a metadata NCA and "
401 "is therefore malformed. Double check your encryption keys."); 401 "is therefore malformed. Check your encryption keys.");
402 return InstallResult::ErrorMetaFailed; 402 return InstallResult::ErrorMetaFailed;
403 } 403 }
404 404
@@ -415,6 +415,9 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
415 const auto cnmt_file = section0->GetFiles()[0]; 415 const auto cnmt_file = section0->GetFiles()[0];
416 const CNMT cnmt(cnmt_file); 416 const CNMT cnmt(cnmt_file);
417 for (const auto& record : cnmt.GetContentRecords()) { 417 for (const auto& record : cnmt.GetContentRecords()) {
418 // Ignore DeltaFragments, they are not useful to us
419 if (record.type == ContentRecordType::DeltaFragment)
420 continue;
418 const auto nca = GetNCAFromNSPForID(nsp, record.nca_id); 421 const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
419 if (nca == nullptr) 422 if (nca == nullptr)
420 return InstallResult::ErrorCopyFailed; 423 return InstallResult::ErrorCopyFailed;
@@ -645,6 +648,20 @@ ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnion
645 return out; 648 return out;
646} 649}
647 650
651std::optional<ContentProviderUnionSlot> ContentProviderUnion::GetSlotForEntry(
652 u64 title_id, ContentRecordType type) const {
653 const auto iter =
654 std::find_if(providers.begin(), providers.end(), [title_id, type](const auto& provider) {
655 return provider.second != nullptr && provider.second->HasEntry(title_id, type);
656 });
657
658 if (iter == providers.end()) {
659 return std::nullopt;
660 }
661
662 return iter->first;
663}
664
648ManualContentProvider::~ManualContentProvider() = default; 665ManualContentProvider::~ManualContentProvider() = default;
649 666
650void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type, 667void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index ec9052653..4398d63e1 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -199,6 +199,9 @@ public:
199 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 199 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
200 std::optional<u64> title_id = {}) const; 200 std::optional<u64> title_id = {}) const;
201 201
202 std::optional<ContentProviderUnionSlot> GetSlotForEntry(u64 title_id,
203 ContentRecordType type) const;
204
202private: 205private:
203 std::map<ContentProviderUnionSlot, ContentProvider*> providers; 206 std::map<ContentProviderUnionSlot, ContentProvider*> providers;
204}; 207};
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index d0428a457..8b3b14e25 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -248,10 +248,13 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
248 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); 248 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
249 249
250 if (next_file == nullptr) { 250 if (next_file == nullptr) {
251 LOG_WARNING(Service_FS, 251 if (rec.type != ContentRecordType::DeltaFragment) {
252 "NCA with ID {}.nca is listed in content metadata, but cannot " 252 LOG_WARNING(Service_FS,
253 "be found in PFS. NSP appears to be corrupted.", 253 "NCA with ID {}.nca is listed in content metadata, but cannot "
254 id_string); 254 "be found in PFS. NSP appears to be corrupted.",
255 id_string);
256 }
257
255 continue; 258 continue;
256 } 259 }
257 260
diff --git a/src/core/frontend/applets/general_frontend.cpp b/src/core/frontend/applets/general_frontend.cpp
index b974f2289..c30b36de7 100644
--- a/src/core/frontend/applets/general_frontend.cpp
+++ b/src/core/frontend/applets/general_frontend.cpp
@@ -7,9 +7,38 @@
7 7
8namespace Core::Frontend { 8namespace Core::Frontend {
9 9
10ParentalControlsApplet::~ParentalControlsApplet() = default;
11
12DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default;
13
14void DefaultParentalControlsApplet::VerifyPIN(std::function<void(bool)> finished,
15 bool suspend_future_verification_temporarily) {
16 LOG_INFO(Service_AM,
17 "Application requested frontend to verify PIN (normal), "
18 "suspend_future_verification_temporarily={}, verifying as correct.",
19 suspend_future_verification_temporarily);
20 finished(true);
21}
22
23void DefaultParentalControlsApplet::VerifyPINForSettings(std::function<void(bool)> finished) {
24 LOG_INFO(Service_AM,
25 "Application requested frontend to verify PIN (settings), verifying as correct.");
26 finished(true);
27}
28
29void DefaultParentalControlsApplet::RegisterPIN(std::function<void()> finished) {
30 LOG_INFO(Service_AM, "Application requested frontend to register new PIN");
31 finished();
32}
33
34void DefaultParentalControlsApplet::ChangePIN(std::function<void()> finished) {
35 LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value");
36 finished();
37}
38
10PhotoViewerApplet::~PhotoViewerApplet() = default; 39PhotoViewerApplet::~PhotoViewerApplet() = default;
11 40
12DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {} 41DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default;
13 42
14void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id, 43void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id,
15 std::function<void()> finished) const { 44 std::function<void()> finished) const {
@@ -24,4 +53,72 @@ void DefaultPhotoViewerApplet::ShowAllPhotos(std::function<void()> finished) con
24 finished(); 53 finished();
25} 54}
26 55
56ECommerceApplet::~ECommerceApplet() = default;
57
58DefaultECommerceApplet::~DefaultECommerceApplet() = default;
59
60void DefaultECommerceApplet::ShowApplicationInformation(
61 std::function<void()> finished, u64 title_id, std::optional<u128> user_id,
62 std::optional<bool> full_display, std::optional<std::string> extra_parameter) {
63 const auto value = user_id.value_or(u128{});
64 LOG_INFO(Service_AM,
65 "Application requested frontend show application information for EShop, "
66 "title_id={:016X}, user_id={:016X}{:016X}, full_display={}, extra_parameter={}",
67 title_id, value[1], value[0],
68 full_display.has_value() ? fmt::format("{}", *full_display) : "null",
69 extra_parameter.value_or("null"));
70 finished();
71}
72
73void DefaultECommerceApplet::ShowAddOnContentList(std::function<void()> finished, u64 title_id,
74 std::optional<u128> user_id,
75 std::optional<bool> full_display) {
76 const auto value = user_id.value_or(u128{});
77 LOG_INFO(Service_AM,
78 "Application requested frontend show add on content list for EShop, "
79 "title_id={:016X}, user_id={:016X}{:016X}, full_display={}",
80 title_id, value[1], value[0],
81 full_display.has_value() ? fmt::format("{}", *full_display) : "null");
82 finished();
83}
84
85void DefaultECommerceApplet::ShowSubscriptionList(std::function<void()> finished, u64 title_id,
86 std::optional<u128> user_id) {
87 const auto value = user_id.value_or(u128{});
88 LOG_INFO(Service_AM,
89 "Application requested frontend show subscription list for EShop, title_id={:016X}, "
90 "user_id={:016X}{:016X}",
91 title_id, value[1], value[0]);
92 finished();
93}
94
95void DefaultECommerceApplet::ShowConsumableItemList(std::function<void()> finished, u64 title_id,
96 std::optional<u128> user_id) {
97 const auto value = user_id.value_or(u128{});
98 LOG_INFO(
99 Service_AM,
100 "Application requested frontend show consumable item list for EShop, title_id={:016X}, "
101 "user_id={:016X}{:016X}",
102 title_id, value[1], value[0]);
103 finished();
104}
105
106void DefaultECommerceApplet::ShowShopHome(std::function<void()> finished, u128 user_id,
107 bool full_display) {
108 LOG_INFO(Service_AM,
109 "Application requested frontend show home menu for EShop, user_id={:016X}{:016X}, "
110 "full_display={}",
111 user_id[1], user_id[0], full_display);
112 finished();
113}
114
115void DefaultECommerceApplet::ShowSettings(std::function<void()> finished, u128 user_id,
116 bool full_display) {
117 LOG_INFO(Service_AM,
118 "Application requested frontend show settings menu for EShop, user_id={:016X}{:016X}, "
119 "full_display={}",
120 user_id[1], user_id[0], full_display);
121 finished();
122}
123
27} // namespace Core::Frontend 124} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/general_frontend.h b/src/core/frontend/applets/general_frontend.h
index d4506c999..4b63f828e 100644
--- a/src/core/frontend/applets/general_frontend.h
+++ b/src/core/frontend/applets/general_frontend.h
@@ -5,10 +5,43 @@
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <functional>
8#include <optional>
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
10namespace Core::Frontend { 11namespace Core::Frontend {
11 12
13class ParentalControlsApplet {
14public:
15 virtual ~ParentalControlsApplet();
16
17 // Prompts the user to enter a PIN and calls the callback with whether or not it matches the
18 // correct PIN. If the bool is passed, and the PIN was recently entered correctly, the frontend
19 // should not prompt and simply return true.
20 virtual void VerifyPIN(std::function<void(bool)> finished,
21 bool suspend_future_verification_temporarily) = 0;
22
23 // Prompts the user to enter a PIN and calls the callback for correctness. Frontends can
24 // optionally alert the user that this is to change parental controls settings.
25 virtual void VerifyPINForSettings(std::function<void(bool)> finished) = 0;
26
27 // Prompts the user to create a new PIN for pctl and stores it with the service.
28 virtual void RegisterPIN(std::function<void()> finished) = 0;
29
30 // Prompts the user to verify the current PIN and then store a new one into pctl.
31 virtual void ChangePIN(std::function<void()> finished) = 0;
32};
33
34class DefaultParentalControlsApplet final : public ParentalControlsApplet {
35public:
36 ~DefaultParentalControlsApplet() override;
37
38 void VerifyPIN(std::function<void(bool)> finished,
39 bool suspend_future_verification_temporarily) override;
40 void VerifyPINForSettings(std::function<void(bool)> finished) override;
41 void RegisterPIN(std::function<void()> finished) override;
42 void ChangePIN(std::function<void()> finished) override;
43};
44
12class PhotoViewerApplet { 45class PhotoViewerApplet {
13public: 46public:
14 virtual ~PhotoViewerApplet(); 47 virtual ~PhotoViewerApplet();
@@ -25,4 +58,55 @@ public:
25 void ShowAllPhotos(std::function<void()> finished) const override; 58 void ShowAllPhotos(std::function<void()> finished) const override;
26}; 59};
27 60
61class ECommerceApplet {
62public:
63 virtual ~ECommerceApplet();
64
65 // Shows a page with application icons, description, name, and price.
66 virtual void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
67 std::optional<u128> user_id = {},
68 std::optional<bool> full_display = {},
69 std::optional<std::string> extra_parameter = {}) = 0;
70
71 // Shows a page with all of the add on content available for a game, with name, description, and
72 // price.
73 virtual void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
74 std::optional<u128> user_id = {},
75 std::optional<bool> full_display = {}) = 0;
76
77 // Shows a page with all of the subscriptions (recurring payments) for a game, with name,
78 // description, price, and renewal period.
79 virtual void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
80 std::optional<u128> user_id = {}) = 0;
81
82 // Shows a page with a list of any additional game related purchasable items (DLC,
83 // subscriptions, etc) for a particular game, with name, description, type, and price.
84 virtual void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
85 std::optional<u128> user_id = {}) = 0;
86
87 // Shows the home page of the shop.
88 virtual void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) = 0;
89
90 // Shows the user settings page of the shop.
91 virtual void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) = 0;
92};
93
94class DefaultECommerceApplet : public ECommerceApplet {
95public:
96 ~DefaultECommerceApplet() override;
97
98 void ShowApplicationInformation(std::function<void()> finished, u64 title_id,
99 std::optional<u128> user_id, std::optional<bool> full_display,
100 std::optional<std::string> extra_parameter) override;
101 void ShowAddOnContentList(std::function<void()> finished, u64 title_id,
102 std::optional<u128> user_id,
103 std::optional<bool> full_display) override;
104 void ShowSubscriptionList(std::function<void()> finished, u64 title_id,
105 std::optional<u128> user_id) override;
106 void ShowConsumableItemList(std::function<void()> finished, u64 title_id,
107 std::optional<u128> user_id) override;
108 void ShowShopHome(std::function<void()> finished, u128 user_id, bool full_display) override;
109 void ShowSettings(std::function<void()> finished, u128 user_id, bool full_display) override;
110};
111
28} // namespace Core::Frontend 112} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/web_browser.cpp b/src/core/frontend/applets/web_browser.cpp
index 3a3d3d0bf..528295ffc 100644
--- a/src/core/frontend/applets/web_browser.cpp
+++ b/src/core/frontend/applets/web_browser.cpp
@@ -11,9 +11,9 @@ WebBrowserApplet::~WebBrowserApplet() = default;
11 11
12DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; 12DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default;
13 13
14void DefaultWebBrowserApplet::OpenPage(std::string_view filename, 14void DefaultWebBrowserApplet::OpenPageLocal(std::string_view filename,
15 std::function<void()> unpack_romfs_callback, 15 std::function<void()> unpack_romfs_callback,
16 std::function<void()> finished_callback) { 16 std::function<void()> finished_callback) {
17 LOG_INFO(Service_AM, 17 LOG_INFO(Service_AM,
18 "(STUBBED) called - No suitable web browser implementation found to open website page " 18 "(STUBBED) called - No suitable web browser implementation found to open website page "
19 "at '{}'!", 19 "at '{}'!",
diff --git a/src/core/frontend/applets/web_browser.h b/src/core/frontend/applets/web_browser.h
index f952856af..110e33bc4 100644
--- a/src/core/frontend/applets/web_browser.h
+++ b/src/core/frontend/applets/web_browser.h
@@ -13,16 +13,16 @@ class WebBrowserApplet {
13public: 13public:
14 virtual ~WebBrowserApplet(); 14 virtual ~WebBrowserApplet();
15 15
16 virtual void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, 16 virtual void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
17 std::function<void()> finished_callback) = 0; 17 std::function<void()> finished_callback) = 0;
18}; 18};
19 19
20class DefaultWebBrowserApplet final : public WebBrowserApplet { 20class DefaultWebBrowserApplet final : public WebBrowserApplet {
21public: 21public:
22 ~DefaultWebBrowserApplet() override; 22 ~DefaultWebBrowserApplet() override;
23 23
24 void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, 24 void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
25 std::function<void()> finished_callback) override; 25 std::function<void()> finished_callback) override;
26}; 26};
27 27
28} // namespace Core::Frontend 28} // namespace Core::Frontend
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 757e5f21f..799e5e0d8 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -99,7 +99,8 @@ struct KernelCore::Impl {
99 99
100 void Shutdown() { 100 void Shutdown() {
101 next_object_id = 0; 101 next_object_id = 0;
102 next_process_id = Process::ProcessIDMin; 102 next_kernel_process_id = Process::InitialKIPIDMin;
103 next_user_process_id = Process::ProcessIDMin;
103 next_thread_id = 1; 104 next_thread_id = 1;
104 105
105 process_list.clear(); 106 process_list.clear();
@@ -132,7 +133,8 @@ struct KernelCore::Impl {
132 } 133 }
133 134
134 std::atomic<u32> next_object_id{0}; 135 std::atomic<u32> next_object_id{0};
135 std::atomic<u64> next_process_id{Process::ProcessIDMin}; 136 std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin};
137 std::atomic<u64> next_user_process_id{Process::ProcessIDMin};
136 std::atomic<u64> next_thread_id{1}; 138 std::atomic<u64> next_thread_id{1};
137 139
138 // Lists all processes that exist in the current session. 140 // Lists all processes that exist in the current session.
@@ -226,8 +228,12 @@ u64 KernelCore::CreateNewThreadID() {
226 return impl->next_thread_id++; 228 return impl->next_thread_id++;
227} 229}
228 230
229u64 KernelCore::CreateNewProcessID() { 231u64 KernelCore::CreateNewKernelProcessID() {
230 return impl->next_process_id++; 232 return impl->next_kernel_process_id++;
233}
234
235u64 KernelCore::CreateNewUserProcessID() {
236 return impl->next_user_process_id++;
231} 237}
232 238
233Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const { 239Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 6b8738599..0cc44ee76 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -96,7 +96,10 @@ private:
96 u32 CreateNewObjectID(); 96 u32 CreateNewObjectID();
97 97
98 /// Creates a new process ID, incrementing the internal process ID counter; 98 /// Creates a new process ID, incrementing the internal process ID counter;
99 u64 CreateNewProcessID(); 99 u64 CreateNewKernelProcessID();
100
101 /// Creates a new process ID, incrementing the internal process ID counter;
102 u64 CreateNewUserProcessID();
100 103
101 /// Creates a new thread ID, incrementing the internal thread ID counter. 104 /// Creates a new thread ID, incrementing the internal thread ID counter.
102 u64 CreateNewThreadID(); 105 u64 CreateNewThreadID();
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 2b81a8d4f..f45ef05f6 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <bitset>
6#include <memory> 7#include <memory>
7#include <random> 8#include <random>
8#include "common/alignment.h" 9#include "common/alignment.h"
@@ -48,7 +49,58 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
48} 49}
49} // Anonymous namespace 50} // Anonymous namespace
50 51
51SharedPtr<Process> Process::Create(Core::System& system, std::string name) { 52// Represents a page used for thread-local storage.
53//
54// Each TLS page contains slots that may be used by processes and threads.
55// Every process and thread is created with a slot in some arbitrary page
56// (whichever page happens to have an available slot).
57class TLSPage {
58public:
59 static constexpr std::size_t num_slot_entries = Memory::PAGE_SIZE / Memory::TLS_ENTRY_SIZE;
60
61 explicit TLSPage(VAddr address) : base_address{address} {}
62
63 bool HasAvailableSlots() const {
64 return !is_slot_used.all();
65 }
66
67 VAddr GetBaseAddress() const {
68 return base_address;
69 }
70
71 std::optional<VAddr> ReserveSlot() {
72 for (std::size_t i = 0; i < is_slot_used.size(); i++) {
73 if (is_slot_used[i]) {
74 continue;
75 }
76
77 is_slot_used[i] = true;
78 return base_address + (i * Memory::TLS_ENTRY_SIZE);
79 }
80
81 return std::nullopt;
82 }
83
84 void ReleaseSlot(VAddr address) {
85 // Ensure that all given addresses are consistent with how TLS pages
86 // are intended to be used when releasing slots.
87 ASSERT(IsWithinPage(address));
88 ASSERT((address % Memory::TLS_ENTRY_SIZE) == 0);
89
90 const std::size_t index = (address - base_address) / Memory::TLS_ENTRY_SIZE;
91 is_slot_used[index] = false;
92 }
93
94private:
95 bool IsWithinPage(VAddr address) const {
96 return base_address <= address && address < base_address + Memory::PAGE_SIZE;
97 }
98
99 VAddr base_address;
100 std::bitset<num_slot_entries> is_slot_used;
101};
102
103SharedPtr<Process> Process::Create(Core::System& system, std::string name, ProcessType type) {
52 auto& kernel = system.Kernel(); 104 auto& kernel = system.Kernel();
53 105
54 SharedPtr<Process> process(new Process(system)); 106 SharedPtr<Process> process(new Process(system));
@@ -56,7 +108,8 @@ SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
56 process->resource_limit = kernel.GetSystemResourceLimit(); 108 process->resource_limit = kernel.GetSystemResourceLimit();
57 process->status = ProcessStatus::Created; 109 process->status = ProcessStatus::Created;
58 process->program_id = 0; 110 process->program_id = 0;
59 process->process_id = kernel.CreateNewProcessID(); 111 process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
112 : kernel.CreateNewUserProcessID();
60 process->capabilities.InitializeForMetadatalessProcess(); 113 process->capabilities.InitializeForMetadatalessProcess();
61 114
62 std::mt19937 rng(Settings::values.rng_seed.value_or(0)); 115 std::mt19937 rng(Settings::values.rng_seed.value_or(0));
@@ -179,61 +232,55 @@ void Process::PrepareForTermination() {
179} 232}
180 233
181/** 234/**
182 * Finds a free location for the TLS section of a thread. 235 * Attempts to find a TLS page that contains a free slot for
183 * @param tls_slots The TLS page array of the thread's owner process. 236 * use by a thread.
184 * Returns a tuple of (page, slot, alloc_needed) where: 237 *
185 * page: The index of the first allocated TLS page that has free slots. 238 * @returns If a page with an available slot is found, then an iterator
186 * slot: The index of the first free slot in the indicated page. 239 * pointing to the page is returned. Otherwise the end iterator
187 * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). 240 * is returned instead.
188 */ 241 */
189static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( 242static auto FindTLSPageWithAvailableSlots(std::vector<TLSPage>& tls_pages) {
190 const std::vector<std::bitset<8>>& tls_slots) { 243 return std::find_if(tls_pages.begin(), tls_pages.end(),
191 // Iterate over all the allocated pages, and try to find one where not all slots are used. 244 [](const auto& page) { return page.HasAvailableSlots(); });
192 for (std::size_t page = 0; page < tls_slots.size(); ++page) {
193 const auto& page_tls_slots = tls_slots[page];
194 if (!page_tls_slots.all()) {
195 // We found a page with at least one free slot, find which slot it is
196 for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
197 if (!page_tls_slots.test(slot)) {
198 return std::make_tuple(page, slot, false);
199 }
200 }
201 }
202 }
203
204 return std::make_tuple(0, 0, true);
205} 245}
206 246
207VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { 247VAddr Process::CreateTLSRegion() {
208 auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots); 248 auto tls_page_iter = FindTLSPageWithAvailableSlots(tls_pages);
209 const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress();
210 249
211 if (needs_allocation) { 250 if (tls_page_iter == tls_pages.cend()) {
212 tls_slots.emplace_back(0); // The page is completely available at the start 251 const auto region_address =
213 available_page = tls_slots.size() - 1; 252 vm_manager.FindFreeRegion(vm_manager.GetTLSIORegionBaseAddress(),
214 available_slot = 0; // Use the first slot in the new page 253 vm_manager.GetTLSIORegionEndAddress(), Memory::PAGE_SIZE);
254 ASSERT(region_address.Succeeded());
215 255
216 // Allocate some memory from the end of the linear heap for this region. 256 const auto map_result = vm_manager.MapMemoryBlock(
217 auto& tls_memory = thread.GetTLSMemory(); 257 *region_address, std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE), 0,
218 tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0); 258 Memory::PAGE_SIZE, MemoryState::ThreadLocal);
259 ASSERT(map_result.Succeeded());
219 260
220 vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); 261 tls_pages.emplace_back(*region_address);
221 262
222 vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0, 263 const auto reserve_result = tls_pages.back().ReserveSlot();
223 Memory::PAGE_SIZE, MemoryState::ThreadLocal); 264 ASSERT(reserve_result.has_value());
224 }
225 265
226 tls_slots[available_page].set(available_slot); 266 return *reserve_result;
267 }
227 268
228 return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; 269 return *tls_page_iter->ReserveSlot();
229} 270}
230 271
231void Process::FreeTLSSlot(VAddr tls_address) { 272void Process::FreeTLSRegion(VAddr tls_address) {
232 const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress(); 273 const VAddr aligned_address = Common::AlignDown(tls_address, Memory::PAGE_SIZE);
233 const VAddr tls_page = tls_base / Memory::PAGE_SIZE; 274 auto iter =
234 const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; 275 std::find_if(tls_pages.begin(), tls_pages.end(), [aligned_address](const auto& page) {
276 return page.GetBaseAddress() == aligned_address;
277 });
278
279 // Something has gone very wrong if we're freeing a region
280 // with no actual page available.
281 ASSERT(iter != tls_pages.cend());
235 282
236 tls_slots[tls_page].reset(tls_slot); 283 iter->ReleaseSlot(tls_address);
237} 284}
238 285
239void Process::LoadModule(CodeSet module_, VAddr base_addr) { 286void Process::LoadModule(CodeSet module_, VAddr base_addr) {
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 29e016983..83ea02bee 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <bitset>
9#include <cstddef> 8#include <cstddef>
10#include <list> 9#include <list>
11#include <string> 10#include <string>
@@ -32,6 +31,7 @@ namespace Kernel {
32class KernelCore; 31class KernelCore;
33class ResourceLimit; 32class ResourceLimit;
34class Thread; 33class Thread;
34class TLSPage;
35 35
36struct CodeSet; 36struct CodeSet;
37 37
@@ -73,9 +73,15 @@ public:
73 ProcessIDMax = 0xFFFFFFFFFFFFFFFF, 73 ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
74 }; 74 };
75 75
76 // Used to determine how process IDs are assigned.
77 enum class ProcessType {
78 KernelInternal,
79 Userland,
80 };
81
76 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4; 82 static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
77 83
78 static SharedPtr<Process> Create(Core::System& system, std::string name); 84 static SharedPtr<Process> Create(Core::System& system, std::string name, ProcessType type);
79 85
80 std::string GetTypeName() const override { 86 std::string GetTypeName() const override {
81 return "Process"; 87 return "Process";
@@ -254,10 +260,10 @@ public:
254 // Thread-local storage management 260 // Thread-local storage management
255 261
256 // Marks the next available region as used and returns the address of the slot. 262 // Marks the next available region as used and returns the address of the slot.
257 VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread); 263 [[nodiscard]] VAddr CreateTLSRegion();
258 264
259 // Frees a used TLS slot identified by the given address 265 // Frees a used TLS slot identified by the given address
260 void FreeTLSSlot(VAddr tls_address); 266 void FreeTLSRegion(VAddr tls_address);
261 267
262private: 268private:
263 explicit Process(Core::System& system); 269 explicit Process(Core::System& system);
@@ -284,7 +290,7 @@ private:
284 u64 code_memory_size = 0; 290 u64 code_memory_size = 0;
285 291
286 /// Current status of the process 292 /// Current status of the process
287 ProcessStatus status; 293 ProcessStatus status{};
288 294
289 /// The ID of this process 295 /// The ID of this process
290 u64 process_id = 0; 296 u64 process_id = 0;
@@ -304,7 +310,7 @@ private:
304 /// holds the TLS for a specific thread. This vector contains which parts are in use for each 310 /// holds the TLS for a specific thread. This vector contains which parts are in use for each
305 /// page as a bitmask. 311 /// page as a bitmask.
306 /// This vector will grow as more pages are allocated for new threads. 312 /// This vector will grow as more pages are allocated for new threads.
307 std::vector<std::bitset<8>> tls_slots; 313 std::vector<TLSPage> tls_pages;
308 314
309 /// Contains the parsed process capability descriptors. 315 /// Contains the parsed process capability descriptors.
310 ProcessCapabilities capabilities; 316 ProcessCapabilities capabilities;
@@ -333,7 +339,7 @@ private:
333 Mutex mutex; 339 Mutex mutex;
334 340
335 /// Random values for svcGetInfo RandomEntropy 341 /// Random values for svcGetInfo RandomEntropy
336 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy; 342 std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
337 343
338 /// List of threads that are running with this process as their owner. 344 /// List of threads that are running with this process as their owner.
339 std::list<const Thread*> thread_list; 345 std::list<const Thread*> thread_list;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index f9c606bc5..de6363ff2 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -38,6 +38,7 @@
38#include "core/hle/result.h" 38#include "core/hle/result.h"
39#include "core/hle/service/service.h" 39#include "core/hle/service/service.h"
40#include "core/memory.h" 40#include "core/memory.h"
41#include "core/reporter.h"
41 42
42namespace Kernel { 43namespace Kernel {
43namespace { 44namespace {
@@ -594,6 +595,7 @@ struct BreakReason {
594static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { 595static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
595 BreakReason break_reason{reason}; 596 BreakReason break_reason{reason};
596 bool has_dumped_buffer{}; 597 bool has_dumped_buffer{};
598 std::vector<u8> debug_buffer;
597 599
598 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) { 600 const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
599 if (sz == 0 || addr == 0 || has_dumped_buffer) { 601 if (sz == 0 || addr == 0 || has_dumped_buffer) {
@@ -605,7 +607,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
605 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr)); 607 LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
606 } else { 608 } else {
607 // We don't know what's in here so we'll hexdump it 609 // We don't know what's in here so we'll hexdump it
608 std::vector<u8> debug_buffer(sz); 610 debug_buffer.resize(sz);
609 Memory::ReadBlock(addr, debug_buffer.data(), sz); 611 Memory::ReadBlock(addr, debug_buffer.data(), sz);
610 std::string hexdump; 612 std::string hexdump;
611 for (std::size_t i = 0; i < debug_buffer.size(); i++) { 613 for (std::size_t i = 0; i < debug_buffer.size(); i++) {
@@ -664,6 +666,10 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
664 break; 666 break;
665 } 667 }
666 668
669 system.GetReporter().SaveSvcBreakReport(
670 static_cast<u32>(break_reason.break_type.Value()), break_reason.signal_debugger, info1,
671 info2, has_dumped_buffer ? std::make_optional(debug_buffer) : std::nullopt);
672
667 if (!break_reason.signal_debugger) { 673 if (!break_reason.signal_debugger) {
668 LOG_CRITICAL( 674 LOG_CRITICAL(
669 Debug_Emulated, 675 Debug_Emulated,
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c73a40977..ec529e7f2 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -65,7 +65,7 @@ void Thread::Stop() {
65 owner_process->UnregisterThread(this); 65 owner_process->UnregisterThread(this);
66 66
67 // Mark the TLS slot in the thread's page as free. 67 // Mark the TLS slot in the thread's page as free.
68 owner_process->FreeTLSSlot(tls_address); 68 owner_process->FreeTLSRegion(tls_address);
69} 69}
70 70
71void Thread::WakeAfterDelay(s64 nanoseconds) { 71void Thread::WakeAfterDelay(s64 nanoseconds) {
@@ -76,13 +76,13 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
76 // This function might be called from any thread so we have to be cautious and use the 76 // This function might be called from any thread so we have to be cautious and use the
77 // thread-safe version of ScheduleEvent. 77 // thread-safe version of ScheduleEvent.
78 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); 78 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
79 Core::System::GetInstance().CoreTiming().ScheduleEventThreadsafe( 79 Core::System::GetInstance().CoreTiming().ScheduleEvent(
80 cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle); 80 cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle);
81} 81}
82 82
83void Thread::CancelWakeupTimer() { 83void Thread::CancelWakeupTimer() {
84 Core::System::GetInstance().CoreTiming().UnscheduleEventThreadsafe( 84 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
85 kernel.ThreadWakeupCallbackEventType(), callback_handle); 85 callback_handle);
86} 86}
87 87
88static std::optional<s32> GetNextProcessorId(u64 mask) { 88static std::optional<s32> GetNextProcessorId(u64 mask) {
@@ -205,9 +205,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
205 thread->name = std::move(name); 205 thread->name = std::move(name);
206 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); 206 thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
207 thread->owner_process = &owner_process; 207 thread->owner_process = &owner_process;
208 thread->tls_address = thread->owner_process->CreateTLSRegion();
208 thread->scheduler = &system.Scheduler(processor_id); 209 thread->scheduler = &system.Scheduler(processor_id);
209 thread->scheduler->AddThread(thread); 210 thread->scheduler->AddThread(thread);
210 thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
211 211
212 thread->owner_process->RegisterThread(thread.get()); 212 thread->owner_process->RegisterThread(thread.get());
213 213
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index b4b9cda7c..07e989637 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <functional>
8#include <memory>
9#include <string> 8#include <string>
10#include <vector> 9#include <vector>
11 10
@@ -78,9 +77,6 @@ enum class ThreadActivity : u32 {
78 77
79class Thread final : public WaitObject { 78class Thread final : public WaitObject {
80public: 79public:
81 using TLSMemory = std::vector<u8>;
82 using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
83
84 using MutexWaitingThreads = std::vector<SharedPtr<Thread>>; 80 using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
85 81
86 using ThreadContext = Core::ARM_Interface::ThreadContext; 82 using ThreadContext = Core::ARM_Interface::ThreadContext;
@@ -169,14 +165,6 @@ public:
169 return thread_id; 165 return thread_id;
170 } 166 }
171 167
172 TLSMemoryPtr& GetTLSMemory() {
173 return tls_memory;
174 }
175
176 const TLSMemoryPtr& GetTLSMemory() const {
177 return tls_memory;
178 }
179
180 /// Resumes a thread from waiting 168 /// Resumes a thread from waiting
181 void ResumeFromWait(); 169 void ResumeFromWait();
182 170
@@ -463,11 +451,9 @@ private:
463 u32 ideal_core{0xFFFFFFFF}; 451 u32 ideal_core{0xFFFFFFFF};
464 u64 affinity_mask{0x1}; 452 u64 affinity_mask{0x1};
465 453
466 TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); 454 ThreadActivity activity = ThreadActivity::Normal;
467 455
468 std::string name; 456 std::string name;
469
470 ThreadActivity activity = ThreadActivity::Normal;
471}; 457};
472 458
473/** 459/**
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index c929c2a52..3df5ccb7f 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -152,22 +152,33 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
152} 152}
153 153
154ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { 154ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
155 // Find the first Free VMA. 155 return FindFreeRegion(GetASLRRegionBaseAddress(), GetASLRRegionEndAddress(), size);
156 const VAddr base = GetASLRRegionBaseAddress(); 156}
157 const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
158 if (vma.second.type != VMAType::Free)
159 return false;
160 157
161 const VAddr vma_end = vma.second.base + vma.second.size; 158ResultVal<VAddr> VMManager::FindFreeRegion(VAddr begin, VAddr end, u64 size) const {
162 return vma_end > base && vma_end >= base + size; 159 ASSERT(begin < end);
163 }); 160 ASSERT(size <= end - begin);
164 161
165 if (vma_handle == vma_map.end()) { 162 const VMAHandle vma_handle =
163 std::find_if(vma_map.begin(), vma_map.end(), [begin, end, size](const auto& vma) {
164 if (vma.second.type != VMAType::Free) {
165 return false;
166 }
167 const VAddr vma_base = vma.second.base;
168 const VAddr vma_end = vma_base + vma.second.size;
169 const VAddr assumed_base = (begin < vma_base) ? vma_base : begin;
170 const VAddr used_range = assumed_base + size;
171
172 return vma_base <= assumed_base && assumed_base < used_range && used_range < end &&
173 used_range <= vma_end;
174 });
175
176 if (vma_handle == vma_map.cend()) {
166 // TODO(Subv): Find the correct error code here. 177 // TODO(Subv): Find the correct error code here.
167 return ResultCode(-1); 178 return ResultCode(-1);
168 } 179 }
169 180
170 const VAddr target = std::max(base, vma_handle->second.base); 181 const VAddr target = std::max(begin, vma_handle->second.base);
171 return MakeResult<VAddr>(target); 182 return MakeResult<VAddr>(target);
172} 183}
173 184
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index dfbf7a894..752ae62f9 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -362,14 +362,39 @@ public:
362 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); 362 ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
363 363
364 /** 364 /**
365 * Finds the first free address that can hold a region of the desired size. 365 * Finds the first free memory region of the given size within
366 * the user-addressable ASLR memory region.
366 * 367 *
367 * @param size Size of the desired region. 368 * @param size The size of the desired region in bytes.
368 * @return The found free address. 369 *
370 * @returns If successful, the base address of the free region with
371 * the given size.
369 */ 372 */
370 ResultVal<VAddr> FindFreeRegion(u64 size) const; 373 ResultVal<VAddr> FindFreeRegion(u64 size) const;
371 374
372 /** 375 /**
376 * Finds the first free address range that can hold a region of the desired size
377 *
378 * @param begin The starting address of the range.
379 * This is treated as an inclusive beginning address.
380 *
381 * @param end The ending address of the range.
382 * This is treated as an exclusive ending address.
383 *
384 * @param size The size of the free region to attempt to locate,
385 * in bytes.
386 *
387 * @returns If successful, the base address of the free region with
388 * the given size.
389 *
390 * @returns If unsuccessful, a result containing an error code.
391 *
392 * @pre The starting address must be less than the ending address.
393 * @pre The size must not exceed the address range itself.
394 */
395 ResultVal<VAddr> FindFreeRegion(VAddr begin, VAddr end, u64 size) const;
396
397 /**
373 * Maps a memory-mapped IO region at a given address. 398 * Maps a memory-mapped IO region at a given address.
374 * 399 *
375 * @param target The guest address to start the mapping at. 400 * @param target The guest address to start the mapping at.
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 025714e5a..c01ee3eda 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -15,13 +15,18 @@
15#include "core/file_sys/control_metadata.h" 15#include "core/file_sys/control_metadata.h"
16#include "core/file_sys/patch_manager.h" 16#include "core/file_sys/patch_manager.h"
17#include "core/hle/ipc_helpers.h" 17#include "core/hle/ipc_helpers.h"
18#include "core/hle/kernel/kernel.h"
18#include "core/hle/kernel/process.h" 19#include "core/hle/kernel/process.h"
19#include "core/hle/service/acc/acc.h" 20#include "core/hle/service/acc/acc.h"
20#include "core/hle/service/acc/acc_aa.h" 21#include "core/hle/service/acc/acc_aa.h"
21#include "core/hle/service/acc/acc_su.h" 22#include "core/hle/service/acc/acc_su.h"
22#include "core/hle/service/acc/acc_u0.h" 23#include "core/hle/service/acc/acc_u0.h"
23#include "core/hle/service/acc/acc_u1.h" 24#include "core/hle/service/acc/acc_u1.h"
25#include "core/hle/service/acc/errors.h"
24#include "core/hle/service/acc/profile_manager.h" 26#include "core/hle/service/acc/profile_manager.h"
27#include "core/hle/service/glue/arp.h"
28#include "core/hle/service/glue/manager.h"
29#include "core/hle/service/sm/sm.h"
25#include "core/loader/loader.h" 30#include "core/loader/loader.h"
26 31
27namespace Service::Account { 32namespace Service::Account {
@@ -94,7 +99,7 @@ private:
94 LOG_WARNING(Service_ACC, 99 LOG_WARNING(Service_ACC,
95 "Failed to load user provided image! Falling back to built-in backup..."); 100 "Failed to load user provided image! Falling back to built-in backup...");
96 ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG); 101 ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
97 rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()); 102 rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
98 return; 103 return;
99 } 104 }
100 105
@@ -116,9 +121,9 @@ private:
116 if (!image.IsOpen()) { 121 if (!image.IsOpen()) {
117 LOG_WARNING(Service_ACC, 122 LOG_WARNING(Service_ACC,
118 "Failed to load user provided image! Falling back to built-in backup..."); 123 "Failed to load user provided image! Falling back to built-in backup...");
119 rb.Push<u32>(Core::Constants::ACCOUNT_BACKUP_JPEG.size()); 124 rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
120 } else { 125 } else {
121 rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); 126 rb.Push(SanitizeJPEGSize(image.GetSize()));
122 } 127 }
123 } 128 }
124 129
@@ -217,10 +222,72 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon
217 rb.Push(profile_manager->CanSystemRegisterUser()); 222 rb.Push(profile_manager->CanSystemRegisterUser());
218} 223}
219 224
220void Module::Interface::InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx) { 225void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
221 LOG_WARNING(Service_ACC, "(STUBBED) called"); 226 IPC::RequestParser rp{ctx};
227 auto pid = rp.Pop<u64>();
228
229 LOG_DEBUG(Service_ACC, "called, process_id={}", pid);
222 IPC::ResponseBuilder rb{ctx, 2}; 230 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(RESULT_SUCCESS); 231 rb.Push(InitializeApplicationInfoBase(pid));
232}
233
234void Module::Interface::InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx) {
235 IPC::RequestParser rp{ctx};
236 auto pid = rp.Pop<u64>();
237
238 LOG_WARNING(Service_ACC, "(Partial implementation) called, process_id={}", pid);
239
240 // TODO(ogniK): We require checking if the user actually owns the title and what not. As of
241 // currently, we assume the user owns the title. InitializeApplicationInfoBase SHOULD be called
242 // first then we do extra checks if the game is a digital copy.
243
244 IPC::ResponseBuilder rb{ctx, 2};
245 rb.Push(InitializeApplicationInfoBase(pid));
246}
247
248ResultCode Module::Interface::InitializeApplicationInfoBase(u64 process_id) {
249 if (application_info) {
250 LOG_ERROR(Service_ACC, "Application already initialized");
251 return ERR_ACCOUNTINFO_ALREADY_INITIALIZED;
252 }
253
254 const auto& list = system.Kernel().GetProcessList();
255 const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
256 return process->GetProcessID() == process_id;
257 });
258
259 if (iter == list.end()) {
260 LOG_ERROR(Service_ACC, "Failed to find process ID");
261 application_info.application_type = ApplicationType::Unknown;
262
263 return ERR_ACCOUNTINFO_BAD_APPLICATION;
264 }
265
266 const auto launch_property = system.GetARPManager().GetLaunchProperty((*iter)->GetTitleID());
267
268 if (launch_property.Failed()) {
269 LOG_ERROR(Service_ACC, "Failed to get launch property");
270 return ERR_ACCOUNTINFO_BAD_APPLICATION;
271 }
272
273 switch (launch_property->base_game_storage_id) {
274 case FileSys::StorageId::GameCard:
275 application_info.application_type = ApplicationType::GameCard;
276 break;
277 case FileSys::StorageId::Host:
278 case FileSys::StorageId::NandUser:
279 case FileSys::StorageId::SdCard:
280 application_info.application_type = ApplicationType::Digital;
281 break;
282 default:
283 LOG_ERROR(Service_ACC, "Invalid game storage ID");
284 return ERR_ACCOUNTINFO_BAD_APPLICATION;
285 }
286
287 LOG_WARNING(Service_ACC, "ApplicationInfo init required");
288 // TODO(ogniK): Actual initalization here
289
290 return RESULT_SUCCESS;
224} 291}
225 292
226void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) { 293void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index 350f123a0..f651773b7 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include "core/hle/service/glue/manager.h"
7#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
8 9
9namespace Service::Account { 10namespace Service::Account {
@@ -25,12 +26,33 @@ public:
25 void ListOpenUsers(Kernel::HLERequestContext& ctx); 26 void ListOpenUsers(Kernel::HLERequestContext& ctx);
26 void GetLastOpenedUser(Kernel::HLERequestContext& ctx); 27 void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
27 void GetProfile(Kernel::HLERequestContext& ctx); 28 void GetProfile(Kernel::HLERequestContext& ctx);
28 void InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx); 29 void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
30 void InitializeApplicationInfoRestricted(Kernel::HLERequestContext& ctx);
29 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx); 31 void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
30 void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx); 32 void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
31 void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx); 33 void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
32 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); 34 void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
33 35
36 private:
37 ResultCode InitializeApplicationInfoBase(u64 process_id);
38
39 enum class ApplicationType : u32_le {
40 GameCard = 0,
41 Digital = 1,
42 Unknown = 3,
43 };
44
45 struct ApplicationInfo {
46 Service::Glue::ApplicationLaunchProperty launch_property;
47 ApplicationType application_type;
48
49 constexpr explicit operator bool() const {
50 return launch_property.title_id != 0x0;
51 }
52 };
53
54 ApplicationInfo application_info{};
55
34 protected: 56 protected:
35 std::shared_ptr<Module> module; 57 std::shared_ptr<Module> module;
36 std::shared_ptr<ProfileManager> profile_manager; 58 std::shared_ptr<ProfileManager> profile_manager;
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 2f239e8c0..0ac19f4ff 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -22,7 +22,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
22 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"}, 22 {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
23 {60, nullptr, "ListOpenContextStoredUsers"}, 23 {60, nullptr, "ListOpenContextStoredUsers"},
24 {99, nullptr, "DebugActivateOpenContextRetention"}, 24 {99, nullptr, "DebugActivateOpenContextRetention"},
25 {100, &ACC_U0::InitializeApplicationInfoOld, "InitializeApplicationInfoOld"}, 25 {100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
26 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"}, 26 {101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
27 {102, nullptr, "AuthenticateApplicationAsync"}, 27 {102, nullptr, "AuthenticateApplicationAsync"},
28 {103, nullptr, "CheckNetworkServiceAvailabilityAsync"}, 28 {103, nullptr, "CheckNetworkServiceAvailabilityAsync"},
@@ -31,7 +31,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
31 {120, nullptr, "CreateGuestLoginRequest"}, 31 {120, nullptr, "CreateGuestLoginRequest"},
32 {130, nullptr, "LoadOpenContext"}, 32 {130, nullptr, "LoadOpenContext"},
33 {131, nullptr, "ListOpenContextStoredUsers"}, 33 {131, nullptr, "ListOpenContextStoredUsers"},
34 {140, nullptr, "InitializeApplicationInfo"}, 34 {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"},
35 {141, nullptr, "ListQualifiedUsers"}, 35 {141, nullptr, "ListQualifiedUsers"},
36 {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"}, 36 {150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
37 }; 37 };
diff --git a/src/core/hle/service/acc/errors.h b/src/core/hle/service/acc/errors.h
new file mode 100644
index 000000000..1f0577239
--- /dev/null
+++ b/src/core/hle/service/acc/errors.h
@@ -0,0 +1,14 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Service::Account {
10
11constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22};
12constexpr ResultCode ERR_ACCOUNTINFO_ALREADY_INITIALIZED{ErrorModule::Account, 41};
13
14} // namespace Service::Account
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 4a7bf4acb..33cebb48b 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -887,7 +887,9 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
887 rb.Push(RESULT_SUCCESS); 887 rb.Push(RESULT_SUCCESS);
888} 888}
889 889
890ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { 890ILibraryAppletCreator::ILibraryAppletCreator(u64 current_process_title_id)
891 : ServiceFramework("ILibraryAppletCreator"),
892 current_process_title_id(current_process_title_id) {
891 static const FunctionInfo functions[] = { 893 static const FunctionInfo functions[] = {
892 {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"}, 894 {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
893 {1, nullptr, "TerminateAllLibraryApplets"}, 895 {1, nullptr, "TerminateAllLibraryApplets"},
@@ -910,7 +912,7 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
910 static_cast<u32>(applet_id), applet_mode); 912 static_cast<u32>(applet_id), applet_mode);
911 913
912 const auto& applet_manager{Core::System::GetInstance().GetAppletManager()}; 914 const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
913 const auto applet = applet_manager.GetApplet(applet_id); 915 const auto applet = applet_manager.GetApplet(applet_id, current_process_title_id);
914 916
915 if (applet == nullptr) { 917 if (applet == nullptr) {
916 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id)); 918 LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
@@ -1234,13 +1236,13 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
1234} 1236}
1235 1237
1236void InstallInterfaces(SM::ServiceManager& service_manager, 1238void InstallInterfaces(SM::ServiceManager& service_manager,
1237 std::shared_ptr<NVFlinger::NVFlinger> nvflinger) { 1239 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
1238 auto message_queue = std::make_shared<AppletMessageQueue>(); 1240 auto message_queue = std::make_shared<AppletMessageQueue>();
1239 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on 1241 message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
1240 // game boot 1242 // game boot
1241 1243
1242 std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager); 1244 std::make_shared<AppletAE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
1243 std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager); 1245 std::make_shared<AppletOE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
1244 std::make_shared<IdleSys>()->InstallAsService(service_manager); 1246 std::make_shared<IdleSys>()->InstallAsService(service_manager);
1245 std::make_shared<OMM>()->InstallAsService(service_manager); 1247 std::make_shared<OMM>()->InstallAsService(service_manager);
1246 std::make_shared<SPSM>()->InstallAsService(service_manager); 1248 std::make_shared<SPSM>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 1fa069e56..4ea609d23 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -201,13 +201,15 @@ private:
201 201
202class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { 202class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
203public: 203public:
204 ILibraryAppletCreator(); 204 ILibraryAppletCreator(u64 current_process_title_id);
205 ~ILibraryAppletCreator() override; 205 ~ILibraryAppletCreator() override;
206 206
207private: 207private:
208 void CreateLibraryApplet(Kernel::HLERequestContext& ctx); 208 void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
209 void CreateStorage(Kernel::HLERequestContext& ctx); 209 void CreateStorage(Kernel::HLERequestContext& ctx);
210 void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx); 210 void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
211
212 u64 current_process_title_id;
211}; 213};
212 214
213class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { 215class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -264,7 +266,7 @@ public:
264 266
265/// Registers all AM services with the specified service manager. 267/// Registers all AM services with the specified service manager.
266void InstallInterfaces(SM::ServiceManager& service_manager, 268void InstallInterfaces(SM::ServiceManager& service_manager,
267 std::shared_ptr<NVFlinger::NVFlinger> nvflinger); 269 std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system);
268 270
269} // namespace AM 271} // namespace AM
270} // namespace Service 272} // namespace Service
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 488add8e7..fe5beb8f9 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/process.h"
7#include "core/hle/service/am/am.h" 8#include "core/hle/service/am/am.h"
8#include "core/hle/service/am/applet_ae.h" 9#include "core/hle/service/am/applet_ae.h"
9#include "core/hle/service/nvflinger/nvflinger.h" 10#include "core/hle/service/nvflinger/nvflinger.h"
@@ -13,9 +14,10 @@ namespace Service::AM {
13class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { 14class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
14public: 15public:
15 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 16 explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
16 std::shared_ptr<AppletMessageQueue> msg_queue) 17 std::shared_ptr<AppletMessageQueue> msg_queue,
18 Core::System& system)
17 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)), 19 : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) { 20 msg_queue(std::move(msg_queue)), system(system) {
19 // clang-format off 21 // clang-format off
20 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
21 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 23 {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -96,7 +98,7 @@ private:
96 98
97 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 99 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
98 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
99 rb.PushIpcInterface<ILibraryAppletCreator>(); 101 rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
100 } 102 }
101 103
102 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { 104 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
@@ -109,14 +111,15 @@ private:
109 111
110 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 112 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
111 std::shared_ptr<AppletMessageQueue> msg_queue; 113 std::shared_ptr<AppletMessageQueue> msg_queue;
114 Core::System& system;
112}; 115};
113 116
114class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { 117class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
115public: 118public:
116 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 119 explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
117 std::shared_ptr<AppletMessageQueue> msg_queue) 120 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
118 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)), 121 : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
119 msg_queue(std::move(msg_queue)) { 122 msg_queue(std::move(msg_queue)), system(system) {
120 // clang-format off 123 // clang-format off
121 static const FunctionInfo functions[] = { 124 static const FunctionInfo functions[] = {
122 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 125 {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -191,7 +194,7 @@ private:
191 194
192 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 195 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
193 rb.Push(RESULT_SUCCESS); 196 rb.Push(RESULT_SUCCESS);
194 rb.PushIpcInterface<ILibraryAppletCreator>(); 197 rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
195 } 198 }
196 199
197 void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) { 200 void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) {
@@ -219,6 +222,7 @@ private:
219 } 222 }
220 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 223 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
221 std::shared_ptr<AppletMessageQueue> msg_queue; 224 std::shared_ptr<AppletMessageQueue> msg_queue;
225 Core::System& system;
222}; 226};
223 227
224void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { 228void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
@@ -226,7 +230,7 @@ void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
226 230
227 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 231 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
228 rb.Push(RESULT_SUCCESS); 232 rb.Push(RESULT_SUCCESS);
229 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue); 233 rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue, system);
230} 234}
231 235
232void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { 236void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
@@ -234,7 +238,7 @@ void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
234 238
235 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 239 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
236 rb.Push(RESULT_SUCCESS); 240 rb.Push(RESULT_SUCCESS);
237 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); 241 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
238} 242}
239 243
240void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { 244void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
@@ -242,13 +246,13 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
242 246
243 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 247 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
244 rb.Push(RESULT_SUCCESS); 248 rb.Push(RESULT_SUCCESS);
245 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue); 249 rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue, system);
246} 250}
247 251
248AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 252AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
249 std::shared_ptr<AppletMessageQueue> msg_queue) 253 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
250 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)), 254 : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
251 msg_queue(std::move(msg_queue)) { 255 msg_queue(std::move(msg_queue)), system(system) {
252 // clang-format off 256 // clang-format off
253 static const FunctionInfo functions[] = { 257 static const FunctionInfo functions[] = {
254 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"}, 258 {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 902db2665..9e006cd9d 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -18,7 +18,7 @@ namespace AM {
18class AppletAE final : public ServiceFramework<AppletAE> { 18class AppletAE final : public ServiceFramework<AppletAE> {
19public: 19public:
20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 20 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
21 std::shared_ptr<AppletMessageQueue> msg_queue); 21 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
22 ~AppletAE() override; 22 ~AppletAE() override;
23 23
24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; 24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
@@ -30,6 +30,7 @@ private:
30 30
31 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 31 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
32 std::shared_ptr<AppletMessageQueue> msg_queue; 32 std::shared_ptr<AppletMessageQueue> msg_queue;
33 Core::System& system;
33}; 34};
34 35
35} // namespace AM 36} // namespace AM
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index d3a0a1568..6e255fe95 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -4,6 +4,7 @@
4 4
5#include "common/logging/log.h" 5#include "common/logging/log.h"
6#include "core/hle/ipc_helpers.h" 6#include "core/hle/ipc_helpers.h"
7#include "core/hle/kernel/process.h"
7#include "core/hle/service/am/am.h" 8#include "core/hle/service/am/am.h"
8#include "core/hle/service/am/applet_oe.h" 9#include "core/hle/service/am/applet_oe.h"
9#include "core/hle/service/nvflinger/nvflinger.h" 10#include "core/hle/service/nvflinger/nvflinger.h"
@@ -13,9 +14,9 @@ namespace Service::AM {
13class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { 14class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
14public: 15public:
15 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 16 explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
16 std::shared_ptr<AppletMessageQueue> msg_queue) 17 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
17 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)), 18 : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
18 msg_queue(std::move(msg_queue)) { 19 msg_queue(std::move(msg_queue)), system(system) {
19 // clang-format off 20 // clang-format off
20 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
21 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"}, 22 {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -87,7 +88,7 @@ private:
87 88
88 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 89 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
89 rb.Push(RESULT_SUCCESS); 90 rb.Push(RESULT_SUCCESS);
90 rb.PushIpcInterface<ILibraryAppletCreator>(); 91 rb.PushIpcInterface<ILibraryAppletCreator>(system.CurrentProcess()->GetTitleID());
91 } 92 }
92 93
93 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) { 94 void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
@@ -100,6 +101,7 @@ private:
100 101
101 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 102 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
102 std::shared_ptr<AppletMessageQueue> msg_queue; 103 std::shared_ptr<AppletMessageQueue> msg_queue;
104 Core::System& system;
103}; 105};
104 106
105void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { 107void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
@@ -107,13 +109,13 @@ void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
107 109
108 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 110 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
109 rb.Push(RESULT_SUCCESS); 111 rb.Push(RESULT_SUCCESS);
110 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue); 112 rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue, system);
111} 113}
112 114
113AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 115AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
114 std::shared_ptr<AppletMessageQueue> msg_queue) 116 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system)
115 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)), 117 : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
116 msg_queue(std::move(msg_queue)) { 118 msg_queue(std::move(msg_queue)), system(system) {
117 static const FunctionInfo functions[] = { 119 static const FunctionInfo functions[] = {
118 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"}, 120 {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
119 }; 121 };
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index bbd0108ef..22c05419d 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -18,7 +18,7 @@ namespace AM {
18class AppletOE final : public ServiceFramework<AppletOE> { 18class AppletOE final : public ServiceFramework<AppletOE> {
19public: 19public:
20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 20 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
21 std::shared_ptr<AppletMessageQueue> msg_queue); 21 std::shared_ptr<AppletMessageQueue> msg_queue, Core::System& system);
22 ~AppletOE() override; 22 ~AppletOE() override;
23 23
24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const; 24 const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
@@ -28,6 +28,7 @@ private:
28 28
29 std::shared_ptr<NVFlinger::NVFlinger> nvflinger; 29 std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
30 std::shared_ptr<AppletMessageQueue> msg_queue; 30 std::shared_ptr<AppletMessageQueue> msg_queue;
31 Core::System& system;
31}; 32};
32 33
33} // namespace AM 34} // namespace AM
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 14fa92318..6bdba2468 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -35,12 +35,28 @@ AppletDataBroker::AppletDataBroker() {
35 35
36AppletDataBroker::~AppletDataBroker() = default; 36AppletDataBroker::~AppletDataBroker() = default;
37 37
38AppletDataBroker::RawChannelData AppletDataBroker::PeekDataToAppletForDebug() const {
39 std::vector<std::vector<u8>> out_normal;
40
41 for (const auto& storage : in_channel) {
42 out_normal.push_back(storage->GetData());
43 }
44
45 std::vector<std::vector<u8>> out_interactive;
46
47 for (const auto& storage : in_interactive_channel) {
48 out_interactive.push_back(storage->GetData());
49 }
50
51 return {std::move(out_normal), std::move(out_interactive)};
52}
53
38std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { 54std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
39 if (out_channel.empty()) 55 if (out_channel.empty())
40 return nullptr; 56 return nullptr;
41 57
42 auto out = std::move(out_channel.front()); 58 auto out = std::move(out_channel.front());
43 out_channel.pop(); 59 out_channel.pop_front();
44 return out; 60 return out;
45} 61}
46 62
@@ -49,7 +65,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
49 return nullptr; 65 return nullptr;
50 66
51 auto out = std::move(in_channel.front()); 67 auto out = std::move(in_channel.front());
52 in_channel.pop(); 68 in_channel.pop_front();
53 return out; 69 return out;
54} 70}
55 71
@@ -58,7 +74,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
58 return nullptr; 74 return nullptr;
59 75
60 auto out = std::move(out_interactive_channel.front()); 76 auto out = std::move(out_interactive_channel.front());
61 out_interactive_channel.pop(); 77 out_interactive_channel.pop_front();
62 return out; 78 return out;
63} 79}
64 80
@@ -67,25 +83,25 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
67 return nullptr; 83 return nullptr;
68 84
69 auto out = std::move(in_interactive_channel.front()); 85 auto out = std::move(in_interactive_channel.front());
70 in_interactive_channel.pop(); 86 in_interactive_channel.pop_front();
71 return out; 87 return out;
72} 88}
73 89
74void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { 90void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
75 in_channel.push(std::make_unique<IStorage>(storage)); 91 in_channel.push_back(std::make_unique<IStorage>(storage));
76} 92}
77 93
78void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { 94void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
79 out_channel.push(std::make_unique<IStorage>(storage)); 95 out_channel.push_back(std::make_unique<IStorage>(storage));
80 pop_out_data_event.writable->Signal(); 96 pop_out_data_event.writable->Signal();
81} 97}
82 98
83void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { 99void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
84 in_interactive_channel.push(std::make_unique<IStorage>(storage)); 100 in_interactive_channel.push_back(std::make_unique<IStorage>(storage));
85} 101}
86 102
87void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { 103void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
88 out_interactive_channel.push(std::make_unique<IStorage>(storage)); 104 out_interactive_channel.push_back(std::make_unique<IStorage>(storage));
89 pop_interactive_out_data_event.writable->Signal(); 105 pop_interactive_out_data_event.writable->Signal();
90} 106}
91 107
@@ -123,12 +139,14 @@ void Applet::Initialize() {
123 139
124AppletFrontendSet::AppletFrontendSet() = default; 140AppletFrontendSet::AppletFrontendSet() = default;
125 141
126AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, 142AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
127 ProfileSelect profile_select, 143 PhotoViewer photo_viewer, ProfileSelect profile_select,
128 SoftwareKeyboard software_keyboard, WebBrowser web_browser) 144 SoftwareKeyboard software_keyboard, WebBrowser web_browser,
129 : error{std::move(error)}, photo_viewer{std::move(photo_viewer)}, profile_select{std::move( 145 ECommerceApplet e_commerce)
130 profile_select)}, 146 : parental_controls{std::move(parental_controls)}, error{std::move(error)},
131 software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)} {} 147 photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)},
148 software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)},
149 e_commerce{std::move(e_commerce)} {}
132 150
133AppletFrontendSet::~AppletFrontendSet() = default; 151AppletFrontendSet::~AppletFrontendSet() = default;
134 152
@@ -141,6 +159,8 @@ AppletManager::AppletManager() = default;
141AppletManager::~AppletManager() = default; 159AppletManager::~AppletManager() = default;
142 160
143void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 161void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
162 if (set.parental_controls != nullptr)
163 frontend.parental_controls = std::move(set.parental_controls);
144 if (set.error != nullptr) 164 if (set.error != nullptr)
145 frontend.error = std::move(set.error); 165 frontend.error = std::move(set.error);
146 if (set.photo_viewer != nullptr) 166 if (set.photo_viewer != nullptr)
@@ -151,17 +171,21 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
151 frontend.software_keyboard = std::move(set.software_keyboard); 171 frontend.software_keyboard = std::move(set.software_keyboard);
152 if (set.web_browser != nullptr) 172 if (set.web_browser != nullptr)
153 frontend.web_browser = std::move(set.web_browser); 173 frontend.web_browser = std::move(set.web_browser);
174 if (set.e_commerce != nullptr)
175 frontend.e_commerce = std::move(set.e_commerce);
154} 176}
155 177
156void AppletManager::SetDefaultAppletFrontendSet() { 178void AppletManager::SetDefaultAppletFrontendSet() {
157 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 179 ClearAll();
158 frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); 180 SetDefaultAppletsIfMissing();
159 frontend.profile_select = std::make_unique<Core::Frontend::DefaultProfileSelectApplet>();
160 frontend.software_keyboard = std::make_unique<Core::Frontend::DefaultSoftwareKeyboardApplet>();
161 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
162} 181}
163 182
164void AppletManager::SetDefaultAppletsIfMissing() { 183void AppletManager::SetDefaultAppletsIfMissing() {
184 if (frontend.parental_controls == nullptr) {
185 frontend.parental_controls =
186 std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
187 }
188
165 if (frontend.error == nullptr) { 189 if (frontend.error == nullptr) {
166 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); 190 frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
167 } 191 }
@@ -182,14 +206,20 @@ void AppletManager::SetDefaultAppletsIfMissing() {
182 if (frontend.web_browser == nullptr) { 206 if (frontend.web_browser == nullptr) {
183 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); 207 frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
184 } 208 }
209
210 if (frontend.e_commerce == nullptr) {
211 frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
212 }
185} 213}
186 214
187void AppletManager::ClearAll() { 215void AppletManager::ClearAll() {
188 frontend = {}; 216 frontend = {};
189} 217}
190 218
191std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { 219std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, u64 current_process_title_id) const {
192 switch (id) { 220 switch (id) {
221 case AppletId::Auth:
222 return std::make_shared<Auth>(*frontend.parental_controls);
193 case AppletId::Error: 223 case AppletId::Error:
194 return std::make_shared<Error>(*frontend.error); 224 return std::make_shared<Error>(*frontend.error);
195 case AppletId::ProfileSelect: 225 case AppletId::ProfileSelect:
@@ -198,13 +228,16 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
198 return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard); 228 return std::make_shared<SoftwareKeyboard>(*frontend.software_keyboard);
199 case AppletId::PhotoViewer: 229 case AppletId::PhotoViewer:
200 return std::make_shared<PhotoViewer>(*frontend.photo_viewer); 230 return std::make_shared<PhotoViewer>(*frontend.photo_viewer);
231 case AppletId::LibAppletShop:
232 return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id,
233 frontend.e_commerce.get());
201 case AppletId::LibAppletOff: 234 case AppletId::LibAppletOff:
202 return std::make_shared<WebBrowser>(*frontend.web_browser); 235 return std::make_shared<WebBrowser>(*frontend.web_browser, current_process_title_id);
203 default: 236 default:
204 UNIMPLEMENTED_MSG( 237 UNIMPLEMENTED_MSG(
205 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.", 238 "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
206 static_cast<u8>(id)); 239 static_cast<u8>(id));
207 return std::make_shared<StubApplet>(); 240 return std::make_shared<StubApplet>(id);
208 } 241 }
209} 242}
210 243
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index b46e10a4a..adc973dad 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -13,7 +13,9 @@
13union ResultCode; 13union ResultCode;
14 14
15namespace Core::Frontend { 15namespace Core::Frontend {
16class ECommerceApplet;
16class ErrorApplet; 17class ErrorApplet;
18class ParentalControlsApplet;
17class PhotoViewerApplet; 19class PhotoViewerApplet;
18class ProfileSelectApplet; 20class ProfileSelectApplet;
19class SoftwareKeyboardApplet; 21class SoftwareKeyboardApplet;
@@ -54,6 +56,14 @@ public:
54 AppletDataBroker(); 56 AppletDataBroker();
55 ~AppletDataBroker(); 57 ~AppletDataBroker();
56 58
59 struct RawChannelData {
60 std::vector<std::vector<u8>> normal;
61 std::vector<std::vector<u8>> interactive;
62 };
63
64 // Retrieves but does not pop the data sent to applet.
65 RawChannelData PeekDataToAppletForDebug() const;
66
57 std::unique_ptr<IStorage> PopNormalDataToGame(); 67 std::unique_ptr<IStorage> PopNormalDataToGame();
58 std::unique_ptr<IStorage> PopNormalDataToApplet(); 68 std::unique_ptr<IStorage> PopNormalDataToApplet();
59 69
@@ -76,16 +86,16 @@ private:
76 // Queues are named from applet's perspective 86 // Queues are named from applet's perspective
77 87
78 // PopNormalDataToApplet and PushNormalDataFromGame 88 // PopNormalDataToApplet and PushNormalDataFromGame
79 std::queue<std::unique_ptr<IStorage>> in_channel; 89 std::deque<std::unique_ptr<IStorage>> in_channel;
80 90
81 // PopNormalDataToGame and PushNormalDataFromApplet 91 // PopNormalDataToGame and PushNormalDataFromApplet
82 std::queue<std::unique_ptr<IStorage>> out_channel; 92 std::deque<std::unique_ptr<IStorage>> out_channel;
83 93
84 // PopInteractiveDataToApplet and PushInteractiveDataFromGame 94 // PopInteractiveDataToApplet and PushInteractiveDataFromGame
85 std::queue<std::unique_ptr<IStorage>> in_interactive_channel; 95 std::deque<std::unique_ptr<IStorage>> in_interactive_channel;
86 96
87 // PopInteractiveDataToGame and PushInteractiveDataFromApplet 97 // PopInteractiveDataToGame and PushInteractiveDataFromApplet
88 std::queue<std::unique_ptr<IStorage>> out_interactive_channel; 98 std::deque<std::unique_ptr<IStorage>> out_interactive_channel;
89 99
90 Kernel::EventPair state_changed_event; 100 Kernel::EventPair state_changed_event;
91 101
@@ -137,15 +147,19 @@ protected:
137}; 147};
138 148
139struct AppletFrontendSet { 149struct AppletFrontendSet {
150 using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
140 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; 151 using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
141 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; 152 using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
142 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; 153 using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
143 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; 154 using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
144 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; 155 using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
156 using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
145 157
146 AppletFrontendSet(); 158 AppletFrontendSet();
147 AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, ProfileSelect profile_select, 159 AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
148 SoftwareKeyboard software_keyboard, WebBrowser web_browser); 160 PhotoViewer photo_viewer, ProfileSelect profile_select,
161 SoftwareKeyboard software_keyboard, WebBrowser web_browser,
162 ECommerceApplet e_commerce);
149 ~AppletFrontendSet(); 163 ~AppletFrontendSet();
150 164
151 AppletFrontendSet(const AppletFrontendSet&) = delete; 165 AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -154,11 +168,13 @@ struct AppletFrontendSet {
154 AppletFrontendSet(AppletFrontendSet&&) noexcept; 168 AppletFrontendSet(AppletFrontendSet&&) noexcept;
155 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; 169 AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
156 170
171 ParentalControlsApplet parental_controls;
157 ErrorApplet error; 172 ErrorApplet error;
158 PhotoViewer photo_viewer; 173 PhotoViewer photo_viewer;
159 ProfileSelect profile_select; 174 ProfileSelect profile_select;
160 SoftwareKeyboard software_keyboard; 175 SoftwareKeyboard software_keyboard;
161 WebBrowser web_browser; 176 WebBrowser web_browser;
177 ECommerceApplet e_commerce;
162}; 178};
163 179
164class AppletManager { 180class AppletManager {
@@ -171,7 +187,7 @@ public:
171 void SetDefaultAppletsIfMissing(); 187 void SetDefaultAppletsIfMissing();
172 void ClearAll(); 188 void ClearAll();
173 189
174 std::shared_ptr<Applet> GetApplet(AppletId id) const; 190 std::shared_ptr<Applet> GetApplet(AppletId id, u64 current_process_title_id) const;
175 191
176private: 192private:
177 AppletFrontendSet frontend; 193 AppletFrontendSet frontend;
diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp
index 04774bedc..af3a900f8 100644
--- a/src/core/hle/service/am/applets/error.cpp
+++ b/src/core/hle/service/am/applets/error.cpp
@@ -9,8 +9,10 @@
9#include "common/string_util.h" 9#include "common/string_util.h"
10#include "core/core.h" 10#include "core/core.h"
11#include "core/frontend/applets/error.h" 11#include "core/frontend/applets/error.h"
12#include "core/hle/kernel/process.h"
12#include "core/hle/service/am/am.h" 13#include "core/hle/service/am/am.h"
13#include "core/hle/service/am/applets/error.h" 14#include "core/hle/service/am/applets/error.h"
15#include "core/reporter.h"
14 16
15namespace Service::AM::Applets { 17namespace Service::AM::Applets {
16 18
@@ -143,9 +145,12 @@ void Error::Execute() {
143 } 145 }
144 146
145 const auto callback = [this] { DisplayCompleted(); }; 147 const auto callback = [this] { DisplayCompleted(); };
148 const auto title_id = Core::CurrentProcess()->GetTitleID();
149 const auto& reporter{Core::System::GetInstance().GetReporter()};
146 150
147 switch (mode) { 151 switch (mode) {
148 case ErrorAppletMode::ShowError: 152 case ErrorAppletMode::ShowError:
153 reporter.SaveErrorReport(title_id, error_code);
149 frontend.ShowError(error_code, callback); 154 frontend.ShowError(error_code, callback);
150 break; 155 break;
151 case ErrorAppletMode::ShowSystemError: 156 case ErrorAppletMode::ShowSystemError:
@@ -156,14 +161,18 @@ void Error::Execute() {
156 const auto& detail_text = 161 const auto& detail_text =
157 system ? args->system_error.detail_text : args->application_error.detail_text; 162 system ? args->system_error.detail_text : args->application_error.detail_text;
158 163
159 frontend.ShowCustomErrorText( 164 const auto main_text_string =
160 error_code, 165 Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size());
161 Common::StringFromFixedZeroTerminatedBuffer(main_text.data(), main_text.size()), 166 const auto detail_text_string =
162 Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size()), 167 Common::StringFromFixedZeroTerminatedBuffer(detail_text.data(), detail_text.size());
163 callback); 168
169 reporter.SaveErrorReport(title_id, error_code, main_text_string, detail_text_string);
170 frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback);
164 break; 171 break;
165 } 172 }
166 case ErrorAppletMode::ShowErrorRecord: 173 case ErrorAppletMode::ShowErrorRecord:
174 reporter.SaveErrorReport(title_id, error_code,
175 fmt::format("{:016X}", args->error_record.posix_time));
167 frontend.ShowErrorWithTimestamp( 176 frontend.ShowErrorWithTimestamp(
168 error_code, std::chrono::seconds{args->error_record.posix_time}, callback); 177 error_code, std::chrono::seconds{args->error_record.posix_time}, callback);
169 break; 178 break;
diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp
index 76fc8906d..e0def8dff 100644
--- a/src/core/hle/service/am/applets/general_backend.cpp
+++ b/src/core/hle/service/am/applets/general_backend.cpp
@@ -13,9 +13,12 @@
13#include "core/hle/result.h" 13#include "core/hle/result.h"
14#include "core/hle/service/am/am.h" 14#include "core/hle/service/am/am.h"
15#include "core/hle/service/am/applets/general_backend.h" 15#include "core/hle/service/am/applets/general_backend.h"
16#include "core/reporter.h"
16 17
17namespace Service::AM::Applets { 18namespace Service::AM::Applets {
18 19
20constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221};
21
19static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { 22static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
20 std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet(); 23 std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
21 for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) { 24 for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
@@ -34,6 +37,120 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix)
34 } 37 }
35} 38}
36 39
40Auth::Auth(Core::Frontend::ParentalControlsApplet& frontend) : frontend(frontend) {}
41
42Auth::~Auth() = default;
43
44void Auth::Initialize() {
45 Applet::Initialize();
46 complete = false;
47
48 const auto storage = broker.PopNormalDataToApplet();
49 ASSERT(storage != nullptr);
50 const auto data = storage->GetData();
51 ASSERT(data.size() >= 0xC);
52
53 struct Arg {
54 INSERT_PADDING_BYTES(4);
55 AuthAppletType type;
56 u8 arg0;
57 u8 arg1;
58 u8 arg2;
59 INSERT_PADDING_BYTES(1);
60 };
61 static_assert(sizeof(Arg) == 0xC, "Arg (AuthApplet) has incorrect size.");
62
63 Arg arg{};
64 std::memcpy(&arg, data.data(), sizeof(Arg));
65
66 type = arg.type;
67 arg0 = arg.arg0;
68 arg1 = arg.arg1;
69 arg2 = arg.arg2;
70}
71
72bool Auth::TransactionComplete() const {
73 return complete;
74}
75
76ResultCode Auth::GetStatus() const {
77 return successful ? RESULT_SUCCESS : ERROR_INVALID_PIN;
78}
79
80void Auth::ExecuteInteractive() {
81 UNREACHABLE_MSG("Unexpected interactive applet data.");
82}
83
84void Auth::Execute() {
85 if (complete) {
86 return;
87 }
88
89 const auto unimplemented_log = [this] {
90 UNIMPLEMENTED_MSG("Unimplemented Auth applet type for type={:08X}, arg0={:02X}, "
91 "arg1={:02X}, arg2={:02X}",
92 static_cast<u32>(type), arg0, arg1, arg2);
93 };
94
95 switch (type) {
96 case AuthAppletType::ShowParentalAuthentication: {
97 const auto callback = [this](bool successful) { AuthFinished(successful); };
98
99 if (arg0 == 1 && arg1 == 0 && arg2 == 1) {
100 // ShowAuthenticatorForConfiguration
101 frontend.VerifyPINForSettings(callback);
102 } else if (arg1 == 0 && arg2 == 0) {
103 // ShowParentalAuthentication(bool)
104 frontend.VerifyPIN(callback, static_cast<bool>(arg0));
105 } else {
106 unimplemented_log();
107 }
108 break;
109 }
110 case AuthAppletType::RegisterParentalPasscode: {
111 const auto callback = [this] { AuthFinished(true); };
112
113 if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
114 // RegisterParentalPasscode
115 frontend.RegisterPIN(callback);
116 } else {
117 unimplemented_log();
118 }
119 break;
120 }
121 case AuthAppletType::ChangeParentalPasscode: {
122 const auto callback = [this] { AuthFinished(true); };
123
124 if (arg0 == 0 && arg1 == 0 && arg2 == 0) {
125 // ChangeParentalPasscode
126 frontend.ChangePIN(callback);
127 } else {
128 unimplemented_log();
129 }
130 break;
131 }
132 default:
133 unimplemented_log();
134 }
135}
136
137void Auth::AuthFinished(bool successful) {
138 this->successful = successful;
139
140 struct Return {
141 ResultCode result_code;
142 };
143 static_assert(sizeof(Return) == 0x4, "Return (AuthApplet) has incorrect size.");
144
145 Return return_{GetStatus()};
146
147 std::vector<u8> out(sizeof(Return));
148 std::memcpy(out.data(), &return_, sizeof(Return));
149
150 broker.PushNormalDataFromApplet(IStorage{out});
151 broker.SignalStateChanged();
152}
153
37PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} 154PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {}
38 155
39PhotoViewer::~PhotoViewer() = default; 156PhotoViewer::~PhotoViewer() = default;
@@ -83,13 +200,20 @@ void PhotoViewer::ViewFinished() {
83 broker.SignalStateChanged(); 200 broker.SignalStateChanged();
84} 201}
85 202
86StubApplet::StubApplet() = default; 203StubApplet::StubApplet(AppletId id) : id(id) {}
87 204
88StubApplet::~StubApplet() = default; 205StubApplet::~StubApplet() = default;
89 206
90void StubApplet::Initialize() { 207void StubApplet::Initialize() {
91 LOG_WARNING(Service_AM, "called (STUBBED)"); 208 LOG_WARNING(Service_AM, "called (STUBBED)");
92 Applet::Initialize(); 209 Applet::Initialize();
210
211 const auto data = broker.PeekDataToAppletForDebug();
212 Core::System::GetInstance().GetReporter().SaveUnimplementedAppletReport(
213 static_cast<u32>(id), common_args.arguments_version, common_args.library_version,
214 common_args.theme_color, common_args.play_startup_sound, common_args.system_tick,
215 data.normal, data.interactive);
216
93 LogCurrentStorage(broker, "Initialize"); 217 LogCurrentStorage(broker, "Initialize");
94} 218}
95 219
diff --git a/src/core/hle/service/am/applets/general_backend.h b/src/core/hle/service/am/applets/general_backend.h
index 2dd255d7c..0da252044 100644
--- a/src/core/hle/service/am/applets/general_backend.h
+++ b/src/core/hle/service/am/applets/general_backend.h
@@ -8,6 +8,36 @@
8 8
9namespace Service::AM::Applets { 9namespace Service::AM::Applets {
10 10
11enum class AuthAppletType : u32 {
12 ShowParentalAuthentication,
13 RegisterParentalPasscode,
14 ChangeParentalPasscode,
15};
16
17class Auth final : public Applet {
18public:
19 explicit Auth(Core::Frontend::ParentalControlsApplet& frontend);
20 ~Auth() override;
21
22 void Initialize() override;
23 bool TransactionComplete() const override;
24 ResultCode GetStatus() const override;
25 void ExecuteInteractive() override;
26 void Execute() override;
27
28 void AuthFinished(bool successful = true);
29
30private:
31 Core::Frontend::ParentalControlsApplet& frontend;
32 bool complete = false;
33 bool successful = false;
34
35 AuthAppletType type = AuthAppletType::ShowParentalAuthentication;
36 u8 arg0 = 0;
37 u8 arg1 = 0;
38 u8 arg2 = 0;
39};
40
11enum class PhotoViewerAppletMode : u8 { 41enum class PhotoViewerAppletMode : u8 {
12 CurrentApp = 0, 42 CurrentApp = 0,
13 AllApps = 1, 43 AllApps = 1,
@@ -34,7 +64,7 @@ private:
34 64
35class StubApplet final : public Applet { 65class StubApplet final : public Applet {
36public: 66public:
37 StubApplet(); 67 explicit StubApplet(AppletId id);
38 ~StubApplet() override; 68 ~StubApplet() override;
39 69
40 void Initialize() override; 70 void Initialize() override;
@@ -43,6 +73,9 @@ public:
43 ResultCode GetStatus() const override; 73 ResultCode GetStatus() const override;
44 void ExecuteInteractive() override; 74 void ExecuteInteractive() override;
45 void Execute() override; 75 void Execute() override;
76
77private:
78 AppletId id;
46}; 79};
47 80
48} // namespace Service::AM::Applets 81} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
index 7878f5136..f3c9fef0e 100644
--- a/src/core/hle/service/am/applets/web_browser.cpp
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -19,7 +19,9 @@
19#include "core/file_sys/nca_metadata.h" 19#include "core/file_sys/nca_metadata.h"
20#include "core/file_sys/registered_cache.h" 20#include "core/file_sys/registered_cache.h"
21#include "core/file_sys/romfs.h" 21#include "core/file_sys/romfs.h"
22#include "core/file_sys/system_archive/system_archive.h"
22#include "core/file_sys/vfs_types.h" 23#include "core/file_sys/vfs_types.h"
24#include "core/frontend/applets/general_frontend.h"
23#include "core/frontend/applets/web_browser.h" 25#include "core/frontend/applets/web_browser.h"
24#include "core/hle/kernel/process.h" 26#include "core/hle/kernel/process.h"
25#include "core/hle/service/am/applets/web_browser.h" 27#include "core/hle/service/am/applets/web_browser.h"
@@ -28,74 +30,187 @@
28 30
29namespace Service::AM::Applets { 31namespace Service::AM::Applets {
30 32
31// TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not 33enum class WebArgTLVType : u16 {
32// parsed, for example footer mode and left stick mode. Some of these are not particularly relevant, 34 InitialURL = 0x1,
33// but some may be worth an implementation. 35 ShopArgumentsURL = 0x2, ///< TODO(DarkLordZach): This is not the official name.
34constexpr u16 WEB_ARGUMENT_URL_TYPE = 0x6; 36 CallbackURL = 0x3,
37 CallbackableURL = 0x4,
38 ApplicationID = 0x5,
39 DocumentPath = 0x6,
40 DocumentKind = 0x7,
41 SystemDataID = 0x8,
42 ShareStartPage = 0x9,
43 Whitelist = 0xA,
44 News = 0xB,
45 UserID = 0xE,
46 AlbumEntry0 = 0xF,
47 ScreenShotEnabled = 0x10,
48 EcClientCertEnabled = 0x11,
49 Unk12 = 0x12,
50 PlayReportEnabled = 0x13,
51 Unk14 = 0x14,
52 Unk15 = 0x15,
53 BootDisplayKind = 0x17,
54 BackgroundKind = 0x18,
55 FooterEnabled = 0x19,
56 PointerEnabled = 0x1A,
57 LeftStickMode = 0x1B,
58 KeyRepeatFrame1 = 0x1C,
59 KeyRepeatFrame2 = 0x1D,
60 BootAsMediaPlayerInv = 0x1E,
61 DisplayUrlKind = 0x1F,
62 BootAsMediaPlayer = 0x21,
63 ShopJumpEnabled = 0x22,
64 MediaAutoPlayEnabled = 0x23,
65 LobbyParameter = 0x24,
66 ApplicationAlbumEntry = 0x26,
67 JsExtensionEnabled = 0x27,
68 AdditionalCommentText = 0x28,
69 TouchEnabledOnContents = 0x29,
70 UserAgentAdditionalString = 0x2A,
71 AdditionalMediaData0 = 0x2B,
72 MediaPlayerAutoCloseEnabled = 0x2C,
73 PageCacheEnabled = 0x2D,
74 WebAudioEnabled = 0x2E,
75 Unk2F = 0x2F,
76 YouTubeVideoWhitelist = 0x31,
77 FooterFixedKind = 0x32,
78 PageFadeEnabled = 0x33,
79 MediaCreatorApplicationRatingAge = 0x34,
80 BootLoadingIconEnabled = 0x35,
81 PageScrollIndicationEnabled = 0x36,
82 MediaPlayerSpeedControlEnabled = 0x37,
83 AlbumEntry1 = 0x38,
84 AlbumEntry2 = 0x39,
85 AlbumEntry3 = 0x3A,
86 AdditionalMediaData1 = 0x3B,
87 AdditionalMediaData2 = 0x3C,
88 AdditionalMediaData3 = 0x3D,
89 BootFooterButton = 0x3E,
90 OverrideWebAudioVolume = 0x3F,
91 OverrideMediaAudioVolume = 0x40,
92 BootMode = 0x41,
93 WebSessionEnabled = 0x42,
94};
95
96enum class ShimKind : u32 {
97 Shop = 1,
98 Login = 2,
99 Offline = 3,
100 Share = 4,
101 Web = 5,
102 Wifi = 6,
103 Lobby = 7,
104};
105
106enum class ShopWebTarget {
107 ApplicationInfo,
108 AddOnContentList,
109 SubscriptionList,
110 ConsumableItemList,
111 Home,
112 Settings,
113};
114
115namespace {
35 116
36struct WebBufferHeader { 117constexpr std::size_t SHIM_KIND_COUNT = 0x8;
118
119struct WebArgHeader {
37 u16 count; 120 u16 count;
38 INSERT_PADDING_BYTES(6); 121 INSERT_PADDING_BYTES(2);
122 ShimKind kind;
39}; 123};
40static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size."); 124static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size.");
41 125
42struct WebArgumentHeader { 126struct WebArgTLV {
43 u16 type; 127 WebArgTLVType type;
44 u16 size; 128 u16 size;
45 u32 offset; 129 u32 offset;
46}; 130};
47static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size."); 131static_assert(sizeof(WebArgTLV) == 0x8, "WebArgTLV has incorrect size.");
48 132
49struct WebArgumentResult { 133struct WebCommonReturnValue {
50 u32 result_code; 134 u32 result_code;
135 INSERT_PADDING_BYTES(0x4);
51 std::array<char, 0x1000> last_url; 136 std::array<char, 0x1000> last_url;
52 u64 last_url_size; 137 u64 last_url_size;
53}; 138};
54static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size."); 139static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size.");
55 140
56static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) { 141struct WebWifiPageArg {
57 WebBufferHeader header; 142 INSERT_PADDING_BYTES(4);
58 ASSERT(sizeof(WebBufferHeader) <= data.size()); 143 std::array<char, 0x100> connection_test_url;
59 std::memcpy(&header, data.data(), sizeof(WebBufferHeader)); 144 std::array<char, 0x400> initial_url;
60 145 std::array<u8, 0x10> nifm_network_uuid;
61 u64 offset = sizeof(WebBufferHeader); 146 u32 nifm_requirement;
62 for (u16 i = 0; i < header.count; ++i) { 147};
63 WebArgumentHeader arg; 148static_assert(sizeof(WebWifiPageArg) == 0x518, "WebWifiPageArg has incorrect size.");
64 ASSERT(offset + sizeof(WebArgumentHeader) <= data.size()); 149
65 std::memcpy(&arg, data.data() + offset, sizeof(WebArgumentHeader)); 150struct WebWifiReturnValue {
66 offset += sizeof(WebArgumentHeader); 151 INSERT_PADDING_BYTES(4);
67 152 u32 result;
68 if (arg.type == type) { 153};
69 std::vector<u8> out(arg.size); 154static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size.");
70 offset += arg.offset; 155
71 ASSERT(offset + arg.size <= data.size()); 156enum class OfflineWebSource : u32 {
72 std::memcpy(out.data(), data.data() + offset, out.size()); 157 OfflineHtmlPage = 0x1,
158 ApplicationLegalInformation = 0x2,
159 SystemDataPage = 0x3,
160};
161
162std::map<WebArgTLVType, std::vector<u8>> GetWebArguments(const std::vector<u8>& arg) {
163 if (arg.size() < sizeof(WebArgHeader))
164 return {};
165
166 WebArgHeader header{};
167 std::memcpy(&header, arg.data(), sizeof(WebArgHeader));
168
169 std::map<WebArgTLVType, std::vector<u8>> out;
170 u64 offset = sizeof(WebArgHeader);
171 for (std::size_t i = 0; i < header.count; ++i) {
172 if (arg.size() < (offset + sizeof(WebArgTLV)))
73 return out; 173 return out;
74 }
75 174
76 offset += arg.offset + arg.size; 175 WebArgTLV tlv{};
77 } 176 std::memcpy(&tlv, arg.data() + offset, sizeof(WebArgTLV));
177 offset += sizeof(WebArgTLV);
78 178
79 return {}; 179 offset += tlv.offset;
80} 180 if (arg.size() < (offset + tlv.size))
181 return out;
182
183 std::vector<u8> data(tlv.size);
184 std::memcpy(data.data(), arg.data() + offset, tlv.size);
185 offset += tlv.size;
81 186
82static FileSys::VirtualFile GetManualRomFS() { 187 out.insert_or_assign(tlv.type, data);
83 auto& loader{Core::System::GetInstance().GetAppLoader()}; 188 }
84 189
85 FileSys::VirtualFile out; 190 return out;
86 if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success) 191}
87 return out;
88 192
193FileSys::VirtualFile GetApplicationRomFS(u64 title_id, FileSys::ContentRecordType type) {
89 const auto& installed{Core::System::GetInstance().GetContentProvider()}; 194 const auto& installed{Core::System::GetInstance().GetContentProvider()};
90 const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(), 195 const auto res = installed.GetEntry(title_id, type);
91 FileSys::ContentRecordType::Manual);
92 196
93 if (res != nullptr) 197 if (res != nullptr) {
94 return res->GetRomFS(); 198 return res->GetRomFS();
199 }
200
201 if (type == FileSys::ContentRecordType::Data) {
202 return FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
203 }
204
95 return nullptr; 205 return nullptr;
96} 206}
97 207
98WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {} 208} // Anonymous namespace
209
210WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
211 Core::Frontend::ECommerceApplet* frontend_e_commerce)
212 : frontend(frontend), frontend_e_commerce(frontend_e_commerce),
213 current_process_title_id(current_process_title_id) {}
99 214
100WebBrowser::~WebBrowser() = default; 215WebBrowser::~WebBrowser() = default;
101 216
@@ -111,24 +226,12 @@ void WebBrowser::Initialize() {
111 ASSERT(web_arg_storage != nullptr); 226 ASSERT(web_arg_storage != nullptr);
112 const auto& web_arg = web_arg_storage->GetData(); 227 const auto& web_arg = web_arg_storage->GetData();
113 228
114 const auto url_data = GetArgumentDataForTagType(web_arg, WEB_ARGUMENT_URL_TYPE); 229 ASSERT(web_arg.size() >= 0x8);
115 filename = Common::StringFromFixedZeroTerminatedBuffer( 230 std::memcpy(&kind, web_arg.data() + 0x4, sizeof(ShimKind));
116 reinterpret_cast<const char*>(url_data.data()), url_data.size());
117 231
118 temporary_dir = FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + 232 args = GetWebArguments(web_arg);
119 "web_applet_manual",
120 FileUtil::DirectorySeparator::PlatformDefault);
121 FileUtil::DeleteDirRecursively(temporary_dir);
122 233
123 manual_romfs = GetManualRomFS(); 234 InitializeInternal();
124 if (manual_romfs == nullptr) {
125 status = ResultCode(-1);
126 LOG_ERROR(Service_AM, "Failed to find manual for current process!");
127 }
128
129 filename =
130 FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename,
131 FileUtil::DirectorySeparator::PlatformDefault);
132} 235}
133 236
134bool WebBrowser::TransactionComplete() const { 237bool WebBrowser::TransactionComplete() const {
@@ -144,24 +247,25 @@ void WebBrowser::ExecuteInteractive() {
144} 247}
145 248
146void WebBrowser::Execute() { 249void WebBrowser::Execute() {
147 if (complete) 250 if (complete) {
148 return; 251 return;
252 }
149 253
150 if (status != RESULT_SUCCESS) { 254 if (status != RESULT_SUCCESS) {
151 complete = true; 255 complete = true;
152 return; 256 return;
153 } 257 }
154 258
155 frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); 259 ExecuteInternal();
156} 260}
157 261
158void WebBrowser::UnpackRomFS() { 262void WebBrowser::UnpackRomFS() {
159 if (unpacked) 263 if (unpacked)
160 return; 264 return;
161 265
162 ASSERT(manual_romfs != nullptr); 266 ASSERT(offline_romfs != nullptr);
163 const auto dir = 267 const auto dir =
164 FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard); 268 FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard);
165 const auto& vfs{Core::System::GetInstance().GetFilesystem()}; 269 const auto& vfs{Core::System::GetInstance().GetFilesystem()};
166 const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite); 270 const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
167 FileSys::VfsRawCopyD(dir, temp_dir); 271 FileSys::VfsRawCopyD(dir, temp_dir);
@@ -172,17 +276,275 @@ void WebBrowser::UnpackRomFS() {
172void WebBrowser::Finalize() { 276void WebBrowser::Finalize() {
173 complete = true; 277 complete = true;
174 278
175 WebArgumentResult out{}; 279 WebCommonReturnValue out{};
176 out.result_code = 0; 280 out.result_code = 0;
177 out.last_url_size = 0; 281 out.last_url_size = 0;
178 282
179 std::vector<u8> data(sizeof(WebArgumentResult)); 283 std::vector<u8> data(sizeof(WebCommonReturnValue));
180 std::memcpy(data.data(), &out, sizeof(WebArgumentResult)); 284 std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue));
181 285
182 broker.PushNormalDataFromApplet(IStorage{data}); 286 broker.PushNormalDataFromApplet(IStorage{data});
183 broker.SignalStateChanged(); 287 broker.SignalStateChanged();
184 288
289 if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) {
290 FileUtil::DeleteDirRecursively(temporary_dir);
291 }
292}
293
294void WebBrowser::InitializeInternal() {
295 using WebAppletInitializer = void (WebBrowser::*)();
296
297 constexpr std::array<WebAppletInitializer, SHIM_KIND_COUNT> functions{
298 nullptr, &WebBrowser::InitializeShop,
299 nullptr, &WebBrowser::InitializeOffline,
300 nullptr, nullptr,
301 nullptr, nullptr,
302 };
303
304 const auto index = static_cast<u32>(kind);
305
306 if (index > functions.size() || functions[index] == nullptr) {
307 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
308 return;
309 }
310
311 const auto function = functions[index];
312 (this->*function)();
313}
314
315void WebBrowser::ExecuteInternal() {
316 using WebAppletExecutor = void (WebBrowser::*)();
317
318 constexpr std::array<WebAppletExecutor, SHIM_KIND_COUNT> functions{
319 nullptr, &WebBrowser::ExecuteShop,
320 nullptr, &WebBrowser::ExecuteOffline,
321 nullptr, nullptr,
322 nullptr, nullptr,
323 };
324
325 const auto index = static_cast<u32>(kind);
326
327 if (index > functions.size() || functions[index] == nullptr) {
328 LOG_ERROR(Service_AM, "Invalid shim_kind={:08X}", index);
329 return;
330 }
331
332 const auto function = functions[index];
333 (this->*function)();
334}
335
336void WebBrowser::InitializeShop() {
337 if (frontend_e_commerce == nullptr) {
338 LOG_ERROR(Service_AM, "Missing ECommerce Applet frontend!");
339 status = ResultCode(-1);
340 return;
341 }
342
343 const auto user_id_data = args.find(WebArgTLVType::UserID);
344
345 user_id = std::nullopt;
346 if (user_id_data != args.end()) {
347 user_id = u128{};
348 std::memcpy(user_id->data(), user_id_data->second.data(), sizeof(u128));
349 }
350
351 const auto url = args.find(WebArgTLVType::ShopArgumentsURL);
352
353 if (url == args.end()) {
354 LOG_ERROR(Service_AM, "Missing EShop Arguments URL for initialization!");
355 status = ResultCode(-1);
356 return;
357 }
358
359 std::vector<std::string> split_query;
360 Common::SplitString(Common::StringFromFixedZeroTerminatedBuffer(
361 reinterpret_cast<const char*>(url->second.data()), url->second.size()),
362 '?', split_query);
363
364 // 2 -> Main URL '?' Query Parameters
365 // Less is missing info, More is malformed
366 if (split_query.size() != 2) {
367 LOG_ERROR(Service_AM, "EShop Arguments has more than one question mark, malformed");
368 status = ResultCode(-1);
369 return;
370 }
371
372 std::vector<std::string> queries;
373 Common::SplitString(split_query[1], '&', queries);
374
375 const auto split_single_query =
376 [](const std::string& in) -> std::pair<std::string, std::string> {
377 const auto index = in.find('=');
378 if (index == std::string::npos || index == in.size() - 1) {
379 return {in, ""};
380 }
381
382 return {in.substr(0, index), in.substr(index + 1)};
383 };
384
385 std::transform(queries.begin(), queries.end(),
386 std::inserter(shop_query, std::next(shop_query.begin())), split_single_query);
387
388 const auto scene = shop_query.find("scene");
389
390 if (scene == shop_query.end()) {
391 LOG_ERROR(Service_AM, "No scene parameter was passed via shop query!");
392 status = ResultCode(-1);
393 return;
394 }
395
396 const std::map<std::string, ShopWebTarget, std::less<>> target_map{
397 {"product_detail", ShopWebTarget::ApplicationInfo},
398 {"aocs", ShopWebTarget::AddOnContentList},
399 {"subscriptions", ShopWebTarget::SubscriptionList},
400 {"consumption", ShopWebTarget::ConsumableItemList},
401 {"settings", ShopWebTarget::Settings},
402 {"top", ShopWebTarget::Home},
403 };
404
405 const auto target = target_map.find(scene->second);
406 if (target == target_map.end()) {
407 LOG_ERROR(Service_AM, "Scene for shop query is invalid! (scene={})", scene->second);
408 status = ResultCode(-1);
409 return;
410 }
411
412 shop_web_target = target->second;
413
414 const auto title_id_data = shop_query.find("dst_app_id");
415 if (title_id_data != shop_query.end()) {
416 title_id = std::stoull(title_id_data->second, nullptr, 0x10);
417 }
418
419 const auto mode_data = shop_query.find("mode");
420 if (mode_data != shop_query.end()) {
421 shop_full_display = mode_data->second == "full";
422 }
423}
424
425void WebBrowser::InitializeOffline() {
426 if (args.find(WebArgTLVType::DocumentPath) == args.end() ||
427 args.find(WebArgTLVType::DocumentKind) == args.end() ||
428 args.find(WebArgTLVType::ApplicationID) == args.end()) {
429 status = ResultCode(-1);
430 LOG_ERROR(Service_AM, "Missing necessary parameters for initialization!");
431 }
432
433 const auto url_data = args[WebArgTLVType::DocumentPath];
434 filename = Common::StringFromFixedZeroTerminatedBuffer(
435 reinterpret_cast<const char*>(url_data.data()), url_data.size());
436
437 OfflineWebSource source;
438 ASSERT(args[WebArgTLVType::DocumentKind].size() >= 4);
439 std::memcpy(&source, args[WebArgTLVType::DocumentKind].data(), sizeof(OfflineWebSource));
440
441 constexpr std::array<const char*, 3> WEB_SOURCE_NAMES{
442 "manual",
443 "legal",
444 "system",
445 };
446
447 temporary_dir =
448 FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" +
449 WEB_SOURCE_NAMES[static_cast<u32>(source) - 1],
450 FileUtil::DirectorySeparator::PlatformDefault);
185 FileUtil::DeleteDirRecursively(temporary_dir); 451 FileUtil::DeleteDirRecursively(temporary_dir);
452
453 u64 title_id = 0; // 0 corresponds to current process
454 ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8);
455 std::memcpy(&title_id, args[WebArgTLVType::ApplicationID].data(), sizeof(u64));
456 FileSys::ContentRecordType type = FileSys::ContentRecordType::Data;
457
458 switch (source) {
459 case OfflineWebSource::OfflineHtmlPage:
460 // While there is an AppID TLV field, in official SW this is always ignored.
461 title_id = 0;
462 type = FileSys::ContentRecordType::HtmlDocument;
463 break;
464 case OfflineWebSource::ApplicationLegalInformation:
465 type = FileSys::ContentRecordType::LegalInformation;
466 break;
467 case OfflineWebSource::SystemDataPage:
468 type = FileSys::ContentRecordType::Data;
469 break;
470 }
471
472 if (title_id == 0) {
473 title_id = current_process_title_id;
474 }
475
476 offline_romfs = GetApplicationRomFS(title_id, type);
477 if (offline_romfs == nullptr) {
478 status = ResultCode(-1);
479 LOG_ERROR(Service_AM, "Failed to find offline data for request!");
480 }
481
482 std::string path_additional_directory;
483 if (source == OfflineWebSource::OfflineHtmlPage) {
484 path_additional_directory = std::string(DIR_SEP).append("html-document");
485 }
486
487 filename =
488 FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename,
489 FileUtil::DirectorySeparator::PlatformDefault);
490}
491
492void WebBrowser::ExecuteShop() {
493 const auto callback = [this]() { Finalize(); };
494
495 const auto check_optional_parameter = [this](const auto& p) {
496 if (!p.has_value()) {
497 LOG_ERROR(Service_AM, "Missing one or more necessary parameters for execution!");
498 status = ResultCode(-1);
499 return false;
500 }
501
502 return true;
503 };
504
505 switch (shop_web_target) {
506 case ShopWebTarget::ApplicationInfo:
507 if (!check_optional_parameter(title_id))
508 return;
509 frontend_e_commerce->ShowApplicationInformation(callback, *title_id, user_id,
510 shop_full_display, shop_extra_parameter);
511 break;
512 case ShopWebTarget::AddOnContentList:
513 if (!check_optional_parameter(title_id))
514 return;
515 frontend_e_commerce->ShowAddOnContentList(callback, *title_id, user_id, shop_full_display);
516 break;
517 case ShopWebTarget::ConsumableItemList:
518 if (!check_optional_parameter(title_id))
519 return;
520 frontend_e_commerce->ShowConsumableItemList(callback, *title_id, user_id);
521 break;
522 case ShopWebTarget::Home:
523 if (!check_optional_parameter(user_id))
524 return;
525 if (!check_optional_parameter(shop_full_display))
526 return;
527 frontend_e_commerce->ShowShopHome(callback, *user_id, *shop_full_display);
528 break;
529 case ShopWebTarget::Settings:
530 if (!check_optional_parameter(user_id))
531 return;
532 if (!check_optional_parameter(shop_full_display))
533 return;
534 frontend_e_commerce->ShowSettings(callback, *user_id, *shop_full_display);
535 break;
536 case ShopWebTarget::SubscriptionList:
537 if (!check_optional_parameter(title_id))
538 return;
539 frontend_e_commerce->ShowSubscriptionList(callback, *title_id, user_id);
540 break;
541 default:
542 UNREACHABLE();
543 }
544}
545
546void WebBrowser::ExecuteOffline() {
547 frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
186} 548}
187 549
188} // namespace Service::AM::Applets 550} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h
index 7e0f34c7d..870f57b64 100644
--- a/src/core/hle/service/am/applets/web_browser.h
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -4,15 +4,22 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <map>
7#include "core/file_sys/vfs_types.h" 8#include "core/file_sys/vfs_types.h"
8#include "core/hle/service/am/am.h" 9#include "core/hle/service/am/am.h"
9#include "core/hle/service/am/applets/applets.h" 10#include "core/hle/service/am/applets/applets.h"
10 11
11namespace Service::AM::Applets { 12namespace Service::AM::Applets {
12 13
14enum class ShimKind : u32;
15enum class ShopWebTarget;
16enum class WebArgTLVType : u16;
17
13class WebBrowser final : public Applet { 18class WebBrowser final : public Applet {
14public: 19public:
15 WebBrowser(Core::Frontend::WebBrowserApplet& frontend); 20 WebBrowser(Core::Frontend::WebBrowserApplet& frontend, u64 current_process_title_id,
21 Core::Frontend::ECommerceApplet* frontend_e_commerce = nullptr);
22
16 ~WebBrowser() override; 23 ~WebBrowser() override;
17 24
18 void Initialize() override; 25 void Initialize() override;
@@ -32,15 +39,41 @@ public:
32 void Finalize(); 39 void Finalize();
33 40
34private: 41private:
42 void InitializeInternal();
43 void ExecuteInternal();
44
45 // Specific initializers for the types of web applets
46 void InitializeShop();
47 void InitializeOffline();
48
49 // Specific executors for the types of web applets
50 void ExecuteShop();
51 void ExecuteOffline();
52
35 Core::Frontend::WebBrowserApplet& frontend; 53 Core::Frontend::WebBrowserApplet& frontend;
36 54
55 // Extra frontends for specialized functions
56 Core::Frontend::ECommerceApplet* frontend_e_commerce;
57
37 bool complete = false; 58 bool complete = false;
38 bool unpacked = false; 59 bool unpacked = false;
39 ResultCode status = RESULT_SUCCESS; 60 ResultCode status = RESULT_SUCCESS;
40 61
41 FileSys::VirtualFile manual_romfs; 62 u64 current_process_title_id;
63
64 ShimKind kind;
65 std::map<WebArgTLVType, std::vector<u8>> args;
66
67 FileSys::VirtualFile offline_romfs;
42 std::string temporary_dir; 68 std::string temporary_dir;
43 std::string filename; 69 std::string filename;
70
71 ShopWebTarget shop_web_target;
72 std::map<std::string, std::string, std::less<>> shop_query;
73 std::optional<u64> title_id = 0;
74 std::optional<u128> user_id;
75 std::optional<bool> shop_full_display;
76 std::string shop_extra_parameter;
44}; 77};
45 78
46} // namespace Service::AM::Applets 79} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp
deleted file mode 100644
index e675b0188..000000000
--- a/src/core/hle/service/arp/arp.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/hle_ipc.h"
10#include "core/hle/service/arp/arp.h"
11#include "core/hle/service/service.h"
12#include "core/hle/service/sm/sm.h"
13
14namespace Service::ARP {
15
16class ARP_R final : public ServiceFramework<ARP_R> {
17public:
18 explicit ARP_R() : ServiceFramework{"arp:r"} {
19 // clang-format off
20 static const FunctionInfo functions[] = {
21 {0, nullptr, "GetApplicationLaunchProperty"},
22 {1, nullptr, "GetApplicationLaunchPropertyWithApplicationId"},
23 {2, nullptr, "GetApplicationControlProperty"},
24 {3, nullptr, "GetApplicationControlPropertyWithApplicationId"},
25 };
26 // clang-format on
27
28 RegisterHandlers(functions);
29 }
30};
31
32class IRegistrar final : public ServiceFramework<IRegistrar> {
33public:
34 explicit IRegistrar() : ServiceFramework{"IRegistrar"} {
35 // clang-format off
36 static const FunctionInfo functions[] = {
37 {0, nullptr, "Issue"},
38 {1, nullptr, "SetApplicationLaunchProperty"},
39 {2, nullptr, "SetApplicationControlProperty"},
40 };
41 // clang-format on
42
43 RegisterHandlers(functions);
44 }
45};
46
47class ARP_W final : public ServiceFramework<ARP_W> {
48public:
49 explicit ARP_W() : ServiceFramework{"arp:w"} {
50 // clang-format off
51 static const FunctionInfo functions[] = {
52 {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
53 {1, nullptr, "DeleteProperties"},
54 };
55 // clang-format on
56
57 RegisterHandlers(functions);
58 }
59
60private:
61 void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
62 LOG_DEBUG(Service_ARP, "called");
63
64 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
65 rb.Push(RESULT_SUCCESS);
66 rb.PushIpcInterface<IRegistrar>();
67 }
68};
69
70void InstallInterfaces(SM::ServiceManager& sm) {
71 std::make_shared<ARP_R>()->InstallAsService(sm);
72 std::make_shared<ARP_W>()->InstallAsService(sm);
73}
74
75} // namespace Service::ARP
diff --git a/src/core/hle/service/arp/arp.h b/src/core/hle/service/arp/arp.h
deleted file mode 100644
index 9d100187c..000000000
--- a/src/core/hle/service/arp/arp.h
+++ /dev/null
@@ -1,16 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Service::SM {
8class ServiceManager;
9}
10
11namespace Service::ARP {
12
13/// Registers all ARP services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm);
15
16} // namespace Service::ARP
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 75db0c2dc..3711e1ea1 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -167,13 +167,12 @@ public:
167 {3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"}, 167 {3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
168 {4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"}, 168 {4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
169 {5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"}, 169 {5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
170 {6, &IAudioDevice::ListAudioDeviceName, 170 {6, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceNameAuto"},
171 "ListAudioDeviceNameAuto"}, // TODO(ogniK): Confirm if autos are identical to non auto
172 {7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"}, 171 {7, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolumeAuto"},
173 {8, nullptr, "GetAudioDeviceOutputVolumeAuto"}, 172 {8, nullptr, "GetAudioDeviceOutputVolumeAuto"},
174 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"}, 173 {10, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceNameAuto"},
175 {11, nullptr, "QueryAudioDeviceInputEvent"}, 174 {11, nullptr, "QueryAudioDeviceInputEvent"},
176 {12, nullptr, "QueryAudioDeviceOutputEvent"}, 175 {12, &IAudioDevice::QueryAudioDeviceOutputEvent, "QueryAudioDeviceOutputEvent"},
177 {13, nullptr, "GetAudioSystemMasterVolumeSetting"}, 176 {13, nullptr, "GetAudioSystemMasterVolumeSetting"},
178 }; 177 };
179 RegisterHandlers(functions); 178 RegisterHandlers(functions);
@@ -181,6 +180,11 @@ public:
181 auto& kernel = Core::System::GetInstance().Kernel(); 180 auto& kernel = Core::System::GetInstance().Kernel();
182 buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 181 buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
183 "IAudioOutBufferReleasedEvent"); 182 "IAudioOutBufferReleasedEvent");
183
184 // Should only be signalled when an audio output device has been changed, example: speaker
185 // to headset
186 audio_output_device_switch_event = Kernel::WritableEvent::CreateEventPair(
187 kernel, Kernel::ResetType::Automatic, "IAudioDevice:AudioOutputDeviceSwitchedEvent");
184 } 188 }
185 189
186private: 190private:
@@ -237,7 +241,16 @@ private:
237 rb.Push<u32>(1); 241 rb.Push<u32>(1);
238 } 242 }
239 243
244 void QueryAudioDeviceOutputEvent(Kernel::HLERequestContext& ctx) {
245 LOG_DEBUG(Service_Audio, "called");
246
247 IPC::ResponseBuilder rb{ctx, 2, 1};
248 rb.Push(RESULT_SUCCESS);
249 rb.PushCopyObjects(audio_output_device_switch_event.readable);
250 }
251
240 Kernel::EventPair buffer_event; 252 Kernel::EventPair buffer_event;
253 Kernel::EventPair audio_output_device_switch_event;
241 254
242}; // namespace Audio 255}; // namespace Audio
243 256
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 2c229bcad..fe49c2161 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/service/fatal/fatal.h" 16#include "core/hle/service/fatal/fatal.h"
17#include "core/hle/service/fatal/fatal_p.h" 17#include "core/hle/service/fatal/fatal_p.h"
18#include "core/hle/service/fatal/fatal_u.h" 18#include "core/hle/service/fatal/fatal_u.h"
19#include "core/reporter.h"
19 20
20namespace Service::Fatal { 21namespace Service::Fatal {
21 22
@@ -100,27 +101,10 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
100 101
101 LOG_ERROR(Service_Fatal, "{}", crash_report); 102 LOG_ERROR(Service_Fatal, "{}", crash_report);
102 103
103 const std::string crashreport_dir = 104 Core::System::GetInstance().GetReporter().SaveCrashReport(
104 FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs"; 105 title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
105 106 info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
106 if (!FileUtil::CreateFullPath(crashreport_dir)) { 107 info.backtrace_size, info.ArchAsString(), info.unk10);
107 LOG_ERROR(
108 Service_Fatal,
109 "Unable to create crash report directory. Possible log directory permissions issue.");
110 return;
111 }
112
113 const std::time_t t = std::time(nullptr);
114 const std::string crashreport_filename =
115 fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t));
116
117 auto file = FileUtil::IOFile(crashreport_filename, "wb");
118 if (file.IsOpen()) {
119 file.WriteString(crash_report);
120 LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename);
121 } else {
122 LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename);
123 }
124} 108}
125 109
126static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { 110static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h
new file mode 100644
index 000000000..b3996e275
--- /dev/null
+++ b/src/core/hle/service/friend/errors.h
@@ -0,0 +1,12 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Service::Friend {
10
11constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
12}
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 5100e376c..dec541f2e 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -2,8 +2,13 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <queue>
5#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/uuid.h"
6#include "core/hle/ipc_helpers.h" 8#include "core/hle/ipc_helpers.h"
9#include "core/hle/kernel/readable_event.h"
10#include "core/hle/kernel/writable_event.h"
11#include "core/hle/service/friend/errors.h"
7#include "core/hle/service/friend/friend.h" 12#include "core/hle/service/friend/friend.h"
8#include "core/hle/service/friend/interface.h" 13#include "core/hle/service/friend/interface.h"
9 14
@@ -109,6 +114,105 @@ private:
109 } 114 }
110}; 115};
111 116
117class INotificationService final : public ServiceFramework<INotificationService> {
118public:
119 INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) {
120 // clang-format off
121 static const FunctionInfo functions[] = {
122 {0, &INotificationService::GetEvent, "GetEvent"},
123 {1, &INotificationService::Clear, "Clear"},
124 {2, &INotificationService::Pop, "Pop"}
125 };
126 // clang-format on
127
128 RegisterHandlers(functions);
129 }
130
131private:
132 void GetEvent(Kernel::HLERequestContext& ctx) {
133 LOG_DEBUG(Service_ACC, "called");
134
135 IPC::ResponseBuilder rb{ctx, 2, 1};
136 rb.Push(RESULT_SUCCESS);
137
138 if (!is_event_created) {
139 auto& kernel = Core::System::GetInstance().Kernel();
140 notification_event = Kernel::WritableEvent::CreateEventPair(
141 kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
142 is_event_created = true;
143 }
144 rb.PushCopyObjects(notification_event.readable);
145 }
146
147 void Clear(Kernel::HLERequestContext& ctx) {
148 LOG_DEBUG(Service_ACC, "called");
149 while (!notifications.empty()) {
150 notifications.pop();
151 }
152 std::memset(&states, 0, sizeof(States));
153
154 IPC::ResponseBuilder rb{ctx, 2};
155 rb.Push(RESULT_SUCCESS);
156 }
157
158 void Pop(Kernel::HLERequestContext& ctx) {
159 LOG_DEBUG(Service_ACC, "called");
160
161 if (notifications.empty()) {
162 LOG_ERROR(Service_ACC, "No notifications in queue!");
163 IPC::ResponseBuilder rb{ctx, 2};
164 rb.Push(ERR_NO_NOTIFICATIONS);
165 return;
166 }
167
168 const auto notification = notifications.front();
169 notifications.pop();
170
171 switch (notification.notification_type) {
172 case NotificationTypes::HasUpdatedFriendsList:
173 states.has_updated_friends = false;
174 break;
175 case NotificationTypes::HasReceivedFriendRequest:
176 states.has_received_friend_request = false;
177 break;
178 default:
179 // HOS seems not have an error case for an unknown notification
180 LOG_WARNING(Service_ACC, "Unknown notification {:08X}",
181 static_cast<u32>(notification.notification_type));
182 break;
183 }
184
185 IPC::ResponseBuilder rb{ctx, 6};
186 rb.Push(RESULT_SUCCESS);
187 rb.PushRaw<SizedNotificationInfo>(notification);
188 }
189
190 enum class NotificationTypes : u32 {
191 HasUpdatedFriendsList = 0x65,
192 HasReceivedFriendRequest = 0x1
193 };
194
195 struct SizedNotificationInfo {
196 NotificationTypes notification_type;
197 INSERT_PADDING_WORDS(
198 1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now
199 u64_le account_id;
200 };
201 static_assert(sizeof(SizedNotificationInfo) == 0x10,
202 "SizedNotificationInfo is an incorrect size");
203
204 struct States {
205 bool has_updated_friends;
206 bool has_received_friend_request;
207 };
208
209 Common::UUID uuid;
210 bool is_event_created = false;
211 Kernel::EventPair notification_event;
212 std::queue<SizedNotificationInfo> notifications;
213 States states{};
214};
215
112void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { 216void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
113 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 217 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
114 rb.Push(RESULT_SUCCESS); 218 rb.Push(RESULT_SUCCESS);
@@ -116,6 +220,17 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
116 LOG_DEBUG(Service_ACC, "called"); 220 LOG_DEBUG(Service_ACC, "called");
117} 221}
118 222
223void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) {
224 IPC::RequestParser rp{ctx};
225 auto uuid = rp.PopRaw<Common::UUID>();
226
227 LOG_DEBUG(Service_ACC, "called, uuid={}", uuid.Format());
228
229 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
230 rb.Push(RESULT_SUCCESS);
231 rb.PushIpcInterface<INotificationService>(uuid);
232}
233
119Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 234Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
120 : ServiceFramework(name), module(std::move(module)) {} 235 : ServiceFramework(name), module(std::move(module)) {}
121 236
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
index e762840cb..38d05fa8e 100644
--- a/src/core/hle/service/friend/friend.h
+++ b/src/core/hle/service/friend/friend.h
@@ -16,6 +16,7 @@ public:
16 ~Interface() override; 16 ~Interface() override;
17 17
18 void CreateFriendService(Kernel::HLERequestContext& ctx); 18 void CreateFriendService(Kernel::HLERequestContext& ctx);
19 void CreateNotificationService(Kernel::HLERequestContext& ctx);
19 20
20 protected: 21 protected:
21 std::shared_ptr<Module> module; 22 std::shared_ptr<Module> module;
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp
index 5a6840af5..5b384f733 100644
--- a/src/core/hle/service/friend/interface.cpp
+++ b/src/core/hle/service/friend/interface.cpp
@@ -10,7 +10,7 @@ Friend::Friend(std::shared_ptr<Module> module, const char* name)
10 : Interface(std::move(module), name) { 10 : Interface(std::move(module), name) {
11 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
12 {0, &Friend::CreateFriendService, "CreateFriendService"}, 12 {0, &Friend::CreateFriendService, "CreateFriendService"},
13 {1, nullptr, "CreateNotificationService"}, 13 {1, &Friend::CreateNotificationService, "CreateNotificationService"},
14 {2, nullptr, "CreateDaemonSuspendSessionService"}, 14 {2, nullptr, "CreateDaemonSuspendSessionService"},
15 }; 15 };
16 RegisterHandlers(functions); 16 RegisterHandlers(functions);
diff --git a/src/core/hle/service/glue/arp.cpp b/src/core/hle/service/glue/arp.cpp
new file mode 100644
index 000000000..b591ce31b
--- /dev/null
+++ b/src/core/hle/service/glue/arp.cpp
@@ -0,0 +1,297 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6
7#include "common/logging/log.h"
8#include "core/file_sys/control_metadata.h"
9#include "core/hle/ipc_helpers.h"
10#include "core/hle/kernel/hle_ipc.h"
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/process.h"
13#include "core/hle/service/glue/arp.h"
14#include "core/hle/service/glue/errors.h"
15#include "core/hle/service/glue/manager.h"
16#include "core/hle/service/service.h"
17
18namespace Service::Glue {
19
20namespace {
21std::optional<u64> GetTitleIDForProcessID(const Core::System& system, u64 process_id) {
22 const auto& list = system.Kernel().GetProcessList();
23 const auto iter = std::find_if(list.begin(), list.end(), [&process_id](const auto& process) {
24 return process->GetProcessID() == process_id;
25 });
26
27 if (iter == list.end()) {
28 return std::nullopt;
29 }
30
31 return (*iter)->GetTitleID();
32}
33} // Anonymous namespace
34
35ARP_R::ARP_R(const Core::System& system, const ARPManager& manager)
36 : ServiceFramework{"arp:r"}, system(system), manager(manager) {
37 // clang-format off
38 static const FunctionInfo functions[] = {
39 {0, &ARP_R::GetApplicationLaunchProperty, "GetApplicationLaunchProperty"},
40 {1, &ARP_R::GetApplicationLaunchPropertyWithApplicationId, "GetApplicationLaunchPropertyWithApplicationId"},
41 {2, &ARP_R::GetApplicationControlProperty, "GetApplicationControlProperty"},
42 {3, &ARP_R::GetApplicationControlPropertyWithApplicationId, "GetApplicationControlPropertyWithApplicationId"},
43 };
44 // clang-format on
45
46 RegisterHandlers(functions);
47}
48
49ARP_R::~ARP_R() = default;
50
51void ARP_R::GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
52 IPC::RequestParser rp{ctx};
53 const auto process_id = rp.PopRaw<u64>();
54
55 LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
56
57 const auto title_id = GetTitleIDForProcessID(system, process_id);
58 if (!title_id.has_value()) {
59 LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
60 IPC::ResponseBuilder rb{ctx, 2};
61 rb.Push(ERR_NOT_REGISTERED);
62 return;
63 }
64
65 const auto res = manager.GetLaunchProperty(*title_id);
66
67 if (res.Failed()) {
68 LOG_ERROR(Service_ARP, "Failed to get launch property!");
69 IPC::ResponseBuilder rb{ctx, 2};
70 rb.Push(res.Code());
71 return;
72 }
73
74 IPC::ResponseBuilder rb{ctx, 6};
75 rb.Push(RESULT_SUCCESS);
76 rb.PushRaw(*res);
77}
78
79void ARP_R::GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
80 IPC::RequestParser rp{ctx};
81 const auto title_id = rp.PopRaw<u64>();
82
83 LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
84
85 const auto res = manager.GetLaunchProperty(title_id);
86
87 if (res.Failed()) {
88 LOG_ERROR(Service_ARP, "Failed to get launch property!");
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(res.Code());
91 return;
92 }
93
94 IPC::ResponseBuilder rb{ctx, 6};
95 rb.Push(RESULT_SUCCESS);
96 rb.PushRaw(*res);
97}
98
99void ARP_R::GetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
100 IPC::RequestParser rp{ctx};
101 const auto process_id = rp.PopRaw<u64>();
102
103 LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
104
105 const auto title_id = GetTitleIDForProcessID(system, process_id);
106 if (!title_id.has_value()) {
107 LOG_ERROR(Service_ARP, "Failed to get title ID for process ID!");
108 IPC::ResponseBuilder rb{ctx, 2};
109 rb.Push(ERR_NOT_REGISTERED);
110 return;
111 }
112
113 const auto res = manager.GetControlProperty(*title_id);
114
115 if (res.Failed()) {
116 LOG_ERROR(Service_ARP, "Failed to get control property!");
117 IPC::ResponseBuilder rb{ctx, 2};
118 rb.Push(res.Code());
119 return;
120 }
121
122 ctx.WriteBuffer(*res);
123
124 IPC::ResponseBuilder rb{ctx, 2};
125 rb.Push(RESULT_SUCCESS);
126}
127
128void ARP_R::GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx) {
129 IPC::RequestParser rp{ctx};
130 const auto title_id = rp.PopRaw<u64>();
131
132 LOG_DEBUG(Service_ARP, "called, title_id={:016X}", title_id);
133
134 const auto res = manager.GetControlProperty(title_id);
135
136 if (res.Failed()) {
137 LOG_ERROR(Service_ARP, "Failed to get control property!");
138 IPC::ResponseBuilder rb{ctx, 2};
139 rb.Push(res.Code());
140 return;
141 }
142
143 ctx.WriteBuffer(*res);
144
145 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(RESULT_SUCCESS);
147}
148
149class IRegistrar final : public ServiceFramework<IRegistrar> {
150 friend class ARP_W;
151
152public:
153 explicit IRegistrar(
154 std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issuer)
155 : ServiceFramework{"IRegistrar"}, issue_process_id(std::move(issuer)) {
156 // clang-format off
157 static const FunctionInfo functions[] = {
158 {0, &IRegistrar::Issue, "Issue"},
159 {1, &IRegistrar::SetApplicationLaunchProperty, "SetApplicationLaunchProperty"},
160 {2, &IRegistrar::SetApplicationControlProperty, "SetApplicationControlProperty"},
161 };
162 // clang-format on
163
164 RegisterHandlers(functions);
165 }
166
167private:
168 void Issue(Kernel::HLERequestContext& ctx) {
169 IPC::RequestParser rp{ctx};
170 const auto process_id = rp.PopRaw<u64>();
171
172 LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
173
174 if (process_id == 0) {
175 LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
176 IPC::ResponseBuilder rb{ctx, 2};
177 rb.Push(ERR_INVALID_PROCESS_ID);
178 return;
179 }
180
181 if (issued) {
182 LOG_ERROR(Service_ARP,
183 "Attempted to issue registrar, but registrar is already issued!");
184 IPC::ResponseBuilder rb{ctx, 2};
185 rb.Push(ERR_INVALID_ACCESS);
186 return;
187 }
188
189 issue_process_id(process_id, launch, std::move(control));
190 issued = true;
191
192 IPC::ResponseBuilder rb{ctx, 2};
193 rb.Push(RESULT_SUCCESS);
194 }
195
196 void SetApplicationLaunchProperty(Kernel::HLERequestContext& ctx) {
197 LOG_DEBUG(Service_ARP, "called");
198
199 if (issued) {
200 LOG_ERROR(
201 Service_ARP,
202 "Attempted to set application launch property, but registrar is already issued!");
203 IPC::ResponseBuilder rb{ctx, 2};
204 rb.Push(ERR_INVALID_ACCESS);
205 return;
206 }
207
208 IPC::RequestParser rp{ctx};
209 launch = rp.PopRaw<ApplicationLaunchProperty>();
210
211 IPC::ResponseBuilder rb{ctx, 2};
212 rb.Push(RESULT_SUCCESS);
213 }
214
215 void SetApplicationControlProperty(Kernel::HLERequestContext& ctx) {
216 LOG_DEBUG(Service_ARP, "called");
217
218 if (issued) {
219 LOG_ERROR(
220 Service_ARP,
221 "Attempted to set application control property, but registrar is already issued!");
222 IPC::ResponseBuilder rb{ctx, 2};
223 rb.Push(ERR_INVALID_ACCESS);
224 return;
225 }
226
227 control = ctx.ReadBuffer();
228
229 IPC::ResponseBuilder rb{ctx, 2};
230 rb.Push(RESULT_SUCCESS);
231 }
232
233 std::function<ResultCode(u64, ApplicationLaunchProperty, std::vector<u8>)> issue_process_id;
234 bool issued = false;
235 ApplicationLaunchProperty launch;
236 std::vector<u8> control;
237};
238
239ARP_W::ARP_W(const Core::System& system, ARPManager& manager)
240 : ServiceFramework{"arp:w"}, system(system), manager(manager) {
241 // clang-format off
242 static const FunctionInfo functions[] = {
243 {0, &ARP_W::AcquireRegistrar, "AcquireRegistrar"},
244 {1, &ARP_W::DeleteProperties, "DeleteProperties"},
245 };
246 // clang-format on
247
248 RegisterHandlers(functions);
249}
250
251ARP_W::~ARP_W() = default;
252
253void ARP_W::AcquireRegistrar(Kernel::HLERequestContext& ctx) {
254 LOG_DEBUG(Service_ARP, "called");
255
256 registrar = std::make_shared<IRegistrar>(
257 [this](u64 process_id, ApplicationLaunchProperty launch, std::vector<u8> control) {
258 const auto res = GetTitleIDForProcessID(system, process_id);
259 if (!res.has_value()) {
260 return ERR_NOT_REGISTERED;
261 }
262
263 return manager.Register(*res, launch, std::move(control));
264 });
265
266 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
267 rb.Push(RESULT_SUCCESS);
268 rb.PushIpcInterface(registrar);
269}
270
271void ARP_W::DeleteProperties(Kernel::HLERequestContext& ctx) {
272 IPC::RequestParser rp{ctx};
273 const auto process_id = rp.PopRaw<u64>();
274
275 LOG_DEBUG(Service_ARP, "called, process_id={:016X}", process_id);
276
277 if (process_id == 0) {
278 LOG_ERROR(Service_ARP, "Must have non-zero process ID!");
279 IPC::ResponseBuilder rb{ctx, 2};
280 rb.Push(ERR_INVALID_PROCESS_ID);
281 return;
282 }
283
284 const auto title_id = GetTitleIDForProcessID(system, process_id);
285
286 if (!title_id.has_value()) {
287 LOG_ERROR(Service_ARP, "No title ID for process ID!");
288 IPC::ResponseBuilder rb{ctx, 2};
289 rb.Push(ERR_NOT_REGISTERED);
290 return;
291 }
292
293 IPC::ResponseBuilder rb{ctx, 2};
294 rb.Push(manager.Unregister(*title_id));
295}
296
297} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/arp.h b/src/core/hle/service/glue/arp.h
new file mode 100644
index 000000000..d5f8a7e7a
--- /dev/null
+++ b/src/core/hle/service/glue/arp.h
@@ -0,0 +1,43 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service::Glue {
10
11class ARPManager;
12class IRegistrar;
13
14class ARP_R final : public ServiceFramework<ARP_R> {
15public:
16 explicit ARP_R(const Core::System& system, const ARPManager& manager);
17 ~ARP_R() override;
18
19private:
20 void GetApplicationLaunchProperty(Kernel::HLERequestContext& ctx);
21 void GetApplicationLaunchPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
22 void GetApplicationControlProperty(Kernel::HLERequestContext& ctx);
23 void GetApplicationControlPropertyWithApplicationId(Kernel::HLERequestContext& ctx);
24
25 const Core::System& system;
26 const ARPManager& manager;
27};
28
29class ARP_W final : public ServiceFramework<ARP_W> {
30public:
31 explicit ARP_W(const Core::System& system, ARPManager& manager);
32 ~ARP_W() override;
33
34private:
35 void AcquireRegistrar(Kernel::HLERequestContext& ctx);
36 void DeleteProperties(Kernel::HLERequestContext& ctx);
37
38 const Core::System& system;
39 ARPManager& manager;
40 std::shared_ptr<IRegistrar> registrar;
41};
42
43} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/bgtc.cpp b/src/core/hle/service/glue/bgtc.cpp
new file mode 100644
index 000000000..cd89d088f
--- /dev/null
+++ b/src/core/hle/service/glue/bgtc.cpp
@@ -0,0 +1,50 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/glue/bgtc.h"
6
7namespace Service::Glue {
8
9BGTC_T::BGTC_T() : ServiceFramework{"bgtc:t"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {1, nullptr, "NotifyTaskStarting"},
13 {2, nullptr, "NotifyTaskFinished"},
14 {3, nullptr, "GetTriggerEvent"},
15 {4, nullptr, "IsInHalfAwake"},
16 {5, nullptr, "NotifyClientName"},
17 {6, nullptr, "IsInFullAwake"},
18 {11, nullptr, "ScheduleTask"},
19 {12, nullptr, "GetScheduledTaskInterval"},
20 {13, nullptr, "UnscheduleTask"},
21 {14, nullptr, "GetScheduleEvent"},
22 {15, nullptr, "SchedulePeriodicTask"},
23 {101, nullptr, "GetOperationMode"},
24 {102, nullptr, "WillDisconnectNetworkWhenEnteringSleep"},
25 {103, nullptr, "WillStayHalfAwakeInsteadSleep"},
26 };
27 // clang-format on
28
29 RegisterHandlers(functions);
30}
31
32BGTC_T::~BGTC_T() = default;
33
34BGTC_SC::BGTC_SC() : ServiceFramework{"bgtc:sc"} {
35 // clang-format off
36 static const FunctionInfo functions[] = {
37 {1, nullptr, "GetState"},
38 {2, nullptr, "GetStateChangedEvent"},
39 {3, nullptr, "NotifyEnteringHalfAwake"},
40 {4, nullptr, "NotifyLeavingHalfAwake"},
41 {5, nullptr, "SetIsUsingSleepUnsupportedDevices"},
42 };
43 // clang-format on
44
45 RegisterHandlers(functions);
46}
47
48BGTC_SC::~BGTC_SC() = default;
49
50} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/bgtc.h b/src/core/hle/service/glue/bgtc.h
new file mode 100644
index 000000000..81844f03e
--- /dev/null
+++ b/src/core/hle/service/glue/bgtc.h
@@ -0,0 +1,23 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/service/service.h"
8
9namespace Service::Glue {
10
11class BGTC_T final : public ServiceFramework<BGTC_T> {
12public:
13 BGTC_T();
14 ~BGTC_T() override;
15};
16
17class BGTC_SC final : public ServiceFramework<BGTC_SC> {
18public:
19 BGTC_SC();
20 ~BGTC_SC() override;
21};
22
23} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/errors.h b/src/core/hle/service/glue/errors.h
new file mode 100644
index 000000000..c2874c585
--- /dev/null
+++ b/src/core/hle/service/glue/errors.h
@@ -0,0 +1,16 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/hle/result.h"
8
9namespace Service::Glue {
10
11constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 0x1E};
12constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 0x1F};
13constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 0x2A};
14constexpr ResultCode ERR_NOT_REGISTERED{ErrorModule::ARP, 0x66};
15
16} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
new file mode 100644
index 000000000..c728e815c
--- /dev/null
+++ b/src/core/hle/service/glue/glue.cpp
@@ -0,0 +1,25 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "core/core.h"
7#include "core/hle/service/glue/arp.h"
8#include "core/hle/service/glue/bgtc.h"
9#include "core/hle/service/glue/glue.h"
10
11namespace Service::Glue {
12
13void InstallInterfaces(Core::System& system) {
14 // ARP
15 std::make_shared<ARP_R>(system, system.GetARPManager())
16 ->InstallAsService(system.ServiceManager());
17 std::make_shared<ARP_W>(system, system.GetARPManager())
18 ->InstallAsService(system.ServiceManager());
19
20 // BackGround Task Controller
21 std::make_shared<BGTC_T>()->InstallAsService(system.ServiceManager());
22 std::make_shared<BGTC_SC>()->InstallAsService(system.ServiceManager());
23}
24
25} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/glue.h b/src/core/hle/service/glue/glue.h
new file mode 100644
index 000000000..112cd238b
--- /dev/null
+++ b/src/core/hle/service/glue/glue.h
@@ -0,0 +1,16 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace Core {
8class System;
9} // namespace Core
10
11namespace Service::Glue {
12
13/// Registers all Glue services with the specified service manager.
14void InstallInterfaces(Core::System& system);
15
16} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/manager.cpp b/src/core/hle/service/glue/manager.cpp
new file mode 100644
index 000000000..6da52d2d6
--- /dev/null
+++ b/src/core/hle/service/glue/manager.cpp
@@ -0,0 +1,78 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hle/service/glue/errors.h"
6#include "core/hle/service/glue/manager.h"
7
8namespace Service::Glue {
9
10struct ARPManager::MapEntry {
11 ApplicationLaunchProperty launch;
12 std::vector<u8> control;
13};
14
15ARPManager::ARPManager() = default;
16
17ARPManager::~ARPManager() = default;
18
19ResultVal<ApplicationLaunchProperty> ARPManager::GetLaunchProperty(u64 title_id) const {
20 if (title_id == 0) {
21 return ERR_INVALID_PROCESS_ID;
22 }
23
24 const auto iter = entries.find(title_id);
25 if (iter == entries.end()) {
26 return ERR_NOT_REGISTERED;
27 }
28
29 return MakeResult<ApplicationLaunchProperty>(iter->second.launch);
30}
31
32ResultVal<std::vector<u8>> ARPManager::GetControlProperty(u64 title_id) const {
33 if (title_id == 0) {
34 return ERR_INVALID_PROCESS_ID;
35 }
36
37 const auto iter = entries.find(title_id);
38 if (iter == entries.end()) {
39 return ERR_NOT_REGISTERED;
40 }
41
42 return MakeResult<std::vector<u8>>(iter->second.control);
43}
44
45ResultCode ARPManager::Register(u64 title_id, ApplicationLaunchProperty launch,
46 std::vector<u8> control) {
47 if (title_id == 0) {
48 return ERR_INVALID_PROCESS_ID;
49 }
50
51 const auto iter = entries.find(title_id);
52 if (iter != entries.end()) {
53 return ERR_INVALID_ACCESS;
54 }
55
56 entries.insert_or_assign(title_id, MapEntry{launch, std::move(control)});
57 return RESULT_SUCCESS;
58}
59
60ResultCode ARPManager::Unregister(u64 title_id) {
61 if (title_id == 0) {
62 return ERR_INVALID_PROCESS_ID;
63 }
64
65 const auto iter = entries.find(title_id);
66 if (iter == entries.end()) {
67 return ERR_NOT_REGISTERED;
68 }
69
70 entries.erase(iter);
71 return RESULT_SUCCESS;
72}
73
74void ARPManager::ResetAll() {
75 entries.clear();
76}
77
78} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/manager.h b/src/core/hle/service/glue/manager.h
new file mode 100644
index 000000000..a7f5ce3ee
--- /dev/null
+++ b/src/core/hle/service/glue/manager.h
@@ -0,0 +1,63 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8#include <vector>
9#include "common/common_types.h"
10#include "core/file_sys/control_metadata.h"
11#include "core/file_sys/romfs_factory.h"
12#include "core/hle/result.h"
13
14namespace Service::Glue {
15
16struct ApplicationLaunchProperty {
17 u64 title_id;
18 u32 version;
19 FileSys::StorageId base_game_storage_id;
20 FileSys::StorageId update_storage_id;
21 u8 program_index;
22 u8 reserved;
23};
24static_assert(sizeof(ApplicationLaunchProperty) == 0x10,
25 "ApplicationLaunchProperty has incorrect size.");
26
27// A class to manage state related to the arp:w and arp:r services, specifically the registration
28// and unregistration of launch and control properties.
29class ARPManager {
30public:
31 ARPManager();
32 ~ARPManager();
33
34 // Returns the ApplicationLaunchProperty corresponding to the provided title ID if it was
35 // previously registered, otherwise ERR_NOT_REGISTERED if it was never registered or
36 // ERR_INVALID_PROCESS_ID if the title ID is 0.
37 ResultVal<ApplicationLaunchProperty> GetLaunchProperty(u64 title_id) const;
38
39 // Returns a vector of the raw bytes of NACP data (necessarily 0x4000 in size) corresponding to
40 // the provided title ID if it was previously registered, otherwise ERR_NOT_REGISTERED if it was
41 // never registered or ERR_INVALID_PROCESS_ID if the title ID is 0.
42 ResultVal<std::vector<u8>> GetControlProperty(u64 title_id) const;
43
44 // Adds a new entry to the internal database with the provided parameters, returning
45 // ERR_INVALID_ACCESS if attempting to re-register a title ID without an intermediate Unregister
46 // step, and ERR_INVALID_PROCESS_ID if the title ID is 0.
47 ResultCode Register(u64 title_id, ApplicationLaunchProperty launch, std::vector<u8> control);
48
49 // Removes the registration for the provided title ID from the database, returning
50 // ERR_NOT_REGISTERED if it doesn't exist in the database and ERR_INVALID_PROCESS_ID if the
51 // title ID is 0.
52 ResultCode Unregister(u64 title_id);
53
54 // Removes all entries from the database, always succeeds. Should only be used when resetting
55 // system state.
56 void ResetAll();
57
58private:
59 struct MapEntry;
60 std::map<u64, MapEntry> entries;
61};
62
63} // namespace Service::Glue
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index e4fcee9f8..7e134f5c1 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -2,10 +2,18 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <json.hpp>
6#include "common/file_util.h"
7#include "common/hex_util.h"
5#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "common/scm_rev.h"
6#include "core/hle/ipc_helpers.h" 10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/process.h"
12#include "core/hle/service/acc/profile_manager.h"
7#include "core/hle/service/prepo/prepo.h" 13#include "core/hle/service/prepo/prepo.h"
8#include "core/hle/service/service.h" 14#include "core/hle/service/service.h"
15#include "core/reporter.h"
16#include "core/settings.h"
9 17
10namespace Service::PlayReport { 18namespace Service::PlayReport {
11 19
@@ -40,8 +48,21 @@ public:
40 48
41private: 49private:
42 void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) { 50 void SaveReportWithUserOld(Kernel::HLERequestContext& ctx) {
43 // TODO(ogniK): Do we want to add play report? 51 IPC::RequestParser rp{ctx};
44 LOG_WARNING(Service_PREPO, "(STUBBED) called"); 52 const auto user_id = rp.PopRaw<u128>();
53 const auto process_id = rp.PopRaw<u64>();
54
55 const auto data1 = ctx.ReadBuffer(0);
56 const auto data2 = ctx.ReadBuffer(1);
57
58 LOG_DEBUG(
59 Service_PREPO,
60 "called, user_id={:016X}{:016X}, unk1={:016X}, data1_size={:016X}, data2_size={:016X}",
61 user_id[1], user_id[0], process_id, data1.size(), data2.size());
62
63 const auto& reporter{Core::System::GetInstance().GetReporter()};
64 reporter.SavePlayReport(Core::CurrentProcess()->GetTitleID(), process_id, {data1, data2},
65 user_id);
45 66
46 IPC::ResponseBuilder rb{ctx, 2}; 67 IPC::ResponseBuilder rb{ctx, 2};
47 rb.Push(RESULT_SUCCESS); 68 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 6c69f899e..5fc7d3cab 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -19,7 +19,6 @@
19#include "core/hle/service/am/am.h" 19#include "core/hle/service/am/am.h"
20#include "core/hle/service/aoc/aoc_u.h" 20#include "core/hle/service/aoc/aoc_u.h"
21#include "core/hle/service/apm/apm.h" 21#include "core/hle/service/apm/apm.h"
22#include "core/hle/service/arp/arp.h"
23#include "core/hle/service/audio/audio.h" 22#include "core/hle/service/audio/audio.h"
24#include "core/hle/service/bcat/module.h" 23#include "core/hle/service/bcat/module.h"
25#include "core/hle/service/bpc/bpc.h" 24#include "core/hle/service/bpc/bpc.h"
@@ -33,6 +32,7 @@
33#include "core/hle/service/fgm/fgm.h" 32#include "core/hle/service/fgm/fgm.h"
34#include "core/hle/service/filesystem/filesystem.h" 33#include "core/hle/service/filesystem/filesystem.h"
35#include "core/hle/service/friend/friend.h" 34#include "core/hle/service/friend/friend.h"
35#include "core/hle/service/glue/glue.h"
36#include "core/hle/service/grc/grc.h" 36#include "core/hle/service/grc/grc.h"
37#include "core/hle/service/hid/hid.h" 37#include "core/hle/service/hid/hid.h"
38#include "core/hle/service/lbl/lbl.h" 38#include "core/hle/service/lbl/lbl.h"
@@ -68,6 +68,7 @@
68#include "core/hle/service/usb/usb.h" 68#include "core/hle/service/usb/usb.h"
69#include "core/hle/service/vi/vi.h" 69#include "core/hle/service/vi/vi.h"
70#include "core/hle/service/wlan/wlan.h" 70#include "core/hle/service/wlan/wlan.h"
71#include "core/reporter.h"
71 72
72namespace Service { 73namespace Service {
73 74
@@ -148,6 +149,8 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
148 } 149 }
149 buf.push_back('}'); 150 buf.push_back('}');
150 151
152 Core::System::GetInstance().GetReporter().SaveUnimplementedFunctionReport(
153 ctx, ctx.GetCommand(), function_name, service_name);
151 UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); 154 UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
152} 155}
153 156
@@ -201,10 +204,9 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
201 SM::ServiceManager::InstallInterfaces(sm); 204 SM::ServiceManager::InstallInterfaces(sm);
202 205
203 Account::InstallInterfaces(system); 206 Account::InstallInterfaces(system);
204 AM::InstallInterfaces(*sm, nv_flinger); 207 AM::InstallInterfaces(*sm, nv_flinger, system);
205 AOC::InstallInterfaces(*sm); 208 AOC::InstallInterfaces(*sm);
206 APM::InstallInterfaces(*sm); 209 APM::InstallInterfaces(*sm);
207 ARP::InstallInterfaces(*sm);
208 Audio::InstallInterfaces(*sm); 210 Audio::InstallInterfaces(*sm);
209 BCAT::InstallInterfaces(*sm); 211 BCAT::InstallInterfaces(*sm);
210 BPC::InstallInterfaces(*sm); 212 BPC::InstallInterfaces(*sm);
@@ -218,6 +220,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
218 FGM::InstallInterfaces(*sm); 220 FGM::InstallInterfaces(*sm);
219 FileSystem::InstallInterfaces(*sm, vfs); 221 FileSystem::InstallInterfaces(*sm, vfs);
220 Friend::InstallInterfaces(*sm); 222 Friend::InstallInterfaces(*sm);
223 Glue::InstallInterfaces(system);
221 GRC::InstallInterfaces(*sm); 224 GRC::InstallInterfaces(*sm);
222 HID::InstallInterfaces(*sm); 225 HID::InstallInterfaces(*sm);
223 LBL::InstallInterfaces(*sm); 226 LBL::InstallInterfaces(*sm);
@@ -246,7 +249,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
246 Sockets::InstallInterfaces(*sm); 249 Sockets::InstallInterfaces(*sm);
247 SPL::InstallInterfaces(*sm); 250 SPL::InstallInterfaces(*sm);
248 SSL::InstallInterfaces(*sm); 251 SSL::InstallInterfaces(*sm);
249 Time::InstallInterfaces(*sm); 252 Time::InstallInterfaces(system);
250 USB::InstallInterfaces(*sm); 253 USB::InstallInterfaces(*sm);
251 VI::InstallInterfaces(*sm, nv_flinger); 254 VI::InstallInterfaces(*sm, nv_flinger);
252 WLAN::InstallInterfaces(*sm); 255 WLAN::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 298d85011..b54214421 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -95,6 +95,14 @@ void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
95 PushResponseLanguageCode(ctx, post4_0_0_max_entries); 95 PushResponseLanguageCode(ctx, post4_0_0_max_entries);
96} 96}
97 97
98void SET::GetQuestFlag(Kernel::HLERequestContext& ctx) {
99 LOG_DEBUG(Service_SET, "called");
100
101 IPC::ResponseBuilder rb{ctx, 3};
102 rb.Push(RESULT_SUCCESS);
103 rb.Push(static_cast<u32>(Settings::values.quest_flag));
104}
105
98void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { 106void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
99 LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index); 107 LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
100 108
@@ -114,7 +122,7 @@ SET::SET() : ServiceFramework("set") {
114 {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"}, 122 {5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
115 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"}, 123 {6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
116 {7, nullptr, "GetKeyCodeMap"}, 124 {7, nullptr, "GetKeyCodeMap"},
117 {8, nullptr, "GetQuestFlag"}, 125 {8, &SET::GetQuestFlag, "GetQuestFlag"},
118 {9, nullptr, "GetKeyCodeMap2"}, 126 {9, nullptr, "GetKeyCodeMap2"},
119 }; 127 };
120 // clang-format on 128 // clang-format on
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 31f9cb296..b154e08aa 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -42,6 +42,7 @@ private:
42 void GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx); 42 void GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx);
43 void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx); 43 void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
44 void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx); 44 void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
45 void GetQuestFlag(Kernel::HLERequestContext& ctx);
45}; 46};
46 47
47} // namespace Service::Set 48} // namespace Service::Set
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 8d122ae33..1030185e0 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -6,8 +6,9 @@
6 6
7namespace Service::Time { 7namespace Service::Time {
8 8
9Time::Time(std::shared_ptr<Module> time, const char* name) 9Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
10 : Module::Interface(std::move(time), name) { 10 const char* name)
11 : Module::Interface(std::move(time), std::move(shared_memory), name) {
11 // clang-format off 12 // clang-format off
12 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
13 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, 14 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
@@ -16,12 +17,12 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
16 {3, &Time::GetTimeZoneService, "GetTimeZoneService"}, 17 {3, &Time::GetTimeZoneService, "GetTimeZoneService"},
17 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"}, 18 {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
18 {5, nullptr, "GetEphemeralNetworkSystemClock"}, 19 {5, nullptr, "GetEphemeralNetworkSystemClock"},
19 {20, nullptr, "GetSharedMemoryNativeHandle"}, 20 {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
20 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"}, 21 {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
21 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"}, 22 {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
22 {50, nullptr, "SetStandardSteadyClockInternalOffset"}, 23 {50, nullptr, "SetStandardSteadyClockInternalOffset"},
23 {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"}, 24 {100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
24 {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"}, 25 {101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
25 {102, nullptr, "GetStandardUserSystemClockInitialYear"}, 26 {102, nullptr, "GetStandardUserSystemClockInitialYear"},
26 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"}, 27 {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
27 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"}, 28 {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index cd6b44dec..bdf0883e2 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -8,9 +8,12 @@
8 8
9namespace Service::Time { 9namespace Service::Time {
10 10
11class SharedMemory;
12
11class Time final : public Module::Interface { 13class Time final : public Module::Interface {
12public: 14public:
13 explicit Time(std::shared_ptr<Module> time, const char* name); 15 explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
16 const char* name);
14 ~Time() override; 17 ~Time() override;
15}; 18};
16 19
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 346bad80d..ae6446204 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -13,6 +13,7 @@
13#include "core/hle/kernel/client_session.h" 13#include "core/hle/kernel/client_session.h"
14#include "core/hle/service/time/interface.h" 14#include "core/hle/service/time/interface.h"
15#include "core/hle/service/time/time.h" 15#include "core/hle/service/time/time.h"
16#include "core/hle/service/time/time_sharedmemory.h"
16#include "core/settings.h" 17#include "core/settings.h"
17 18
18namespace Service::Time { 19namespace Service::Time {
@@ -61,9 +62,18 @@ static u64 CalendarToPosix(const CalendarTime& calendar_time,
61 return static_cast<u64>(epoch_time); 62 return static_cast<u64>(epoch_time);
62} 63}
63 64
65enum class ClockContextType {
66 StandardSteady,
67 StandardUserSystem,
68 StandardNetworkSystem,
69 StandardLocalSystem,
70};
71
64class ISystemClock final : public ServiceFramework<ISystemClock> { 72class ISystemClock final : public ServiceFramework<ISystemClock> {
65public: 73public:
66 ISystemClock() : ServiceFramework("ISystemClock") { 74 ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory,
75 ClockContextType clock_type)
76 : ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) {
67 static const FunctionInfo functions[] = { 77 static const FunctionInfo functions[] = {
68 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"}, 78 {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
69 {1, nullptr, "SetCurrentTime"}, 79 {1, nullptr, "SetCurrentTime"},
@@ -72,6 +82,8 @@ public:
72 82
73 }; 83 };
74 RegisterHandlers(functions); 84 RegisterHandlers(functions);
85
86 UpdateSharedMemoryContext(system_clock_context);
75 } 87 }
76 88
77private: 89private:
@@ -87,34 +99,63 @@ private:
87 void GetSystemClockContext(Kernel::HLERequestContext& ctx) { 99 void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
88 LOG_WARNING(Service_Time, "(STUBBED) called"); 100 LOG_WARNING(Service_Time, "(STUBBED) called");
89 101
90 SystemClockContext system_clock_ontext{}; 102 // TODO(ogniK): This should be updated periodically however since we have it stubbed we'll
103 // only update when we get a new context
104 UpdateSharedMemoryContext(system_clock_context);
105
91 IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2}; 106 IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
92 rb.Push(RESULT_SUCCESS); 107 rb.Push(RESULT_SUCCESS);
93 rb.PushRaw(system_clock_ontext); 108 rb.PushRaw(system_clock_context);
94 } 109 }
110
111 void UpdateSharedMemoryContext(const SystemClockContext& clock_context) {
112 switch (clock_type) {
113 case ClockContextType::StandardLocalSystem:
114 shared_memory->SetStandardLocalSystemClockContext(clock_context);
115 break;
116 case ClockContextType::StandardNetworkSystem:
117 shared_memory->SetStandardNetworkSystemClockContext(clock_context);
118 break;
119 }
120 }
121
122 SystemClockContext system_clock_context{};
123 std::shared_ptr<Service::Time::SharedMemory> shared_memory;
124 ClockContextType clock_type;
95}; 125};
96 126
97class ISteadyClock final : public ServiceFramework<ISteadyClock> { 127class ISteadyClock final : public ServiceFramework<ISteadyClock> {
98public: 128public:
99 ISteadyClock() : ServiceFramework("ISteadyClock") { 129 ISteadyClock(std::shared_ptr<SharedMemory> shared_memory)
130 : ServiceFramework("ISteadyClock"), shared_memory(shared_memory) {
100 static const FunctionInfo functions[] = { 131 static const FunctionInfo functions[] = {
101 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, 132 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
102 }; 133 };
103 RegisterHandlers(functions); 134 RegisterHandlers(functions);
135
136 shared_memory->SetStandardSteadyClockTimepoint(GetCurrentTimePoint());
104 } 137 }
105 138
106private: 139private:
107 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) { 140 void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
108 LOG_DEBUG(Service_Time, "called"); 141 LOG_DEBUG(Service_Time, "called");
109 142
110 const auto& core_timing = Core::System::GetInstance().CoreTiming(); 143 const auto time_point = GetCurrentTimePoint();
111 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); 144 // TODO(ogniK): This should be updated periodically
112 const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), 145 shared_memory->SetStandardSteadyClockTimepoint(time_point);
113 {}}; 146
114 IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2}; 147 IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
115 rb.Push(RESULT_SUCCESS); 148 rb.Push(RESULT_SUCCESS);
116 rb.PushRaw(steady_clock_time_point); 149 rb.PushRaw(time_point);
117 } 150 }
151
152 SteadyClockTimePoint GetCurrentTimePoint() const {
153 const auto& core_timing = Core::System::GetInstance().CoreTiming();
154 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
155 return {static_cast<u64_le>(ms.count() / 1000), {}};
156 }
157
158 std::shared_ptr<SharedMemory> shared_memory;
118}; 159};
119 160
120class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { 161class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
@@ -233,7 +274,7 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
233 274
234 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 275 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
235 rb.Push(RESULT_SUCCESS); 276 rb.Push(RESULT_SUCCESS);
236 rb.PushIpcInterface<ISystemClock>(); 277 rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem);
237} 278}
238 279
239void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { 280void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
@@ -241,7 +282,7 @@ void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext&
241 282
242 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 283 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
243 rb.Push(RESULT_SUCCESS); 284 rb.Push(RESULT_SUCCESS);
244 rb.PushIpcInterface<ISystemClock>(); 285 rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem);
245} 286}
246 287
247void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { 288void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
@@ -249,7 +290,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
249 290
250 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 291 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
251 rb.Push(RESULT_SUCCESS); 292 rb.Push(RESULT_SUCCESS);
252 rb.PushIpcInterface<ISteadyClock>(); 293 rb.PushIpcInterface<ISteadyClock>(shared_memory);
253} 294}
254 295
255void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { 296void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
@@ -265,7 +306,7 @@ void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& c
265 306
266 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 307 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
267 rb.Push(RESULT_SUCCESS); 308 rb.Push(RESULT_SUCCESS);
268 rb.PushIpcInterface<ISystemClock>(); 309 rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem);
269} 310}
270 311
271void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { 312void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
@@ -333,16 +374,52 @@ void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
333 rb.PushRaw<u64>(difference); 374 rb.PushRaw<u64>(difference);
334} 375}
335 376
336Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) 377void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
337 : ServiceFramework(name), time(std::move(time)) {} 378 LOG_DEBUG(Service_Time, "called");
379 IPC::ResponseBuilder rb{ctx, 2, 1};
380 rb.Push(RESULT_SUCCESS);
381 rb.PushCopyObjects(shared_memory->GetSharedMemoryHolder());
382}
383
384void Module::Interface::IsStandardUserSystemClockAutomaticCorrectionEnabled(
385 Kernel::HLERequestContext& ctx) {
386 // ogniK(TODO): When clock contexts are implemented, the value should be read from the context
387 // instead of our shared memory holder
388 LOG_DEBUG(Service_Time, "called");
389
390 IPC::ResponseBuilder rb{ctx, 3};
391 rb.Push(RESULT_SUCCESS);
392 rb.Push<u8>(shared_memory->GetStandardUserSystemClockAutomaticCorrectionEnabled());
393}
394
395void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
396 Kernel::HLERequestContext& ctx) {
397 IPC::RequestParser rp{ctx};
398 const auto enabled = rp.Pop<u8>();
399
400 LOG_WARNING(Service_Time, "(PARTIAL IMPLEMENTATION) called");
401
402 // TODO(ogniK): Update clock contexts and correct timespans
403
404 shared_memory->SetStandardUserSystemClockAutomaticCorrectionEnabled(enabled > 0);
405 IPC::ResponseBuilder rb{ctx, 2};
406 rb.Push(RESULT_SUCCESS);
407}
408
409Module::Interface::Interface(std::shared_ptr<Module> time,
410 std::shared_ptr<SharedMemory> shared_memory, const char* name)
411 : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)) {}
338 412
339Module::Interface::~Interface() = default; 413Module::Interface::~Interface() = default;
340 414
341void InstallInterfaces(SM::ServiceManager& service_manager) { 415void InstallInterfaces(Core::System& system) {
342 auto time = std::make_shared<Module>(); 416 auto time = std::make_shared<Module>();
343 std::make_shared<Time>(time, "time:a")->InstallAsService(service_manager); 417 auto shared_mem = std::make_shared<SharedMemory>(system);
344 std::make_shared<Time>(time, "time:s")->InstallAsService(service_manager); 418
345 std::make_shared<Time>(time, "time:u")->InstallAsService(service_manager); 419 std::make_shared<Time>(time, shared_mem, "time:a")->InstallAsService(system.ServiceManager());
420 std::make_shared<Time>(time, shared_mem, "time:s")->InstallAsService(system.ServiceManager());
421 std::make_shared<Time>(std::move(time), shared_mem, "time:u")
422 ->InstallAsService(system.ServiceManager());
346} 423}
347 424
348} // namespace Service::Time 425} // namespace Service::Time
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index f11affe95..e0708f856 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -10,6 +10,8 @@
10 10
11namespace Service::Time { 11namespace Service::Time {
12 12
13class SharedMemory;
14
13struct LocationName { 15struct LocationName {
14 std::array<u8, 0x24> name; 16 std::array<u8, 0x24> name;
15}; 17};
@@ -77,7 +79,8 @@ class Module final {
77public: 79public:
78 class Interface : public ServiceFramework<Interface> { 80 class Interface : public ServiceFramework<Interface> {
79 public: 81 public:
80 explicit Interface(std::shared_ptr<Module> time, const char* name); 82 explicit Interface(std::shared_ptr<Module> time,
83 std::shared_ptr<SharedMemory> shared_memory, const char* name);
81 ~Interface() override; 84 ~Interface() override;
82 85
83 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); 86 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
@@ -87,13 +90,17 @@ public:
87 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx); 90 void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
88 void GetClockSnapshot(Kernel::HLERequestContext& ctx); 91 void GetClockSnapshot(Kernel::HLERequestContext& ctx);
89 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx); 92 void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
93 void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
94 void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
95 void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
90 96
91 protected: 97 protected:
92 std::shared_ptr<Module> time; 98 std::shared_ptr<Module> time;
99 std::shared_ptr<SharedMemory> shared_memory;
93 }; 100 };
94}; 101};
95 102
96/// Registers all Time services with the specified service manager. 103/// Registers all Time services with the specified service manager.
97void InstallInterfaces(SM::ServiceManager& service_manager); 104void InstallInterfaces(Core::System& system);
98 105
99} // namespace Service::Time 106} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
new file mode 100644
index 000000000..bfc81b83c
--- /dev/null
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -0,0 +1,68 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/core.h"
6#include "core/hle/service/time/time_sharedmemory.h"
7
8namespace Service::Time {
9const std::size_t SHARED_MEMORY_SIZE = 0x1000;
10
11SharedMemory::SharedMemory(Core::System& system) : system(system) {
12 shared_memory_holder = Kernel::SharedMemory::Create(
13 system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
14 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory");
15
16 // Seems static from 1.0.0 -> 8.1.0. Specific games seem to check this value and crash
17 // if it's set to anything else
18 shared_memory_format.format_version = 14;
19 std::memcpy(shared_memory_holder->GetPointer(), &shared_memory_format, sizeof(Format));
20}
21
22SharedMemory::~SharedMemory() = default;
23
24Kernel::SharedPtr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() const {
25 return shared_memory_holder;
26}
27
28void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) {
29 shared_memory_format.standard_steady_clock_timepoint.StoreData(
30 shared_memory_holder->GetPointer(), timepoint);
31}
32
33void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) {
34 shared_memory_format.standard_local_system_clock_context.StoreData(
35 shared_memory_holder->GetPointer(), context);
36}
37
38void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) {
39 shared_memory_format.standard_network_system_clock_context.StoreData(
40 shared_memory_holder->GetPointer(), context);
41}
42
43void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) {
44 shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
45 shared_memory_holder->GetPointer(), enabled);
46}
47
48SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() {
49 return shared_memory_format.standard_steady_clock_timepoint.ReadData(
50 shared_memory_holder->GetPointer());
51}
52
53SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() {
54 return shared_memory_format.standard_local_system_clock_context.ReadData(
55 shared_memory_holder->GetPointer());
56}
57
58SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() {
59 return shared_memory_format.standard_network_system_clock_context.ReadData(
60 shared_memory_holder->GetPointer());
61}
62
63bool SharedMemory::GetStandardUserSystemClockAutomaticCorrectionEnabled() {
64 return shared_memory_format.standard_user_system_clock_automatic_correction.ReadData(
65 shared_memory_holder->GetPointer());
66}
67
68} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
new file mode 100644
index 000000000..cb8253541
--- /dev/null
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -0,0 +1,74 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/kernel/shared_memory.h"
9#include "core/hle/service/time/time.h"
10
11namespace Service::Time {
12class SharedMemory {
13public:
14 explicit SharedMemory(Core::System& system);
15 ~SharedMemory();
16
17 // Return the shared memory handle
18 Kernel::SharedPtr<Kernel::SharedMemory> GetSharedMemoryHolder() const;
19
20 // Set memory barriers in shared memory and update them
21 void SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint);
22 void SetStandardLocalSystemClockContext(const SystemClockContext& context);
23 void SetStandardNetworkSystemClockContext(const SystemClockContext& context);
24 void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled);
25
26 // Pull from memory barriers in the shared memory
27 SteadyClockTimePoint GetStandardSteadyClockTimepoint();
28 SystemClockContext GetStandardLocalSystemClockContext();
29 SystemClockContext GetStandardNetworkSystemClockContext();
30 bool GetStandardUserSystemClockAutomaticCorrectionEnabled();
31
32 // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
33 template <typename T, std::size_t Offset>
34 struct MemoryBarrier {
35 static_assert(std::is_trivially_constructible_v<T>, "T must be trivially constructable");
36 u32_le read_attempt{};
37 std::array<T, 2> data{};
38
39 // These are not actually memory barriers at the moment as we don't have multicore and all
40 // HLE is mutexed. This will need to properly be implemented when we start updating the time
41 // points on threads. As of right now, we'll be updated both values synchronously and just
42 // incrementing the read_attempt to indicate that we waited.
43 void StoreData(u8* shared_memory, T data_to_store) {
44 std::memcpy(this, shared_memory + Offset, sizeof(*this));
45 read_attempt++;
46 data[read_attempt & 1] = data_to_store;
47 std::memcpy(shared_memory + Offset, this, sizeof(*this));
48 }
49
50 // For reading we're just going to read the last stored value. If there was no value stored
51 // it will just end up reading an empty value as intended.
52 T ReadData(u8* shared_memory) {
53 std::memcpy(this, shared_memory + Offset, sizeof(*this));
54 return data[(read_attempt - 1) & 1];
55 }
56 };
57
58 // Shared memory format
59 struct Format {
60 MemoryBarrier<SteadyClockTimePoint, 0x0> standard_steady_clock_timepoint;
61 MemoryBarrier<SystemClockContext, 0x38> standard_local_system_clock_context;
62 MemoryBarrier<SystemClockContext, 0x80> standard_network_system_clock_context;
63 MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
64 u32_le format_version;
65 };
66 static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
67
68private:
69 Kernel::SharedPtr<Kernel::SharedMemory> shared_memory_holder{};
70 Core::System& system;
71 Format shared_memory_format{};
72};
73
74} // namespace Service::Time
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 10b13fb1d..f9e88be2b 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -141,6 +141,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
141 const FileSys::PatchManager pm(metadata.GetTitleID()); 141 const FileSys::PatchManager pm(metadata.GetTitleID());
142 142
143 // Load NSO modules 143 // Load NSO modules
144 modules.clear();
144 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 145 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
145 VAddr next_load_addr = base_address; 146 VAddr next_load_addr = base_address;
146 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 147 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
@@ -159,6 +160,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
159 } 160 }
160 161
161 next_load_addr = *tentative_next_load_addr; 162 next_load_addr = *tentative_next_load_addr;
163 modules.insert_or_assign(load_addr, module);
162 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 164 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
163 // Register module with GDBStub 165 // Register module with GDBStub
164 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); 166 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
@@ -212,4 +214,13 @@ bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
212 return false; 214 return false;
213} 215}
214 216
217ResultStatus AppLoader_DeconstructedRomDirectory::ReadNSOModules(Modules& modules) {
218 if (!is_loaded) {
219 return ResultStatus::ErrorNotInitialized;
220 }
221
222 modules = this->modules;
223 return ResultStatus::Success;
224}
225
215} // namespace Loader 226} // namespace Loader
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1a65c16a4..1c0a354a4 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -45,6 +45,8 @@ public:
45 ResultStatus ReadTitle(std::string& title) override; 45 ResultStatus ReadTitle(std::string& title) override;
46 bool IsRomFSUpdatable() const override; 46 bool IsRomFSUpdatable() const override;
47 47
48 ResultStatus ReadNSOModules(Modules& modules) override;
49
48private: 50private:
49 FileSys::ProgramMetadata metadata; 51 FileSys::ProgramMetadata metadata;
50 FileSys::VirtualFile romfs; 52 FileSys::VirtualFile romfs;
@@ -54,6 +56,8 @@ private:
54 std::string name; 56 std::string name;
55 u64 title_id{}; 57 u64 title_id{};
56 bool override_update; 58 bool override_update;
59
60 Modules modules;
57}; 61};
58 62
59} // namespace Loader 63} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
new file mode 100644
index 000000000..70051c13a
--- /dev/null
+++ b/src/core/loader/kip.cpp
@@ -0,0 +1,102 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/file_sys/kernel_executable.h"
6#include "core/file_sys/program_metadata.h"
7#include "core/gdbstub/gdbstub.h"
8#include "core/hle/kernel/code_set.h"
9#include "core/hle/kernel/process.h"
10#include "core/loader/kip.h"
11
12namespace Loader {
13
14namespace {
15constexpr u32 PageAlignSize(u32 size) {
16 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
17}
18} // Anonymous namespace
19
20AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
21 : AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
22
23AppLoader_KIP::~AppLoader_KIP() = default;
24
25FileType AppLoader_KIP::IdentifyType(const FileSys::VirtualFile& file) {
26 u32_le magic{};
27 if (file->GetSize() < sizeof(u32) || file->ReadObject(&magic) != sizeof(u32)) {
28 return FileType::Error;
29 }
30
31 if (magic == Common::MakeMagic('K', 'I', 'P', '1')) {
32 return FileType::KIP;
33 }
34
35 return FileType::Error;
36}
37
38FileType AppLoader_KIP::GetFileType() const {
39 return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP
40 : FileType::Error;
41}
42
43AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
44 if (is_loaded) {
45 return {ResultStatus::ErrorAlreadyLoaded, {}};
46 }
47
48 if (kip == nullptr) {
49 return {ResultStatus::ErrorNullFile, {}};
50 }
51
52 if (kip->GetStatus() != ResultStatus::Success) {
53 return {kip->GetStatus(), {}};
54 }
55
56 const auto get_kip_address_space_type = [](const auto& kip) {
57 return kip.Is64Bit()
58 ? (kip.Is39BitAddressSpace() ? FileSys::ProgramAddressSpaceType::Is39Bit
59 : FileSys::ProgramAddressSpaceType::Is36Bit)
60 : FileSys::ProgramAddressSpaceType::Is32Bit;
61 };
62
63 const auto address_space = get_kip_address_space_type(*kip);
64
65 FileSys::ProgramMetadata metadata;
66 metadata.LoadManual(kip->Is64Bit(), address_space, kip->GetMainThreadPriority(),
67 kip->GetMainThreadCpuCore(), kip->GetMainThreadStackSize(),
68 kip->GetTitleID(), 0xFFFFFFFFFFFFFFFF, kip->GetKernelCapabilities());
69
70 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
71 Kernel::CodeSet codeset;
72 std::vector<u8> program_image;
73
74 const auto load_segment = [&program_image](Kernel::CodeSet::Segment& segment,
75 const std::vector<u8>& data, u32 offset) {
76 segment.addr = offset;
77 segment.offset = offset;
78 segment.size = PageAlignSize(static_cast<u32>(data.size()));
79 program_image.resize(offset);
80 program_image.insert(program_image.end(), data.begin(), data.end());
81 };
82
83 load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());
84 load_segment(codeset.RODataSegment(), kip->GetRODataSection(), kip->GetRODataOffset());
85 load_segment(codeset.DataSegment(), kip->GetDataSection(), kip->GetDataOffset());
86
87 program_image.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize());
88 codeset.DataSegment().size += kip->GetBSSSize();
89
90 GDBStub::RegisterModule(kip->GetName(), base_address, base_address + program_image.size());
91
92 codeset.memory = std::move(program_image);
93 process.LoadModule(std::move(codeset), base_address);
94
95 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address);
96
97 is_loaded = true;
98 return {ResultStatus::Success,
99 LoadParameters{kip->GetMainThreadPriority(), kip->GetMainThreadStackSize()}};
100}
101
102} // namespace Loader
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h
new file mode 100644
index 000000000..12ca40269
--- /dev/null
+++ b/src/core/loader/kip.h
@@ -0,0 +1,35 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/loader/loader.h"
8
9namespace FileSys {
10class KIP;
11}
12
13namespace Loader {
14
15class AppLoader_KIP final : public AppLoader {
16public:
17 explicit AppLoader_KIP(FileSys::VirtualFile file);
18 ~AppLoader_KIP() override;
19
20 /**
21 * Returns the type of the file
22 * @param file std::shared_ptr<VfsFile> open file
23 * @return FileType found, or FileType::Error if this loader doesn't know it
24 */
25 static FileType IdentifyType(const FileSys::VirtualFile& file);
26
27 FileType GetFileType() const override;
28
29 LoadResult Load(Kernel::Process& process) override;
30
31private:
32 std::unique_ptr<FileSys::KIP> kip;
33};
34
35} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index d8cc30959..59ca7091a 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/kernel/process.h" 11#include "core/hle/kernel/process.h"
12#include "core/loader/deconstructed_rom_directory.h" 12#include "core/loader/deconstructed_rom_directory.h"
13#include "core/loader/elf.h" 13#include "core/loader/elf.h"
14#include "core/loader/kip.h"
14#include "core/loader/nax.h" 15#include "core/loader/nax.h"
15#include "core/loader/nca.h" 16#include "core/loader/nca.h"
16#include "core/loader/nro.h" 17#include "core/loader/nro.h"
@@ -36,6 +37,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
36 CHECK_TYPE(XCI) 37 CHECK_TYPE(XCI)
37 CHECK_TYPE(NAX) 38 CHECK_TYPE(NAX)
38 CHECK_TYPE(NSP) 39 CHECK_TYPE(NSP)
40 CHECK_TYPE(KIP)
39 41
40#undef CHECK_TYPE 42#undef CHECK_TYPE
41 43
@@ -63,6 +65,8 @@ FileType GuessFromFilename(const std::string& name) {
63 return FileType::XCI; 65 return FileType::XCI;
64 if (extension == "nsp") 66 if (extension == "nsp")
65 return FileType::NSP; 67 return FileType::NSP;
68 if (extension == "kip")
69 return FileType::KIP;
66 70
67 return FileType::Unknown; 71 return FileType::Unknown;
68} 72}
@@ -83,6 +87,8 @@ std::string GetFileTypeString(FileType type) {
83 return "NAX"; 87 return "NAX";
84 case FileType::NSP: 88 case FileType::NSP:
85 return "NSP"; 89 return "NSP";
90 case FileType::KIP:
91 return "KIP";
86 case FileType::DeconstructedRomDirectory: 92 case FileType::DeconstructedRomDirectory:
87 return "Directory"; 93 return "Directory";
88 case FileType::Error: 94 case FileType::Error:
@@ -93,7 +99,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 99 return "unknown";
94} 100}
95 101
96constexpr std::array<const char*, 62> RESULT_MESSAGES{ 102constexpr std::array<const char*, 66> RESULT_MESSAGES{
97 "The operation completed successfully.", 103 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 104 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 105 "The operation is not implemented.",
@@ -156,6 +162,10 @@ constexpr std::array<const char*, 62> RESULT_MESSAGES{
156 "The BKTR-type NCA has a bad Subsection bucket.", 162 "The BKTR-type NCA has a bad Subsection bucket.",
157 "The BKTR-type NCA is missing the base RomFS.", 163 "The BKTR-type NCA is missing the base RomFS.",
158 "The NSP or XCI does not contain an update in addition to the base game.", 164 "The NSP or XCI does not contain an update in addition to the base game.",
165 "The KIP file has a bad header.",
166 "The KIP BLZ decompression of the section failed unexpectedly.",
167 "The INI file has a bad header.",
168 "The INI file contains more than the maximum allowable number of KIP files.",
159}; 169};
160 170
161std::ostream& operator<<(std::ostream& os, ResultStatus status) { 171std::ostream& operator<<(std::ostream& os, ResultStatus status) {
@@ -205,6 +215,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
205 case FileType::NSP: 215 case FileType::NSP:
206 return std::make_unique<AppLoader_NSP>(std::move(file)); 216 return std::make_unique<AppLoader_NSP>(std::move(file));
207 217
218 // NX KIP (Kernel Internal Process) file format
219 case FileType::KIP:
220 return std::make_unique<AppLoader_KIP>(std::move(file));
221
208 // NX deconstructed ROM directory. 222 // NX deconstructed ROM directory.
209 case FileType::DeconstructedRomDirectory: 223 case FileType::DeconstructedRomDirectory:
210 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); 224 return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 869406b75..227ecc704 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -37,6 +37,7 @@ enum class FileType {
37 NSP, 37 NSP,
38 XCI, 38 XCI,
39 NAX, 39 NAX,
40 KIP,
40 DeconstructedRomDirectory, 41 DeconstructedRomDirectory,
41}; 42};
42 43
@@ -124,6 +125,10 @@ enum class ResultStatus : u16 {
124 ErrorBadSubsectionBuckets, 125 ErrorBadSubsectionBuckets,
125 ErrorMissingBKTRBaseRomFS, 126 ErrorMissingBKTRBaseRomFS,
126 ErrorNoPackedUpdate, 127 ErrorNoPackedUpdate,
128 ErrorBadKIPHeader,
129 ErrorBLZDecompressionFailed,
130 ErrorBadINIHeader,
131 ErrorINITooManyKIPs,
127}; 132};
128 133
129std::ostream& operator<<(std::ostream& os, ResultStatus status); 134std::ostream& operator<<(std::ostream& os, ResultStatus status);
@@ -267,6 +272,12 @@ public:
267 return ResultStatus::ErrorNotImplemented; 272 return ResultStatus::ErrorNotImplemented;
268 } 273 }
269 274
275 using Modules = std::map<VAddr, std::string>;
276
277 virtual ResultStatus ReadNSOModules(Modules& modules) {
278 return ResultStatus::ErrorNotImplemented;
279 }
280
270protected: 281protected:
271 FileSys::VirtualFile file; 282 FileSys::VirtualFile file;
272 bool is_loaded = false; 283 bool is_loaded = false;
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 34efef09a..a152981a0 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -94,4 +94,8 @@ ResultStatus AppLoader_NAX::ReadLogo(std::vector<u8>& buffer) {
94 return nca_loader->ReadLogo(buffer); 94 return nca_loader->ReadLogo(buffer);
95} 95}
96 96
97ResultStatus AppLoader_NAX::ReadNSOModules(Modules& modules) {
98 return nca_loader->ReadNSOModules(modules);
99}
100
97} // namespace Loader 101} // namespace Loader
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index 00f1659c1..eaec9bf58 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -42,6 +42,8 @@ public:
42 ResultStatus ReadBanner(std::vector<u8>& buffer) override; 42 ResultStatus ReadBanner(std::vector<u8>& buffer) override;
43 ResultStatus ReadLogo(std::vector<u8>& buffer) override; 43 ResultStatus ReadLogo(std::vector<u8>& buffer) override;
44 44
45 ResultStatus ReadNSOModules(Modules& modules) override;
46
45private: 47private:
46 std::unique_ptr<FileSys::NAX> nax; 48 std::unique_ptr<FileSys::NAX> nax;
47 std::unique_ptr<AppLoader_NCA> nca_loader; 49 std::unique_ptr<AppLoader_NCA> nca_loader;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index b3f8f1083..0f65fb637 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -105,4 +105,13 @@ ResultStatus AppLoader_NCA::ReadLogo(std::vector<u8>& buffer) {
105 buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes(); 105 buffer = logo->GetFile("NintendoLogo.png")->ReadAllBytes();
106 return ResultStatus::Success; 106 return ResultStatus::Success;
107} 107}
108
109ResultStatus AppLoader_NCA::ReadNSOModules(Modules& modules) {
110 if (directory_loader == nullptr) {
111 return ResultStatus::ErrorNotInitialized;
112 }
113
114 return directory_loader->ReadNSOModules(modules);
115}
116
108} // namespace Loader 117} // namespace Loader
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index 94f0ed677..e47dc0e47 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -42,6 +42,8 @@ public:
42 ResultStatus ReadBanner(std::vector<u8>& buffer) override; 42 ResultStatus ReadBanner(std::vector<u8>& buffer) override;
43 ResultStatus ReadLogo(std::vector<u8>& buffer) override; 43 ResultStatus ReadLogo(std::vector<u8>& buffer) override;
44 44
45 ResultStatus ReadNSOModules(Modules& modules) override;
46
45private: 47private:
46 std::unique_ptr<FileSys::NCA> nca; 48 std::unique_ptr<FileSys::NCA> nca;
47 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader; 49 std::unique_ptr<AppLoader_DeconstructedRomDirectory> directory_loader;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 80090b792..29311404a 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -172,11 +172,15 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
172 return {ResultStatus::ErrorAlreadyLoaded, {}}; 172 return {ResultStatus::ErrorAlreadyLoaded, {}};
173 } 173 }
174 174
175 modules.clear();
176
175 // Load module 177 // Load module
176 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 178 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
177 if (!LoadModule(process, *file, base_address, true)) { 179 if (!LoadModule(process, *file, base_address, true)) {
178 return {ResultStatus::ErrorLoadingNSO, {}}; 180 return {ResultStatus::ErrorLoadingNSO, {}};
179 } 181 }
182
183 modules.insert_or_assign(base_address, file->GetName());
180 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 184 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
181 185
182 is_loaded = true; 186 is_loaded = true;
@@ -184,4 +188,9 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
184 LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}}; 188 LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
185} 189}
186 190
191ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) {
192 modules = this->modules;
193 return ResultStatus::Success;
194}
195
187} // namespace Loader 196} // namespace Loader
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index fdce9191c..58cbe162d 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -85,6 +85,11 @@ public:
85 std::optional<FileSys::PatchManager> pm = {}); 85 std::optional<FileSys::PatchManager> pm = {});
86 86
87 LoadResult Load(Kernel::Process& process) override; 87 LoadResult Load(Kernel::Process& process) override;
88
89 ResultStatus ReadNSOModules(Modules& modules) override;
90
91private:
92 Modules modules;
88}; 93};
89 94
90} // namespace Loader 95} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index ad56bbb38..b1171ce65 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -168,7 +168,8 @@ ResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) {
168} 168}
169 169
170ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) { 170ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) {
171 const auto nca = nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Manual); 171 const auto nca =
172 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::HtmlDocument);
172 if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr) 173 if (nsp->GetStatus() != ResultStatus::Success || nca == nullptr)
173 return ResultStatus::ErrorNoRomFS; 174 return ResultStatus::ErrorNoRomFS;
174 file = nca->GetRomFS(); 175 file = nca->GetRomFS();
@@ -183,4 +184,8 @@ ResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) {
183 return secondary_loader->ReadLogo(buffer); 184 return secondary_loader->ReadLogo(buffer);
184} 185}
185 186
187ResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) {
188 return secondary_loader->ReadNSOModules(modules);
189}
190
186} // namespace Loader 191} // namespace Loader
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 85e870bdf..868b028d3 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -49,6 +49,8 @@ public:
49 ResultStatus ReadBanner(std::vector<u8>& buffer) override; 49 ResultStatus ReadBanner(std::vector<u8>& buffer) override;
50 ResultStatus ReadLogo(std::vector<u8>& buffer) override; 50 ResultStatus ReadLogo(std::vector<u8>& buffer) override;
51 51
52 ResultStatus ReadNSOModules(Modules& modules) override;
53
52private: 54private:
53 std::unique_ptr<FileSys::NSP> nsp; 55 std::unique_ptr<FileSys::NSP> nsp;
54 std::unique_ptr<AppLoader> secondary_loader; 56 std::unique_ptr<AppLoader> secondary_loader;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 1e285a053..5e8553db9 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -134,7 +134,7 @@ ResultStatus AppLoader_XCI::ReadControlData(FileSys::NACP& control) {
134 134
135ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) { 135ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) {
136 const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(), 136 const auto nca = xci->GetSecurePartitionNSP()->GetNCA(xci->GetProgramTitleID(),
137 FileSys::ContentRecordType::Manual); 137 FileSys::ContentRecordType::HtmlDocument);
138 if (xci->GetStatus() != ResultStatus::Success || nca == nullptr) 138 if (xci->GetStatus() != ResultStatus::Success || nca == nullptr)
139 return ResultStatus::ErrorXCIMissingPartition; 139 return ResultStatus::ErrorXCIMissingPartition;
140 file = nca->GetRomFS(); 140 file = nca->GetRomFS();
@@ -149,4 +149,8 @@ ResultStatus AppLoader_XCI::ReadLogo(std::vector<u8>& buffer) {
149 return nca_loader->ReadLogo(buffer); 149 return nca_loader->ReadLogo(buffer);
150} 150}
151 151
152ResultStatus AppLoader_XCI::ReadNSOModules(Modules& modules) {
153 return nca_loader->ReadNSOModules(modules);
154}
155
152} // namespace Loader 156} // namespace Loader
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index ae7145b14..618ae2f47 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -49,6 +49,8 @@ public:
49 ResultStatus ReadBanner(std::vector<u8>& buffer) override; 49 ResultStatus ReadBanner(std::vector<u8>& buffer) override;
50 ResultStatus ReadLogo(std::vector<u8>& buffer) override; 50 ResultStatus ReadLogo(std::vector<u8>& buffer) override;
51 51
52 ResultStatus ReadNSOModules(Modules& modules) override;
53
52private: 54private:
53 std::unique_ptr<FileSys::XCI> xci; 55 std::unique_ptr<FileSys::XCI> xci;
54 std::unique_ptr<AppLoader_NCA> nca_loader; 56 std::unique_ptr<AppLoader_NCA> nca_loader;
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp
new file mode 100644
index 000000000..774022569
--- /dev/null
+++ b/src/core/reporter.cpp
@@ -0,0 +1,353 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <fstream>
6#include <json.hpp>
7#include "common/file_util.h"
8#include "common/hex_util.h"
9#include "common/scm_rev.h"
10#include "core/arm/arm_interface.h"
11#include "core/core.h"
12#include "core/hle/kernel/hle_ipc.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/result.h"
15#include "core/reporter.h"
16#include "core/settings.h"
17#include "fmt/time.h"
18
19namespace {
20
21std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) {
22 return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir),
23 type, title_id, timestamp);
24}
25
26std::string GetTimestamp() {
27 const auto time = std::time(nullptr);
28 return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time));
29}
30
31using namespace nlohmann;
32
33void SaveToFile(const json& json, const std::string& filename) {
34 if (!FileUtil::CreateFullPath(filename))
35 LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename);
36
37 std::ofstream file(
38 FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault));
39 file << std::setw(4) << json << std::endl;
40}
41
42json GetYuzuVersionData() {
43 return {
44 {"scm_rev", std::string(Common::g_scm_rev)},
45 {"scm_branch", std::string(Common::g_scm_branch)},
46 {"scm_desc", std::string(Common::g_scm_desc)},
47 {"build_name", std::string(Common::g_build_name)},
48 {"build_date", std::string(Common::g_build_date)},
49 {"build_fullname", std::string(Common::g_build_fullname)},
50 {"build_version", std::string(Common::g_build_version)},
51 {"shader_cache_version", std::string(Common::g_shader_cache_version)},
52 };
53}
54
55json GetReportCommonData(u64 title_id, ResultCode result, const std::string& timestamp,
56 std::optional<u128> user_id = {}) {
57 auto out = json{
58 {"title_id", fmt::format("{:016X}", title_id)},
59 {"result_raw", fmt::format("{:08X}", result.raw)},
60 {"result_module", fmt::format("{:08X}", static_cast<u32>(result.module.Value()))},
61 {"result_description", fmt::format("{:08X}", result.description.Value())},
62 {"timestamp", timestamp},
63 };
64 if (user_id.has_value())
65 out["user_id"] = fmt::format("{:016X}{:016X}", (*user_id)[1], (*user_id)[0]);
66 return out;
67}
68
69json GetProcessorStateData(const std::string& architecture, u64 entry_point, u64 sp, u64 pc,
70 u64 pstate, std::array<u64, 31> registers,
71 std::optional<std::array<u64, 32>> backtrace = {}) {
72 auto out = json{
73 {"entry_point", fmt::format("{:016X}", entry_point)},
74 {"sp", fmt::format("{:016X}", sp)},
75 {"pc", fmt::format("{:016X}", pc)},
76 {"pstate", fmt::format("{:016X}", pstate)},
77 {"architecture", architecture},
78 };
79
80 auto registers_out = json::object();
81 for (std::size_t i = 0; i < registers.size(); ++i) {
82 registers_out[fmt::format("X{:02d}", i)] = fmt::format("{:016X}", registers[i]);
83 }
84
85 out["registers"] = std::move(registers_out);
86
87 if (backtrace.has_value()) {
88 auto backtrace_out = json::array();
89 for (const auto& entry : *backtrace) {
90 backtrace_out.push_back(fmt::format("{:016X}", entry));
91 }
92 out["backtrace"] = std::move(backtrace_out);
93 }
94
95 return out;
96}
97
98json GetProcessorStateDataAuto(Core::System& system) {
99 const auto* process{system.CurrentProcess()};
100 const auto& vm_manager{process->VMManager()};
101 auto& arm{system.CurrentArmInterface()};
102
103 Core::ARM_Interface::ThreadContext context{};
104 arm.SaveContext(context);
105
106 return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32",
107 vm_manager.GetCodeRegionBaseAddress(), context.sp, context.pc,
108 context.pstate, context.cpu_registers);
109}
110
111json GetBacktraceData(Core::System& system) {
112 auto out = json::array();
113 const auto& backtrace{system.CurrentArmInterface().GetBacktrace()};
114 for (const auto& entry : backtrace) {
115 out.push_back({
116 {"module", entry.module},
117 {"address", fmt::format("{:016X}", entry.address)},
118 {"original_address", fmt::format("{:016X}", entry.original_address)},
119 {"offset", fmt::format("{:016X}", entry.offset)},
120 {"symbol_name", entry.name},
121 });
122 }
123
124 return out;
125}
126
127json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& system) {
128 json out;
129
130 out["yuzu_version"] = GetYuzuVersionData();
131 out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp);
132 out["processor_state"] = GetProcessorStateDataAuto(system);
133 out["backtrace"] = GetBacktraceData(system);
134
135 return out;
136}
137
138template <bool read_value, typename DescriptorType>
139json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer) {
140 auto buffer_out = json::array();
141 for (const auto& desc : buffer) {
142 auto entry = json{
143 {"address", fmt::format("{:016X}", desc.Address())},
144 {"size", fmt::format("{:016X}", desc.Size())},
145 };
146
147 if constexpr (read_value) {
148 std::vector<u8> data(desc.Size());
149 Memory::ReadBlock(desc.Address(), data.data(), desc.Size());
150 entry["data"] = Common::HexToString(data);
151 }
152
153 buffer_out.push_back(std::move(entry));
154 }
155
156 return buffer_out;
157}
158
159json GetHLERequestContextData(Kernel::HLERequestContext& ctx) {
160 json out;
161
162 auto cmd_buf = json::array();
163 for (std::size_t i = 0; i < IPC::COMMAND_BUFFER_LENGTH; ++i) {
164 cmd_buf.push_back(fmt::format("{:08X}", ctx.CommandBuffer()[i]));
165 }
166
167 out["command_buffer"] = std::move(cmd_buf);
168
169 out["buffer_descriptor_a"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorA());
170 out["buffer_descriptor_b"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorB());
171 out["buffer_descriptor_c"] = GetHLEBufferDescriptorData<false>(ctx.BufferDescriptorC());
172 out["buffer_descriptor_x"] = GetHLEBufferDescriptorData<true>(ctx.BufferDescriptorX());
173
174 return std::move(out);
175}
176
177} // Anonymous namespace
178
179namespace Core {
180
181Reporter::Reporter(Core::System& system) : system(system) {}
182
183Reporter::~Reporter() = default;
184
185void Reporter::SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point,
186 u64 sp, u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
187 const std::array<u64, 31>& registers,
188 const std::array<u64, 32>& backtrace, u32 backtrace_size,
189 const std::string& arch, u32 unk10) const {
190 if (!IsReportingEnabled())
191 return;
192
193 const auto timestamp = GetTimestamp();
194 json out;
195
196 out["yuzu_version"] = GetYuzuVersionData();
197 out["report_common"] = GetReportCommonData(title_id, result, timestamp);
198
199 auto proc_out = GetProcessorStateData(arch, entry_point, sp, pc, pstate, registers, backtrace);
200 proc_out["set_flags"] = fmt::format("{:016X}", set_flags);
201 proc_out["afsr0"] = fmt::format("{:016X}", afsr0);
202 proc_out["afsr1"] = fmt::format("{:016X}", afsr1);
203 proc_out["esr"] = fmt::format("{:016X}", esr);
204 proc_out["far"] = fmt::format("{:016X}", far);
205 proc_out["backtrace_size"] = fmt::format("{:08X}", backtrace_size);
206 proc_out["unknown_10"] = fmt::format("{:08X}", unk10);
207
208 out["processor_state"] = std::move(proc_out);
209
210 SaveToFile(std::move(out), GetPath("crash_report", title_id, timestamp));
211}
212
213void Reporter::SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
214 std::optional<std::vector<u8>> resolved_buffer) const {
215 if (!IsReportingEnabled())
216 return;
217
218 const auto timestamp = GetTimestamp();
219 const auto title_id = system.CurrentProcess()->GetTitleID();
220 auto out = GetFullDataAuto(timestamp, title_id, system);
221
222 auto break_out = json{
223 {"type", fmt::format("{:08X}", type)},
224 {"signal_debugger", fmt::format("{}", signal_debugger)},
225 {"info1", fmt::format("{:016X}", info1)},
226 {"info2", fmt::format("{:016X}", info2)},
227 };
228
229 if (resolved_buffer.has_value()) {
230 break_out["debug_buffer"] = Common::HexToString(*resolved_buffer);
231 }
232
233 out["svc_break"] = std::move(break_out);
234
235 SaveToFile(std::move(out), GetPath("svc_break_report", title_id, timestamp));
236}
237
238void Reporter::SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
239 const std::string& name,
240 const std::string& service_name) const {
241 if (!IsReportingEnabled())
242 return;
243
244 const auto timestamp = GetTimestamp();
245 const auto title_id = system.CurrentProcess()->GetTitleID();
246 auto out = GetFullDataAuto(timestamp, title_id, system);
247
248 auto function_out = GetHLERequestContextData(ctx);
249 function_out["command_id"] = command_id;
250 function_out["function_name"] = name;
251 function_out["service_name"] = service_name;
252
253 out["function"] = std::move(function_out);
254
255 SaveToFile(std::move(out), GetPath("unimpl_func_report", title_id, timestamp));
256}
257
258void Reporter::SaveUnimplementedAppletReport(
259 u32 applet_id, u32 common_args_version, u32 library_version, u32 theme_color,
260 bool startup_sound, u64 system_tick, std::vector<std::vector<u8>> normal_channel,
261 std::vector<std::vector<u8>> interactive_channel) const {
262 if (!IsReportingEnabled())
263 return;
264
265 const auto timestamp = GetTimestamp();
266 const auto title_id = system.CurrentProcess()->GetTitleID();
267 auto out = GetFullDataAuto(timestamp, title_id, system);
268
269 out["applet_common_args"] = {
270 {"applet_id", fmt::format("{:02X}", applet_id)},
271 {"common_args_version", fmt::format("{:08X}", common_args_version)},
272 {"library_version", fmt::format("{:08X}", library_version)},
273 {"theme_color", fmt::format("{:08X}", theme_color)},
274 {"startup_sound", fmt::format("{}", startup_sound)},
275 {"system_tick", fmt::format("{:016X}", system_tick)},
276 };
277
278 auto normal_out = json::array();
279 for (const auto& data : normal_channel) {
280 normal_out.push_back(Common::HexToString(data));
281 }
282
283 auto interactive_out = json::array();
284 for (const auto& data : interactive_channel) {
285 interactive_out.push_back(Common::HexToString(data));
286 }
287
288 out["applet_normal_data"] = std::move(normal_out);
289 out["applet_interactive_data"] = std::move(interactive_out);
290
291 SaveToFile(std::move(out), GetPath("unimpl_applet_report", title_id, timestamp));
292}
293
294void Reporter::SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
295 std::optional<u128> user_id) const {
296 if (!IsReportingEnabled())
297 return;
298
299 const auto timestamp = GetTimestamp();
300 json out;
301
302 out["yuzu_version"] = GetYuzuVersionData();
303 out["report_common"] = GetReportCommonData(title_id, RESULT_SUCCESS, timestamp, user_id);
304
305 auto data_out = json::array();
306 for (const auto& d : data) {
307 data_out.push_back(Common::HexToString(d));
308 }
309
310 out["play_report_process_id"] = fmt::format("{:016X}", process_id);
311 out["play_report_data"] = std::move(data_out);
312
313 SaveToFile(std::move(out), GetPath("play_report", title_id, timestamp));
314}
315
316void Reporter::SaveErrorReport(u64 title_id, ResultCode result,
317 std::optional<std::string> custom_text_main,
318 std::optional<std::string> custom_text_detail) const {
319 if (!IsReportingEnabled())
320 return;
321
322 const auto timestamp = GetTimestamp();
323 json out;
324
325 out["yuzu_version"] = GetYuzuVersionData();
326 out["report_common"] = GetReportCommonData(title_id, result, timestamp);
327 out["processor_state"] = GetProcessorStateDataAuto(system);
328 out["backtrace"] = GetBacktraceData(system);
329
330 out["error_custom_text"] = {
331 {"main", *custom_text_main},
332 {"detail", *custom_text_detail},
333 };
334
335 SaveToFile(std::move(out), GetPath("error_report", title_id, timestamp));
336}
337
338void Reporter::SaveUserReport() const {
339 if (!IsReportingEnabled())
340 return;
341
342 const auto timestamp = GetTimestamp();
343 const auto title_id = system.CurrentProcess()->GetTitleID();
344
345 SaveToFile(GetFullDataAuto(timestamp, title_id, system),
346 GetPath("user_report", title_id, timestamp));
347}
348
349bool Reporter::IsReportingEnabled() const {
350 return Settings::values.reporting_services;
351}
352
353} // namespace Core
diff --git a/src/core/reporter.h b/src/core/reporter.h
new file mode 100644
index 000000000..3de19c0f7
--- /dev/null
+++ b/src/core/reporter.h
@@ -0,0 +1,56 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <optional>
8#include <vector>
9#include "common/common_types.h"
10
11union ResultCode;
12
13namespace Kernel {
14class HLERequestContext;
15} // namespace Kernel
16
17namespace Core {
18
19class Reporter {
20public:
21 explicit Reporter(Core::System& system);
22 ~Reporter();
23
24 void SaveCrashReport(u64 title_id, ResultCode result, u64 set_flags, u64 entry_point, u64 sp,
25 u64 pc, u64 pstate, u64 afsr0, u64 afsr1, u64 esr, u64 far,
26 const std::array<u64, 31>& registers, const std::array<u64, 32>& backtrace,
27 u32 backtrace_size, const std::string& arch, u32 unk10) const;
28
29 void SaveSvcBreakReport(u32 type, bool signal_debugger, u64 info1, u64 info2,
30 std::optional<std::vector<u8>> resolved_buffer = {}) const;
31
32 void SaveUnimplementedFunctionReport(Kernel::HLERequestContext& ctx, u32 command_id,
33 const std::string& name,
34 const std::string& service_name) const;
35
36 void SaveUnimplementedAppletReport(u32 applet_id, u32 common_args_version, u32 library_version,
37 u32 theme_color, bool startup_sound, u64 system_tick,
38 std::vector<std::vector<u8>> normal_channel,
39 std::vector<std::vector<u8>> interactive_channel) const;
40
41 void SavePlayReport(u64 title_id, u64 process_id, std::vector<std::vector<u8>> data,
42 std::optional<u128> user_id = {}) const;
43
44 void SaveErrorReport(u64 title_id, ResultCode result,
45 std::optional<std::string> custom_text_main = {},
46 std::optional<std::string> custom_text_detail = {}) const;
47
48 void SaveUserReport() const;
49
50private:
51 bool IsReportingEnabled() const;
52
53 Core::System& system;
54};
55
56} // namespace Core
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 6d32ebea3..63aa59690 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -85,7 +85,7 @@ void LogSettings() {
85 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0)); 85 LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
86 LogSetting("System_CurrentUser", Settings::values.current_user); 86 LogSetting("System_CurrentUser", Settings::values.current_user);
87 LogSetting("System_LanguageIndex", Settings::values.language_index); 87 LogSetting("System_LanguageIndex", Settings::values.language_index);
88 LogSetting("Core_UseCpuJit", Settings::values.use_cpu_jit); 88 LogSetting("Core_CpuJitEnabled", Settings::values.cpu_jit_enabled);
89 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core); 89 LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
90 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor); 90 LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
91 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit); 91 LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
diff --git a/src/core/settings.h b/src/core/settings.h
index b84390745..acf18d653 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -378,7 +378,7 @@ struct Values {
378 std::atomic_bool is_device_reload_pending{true}; 378 std::atomic_bool is_device_reload_pending{true};
379 379
380 // Core 380 // Core
381 bool use_cpu_jit; 381 bool cpu_jit_enabled;
382 bool use_multi_core; 382 bool use_multi_core;
383 383
384 // Data Storage 384 // Data Storage
@@ -415,6 +415,8 @@ struct Values {
415 std::string program_args; 415 std::string program_args;
416 bool dump_exefs; 416 bool dump_exefs;
417 bool dump_nso; 417 bool dump_nso;
418 bool reporting_services;
419 bool quest_flag;
418 420
419 // WebService 421 // WebService
420 bool enable_telemetry; 422 bool enable_telemetry;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 90d06830f..98f49042a 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -168,7 +168,7 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
168 AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id); 168 AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id);
169 AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", 169 AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
170 Settings::values.enable_audio_stretching); 170 Settings::values.enable_audio_stretching);
171 AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit); 171 AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.cpu_jit_enabled);
172 AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore", 172 AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
173 Settings::values.use_multi_core); 173 Settings::values.use_multi_core);
174 AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor", 174 AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor",
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
new file mode 100644
index 000000000..17f050068
--- /dev/null
+++ b/src/core/tools/freezer.cpp
@@ -0,0 +1,188 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/core_timing.h"
9#include "core/core_timing_util.h"
10#include "core/memory.h"
11#include "core/tools/freezer.h"
12
13namespace Tools {
14
15namespace {
16
17constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
18
19u64 MemoryReadWidth(u32 width, VAddr addr) {
20 switch (width) {
21 case 1:
22 return Memory::Read8(addr);
23 case 2:
24 return Memory::Read16(addr);
25 case 4:
26 return Memory::Read32(addr);
27 case 8:
28 return Memory::Read64(addr);
29 default:
30 UNREACHABLE();
31 return 0;
32 }
33}
34
35void MemoryWriteWidth(u32 width, VAddr addr, u64 value) {
36 switch (width) {
37 case 1:
38 Memory::Write8(addr, static_cast<u8>(value));
39 break;
40 case 2:
41 Memory::Write16(addr, static_cast<u16>(value));
42 break;
43 case 4:
44 Memory::Write32(addr, static_cast<u32>(value));
45 break;
46 case 8:
47 Memory::Write64(addr, value);
48 break;
49 default:
50 UNREACHABLE();
51 }
52}
53
54} // Anonymous namespace
55
56Freezer::Freezer(Core::Timing::CoreTiming& core_timing) : core_timing(core_timing) {
57 event = core_timing.RegisterEvent(
58 "MemoryFreezer::FrameCallback",
59 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
60 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
61}
62
63Freezer::~Freezer() {
64 core_timing.UnscheduleEvent(event, 0);
65}
66
67void Freezer::SetActive(bool active) {
68 if (!this->active.exchange(active)) {
69 FillEntryReads();
70 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
71 LOG_DEBUG(Common_Memory, "Memory freezer activated!");
72 } else {
73 LOG_DEBUG(Common_Memory, "Memory freezer deactivated!");
74 }
75}
76
77bool Freezer::IsActive() const {
78 return active.load(std::memory_order_relaxed);
79}
80
81void Freezer::Clear() {
82 std::lock_guard lock{entries_mutex};
83
84 LOG_DEBUG(Common_Memory, "Clearing all frozen memory values.");
85
86 entries.clear();
87}
88
89u64 Freezer::Freeze(VAddr address, u32 width) {
90 std::lock_guard lock{entries_mutex};
91
92 const auto current_value = MemoryReadWidth(width, address);
93 entries.push_back({address, width, current_value});
94
95 LOG_DEBUG(Common_Memory,
96 "Freezing memory for address={:016X}, width={:02X}, current_value={:016X}", address,
97 width, current_value);
98
99 return current_value;
100}
101
102void Freezer::Unfreeze(VAddr address) {
103 std::lock_guard lock{entries_mutex};
104
105 LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address);
106
107 entries.erase(
108 std::remove_if(entries.begin(), entries.end(),
109 [&address](const Entry& entry) { return entry.address == address; }),
110 entries.end());
111}
112
113bool Freezer::IsFrozen(VAddr address) const {
114 std::lock_guard lock{entries_mutex};
115
116 return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
117 return entry.address == address;
118 }) != entries.end();
119}
120
121void Freezer::SetFrozenValue(VAddr address, u64 value) {
122 std::lock_guard lock{entries_mutex};
123
124 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
125 return entry.address == address;
126 });
127
128 if (iter == entries.end()) {
129 LOG_ERROR(Common_Memory,
130 "Tried to set freeze value for address={:016X} that is not frozen!", address);
131 return;
132 }
133
134 LOG_DEBUG(Common_Memory,
135 "Manually overridden freeze value for address={:016X}, width={:02X} to value={:016X}",
136 iter->address, iter->width, value);
137 iter->value = value;
138}
139
140std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const {
141 std::lock_guard lock{entries_mutex};
142
143 const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) {
144 return entry.address == address;
145 });
146
147 if (iter == entries.end()) {
148 return std::nullopt;
149 }
150
151 return *iter;
152}
153
154std::vector<Freezer::Entry> Freezer::GetEntries() const {
155 std::lock_guard lock{entries_mutex};
156
157 return entries;
158}
159
160void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
161 if (!IsActive()) {
162 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
163 return;
164 }
165
166 std::lock_guard lock{entries_mutex};
167
168 for (const auto& entry : entries) {
169 LOG_DEBUG(Common_Memory,
170 "Enforcing memory freeze at address={:016X}, value={:016X}, width={:02X}",
171 entry.address, entry.value, entry.width);
172 MemoryWriteWidth(entry.width, entry.address, entry.value);
173 }
174
175 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event);
176}
177
178void Freezer::FillEntryReads() {
179 std::lock_guard lock{entries_mutex};
180
181 LOG_DEBUG(Common_Memory, "Updating memory freeze entries to current values.");
182
183 for (auto& entry : entries) {
184 entry.value = MemoryReadWidth(entry.width, entry.address);
185 }
186}
187
188} // namespace Tools
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h
new file mode 100644
index 000000000..b58de5472
--- /dev/null
+++ b/src/core/tools/freezer.h
@@ -0,0 +1,82 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <atomic>
8#include <mutex>
9#include <optional>
10#include <vector>
11#include "common/common_types.h"
12
13namespace Core::Timing {
14class CoreTiming;
15struct EventType;
16} // namespace Core::Timing
17
18namespace Tools {
19
20/**
21 * This class allows the user to prevent an application from writing new values to certain memory
22 * locations. This has a variety of uses when attempting to reverse a game.
23 *
24 * One example could be a cheat to prevent Mario from taking damage in SMO. One could freeze the
25 * memory address that the game uses to store Mario's health so when he takes damage (and the game
26 * tries to write the new health value to memory), the value won't change.
27 */
28class Freezer {
29public:
30 struct Entry {
31 VAddr address;
32 u32 width;
33 u64 value;
34 };
35
36 explicit Freezer(Core::Timing::CoreTiming& core_timing);
37 ~Freezer();
38
39 // Enables or disables the entire memory freezer.
40 void SetActive(bool active);
41
42 // Returns whether or not the freezer is active.
43 bool IsActive() const;
44
45 // Removes all entries from the freezer.
46 void Clear();
47
48 // Freezes a value to its current memory address. The value the memory is kept at will be the
49 // value that is read during this function. Width can be 1, 2, 4, or 8 (in bytes).
50 u64 Freeze(VAddr address, u32 width);
51
52 // Unfreezes the memory value at address. If the address isn't frozen, this is a no-op.
53 void Unfreeze(VAddr address);
54
55 // Returns whether or not the address is frozen.
56 bool IsFrozen(VAddr address) const;
57
58 // Sets the value that address should be frozen to. This doesn't change the width set by using
59 // Freeze(). If the value isn't frozen, this will not freeze it and is thus a no-op.
60 void SetFrozenValue(VAddr address, u64 value);
61
62 // Returns the entry corresponding to the address if the address is frozen, otherwise
63 // std::nullopt.
64 std::optional<Entry> GetEntry(VAddr address) const;
65
66 // Returns all the entries in the freezer, an empty vector means nothing is frozen.
67 std::vector<Entry> GetEntries() const;
68
69private:
70 void FrameCallback(u64 userdata, s64 cycles_late);
71 void FillEntryReads();
72
73 std::atomic_bool active{false};
74
75 mutable std::mutex entries_mutex;
76 std::vector<Entry> entries;
77
78 Core::Timing::EventType* event;
79 Core::Timing::CoreTiming& core_timing;
80};
81
82} // namespace Tools
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 58af41f6e..ac7ae3e52 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -16,7 +16,8 @@ namespace ArmTests {
16TestEnvironment::TestEnvironment(bool mutable_memory_) 16TestEnvironment::TestEnvironment(bool mutable_memory_)
17 : mutable_memory(mutable_memory_), 17 : mutable_memory(mutable_memory_),
18 test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} { 18 test_memory(std::make_shared<TestMemory>(this)), kernel{Core::System::GetInstance()} {
19 auto process = Kernel::Process::Create(Core::System::GetInstance(), ""); 19 auto process = Kernel::Process::Create(Core::System::GetInstance(), "",
20 Kernel::Process::ProcessType::Userland);
20 page_table = &process->VMManager().page_table; 21 page_table = &process->VMManager().page_table;
21 22
22 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); 23 std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr);
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index 340d6a272..f8be8fd19 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -99,24 +99,24 @@ TEST_CASE("CoreTiming[Threadsave]", "[core]") {
99 core_timing.Advance(); 99 core_timing.Advance();
100 100
101 // D -> B -> C -> A -> E 101 // D -> B -> C -> A -> E
102 core_timing.ScheduleEventThreadsafe(1000, cb_a, CB_IDS[0]); 102 core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
103 // Manually force since ScheduleEventThreadsafe doesn't call it 103 // Manually force since ScheduleEvent doesn't call it
104 core_timing.ForceExceptionCheck(1000); 104 core_timing.ForceExceptionCheck(1000);
105 REQUIRE(1000 == core_timing.GetDowncount()); 105 REQUIRE(1000 == core_timing.GetDowncount());
106 core_timing.ScheduleEventThreadsafe(500, cb_b, CB_IDS[1]); 106 core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
107 // Manually force since ScheduleEventThreadsafe doesn't call it 107 // Manually force since ScheduleEvent doesn't call it
108 core_timing.ForceExceptionCheck(500); 108 core_timing.ForceExceptionCheck(500);
109 REQUIRE(500 == core_timing.GetDowncount()); 109 REQUIRE(500 == core_timing.GetDowncount());
110 core_timing.ScheduleEventThreadsafe(800, cb_c, CB_IDS[2]); 110 core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
111 // Manually force since ScheduleEventThreadsafe doesn't call it 111 // Manually force since ScheduleEvent doesn't call it
112 core_timing.ForceExceptionCheck(800); 112 core_timing.ForceExceptionCheck(800);
113 REQUIRE(500 == core_timing.GetDowncount()); 113 REQUIRE(500 == core_timing.GetDowncount());
114 core_timing.ScheduleEventThreadsafe(100, cb_d, CB_IDS[3]); 114 core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
115 // Manually force since ScheduleEventThreadsafe doesn't call it 115 // Manually force since ScheduleEvent doesn't call it
116 core_timing.ForceExceptionCheck(100); 116 core_timing.ForceExceptionCheck(100);
117 REQUIRE(100 == core_timing.GetDowncount()); 117 REQUIRE(100 == core_timing.GetDowncount());
118 core_timing.ScheduleEventThreadsafe(1200, cb_e, CB_IDS[4]); 118 core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
119 // Manually force since ScheduleEventThreadsafe doesn't call it 119 // Manually force since ScheduleEvent doesn't call it
120 core_timing.ForceExceptionCheck(1200); 120 core_timing.ForceExceptionCheck(1200);
121 REQUIRE(100 == core_timing.GetDowncount()); 121 REQUIRE(100 == core_timing.GetDowncount());
122 122
diff --git a/src/video_core/rasterizer_cache.h b/src/video_core/rasterizer_cache.h
index 0c4ea1494..6de1597a2 100644
--- a/src/video_core/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache.h
@@ -169,6 +169,8 @@ protected:
169 object->MarkAsModified(false, *this); 169 object->MarkAsModified(false, *this);
170 } 170 }
171 171
172 std::recursive_mutex mutex;
173
172private: 174private:
173 /// Returns a list of cached objects from the specified memory region, ordered by access time 175 /// Returns a list of cached objects from the specified memory region, ordered by access time
174 std::vector<T> GetSortedObjectsFromRegion(CacheAddr addr, u64 size) { 176 std::vector<T> GetSortedObjectsFromRegion(CacheAddr addr, u64 size) {
@@ -208,5 +210,4 @@ private:
208 IntervalCache interval_cache; ///< Cache of objects 210 IntervalCache interval_cache; ///< Cache of objects
209 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing 211 u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
210 VideoCore::RasterizerInterface& rasterizer; 212 VideoCore::RasterizerInterface& rasterizer;
211 std::recursive_mutex mutex;
212}; 213};
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 48b86f3bd..2b9bd142e 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -23,6 +23,7 @@ OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, std::size_t size)
23 23
24GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment, 24GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment,
25 bool cache) { 25 bool cache) {
26 std::lock_guard lock{mutex};
26 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); 27 auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
27 28
28 // Cache management is a big overhead, so only cache entries with a given size. 29 // Cache management is a big overhead, so only cache entries with a given size.
@@ -62,6 +63,7 @@ GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::
62 63
63GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t size, 64GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t size,
64 std::size_t alignment) { 65 std::size_t alignment) {
66 std::lock_guard lock{mutex};
65 AlignBuffer(alignment); 67 AlignBuffer(alignment);
66 std::memcpy(buffer_ptr, raw_pointer, size); 68 std::memcpy(buffer_ptr, raw_pointer, size);
67 const GLintptr uploaded_offset = buffer_offset; 69 const GLintptr uploaded_offset = buffer_offset;
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 65a88b06c..a48e14d2e 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -43,8 +43,9 @@ bool Device::TestVariableAoffi() {
43// This is a unit test, please ignore me on apitrace bug reports. 43// This is a unit test, please ignore me on apitrace bug reports.
44uniform sampler2D tex; 44uniform sampler2D tex;
45uniform ivec2 variable_offset; 45uniform ivec2 variable_offset;
46out vec4 output_attribute;
46void main() { 47void main() {
47 gl_Position = textureOffset(tex, vec2(0), variable_offset); 48 output_attribute = textureOffset(tex, vec2(0), variable_offset);
48} 49}
49)"; 50)";
50 const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &AOFFI_TEST)}; 51 const GLuint shader{glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &AOFFI_TEST)};
diff --git a/src/video_core/renderer_opengl/gl_global_cache.cpp b/src/video_core/renderer_opengl/gl_global_cache.cpp
index ea4a593af..d5e385151 100644
--- a/src/video_core/renderer_opengl/gl_global_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_global_cache.cpp
@@ -76,6 +76,7 @@ GlobalRegionCacheOpenGL::GlobalRegionCacheOpenGL(RasterizerOpenGL& rasterizer)
76GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( 76GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion(
77 const GLShader::GlobalMemoryEntry& global_region, 77 const GLShader::GlobalMemoryEntry& global_region,
78 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) { 78 Tegra::Engines::Maxwell3D::Regs::ShaderStage stage) {
79 std::lock_guard lock{mutex};
79 80
80 auto& gpu{Core::System::GetInstance().GPU()}; 81 auto& gpu{Core::System::GetInstance().GPU()};
81 auto& memory_manager{gpu.MemoryManager()}; 82 auto& memory_manager{gpu.MemoryManager()};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 1bd182d98..f9b2b03a0 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -244,44 +244,37 @@ std::set<GLenum> GetSupportedFormats() {
244 244
245} // Anonymous namespace 245} // Anonymous namespace
246 246
247CachedShader::CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier, 247CachedShader::CachedShader(const ShaderParameters& params, Maxwell::ShaderProgram program_type,
248 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, 248 GLShader::ProgramResult result)
249 const PrecompiledPrograms& precompiled_programs, 249 : RasterizerCacheObject{params.host_ptr}, host_ptr{params.host_ptr}, cpu_addr{params.cpu_addr},
250 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr) 250 unique_identifier{params.unique_identifier}, program_type{program_type},
251 : RasterizerCacheObject{host_ptr}, host_ptr{host_ptr}, cpu_addr{cpu_addr}, 251 disk_cache{params.disk_cache}, precompiled_programs{params.precompiled_programs},
252 unique_identifier{unique_identifier}, program_type{program_type}, disk_cache{disk_cache}, 252 entries{result.second}, code{std::move(result.first)}, shader_length{entries.shader_length} {}
253 precompiled_programs{precompiled_programs} { 253
254 const std::size_t code_size{CalculateProgramSize(program_code)}; 254Shader CachedShader::CreateStageFromMemory(const ShaderParameters& params,
255 const std::size_t code_size_b{program_code_b.empty() ? 0 255 Maxwell::ShaderProgram program_type,
256 : CalculateProgramSize(program_code_b)}; 256 ProgramCode&& program_code,
257 GLShader::ProgramResult program_result{ 257 ProgramCode&& program_code_b) {
258 CreateProgram(device, program_type, program_code, program_code_b)}; 258 const auto code_size{CalculateProgramSize(program_code)};
259 if (program_result.first.empty()) { 259 const auto code_size_b{CalculateProgramSize(program_code_b)};
260 auto result{CreateProgram(params.device, program_type, program_code, program_code_b)};
261 if (result.first.empty()) {
260 // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now 262 // TODO(Rodrigo): Unimplemented shader stages hit here, avoid using these for now
261 return; 263 return {};
262 } 264 }
263 265
264 code = program_result.first; 266 params.disk_cache.SaveRaw(ShaderDiskCacheRaw(
265 entries = program_result.second; 267 params.unique_identifier, program_type, static_cast<u32>(code_size / sizeof(u64)),
266 shader_length = entries.shader_length; 268 static_cast<u32>(code_size_b / sizeof(u64)), std::move(program_code),
269 std::move(program_code_b)));
267 270
268 const ShaderDiskCacheRaw raw(unique_identifier, program_type, 271 return std::shared_ptr<CachedShader>(new CachedShader(params, program_type, std::move(result)));
269 static_cast<u32>(code_size / sizeof(u64)),
270 static_cast<u32>(code_size_b / sizeof(u64)),
271 std::move(program_code), std::move(program_code_b));
272 disk_cache.SaveRaw(raw);
273} 272}
274 273
275CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, 274Shader CachedShader::CreateStageFromCache(const ShaderParameters& params,
276 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, 275 Maxwell::ShaderProgram program_type,
277 const PrecompiledPrograms& precompiled_programs, 276 GLShader::ProgramResult result) {
278 GLShader::ProgramResult result, u8* host_ptr) 277 return std::shared_ptr<CachedShader>(new CachedShader(params, program_type, std::move(result)));
279 : RasterizerCacheObject{host_ptr}, cpu_addr{cpu_addr}, unique_identifier{unique_identifier},
280 program_type{program_type}, disk_cache{disk_cache}, precompiled_programs{
281 precompiled_programs} {
282 code = std::move(result.first);
283 entries = result.second;
284 shader_length = entries.shader_length;
285} 278}
286 279
287std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) { 280std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) {
@@ -591,18 +584,17 @@ Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
591 memory_manager.GetPointer(program_addr_b)); 584 memory_manager.GetPointer(program_addr_b));
592 } 585 }
593 586
594 const u64 unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b); 587 const auto unique_identifier = GetUniqueIdentifier(program, program_code, program_code_b);
595 const VAddr cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)}; 588 const auto cpu_addr{*memory_manager.GpuToCpuAddress(program_addr)};
589 const ShaderParameters params{disk_cache, precompiled_programs, device, cpu_addr,
590 host_ptr, unique_identifier};
591
596 const auto found = precompiled_shaders.find(unique_identifier); 592 const auto found = precompiled_shaders.find(unique_identifier);
597 if (found != precompiled_shaders.end()) { 593 if (found == precompiled_shaders.end()) {
598 // Create a shader from the cache 594 shader = CachedShader::CreateStageFromMemory(params, program, std::move(program_code),
599 shader = std::make_shared<CachedShader>(cpu_addr, unique_identifier, program, disk_cache, 595 std::move(program_code_b));
600 precompiled_programs, found->second, host_ptr);
601 } else { 596 } else {
602 // Create a shader from guest memory 597 shader = CachedShader::CreateStageFromCache(params, program, found->second);
603 shader = std::make_shared<CachedShader>(
604 device, cpu_addr, unique_identifier, program, disk_cache, precompiled_programs,
605 std::move(program_code), std::move(program_code_b), host_ptr);
606 } 598 }
607 Register(shader); 599 Register(shader);
608 600
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 59bcb14e8..bbb53cdf4 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -42,17 +42,24 @@ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
42using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>; 42using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>;
43using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>; 43using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>;
44 44
45struct ShaderParameters {
46 ShaderDiskCacheOpenGL& disk_cache;
47 const PrecompiledPrograms& precompiled_programs;
48 const Device& device;
49 VAddr cpu_addr;
50 u8* host_ptr;
51 u64 unique_identifier;
52};
53
45class CachedShader final : public RasterizerCacheObject { 54class CachedShader final : public RasterizerCacheObject {
46public: 55public:
47 explicit CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier, 56 static Shader CreateStageFromMemory(const ShaderParameters& params,
48 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, 57 Maxwell::ShaderProgram program_type,
49 const PrecompiledPrograms& precompiled_programs, 58 ProgramCode&& program_code, ProgramCode&& program_code_b);
50 ProgramCode&& program_code, ProgramCode&& program_code_b, u8* host_ptr);
51 59
52 explicit CachedShader(VAddr cpu_addr, u64 unique_identifier, 60 static Shader CreateStageFromCache(const ShaderParameters& params,
53 Maxwell::ShaderProgram program_type, ShaderDiskCacheOpenGL& disk_cache, 61 Maxwell::ShaderProgram program_type,
54 const PrecompiledPrograms& precompiled_programs, 62 GLShader::ProgramResult result);
55 GLShader::ProgramResult result, u8* host_ptr);
56 63
57 VAddr GetCpuAddr() const override { 64 VAddr GetCpuAddr() const override {
58 return cpu_addr; 65 return cpu_addr;
@@ -71,6 +78,9 @@ public:
71 std::tuple<GLuint, BaseBindings> GetProgramHandle(const ProgramVariant& variant); 78 std::tuple<GLuint, BaseBindings> GetProgramHandle(const ProgramVariant& variant);
72 79
73private: 80private:
81 explicit CachedShader(const ShaderParameters& params, Maxwell::ShaderProgram program_type,
82 GLShader::ProgramResult result);
83
74 // Geometry programs. These are needed because GLSL needs an input topology but it's not 84 // Geometry programs. These are needed because GLSL needs an input topology but it's not
75 // declared by the hardware. Workaround this issue by generating a different shader per input 85 // declared by the hardware. Workaround this issue by generating a different shader per input
76 // topology class. 86 // topology class.
@@ -98,10 +108,9 @@ private:
98 ShaderDiskCacheOpenGL& disk_cache; 108 ShaderDiskCacheOpenGL& disk_cache;
99 const PrecompiledPrograms& precompiled_programs; 109 const PrecompiledPrograms& precompiled_programs;
100 110
101 std::size_t shader_length{};
102 GLShader::ShaderEntries entries; 111 GLShader::ShaderEntries entries;
103
104 std::string code; 112 std::string code;
113 std::size_t shader_length{};
105 114
106 std::unordered_map<ProgramVariant, CachedProgram> programs; 115 std::unordered_map<ProgramVariant, CachedProgram> programs;
107 std::unordered_map<ProgramVariant, GeometryPrograms> geometry_programs; 116 std::unordered_map<ProgramVariant, GeometryPrograms> geometry_programs;
diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp
index ac80b2fa2..33f1c385d 100644
--- a/src/yuzu/applets/web_browser.cpp
+++ b/src/yuzu/applets/web_browser.cpp
@@ -87,8 +87,8 @@ QtWebBrowser::QtWebBrowser(GMainWindow& main_window) {
87 87
88QtWebBrowser::~QtWebBrowser() = default; 88QtWebBrowser::~QtWebBrowser() = default;
89 89
90void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, 90void QtWebBrowser::OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
91 std::function<void()> finished_callback) { 91 std::function<void()> finished_callback) {
92 this->unpack_romfs_callback = std::move(unpack_romfs_callback); 92 this->unpack_romfs_callback = std::move(unpack_romfs_callback);
93 this->finished_callback = std::move(finished_callback); 93 this->finished_callback = std::move(finished_callback);
94 94
diff --git a/src/yuzu/applets/web_browser.h b/src/yuzu/applets/web_browser.h
index 1a3d67353..b38437e46 100644
--- a/src/yuzu/applets/web_browser.h
+++ b/src/yuzu/applets/web_browser.h
@@ -37,8 +37,8 @@ public:
37 explicit QtWebBrowser(GMainWindow& main_window); 37 explicit QtWebBrowser(GMainWindow& main_window);
38 ~QtWebBrowser() override; 38 ~QtWebBrowser() override;
39 39
40 void OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, 40 void OpenPageLocal(std::string_view url, std::function<void()> unpack_romfs_callback,
41 std::function<void()> finished_callback) override; 41 std::function<void()> finished_callback) override;
42 42
43signals: 43signals:
44 void MainWindowOpenPage(std::string_view filename, std::string_view additional_args) const; 44 void MainWindowOpenPage(std::string_view filename, std::string_view additional_args) const;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 10e5c5c38..73978ff5b 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -436,7 +436,8 @@ void Config::ReadControlValues() {
436void Config::ReadCoreValues() { 436void Config::ReadCoreValues() {
437 qt_config->beginGroup(QStringLiteral("Core")); 437 qt_config->beginGroup(QStringLiteral("Core"));
438 438
439 Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool(); 439 Settings::values.cpu_jit_enabled =
440 ReadSetting(QStringLiteral("cpu_jit_enabled"), true).toBool();
440 Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool(); 441 Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool();
441 442
442 qt_config->endGroup(); 443 qt_config->endGroup();
@@ -473,6 +474,9 @@ void Config::ReadDebuggingValues() {
473 ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString(); 474 ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString();
474 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool(); 475 Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
475 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool(); 476 Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
477 Settings::values.reporting_services =
478 ReadSetting(QStringLiteral("reporting_services"), false).toBool();
479 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
476 480
477 qt_config->endGroup(); 481 qt_config->endGroup();
478} 482}
@@ -691,7 +695,7 @@ void Config::ReadValues() {
691 ReadDataStorageValues(); 695 ReadDataStorageValues();
692 ReadSystemValues(); 696 ReadSystemValues();
693 ReadMiscellaneousValues(); 697 ReadMiscellaneousValues();
694 ReadDebugValues(); 698 ReadDebuggingValues();
695 ReadWebServiceValues(); 699 ReadWebServiceValues();
696 ReadDisabledAddOnValues(); 700 ReadDisabledAddOnValues();
697 ReadUIValues(); 701 ReadUIValues();
@@ -827,7 +831,7 @@ void Config::SaveControlValues() {
827void Config::SaveCoreValues() { 831void Config::SaveCoreValues() {
828 qt_config->beginGroup(QStringLiteral("Core")); 832 qt_config->beginGroup(QStringLiteral("Core"));
829 833
830 WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true); 834 WriteSetting(QStringLiteral("cpu_jit_enabled"), Settings::values.cpu_jit_enabled, true);
831 WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); 835 WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
832 836
833 qt_config->endGroup(); 837 qt_config->endGroup();
@@ -856,6 +860,7 @@ void Config::SaveDebuggingValues() {
856 QString::fromStdString(Settings::values.program_args), QStringLiteral("")); 860 QString::fromStdString(Settings::values.program_args), QStringLiteral(""));
857 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); 861 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
858 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); 862 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
863 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
859 864
860 qt_config->endGroup(); 865 qt_config->endGroup();
861} 866}
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index efc2bedfd..9a13bb797 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() {
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->dump_exefs->setChecked(Settings::values.dump_exefs); 37 ui->dump_exefs->setChecked(Settings::values.dump_exefs);
38 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso); 38 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
39 ui->reporting_services->setChecked(Settings::values.reporting_services);
40 ui->quest_flag->setChecked(Settings::values.quest_flag);
39} 41}
40 42
41void ConfigureDebug::ApplyConfiguration() { 43void ConfigureDebug::ApplyConfiguration() {
@@ -46,6 +48,8 @@ void ConfigureDebug::ApplyConfiguration() {
46 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 48 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
47 Settings::values.dump_exefs = ui->dump_exefs->isChecked(); 49 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
48 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked(); 50 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
51 Settings::values.reporting_services = ui->reporting_services->isChecked();
52 Settings::values.quest_flag = ui->quest_flag->isChecked();
49 Debugger::ToggleConsole(); 53 Debugger::ToggleConsole();
50 Log::Filter filter; 54 Log::Filter filter;
51 filter.ParseFilterString(Settings::values.log_filter); 55 filter.ParseFilterString(Settings::values.log_filter);
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 5ca9ce0e6..7e109cef0 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -7,7 +7,7 @@
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>400</width>
10 <height>357</height> 10 <height>474</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -155,6 +155,44 @@
155 </property> 155 </property>
156 </widget> 156 </widget>
157 </item> 157 </item>
158 <item>
159 <widget class="QCheckBox" name="reporting_services">
160 <property name="text">
161 <string>Enable Verbose Reporting Services</string>
162 </property>
163 </widget>
164 </item>
165 <item>
166 <widget class="QLabel" name="label_3">
167 <property name="font">
168 <font>
169 <italic>true</italic>
170 </font>
171 </property>
172 <property name="text">
173 <string>This will be reset automatically when yuzu closes.</string>
174 </property>
175 <property name="indent">
176 <number>20</number>
177 </property>
178 </widget>
179 </item>
180 </layout>
181 </widget>
182 </item>
183 <item>
184 <widget class="QGroupBox" name="groupBox_5">
185 <property name="title">
186 <string>Advanced</string>
187 </property>
188 <layout class="QVBoxLayout" name="verticalLayout">
189 <item>
190 <widget class="QCheckBox" name="quest_flag">
191 <property name="text">
192 <string>Kiosk (Quest) Mode</string>
193 </property>
194 </widget>
195 </item>
158 </layout> 196 </layout>
159 </widget> 197 </widget>
160 </item> 198 </item>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 06d368dfc..7a6e921cd 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -22,8 +22,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
22 22
23 connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this, 23 connect(ui->toggle_deepscan, &QCheckBox::stateChanged, this,
24 [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); 24 [] { UISettings::values.is_game_list_reload_pending.exchange(true); });
25
26 ui->use_cpu_jit->setEnabled(!Core::System::GetInstance().IsPoweredOn());
27} 25}
28 26
29ConfigureGeneral::~ConfigureGeneral() = default; 27ConfigureGeneral::~ConfigureGeneral() = default;
@@ -33,7 +31,6 @@ void ConfigureGeneral::SetConfiguration() {
33 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); 31 ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
34 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); 32 ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
35 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); 33 ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme));
36 ui->use_cpu_jit->setChecked(Settings::values.use_cpu_jit);
37} 34}
38 35
39void ConfigureGeneral::ApplyConfiguration() { 36void ConfigureGeneral::ApplyConfiguration() {
@@ -42,8 +39,6 @@ void ConfigureGeneral::ApplyConfiguration() {
42 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); 39 UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
43 UISettings::values.theme = 40 UISettings::values.theme =
44 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); 41 ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString();
45
46 Settings::values.use_cpu_jit = ui->use_cpu_jit->isChecked();
47} 42}
48 43
49void ConfigureGeneral::changeEvent(QEvent* event) { 44void ConfigureGeneral::changeEvent(QEvent* event) {
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 1a5721fe7..184fdd329 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -51,26 +51,6 @@
51 </widget> 51 </widget>
52 </item> 52 </item>
53 <item> 53 <item>
54 <widget class="QGroupBox" name="PerformanceGroupBox">
55 <property name="title">
56 <string>Performance</string>
57 </property>
58 <layout class="QHBoxLayout" name="PerformanceHorizontalLayout">
59 <item>
60 <layout class="QVBoxLayout" name="PerformanceVerticalLayout">
61 <item>
62 <widget class="QCheckBox" name="use_cpu_jit">
63 <property name="text">
64 <string>Enable CPU JIT</string>
65 </property>
66 </widget>
67 </item>
68 </layout>
69 </item>
70 </layout>
71 </widget>
72 </item>
73 <item>
74 <widget class="QGroupBox" name="theme_group_box"> 54 <widget class="QGroupBox" name="theme_group_box">
75 <property name="title"> 55 <property name="title">
76 <string>Theme</string> 56 <string>Theme</string>
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 83d675773..1885587af 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -468,8 +468,7 @@ void GameList::LoadInterfaceLayout() {
468 468
469const QStringList GameList::supported_file_extensions = { 469const QStringList GameList::supported_file_extensions = {
470 QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"), 470 QStringLiteral("nso"), QStringLiteral("nro"), QStringLiteral("nca"),
471 QStringLiteral("xci"), QStringLiteral("nsp"), 471 QStringLiteral("xci"), QStringLiteral("nsp"), QStringLiteral("kip")};
472};
473 472
474void GameList::RefreshGameDirectory() { 473void GameList::RefreshGameDirectory() {
475 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) { 474 if (!UISettings::values.game_directory_path.isEmpty() && current_worker != nullptr) {
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 443fec249..ae21f4753 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -817,11 +817,13 @@ bool GMainWindow::LoadROM(const QString& filename) {
817 system.SetGPUDebugContext(debug_context); 817 system.SetGPUDebugContext(debug_context);
818 818
819 system.SetAppletFrontendSet({ 819 system.SetAppletFrontendSet({
820 std::make_unique<QtErrorDisplay>(*this), 820 nullptr, // Parental Controls
821 nullptr, 821 std::make_unique<QtErrorDisplay>(*this), //
822 std::make_unique<QtProfileSelector>(*this), 822 nullptr, // Photo Viewer
823 std::make_unique<QtSoftwareKeyboard>(*this), 823 std::make_unique<QtProfileSelector>(*this), //
824 std::make_unique<QtWebBrowser>(*this), 824 std::make_unique<QtSoftwareKeyboard>(*this), //
825 std::make_unique<QtWebBrowser>(*this), //
826 nullptr, // E-Commerce
825 }); 827 });
826 828
827 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; 829 const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index f3817bb87..30b22341b 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -340,7 +340,7 @@ void Config::ReadValues() {
340 } 340 }
341 341
342 // Core 342 // Core
343 Settings::values.use_cpu_jit = sdl2_config->GetBoolean("Core", "use_cpu_jit", true); 343 Settings::values.cpu_jit_enabled = sdl2_config->GetBoolean("Core", "cpu_jit_enabled", true);
344 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false); 344 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
345 345
346 // Renderer 346 // Renderer
@@ -381,6 +381,9 @@ void Config::ReadValues() {
381 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", ""); 381 Settings::values.program_args = sdl2_config->Get("Debugging", "program_args", "");
382 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); 382 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
383 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); 383 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
384 Settings::values.reporting_services =
385 sdl2_config->GetBoolean("Debugging", "reporting_services", false);
386 Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
384 387
385 const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); 388 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
386 std::stringstream ss(title_list); 389 std::stringstream ss(title_list);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 6538af098..4f1add434 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -78,7 +78,7 @@ touch_device=
78[Core] 78[Core]
79# Whether to use the Just-In-Time (JIT) compiler for CPU emulation 79# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
80# 0: Interpreter (slow), 1 (default): JIT (fast) 80# 0: Interpreter (slow), 1 (default): JIT (fast)
81use_cpu_jit = 81cpu_jit_enabled =
82 82
83# Whether to use multi-core for CPU emulation 83# Whether to use multi-core for CPU emulation
84# 0 (default): Disabled, 1: Enabled 84# 0 (default): Disabled, 1: Enabled
@@ -224,6 +224,9 @@ gdbstub_port=24689
224dump_exefs=false 224dump_exefs=false
225# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them 225# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
226dump_nso=false 226dump_nso=false
227# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
228# false: Retail/Normal Mode (default), true: Kiosk Mode
229quest_flag =
227 230
228[WebService] 231[WebService]
229# Whether or not to enable telemetry 232# Whether or not to enable telemetry
diff --git a/src/yuzu_tester/CMakeLists.txt b/src/yuzu_tester/CMakeLists.txt
new file mode 100644
index 000000000..06c2ee011
--- /dev/null
+++ b/src/yuzu_tester/CMakeLists.txt
@@ -0,0 +1,34 @@
1set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules)
2
3add_executable(yuzu-tester
4 config.cpp
5 config.h
6 default_ini.h
7 emu_window/emu_window_sdl2_hide.cpp
8 emu_window/emu_window_sdl2_hide.h
9 resource.h
10 service/yuzutest.cpp
11 service/yuzutest.h
12 yuzu.cpp
13 yuzu.rc
14)
15
16create_target_directory_groups(yuzu-tester)
17
18target_link_libraries(yuzu-tester PRIVATE common core input_common)
19target_link_libraries(yuzu-tester PRIVATE inih glad)
20if (MSVC)
21 target_link_libraries(yuzu-tester PRIVATE getopt)
22endif()
23target_link_libraries(yuzu-tester PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads)
24
25if(UNIX AND NOT APPLE)
26 install(TARGETS yuzu-tester RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
27endif()
28
29if (MSVC)
30 include(CopyYuzuSDLDeps)
31 include(CopyYuzuUnicornDeps)
32 copy_yuzu_SDL_deps(yuzu-tester)
33 copy_yuzu_unicorn_deps(yuzu-tester)
34endif()
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
new file mode 100644
index 000000000..b96b7d279
--- /dev/null
+++ b/src/yuzu_tester/config.cpp
@@ -0,0 +1,184 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <sstream>
7#include <SDL.h>
8#include <inih/cpp/INIReader.h>
9#include "common/file_util.h"
10#include "common/logging/log.h"
11#include "common/param_package.h"
12#include "core/hle/service/acc/profile_manager.h"
13#include "core/settings.h"
14#include "input_common/main.h"
15#include "yuzu_tester/config.h"
16#include "yuzu_tester/default_ini.h"
17
18Config::Config() {
19 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
20 sdl2_config_loc =
21 FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-tester-config.ini";
22 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
23
24 Reload();
25}
26
27Config::~Config() = default;
28
29bool Config::LoadINI(const std::string& default_contents, bool retry) {
30 const char* location = this->sdl2_config_loc.c_str();
31 if (sdl2_config->ParseError() < 0) {
32 if (retry) {
33 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
34 FileUtil::CreateFullPath(location);
35 FileUtil::WriteStringToFile(true, default_contents, location);
36 sdl2_config = std::make_unique<INIReader>(location); // Reopen file
37
38 return LoadINI(default_contents, false);
39 }
40 LOG_ERROR(Config, "Failed.");
41 return false;
42 }
43 LOG_INFO(Config, "Successfully loaded {}", location);
44 return true;
45}
46
47void Config::ReadValues() {
48 // Controls
49 for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
50 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
51 Settings::values.players[p].buttons[i] = "";
52 }
53
54 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
55 Settings::values.players[p].analogs[i] = "";
56 }
57 }
58
59 Settings::values.mouse_enabled = false;
60 for (int i = 0; i < Settings::NativeMouseButton::NumMouseButtons; ++i) {
61 Settings::values.mouse_buttons[i] = "";
62 }
63
64 Settings::values.motion_device = "";
65
66 Settings::values.keyboard_enabled = false;
67
68 Settings::values.debug_pad_enabled = false;
69 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
70 Settings::values.debug_pad_buttons[i] = "";
71 }
72
73 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
74 Settings::values.debug_pad_analogs[i] = "";
75 }
76
77 Settings::values.touchscreen.enabled = "";
78 Settings::values.touchscreen.device = "";
79 Settings::values.touchscreen.finger = 0;
80 Settings::values.touchscreen.rotation_angle = 0;
81 Settings::values.touchscreen.diameter_x = 15;
82 Settings::values.touchscreen.diameter_y = 15;
83
84 // Data Storage
85 Settings::values.use_virtual_sd =
86 sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
87 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
88 sdl2_config->Get("Data Storage", "nand_directory",
89 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
90 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
91 sdl2_config->Get("Data Storage", "sdmc_directory",
92 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
93
94 // System
95 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
96 const auto size = sdl2_config->GetInteger("System", "users_size", 0);
97
98 Settings::values.current_user = std::clamp<int>(
99 sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1);
100
101 const auto rng_seed_enabled = sdl2_config->GetBoolean("System", "rng_seed_enabled", false);
102 if (rng_seed_enabled) {
103 Settings::values.rng_seed = sdl2_config->GetInteger("System", "rng_seed", 0);
104 } else {
105 Settings::values.rng_seed = std::nullopt;
106 }
107
108 const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
109 if (custom_rtc_enabled) {
110 Settings::values.custom_rtc =
111 std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0));
112 } else {
113 Settings::values.custom_rtc = std::nullopt;
114 }
115
116 // Core
117 Settings::values.cpu_jit_enabled = sdl2_config->GetBoolean("Core", "cpu_jit_enabled", true);
118 Settings::values.use_multi_core = sdl2_config->GetBoolean("Core", "use_multi_core", false);
119
120 // Renderer
121 Settings::values.resolution_factor =
122 static_cast<float>(sdl2_config->GetReal("Renderer", "resolution_factor", 1.0));
123 Settings::values.use_frame_limit = false;
124 Settings::values.frame_limit = 100;
125 Settings::values.use_disk_shader_cache =
126 sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", false);
127 Settings::values.use_accurate_gpu_emulation =
128 sdl2_config->GetBoolean("Renderer", "use_accurate_gpu_emulation", false);
129 Settings::values.use_asynchronous_gpu_emulation =
130 sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false);
131
132 Settings::values.bg_red = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_red", 0.0));
133 Settings::values.bg_green =
134 static_cast<float>(sdl2_config->GetReal("Renderer", "bg_green", 0.0));
135 Settings::values.bg_blue = static_cast<float>(sdl2_config->GetReal("Renderer", "bg_blue", 0.0));
136
137 // Audio
138 Settings::values.sink_id = "null";
139 Settings::values.enable_audio_stretching = false;
140 Settings::values.audio_device_id = "auto";
141 Settings::values.volume = 0;
142
143 Settings::values.language_index = sdl2_config->GetInteger("System", "language_index", 1);
144
145 // Miscellaneous
146 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace");
147 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
148
149 // Debugging
150 Settings::values.use_gdbstub = false;
151 Settings::values.program_args = "";
152 Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false);
153 Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false);
154
155 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
156 std::stringstream ss(title_list);
157 std::string line;
158 while (std::getline(ss, line, '|')) {
159 const auto title_id = std::stoul(line, nullptr, 16);
160 const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, "");
161
162 std::stringstream inner_ss(disabled_list);
163 std::string inner_line;
164 std::vector<std::string> out;
165 while (std::getline(inner_ss, inner_line, '|')) {
166 out.push_back(inner_line);
167 }
168
169 Settings::values.disabled_addons.insert_or_assign(title_id, out);
170 }
171
172 // Web Service
173 Settings::values.enable_telemetry =
174 sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
175 Settings::values.web_api_url =
176 sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org");
177 Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", "");
178 Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", "");
179}
180
181void Config::Reload() {
182 LoadINI(DefaultINI::sdl2_config_file);
183 ReadValues();
184}
diff --git a/src/yuzu_tester/config.h b/src/yuzu_tester/config.h
new file mode 100644
index 000000000..3b68e5bc9
--- /dev/null
+++ b/src/yuzu_tester/config.h
@@ -0,0 +1,24 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <string>
9
10class INIReader;
11
12class Config {
13 std::unique_ptr<INIReader> sdl2_config;
14 std::string sdl2_config_loc;
15
16 bool LoadINI(const std::string& default_contents = "", bool retry = true);
17 void ReadValues();
18
19public:
20 Config();
21 ~Config();
22
23 void Reload();
24};
diff --git a/src/yuzu_tester/default_ini.h b/src/yuzu_tester/default_ini.h
new file mode 100644
index 000000000..0f880d8c7
--- /dev/null
+++ b/src/yuzu_tester/default_ini.h
@@ -0,0 +1,150 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7namespace DefaultINI {
8
9const char* sdl2_config_file = R"(
10[Core]
11# Whether to use the Just-In-Time (JIT) compiler for CPU emulation
12# 0: Interpreter (slow), 1 (default): JIT (fast)
13cpu_jit_enabled =
14
15# Whether to use multi-core for CPU emulation
16# 0 (default): Disabled, 1: Enabled
17use_multi_core=
18
19[Renderer]
20# Whether to use software or hardware rendering.
21# 0: Software, 1 (default): Hardware
22use_hw_renderer =
23
24# Whether to use the Just-In-Time (JIT) compiler for shader emulation
25# 0: Interpreter (slow), 1 (default): JIT (fast)
26use_shader_jit =
27
28# Resolution scale factor
29# 0: Auto (scales resolution to window size), 1: Native Switch screen resolution, Otherwise a scale
30# factor for the Switch resolution
31resolution_factor =
32
33# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
34# 0 (default): Off, 1: On
35use_vsync =
36
37# Whether to use disk based shader cache
38# 0 (default): Off, 1 : On
39use_disk_shader_cache =
40
41# Whether to use accurate GPU emulation
42# 0 (default): Off (fast), 1 : On (slow)
43use_accurate_gpu_emulation =
44
45# Whether to use asynchronous GPU emulation
46# 0 : Off (slow), 1 (default): On (fast)
47use_asynchronous_gpu_emulation =
48
49# The clear color for the renderer. What shows up on the sides of the bottom screen.
50# Must be in range of 0.0-1.0. Defaults to 1.0 for all.
51bg_red =
52bg_blue =
53bg_green =
54
55[Layout]
56# Layout for the screen inside the render window.
57# 0 (default): Default Top Bottom Screen, 1: Single Screen Only, 2: Large Screen Small Screen
58layout_option =
59
60# Toggle custom layout (using the settings below) on or off.
61# 0 (default): Off, 1: On
62custom_layout =
63
64# Screen placement when using Custom layout option
65# 0x, 0y is the top left corner of the render window.
66custom_top_left =
67custom_top_top =
68custom_top_right =
69custom_top_bottom =
70custom_bottom_left =
71custom_bottom_top =
72custom_bottom_right =
73custom_bottom_bottom =
74
75# Swaps the prominent screen with the other screen.
76# For example, if Single Screen is chosen, setting this to 1 will display the bottom screen instead of the top screen.
77# 0 (default): Top Screen is prominent, 1: Bottom Screen is prominent
78swap_screen =
79
80[Data Storage]
81# Whether to create a virtual SD card.
82# 1 (default): Yes, 0: No
83use_virtual_sd =
84
85[System]
86# Whether the system is docked
87# 1: Yes, 0 (default): No
88use_docked_mode =
89
90# Allow the use of NFC in games
91# 1 (default): Yes, 0 : No
92enable_nfc =
93
94# Sets the seed for the RNG generator built into the switch
95# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
96rng_seed_enabled =
97rng_seed =
98
99# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
100# This will auto-increment, with the time set being the time the game is started
101# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
102custom_rtc_enabled =
103custom_rtc =
104
105# Sets the account username, max length is 32 characters
106# yuzu (default)
107username = yuzu
108
109# Sets the systems language index
110# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
111# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
112# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese
113language_index =
114
115# The system region that yuzu will use during emulation
116# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
117region_value =
118
119[Miscellaneous]
120# A filter which removes logs below a certain logging level.
121# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
122log_filter = *:Trace
123
124[Debugging]
125# Arguments to be passed to argv/argc in the emulated program. It is preferable to use the testing service datastring
126program_args=
127# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
128dump_exefs=false
129# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
130dump_nso=false
131
132[WebService]
133# Whether or not to enable telemetry
134# 0: No, 1 (default): Yes
135enable_telemetry =
136# URL for Web API
137web_api_url = https://api.yuzu-emu.org
138# Username and token for yuzu Web Service
139# See https://profile.yuzu-emu.org/ for more info
140yuzu_username =
141yuzu_token =
142
143[AddOns]
144# Used to disable add-ons
145# List of title IDs of games that will have add-ons disabled (separated by '|'):
146title_ids =
147# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
148# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
149)";
150}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
new file mode 100644
index 000000000..e7fe8decf
--- /dev/null
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -0,0 +1,122 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <cstdlib>
7#include <string>
8#define SDL_MAIN_HANDLED
9#include <SDL.h>
10#include <fmt/format.h>
11#include <glad/glad.h>
12#include "common/logging/log.h"
13#include "common/scm_rev.h"
14#include "core/settings.h"
15#include "input_common/main.h"
16#include "yuzu_tester/emu_window/emu_window_sdl2_hide.h"
17
18bool EmuWindow_SDL2_Hide::SupportsRequiredGLExtensions() {
19 std::vector<std::string> unsupported_ext;
20
21 if (!GLAD_GL_ARB_direct_state_access)
22 unsupported_ext.push_back("ARB_direct_state_access");
23 if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
24 unsupported_ext.push_back("ARB_vertex_type_10f_11f_11f_rev");
25 if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
26 unsupported_ext.push_back("ARB_texture_mirror_clamp_to_edge");
27 if (!GLAD_GL_ARB_multi_bind)
28 unsupported_ext.push_back("ARB_multi_bind");
29
30 // Extensions required to support some texture formats.
31 if (!GLAD_GL_EXT_texture_compression_s3tc)
32 unsupported_ext.push_back("EXT_texture_compression_s3tc");
33 if (!GLAD_GL_ARB_texture_compression_rgtc)
34 unsupported_ext.push_back("ARB_texture_compression_rgtc");
35 if (!GLAD_GL_ARB_depth_buffer_float)
36 unsupported_ext.push_back("ARB_depth_buffer_float");
37
38 for (const std::string& ext : unsupported_ext)
39 LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext);
40
41 return unsupported_ext.empty();
42}
43
44EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
45 // Initialize the window
46 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
47 LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
48 exit(1);
49 }
50
51 InputCommon::Init();
52
53 SDL_SetMainReady();
54
55 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
56 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
57 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
58 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
59 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
60 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
61 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
62 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
63
64 std::string window_title = fmt::format("yuzu-tester {} | {}-{}", Common::g_build_fullname,
65 Common::g_scm_branch, Common::g_scm_desc);
66 render_window = SDL_CreateWindow(window_title.c_str(),
67 SDL_WINDOWPOS_UNDEFINED, // x position
68 SDL_WINDOWPOS_UNDEFINED, // y position
69 Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
70 SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE |
71 SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
72
73 if (render_window == nullptr) {
74 LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError());
75 exit(1);
76 }
77
78 gl_context = SDL_GL_CreateContext(render_window);
79
80 if (gl_context == nullptr) {
81 LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError());
82 exit(1);
83 }
84
85 if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) {
86 LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError());
87 exit(1);
88 }
89
90 if (!SupportsRequiredGLExtensions()) {
91 LOG_CRITICAL(Frontend, "GPU does not support all required OpenGL extensions! Exiting...");
92 exit(1);
93 }
94
95 SDL_PumpEvents();
96 SDL_GL_SetSwapInterval(false);
97 LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,
98 Common::g_scm_branch, Common::g_scm_desc);
99 Settings::LogSettings();
100
101 DoneCurrent();
102}
103
104EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
105 InputCommon::Shutdown();
106 SDL_GL_DeleteContext(gl_context);
107 SDL_Quit();
108}
109
110void EmuWindow_SDL2_Hide::SwapBuffers() {
111 SDL_GL_SwapWindow(render_window);
112}
113
114void EmuWindow_SDL2_Hide::PollEvents() {}
115
116void EmuWindow_SDL2_Hide::MakeCurrent() {
117 SDL_GL_MakeCurrent(render_window, gl_context);
118}
119
120void EmuWindow_SDL2_Hide::DoneCurrent() {
121 SDL_GL_MakeCurrent(render_window, nullptr);
122}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
new file mode 100644
index 000000000..1a8953c75
--- /dev/null
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -0,0 +1,41 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/frontend/emu_window.h"
8
9struct SDL_Window;
10
11class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
12public:
13 explicit EmuWindow_SDL2_Hide();
14 ~EmuWindow_SDL2_Hide();
15
16 /// Swap buffers to display the next frame
17 void SwapBuffers() override;
18
19 /// Polls window events
20 void PollEvents() override;
21
22 /// Makes the graphics context current for the caller thread
23 void MakeCurrent() override;
24
25 /// Releases the GL context from the caller thread
26 void DoneCurrent() override;
27
28 /// Whether the window is still open, and a close request hasn't yet been sent
29 bool IsOpen() const;
30
31private:
32 /// Whether the GPU and driver supports the OpenGL extension required
33 bool SupportsRequiredGLExtensions();
34
35 /// Internal SDL2 render window
36 SDL_Window* render_window;
37
38 using SDL_GLContext = void*;
39 /// The OpenGL context associated with the window
40 SDL_GLContext gl_context;
41};
diff --git a/src/yuzu_tester/resource.h b/src/yuzu_tester/resource.h
new file mode 100644
index 000000000..df8e459e4
--- /dev/null
+++ b/src/yuzu_tester/resource.h
@@ -0,0 +1,16 @@
1//{{NO_DEPENDENCIES}}
2// Microsoft Visual C++ generated include file.
3// Used by pcafe.rc
4//
5#define IDI_ICON3 103
6
7// Next default values for new objects
8//
9#ifdef APSTUDIO_INVOKED
10#ifndef APSTUDIO_READONLY_SYMBOLS
11#define _APS_NEXT_RESOURCE_VALUE 105
12#define _APS_NEXT_COMMAND_VALUE 40001
13#define _APS_NEXT_CONTROL_VALUE 1001
14#define _APS_NEXT_SYMED_VALUE 101
15#endif
16#endif
diff --git a/src/yuzu_tester/service/yuzutest.cpp b/src/yuzu_tester/service/yuzutest.cpp
new file mode 100644
index 000000000..85d3f436b
--- /dev/null
+++ b/src/yuzu_tester/service/yuzutest.cpp
@@ -0,0 +1,112 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "common/string_util.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/service.h"
9#include "core/hle/service/sm/sm.h"
10#include "yuzu_tester/service/yuzutest.h"
11
12namespace Service::Yuzu {
13
14constexpr u64 SERVICE_VERSION = 0x00000002;
15
16class YuzuTest final : public ServiceFramework<YuzuTest> {
17public:
18 explicit YuzuTest(std::string data,
19 std::function<void(std::vector<TestResult>)> finish_callback)
20 : ServiceFramework{"yuzutest"}, data(std::move(data)),
21 finish_callback(std::move(finish_callback)) {
22 static const FunctionInfo functions[] = {
23 {0, &YuzuTest::Initialize, "Initialize"},
24 {1, &YuzuTest::GetServiceVersion, "GetServiceVersion"},
25 {2, &YuzuTest::GetData, "GetData"},
26 {10, &YuzuTest::StartIndividual, "StartIndividual"},
27 {20, &YuzuTest::FinishIndividual, "FinishIndividual"},
28 {100, &YuzuTest::ExitProgram, "ExitProgram"},
29 };
30
31 RegisterHandlers(functions);
32 }
33
34private:
35 void Initialize(Kernel::HLERequestContext& ctx) {
36 LOG_DEBUG(Frontend, "called");
37 IPC::ResponseBuilder rb{ctx, 2};
38 rb.Push(RESULT_SUCCESS);
39 }
40
41 void GetServiceVersion(Kernel::HLERequestContext& ctx) {
42 LOG_DEBUG(Frontend, "called");
43 IPC::ResponseBuilder rb{ctx, 4};
44 rb.Push(RESULT_SUCCESS);
45 rb.Push(SERVICE_VERSION);
46 }
47
48 void GetData(Kernel::HLERequestContext& ctx) {
49 LOG_DEBUG(Frontend, "called");
50 const auto size = ctx.GetWriteBufferSize();
51 const auto write_size = std::min(size, data.size());
52 ctx.WriteBuffer(data.data(), write_size);
53
54 IPC::ResponseBuilder rb{ctx, 3};
55 rb.Push(RESULT_SUCCESS);
56 rb.Push<u32>(write_size);
57 }
58
59 void StartIndividual(Kernel::HLERequestContext& ctx) {
60 const auto name_raw = ctx.ReadBuffer();
61
62 const auto name = Common::StringFromFixedZeroTerminatedBuffer(
63 reinterpret_cast<const char*>(name_raw.data()), name_raw.size());
64
65 LOG_DEBUG(Frontend, "called, name={}", name);
66
67 IPC::ResponseBuilder rb{ctx, 2};
68 rb.Push(RESULT_SUCCESS);
69 }
70
71 void FinishIndividual(Kernel::HLERequestContext& ctx) {
72 IPC::RequestParser rp{ctx};
73
74 const auto code = rp.PopRaw<u32>();
75
76 const auto result_data_raw = ctx.ReadBuffer();
77 const auto test_name_raw = ctx.ReadBuffer(1);
78
79 const auto data = Common::StringFromFixedZeroTerminatedBuffer(
80 reinterpret_cast<const char*>(result_data_raw.data()), result_data_raw.size());
81 const auto test_name = Common::StringFromFixedZeroTerminatedBuffer(
82 reinterpret_cast<const char*>(test_name_raw.data()), test_name_raw.size());
83
84 LOG_INFO(Frontend, "called, result_code={:08X}, data={}, name={}", code, data, test_name);
85
86 results.push_back({code, data, test_name});
87
88 IPC::ResponseBuilder rb{ctx, 2};
89 rb.Push(RESULT_SUCCESS);
90 }
91
92 void ExitProgram(Kernel::HLERequestContext& ctx) {
93 LOG_DEBUG(Frontend, "called");
94
95 IPC::ResponseBuilder rb{ctx, 2};
96 rb.Push(RESULT_SUCCESS);
97
98 finish_callback(std::move(results));
99 }
100
101 std::string data;
102
103 std::vector<TestResult> results;
104 std::function<void(std::vector<TestResult>)> finish_callback;
105};
106
107void InstallInterfaces(SM::ServiceManager& sm, std::string data,
108 std::function<void(std::vector<TestResult>)> finish_callback) {
109 std::make_shared<YuzuTest>(data, finish_callback)->InstallAsService(sm);
110}
111
112} // namespace Service::Yuzu
diff --git a/src/yuzu_tester/service/yuzutest.h b/src/yuzu_tester/service/yuzutest.h
new file mode 100644
index 000000000..eca129c8c
--- /dev/null
+++ b/src/yuzu_tester/service/yuzutest.h
@@ -0,0 +1,25 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <string>
9
10namespace Service::SM {
11class ServiceManager;
12}
13
14namespace Service::Yuzu {
15
16struct TestResult {
17 u32 code;
18 std::string data;
19 std::string name;
20};
21
22void InstallInterfaces(SM::ServiceManager& sm, std::string data,
23 std::function<void(std::vector<TestResult>)> finish_callback);
24
25} // namespace Service::Yuzu
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
new file mode 100644
index 000000000..b589c3de3
--- /dev/null
+++ b/src/yuzu_tester/yuzu.cpp
@@ -0,0 +1,267 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <iostream>
6#include <memory>
7#include <string>
8#include <thread>
9
10#include <fmt/ostream.h>
11
12#include "common/common_paths.h"
13#include "common/detached_tasks.h"
14#include "common/file_util.h"
15#include "common/logging/backend.h"
16#include "common/logging/filter.h"
17#include "common/logging/log.h"
18#include "common/microprofile.h"
19#include "common/scm_rev.h"
20#include "common/scope_exit.h"
21#include "common/string_util.h"
22#include "common/telemetry.h"
23#include "core/core.h"
24#include "core/crypto/key_manager.h"
25#include "core/file_sys/vfs_real.h"
26#include "core/hle/service/filesystem/filesystem.h"
27#include "core/loader/loader.h"
28#include "core/settings.h"
29#include "core/telemetry_session.h"
30#include "video_core/renderer_base.h"
31#include "yuzu_tester/config.h"
32#include "yuzu_tester/emu_window/emu_window_sdl2_hide.h"
33#include "yuzu_tester/service/yuzutest.h"
34
35#ifdef _WIN32
36// windows.h needs to be included before shellapi.h
37#include <windows.h>
38
39#include <shellapi.h>
40#endif
41
42#undef _UNICODE
43#include <getopt.h>
44#ifndef _MSC_VER
45#include <unistd.h>
46#endif
47
48#ifdef _WIN32
49extern "C" {
50// tells Nvidia and AMD drivers to use the dedicated GPU by default on laptops with switchable
51// graphics
52__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
53__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
54}
55#endif
56
57static void PrintHelp(const char* argv0) {
58 std::cout << "Usage: " << argv0
59 << " [options] <filename>\n"
60 "-h, --help Display this help and exit\n"
61 "-v, --version Output version information and exit\n"
62 "-d, --datastring Pass following string as data to test service command #2\n"
63 "-l, --log Log to console in addition to file (will log to file only "
64 "by default)\n";
65}
66
67static void PrintVersion() {
68 std::cout << "yuzu [Test Utility] " << Common::g_scm_branch << " " << Common::g_scm_desc
69 << std::endl;
70}
71
72static void InitializeLogging(bool console) {
73 Log::Filter log_filter(Log::Level::Debug);
74 log_filter.ParseFilterString(Settings::values.log_filter);
75 Log::SetGlobalFilter(log_filter);
76
77 if (console)
78 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
79
80 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
81 FileUtil::CreateFullPath(log_dir);
82 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
83#ifdef _WIN32
84 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
85#endif
86}
87
88/// Application entry point
89int main(int argc, char** argv) {
90 Common::DetachedTasks detached_tasks;
91 Config config;
92
93 int option_index = 0;
94
95 char* endarg;
96#ifdef _WIN32
97 int argc_w;
98 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
99
100 if (argv_w == nullptr) {
101 std::cout << "Failed to get command line arguments" << std::endl;
102 return -1;
103 }
104#endif
105 std::string filepath;
106
107 static struct option long_options[] = {
108 {"help", no_argument, 0, 'h'},
109 {"version", no_argument, 0, 'v'},
110 {"datastring", optional_argument, 0, 'd'},
111 {"log", no_argument, 0, 'l'},
112 {0, 0, 0, 0},
113 };
114
115 bool console_log = false;
116 std::string datastring;
117
118 while (optind < argc) {
119 int arg = getopt_long(argc, argv, "hvdl::", long_options, &option_index);
120 if (arg != -1) {
121 switch (static_cast<char>(arg)) {
122 case 'h':
123 PrintHelp(argv[0]);
124 return 0;
125 case 'v':
126 PrintVersion();
127 return 0;
128 case 'd':
129 datastring = argv[optind];
130 ++optind;
131 break;
132 case 'l':
133 console_log = true;
134 break;
135 }
136 } else {
137#ifdef _WIN32
138 filepath = Common::UTF16ToUTF8(argv_w[optind]);
139#else
140 filepath = argv[optind];
141#endif
142 optind++;
143 }
144 }
145
146 InitializeLogging(console_log);
147
148#ifdef _WIN32
149 LocalFree(argv_w);
150#endif
151
152 MicroProfileOnThreadCreate("EmuThread");
153 SCOPE_EXIT({ MicroProfileShutdown(); });
154
155 if (filepath.empty()) {
156 LOG_CRITICAL(Frontend, "Failed to load application: No application specified");
157 std::cout << "Failed to load application: No application specified" << std::endl;
158 PrintHelp(argv[0]);
159 return -1;
160 }
161
162 Settings::values.use_gdbstub = false;
163 Settings::Apply();
164
165 std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
166
167 if (!Settings::values.use_multi_core) {
168 // Single core mode must acquire OpenGL context for entire emulation session
169 emu_window->MakeCurrent();
170 }
171
172 bool finished = false;
173 int return_value = 0;
174 const auto callback = [&finished,
175 &return_value](std::vector<Service::Yuzu::TestResult> results) {
176 finished = true;
177 return_value = 0;
178
179 // Find the minimum length needed to fully enclose all test names (and the header field) in
180 // the fmt::format column by first finding the maximum size of any test name and comparing
181 // that to 9, the string length of 'Test Name'
182 const auto needed_length_name =
183 std::max<u64>(std::max_element(results.begin(), results.end(),
184 [](const auto& lhs, const auto& rhs) {
185 return lhs.name.size() < rhs.name.size();
186 })
187 ->name.size(),
188 9ull);
189
190 std::size_t passed = 0;
191 std::size_t failed = 0;
192
193 std::cout << fmt::format("Result [Res Code] | {:<{}} | Extra Data", "Test Name",
194 needed_length_name)
195 << std::endl;
196
197 for (const auto& res : results) {
198 const auto main_res = res.code == 0 ? "PASSED" : "FAILED";
199 if (res.code == 0)
200 ++passed;
201 else
202 ++failed;
203 std::cout << fmt::format("{} [{:08X}] | {:<{}} | {}", main_res, res.code, res.name,
204 needed_length_name, res.data)
205 << std::endl;
206 }
207
208 std::cout << std::endl
209 << fmt::format("{:4d} Passed | {:4d} Failed | {:4d} Total | {:2.2f} Passed Ratio",
210 passed, failed, passed + failed,
211 static_cast<float>(passed) / (passed + failed))
212 << std::endl
213 << (failed == 0 ? "PASSED" : "FAILED") << std::endl;
214
215 if (failed > 0)
216 return_value = -1;
217 };
218
219 Core::System& system{Core::System::GetInstance()};
220 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
221 Service::FileSystem::CreateFactories(*system.GetFilesystem());
222
223 SCOPE_EXIT({ system.Shutdown(); });
224
225 const Core::System::ResultStatus load_result{system.Load(*emu_window, filepath)};
226
227 switch (load_result) {
228 case Core::System::ResultStatus::ErrorGetLoader:
229 LOG_CRITICAL(Frontend, "Failed to obtain loader for %s!", filepath.c_str());
230 return -1;
231 case Core::System::ResultStatus::ErrorLoader:
232 LOG_CRITICAL(Frontend, "Failed to load ROM!");
233 return -1;
234 case Core::System::ResultStatus::ErrorNotInitialized:
235 LOG_CRITICAL(Frontend, "CPUCore not initialized");
236 return -1;
237 case Core::System::ResultStatus::ErrorVideoCore:
238 LOG_CRITICAL(Frontend, "Failed to initialize VideoCore!");
239 return -1;
240 case Core::System::ResultStatus::Success:
241 break; // Expected case
242 default:
243 if (static_cast<u32>(load_result) >
244 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
245 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
246 const u16 error_id = static_cast<u16>(load_result) - loader_id;
247 LOG_CRITICAL(Frontend,
248 "While attempting to load the ROM requested, an error occured. Please "
249 "refer to the yuzu wiki for more information or the yuzu discord for "
250 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}",
251 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id));
252 }
253 }
254
255 Service::Yuzu::InstallInterfaces(system.ServiceManager(), datastring, callback);
256
257 system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
258
259 system.Renderer().Rasterizer().LoadDiskResources();
260
261 while (!finished) {
262 system.RunLoop();
263 }
264
265 detached_tasks.WaitForAllTasks();
266 return return_value;
267}
diff --git a/src/yuzu_tester/yuzu.rc b/src/yuzu_tester/yuzu.rc
new file mode 100644
index 000000000..7de8ef3d9
--- /dev/null
+++ b/src/yuzu_tester/yuzu.rc
@@ -0,0 +1,17 @@
1#include "winresrc.h"
2/////////////////////////////////////////////////////////////////////////////
3//
4// Icon
5//
6
7// Icon with lowest ID value placed first to ensure application icon
8// remains consistent on all systems.
9YUZU_ICON ICON "../../dist/yuzu.ico"
10
11
12/////////////////////////////////////////////////////////////////////////////
13//
14// RT_MANIFEST
15//
16
171 RT_MANIFEST "../../dist/yuzu.manifest"