diff options
Diffstat (limited to 'src')
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 | ||
| 89 | if (ENABLE_SDL2) | 89 | if (ENABLE_SDL2) |
| 90 | add_subdirectory(yuzu_cmd) | 90 | add_subdirectory(yuzu_cmd) |
| 91 | add_subdirectory(yuzu_tester) | ||
| 91 | endif() | 92 | endif() |
| 92 | 93 | ||
| 93 | if (ENABLE_QT) | 94 | if (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 | ||
| 111 | void Stream::ReleaseActiveBuffer() { | 111 | void 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 | ||
| 468 | create_target_directory_groups(core) | 487 | create_target_directory_groups(core) |
| 469 | 488 | ||
| 470 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 489 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 471 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt mbedtls opus unicorn open_source_archives) | 490 | target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives) |
| 472 | if (ENABLE_WEB_SERVICE) | 491 | if (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 | ||
| 10 | namespace Core { | 15 | namespace Core { |
| 11 | void 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); | 17 | namespace { |
| 18 | |||
| 19 | constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; | ||
| 20 | constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; | ||
| 21 | constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; | ||
| 22 | constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; | ||
| 23 | |||
| 24 | enum 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 | |||
| 34 | enum class ELFSymbolBinding : u8 { | ||
| 35 | Local = 0, | ||
| 36 | Global = 1, | ||
| 37 | Weak = 2, | ||
| 38 | }; | ||
| 39 | |||
| 40 | enum class ELFSymbolVisibility : u8 { | ||
| 41 | Default = 0, | ||
| 42 | Internal = 1, | ||
| 43 | Hidden = 2, | ||
| 44 | Protected = 3, | ||
| 45 | }; | ||
| 46 | |||
| 47 | struct 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 | }; | ||
| 60 | static_assert(sizeof(ELFSymbol) == 0x18, "ELFSymbol has incorrect size."); | ||
| 61 | |||
| 62 | using Symbols = std::vector<std::pair<ELFSymbol, std::string>>; | ||
| 63 | |||
| 64 | Symbols 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 | |||
| 124 | std::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 | |||
| 141 | constexpr u64 SEGMENT_BASE = 0x7100000000ull; | ||
| 142 | |||
| 143 | std::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 | |||
| 199 | void 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 | ||
| 10 | namespace Common { | 11 | namespace 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 | ||
| 39 | namespace Core { | 43 | namespace Core { |
| 40 | 44 | ||
| 45 | namespace { | ||
| 46 | |||
| 47 | FileSys::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 | ||
| 43 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | 71 | FileSys::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 | } |
| 76 | struct System::Impl { | 104 | struct 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 | ||
| 559 | const Reporter& System::GetReporter() const { | ||
| 560 | return impl->reporter; | ||
| 561 | } | ||
| 562 | |||
| 563 | Service::Glue::ARPManager& System::GetARPManager() { | ||
| 564 | return impl->arp_manager; | ||
| 565 | } | ||
| 566 | |||
| 567 | const Service::Glue::ARPManager& System::GetARPManager() const { | ||
| 568 | return impl->arp_manager; | ||
| 569 | } | ||
| 570 | |||
| 495 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 571 | System::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; | |||
| 42 | class AppletManager; | 43 | class AppletManager; |
| 43 | } // namespace AM::Applets | 44 | } // namespace AM::Applets |
| 44 | 45 | ||
| 46 | namespace Glue { | ||
| 47 | class ARPManager; | ||
| 48 | } | ||
| 49 | |||
| 45 | namespace SM { | 50 | namespace SM { |
| 46 | class ServiceManager; | 51 | class ServiceManager; |
| 47 | } // namespace SM | 52 | } // namespace SM |
| @@ -68,6 +73,7 @@ class Cpu; | |||
| 68 | class ExclusiveMonitor; | 73 | class ExclusiveMonitor; |
| 69 | class FrameLimiter; | 74 | class FrameLimiter; |
| 70 | class PerfStats; | 75 | class PerfStats; |
| 76 | class Reporter; | ||
| 71 | class TelemetrySession; | 77 | class TelemetrySession; |
| 72 | 78 | ||
| 73 | struct PerfStatsResults; | 79 | struct 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 | |||
| 287 | private: | 299 | private: |
| 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() { | |||
| 53 | Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, | 53 | Cpu::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 | |||
| 70 | Cpu::~Cpu() = default; | 70 | Cpu::~Cpu() = default; |
| 71 | 71 | ||
| 72 | std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { | 72 | std::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 | ||
| 58 | void CoreTiming::Shutdown() { | 58 | void CoreTiming::Shutdown() { |
| 59 | MoveEvents(); | ||
| 60 | ClearPendingEvents(); | 59 | ClearPendingEvents(); |
| 61 | UnregisterAllEvents(); | 60 | UnregisterAllEvents(); |
| 62 | } | 61 | } |
| 63 | 62 | ||
| 64 | EventType* CoreTiming::RegisterEvent(const std::string& name, TimedCallback callback) { | 63 | EventType* 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 | ||
| 83 | void CoreTiming::ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata) { | 83 | void 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 | ||
| 96 | void 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 | |||
| 101 | void CoreTiming::UnscheduleEvent(const EventType* event_type, u64 userdata) { | 97 | void 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 | ||
| 113 | void CoreTiming::UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata) { | ||
| 114 | unschedule_queue.Push(std::make_pair(event_type, userdata)); | ||
| 115 | } | ||
| 116 | |||
| 117 | u64 CoreTiming::GetTicks() const { | 110 | u64 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 | ||
| 137 | void CoreTiming::RemoveEvent(const EventType* event_type) { | 130 | void 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 | ||
| 148 | void CoreTiming::RemoveNormalAndThreadsafeEvent(const EventType* event_type) { | ||
| 149 | MoveEvents(); | ||
| 150 | RemoveEvent(event_type); | ||
| 151 | } | ||
| 152 | |||
| 153 | void CoreTiming::ForceExceptionCheck(s64 cycles) { | 142 | void 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 | ||
| 165 | void 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 | |||
| 173 | void CoreTiming::Advance() { | 154 | void 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 | ||
| 28 | using namespace Common; | 30 | using namespace Common; |
| 29 | 31 | ||
| @@ -45,36 +47,6 @@ struct Package2Header { | |||
| 45 | }; | 47 | }; |
| 46 | static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); | 48 | static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); |
| 47 | 49 | ||
| 48 | struct INIHeader { | ||
| 49 | u32_le magic; | ||
| 50 | u32_le size; | ||
| 51 | u32_le process_count; | ||
| 52 | INSERT_PADDING_BYTES(4); | ||
| 53 | }; | ||
| 54 | static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size."); | ||
| 55 | |||
| 56 | struct SectionHeader { | ||
| 57 | u32_le offset; | ||
| 58 | u32_le size_decompressed; | ||
| 59 | u32_le size_compressed; | ||
| 60 | u32_le attribute; | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size."); | ||
| 63 | |||
| 64 | struct 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 | }; | ||
| 76 | static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size."); | ||
| 77 | |||
| 78 | const std::array<SHA256Hash, 0x10> source_hashes{ | 50 | const 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 | ||
| 173 | static 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 | |||
| 232 | static u8 CalculateMaxKeyblobSourceHash() { | 145 | static 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 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | constexpr u32 INI_MAX_KIPS = 0x50; | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | bool 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 | |||
| 82 | KIP::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 | |||
| 117 | Loader::ResultStatus KIP::GetStatus() const { | ||
| 118 | return status; | ||
| 119 | } | ||
| 120 | |||
| 121 | std::string KIP::GetName() const { | ||
| 122 | return Common::StringFromFixedZeroTerminatedBuffer(header.name.data(), header.name.size()); | ||
| 123 | } | ||
| 124 | |||
| 125 | u64 KIP::GetTitleID() const { | ||
| 126 | return header.title_id; | ||
| 127 | } | ||
| 128 | |||
| 129 | std::vector<u8> KIP::GetSectionDecompressed(u8 index) const { | ||
| 130 | return decompressed_sections[index]; | ||
| 131 | } | ||
| 132 | |||
| 133 | bool KIP::Is64Bit() const { | ||
| 134 | return (header.flags & 0x8) != 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | bool KIP::Is39BitAddressSpace() const { | ||
| 138 | return (header.flags & 0x10) != 0; | ||
| 139 | } | ||
| 140 | |||
| 141 | bool KIP::IsService() const { | ||
| 142 | return (header.flags & 0x20) != 0; | ||
| 143 | } | ||
| 144 | |||
| 145 | std::vector<u32> KIP::GetKernelCapabilities() const { | ||
| 146 | return std::vector<u32>(header.capabilities.begin(), header.capabilities.end()); | ||
| 147 | } | ||
| 148 | |||
| 149 | s32 KIP::GetMainThreadPriority() const { | ||
| 150 | return header.main_thread_priority; | ||
| 151 | } | ||
| 152 | |||
| 153 | u32 KIP::GetMainThreadStackSize() const { | ||
| 154 | return header.sections[1].attribute; | ||
| 155 | } | ||
| 156 | |||
| 157 | u32 KIP::GetMainThreadCpuCore() const { | ||
| 158 | return header.default_core; | ||
| 159 | } | ||
| 160 | |||
| 161 | const std::vector<u8>& KIP::GetTextSection() const { | ||
| 162 | return decompressed_sections[0]; | ||
| 163 | } | ||
| 164 | |||
| 165 | const std::vector<u8>& KIP::GetRODataSection() const { | ||
| 166 | return decompressed_sections[1]; | ||
| 167 | } | ||
| 168 | |||
| 169 | const std::vector<u8>& KIP::GetDataSection() const { | ||
| 170 | return decompressed_sections[2]; | ||
| 171 | } | ||
| 172 | |||
| 173 | u32 KIP::GetTextOffset() const { | ||
| 174 | return header.sections[0].offset; | ||
| 175 | } | ||
| 176 | |||
| 177 | u32 KIP::GetRODataOffset() const { | ||
| 178 | return header.sections[1].offset; | ||
| 179 | } | ||
| 180 | |||
| 181 | u32 KIP::GetDataOffset() const { | ||
| 182 | return header.sections[2].offset; | ||
| 183 | } | ||
| 184 | |||
| 185 | u32 KIP::GetBSSSize() const { | ||
| 186 | return header.sections[3].decompressed_size; | ||
| 187 | } | ||
| 188 | |||
| 189 | u32 KIP::GetBSSOffset() const { | ||
| 190 | return header.sections[3].offset; | ||
| 191 | } | ||
| 192 | |||
| 193 | INI::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 | |||
| 220 | Loader::ResultStatus INI::GetStatus() const { | ||
| 221 | return status; | ||
| 222 | } | ||
| 223 | |||
| 224 | const 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 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | struct KIPSectionHeader { | ||
| 15 | u32_le offset; | ||
| 16 | u32_le decompressed_size; | ||
| 17 | u32_le compressed_size; | ||
| 18 | u32_le attribute; | ||
| 19 | }; | ||
| 20 | static_assert(sizeof(KIPSectionHeader) == 0x10, "KIPSectionHeader has incorrect size."); | ||
| 21 | |||
| 22 | struct 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 | }; | ||
| 34 | static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size."); | ||
| 35 | |||
| 36 | struct INIHeader { | ||
| 37 | u32_le magic; | ||
| 38 | u32_le size; | ||
| 39 | u32_le kip_count; | ||
| 40 | INSERT_PADDING_BYTES(0x4); | ||
| 41 | }; | ||
| 42 | static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size."); | ||
| 43 | |||
| 44 | // Kernel Internal Process | ||
| 45 | class KIP { | ||
| 46 | public: | ||
| 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 | |||
| 77 | private: | ||
| 78 | Loader::ResultStatus status; | ||
| 79 | |||
| 80 | KIPHeader header{}; | ||
| 81 | std::array<std::vector<u8>, 6> decompressed_sections; | ||
| 82 | }; | ||
| 83 | |||
| 84 | class INI { | ||
| 85 | public: | ||
| 86 | explicit INI(const VirtualFile& file); | ||
| 87 | |||
| 88 | Loader::ResultStatus GetStatus() const; | ||
| 89 | |||
| 90 | const std::vector<KIP>& GetKIPs() const; | ||
| 91 | |||
| 92 | private: | ||
| 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 | ||
| 43 | struct ContentRecord { | 43 | struct 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 | ||
| 496 | std::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 | |||
| 496 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 506 | std::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 | ||
| 54 | void 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 | |||
| 54 | bool ProgramMetadata::Is64BitProgram() const { | 69 | bool 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 | ||
| 651 | std::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 | |||
| 648 | ManualContentProvider::~ManualContentProvider() = default; | 665 | ManualContentProvider::~ManualContentProvider() = default; |
| 649 | 666 | ||
| 650 | void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type, | 667 | void 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 | |||
| 202 | private: | 205 | private: |
| 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 | ||
| 8 | namespace Core::Frontend { | 8 | namespace Core::Frontend { |
| 9 | 9 | ||
| 10 | ParentalControlsApplet::~ParentalControlsApplet() = default; | ||
| 11 | |||
| 12 | DefaultParentalControlsApplet::~DefaultParentalControlsApplet() = default; | ||
| 13 | |||
| 14 | void 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 | |||
| 23 | void 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 | |||
| 29 | void DefaultParentalControlsApplet::RegisterPIN(std::function<void()> finished) { | ||
| 30 | LOG_INFO(Service_AM, "Application requested frontend to register new PIN"); | ||
| 31 | finished(); | ||
| 32 | } | ||
| 33 | |||
| 34 | void DefaultParentalControlsApplet::ChangePIN(std::function<void()> finished) { | ||
| 35 | LOG_INFO(Service_AM, "Application requested frontend to change PIN to new value"); | ||
| 36 | finished(); | ||
| 37 | } | ||
| 38 | |||
| 10 | PhotoViewerApplet::~PhotoViewerApplet() = default; | 39 | PhotoViewerApplet::~PhotoViewerApplet() = default; |
| 11 | 40 | ||
| 12 | DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() {} | 41 | DefaultPhotoViewerApplet::~DefaultPhotoViewerApplet() = default; |
| 13 | 42 | ||
| 14 | void DefaultPhotoViewerApplet::ShowPhotosForApplication(u64 title_id, | 43 | void 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 | ||
| 56 | ECommerceApplet::~ECommerceApplet() = default; | ||
| 57 | |||
| 58 | DefaultECommerceApplet::~DefaultECommerceApplet() = default; | ||
| 59 | |||
| 60 | void 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 | |||
| 73 | void 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 | |||
| 85 | void 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 | |||
| 95 | void 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 | |||
| 106 | void 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 | |||
| 115 | void 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 | ||
| 10 | namespace Core::Frontend { | 11 | namespace Core::Frontend { |
| 11 | 12 | ||
| 13 | class ParentalControlsApplet { | ||
| 14 | public: | ||
| 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 | |||
| 34 | class DefaultParentalControlsApplet final : public ParentalControlsApplet { | ||
| 35 | public: | ||
| 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 | |||
| 12 | class PhotoViewerApplet { | 45 | class PhotoViewerApplet { |
| 13 | public: | 46 | public: |
| 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 | ||
| 61 | class ECommerceApplet { | ||
| 62 | public: | ||
| 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 | |||
| 94 | class DefaultECommerceApplet : public ECommerceApplet { | ||
| 95 | public: | ||
| 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 | ||
| 12 | DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; | 12 | DefaultWebBrowserApplet::~DefaultWebBrowserApplet() = default; |
| 13 | 13 | ||
| 14 | void DefaultWebBrowserApplet::OpenPage(std::string_view filename, | 14 | void 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 { | |||
| 13 | public: | 13 | public: |
| 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 | ||
| 20 | class DefaultWebBrowserApplet final : public WebBrowserApplet { | 20 | class DefaultWebBrowserApplet final : public WebBrowserApplet { |
| 21 | public: | 21 | public: |
| 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 | ||
| 229 | u64 KernelCore::CreateNewProcessID() { | 231 | u64 KernelCore::CreateNewKernelProcessID() { |
| 230 | return impl->next_process_id++; | 232 | return impl->next_kernel_process_id++; |
| 233 | } | ||
| 234 | |||
| 235 | u64 KernelCore::CreateNewUserProcessID() { | ||
| 236 | return impl->next_user_process_id++; | ||
| 231 | } | 237 | } |
| 232 | 238 | ||
| 233 | Core::Timing::EventType* KernelCore::ThreadWakeupCallbackEventType() const { | 239 | Core::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 | ||
| 51 | SharedPtr<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). | ||
| 57 | class TLSPage { | ||
| 58 | public: | ||
| 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 | |||
| 94 | private: | ||
| 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 | |||
| 103 | SharedPtr<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 | */ |
| 189 | static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( | 242 | static 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 | ||
| 207 | VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { | 247 | VAddr 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 | ||
| 231 | void Process::FreeTLSSlot(VAddr tls_address) { | 272 | void 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 | ||
| 239 | void Process::LoadModule(CodeSet module_, VAddr base_addr) { | 286 | void 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 { | |||
| 32 | class KernelCore; | 31 | class KernelCore; |
| 33 | class ResourceLimit; | 32 | class ResourceLimit; |
| 34 | class Thread; | 33 | class Thread; |
| 34 | class TLSPage; | ||
| 35 | 35 | ||
| 36 | struct CodeSet; | 36 | struct 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 | ||
| 262 | private: | 268 | private: |
| 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 | ||
| 42 | namespace Kernel { | 43 | namespace Kernel { |
| 43 | namespace { | 44 | namespace { |
| @@ -594,6 +595,7 @@ struct BreakReason { | |||
| 594 | static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { | 595 | static 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 | ||
| 71 | void Thread::WakeAfterDelay(s64 nanoseconds) { | 71 | void 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 | ||
| 83 | void Thread::CancelWakeupTimer() { | 83 | void 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 | ||
| 88 | static std::optional<s32> GetNextProcessorId(u64 mask) { | 88 | static 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 | ||
| 79 | class Thread final : public WaitObject { | 78 | class Thread final : public WaitObject { |
| 80 | public: | 79 | public: |
| 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 | ||
| 154 | ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { | 154 | ResultVal<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; | 158 | ResultVal<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 | ||
| 27 | namespace Service::Account { | 32 | namespace 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 | ||
| 220 | void Module::Interface::InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx) { | 225 | void 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 | |||
| 234 | void 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 | |||
| 248 | ResultCode 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 | ||
| 226 | void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) { | 293 | void 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 | ||
| 9 | namespace Service::Account { | 10 | namespace 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 | |||
| 9 | namespace Service::Account { | ||
| 10 | |||
| 11 | constexpr ResultCode ERR_ACCOUNTINFO_BAD_APPLICATION{ErrorModule::Account, 22}; | ||
| 12 | constexpr 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 | ||
| 890 | ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") { | 890 | ILibraryAppletCreator::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 | ||
| 1236 | void InstallInterfaces(SM::ServiceManager& service_manager, | 1238 | void 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 | ||
| 202 | class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { | 202 | class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> { |
| 203 | public: | 203 | public: |
| 204 | ILibraryAppletCreator(); | 204 | ILibraryAppletCreator(u64 current_process_title_id); |
| 205 | ~ILibraryAppletCreator() override; | 205 | ~ILibraryAppletCreator() override; |
| 206 | 206 | ||
| 207 | private: | 207 | private: |
| 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 | ||
| 213 | class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> { | 215 | class 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. |
| 266 | void InstallInterfaces(SM::ServiceManager& service_manager, | 268 | void 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 { | |||
| 13 | class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { | 14 | class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> { |
| 14 | public: | 15 | public: |
| 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 | ||
| 114 | class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { | 117 | class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> { |
| 115 | public: | 118 | public: |
| 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 | ||
| 224 | void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) { | 228 | void 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 | ||
| 232 | void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) { | 236 | void 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 | ||
| 240 | void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) { | 244 | void 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 | ||
| 248 | AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, | 252 | AppletAE::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 { | |||
| 18 | class AppletAE final : public ServiceFramework<AppletAE> { | 18 | class AppletAE final : public ServiceFramework<AppletAE> { |
| 19 | public: | 19 | public: |
| 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 { | |||
| 13 | class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { | 14 | class IApplicationProxy final : public ServiceFramework<IApplicationProxy> { |
| 14 | public: | 15 | public: |
| 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 | ||
| 105 | void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) { | 107 | void 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 | ||
| 113 | AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, | 115 | AppletOE::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 { | |||
| 18 | class AppletOE final : public ServiceFramework<AppletOE> { | 18 | class AppletOE final : public ServiceFramework<AppletOE> { |
| 19 | public: | 19 | public: |
| 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 | ||
| 36 | AppletDataBroker::~AppletDataBroker() = default; | 36 | AppletDataBroker::~AppletDataBroker() = default; |
| 37 | 37 | ||
| 38 | AppletDataBroker::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 | |||
| 38 | std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() { | 54 | std::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 | ||
| 74 | void AppletDataBroker::PushNormalDataFromGame(IStorage storage) { | 90 | void 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 | ||
| 78 | void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) { | 94 | void 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 | ||
| 83 | void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) { | 99 | void 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 | ||
| 87 | void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) { | 103 | void 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 | ||
| 124 | AppletFrontendSet::AppletFrontendSet() = default; | 140 | AppletFrontendSet::AppletFrontendSet() = default; |
| 125 | 141 | ||
| 126 | AppletFrontendSet::AppletFrontendSet(ErrorApplet error, PhotoViewer photo_viewer, | 142 | AppletFrontendSet::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 | ||
| 133 | AppletFrontendSet::~AppletFrontendSet() = default; | 151 | AppletFrontendSet::~AppletFrontendSet() = default; |
| 134 | 152 | ||
| @@ -141,6 +159,8 @@ AppletManager::AppletManager() = default; | |||
| 141 | AppletManager::~AppletManager() = default; | 159 | AppletManager::~AppletManager() = default; |
| 142 | 160 | ||
| 143 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | 161 | void 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 | ||
| 156 | void AppletManager::SetDefaultAppletFrontendSet() { | 178 | void 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 | ||
| 164 | void AppletManager::SetDefaultAppletsIfMissing() { | 183 | void 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 | ||
| 187 | void AppletManager::ClearAll() { | 215 | void AppletManager::ClearAll() { |
| 188 | frontend = {}; | 216 | frontend = {}; |
| 189 | } | 217 | } |
| 190 | 218 | ||
| 191 | std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { | 219 | std::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 @@ | |||
| 13 | union ResultCode; | 13 | union ResultCode; |
| 14 | 14 | ||
| 15 | namespace Core::Frontend { | 15 | namespace Core::Frontend { |
| 16 | class ECommerceApplet; | ||
| 16 | class ErrorApplet; | 17 | class ErrorApplet; |
| 18 | class ParentalControlsApplet; | ||
| 17 | class PhotoViewerApplet; | 19 | class PhotoViewerApplet; |
| 18 | class ProfileSelectApplet; | 20 | class ProfileSelectApplet; |
| 19 | class SoftwareKeyboardApplet; | 21 | class 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 | ||
| 139 | struct AppletFrontendSet { | 149 | struct 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 | ||
| 164 | class AppletManager { | 180 | class 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 | ||
| 176 | private: | 192 | private: |
| 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 | ||
| 15 | namespace Service::AM::Applets { | 17 | namespace 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 | ||
| 17 | namespace Service::AM::Applets { | 18 | namespace Service::AM::Applets { |
| 18 | 19 | ||
| 20 | constexpr ResultCode ERROR_INVALID_PIN{ErrorModule::PCTL, 221}; | ||
| 21 | |||
| 19 | static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) { | 22 | static 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 | ||
| 40 | Auth::Auth(Core::Frontend::ParentalControlsApplet& frontend) : frontend(frontend) {} | ||
| 41 | |||
| 42 | Auth::~Auth() = default; | ||
| 43 | |||
| 44 | void 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 | |||
| 72 | bool Auth::TransactionComplete() const { | ||
| 73 | return complete; | ||
| 74 | } | ||
| 75 | |||
| 76 | ResultCode Auth::GetStatus() const { | ||
| 77 | return successful ? RESULT_SUCCESS : ERROR_INVALID_PIN; | ||
| 78 | } | ||
| 79 | |||
| 80 | void Auth::ExecuteInteractive() { | ||
| 81 | UNREACHABLE_MSG("Unexpected interactive applet data."); | ||
| 82 | } | ||
| 83 | |||
| 84 | void 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 | |||
| 137 | void 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 | |||
| 37 | PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} | 154 | PhotoViewer::PhotoViewer(const Core::Frontend::PhotoViewerApplet& frontend) : frontend(frontend) {} |
| 38 | 155 | ||
| 39 | PhotoViewer::~PhotoViewer() = default; | 156 | PhotoViewer::~PhotoViewer() = default; |
| @@ -83,13 +200,20 @@ void PhotoViewer::ViewFinished() { | |||
| 83 | broker.SignalStateChanged(); | 200 | broker.SignalStateChanged(); |
| 84 | } | 201 | } |
| 85 | 202 | ||
| 86 | StubApplet::StubApplet() = default; | 203 | StubApplet::StubApplet(AppletId id) : id(id) {} |
| 87 | 204 | ||
| 88 | StubApplet::~StubApplet() = default; | 205 | StubApplet::~StubApplet() = default; |
| 89 | 206 | ||
| 90 | void StubApplet::Initialize() { | 207 | void 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 | ||
| 9 | namespace Service::AM::Applets { | 9 | namespace Service::AM::Applets { |
| 10 | 10 | ||
| 11 | enum class AuthAppletType : u32 { | ||
| 12 | ShowParentalAuthentication, | ||
| 13 | RegisterParentalPasscode, | ||
| 14 | ChangeParentalPasscode, | ||
| 15 | }; | ||
| 16 | |||
| 17 | class Auth final : public Applet { | ||
| 18 | public: | ||
| 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 | |||
| 30 | private: | ||
| 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 | |||
| 11 | enum class PhotoViewerAppletMode : u8 { | 41 | enum class PhotoViewerAppletMode : u8 { |
| 12 | CurrentApp = 0, | 42 | CurrentApp = 0, |
| 13 | AllApps = 1, | 43 | AllApps = 1, |
| @@ -34,7 +64,7 @@ private: | |||
| 34 | 64 | ||
| 35 | class StubApplet final : public Applet { | 65 | class StubApplet final : public Applet { |
| 36 | public: | 66 | public: |
| 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 | |||
| 77 | private: | ||
| 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 | ||
| 29 | namespace Service::AM::Applets { | 31 | namespace Service::AM::Applets { |
| 30 | 32 | ||
| 31 | // TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not | 33 | enum 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. |
| 34 | constexpr 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 | |||
| 96 | enum 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 | |||
| 106 | enum class ShopWebTarget { | ||
| 107 | ApplicationInfo, | ||
| 108 | AddOnContentList, | ||
| 109 | SubscriptionList, | ||
| 110 | ConsumableItemList, | ||
| 111 | Home, | ||
| 112 | Settings, | ||
| 113 | }; | ||
| 114 | |||
| 115 | namespace { | ||
| 35 | 116 | ||
| 36 | struct WebBufferHeader { | 117 | constexpr std::size_t SHIM_KIND_COUNT = 0x8; |
| 118 | |||
| 119 | struct WebArgHeader { | ||
| 37 | u16 count; | 120 | u16 count; |
| 38 | INSERT_PADDING_BYTES(6); | 121 | INSERT_PADDING_BYTES(2); |
| 122 | ShimKind kind; | ||
| 39 | }; | 123 | }; |
| 40 | static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size."); | 124 | static_assert(sizeof(WebArgHeader) == 0x8, "WebArgHeader has incorrect size."); |
| 41 | 125 | ||
| 42 | struct WebArgumentHeader { | 126 | struct WebArgTLV { |
| 43 | u16 type; | 127 | WebArgTLVType type; |
| 44 | u16 size; | 128 | u16 size; |
| 45 | u32 offset; | 129 | u32 offset; |
| 46 | }; | 130 | }; |
| 47 | static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size."); | 131 | static_assert(sizeof(WebArgTLV) == 0x8, "WebArgTLV has incorrect size."); |
| 48 | 132 | ||
| 49 | struct WebArgumentResult { | 133 | struct 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 | }; |
| 54 | static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size."); | 139 | static_assert(sizeof(WebCommonReturnValue) == 0x1010, "WebCommonReturnValue has incorrect size."); |
| 55 | 140 | ||
| 56 | static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) { | 141 | struct 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; | 148 | static_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)); | 150 | struct 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); | 154 | static_assert(sizeof(WebWifiReturnValue) == 0x8, "WebWifiReturnValue has incorrect size."); |
| 70 | offset += arg.offset; | 155 | |
| 71 | ASSERT(offset + arg.size <= data.size()); | 156 | enum class OfflineWebSource : u32 { |
| 72 | std::memcpy(out.data(), data.data() + offset, out.size()); | 157 | OfflineHtmlPage = 0x1, |
| 158 | ApplicationLegalInformation = 0x2, | ||
| 159 | SystemDataPage = 0x3, | ||
| 160 | }; | ||
| 161 | |||
| 162 | std::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 | ||
| 82 | static 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 | ||
| 193 | FileSys::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 | ||
| 98 | WebBrowser::WebBrowser(Core::Frontend::WebBrowserApplet& frontend) : frontend(frontend) {} | 208 | } // Anonymous namespace |
| 209 | |||
| 210 | WebBrowser::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 | ||
| 100 | WebBrowser::~WebBrowser() = default; | 215 | WebBrowser::~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 | ||
| 134 | bool WebBrowser::TransactionComplete() const { | 237 | bool WebBrowser::TransactionComplete() const { |
| @@ -144,24 +247,25 @@ void WebBrowser::ExecuteInteractive() { | |||
| 144 | } | 247 | } |
| 145 | 248 | ||
| 146 | void WebBrowser::Execute() { | 249 | void 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 | ||
| 158 | void WebBrowser::UnpackRomFS() { | 262 | void 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() { | |||
| 172 | void WebBrowser::Finalize() { | 276 | void 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 | |||
| 294 | void 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 | |||
| 315 | void 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 | |||
| 336 | void 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 | |||
| 425 | void 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 | |||
| 492 | void 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 | |||
| 546 | void 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 | ||
| 11 | namespace Service::AM::Applets { | 12 | namespace Service::AM::Applets { |
| 12 | 13 | ||
| 14 | enum class ShimKind : u32; | ||
| 15 | enum class ShopWebTarget; | ||
| 16 | enum class WebArgTLVType : u16; | ||
| 17 | |||
| 13 | class WebBrowser final : public Applet { | 18 | class WebBrowser final : public Applet { |
| 14 | public: | 19 | public: |
| 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 | ||
| 34 | private: | 41 | private: |
| 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 | |||
| 14 | namespace Service::ARP { | ||
| 15 | |||
| 16 | class ARP_R final : public ServiceFramework<ARP_R> { | ||
| 17 | public: | ||
| 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 | |||
| 32 | class IRegistrar final : public ServiceFramework<IRegistrar> { | ||
| 33 | public: | ||
| 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 | |||
| 47 | class ARP_W final : public ServiceFramework<ARP_W> { | ||
| 48 | public: | ||
| 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 | |||
| 60 | private: | ||
| 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 | |||
| 70 | void 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 | |||
| 7 | namespace Service::SM { | ||
| 8 | class ServiceManager; | ||
| 9 | } | ||
| 10 | |||
| 11 | namespace Service::ARP { | ||
| 12 | |||
| 13 | /// Registers all ARP services with the specified service manager. | ||
| 14 | void 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 | ||
| 186 | private: | 190 | private: |
| @@ -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 | ||
| 20 | namespace Service::Fatal { | 21 | namespace 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 | ||
| 126 | static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { | 110 | static 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 | |||
| 9 | namespace Service::Friend { | ||
| 10 | |||
| 11 | constexpr 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 | ||
| 117 | class INotificationService final : public ServiceFramework<INotificationService> { | ||
| 118 | public: | ||
| 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 | |||
| 131 | private: | ||
| 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 | |||
| 112 | void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) { | 216 | void 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 | ||
| 223 | void 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 | |||
| 119 | Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | 234 | Module::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 | |||
| 18 | namespace Service::Glue { | ||
| 19 | |||
| 20 | namespace { | ||
| 21 | std::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 | |||
| 35 | ARP_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 | |||
| 49 | ARP_R::~ARP_R() = default; | ||
| 50 | |||
| 51 | void 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 | |||
| 79 | void 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 | |||
| 99 | void 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 | |||
| 128 | void 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 | |||
| 149 | class IRegistrar final : public ServiceFramework<IRegistrar> { | ||
| 150 | friend class ARP_W; | ||
| 151 | |||
| 152 | public: | ||
| 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 | |||
| 167 | private: | ||
| 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 | |||
| 239 | ARP_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 | |||
| 251 | ARP_W::~ARP_W() = default; | ||
| 252 | |||
| 253 | void 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 | |||
| 271 | void 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 | |||
| 9 | namespace Service::Glue { | ||
| 10 | |||
| 11 | class ARPManager; | ||
| 12 | class IRegistrar; | ||
| 13 | |||
| 14 | class ARP_R final : public ServiceFramework<ARP_R> { | ||
| 15 | public: | ||
| 16 | explicit ARP_R(const Core::System& system, const ARPManager& manager); | ||
| 17 | ~ARP_R() override; | ||
| 18 | |||
| 19 | private: | ||
| 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 | |||
| 29 | class ARP_W final : public ServiceFramework<ARP_W> { | ||
| 30 | public: | ||
| 31 | explicit ARP_W(const Core::System& system, ARPManager& manager); | ||
| 32 | ~ARP_W() override; | ||
| 33 | |||
| 34 | private: | ||
| 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 | |||
| 7 | namespace Service::Glue { | ||
| 8 | |||
| 9 | BGTC_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 | |||
| 32 | BGTC_T::~BGTC_T() = default; | ||
| 33 | |||
| 34 | BGTC_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 | |||
| 48 | BGTC_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 | |||
| 9 | namespace Service::Glue { | ||
| 10 | |||
| 11 | class BGTC_T final : public ServiceFramework<BGTC_T> { | ||
| 12 | public: | ||
| 13 | BGTC_T(); | ||
| 14 | ~BGTC_T() override; | ||
| 15 | }; | ||
| 16 | |||
| 17 | class BGTC_SC final : public ServiceFramework<BGTC_SC> { | ||
| 18 | public: | ||
| 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 | |||
| 9 | namespace Service::Glue { | ||
| 10 | |||
| 11 | constexpr ResultCode ERR_INVALID_RESOURCE{ErrorModule::ARP, 0x1E}; | ||
| 12 | constexpr ResultCode ERR_INVALID_PROCESS_ID{ErrorModule::ARP, 0x1F}; | ||
| 13 | constexpr ResultCode ERR_INVALID_ACCESS{ErrorModule::ARP, 0x2A}; | ||
| 14 | constexpr 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 | |||
| 11 | namespace Service::Glue { | ||
| 12 | |||
| 13 | void 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 | |||
| 7 | namespace Core { | ||
| 8 | class System; | ||
| 9 | } // namespace Core | ||
| 10 | |||
| 11 | namespace Service::Glue { | ||
| 12 | |||
| 13 | /// Registers all Glue services with the specified service manager. | ||
| 14 | void 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 | |||
| 8 | namespace Service::Glue { | ||
| 9 | |||
| 10 | struct ARPManager::MapEntry { | ||
| 11 | ApplicationLaunchProperty launch; | ||
| 12 | std::vector<u8> control; | ||
| 13 | }; | ||
| 14 | |||
| 15 | ARPManager::ARPManager() = default; | ||
| 16 | |||
| 17 | ARPManager::~ARPManager() = default; | ||
| 18 | |||
| 19 | ResultVal<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 | |||
| 32 | ResultVal<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 | |||
| 45 | ResultCode 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 | |||
| 60 | ResultCode 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 | |||
| 74 | void 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 | |||
| 14 | namespace Service::Glue { | ||
| 15 | |||
| 16 | struct 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 | }; | ||
| 24 | static_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. | ||
| 29 | class ARPManager { | ||
| 30 | public: | ||
| 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 | |||
| 58 | private: | ||
| 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 | ||
| 10 | namespace Service::PlayReport { | 18 | namespace Service::PlayReport { |
| 11 | 19 | ||
| @@ -40,8 +48,21 @@ public: | |||
| 40 | 48 | ||
| 41 | private: | 49 | private: |
| 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 | ||
| 72 | namespace Service { | 73 | namespace 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 | ||
| 98 | void 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 | |||
| 98 | void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) { | 106 | void 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 | ||
| 7 | namespace Service::Time { | 7 | namespace Service::Time { |
| 8 | 8 | ||
| 9 | Time::Time(std::shared_ptr<Module> time, const char* name) | 9 | Time::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 | ||
| 9 | namespace Service::Time { | 9 | namespace Service::Time { |
| 10 | 10 | ||
| 11 | class SharedMemory; | ||
| 12 | |||
| 11 | class Time final : public Module::Interface { | 13 | class Time final : public Module::Interface { |
| 12 | public: | 14 | public: |
| 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 | ||
| 18 | namespace Service::Time { | 19 | namespace 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 | ||
| 65 | enum class ClockContextType { | ||
| 66 | StandardSteady, | ||
| 67 | StandardUserSystem, | ||
| 68 | StandardNetworkSystem, | ||
| 69 | StandardLocalSystem, | ||
| 70 | }; | ||
| 71 | |||
| 64 | class ISystemClock final : public ServiceFramework<ISystemClock> { | 72 | class ISystemClock final : public ServiceFramework<ISystemClock> { |
| 65 | public: | 73 | public: |
| 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 | ||
| 77 | private: | 89 | private: |
| @@ -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 | ||
| 97 | class ISteadyClock final : public ServiceFramework<ISteadyClock> { | 127 | class ISteadyClock final : public ServiceFramework<ISteadyClock> { |
| 98 | public: | 128 | public: |
| 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 | ||
| 106 | private: | 139 | private: |
| 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 | ||
| 120 | class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { | 161 | class 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 | ||
| 239 | void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) { | 280 | void 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 | ||
| 247 | void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) { | 288 | void 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 | ||
| 255 | void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { | 296 | void 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 | ||
| 271 | void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | 312 | void 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 | ||
| 336 | Module::Interface::Interface(std::shared_ptr<Module> time, const char* name) | 377 | void 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 | |||
| 384 | void 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 | |||
| 395 | void 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 | |||
| 409 | Module::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 | ||
| 339 | Module::Interface::~Interface() = default; | 413 | Module::Interface::~Interface() = default; |
| 340 | 414 | ||
| 341 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 415 | void 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 | ||
| 11 | namespace Service::Time { | 11 | namespace Service::Time { |
| 12 | 12 | ||
| 13 | class SharedMemory; | ||
| 14 | |||
| 13 | struct LocationName { | 15 | struct LocationName { |
| 14 | std::array<u8, 0x24> name; | 16 | std::array<u8, 0x24> name; |
| 15 | }; | 17 | }; |
| @@ -77,7 +79,8 @@ class Module final { | |||
| 77 | public: | 79 | public: |
| 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. |
| 97 | void InstallInterfaces(SM::ServiceManager& service_manager); | 104 | void 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 | |||
| 8 | namespace Service::Time { | ||
| 9 | const std::size_t SHARED_MEMORY_SIZE = 0x1000; | ||
| 10 | |||
| 11 | SharedMemory::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 | |||
| 22 | SharedMemory::~SharedMemory() = default; | ||
| 23 | |||
| 24 | Kernel::SharedPtr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() const { | ||
| 25 | return shared_memory_holder; | ||
| 26 | } | ||
| 27 | |||
| 28 | void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) { | ||
| 29 | shared_memory_format.standard_steady_clock_timepoint.StoreData( | ||
| 30 | shared_memory_holder->GetPointer(), timepoint); | ||
| 31 | } | ||
| 32 | |||
| 33 | void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) { | ||
| 34 | shared_memory_format.standard_local_system_clock_context.StoreData( | ||
| 35 | shared_memory_holder->GetPointer(), context); | ||
| 36 | } | ||
| 37 | |||
| 38 | void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) { | ||
| 39 | shared_memory_format.standard_network_system_clock_context.StoreData( | ||
| 40 | shared_memory_holder->GetPointer(), context); | ||
| 41 | } | ||
| 42 | |||
| 43 | void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) { | ||
| 44 | shared_memory_format.standard_user_system_clock_automatic_correction.StoreData( | ||
| 45 | shared_memory_holder->GetPointer(), enabled); | ||
| 46 | } | ||
| 47 | |||
| 48 | SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() { | ||
| 49 | return shared_memory_format.standard_steady_clock_timepoint.ReadData( | ||
| 50 | shared_memory_holder->GetPointer()); | ||
| 51 | } | ||
| 52 | |||
| 53 | SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() { | ||
| 54 | return shared_memory_format.standard_local_system_clock_context.ReadData( | ||
| 55 | shared_memory_holder->GetPointer()); | ||
| 56 | } | ||
| 57 | |||
| 58 | SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() { | ||
| 59 | return shared_memory_format.standard_network_system_clock_context.ReadData( | ||
| 60 | shared_memory_holder->GetPointer()); | ||
| 61 | } | ||
| 62 | |||
| 63 | bool 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 | |||
| 11 | namespace Service::Time { | ||
| 12 | class SharedMemory { | ||
| 13 | public: | ||
| 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 | |||
| 68 | private: | ||
| 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 | ||
| 217 | ResultStatus 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 | |||
| 48 | private: | 50 | private: |
| 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 | |||
| 12 | namespace Loader { | ||
| 13 | |||
| 14 | namespace { | ||
| 15 | constexpr u32 PageAlignSize(u32 size) { | ||
| 16 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | ||
| 17 | } | ||
| 18 | } // Anonymous namespace | ||
| 19 | |||
| 20 | AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_) | ||
| 21 | : AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {} | ||
| 22 | |||
| 23 | AppLoader_KIP::~AppLoader_KIP() = default; | ||
| 24 | |||
| 25 | FileType 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 | |||
| 38 | FileType AppLoader_KIP::GetFileType() const { | ||
| 39 | return (kip != nullptr && kip->GetStatus() == ResultStatus::Success) ? FileType::KIP | ||
| 40 | : FileType::Error; | ||
| 41 | } | ||
| 42 | |||
| 43 | AppLoader::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 | |||
| 9 | namespace FileSys { | ||
| 10 | class KIP; | ||
| 11 | } | ||
| 12 | |||
| 13 | namespace Loader { | ||
| 14 | |||
| 15 | class AppLoader_KIP final : public AppLoader { | ||
| 16 | public: | ||
| 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 | |||
| 31 | private: | ||
| 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 | ||
| 96 | constexpr std::array<const char*, 62> RESULT_MESSAGES{ | 102 | constexpr 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 | ||
| 161 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { | 171 | std::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 | ||
| 129 | std::ostream& operator<<(std::ostream& os, ResultStatus status); | 134 | std::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 | |||
| 270 | protected: | 281 | protected: |
| 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 | ||
| 97 | ResultStatus 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 | |||
| 45 | private: | 47 | private: |
| 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 | |||
| 109 | ResultStatus 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 | |||
| 45 | private: | 47 | private: |
| 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 | ||
| 191 | ResultStatus 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 | |||
| 91 | private: | ||
| 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 | ||
| 170 | ResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& file) { | 170 | ResultStatus 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 | ||
| 187 | ResultStatus 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 | |||
| 52 | private: | 54 | private: |
| 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 | ||
| 135 | ResultStatus AppLoader_XCI::ReadManualRomFS(FileSys::VirtualFile& file) { | 135 | ResultStatus 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 | ||
| 152 | ResultStatus 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 | |||
| 52 | private: | 54 | private: |
| 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 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | std::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 | |||
| 26 | std::string GetTimestamp() { | ||
| 27 | const auto time = std::time(nullptr); | ||
| 28 | return fmt::format("{:%FT%H-%M-%S}", *std::localtime(&time)); | ||
| 29 | } | ||
| 30 | |||
| 31 | using namespace nlohmann; | ||
| 32 | |||
| 33 | void 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 | |||
| 42 | json 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 | |||
| 55 | json 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 | |||
| 69 | json 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 | |||
| 98 | json 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 | |||
| 111 | json 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 | |||
| 127 | json 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 | |||
| 138 | template <bool read_value, typename DescriptorType> | ||
| 139 | json 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 | |||
| 159 | json 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 | |||
| 179 | namespace Core { | ||
| 180 | |||
| 181 | Reporter::Reporter(Core::System& system) : system(system) {} | ||
| 182 | |||
| 183 | Reporter::~Reporter() = default; | ||
| 184 | |||
| 185 | void 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 | |||
| 213 | void 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 | |||
| 238 | void 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 | |||
| 258 | void 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 | |||
| 294 | void 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 | |||
| 316 | void 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 | |||
| 338 | void 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 | |||
| 349 | bool 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 | |||
| 11 | union ResultCode; | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | class HLERequestContext; | ||
| 15 | } // namespace Kernel | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | |||
| 19 | class Reporter { | ||
| 20 | public: | ||
| 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 | |||
| 50 | private: | ||
| 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 | |||
| 13 | namespace Tools { | ||
| 14 | |||
| 15 | namespace { | ||
| 16 | |||
| 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); | ||
| 18 | |||
| 19 | u64 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 | |||
| 35 | void 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 | |||
| 56 | Freezer::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 | |||
| 63 | Freezer::~Freezer() { | ||
| 64 | core_timing.UnscheduleEvent(event, 0); | ||
| 65 | } | ||
| 66 | |||
| 67 | void 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 | |||
| 77 | bool Freezer::IsActive() const { | ||
| 78 | return active.load(std::memory_order_relaxed); | ||
| 79 | } | ||
| 80 | |||
| 81 | void 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 | |||
| 89 | u64 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 | |||
| 102 | void 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 | |||
| 113 | bool 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 | |||
| 121 | void 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 | |||
| 140 | std::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 | |||
| 154 | std::vector<Freezer::Entry> Freezer::GetEntries() const { | ||
| 155 | std::lock_guard lock{entries_mutex}; | ||
| 156 | |||
| 157 | return entries; | ||
| 158 | } | ||
| 159 | |||
| 160 | void 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 | |||
| 178 | void 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 | |||
| 13 | namespace Core::Timing { | ||
| 14 | class CoreTiming; | ||
| 15 | struct EventType; | ||
| 16 | } // namespace Core::Timing | ||
| 17 | |||
| 18 | namespace 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 | */ | ||
| 28 | class Freezer { | ||
| 29 | public: | ||
| 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 | |||
| 69 | private: | ||
| 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 { | |||
| 16 | TestEnvironment::TestEnvironment(bool mutable_memory_) | 16 | TestEnvironment::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 | |||
| 172 | private: | 174 | private: |
| 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 | ||
| 24 | GLintptr OGLBufferCache::UploadMemory(GPUVAddr gpu_addr, std::size_t size, std::size_t alignment, | 24 | GLintptr 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 | ||
| 63 | GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t size, | 64 | GLintptr 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. |
| 44 | uniform sampler2D tex; | 44 | uniform sampler2D tex; |
| 45 | uniform ivec2 variable_offset; | 45 | uniform ivec2 variable_offset; |
| 46 | out vec4 output_attribute; | ||
| 46 | void main() { | 47 | void 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) | |||
| 76 | GlobalRegion GlobalRegionCacheOpenGL::GetGlobalRegion( | 76 | GlobalRegion 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 | ||
| 247 | CachedShader::CachedShader(const Device& device, VAddr cpu_addr, u64 unique_identifier, | 247 | CachedShader::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)}; | 254 | Shader 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 | ||
| 275 | CachedShader::CachedShader(VAddr cpu_addr, u64 unique_identifier, | 274 | Shader 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 | ||
| 287 | std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) { | 280 | std::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; | |||
| 42 | using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>; | 42 | using PrecompiledPrograms = std::unordered_map<ShaderDiskCacheUsage, CachedProgram>; |
| 43 | using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>; | 43 | using PrecompiledShaders = std::unordered_map<u64, GLShader::ProgramResult>; |
| 44 | 44 | ||
| 45 | struct 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 | |||
| 45 | class CachedShader final : public RasterizerCacheObject { | 54 | class CachedShader final : public RasterizerCacheObject { |
| 46 | public: | 55 | public: |
| 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 | ||
| 73 | private: | 80 | private: |
| 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 | ||
| 88 | QtWebBrowser::~QtWebBrowser() = default; | 88 | QtWebBrowser::~QtWebBrowser() = default; |
| 89 | 89 | ||
| 90 | void QtWebBrowser::OpenPage(std::string_view url, std::function<void()> unpack_romfs_callback, | 90 | void 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 | ||
| 43 | signals: | 43 | signals: |
| 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() { | |||
| 436 | void Config::ReadCoreValues() { | 436 | void 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() { | |||
| 827 | void Config::SaveCoreValues() { | 831 | void 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 | ||
| 41 | void ConfigureDebug::ApplyConfiguration() { | 43 | void 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 | ||
| 29 | ConfigureGeneral::~ConfigureGeneral() = default; | 27 | ConfigureGeneral::~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 | ||
| 39 | void ConfigureGeneral::ApplyConfiguration() { | 36 | void 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 | ||
| 49 | void ConfigureGeneral::changeEvent(QEvent* event) { | 44 | void 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 | ||
| 469 | const QStringList GameList::supported_file_extensions = { | 469 | const 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 | ||
| 474 | void GameList::RefreshGameDirectory() { | 473 | void 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) |
| 81 | use_cpu_jit = | 81 | cpu_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 | |||
| 224 | dump_exefs=false | 224 | dump_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 |
| 226 | dump_nso=false | 226 | dump_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 | ||
| 229 | quest_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 @@ | |||
| 1 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/CMakeModules) | ||
| 2 | |||
| 3 | add_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 | |||
| 16 | create_target_directory_groups(yuzu-tester) | ||
| 17 | |||
| 18 | target_link_libraries(yuzu-tester PRIVATE common core input_common) | ||
| 19 | target_link_libraries(yuzu-tester PRIVATE inih glad) | ||
| 20 | if (MSVC) | ||
| 21 | target_link_libraries(yuzu-tester PRIVATE getopt) | ||
| 22 | endif() | ||
| 23 | target_link_libraries(yuzu-tester PRIVATE ${PLATFORM_LIBRARIES} SDL2 Threads::Threads) | ||
| 24 | |||
| 25 | if(UNIX AND NOT APPLE) | ||
| 26 | install(TARGETS yuzu-tester RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") | ||
| 27 | endif() | ||
| 28 | |||
| 29 | if (MSVC) | ||
| 30 | include(CopyYuzuSDLDeps) | ||
| 31 | include(CopyYuzuUnicornDeps) | ||
| 32 | copy_yuzu_SDL_deps(yuzu-tester) | ||
| 33 | copy_yuzu_unicorn_deps(yuzu-tester) | ||
| 34 | endif() | ||
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 | |||
| 18 | Config::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 | |||
| 27 | Config::~Config() = default; | ||
| 28 | |||
| 29 | bool 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 | |||
| 47 | void 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 | |||
| 181 | void 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 | |||
| 10 | class INIReader; | ||
| 11 | |||
| 12 | class 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 | |||
| 19 | public: | ||
| 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 | |||
| 7 | namespace DefaultINI { | ||
| 8 | |||
| 9 | const 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) | ||
| 13 | cpu_jit_enabled = | ||
| 14 | |||
| 15 | # Whether to use multi-core for CPU emulation | ||
| 16 | # 0 (default): Disabled, 1: Enabled | ||
| 17 | use_multi_core= | ||
| 18 | |||
| 19 | [Renderer] | ||
| 20 | # Whether to use software or hardware rendering. | ||
| 21 | # 0: Software, 1 (default): Hardware | ||
| 22 | use_hw_renderer = | ||
| 23 | |||
| 24 | # Whether to use the Just-In-Time (JIT) compiler for shader emulation | ||
| 25 | # 0: Interpreter (slow), 1 (default): JIT (fast) | ||
| 26 | use_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 | ||
| 31 | resolution_factor = | ||
| 32 | |||
| 33 | # Whether to enable V-Sync (caps the framerate at 60FPS) or not. | ||
| 34 | # 0 (default): Off, 1: On | ||
| 35 | use_vsync = | ||
| 36 | |||
| 37 | # Whether to use disk based shader cache | ||
| 38 | # 0 (default): Off, 1 : On | ||
| 39 | use_disk_shader_cache = | ||
| 40 | |||
| 41 | # Whether to use accurate GPU emulation | ||
| 42 | # 0 (default): Off (fast), 1 : On (slow) | ||
| 43 | use_accurate_gpu_emulation = | ||
| 44 | |||
| 45 | # Whether to use asynchronous GPU emulation | ||
| 46 | # 0 : Off (slow), 1 (default): On (fast) | ||
| 47 | use_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. | ||
| 51 | bg_red = | ||
| 52 | bg_blue = | ||
| 53 | bg_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 | ||
| 58 | layout_option = | ||
| 59 | |||
| 60 | # Toggle custom layout (using the settings below) on or off. | ||
| 61 | # 0 (default): Off, 1: On | ||
| 62 | custom_layout = | ||
| 63 | |||
| 64 | # Screen placement when using Custom layout option | ||
| 65 | # 0x, 0y is the top left corner of the render window. | ||
| 66 | custom_top_left = | ||
| 67 | custom_top_top = | ||
| 68 | custom_top_right = | ||
| 69 | custom_top_bottom = | ||
| 70 | custom_bottom_left = | ||
| 71 | custom_bottom_top = | ||
| 72 | custom_bottom_right = | ||
| 73 | custom_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 | ||
| 78 | swap_screen = | ||
| 79 | |||
| 80 | [Data Storage] | ||
| 81 | # Whether to create a virtual SD card. | ||
| 82 | # 1 (default): Yes, 0: No | ||
| 83 | use_virtual_sd = | ||
| 84 | |||
| 85 | [System] | ||
| 86 | # Whether the system is docked | ||
| 87 | # 1: Yes, 0 (default): No | ||
| 88 | use_docked_mode = | ||
| 89 | |||
| 90 | # Allow the use of NFC in games | ||
| 91 | # 1 (default): Yes, 0 : No | ||
| 92 | enable_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 | ||
| 96 | rng_seed_enabled = | ||
| 97 | rng_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 | ||
| 102 | custom_rtc_enabled = | ||
| 103 | custom_rtc = | ||
| 104 | |||
| 105 | # Sets the account username, max length is 32 characters | ||
| 106 | # yuzu (default) | ||
| 107 | username = 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 | ||
| 113 | language_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 | ||
| 117 | region_value = | ||
| 118 | |||
| 119 | [Miscellaneous] | ||
| 120 | # A filter which removes logs below a certain logging level. | ||
| 121 | # Examples: *:Debug Kernel.SVC:Trace Service.*:Critical | ||
| 122 | log_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 | ||
| 126 | program_args= | ||
| 127 | # Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them | ||
| 128 | dump_exefs=false | ||
| 129 | # Determines whether or not yuzu will dump all NSOs it attempts to load while loading them | ||
| 130 | dump_nso=false | ||
| 131 | |||
| 132 | [WebService] | ||
| 133 | # Whether or not to enable telemetry | ||
| 134 | # 0: No, 1 (default): Yes | ||
| 135 | enable_telemetry = | ||
| 136 | # URL for Web API | ||
| 137 | web_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 | ||
| 140 | yuzu_username = | ||
| 141 | yuzu_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 '|'): | ||
| 146 | title_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 | |||
| 18 | bool 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 | |||
| 44 | EmuWindow_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 | |||
| 104 | EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { | ||
| 105 | InputCommon::Shutdown(); | ||
| 106 | SDL_GL_DeleteContext(gl_context); | ||
| 107 | SDL_Quit(); | ||
| 108 | } | ||
| 109 | |||
| 110 | void EmuWindow_SDL2_Hide::SwapBuffers() { | ||
| 111 | SDL_GL_SwapWindow(render_window); | ||
| 112 | } | ||
| 113 | |||
| 114 | void EmuWindow_SDL2_Hide::PollEvents() {} | ||
| 115 | |||
| 116 | void EmuWindow_SDL2_Hide::MakeCurrent() { | ||
| 117 | SDL_GL_MakeCurrent(render_window, gl_context); | ||
| 118 | } | ||
| 119 | |||
| 120 | void 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 | |||
| 9 | struct SDL_Window; | ||
| 10 | |||
| 11 | class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow { | ||
| 12 | public: | ||
| 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 | |||
| 31 | private: | ||
| 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 | |||
| 12 | namespace Service::Yuzu { | ||
| 13 | |||
| 14 | constexpr u64 SERVICE_VERSION = 0x00000002; | ||
| 15 | |||
| 16 | class YuzuTest final : public ServiceFramework<YuzuTest> { | ||
| 17 | public: | ||
| 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 | |||
| 34 | private: | ||
| 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 | |||
| 107 | void 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 | |||
| 10 | namespace Service::SM { | ||
| 11 | class ServiceManager; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Service::Yuzu { | ||
| 15 | |||
| 16 | struct TestResult { | ||
| 17 | u32 code; | ||
| 18 | std::string data; | ||
| 19 | std::string name; | ||
| 20 | }; | ||
| 21 | |||
| 22 | void 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 | ||
| 49 | extern "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 | |||
| 57 | static 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 | |||
| 67 | static void PrintVersion() { | ||
| 68 | std::cout << "yuzu [Test Utility] " << Common::g_scm_branch << " " << Common::g_scm_desc | ||
| 69 | << std::endl; | ||
| 70 | } | ||
| 71 | |||
| 72 | static 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 | ||
| 89 | int 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. | ||
| 9 | YUZU_ICON ICON "../../dist/yuzu.ico" | ||
| 10 | |||
| 11 | |||
| 12 | ///////////////////////////////////////////////////////////////////////////// | ||
| 13 | // | ||
| 14 | // RT_MANIFEST | ||
| 15 | // | ||
| 16 | |||
| 17 | 1 RT_MANIFEST "../../dist/yuzu.manifest" | ||