diff options
Diffstat (limited to 'src/core')
199 files changed, 6283 insertions, 2697 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index c42f95705..d0c405ec7 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -126,6 +126,8 @@ add_library(core STATIC | |||
| 126 | file_sys/vfs_vector.h | 126 | file_sys/vfs_vector.h |
| 127 | file_sys/xts_archive.cpp | 127 | file_sys/xts_archive.cpp |
| 128 | file_sys/xts_archive.h | 128 | file_sys/xts_archive.h |
| 129 | frontend/applets/controller.cpp | ||
| 130 | frontend/applets/controller.h | ||
| 129 | frontend/applets/error.cpp | 131 | frontend/applets/error.cpp |
| 130 | frontend/applets/error.h | 132 | frontend/applets/error.h |
| 131 | frontend/applets/general_frontend.cpp | 133 | frontend/applets/general_frontend.cpp |
| @@ -244,6 +246,8 @@ add_library(core STATIC | |||
| 244 | hle/service/am/applet_oe.h | 246 | hle/service/am/applet_oe.h |
| 245 | hle/service/am/applets/applets.cpp | 247 | hle/service/am/applets/applets.cpp |
| 246 | hle/service/am/applets/applets.h | 248 | hle/service/am/applets/applets.h |
| 249 | hle/service/am/applets/controller.cpp | ||
| 250 | hle/service/am/applets/controller.h | ||
| 247 | hle/service/am/applets/error.cpp | 251 | hle/service/am/applets/error.cpp |
| 248 | hle/service/am/applets/error.h | 252 | hle/service/am/applets/error.h |
| 249 | hle/service/am/applets/general_backend.cpp | 253 | hle/service/am/applets/general_backend.cpp |
| @@ -491,6 +495,7 @@ add_library(core STATIC | |||
| 491 | hle/service/sm/controller.h | 495 | hle/service/sm/controller.h |
| 492 | hle/service/sm/sm.cpp | 496 | hle/service/sm/sm.cpp |
| 493 | hle/service/sm/sm.h | 497 | hle/service/sm/sm.h |
| 498 | hle/service/sockets/blocking_worker.h | ||
| 494 | hle/service/sockets/bsd.cpp | 499 | hle/service/sockets/bsd.cpp |
| 495 | hle/service/sockets/bsd.h | 500 | hle/service/sockets/bsd.h |
| 496 | hle/service/sockets/ethc.cpp | 501 | hle/service/sockets/ethc.cpp |
| @@ -501,6 +506,8 @@ add_library(core STATIC | |||
| 501 | hle/service/sockets/sfdnsres.h | 506 | hle/service/sockets/sfdnsres.h |
| 502 | hle/service/sockets/sockets.cpp | 507 | hle/service/sockets/sockets.cpp |
| 503 | hle/service/sockets/sockets.h | 508 | hle/service/sockets/sockets.h |
| 509 | hle/service/sockets/sockets_translate.cpp | ||
| 510 | hle/service/sockets/sockets_translate.h | ||
| 504 | hle/service/spl/csrng.cpp | 511 | hle/service/spl/csrng.cpp |
| 505 | hle/service/spl/csrng.h | 512 | hle/service/spl/csrng.h |
| 506 | hle/service/spl/module.cpp | 513 | hle/service/spl/module.cpp |
| @@ -586,6 +593,9 @@ add_library(core STATIC | |||
| 586 | memory/dmnt_cheat_vm.h | 593 | memory/dmnt_cheat_vm.h |
| 587 | memory.cpp | 594 | memory.cpp |
| 588 | memory.h | 595 | memory.h |
| 596 | network/network.cpp | ||
| 597 | network/network.h | ||
| 598 | network/sockets.h | ||
| 589 | perf_stats.cpp | 599 | perf_stats.cpp |
| 590 | perf_stats.h | 600 | perf_stats.h |
| 591 | reporter.cpp | 601 | reporter.cpp |
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp index df0350881..9c8898700 100644 --- a/src/core/arm/cpu_interrupt_handler.cpp +++ b/src/core/arm/cpu_interrupt_handler.cpp | |||
| @@ -7,9 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | namespace Core { | 8 | namespace Core { |
| 9 | 9 | ||
| 10 | CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} { | 10 | CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {} |
| 11 | interrupt_event = std::make_unique<Common::Event>(); | ||
| 12 | } | ||
| 13 | 11 | ||
| 14 | CPUInterruptHandler::~CPUInterruptHandler() = default; | 12 | CPUInterruptHandler::~CPUInterruptHandler() = default; |
| 15 | 13 | ||
| @@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) { | |||
| 17 | if (is_interrupted_) { | 15 | if (is_interrupted_) { |
| 18 | interrupt_event->Set(); | 16 | interrupt_event->Set(); |
| 19 | } | 17 | } |
| 20 | this->is_interrupted = is_interrupted_; | 18 | is_interrupted = is_interrupted_; |
| 21 | } | 19 | } |
| 22 | 20 | ||
| 23 | void CPUInterruptHandler::AwaitInterrupt() { | 21 | void CPUInterruptHandler::AwaitInterrupt() { |
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h index 3d062d326..71e582f79 100644 --- a/src/core/arm/cpu_interrupt_handler.h +++ b/src/core/arm/cpu_interrupt_handler.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | 9 | ||
| 9 | namespace Common { | 10 | namespace Common { |
| @@ -32,8 +33,8 @@ public: | |||
| 32 | void AwaitInterrupt(); | 33 | void AwaitInterrupt(); |
| 33 | 34 | ||
| 34 | private: | 35 | private: |
| 35 | bool is_interrupted{}; | ||
| 36 | std::unique_ptr<Common::Event> interrupt_event; | 36 | std::unique_ptr<Common::Event> interrupt_event; |
| 37 | std::atomic_bool is_interrupted{false}; | ||
| 37 | }; | 38 | }; |
| 38 | 39 | ||
| 39 | } // namespace Core | 40 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 443ca72eb..b5f28a86e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp | |||
| @@ -143,7 +143,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& | |||
| 143 | config.wall_clock_cntpct = uses_wall_clock; | 143 | config.wall_clock_cntpct = uses_wall_clock; |
| 144 | 144 | ||
| 145 | // Safe optimizations | 145 | // Safe optimizations |
| 146 | if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { | 146 | if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { |
| 147 | if (!Settings::values.cpuopt_page_tables) { | 147 | if (!Settings::values.cpuopt_page_tables) { |
| 148 | config.page_table = nullptr; | 148 | config.page_table = nullptr; |
| 149 | } | 149 | } |
| @@ -170,6 +170,17 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& | |||
| 170 | } | 170 | } |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | // Unsafe optimizations | ||
| 174 | if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) { | ||
| 175 | config.unsafe_optimizations = true; | ||
| 176 | if (Settings::values.cpuopt_unsafe_unfuse_fma) { | ||
| 177 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; | ||
| 178 | } | ||
| 179 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { | ||
| 180 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 173 | return std::make_unique<Dynarmic::A32::Jit>(config); | 184 | return std::make_unique<Dynarmic::A32::Jit>(config); |
| 174 | } | 185 | } |
| 175 | 186 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index a63a04a25..ce9968724 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp | |||
| @@ -195,7 +195,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& | |||
| 195 | config.wall_clock_cntpct = uses_wall_clock; | 195 | config.wall_clock_cntpct = uses_wall_clock; |
| 196 | 196 | ||
| 197 | // Safe optimizations | 197 | // Safe optimizations |
| 198 | if (Settings::values.cpu_accuracy != Settings::CPUAccuracy::Accurate) { | 198 | if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::DebugMode) { |
| 199 | if (!Settings::values.cpuopt_page_tables) { | 199 | if (!Settings::values.cpuopt_page_tables) { |
| 200 | config.page_table = nullptr; | 200 | config.page_table = nullptr; |
| 201 | } | 201 | } |
| @@ -222,6 +222,17 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable& | |||
| 222 | } | 222 | } |
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | // Unsafe optimizations | ||
| 226 | if (Settings::values.cpu_accuracy == Settings::CPUAccuracy::Unsafe) { | ||
| 227 | config.unsafe_optimizations = true; | ||
| 228 | if (Settings::values.cpuopt_unsafe_unfuse_fma) { | ||
| 229 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA; | ||
| 230 | } | ||
| 231 | if (Settings::values.cpuopt_unsafe_reduce_fp_error) { | ||
| 232 | config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_ReducedErrorFP; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 225 | return std::make_shared<Dynarmic::A64::Jit>(config); | 236 | return std::make_shared<Dynarmic::A64::Jit>(config); |
| 226 | } | 237 | } |
| 227 | 238 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp index 54556e0f9..caefc09f4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp | |||
| @@ -34,7 +34,7 @@ std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigne | |||
| 34 | CoprocReg CRm, unsigned opc2) { | 34 | CoprocReg CRm, unsigned opc2) { |
| 35 | LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn, | 35 | LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn, |
| 36 | CRm, opc2); | 36 | CRm, opc2); |
| 37 | return {}; | 37 | return std::nullopt; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, | 40 | CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn, |
| @@ -115,7 +115,7 @@ std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_trans | |||
| 115 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", | 115 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", |
| 116 | long_transfer ? "l" : "", CRd); | 116 | long_transfer ? "l" : "", CRd); |
| 117 | } | 117 | } |
| 118 | return {}; | 118 | return std::nullopt; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, | 121 | std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd, |
| @@ -127,7 +127,7 @@ std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_tran | |||
| 127 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", | 127 | LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "", |
| 128 | long_transfer ? "l" : "", CRd); | 128 | long_transfer ? "l" : "", CRd); |
| 129 | } | 129 | } |
| 130 | return {}; | 130 | return std::nullopt; |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | } // namespace Core | 133 | } // namespace Core |
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h index 7356d252e..dc6f4af3a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h +++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h | |||
| @@ -35,8 +35,8 @@ public: | |||
| 35 | std::optional<u8> option) override; | 35 | std::optional<u8> option) override; |
| 36 | 36 | ||
| 37 | ARM_Dynarmic_32& parent; | 37 | ARM_Dynarmic_32& parent; |
| 38 | u32 uprw; | 38 | u32 uprw = 0; |
| 39 | u32 uro; | 39 | u32 uro = 0; |
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | } // namespace Core | 42 | } // namespace Core |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 69a1aa0a5..81e8cc338 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | #include "core/loader/loader.h" | 43 | #include "core/loader/loader.h" |
| 44 | #include "core/memory.h" | 44 | #include "core/memory.h" |
| 45 | #include "core/memory/cheat_engine.h" | 45 | #include "core/memory/cheat_engine.h" |
| 46 | #include "core/network/network.h" | ||
| 46 | #include "core/perf_stats.h" | 47 | #include "core/perf_stats.h" |
| 47 | #include "core/reporter.h" | 48 | #include "core/reporter.h" |
| 48 | #include "core/settings.h" | 49 | #include "core/settings.h" |
| @@ -112,7 +113,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 112 | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); | 113 | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); |
| 113 | } | 114 | } |
| 114 | 115 | ||
| 115 | if (FileUtil::IsDirectory(path)) | 116 | if (Common::FS::IsDirectory(path)) |
| 116 | return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); | 117 | return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); |
| 117 | 118 | ||
| 118 | return vfs->OpenFile(path, FileSys::Mode::Read); | 119 | return vfs->OpenFile(path, FileSys::Mode::Read); |
| @@ -145,7 +146,7 @@ struct System::Impl { | |||
| 145 | ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { | 146 | ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) { |
| 146 | LOG_DEBUG(HW_Memory, "initialized OK"); | 147 | LOG_DEBUG(HW_Memory, "initialized OK"); |
| 147 | 148 | ||
| 148 | device_memory = std::make_unique<Core::DeviceMemory>(system); | 149 | device_memory = std::make_unique<Core::DeviceMemory>(); |
| 149 | 150 | ||
| 150 | is_multicore = Settings::values.use_multi_core.GetValue(); | 151 | is_multicore = Settings::values.use_multi_core.GetValue(); |
| 151 | is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); | 152 | is_async_gpu = is_multicore || Settings::values.use_asynchronous_gpu_emulation.GetValue(); |
| @@ -177,7 +178,7 @@ struct System::Impl { | |||
| 177 | arp_manager.ResetAll(); | 178 | arp_manager.ResetAll(); |
| 178 | 179 | ||
| 179 | telemetry_session = std::make_unique<Core::TelemetrySession>(); | 180 | telemetry_session = std::make_unique<Core::TelemetrySession>(); |
| 180 | service_manager = std::make_shared<Service::SM::ServiceManager>(); | 181 | service_manager = std::make_shared<Service::SM::ServiceManager>(kernel); |
| 181 | 182 | ||
| 182 | Service::Init(service_manager, system); | 183 | Service::Init(service_manager, system); |
| 183 | GDBStub::DeferStart(); | 184 | GDBStub::DeferStart(); |
| @@ -187,7 +188,6 @@ struct System::Impl { | |||
| 187 | if (!gpu_core) { | 188 | if (!gpu_core) { |
| 188 | return ResultStatus::ErrorVideoCore; | 189 | return ResultStatus::ErrorVideoCore; |
| 189 | } | 190 | } |
| 190 | gpu_core->Renderer().Rasterizer().SetupDirtyFlags(); | ||
| 191 | 191 | ||
| 192 | is_powered_on = true; | 192 | is_powered_on = true; |
| 193 | exit_lock = false; | 193 | exit_lock = false; |
| @@ -221,7 +221,7 @@ struct System::Impl { | |||
| 221 | telemetry_session->AddInitialInfo(*app_loader); | 221 | telemetry_session->AddInitialInfo(*app_loader); |
| 222 | auto main_process = | 222 | auto main_process = |
| 223 | Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); | 223 | Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland); |
| 224 | const auto [load_result, load_parameters] = app_loader->Load(*main_process); | 224 | const auto [load_result, load_parameters] = app_loader->Load(*main_process, system); |
| 225 | if (load_result != Loader::ResultStatus::Success) { | 225 | if (load_result != Loader::ResultStatus::Success) { |
| 226 | LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); | 226 | LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); |
| 227 | Shutdown(); | 227 | Shutdown(); |
| @@ -268,14 +268,14 @@ struct System::Impl { | |||
| 268 | // Log last frame performance stats if game was loded | 268 | // Log last frame performance stats if game was loded |
| 269 | if (perf_stats) { | 269 | if (perf_stats) { |
| 270 | const auto perf_results = GetAndResetPerfStats(); | 270 | const auto perf_results = GetAndResetPerfStats(); |
| 271 | telemetry_session->AddField(Telemetry::FieldType::Performance, | 271 | constexpr auto performance = Common::Telemetry::FieldType::Performance; |
| 272 | "Shutdown_EmulationSpeed", | 272 | |
| 273 | telemetry_session->AddField(performance, "Shutdown_EmulationSpeed", | ||
| 273 | perf_results.emulation_speed * 100.0); | 274 | perf_results.emulation_speed * 100.0); |
| 274 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate", | 275 | telemetry_session->AddField(performance, "Shutdown_Framerate", perf_results.game_fps); |
| 275 | perf_results.game_fps); | 276 | telemetry_session->AddField(performance, "Shutdown_Frametime", |
| 276 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", | ||
| 277 | perf_results.frametime * 1000.0); | 277 | perf_results.frametime * 1000.0); |
| 278 | telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS", | 278 | telemetry_session->AddField(performance, "Mean_Frametime_MS", |
| 279 | perf_stats->GetMeanFrametime()); | 279 | perf_stats->GetMeanFrametime()); |
| 280 | } | 280 | } |
| 281 | 281 | ||
| @@ -394,6 +394,9 @@ struct System::Impl { | |||
| 394 | /// Telemetry session for this emulation session | 394 | /// Telemetry session for this emulation session |
| 395 | std::unique_ptr<Core::TelemetrySession> telemetry_session; | 395 | std::unique_ptr<Core::TelemetrySession> telemetry_session; |
| 396 | 396 | ||
| 397 | /// Network instance | ||
| 398 | Network::NetworkInstance network_instance; | ||
| 399 | |||
| 397 | ResultStatus status = ResultStatus::Success; | 400 | ResultStatus status = ResultStatus::Success; |
| 398 | std::string status_details = ""; | 401 | std::string status_details = ""; |
| 399 | 402 | ||
| @@ -626,11 +629,11 @@ Loader::AppLoader& System::GetAppLoader() const { | |||
| 626 | return *impl->app_loader; | 629 | return *impl->app_loader; |
| 627 | } | 630 | } |
| 628 | 631 | ||
| 629 | void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) { | 632 | void System::SetFilesystem(FileSys::VirtualFilesystem vfs) { |
| 630 | impl->virtual_filesystem = std::move(vfs); | 633 | impl->virtual_filesystem = std::move(vfs); |
| 631 | } | 634 | } |
| 632 | 635 | ||
| 633 | std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const { | 636 | FileSys::VirtualFilesystem System::GetFilesystem() const { |
| 634 | return impl->virtual_filesystem; | 637 | return impl->virtual_filesystem; |
| 635 | } | 638 | } |
| 636 | 639 | ||
diff --git a/src/core/core.h b/src/core/core.h index 5c6cfbffe..27efe30bb 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -120,7 +120,7 @@ public: | |||
| 120 | * Gets the instance of the System singleton class. | 120 | * Gets the instance of the System singleton class. |
| 121 | * @returns Reference to the instance of the System singleton class. | 121 | * @returns Reference to the instance of the System singleton class. |
| 122 | */ | 122 | */ |
| 123 | static System& GetInstance() { | 123 | [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { |
| 124 | return s_instance; | 124 | return s_instance; |
| 125 | } | 125 | } |
| 126 | 126 | ||
| @@ -316,9 +316,9 @@ public: | |||
| 316 | Service::SM::ServiceManager& ServiceManager(); | 316 | Service::SM::ServiceManager& ServiceManager(); |
| 317 | const Service::SM::ServiceManager& ServiceManager() const; | 317 | const Service::SM::ServiceManager& ServiceManager() const; |
| 318 | 318 | ||
| 319 | void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs); | 319 | void SetFilesystem(FileSys::VirtualFilesystem vfs); |
| 320 | 320 | ||
| 321 | std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const; | 321 | FileSys::VirtualFilesystem GetFilesystem() const; |
| 322 | 322 | ||
| 323 | void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, | 323 | void RegisterCheatList(const std::vector<Memory::CheatEntry>& list, |
| 324 | const std::array<u8, 0x20>& build_id, VAddr main_region_begin, | 324 | const std::array<u8, 0x20>& build_id, VAddr main_region_begin, |
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index a63e60461..e6c8461a5 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -7,14 +7,14 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <tuple> | 8 | #include <tuple> |
| 9 | 9 | ||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/microprofile.h" | 10 | #include "common/microprofile.h" |
| 12 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 13 | #include "core/core_timing_util.h" | 12 | #include "core/core_timing_util.h" |
| 13 | #include "core/hardware_properties.h" | ||
| 14 | 14 | ||
| 15 | namespace Core::Timing { | 15 | namespace Core::Timing { |
| 16 | 16 | ||
| 17 | constexpr u64 MAX_SLICE_LENGTH = 4000; | 17 | constexpr s64 MAX_SLICE_LENGTH = 4000; |
| 18 | 18 | ||
| 19 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { | 19 | std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { |
| 20 | return std::make_shared<EventType>(std::move(callback), std::move(name)); | 20 | return std::make_shared<EventType>(std::move(callback), std::move(name)); |
| @@ -23,7 +23,7 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac | |||
| 23 | struct CoreTiming::Event { | 23 | struct CoreTiming::Event { |
| 24 | u64 time; | 24 | u64 time; |
| 25 | u64 fifo_order; | 25 | u64 fifo_order; |
| 26 | u64 userdata; | 26 | std::uintptr_t user_data; |
| 27 | std::weak_ptr<EventType> type; | 27 | std::weak_ptr<EventType> type; |
| 28 | 28 | ||
| 29 | // Sort by time, unless the times are the same, in which case sort by | 29 | // Sort by time, unless the times are the same, in which case sort by |
| @@ -37,10 +37,8 @@ struct CoreTiming::Event { | |||
| 37 | } | 37 | } |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | CoreTiming::CoreTiming() { | 40 | CoreTiming::CoreTiming() |
| 41 | clock = | 41 | : clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {} |
| 42 | Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ); | ||
| 43 | } | ||
| 44 | 42 | ||
| 45 | CoreTiming::~CoreTiming() = default; | 43 | CoreTiming::~CoreTiming() = default; |
| 46 | 44 | ||
| @@ -53,12 +51,12 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { | |||
| 53 | instance.ThreadLoop(); | 51 | instance.ThreadLoop(); |
| 54 | } | 52 | } |
| 55 | 53 | ||
| 56 | void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) { | 54 | void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { |
| 57 | on_thread_init = std::move(on_thread_init_); | 55 | on_thread_init = std::move(on_thread_init_); |
| 58 | event_fifo_id = 0; | 56 | event_fifo_id = 0; |
| 59 | shutting_down = false; | 57 | shutting_down = false; |
| 60 | ticks = 0; | 58 | ticks = 0; |
| 61 | const auto empty_timed_callback = [](u64, s64) {}; | 59 | const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {}; |
| 62 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); | 60 | ev_lost = CreateEvent("_lost_event", empty_timed_callback); |
| 63 | if (is_multicore) { | 61 | if (is_multicore) { |
| 64 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); | 62 | timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); |
| @@ -106,23 +104,25 @@ bool CoreTiming::HasPendingEvents() const { | |||
| 106 | return !(wait_set && event_queue.empty()); | 104 | return !(wait_set && event_queue.empty()); |
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | 107 | void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 110 | u64 userdata) { | 108 | const std::shared_ptr<EventType>& event_type, |
| 109 | std::uintptr_t user_data) { | ||
| 111 | { | 110 | { |
| 112 | std::scoped_lock scope{basic_lock}; | 111 | std::scoped_lock scope{basic_lock}; |
| 113 | const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future); | 112 | const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count()); |
| 114 | 113 | ||
| 115 | event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); | 114 | event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type}); |
| 116 | 115 | ||
| 117 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); | 116 | std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); |
| 118 | } | 117 | } |
| 119 | event.Set(); | 118 | event.Set(); |
| 120 | } | 119 | } |
| 121 | 120 | ||
| 122 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { | 121 | void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, |
| 122 | std::uintptr_t user_data) { | ||
| 123 | std::scoped_lock scope{basic_lock}; | 123 | std::scoped_lock scope{basic_lock}; |
| 124 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { | 124 | const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { |
| 125 | return e.type.lock().get() == event_type.get() && e.userdata == userdata; | 125 | return e.type.lock().get() == event_type.get() && e.user_data == user_data; |
| 126 | }); | 126 | }); |
| 127 | 127 | ||
| 128 | // Removing random items breaks the invariant so we have to re-establish it. | 128 | // Removing random items breaks the invariant so we have to re-establish it. |
| @@ -134,7 +134,7 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u | |||
| 134 | 134 | ||
| 135 | void CoreTiming::AddTicks(u64 ticks) { | 135 | void CoreTiming::AddTicks(u64 ticks) { |
| 136 | this->ticks += ticks; | 136 | this->ticks += ticks; |
| 137 | downcount -= ticks; | 137 | downcount -= static_cast<s64>(ticks); |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | void CoreTiming::Idle() { | 140 | void CoreTiming::Idle() { |
| @@ -195,8 +195,9 @@ std::optional<s64> CoreTiming::Advance() { | |||
| 195 | event_queue.pop_back(); | 195 | event_queue.pop_back(); |
| 196 | basic_lock.unlock(); | 196 | basic_lock.unlock(); |
| 197 | 197 | ||
| 198 | if (auto event_type{evt.type.lock()}) { | 198 | if (const auto event_type{evt.type.lock()}) { |
| 199 | event_type->callback(evt.userdata, global_timer - evt.time); | 199 | event_type->callback( |
| 200 | evt.user_data, std::chrono::nanoseconds{static_cast<s64>(global_timer - evt.time)}); | ||
| 200 | } | 201 | } |
| 201 | 202 | ||
| 202 | basic_lock.lock(); | 203 | basic_lock.lock(); |
diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 72faaab64..b0b6036e4 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h | |||
| @@ -17,14 +17,13 @@ | |||
| 17 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "common/spin_lock.h" | 18 | #include "common/spin_lock.h" |
| 19 | #include "common/thread.h" | 19 | #include "common/thread.h" |
| 20 | #include "common/threadsafe_queue.h" | ||
| 21 | #include "common/wall_clock.h" | 20 | #include "common/wall_clock.h" |
| 22 | #include "core/hardware_properties.h" | ||
| 23 | 21 | ||
| 24 | namespace Core::Timing { | 22 | namespace Core::Timing { |
| 25 | 23 | ||
| 26 | /// A callback that may be scheduled for a particular core timing event. | 24 | /// A callback that may be scheduled for a particular core timing event. |
| 27 | using TimedCallback = std::function<void(u64 userdata, s64 cycles_late)>; | 25 | using TimedCallback = |
| 26 | std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>; | ||
| 28 | 27 | ||
| 29 | /// Contains the characteristics of a particular event. | 28 | /// Contains the characteristics of a particular event. |
| 30 | struct EventType { | 29 | struct EventType { |
| @@ -42,12 +41,12 @@ struct EventType { | |||
| 42 | * in main CPU clock cycles. | 41 | * in main CPU clock cycles. |
| 43 | * | 42 | * |
| 44 | * To schedule an event, you first have to register its type. This is where you pass in the | 43 | * To schedule an event, you first have to register its type. This is where you pass in the |
| 45 | * callback. You then schedule events using the type id you get back. | 44 | * callback. You then schedule events using the type ID you get back. |
| 46 | * | 45 | * |
| 47 | * The int cyclesLate that the callbacks get is how many cycles late it was. | 46 | * The s64 ns_late that the callbacks get is how many ns late it was. |
| 48 | * So to schedule a new event on a regular basis: | 47 | * So to schedule a new event on a regular basis: |
| 49 | * inside callback: | 48 | * inside callback: |
| 50 | * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") | 49 | * ScheduleEvent(period_in_ns - ns_late, callback, "whatever") |
| 51 | */ | 50 | */ |
| 52 | class CoreTiming { | 51 | class CoreTiming { |
| 53 | public: | 52 | public: |
| @@ -62,7 +61,7 @@ public: | |||
| 62 | 61 | ||
| 63 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is | 62 | /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is |
| 64 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. | 63 | /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. |
| 65 | void Initialize(std::function<void(void)>&& on_thread_init_); | 64 | void Initialize(std::function<void()>&& on_thread_init_); |
| 66 | 65 | ||
| 67 | /// Tears down all timing related functionality. | 66 | /// Tears down all timing related functionality. |
| 68 | void Shutdown(); | 67 | void Shutdown(); |
| @@ -95,10 +94,10 @@ public: | |||
| 95 | bool HasPendingEvents() const; | 94 | bool HasPendingEvents() const; |
| 96 | 95 | ||
| 97 | /// Schedules an event in core timing | 96 | /// Schedules an event in core timing |
| 98 | void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type, | 97 | void ScheduleEvent(std::chrono::nanoseconds ns_into_future, |
| 99 | u64 userdata = 0); | 98 | const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0); |
| 100 | 99 | ||
| 101 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); | 100 | void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data); |
| 102 | 101 | ||
| 103 | /// We only permit one event of each type in the queue at a time. | 102 | /// We only permit one event of each type in the queue at a time. |
| 104 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); | 103 | void RemoveEvent(const std::shared_ptr<EventType>& event_type); |
| @@ -141,8 +140,6 @@ private: | |||
| 141 | 140 | ||
| 142 | u64 global_timer = 0; | 141 | u64 global_timer = 0; |
| 143 | 142 | ||
| 144 | std::chrono::nanoseconds start_point; | ||
| 145 | |||
| 146 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. | 143 | // The queue is a min-heap using std::make_heap/push_heap/pop_heap. |
| 147 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and | 144 | // We don't use std::priority_queue because we need to be able to serialize, unserialize and |
| 148 | // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't | 145 | // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't |
| @@ -161,7 +158,7 @@ private: | |||
| 161 | std::atomic<bool> wait_set{}; | 158 | std::atomic<bool> wait_set{}; |
| 162 | std::atomic<bool> shutting_down{}; | 159 | std::atomic<bool> shutting_down{}; |
| 163 | std::atomic<bool> has_started{}; | 160 | std::atomic<bool> has_started{}; |
| 164 | std::function<void(void)> on_thread_init{}; | 161 | std::function<void()> on_thread_init{}; |
| 165 | 162 | ||
| 166 | bool is_multicore{}; | 163 | bool is_multicore{}; |
| 167 | 164 | ||
diff --git a/src/core/core_timing_util.cpp b/src/core/core_timing_util.cpp index aefc63663..8ce8e602e 100644 --- a/src/core/core_timing_util.cpp +++ b/src/core/core_timing_util.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <limits> | 8 | #include <limits> |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/uint128.h" | 10 | #include "common/uint128.h" |
| 11 | #include "core/hardware_properties.h" | ||
| 11 | 12 | ||
| 12 | namespace Core::Timing { | 13 | namespace Core::Timing { |
| 13 | 14 | ||
diff --git a/src/core/core_timing_util.h b/src/core/core_timing_util.h index 2ed979e14..e4a046bf9 100644 --- a/src/core/core_timing_util.h +++ b/src/core/core_timing_util.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <chrono> | 7 | #include <chrono> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hardware_properties.h" | ||
| 10 | 9 | ||
| 11 | namespace Core::Timing { | 10 | namespace Core::Timing { |
| 12 | 11 | ||
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 32afcf3ae..688b99eba 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -41,9 +41,9 @@ void CpuManager::Shutdown() { | |||
| 41 | running_mode = false; | 41 | running_mode = false; |
| 42 | Pause(false); | 42 | Pause(false); |
| 43 | if (is_multicore) { | 43 | if (is_multicore) { |
| 44 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 44 | for (auto& data : core_data) { |
| 45 | core_data[core].host_thread->join(); | 45 | data.host_thread->join(); |
| 46 | core_data[core].host_thread.reset(); | 46 | data.host_thread.reset(); |
| 47 | } | 47 | } |
| 48 | } else { | 48 | } else { |
| 49 | core_data[0].host_thread->join(); | 49 | core_data[0].host_thread->join(); |
| @@ -52,15 +52,15 @@ void CpuManager::Shutdown() { | |||
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { | 54 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { |
| 55 | return std::function<void(void*)>(GuestThreadFunction); | 55 | return GuestThreadFunction; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { | 58 | std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { |
| 59 | return std::function<void(void*)>(IdleThreadFunction); | 59 | return IdleThreadFunction; |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { | 62 | std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { |
| 63 | return std::function<void(void*)>(SuspendThreadFunction); | 63 | return SuspendThreadFunction; |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { | 66 | void CpuManager::GuestThreadFunction(void* cpu_manager_) { |
| @@ -166,25 +166,23 @@ void CpuManager::MultiCorePause(bool paused) { | |||
| 166 | bool all_not_barrier = false; | 166 | bool all_not_barrier = false; |
| 167 | while (!all_not_barrier) { | 167 | while (!all_not_barrier) { |
| 168 | all_not_barrier = true; | 168 | all_not_barrier = true; |
| 169 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 169 | for (const auto& data : core_data) { |
| 170 | all_not_barrier &= | 170 | all_not_barrier &= !data.is_running.load() && data.initialized.load(); |
| 171 | !core_data[core].is_running.load() && core_data[core].initialized.load(); | ||
| 172 | } | 171 | } |
| 173 | } | 172 | } |
| 174 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 173 | for (auto& data : core_data) { |
| 175 | core_data[core].enter_barrier->Set(); | 174 | data.enter_barrier->Set(); |
| 176 | } | 175 | } |
| 177 | if (paused_state.load()) { | 176 | if (paused_state.load()) { |
| 178 | bool all_barrier = false; | 177 | bool all_barrier = false; |
| 179 | while (!all_barrier) { | 178 | while (!all_barrier) { |
| 180 | all_barrier = true; | 179 | all_barrier = true; |
| 181 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 180 | for (const auto& data : core_data) { |
| 182 | all_barrier &= | 181 | all_barrier &= data.is_paused.load() && data.initialized.load(); |
| 183 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 184 | } | 182 | } |
| 185 | } | 183 | } |
| 186 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 184 | for (auto& data : core_data) { |
| 187 | core_data[core].exit_barrier->Set(); | 185 | data.exit_barrier->Set(); |
| 188 | } | 186 | } |
| 189 | } | 187 | } |
| 190 | } else { | 188 | } else { |
| @@ -192,9 +190,8 @@ void CpuManager::MultiCorePause(bool paused) { | |||
| 192 | bool all_barrier = false; | 190 | bool all_barrier = false; |
| 193 | while (!all_barrier) { | 191 | while (!all_barrier) { |
| 194 | all_barrier = true; | 192 | all_barrier = true; |
| 195 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 193 | for (const auto& data : core_data) { |
| 196 | all_barrier &= | 194 | all_barrier &= data.is_paused.load() && data.initialized.load(); |
| 197 | core_data[core].is_paused.load() && core_data[core].initialized.load(); | ||
| 198 | } | 195 | } |
| 199 | } | 196 | } |
| 200 | /// Don't release the barrier | 197 | /// Don't release the barrier |
| @@ -331,7 +328,7 @@ void CpuManager::RunThread(std::size_t core) { | |||
| 331 | system.RegisterCoreThread(core); | 328 | system.RegisterCoreThread(core); |
| 332 | std::string name; | 329 | std::string name; |
| 333 | if (is_multicore) { | 330 | if (is_multicore) { |
| 334 | name = "yuzu:CoreCPUThread_" + std::to_string(core); | 331 | name = "yuzu:CPUCore_" + std::to_string(core); |
| 335 | } else { | 332 | } else { |
| 336 | name = "yuzu:CPUThread"; | 333 | name = "yuzu:CPUThread"; |
| 337 | } | 334 | } |
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp index 4be76bb43..6a9734812 100644 --- a/src/core/crypto/aes_util.cpp +++ b/src/core/crypto/aes_util.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 <array> | ||
| 5 | #include <mbedtls/cipher.h> | 6 | #include <mbedtls/cipher.h> |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -10,8 +11,10 @@ | |||
| 10 | 11 | ||
| 11 | namespace Core::Crypto { | 12 | namespace Core::Crypto { |
| 12 | namespace { | 13 | namespace { |
| 13 | std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) { | 14 | using NintendoTweak = std::array<u8, 16>; |
| 14 | std::vector<u8> out(0x10); | 15 | |
| 16 | NintendoTweak CalculateNintendoTweak(std::size_t sector_id) { | ||
| 17 | NintendoTweak out{}; | ||
| 15 | for (std::size_t i = 0xF; i <= 0xF; --i) { | 18 | for (std::size_t i = 0xF; i <= 0xF; --i) { |
| 16 | out[i] = sector_id & 0xFF; | 19 | out[i] = sector_id & 0xFF; |
| 17 | sector_id >>= 8; | 20 | sector_id >>= 8; |
| @@ -64,13 +67,6 @@ AESCipher<Key, KeySize>::~AESCipher() { | |||
| 64 | } | 67 | } |
| 65 | 68 | ||
| 66 | template <typename Key, std::size_t KeySize> | 69 | template <typename Key, std::size_t KeySize> |
| 67 | void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { | ||
| 68 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || | ||
| 69 | mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, | ||
| 70 | "Failed to set IV on mbedtls ciphers."); | ||
| 71 | } | ||
| 72 | |||
| 73 | template <typename Key, std::size_t KeySize> | ||
| 74 | void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { | 70 | void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { |
| 75 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; | 71 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; |
| 76 | 72 | ||
| @@ -120,10 +116,17 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8* | |||
| 120 | 116 | ||
| 121 | for (std::size_t i = 0; i < size; i += sector_size) { | 117 | for (std::size_t i = 0; i < size; i += sector_size) { |
| 122 | SetIV(CalculateNintendoTweak(sector_id++)); | 118 | SetIV(CalculateNintendoTweak(sector_id++)); |
| 123 | Transcode<u8, u8>(src + i, sector_size, dest + i, op); | 119 | Transcode(src + i, sector_size, dest + i, op); |
| 124 | } | 120 | } |
| 125 | } | 121 | } |
| 126 | 122 | ||
| 123 | template <typename Key, std::size_t KeySize> | ||
| 124 | void AESCipher<Key, KeySize>::SetIVImpl(const u8* data, std::size_t size) { | ||
| 125 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, data, size) || | ||
| 126 | mbedtls_cipher_set_iv(&ctx->decryption_context, data, size)) == 0, | ||
| 127 | "Failed to set IV on mbedtls ciphers."); | ||
| 128 | } | ||
| 129 | |||
| 127 | template class AESCipher<Key128>; | 130 | template class AESCipher<Key128>; |
| 128 | template class AESCipher<Key256>; | 131 | template class AESCipher<Key256>; |
| 129 | } // namespace Core::Crypto | 132 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index edc4ab910..e2a304186 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <type_traits> | 8 | #include <type_traits> |
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/vfs.h" | 10 | #include "core/file_sys/vfs.h" |
| 12 | 11 | ||
| @@ -32,10 +31,12 @@ class AESCipher { | |||
| 32 | 31 | ||
| 33 | public: | 32 | public: |
| 34 | AESCipher(Key key, Mode mode); | 33 | AESCipher(Key key, Mode mode); |
| 35 | |||
| 36 | ~AESCipher(); | 34 | ~AESCipher(); |
| 37 | 35 | ||
| 38 | void SetIV(std::vector<u8> iv); | 36 | template <typename ContiguousContainer> |
| 37 | void SetIV(const ContiguousContainer& container) { | ||
| 38 | SetIVImpl(std::data(container), std::size(container)); | ||
| 39 | } | ||
| 39 | 40 | ||
| 40 | template <typename Source, typename Dest> | 41 | template <typename Source, typename Dest> |
| 41 | void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { | 42 | void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { |
| @@ -59,6 +60,8 @@ public: | |||
| 59 | std::size_t sector_size, Op op); | 60 | std::size_t sector_size, Op op); |
| 60 | 61 | ||
| 61 | private: | 62 | private: |
| 63 | void SetIVImpl(const u8* data, std::size_t size); | ||
| 64 | |||
| 62 | std::unique_ptr<CipherContext> ctx; | 65 | std::unique_ptr<CipherContext> ctx; |
| 63 | }; | 66 | }; |
| 64 | } // namespace Core::Crypto | 67 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp index 902841c77..5c84bb0a4 100644 --- a/src/core/crypto/ctr_encryption_layer.cpp +++ b/src/core/crypto/ctr_encryption_layer.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 <algorithm> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "core/crypto/ctr_encryption_layer.h" | 8 | #include "core/crypto/ctr_encryption_layer.h" |
| @@ -10,8 +11,7 @@ namespace Core::Crypto { | |||
| 10 | 11 | ||
| 11 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, | 12 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, |
| 12 | std::size_t base_offset) | 13 | std::size_t base_offset) |
| 13 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), | 14 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR) {} |
| 14 | iv(16, 0) {} | ||
| 15 | 15 | ||
| 16 | std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { | 16 | std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 17 | if (length == 0) | 17 | if (length == 0) |
| @@ -39,9 +39,8 @@ std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t o | |||
| 39 | return read + Read(data + read, length - read, offset + read); | 39 | return read + Read(data + read, length - read, offset + read); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { | 42 | void CTREncryptionLayer::SetIV(const IVData& iv_) { |
| 43 | const auto length = std::min(iv_.size(), iv.size()); | 43 | iv = iv_; |
| 44 | iv.assign(iv_.cbegin(), iv_.cbegin() + length); | ||
| 45 | } | 44 | } |
| 46 | 45 | ||
| 47 | void CTREncryptionLayer::UpdateIV(std::size_t offset) const { | 46 | void CTREncryptionLayer::UpdateIV(std::size_t offset) const { |
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h index a7bf810f4..a2429f001 100644 --- a/src/core/crypto/ctr_encryption_layer.h +++ b/src/core/crypto/ctr_encryption_layer.h | |||
| @@ -4,7 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <vector> | 7 | #include <array> |
| 8 | |||
| 8 | #include "core/crypto/aes_util.h" | 9 | #include "core/crypto/aes_util.h" |
| 9 | #include "core/crypto/encryption_layer.h" | 10 | #include "core/crypto/encryption_layer.h" |
| 10 | #include "core/crypto/key_manager.h" | 11 | #include "core/crypto/key_manager.h" |
| @@ -14,18 +15,20 @@ namespace Core::Crypto { | |||
| 14 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. | 15 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. |
| 15 | class CTREncryptionLayer : public EncryptionLayer { | 16 | class CTREncryptionLayer : public EncryptionLayer { |
| 16 | public: | 17 | public: |
| 18 | using IVData = std::array<u8, 16>; | ||
| 19 | |||
| 17 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); | 20 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); |
| 18 | 21 | ||
| 19 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | 22 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 20 | 23 | ||
| 21 | void SetIV(const std::vector<u8>& iv); | 24 | void SetIV(const IVData& iv); |
| 22 | 25 | ||
| 23 | private: | 26 | private: |
| 24 | std::size_t base_offset; | 27 | std::size_t base_offset; |
| 25 | 28 | ||
| 26 | // Must be mutable as operations modify cipher contexts. | 29 | // Must be mutable as operations modify cipher contexts. |
| 27 | mutable AESCipher<Key128> cipher; | 30 | mutable AESCipher<Key128> cipher; |
| 28 | mutable std::vector<u8> iv; | 31 | mutable IVData iv{}; |
| 29 | 32 | ||
| 30 | void UpdateIV(std::size_t offset) const; | 33 | void UpdateIV(std::size_t offset) const; |
| 31 | }; | 34 | }; |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index f87fe0abc..65d246050 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -23,7 +23,6 @@ | |||
| 23 | #include "common/hex_util.h" | 23 | #include "common/hex_util.h" |
| 24 | #include "common/logging/log.h" | 24 | #include "common/logging/log.h" |
| 25 | #include "common/string_util.h" | 25 | #include "common/string_util.h" |
| 26 | #include "core/core.h" | ||
| 27 | #include "core/crypto/aes_util.h" | 26 | #include "core/crypto/aes_util.h" |
| 28 | #include "core/crypto/key_manager.h" | 27 | #include "core/crypto/key_manager.h" |
| 29 | #include "core/crypto/partition_data_manager.h" | 28 | #include "core/crypto/partition_data_manager.h" |
| @@ -36,18 +35,86 @@ | |||
| 36 | #include "core/settings.h" | 35 | #include "core/settings.h" |
| 37 | 36 | ||
| 38 | namespace Core::Crypto { | 37 | namespace Core::Crypto { |
| 38 | namespace { | ||
| 39 | 39 | ||
| 40 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; | 40 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; |
| 41 | constexpr u64 FULL_TICKET_SIZE = 0x400; | 41 | constexpr u64 FULL_TICKET_SIZE = 0x400; |
| 42 | 42 | ||
| 43 | using namespace Common; | 43 | using Common::AsArray; |
| 44 | 44 | ||
| 45 | const std::array<SHA256Hash, 2> eticket_source_hashes{ | 45 | // clang-format off |
| 46 | "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source | 46 | constexpr std::array eticket_source_hashes{ |
| 47 | "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source | 47 | AsArray("B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"), // eticket_rsa_kek_source |
| 48 | AsArray("E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"), // eticket_rsa_kekek_source | ||
| 48 | }; | 49 | }; |
| 50 | // clang-format on | ||
| 49 | 51 | ||
| 50 | const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ | 52 | constexpr std::array<std::pair<std::string_view, KeyIndex<S128KeyType>>, 30> s128_file_id{{ |
| 53 | {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, | ||
| 54 | {"eticket_rsa_kek_source", | ||
| 55 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}}, | ||
| 56 | {"eticket_rsa_kekek_source", | ||
| 57 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}}, | ||
| 58 | {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}}, | ||
| 59 | {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}}, | ||
| 60 | {"rsa_oaep_kek_generation_source", | ||
| 61 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}}, | ||
| 62 | {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}}, | ||
| 63 | {"aes_kek_generation_source", | ||
| 64 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}}, | ||
| 65 | {"aes_key_generation_source", | ||
| 66 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, | ||
| 67 | {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}}, | ||
| 68 | {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}}, | ||
| 69 | {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}}, | ||
| 70 | {"key_area_key_application_source", | ||
| 71 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), | ||
| 72 | static_cast<u64>(KeyAreaKeyType::Application)}}, | ||
| 73 | {"key_area_key_ocean_source", | ||
| 74 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), | ||
| 75 | static_cast<u64>(KeyAreaKeyType::Ocean)}}, | ||
| 76 | {"key_area_key_system_source", | ||
| 77 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), | ||
| 78 | static_cast<u64>(KeyAreaKeyType::System)}}, | ||
| 79 | {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, | ||
| 80 | {"keyblob_mac_key_source", | ||
| 81 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}}, | ||
| 82 | {"tsec_key", {S128KeyType::TSEC, 0, 0}}, | ||
| 83 | {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}}, | ||
| 84 | {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, | ||
| 85 | {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 86 | {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 87 | {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 88 | {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 89 | {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 90 | {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 91 | {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 92 | {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 93 | {"header_kek", {S128KeyType::HeaderKek, 0, 0}}, | ||
| 94 | {"sd_card_kek", {S128KeyType::SDKek, 0, 0}}, | ||
| 95 | }}; | ||
| 96 | |||
| 97 | auto Find128ByName(std::string_view name) { | ||
| 98 | return std::find_if(s128_file_id.begin(), s128_file_id.end(), | ||
| 99 | [&name](const auto& pair) { return pair.first == name; }); | ||
| 100 | } | ||
| 101 | |||
| 102 | constexpr std::array<std::pair<std::string_view, KeyIndex<S256KeyType>>, 6> s256_file_id{{ | ||
| 103 | {"header_key", {S256KeyType::Header, 0, 0}}, | ||
| 104 | {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, | ||
| 105 | {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, | ||
| 106 | {"header_key_source", {S256KeyType::HeaderSource, 0, 0}}, | ||
| 107 | {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}}, | ||
| 108 | {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}}, | ||
| 109 | }}; | ||
| 110 | |||
| 111 | auto Find256ByName(std::string_view name) { | ||
| 112 | return std::find_if(s256_file_id.begin(), s256_file_id.end(), | ||
| 113 | [&name](const auto& pair) { return pair.first == name; }); | ||
| 114 | } | ||
| 115 | |||
| 116 | using KeyArray = std::array<std::pair<std::pair<S128KeyType, u64>, std::string_view>, 7>; | ||
| 117 | constexpr KeyArray KEYS_VARIABLE_LENGTH{{ | ||
| 51 | {{S128KeyType::Master, 0}, "master_key_"}, | 118 | {{S128KeyType::Master, 0}, "master_key_"}, |
| 52 | {{S128KeyType::Package1, 0}, "package1_key_"}, | 119 | {{S128KeyType::Package1, 0}, "package1_key_"}, |
| 53 | {{S128KeyType::Package2, 0}, "package2_key_"}, | 120 | {{S128KeyType::Package2, 0}, "package2_key_"}, |
| @@ -55,14 +122,13 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ | |||
| 55 | {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, | 122 | {{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"}, |
| 56 | {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, | 123 | {{S128KeyType::Keyblob, 0}, "keyblob_key_"}, |
| 57 | {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, | 124 | {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, |
| 58 | }; | 125 | }}; |
| 59 | 126 | ||
| 60 | namespace { | ||
| 61 | template <std::size_t Size> | 127 | template <std::size_t Size> |
| 62 | bool IsAllZeroArray(const std::array<u8, Size>& array) { | 128 | bool IsAllZeroArray(const std::array<u8, Size>& array) { |
| 63 | return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); | 129 | return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); |
| 64 | } | 130 | } |
| 65 | } // namespace | 131 | } // Anonymous namespace |
| 66 | 132 | ||
| 67 | u64 GetSignatureTypeDataSize(SignatureType type) { | 133 | u64 GetSignatureTypeDataSize(SignatureType type) { |
| 68 | switch (type) { | 134 | switch (type) { |
| @@ -94,13 +160,13 @@ u64 GetSignatureTypePaddingSize(SignatureType type) { | |||
| 94 | } | 160 | } |
| 95 | 161 | ||
| 96 | SignatureType Ticket::GetSignatureType() const { | 162 | SignatureType Ticket::GetSignatureType() const { |
| 97 | if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { | 163 | if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { |
| 98 | return ticket->sig_type; | 164 | return ticket->sig_type; |
| 99 | } | 165 | } |
| 100 | if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { | 166 | if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) { |
| 101 | return ticket->sig_type; | 167 | return ticket->sig_type; |
| 102 | } | 168 | } |
| 103 | if (auto ticket = std::get_if<ECDSATicket>(&data)) { | 169 | if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { |
| 104 | return ticket->sig_type; | 170 | return ticket->sig_type; |
| 105 | } | 171 | } |
| 106 | 172 | ||
| @@ -108,13 +174,13 @@ SignatureType Ticket::GetSignatureType() const { | |||
| 108 | } | 174 | } |
| 109 | 175 | ||
| 110 | TicketData& Ticket::GetData() { | 176 | TicketData& Ticket::GetData() { |
| 111 | if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { | 177 | if (auto* ticket = std::get_if<RSA4096Ticket>(&data)) { |
| 112 | return ticket->data; | 178 | return ticket->data; |
| 113 | } | 179 | } |
| 114 | if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { | 180 | if (auto* ticket = std::get_if<RSA2048Ticket>(&data)) { |
| 115 | return ticket->data; | 181 | return ticket->data; |
| 116 | } | 182 | } |
| 117 | if (auto ticket = std::get_if<ECDSATicket>(&data)) { | 183 | if (auto* ticket = std::get_if<ECDSATicket>(&data)) { |
| 118 | return ticket->data; | 184 | return ticket->data; |
| 119 | } | 185 | } |
| 120 | 186 | ||
| @@ -122,13 +188,13 @@ TicketData& Ticket::GetData() { | |||
| 122 | } | 188 | } |
| 123 | 189 | ||
| 124 | const TicketData& Ticket::GetData() const { | 190 | const TicketData& Ticket::GetData() const { |
| 125 | if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { | 191 | if (const auto* ticket = std::get_if<RSA4096Ticket>(&data)) { |
| 126 | return ticket->data; | 192 | return ticket->data; |
| 127 | } | 193 | } |
| 128 | if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { | 194 | if (const auto* ticket = std::get_if<RSA2048Ticket>(&data)) { |
| 129 | return ticket->data; | 195 | return ticket->data; |
| 130 | } | 196 | } |
| 131 | if (auto ticket = std::get_if<ECDSATicket>(&data)) { | 197 | if (const auto* ticket = std::get_if<ECDSATicket>(&data)) { |
| 132 | return ticket->data; | 198 | return ticket->data; |
| 133 | } | 199 | } |
| 134 | 200 | ||
| @@ -231,8 +297,9 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) { | |||
| 231 | } | 297 | } |
| 232 | 298 | ||
| 233 | RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { | 299 | RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { |
| 234 | if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) | 300 | if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) { |
| 235 | return {}; | 301 | return {}; |
| 302 | } | ||
| 236 | 303 | ||
| 237 | const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); | 304 | const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); |
| 238 | 305 | ||
| @@ -259,27 +326,30 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) | |||
| 259 | } | 326 | } |
| 260 | 327 | ||
| 261 | std::optional<Key128> DeriveSDSeed() { | 328 | std::optional<Key128> DeriveSDSeed() { |
| 262 | const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 329 | const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + |
| 263 | "/system/save/8000000000000043", | 330 | "/system/save/8000000000000043", |
| 264 | "rb+"); | 331 | "rb+"); |
| 265 | if (!save_43.IsOpen()) | 332 | if (!save_43.IsOpen()) { |
| 266 | return {}; | 333 | return std::nullopt; |
| 334 | } | ||
| 267 | 335 | ||
| 268 | const FileUtil::IOFile sd_private( | 336 | const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) + |
| 269 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+"); | 337 | "/Nintendo/Contents/private", |
| 270 | if (!sd_private.IsOpen()) | 338 | "rb+"); |
| 271 | return {}; | 339 | if (!sd_private.IsOpen()) { |
| 340 | return std::nullopt; | ||
| 341 | } | ||
| 272 | 342 | ||
| 273 | std::array<u8, 0x10> private_seed{}; | 343 | std::array<u8, 0x10> private_seed{}; |
| 274 | if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { | 344 | if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { |
| 275 | return {}; | 345 | return std::nullopt; |
| 276 | } | 346 | } |
| 277 | 347 | ||
| 278 | std::array<u8, 0x10> buffer{}; | 348 | std::array<u8, 0x10> buffer{}; |
| 279 | std::size_t offset = 0; | 349 | std::size_t offset = 0; |
| 280 | for (; offset + 0x10 < save_43.GetSize(); ++offset) { | 350 | for (; offset + 0x10 < save_43.GetSize(); ++offset) { |
| 281 | if (!save_43.Seek(offset, SEEK_SET)) { | 351 | if (!save_43.Seek(offset, SEEK_SET)) { |
| 282 | return {}; | 352 | return std::nullopt; |
| 283 | } | 353 | } |
| 284 | 354 | ||
| 285 | save_43.ReadBytes(buffer.data(), buffer.size()); | 355 | save_43.ReadBytes(buffer.data(), buffer.size()); |
| @@ -289,23 +359,26 @@ std::optional<Key128> DeriveSDSeed() { | |||
| 289 | } | 359 | } |
| 290 | 360 | ||
| 291 | if (!save_43.Seek(offset + 0x10, SEEK_SET)) { | 361 | if (!save_43.Seek(offset + 0x10, SEEK_SET)) { |
| 292 | return {}; | 362 | return std::nullopt; |
| 293 | } | 363 | } |
| 294 | 364 | ||
| 295 | Key128 seed{}; | 365 | Key128 seed{}; |
| 296 | if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { | 366 | if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { |
| 297 | return {}; | 367 | return std::nullopt; |
| 298 | } | 368 | } |
| 299 | return seed; | 369 | return seed; |
| 300 | } | 370 | } |
| 301 | 371 | ||
| 302 | Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) { | 372 | Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) { |
| 303 | if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) | 373 | if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek))) { |
| 304 | return Loader::ResultStatus::ErrorMissingSDKEKSource; | 374 | return Loader::ResultStatus::ErrorMissingSDKEKSource; |
| 305 | if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) | 375 | } |
| 376 | if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration))) { | ||
| 306 | return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; | 377 | return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource; |
| 307 | if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) | 378 | } |
| 379 | if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration))) { | ||
| 308 | return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; | 380 | return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource; |
| 381 | } | ||
| 309 | 382 | ||
| 310 | const auto sd_kek_source = | 383 | const auto sd_kek_source = |
| 311 | keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)); | 384 | keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)); |
| @@ -318,14 +391,17 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke | |||
| 318 | GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); | 391 | GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen); |
| 319 | keys.SetKey(S128KeyType::SDKek, sd_kek); | 392 | keys.SetKey(S128KeyType::SDKek, sd_kek); |
| 320 | 393 | ||
| 321 | if (!keys.HasKey(S128KeyType::SDSeed)) | 394 | if (!keys.HasKey(S128KeyType::SDSeed)) { |
| 322 | return Loader::ResultStatus::ErrorMissingSDSeed; | 395 | return Loader::ResultStatus::ErrorMissingSDSeed; |
| 396 | } | ||
| 323 | const auto sd_seed = keys.GetKey(S128KeyType::SDSeed); | 397 | const auto sd_seed = keys.GetKey(S128KeyType::SDSeed); |
| 324 | 398 | ||
| 325 | if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) | 399 | if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save))) { |
| 326 | return Loader::ResultStatus::ErrorMissingSDSaveKeySource; | 400 | return Loader::ResultStatus::ErrorMissingSDSaveKeySource; |
| 327 | if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) | 401 | } |
| 402 | if (!keys.HasKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA))) { | ||
| 328 | return Loader::ResultStatus::ErrorMissingSDNCAKeySource; | 403 | return Loader::ResultStatus::ErrorMissingSDNCAKeySource; |
| 404 | } | ||
| 329 | 405 | ||
| 330 | std::array<Key256, 2> sd_key_sources{ | 406 | std::array<Key256, 2> sd_key_sources{ |
| 331 | keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)), | 407 | keys.GetKey(S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save)), |
| @@ -334,8 +410,9 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke | |||
| 334 | 410 | ||
| 335 | // Combine sources and seed | 411 | // Combine sources and seed |
| 336 | for (auto& source : sd_key_sources) { | 412 | for (auto& source : sd_key_sources) { |
| 337 | for (std::size_t i = 0; i < source.size(); ++i) | 413 | for (std::size_t i = 0; i < source.size(); ++i) { |
| 338 | source[i] ^= sd_seed[i & 0xF]; | 414 | source[i] ^= sd_seed[i & 0xF]; |
| 415 | } | ||
| 339 | } | 416 | } |
| 340 | 417 | ||
| 341 | AESCipher<Key128> cipher(sd_kek, Mode::ECB); | 418 | AESCipher<Key128> cipher(sd_kek, Mode::ECB); |
| @@ -353,9 +430,10 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke | |||
| 353 | return Loader::ResultStatus::Success; | 430 | return Loader::ResultStatus::Success; |
| 354 | } | 431 | } |
| 355 | 432 | ||
| 356 | std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { | 433 | std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) { |
| 357 | if (!ticket_save.IsOpen()) | 434 | if (!ticket_save.IsOpen()) { |
| 358 | return {}; | 435 | return {}; |
| 436 | } | ||
| 359 | 437 | ||
| 360 | std::vector<u8> buffer(ticket_save.GetSize()); | 438 | std::vector<u8> buffer(ticket_save.GetSize()); |
| 361 | if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { | 439 | if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { |
| @@ -415,7 +493,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { | |||
| 415 | offset = i + 1; | 493 | offset = i + 1; |
| 416 | break; | 494 | break; |
| 417 | } else if (data[i] != 0x0) { | 495 | } else if (data[i] != 0x0) { |
| 418 | return {}; | 496 | return std::nullopt; |
| 419 | } | 497 | } |
| 420 | } | 498 | } |
| 421 | 499 | ||
| @@ -425,16 +503,18 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { | |||
| 425 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | 503 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, |
| 426 | const RSAKeyPair<2048>& key) { | 504 | const RSAKeyPair<2048>& key) { |
| 427 | const auto issuer = ticket.GetData().issuer; | 505 | const auto issuer = ticket.GetData().issuer; |
| 428 | if (IsAllZeroArray(issuer)) | 506 | if (IsAllZeroArray(issuer)) { |
| 429 | return {}; | 507 | return std::nullopt; |
| 508 | } | ||
| 430 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { | 509 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { |
| 431 | LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); | 510 | LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); |
| 432 | } | 511 | } |
| 433 | 512 | ||
| 434 | Key128 rights_id = ticket.GetData().rights_id; | 513 | Key128 rights_id = ticket.GetData().rights_id; |
| 435 | 514 | ||
| 436 | if (rights_id == Key128{}) | 515 | if (rights_id == Key128{}) { |
| 437 | return {}; | 516 | return std::nullopt; |
| 517 | } | ||
| 438 | 518 | ||
| 439 | if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), | 519 | if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), |
| 440 | ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { | 520 | ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { |
| @@ -466,15 +546,17 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | |||
| 466 | std::array<u8, 0xDF> m_2; | 546 | std::array<u8, 0xDF> m_2; |
| 467 | std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); | 547 | std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size()); |
| 468 | 548 | ||
| 469 | if (m_0 != 0) | 549 | if (m_0 != 0) { |
| 470 | return {}; | 550 | return std::nullopt; |
| 551 | } | ||
| 471 | 552 | ||
| 472 | m_1 = m_1 ^ MGF1<0x20>(m_2); | 553 | m_1 = m_1 ^ MGF1<0x20>(m_2); |
| 473 | m_2 = m_2 ^ MGF1<0xDF>(m_1); | 554 | m_2 = m_2 ^ MGF1<0xDF>(m_1); |
| 474 | 555 | ||
| 475 | const auto offset = FindTicketOffset(m_2); | 556 | const auto offset = FindTicketOffset(m_2); |
| 476 | if (!offset) | 557 | if (!offset) { |
| 477 | return {}; | 558 | return std::nullopt; |
| 559 | } | ||
| 478 | ASSERT(*offset > 0); | 560 | ASSERT(*offset > 0); |
| 479 | 561 | ||
| 480 | Key128 key_temp{}; | 562 | Key128 key_temp{}; |
| @@ -485,8 +567,8 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | |||
| 485 | 567 | ||
| 486 | KeyManager::KeyManager() { | 568 | KeyManager::KeyManager() { |
| 487 | // Initialize keys | 569 | // Initialize keys |
| 488 | const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); | 570 | const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath(); |
| 489 | const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); | 571 | const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir); |
| 490 | if (Settings::values.use_dev_keys) { | 572 | if (Settings::values.use_dev_keys) { |
| 491 | dev_mode = true; | 573 | dev_mode = true; |
| 492 | AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); | 574 | AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); |
| @@ -504,34 +586,39 @@ KeyManager::KeyManager() { | |||
| 504 | } | 586 | } |
| 505 | 587 | ||
| 506 | static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { | 588 | static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { |
| 507 | if (base.size() < begin + length) | 589 | if (base.size() < begin + length) { |
| 508 | return false; | 590 | return false; |
| 591 | } | ||
| 509 | return std::all_of(base.begin() + begin, base.begin() + begin + length, | 592 | return std::all_of(base.begin() + begin, base.begin() + begin + length, |
| 510 | [](u8 c) { return std::isxdigit(c); }); | 593 | [](u8 c) { return std::isxdigit(c); }); |
| 511 | } | 594 | } |
| 512 | 595 | ||
| 513 | void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | 596 | void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { |
| 514 | std::ifstream file; | 597 | std::ifstream file; |
| 515 | OpenFStream(file, filename, std::ios_base::in); | 598 | Common::FS::OpenFStream(file, filename, std::ios_base::in); |
| 516 | if (!file.is_open()) | 599 | if (!file.is_open()) { |
| 517 | return; | 600 | return; |
| 601 | } | ||
| 518 | 602 | ||
| 519 | std::string line; | 603 | std::string line; |
| 520 | while (std::getline(file, line)) { | 604 | while (std::getline(file, line)) { |
| 521 | std::vector<std::string> out; | 605 | std::vector<std::string> out; |
| 522 | std::stringstream stream(line); | 606 | std::stringstream stream(line); |
| 523 | std::string item; | 607 | std::string item; |
| 524 | while (std::getline(stream, item, '=')) | 608 | while (std::getline(stream, item, '=')) { |
| 525 | out.push_back(std::move(item)); | 609 | out.push_back(std::move(item)); |
| 610 | } | ||
| 526 | 611 | ||
| 527 | if (out.size() != 2) | 612 | if (out.size() != 2) { |
| 528 | continue; | 613 | continue; |
| 614 | } | ||
| 529 | 615 | ||
| 530 | out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); | 616 | out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end()); |
| 531 | out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); | 617 | out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end()); |
| 532 | 618 | ||
| 533 | if (out[0].compare(0, 1, "#") == 0) | 619 | if (out[0].compare(0, 1, "#") == 0) { |
| 534 | continue; | 620 | continue; |
| 621 | } | ||
| 535 | 622 | ||
| 536 | if (is_title_keys) { | 623 | if (is_title_keys) { |
| 537 | auto rights_id_raw = Common::HexStringToArray<16>(out[0]); | 624 | auto rights_id_raw = Common::HexStringToArray<16>(out[0]); |
| @@ -541,24 +628,26 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | |||
| 541 | s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; | 628 | s128_keys[{S128KeyType::Titlekey, rights_id[1], rights_id[0]}] = key; |
| 542 | } else { | 629 | } else { |
| 543 | out[0] = Common::ToLower(out[0]); | 630 | out[0] = Common::ToLower(out[0]); |
| 544 | if (s128_file_id.find(out[0]) != s128_file_id.end()) { | 631 | if (const auto iter128 = Find128ByName(out[0]); iter128 != s128_file_id.end()) { |
| 545 | const auto index = s128_file_id.at(out[0]); | 632 | const auto& index = iter128->second; |
| 546 | Key128 key = Common::HexStringToArray<16>(out[1]); | 633 | const Key128 key = Common::HexStringToArray<16>(out[1]); |
| 547 | s128_keys[{index.type, index.field1, index.field2}] = key; | 634 | s128_keys[{index.type, index.field1, index.field2}] = key; |
| 548 | } else if (s256_file_id.find(out[0]) != s256_file_id.end()) { | 635 | } else if (const auto iter256 = Find256ByName(out[0]); iter256 != s256_file_id.end()) { |
| 549 | const auto index = s256_file_id.at(out[0]); | 636 | const auto& index = iter256->second; |
| 550 | Key256 key = Common::HexStringToArray<32>(out[1]); | 637 | const Key256 key = Common::HexStringToArray<32>(out[1]); |
| 551 | s256_keys[{index.type, index.field1, index.field2}] = key; | 638 | s256_keys[{index.type, index.field1, index.field2}] = key; |
| 552 | } else if (out[0].compare(0, 8, "keyblob_") == 0 && | 639 | } else if (out[0].compare(0, 8, "keyblob_") == 0 && |
| 553 | out[0].compare(0, 9, "keyblob_k") != 0) { | 640 | out[0].compare(0, 9, "keyblob_k") != 0) { |
| 554 | if (!ValidCryptoRevisionString(out[0], 8, 2)) | 641 | if (!ValidCryptoRevisionString(out[0], 8, 2)) { |
| 555 | continue; | 642 | continue; |
| 643 | } | ||
| 556 | 644 | ||
| 557 | const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16); | 645 | const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16); |
| 558 | keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); | 646 | keyblobs[index] = Common::HexStringToArray<0x90>(out[1]); |
| 559 | } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { | 647 | } else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) { |
| 560 | if (!ValidCryptoRevisionString(out[0], 18, 2)) | 648 | if (!ValidCryptoRevisionString(out[0], 18, 2)) { |
| 561 | continue; | 649 | continue; |
| 650 | } | ||
| 562 | 651 | ||
| 563 | const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); | 652 | const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); |
| 564 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); | 653 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); |
| @@ -566,8 +655,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | |||
| 566 | eticket_extended_kek = Common::HexStringToArray<576>(out[1]); | 655 | eticket_extended_kek = Common::HexStringToArray<576>(out[1]); |
| 567 | } else { | 656 | } else { |
| 568 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { | 657 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { |
| 569 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) | 658 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) { |
| 570 | continue; | 659 | continue; |
| 660 | } | ||
| 571 | if (out[0].compare(0, kv.second.size(), kv.second) == 0) { | 661 | if (out[0].compare(0, kv.second.size(), kv.second) == 0) { |
| 572 | const auto index = | 662 | const auto index = |
| 573 | std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); | 663 | std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16); |
| @@ -602,10 +692,11 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | |||
| 602 | 692 | ||
| 603 | void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, | 693 | void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, |
| 604 | const std::string& filename, bool title) { | 694 | const std::string& filename, bool title) { |
| 605 | if (FileUtil::Exists(dir1 + DIR_SEP + filename)) | 695 | if (Common::FS::Exists(dir1 + DIR_SEP + filename)) { |
| 606 | LoadFromFile(dir1 + DIR_SEP + filename, title); | 696 | LoadFromFile(dir1 + DIR_SEP + filename, title); |
| 607 | else if (FileUtil::Exists(dir2 + DIR_SEP + filename)) | 697 | } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) { |
| 608 | LoadFromFile(dir2 + DIR_SEP + filename, title); | 698 | LoadFromFile(dir2 + DIR_SEP + filename, title); |
| 699 | } | ||
| 609 | } | 700 | } |
| 610 | 701 | ||
| 611 | bool KeyManager::BaseDeriveNecessary() const { | 702 | bool KeyManager::BaseDeriveNecessary() const { |
| @@ -613,8 +704,9 @@ bool KeyManager::BaseDeriveNecessary() const { | |||
| 613 | return !HasKey(key_type, index1, index2); | 704 | return !HasKey(key_type, index1, index2); |
| 614 | }; | 705 | }; |
| 615 | 706 | ||
| 616 | if (check_key_existence(S256KeyType::Header)) | 707 | if (check_key_existence(S256KeyType::Header)) { |
| 617 | return true; | 708 | return true; |
| 709 | } | ||
| 618 | 710 | ||
| 619 | for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) { | 711 | for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) { |
| 620 | if (check_key_existence(S128KeyType::Master, i) || | 712 | if (check_key_existence(S128KeyType::Master, i) || |
| @@ -639,14 +731,16 @@ bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const { | |||
| 639 | } | 731 | } |
| 640 | 732 | ||
| 641 | Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { | 733 | Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { |
| 642 | if (!HasKey(id, field1, field2)) | 734 | if (!HasKey(id, field1, field2)) { |
| 643 | return {}; | 735 | return {}; |
| 736 | } | ||
| 644 | return s128_keys.at({id, field1, field2}); | 737 | return s128_keys.at({id, field1, field2}); |
| 645 | } | 738 | } |
| 646 | 739 | ||
| 647 | Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { | 740 | Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { |
| 648 | if (!HasKey(id, field1, field2)) | 741 | if (!HasKey(id, field1, field2)) { |
| 649 | return {}; | 742 | return {}; |
| 743 | } | ||
| 650 | return s256_keys.at({id, field1, field2}); | 744 | return s256_keys.at({id, field1, field2}); |
| 651 | } | 745 | } |
| 652 | 746 | ||
| @@ -668,7 +762,7 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const { | |||
| 668 | template <size_t Size> | 762 | template <size_t Size> |
| 669 | void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, | 763 | void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, |
| 670 | const std::array<u8, Size>& key) { | 764 | const std::array<u8, Size>& key) { |
| 671 | const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); | 765 | const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir); |
| 672 | std::string filename = "title.keys_autogenerated"; | 766 | std::string filename = "title.keys_autogenerated"; |
| 673 | if (category == KeyCategory::Standard) { | 767 | if (category == KeyCategory::Standard) { |
| 674 | filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; | 768 | filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; |
| @@ -677,9 +771,9 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, | |||
| 677 | } | 771 | } |
| 678 | 772 | ||
| 679 | const auto path = yuzu_keys_dir + DIR_SEP + filename; | 773 | const auto path = yuzu_keys_dir + DIR_SEP + filename; |
| 680 | const auto add_info_text = !FileUtil::Exists(path); | 774 | const auto add_info_text = !Common::FS::Exists(path); |
| 681 | FileUtil::CreateFullPath(path); | 775 | Common::FS::CreateFullPath(path); |
| 682 | FileUtil::IOFile file{path, "a"}; | 776 | Common::FS::IOFile file{path, "a"}; |
| 683 | if (!file.IsOpen()) { | 777 | if (!file.IsOpen()) { |
| 684 | return; | 778 | return; |
| 685 | } | 779 | } |
| @@ -712,8 +806,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | |||
| 712 | } | 806 | } |
| 713 | 807 | ||
| 714 | const auto iter2 = std::find_if( | 808 | const auto iter2 = std::find_if( |
| 715 | s128_file_id.begin(), s128_file_id.end(), | 809 | s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const auto& elem) { |
| 716 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) { | ||
| 717 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == | 810 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == |
| 718 | std::tie(id, field1, field2); | 811 | std::tie(id, field1, field2); |
| 719 | }); | 812 | }); |
| @@ -723,9 +816,11 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | |||
| 723 | 816 | ||
| 724 | // Variable cases | 817 | // Variable cases |
| 725 | if (id == S128KeyType::KeyArea) { | 818 | if (id == S128KeyType::KeyArea) { |
| 726 | static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}", | 819 | static constexpr std::array<const char*, 3> kak_names = { |
| 727 | "key_area_key_ocean_{:02X}", | 820 | "key_area_key_application_{:02X}", |
| 728 | "key_area_key_system_{:02X}"}; | 821 | "key_area_key_ocean_{:02X}", |
| 822 | "key_area_key_system_{:02X}", | ||
| 823 | }; | ||
| 729 | WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); | 824 | WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key); |
| 730 | } else if (id == S128KeyType::Master) { | 825 | } else if (id == S128KeyType::Master) { |
| 731 | WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); | 826 | WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key); |
| @@ -751,8 +846,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { | |||
| 751 | return; | 846 | return; |
| 752 | } | 847 | } |
| 753 | const auto iter = std::find_if( | 848 | const auto iter = std::find_if( |
| 754 | s256_file_id.begin(), s256_file_id.end(), | 849 | s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const auto& elem) { |
| 755 | [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) { | ||
| 756 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == | 850 | return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == |
| 757 | std::tie(id, field1, field2); | 851 | std::tie(id, field1, field2); |
| 758 | }); | 852 | }); |
| @@ -763,29 +857,31 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { | |||
| 763 | } | 857 | } |
| 764 | 858 | ||
| 765 | bool KeyManager::KeyFileExists(bool title) { | 859 | bool KeyManager::KeyFileExists(bool title) { |
| 766 | const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); | 860 | const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath(); |
| 767 | const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); | 861 | const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir); |
| 768 | if (title) { | 862 | if (title) { |
| 769 | return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || | 863 | return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || |
| 770 | FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); | 864 | Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); |
| 771 | } | 865 | } |
| 772 | 866 | ||
| 773 | if (Settings::values.use_dev_keys) { | 867 | if (Settings::values.use_dev_keys) { |
| 774 | return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || | 868 | return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || |
| 775 | FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); | 869 | Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); |
| 776 | } | 870 | } |
| 777 | 871 | ||
| 778 | return FileUtil::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || | 872 | return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || |
| 779 | FileUtil::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); | 873 | Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); |
| 780 | } | 874 | } |
| 781 | 875 | ||
| 782 | void KeyManager::DeriveSDSeedLazy() { | 876 | void KeyManager::DeriveSDSeedLazy() { |
| 783 | if (HasKey(S128KeyType::SDSeed)) | 877 | if (HasKey(S128KeyType::SDSeed)) { |
| 784 | return; | 878 | return; |
| 879 | } | ||
| 785 | 880 | ||
| 786 | const auto res = DeriveSDSeed(); | 881 | const auto res = DeriveSDSeed(); |
| 787 | if (res) | 882 | if (res) { |
| 788 | SetKey(S128KeyType::SDSeed, *res); | 883 | SetKey(S128KeyType::SDSeed, *res); |
| 884 | } | ||
| 789 | } | 885 | } |
| 790 | 886 | ||
| 791 | static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { | 887 | static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { |
| @@ -797,11 +893,13 @@ static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) { | |||
| 797 | } | 893 | } |
| 798 | 894 | ||
| 799 | void KeyManager::DeriveBase() { | 895 | void KeyManager::DeriveBase() { |
| 800 | if (!BaseDeriveNecessary()) | 896 | if (!BaseDeriveNecessary()) { |
| 801 | return; | 897 | return; |
| 898 | } | ||
| 802 | 899 | ||
| 803 | if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) | 900 | if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC)) { |
| 804 | return; | 901 | return; |
| 902 | } | ||
| 805 | 903 | ||
| 806 | const auto has_bis = [this](u64 id) { | 904 | const auto has_bis = [this](u64 id) { |
| 807 | return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) && | 905 | return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) && |
| @@ -818,10 +916,11 @@ void KeyManager::DeriveBase() { | |||
| 818 | static_cast<u64>(BISKeyType::Tweak)); | 916 | static_cast<u64>(BISKeyType::Tweak)); |
| 819 | }; | 917 | }; |
| 820 | 918 | ||
| 821 | if (has_bis(2) && !has_bis(3)) | 919 | if (has_bis(2) && !has_bis(3)) { |
| 822 | copy_bis(2, 3); | 920 | copy_bis(2, 3); |
| 823 | else if (has_bis(3) && !has_bis(2)) | 921 | } else if (has_bis(3) && !has_bis(2)) { |
| 824 | copy_bis(3, 2); | 922 | copy_bis(3, 2); |
| 923 | } | ||
| 825 | 924 | ||
| 826 | std::bitset<32> revisions(0xFFFFFFFF); | 925 | std::bitset<32> revisions(0xFFFFFFFF); |
| 827 | for (size_t i = 0; i < revisions.size(); ++i) { | 926 | for (size_t i = 0; i < revisions.size(); ++i) { |
| @@ -831,15 +930,17 @@ void KeyManager::DeriveBase() { | |||
| 831 | } | 930 | } |
| 832 | } | 931 | } |
| 833 | 932 | ||
| 834 | if (!revisions.any()) | 933 | if (!revisions.any()) { |
| 835 | return; | 934 | return; |
| 935 | } | ||
| 836 | 936 | ||
| 837 | const auto sbk = GetKey(S128KeyType::SecureBoot); | 937 | const auto sbk = GetKey(S128KeyType::SecureBoot); |
| 838 | const auto tsec = GetKey(S128KeyType::TSEC); | 938 | const auto tsec = GetKey(S128KeyType::TSEC); |
| 839 | 939 | ||
| 840 | for (size_t i = 0; i < revisions.size(); ++i) { | 940 | for (size_t i = 0; i < revisions.size(); ++i) { |
| 841 | if (!revisions[i]) | 941 | if (!revisions[i]) { |
| 842 | continue; | 942 | continue; |
| 943 | } | ||
| 843 | 944 | ||
| 844 | // Derive keyblob key | 945 | // Derive keyblob key |
| 845 | const auto key = DeriveKeyblobKey( | 946 | const auto key = DeriveKeyblobKey( |
| @@ -848,16 +949,18 @@ void KeyManager::DeriveBase() { | |||
| 848 | SetKey(S128KeyType::Keyblob, key, i); | 949 | SetKey(S128KeyType::Keyblob, key, i); |
| 849 | 950 | ||
| 850 | // Derive keyblob MAC key | 951 | // Derive keyblob MAC key |
| 851 | if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) | 952 | if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))) { |
| 852 | continue; | 953 | continue; |
| 954 | } | ||
| 853 | 955 | ||
| 854 | const auto mac_key = DeriveKeyblobMACKey( | 956 | const auto mac_key = DeriveKeyblobMACKey( |
| 855 | key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))); | 957 | key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC))); |
| 856 | SetKey(S128KeyType::KeyblobMAC, mac_key, i); | 958 | SetKey(S128KeyType::KeyblobMAC, mac_key, i); |
| 857 | 959 | ||
| 858 | Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key); | 960 | Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key); |
| 859 | if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) | 961 | if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0) { |
| 860 | continue; | 962 | continue; |
| 963 | } | ||
| 861 | 964 | ||
| 862 | // Decrypt keyblob | 965 | // Decrypt keyblob |
| 863 | if (keyblobs[i] == std::array<u8, 0x90>{}) { | 966 | if (keyblobs[i] == std::array<u8, 0x90>{}) { |
| @@ -881,16 +984,19 @@ void KeyManager::DeriveBase() { | |||
| 881 | 984 | ||
| 882 | revisions.set(); | 985 | revisions.set(); |
| 883 | for (size_t i = 0; i < revisions.size(); ++i) { | 986 | for (size_t i = 0; i < revisions.size(); ++i) { |
| 884 | if (!HasKey(S128KeyType::Master, i)) | 987 | if (!HasKey(S128KeyType::Master, i)) { |
| 885 | revisions.reset(i); | 988 | revisions.reset(i); |
| 989 | } | ||
| 886 | } | 990 | } |
| 887 | 991 | ||
| 888 | if (!revisions.any()) | 992 | if (!revisions.any()) { |
| 889 | return; | 993 | return; |
| 994 | } | ||
| 890 | 995 | ||
| 891 | for (size_t i = 0; i < revisions.size(); ++i) { | 996 | for (size_t i = 0; i < revisions.size(); ++i) { |
| 892 | if (!revisions[i]) | 997 | if (!revisions[i]) { |
| 893 | continue; | 998 | continue; |
| 999 | } | ||
| 894 | 1000 | ||
| 895 | // Derive general purpose keys | 1001 | // Derive general purpose keys |
| 896 | DeriveGeneralPurposeKeys(i); | 1002 | DeriveGeneralPurposeKeys(i); |
| @@ -915,21 +1021,24 @@ void KeyManager::DeriveBase() { | |||
| 915 | } | 1021 | } |
| 916 | } | 1022 | } |
| 917 | 1023 | ||
| 918 | void KeyManager::DeriveETicket(PartitionDataManager& data) { | 1024 | void KeyManager::DeriveETicket(PartitionDataManager& data, |
| 1025 | const FileSys::ContentProvider& provider) { | ||
| 919 | // ETicket keys | 1026 | // ETicket keys |
| 920 | const auto es = Core::System::GetInstance().GetContentProvider().GetEntry( | 1027 | const auto es = provider.GetEntry(0x0100000000000033, FileSys::ContentRecordType::Program); |
| 921 | 0x0100000000000033, FileSys::ContentRecordType::Program); | ||
| 922 | 1028 | ||
| 923 | if (es == nullptr) | 1029 | if (es == nullptr) { |
| 924 | return; | 1030 | return; |
| 1031 | } | ||
| 925 | 1032 | ||
| 926 | const auto exefs = es->GetExeFS(); | 1033 | const auto exefs = es->GetExeFS(); |
| 927 | if (exefs == nullptr) | 1034 | if (exefs == nullptr) { |
| 928 | return; | 1035 | return; |
| 1036 | } | ||
| 929 | 1037 | ||
| 930 | const auto main = exefs->GetFile("main"); | 1038 | const auto main = exefs->GetFile("main"); |
| 931 | if (main == nullptr) | 1039 | if (main == nullptr) { |
| 932 | return; | 1040 | return; |
| 1041 | } | ||
| 933 | 1042 | ||
| 934 | const auto bytes = main->ReadAllBytes(); | 1043 | const auto bytes = main->ReadAllBytes(); |
| 935 | 1044 | ||
| @@ -939,16 +1048,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { | |||
| 939 | const auto seed3 = data.GetRSAKekSeed3(); | 1048 | const auto seed3 = data.GetRSAKekSeed3(); |
| 940 | const auto mask0 = data.GetRSAKekMask0(); | 1049 | const auto mask0 = data.GetRSAKekMask0(); |
| 941 | 1050 | ||
| 942 | if (eticket_kek != Key128{}) | 1051 | if (eticket_kek != Key128{}) { |
| 943 | SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek)); | 1052 | SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek)); |
| 1053 | } | ||
| 944 | if (eticket_kekek != Key128{}) { | 1054 | if (eticket_kekek != Key128{}) { |
| 945 | SetKey(S128KeyType::Source, eticket_kekek, | 1055 | SetKey(S128KeyType::Source, eticket_kekek, |
| 946 | static_cast<size_t>(SourceKeyType::ETicketKekek)); | 1056 | static_cast<size_t>(SourceKeyType::ETicketKekek)); |
| 947 | } | 1057 | } |
| 948 | if (seed3 != Key128{}) | 1058 | if (seed3 != Key128{}) { |
| 949 | SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3)); | 1059 | SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3)); |
| 950 | if (mask0 != Key128{}) | 1060 | } |
| 1061 | if (mask0 != Key128{}) { | ||
| 951 | SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0)); | 1062 | SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0)); |
| 1063 | } | ||
| 952 | if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} || | 1064 | if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} || |
| 953 | mask0 == Key128{}) { | 1065 | mask0 == Key128{}) { |
| 954 | return; | 1066 | return; |
| @@ -974,8 +1086,9 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { | |||
| 974 | AESCipher<Key128> es_kek(temp_kekek, Mode::ECB); | 1086 | AESCipher<Key128> es_kek(temp_kekek, Mode::ECB); |
| 975 | es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt); | 1087 | es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt); |
| 976 | 1088 | ||
| 977 | if (eticket_final == Key128{}) | 1089 | if (eticket_final == Key128{}) { |
| 978 | return; | 1090 | return; |
| 1091 | } | ||
| 979 | 1092 | ||
| 980 | SetKey(S128KeyType::ETicketRSAKek, eticket_final); | 1093 | SetKey(S128KeyType::ETicketRSAKek, eticket_final); |
| 981 | 1094 | ||
| @@ -990,18 +1103,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { | |||
| 990 | void KeyManager::PopulateTickets() { | 1103 | void KeyManager::PopulateTickets() { |
| 991 | const auto rsa_key = GetETicketRSAKey(); | 1104 | const auto rsa_key = GetETicketRSAKey(); |
| 992 | 1105 | ||
| 993 | if (rsa_key == RSAKeyPair<2048>{}) | 1106 | if (rsa_key == RSAKeyPair<2048>{}) { |
| 994 | return; | 1107 | return; |
| 1108 | } | ||
| 995 | 1109 | ||
| 996 | if (!common_tickets.empty() && !personal_tickets.empty()) | 1110 | if (!common_tickets.empty() && !personal_tickets.empty()) { |
| 997 | return; | 1111 | return; |
| 1112 | } | ||
| 998 | 1113 | ||
| 999 | const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 1114 | const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + |
| 1000 | "/system/save/80000000000000e1", | 1115 | "/system/save/80000000000000e1", |
| 1001 | "rb+"); | 1116 | "rb+"); |
| 1002 | const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 1117 | const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + |
| 1003 | "/system/save/80000000000000e2", | 1118 | "/system/save/80000000000000e2", |
| 1004 | "rb+"); | 1119 | "rb+"); |
| 1005 | 1120 | ||
| 1006 | const auto blob2 = GetTicketblob(save2); | 1121 | const auto blob2 = GetTicketblob(save2); |
| 1007 | auto res = GetTicketblob(save1); | 1122 | auto res = GetTicketblob(save1); |
| @@ -1011,8 +1126,10 @@ void KeyManager::PopulateTickets() { | |||
| 1011 | for (std::size_t i = 0; i < res.size(); ++i) { | 1126 | for (std::size_t i = 0; i < res.size(); ++i) { |
| 1012 | const auto common = i < idx; | 1127 | const auto common = i < idx; |
| 1013 | const auto pair = ParseTicket(res[i], rsa_key); | 1128 | const auto pair = ParseTicket(res[i], rsa_key); |
| 1014 | if (!pair) | 1129 | if (!pair) { |
| 1015 | continue; | 1130 | continue; |
| 1131 | } | ||
| 1132 | |||
| 1016 | const auto& [rid, key] = *pair; | 1133 | const auto& [rid, key] = *pair; |
| 1017 | u128 rights_id; | 1134 | u128 rights_id; |
| 1018 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | 1135 | std::memcpy(rights_id.data(), rid.data(), rid.size()); |
| @@ -1041,27 +1158,33 @@ void KeyManager::SynthesizeTickets() { | |||
| 1041 | } | 1158 | } |
| 1042 | 1159 | ||
| 1043 | void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { | 1160 | void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { |
| 1044 | if (key == Key128{}) | 1161 | if (key == Key128{}) { |
| 1045 | return; | 1162 | return; |
| 1163 | } | ||
| 1046 | SetKey(id, key, field1, field2); | 1164 | SetKey(id, key, field1, field2); |
| 1047 | } | 1165 | } |
| 1048 | 1166 | ||
| 1049 | void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) { | 1167 | void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) { |
| 1050 | if (key == Key256{}) | 1168 | if (key == Key256{}) { |
| 1051 | return; | 1169 | return; |
| 1170 | } | ||
| 1171 | |||
| 1052 | SetKey(id, key, field1, field2); | 1172 | SetKey(id, key, field1, field2); |
| 1053 | } | 1173 | } |
| 1054 | 1174 | ||
| 1055 | void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { | 1175 | void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { |
| 1056 | if (!BaseDeriveNecessary()) | 1176 | if (!BaseDeriveNecessary()) { |
| 1057 | return; | 1177 | return; |
| 1178 | } | ||
| 1058 | 1179 | ||
| 1059 | if (!data.HasBoot0()) | 1180 | if (!data.HasBoot0()) { |
| 1060 | return; | 1181 | return; |
| 1182 | } | ||
| 1061 | 1183 | ||
| 1062 | for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) { | 1184 | for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) { |
| 1063 | if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) | 1185 | if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{}) { |
| 1064 | continue; | 1186 | continue; |
| 1187 | } | ||
| 1065 | encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i); | 1188 | encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i); |
| 1066 | WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i), | 1189 | WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i), |
| 1067 | encrypted_keyblobs[i]); | 1190 | encrypted_keyblobs[i]); |
| @@ -1083,8 +1206,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { | |||
| 1083 | static_cast<u64>(SourceKeyType::Keyblob), i); | 1206 | static_cast<u64>(SourceKeyType::Keyblob), i); |
| 1084 | } | 1207 | } |
| 1085 | 1208 | ||
| 1086 | if (data.HasFuses()) | 1209 | if (data.HasFuses()) { |
| 1087 | SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey()); | 1210 | SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey()); |
| 1211 | } | ||
| 1088 | 1212 | ||
| 1089 | DeriveBase(); | 1213 | DeriveBase(); |
| 1090 | 1214 | ||
| @@ -1098,8 +1222,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { | |||
| 1098 | 1222 | ||
| 1099 | const auto masters = data.GetTZMasterKeys(latest_master); | 1223 | const auto masters = data.GetTZMasterKeys(latest_master); |
| 1100 | for (size_t i = 0; i < masters.size(); ++i) { | 1224 | for (size_t i = 0; i < masters.size(); ++i) { |
| 1101 | if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) | 1225 | if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i)) { |
| 1102 | SetKey(S128KeyType::Master, masters[i], i); | 1226 | SetKey(S128KeyType::Master, masters[i], i); |
| 1227 | } | ||
| 1103 | } | 1228 | } |
| 1104 | 1229 | ||
| 1105 | DeriveBase(); | 1230 | DeriveBase(); |
| @@ -1109,8 +1234,9 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { | |||
| 1109 | 1234 | ||
| 1110 | std::array<Key128, 0x20> package2_keys{}; | 1235 | std::array<Key128, 0x20> package2_keys{}; |
| 1111 | for (size_t i = 0; i < package2_keys.size(); ++i) { | 1236 | for (size_t i = 0; i < package2_keys.size(); ++i) { |
| 1112 | if (HasKey(S128KeyType::Package2, i)) | 1237 | if (HasKey(S128KeyType::Package2, i)) { |
| 1113 | package2_keys[i] = GetKey(S128KeyType::Package2, i); | 1238 | package2_keys[i] = GetKey(S128KeyType::Package2, i); |
| 1239 | } | ||
| 1114 | } | 1240 | } |
| 1115 | data.DecryptPackage2(package2_keys, Package2Type::NormalMain); | 1241 | data.DecryptPackage2(package2_keys, Package2Type::NormalMain); |
| 1116 | 1242 | ||
| @@ -1148,12 +1274,15 @@ const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const { | |||
| 1148 | 1274 | ||
| 1149 | bool KeyManager::AddTicketCommon(Ticket raw) { | 1275 | bool KeyManager::AddTicketCommon(Ticket raw) { |
| 1150 | const auto rsa_key = GetETicketRSAKey(); | 1276 | const auto rsa_key = GetETicketRSAKey(); |
| 1151 | if (rsa_key == RSAKeyPair<2048>{}) | 1277 | if (rsa_key == RSAKeyPair<2048>{}) { |
| 1152 | return false; | 1278 | return false; |
| 1279 | } | ||
| 1153 | 1280 | ||
| 1154 | const auto pair = ParseTicket(raw, rsa_key); | 1281 | const auto pair = ParseTicket(raw, rsa_key); |
| 1155 | if (!pair) | 1282 | if (!pair) { |
| 1156 | return false; | 1283 | return false; |
| 1284 | } | ||
| 1285 | |||
| 1157 | const auto& [rid, key] = *pair; | 1286 | const auto& [rid, key] = *pair; |
| 1158 | u128 rights_id; | 1287 | u128 rights_id; |
| 1159 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | 1288 | std::memcpy(rights_id.data(), rid.data(), rid.size()); |
| @@ -1164,12 +1293,15 @@ bool KeyManager::AddTicketCommon(Ticket raw) { | |||
| 1164 | 1293 | ||
| 1165 | bool KeyManager::AddTicketPersonalized(Ticket raw) { | 1294 | bool KeyManager::AddTicketPersonalized(Ticket raw) { |
| 1166 | const auto rsa_key = GetETicketRSAKey(); | 1295 | const auto rsa_key = GetETicketRSAKey(); |
| 1167 | if (rsa_key == RSAKeyPair<2048>{}) | 1296 | if (rsa_key == RSAKeyPair<2048>{}) { |
| 1168 | return false; | 1297 | return false; |
| 1298 | } | ||
| 1169 | 1299 | ||
| 1170 | const auto pair = ParseTicket(raw, rsa_key); | 1300 | const auto pair = ParseTicket(raw, rsa_key); |
| 1171 | if (!pair) | 1301 | if (!pair) { |
| 1172 | return false; | 1302 | return false; |
| 1303 | } | ||
| 1304 | |||
| 1173 | const auto& [rid, key] = *pair; | 1305 | const auto& [rid, key] = *pair; |
| 1174 | u128 rights_id; | 1306 | u128 rights_id; |
| 1175 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | 1307 | std::memcpy(rights_id.data(), rid.data(), rid.size()); |
| @@ -1177,58 +1309,4 @@ bool KeyManager::AddTicketPersonalized(Ticket raw) { | |||
| 1177 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | 1309 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); |
| 1178 | return true; | 1310 | return true; |
| 1179 | } | 1311 | } |
| 1180 | |||
| 1181 | const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { | ||
| 1182 | {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, | ||
| 1183 | {"eticket_rsa_kek_source", | ||
| 1184 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}}, | ||
| 1185 | {"eticket_rsa_kekek_source", | ||
| 1186 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}}, | ||
| 1187 | {"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}}, | ||
| 1188 | {"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}}, | ||
| 1189 | {"rsa_oaep_kek_generation_source", | ||
| 1190 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}}, | ||
| 1191 | {"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}}, | ||
| 1192 | {"aes_kek_generation_source", | ||
| 1193 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}}, | ||
| 1194 | {"aes_key_generation_source", | ||
| 1195 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}}, | ||
| 1196 | {"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}}, | ||
| 1197 | {"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}}, | ||
| 1198 | {"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}}, | ||
| 1199 | {"key_area_key_application_source", | ||
| 1200 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), | ||
| 1201 | static_cast<u64>(KeyAreaKeyType::Application)}}, | ||
| 1202 | {"key_area_key_ocean_source", | ||
| 1203 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), | ||
| 1204 | static_cast<u64>(KeyAreaKeyType::Ocean)}}, | ||
| 1205 | {"key_area_key_system_source", | ||
| 1206 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), | ||
| 1207 | static_cast<u64>(KeyAreaKeyType::System)}}, | ||
| 1208 | {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, | ||
| 1209 | {"keyblob_mac_key_source", | ||
| 1210 | {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}}, | ||
| 1211 | {"tsec_key", {S128KeyType::TSEC, 0, 0}}, | ||
| 1212 | {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}}, | ||
| 1213 | {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, | ||
| 1214 | {"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 1215 | {"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 1216 | {"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 1217 | {"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 1218 | {"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 1219 | {"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 1220 | {"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}}, | ||
| 1221 | {"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}}, | ||
| 1222 | {"header_kek", {S128KeyType::HeaderKek, 0, 0}}, | ||
| 1223 | {"sd_card_kek", {S128KeyType::SDKek, 0, 0}}, | ||
| 1224 | }; | ||
| 1225 | |||
| 1226 | const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { | ||
| 1227 | {"header_key", {S256KeyType::Header, 0, 0}}, | ||
| 1228 | {"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}}, | ||
| 1229 | {"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}}, | ||
| 1230 | {"header_key_source", {S256KeyType::HeaderSource, 0, 0}}, | ||
| 1231 | {"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}}, | ||
| 1232 | {"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}}, | ||
| 1233 | }; | ||
| 1234 | } // namespace Core::Crypto | 1312 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 9269a73f2..0a7220286 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -10,17 +10,20 @@ | |||
| 10 | #include <string> | 10 | #include <string> |
| 11 | 11 | ||
| 12 | #include <variant> | 12 | #include <variant> |
| 13 | #include <boost/container/flat_map.hpp> | ||
| 14 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| 15 | #include "common/common_funcs.h" | 14 | #include "common/common_funcs.h" |
| 16 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 17 | #include "core/crypto/partition_data_manager.h" | 16 | #include "core/crypto/partition_data_manager.h" |
| 18 | #include "core/file_sys/vfs_types.h" | 17 | #include "core/file_sys/vfs_types.h" |
| 19 | 18 | ||
| 20 | namespace FileUtil { | 19 | namespace Common::FS { |
| 21 | class IOFile; | 20 | class IOFile; |
| 22 | } | 21 | } |
| 23 | 22 | ||
| 23 | namespace FileSys { | ||
| 24 | class ContentProvider; | ||
| 25 | } | ||
| 26 | |||
| 24 | namespace Loader { | 27 | namespace Loader { |
| 25 | enum class ResultStatus : u16; | 28 | enum class ResultStatus : u16; |
| 26 | } | 29 | } |
| @@ -253,7 +256,7 @@ public: | |||
| 253 | 256 | ||
| 254 | bool BaseDeriveNecessary() const; | 257 | bool BaseDeriveNecessary() const; |
| 255 | void DeriveBase(); | 258 | void DeriveBase(); |
| 256 | void DeriveETicket(PartitionDataManager& data); | 259 | void DeriveETicket(PartitionDataManager& data, const FileSys::ContentProvider& provider); |
| 257 | void PopulateTickets(); | 260 | void PopulateTickets(); |
| 258 | void SynthesizeTickets(); | 261 | void SynthesizeTickets(); |
| 259 | 262 | ||
| @@ -293,9 +296,6 @@ private: | |||
| 293 | 296 | ||
| 294 | void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); | 297 | void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); |
| 295 | void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); | 298 | void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); |
| 296 | |||
| 297 | static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; | ||
| 298 | static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id; | ||
| 299 | }; | 299 | }; |
| 300 | 300 | ||
| 301 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); | 301 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed); |
| @@ -308,7 +308,7 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo | |||
| 308 | std::optional<Key128> DeriveSDSeed(); | 308 | std::optional<Key128> DeriveSDSeed(); |
| 309 | Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); | 309 | Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); |
| 310 | 310 | ||
| 311 | std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save); | 311 | std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save); |
| 312 | 312 | ||
| 313 | // Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority | 313 | // Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority |
| 314 | // (offset 0x140-0x144 is zero) | 314 | // (offset 0x140-0x144 is zero) |
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp index 7ed71ac3a..5f1c86a09 100644 --- a/src/core/crypto/partition_data_manager.cpp +++ b/src/core/crypto/partition_data_manager.cpp | |||
| @@ -26,8 +26,9 @@ | |||
| 26 | #include "core/file_sys/vfs.h" | 26 | #include "core/file_sys/vfs.h" |
| 27 | #include "core/file_sys/vfs_offset.h" | 27 | #include "core/file_sys/vfs_offset.h" |
| 28 | #include "core/file_sys/vfs_vector.h" | 28 | #include "core/file_sys/vfs_vector.h" |
| 29 | #include "core/loader/loader.h" | ||
| 29 | 30 | ||
| 30 | using namespace Common; | 31 | using Common::AsArray; |
| 31 | 32 | ||
| 32 | namespace Core::Crypto { | 33 | namespace Core::Crypto { |
| 33 | 34 | ||
| @@ -47,105 +48,123 @@ struct Package2Header { | |||
| 47 | }; | 48 | }; |
| 48 | static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); | 49 | static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); |
| 49 | 50 | ||
| 50 | const std::array<SHA256Hash, 0x10> source_hashes{ | 51 | // clang-format off |
| 51 | "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source | 52 | constexpr std::array source_hashes{ |
| 52 | "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source | 53 | AsArray("B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"), // keyblob_mac_key_source |
| 53 | "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source | 54 | AsArray("7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"), // master_key_source |
| 54 | "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source | 55 | AsArray("21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"), // package2_key_source |
| 55 | "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source | 56 | AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // aes_kek_generation_source |
| 56 | "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source | 57 | AsArray("FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"), // aes_key_generation_source |
| 57 | "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source | 58 | AsArray("C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"), // titlekek_source |
| 58 | "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source | 59 | AsArray("04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"), // key_area_key_application_source |
| 59 | "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source | 60 | AsArray("FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"), // key_area_key_ocean_source |
| 60 | "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source | 61 | AsArray("1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"), // key_area_key_system_source |
| 61 | "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source | 62 | AsArray("6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"), // sd_card_kek_source |
| 62 | "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source | 63 | AsArray("D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"), // sd_card_save_key_source |
| 63 | "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source | 64 | AsArray("2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"), // sd_card_nca_key_source |
| 64 | "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source | 65 | AsArray("1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"), // header_kek_source |
| 65 | "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3 | 66 | AsArray("8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"), // header_key_source |
| 66 | "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0 | 67 | AsArray("D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"), // rsa_kek_seed3 |
| 68 | AsArray("FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"), // rsa_kek_mask0 | ||
| 67 | }; | 69 | }; |
| 68 | 70 | // clang-format on | |
| 69 | const std::array<SHA256Hash, 0x20> keyblob_source_hashes{ | 71 | |
| 70 | "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00 | 72 | // clang-format off |
| 71 | "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01 | 73 | constexpr std::array keyblob_source_hashes{ |
| 72 | "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02 | 74 | AsArray("8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"), // keyblob_key_source_00 |
| 73 | "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03 | 75 | AsArray("2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"), // keyblob_key_source_01 |
| 74 | "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04 | 76 | AsArray("61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"), // keyblob_key_source_02 |
| 75 | "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05 | 77 | AsArray("8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"), // keyblob_key_source_03 |
| 76 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06 | 78 | AsArray("95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"), // keyblob_key_source_04 |
| 77 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07 | 79 | AsArray("3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"), // keyblob_key_source_05 |
| 78 | 80 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_06 | |
| 79 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08 | 81 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_07 |
| 80 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09 | 82 | |
| 81 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A | 83 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_08 |
| 82 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B | 84 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_09 |
| 83 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C | 85 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0A |
| 84 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D | 86 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0B |
| 85 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E | 87 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0C |
| 86 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F | 88 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0D |
| 87 | 89 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0E | |
| 88 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10 | 90 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_0F |
| 89 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11 | 91 | |
| 90 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12 | 92 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_10 |
| 91 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13 | 93 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_11 |
| 92 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14 | 94 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_12 |
| 93 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15 | 95 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_13 |
| 94 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16 | 96 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_14 |
| 95 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17 | 97 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_15 |
| 96 | 98 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_16 | |
| 97 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18 | 99 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_17 |
| 98 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19 | 100 | |
| 99 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A | 101 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_18 |
| 100 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B | 102 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_19 |
| 101 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C | 103 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1A |
| 102 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D | 104 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1B |
| 103 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E | 105 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1C |
| 104 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F | 106 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1D |
| 107 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1E | ||
| 108 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // keyblob_key_source_1F | ||
| 105 | }; | 109 | }; |
| 106 | 110 | // clang-format on | |
| 107 | const std::array<SHA256Hash, 0x20> master_key_hashes{ | 111 | |
| 108 | "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00 | 112 | // clang-format off |
| 109 | "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01 | 113 | constexpr std::array master_key_hashes{ |
| 110 | "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02 | 114 | AsArray("0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"), // master_key_00 |
| 111 | "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03 | 115 | AsArray("4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"), // master_key_01 |
| 112 | "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04 | 116 | AsArray("79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"), // master_key_02 |
| 113 | "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05 | 117 | AsArray("4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"), // master_key_03 |
| 114 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06 | 118 | AsArray("75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"), // master_key_04 |
| 115 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07 | 119 | AsArray("EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"), // master_key_05 |
| 116 | 120 | AsArray("9497E6779F5D840F2BBA1DE4E95BA1D6F21EFC94717D5AE5CA37D7EC5BD37A19"), // master_key_06 | |
| 117 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08 | 121 | AsArray("4EC96B8CB01B8DCE382149443430B2B6EBCB2983348AFA04A25E53609DABEDF6"), // master_key_07 |
| 118 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09 | 122 | |
| 119 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A | 123 | AsArray("2998E2E23609BC2675FF062A2D64AF5B1B78DFF463B24119D64A1B64F01B2D51"), // master_key_08 |
| 120 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B | 124 | AsArray("9D486A98067C44B37CF173D3BF577891EB6081FF6B4A166347D9DBBF7025076B"), // master_key_09 |
| 121 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C | 125 | AsArray("4EC5A237A75A083A9C5F6CF615601522A7F822D06BD4BA32612C9CEBBB29BD45"), // master_key_0A |
| 122 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D | 126 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0B |
| 123 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E | 127 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0C |
| 124 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F | 128 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0D |
| 125 | 129 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0E | |
| 126 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10 | 130 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_0F |
| 127 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11 | 131 | |
| 128 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12 | 132 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_10 |
| 129 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13 | 133 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_11 |
| 130 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14 | 134 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_12 |
| 131 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15 | 135 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_13 |
| 132 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16 | 136 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_14 |
| 133 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17 | 137 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_15 |
| 134 | 138 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_16 | |
| 135 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18 | 139 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_17 |
| 136 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19 | 140 | |
| 137 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A | 141 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_18 |
| 138 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B | 142 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_19 |
| 139 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C | 143 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1A |
| 140 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D | 144 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1B |
| 141 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E | 145 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1C |
| 142 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F | 146 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1D |
| 147 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1E | ||
| 148 | AsArray("0000000000000000000000000000000000000000000000000000000000000000"), // master_key_1F | ||
| 143 | }; | 149 | }; |
| 150 | // clang-format on | ||
| 151 | |||
| 152 | static constexpr u8 CalculateMaxKeyblobSourceHash() { | ||
| 153 | const auto is_zero = [](const auto& data) { | ||
| 154 | // TODO: Replace with std::all_of whenever mingw decides to update their | ||
| 155 | // libraries to include the constexpr variant of it. | ||
| 156 | for (const auto element : data) { | ||
| 157 | if (element != 0) { | ||
| 158 | return false; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | return true; | ||
| 162 | }; | ||
| 144 | 163 | ||
| 145 | static u8 CalculateMaxKeyblobSourceHash() { | ||
| 146 | for (s8 i = 0x1F; i >= 0; --i) { | 164 | for (s8 i = 0x1F; i >= 0; --i) { |
| 147 | if (keyblob_source_hashes[i] != SHA256Hash{}) | 165 | if (!is_zero(keyblob_source_hashes[i])) { |
| 148 | return static_cast<u8>(i + 1); | 166 | return static_cast<u8>(i + 1); |
| 167 | } | ||
| 149 | } | 168 | } |
| 150 | 169 | ||
| 151 | return 0; | 170 | return 0; |
| @@ -346,12 +365,11 @@ FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) con | |||
| 346 | } | 365 | } |
| 347 | 366 | ||
| 348 | static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { | 367 | static bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) { |
| 349 | const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end()); | ||
| 350 | Package2Header temp = header; | 368 | Package2Header temp = header; |
| 351 | AESCipher<Key128> cipher(key, Mode::CTR); | 369 | AESCipher<Key128> cipher(key, Mode::CTR); |
| 352 | cipher.SetIV(iv); | 370 | cipher.SetIV(header.header_ctr); |
| 353 | cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, | 371 | cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - sizeof(Package2Header::signature), |
| 354 | Op::Decrypt); | 372 | &temp.header_ctr, Op::Decrypt); |
| 355 | if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { | 373 | if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { |
| 356 | header = temp; | 374 | header = temp; |
| 357 | return true; | 375 | return true; |
| @@ -388,7 +406,7 @@ void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& packa | |||
| 388 | auto c = a->ReadAllBytes(); | 406 | auto c = a->ReadAllBytes(); |
| 389 | 407 | ||
| 390 | AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); | 408 | AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); |
| 391 | cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()}); | 409 | cipher.SetIV(header.section_ctr[1]); |
| 392 | cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); | 410 | cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); |
| 393 | 411 | ||
| 394 | const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c); | 412 | const auto ini_file = std::make_shared<FileSys::VectorVfsFile>(c); |
diff --git a/src/core/device_memory.cpp b/src/core/device_memory.cpp index 51097ced3..0c4b440ed 100644 --- a/src/core/device_memory.cpp +++ b/src/core/device_memory.cpp | |||
| @@ -2,14 +2,11 @@ | |||
| 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 "core/core.h" | ||
| 6 | #include "core/device_memory.h" | 5 | #include "core/device_memory.h" |
| 7 | #include "core/memory.h" | ||
| 8 | 6 | ||
| 9 | namespace Core { | 7 | namespace Core { |
| 10 | 8 | ||
| 11 | DeviceMemory::DeviceMemory(System& system) : buffer{DramMemoryMap::Size}, system{system} {} | 9 | DeviceMemory::DeviceMemory() : buffer{DramMemoryMap::Size} {} |
| 12 | |||
| 13 | DeviceMemory::~DeviceMemory() = default; | 10 | DeviceMemory::~DeviceMemory() = default; |
| 14 | 11 | ||
| 15 | } // namespace Core | 12 | } // namespace Core |
diff --git a/src/core/device_memory.h b/src/core/device_memory.h index 9efa088d0..5b1ae28f3 100644 --- a/src/core/device_memory.h +++ b/src/core/device_memory.h | |||
| @@ -4,14 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/virtual_buffer.h" | 8 | #include "common/virtual_buffer.h" |
| 10 | 9 | ||
| 11 | namespace Core { | 10 | namespace Core { |
| 12 | 11 | ||
| 13 | class System; | ||
| 14 | |||
| 15 | namespace DramMemoryMap { | 12 | namespace DramMemoryMap { |
| 16 | enum : u64 { | 13 | enum : u64 { |
| 17 | Base = 0x80000000ULL, | 14 | Base = 0x80000000ULL, |
| @@ -26,7 +23,7 @@ enum : u64 { | |||
| 26 | 23 | ||
| 27 | class DeviceMemory : NonCopyable { | 24 | class DeviceMemory : NonCopyable { |
| 28 | public: | 25 | public: |
| 29 | explicit DeviceMemory(Core::System& system); | 26 | explicit DeviceMemory(); |
| 30 | ~DeviceMemory(); | 27 | ~DeviceMemory(); |
| 31 | 28 | ||
| 32 | template <typename T> | 29 | template <typename T> |
| @@ -45,7 +42,6 @@ public: | |||
| 45 | 42 | ||
| 46 | private: | 43 | private: |
| 47 | Common::VirtualBuffer<u8> buffer; | 44 | Common::VirtualBuffer<u8> buffer; |
| 48 | Core::System& system; | ||
| 49 | }; | 45 | }; |
| 50 | 46 | ||
| 51 | } // namespace Core | 47 | } // namespace Core |
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 285277ef8..7c6304ff0 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -4,11 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | 5 | #include <fmt/format.h> |
| 6 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 7 | #include "core/core.h" | ||
| 8 | #include "core/file_sys/bis_factory.h" | 7 | #include "core/file_sys/bis_factory.h" |
| 9 | #include "core/file_sys/mode.h" | 8 | #include "core/file_sys/mode.h" |
| 10 | #include "core/file_sys/registered_cache.h" | 9 | #include "core/file_sys/registered_cache.h" |
| 11 | #include "core/settings.h" | 10 | #include "core/file_sys/vfs.h" |
| 12 | 11 | ||
| 13 | namespace FileSys { | 12 | namespace FileSys { |
| 14 | 13 | ||
| @@ -82,11 +81,11 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { | |||
| 82 | } | 81 | } |
| 83 | } | 82 | } |
| 84 | 83 | ||
| 85 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { | 84 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id, |
| 85 | VirtualFilesystem file_system) const { | ||
| 86 | auto& keys = Core::Crypto::KeyManager::Instance(); | 86 | auto& keys = Core::Crypto::KeyManager::Instance(); |
| 87 | Core::Crypto::PartitionDataManager pdm{ | 87 | Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory( |
| 88 | Core::System::GetInstance().GetFilesystem()->OpenDirectory( | 88 | Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)}; |
| 89 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; | ||
| 90 | keys.PopulateFromPartitionData(pdm); | 89 | keys.PopulateFromPartitionData(pdm); |
| 91 | 90 | ||
| 92 | switch (id) { | 91 | switch (id) { |
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 8f0451c98..136485881 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h | |||
| @@ -6,7 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | 8 | ||
| 9 | #include "core/file_sys/vfs.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/file_sys/vfs_types.h" | ||
| 10 | 11 | ||
| 11 | namespace FileSys { | 12 | namespace FileSys { |
| 12 | 13 | ||
| @@ -51,7 +52,7 @@ public: | |||
| 51 | VirtualDir GetModificationDumpRoot(u64 title_id) const; | 52 | VirtualDir GetModificationDumpRoot(u64 title_id) const; |
| 52 | 53 | ||
| 53 | VirtualDir OpenPartition(BisPartitionId id) const; | 54 | VirtualDir OpenPartition(BisPartitionId id) const; |
| 54 | VirtualFile OpenPartitionStorage(BisPartitionId id) const; | 55 | VirtualFile OpenPartitionStorage(BisPartitionId id, VirtualFilesystem file_system) const; |
| 55 | 56 | ||
| 56 | VirtualDir GetImageDirectory() const; | 57 | VirtualDir GetImageDirectory() const; |
| 57 | 58 | ||
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 664a47e7f..956da68f7 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -8,11 +8,11 @@ | |||
| 8 | #include <fmt/ostream.h> | 8 | #include <fmt/ostream.h> |
| 9 | 9 | ||
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/crypto/key_manager.h" | ||
| 11 | #include "core/file_sys/card_image.h" | 12 | #include "core/file_sys/card_image.h" |
| 12 | #include "core/file_sys/content_archive.h" | 13 | #include "core/file_sys/content_archive.h" |
| 13 | #include "core/file_sys/nca_metadata.h" | 14 | #include "core/file_sys/nca_metadata.h" |
| 14 | #include "core/file_sys/partition_filesystem.h" | 15 | #include "core/file_sys/partition_filesystem.h" |
| 15 | #include "core/file_sys/romfs.h" | ||
| 16 | #include "core/file_sys/submission_package.h" | 16 | #include "core/file_sys/submission_package.h" |
| 17 | #include "core/file_sys/vfs_concat.h" | 17 | #include "core/file_sys/vfs_concat.h" |
| 18 | #include "core/file_sys/vfs_offset.h" | 18 | #include "core/file_sys/vfs_offset.h" |
| @@ -31,7 +31,8 @@ constexpr std::array partition_names{ | |||
| 31 | 31 | ||
| 32 | XCI::XCI(VirtualFile file_) | 32 | XCI::XCI(VirtualFile file_) |
| 33 | : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, | 33 | : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, |
| 34 | partitions(partition_names.size()), partitions_raw(partition_names.size()) { | 34 | partitions(partition_names.size()), |
| 35 | partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { | ||
| 35 | if (file->ReadObject(&header) != sizeof(GamecardHeader)) { | 36 | if (file->ReadObject(&header) != sizeof(GamecardHeader)) { |
| 36 | status = Loader::ResultStatus::ErrorBadXCIHeader; | 37 | status = Loader::ResultStatus::ErrorBadXCIHeader; |
| 37 | return; | 38 | return; |
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index e1b136426..2d0a0f285 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -9,9 +9,12 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/crypto/key_manager.h" | ||
| 13 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 14 | 13 | ||
| 14 | namespace Core::Crypto { | ||
| 15 | class KeyManager; | ||
| 16 | } | ||
| 17 | |||
| 15 | namespace Loader { | 18 | namespace Loader { |
| 16 | enum class ResultStatus : u16; | 19 | enum class ResultStatus : u16; |
| 17 | } | 20 | } |
| @@ -140,6 +143,6 @@ private: | |||
| 140 | 143 | ||
| 141 | u64 update_normal_partition_end; | 144 | u64 update_normal_partition_end; |
| 142 | 145 | ||
| 143 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); | 146 | Core::Crypto::KeyManager& keys; |
| 144 | }; | 147 | }; |
| 145 | } // namespace FileSys | 148 | } // namespace FileSys |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 473245d5a..76af47ff9 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/crypto/aes_util.h" | 11 | #include "core/crypto/aes_util.h" |
| 12 | #include "core/crypto/ctr_encryption_layer.h" | 12 | #include "core/crypto/ctr_encryption_layer.h" |
| 13 | #include "core/crypto/key_manager.h" | ||
| 13 | #include "core/file_sys/content_archive.h" | 14 | #include "core/file_sys/content_archive.h" |
| 14 | #include "core/file_sys/nca_patch.h" | 15 | #include "core/file_sys/nca_patch.h" |
| 15 | #include "core/file_sys/partition_filesystem.h" | 16 | #include "core/file_sys/partition_filesystem.h" |
| 16 | #include "core/file_sys/romfs.h" | ||
| 17 | #include "core/file_sys/vfs_offset.h" | 17 | #include "core/file_sys/vfs_offset.h" |
| 18 | #include "core/loader/loader.h" | 18 | #include "core/loader/loader.h" |
| 19 | 19 | ||
| @@ -119,7 +119,8 @@ static bool IsValidNCA(const NCAHeader& header) { | |||
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) | 121 | NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset) |
| 122 | : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) { | 122 | : file(std::move(file_)), |
| 123 | bktr_base_romfs(std::move(bktr_base_romfs_)), keys{Core::Crypto::KeyManager::Instance()} { | ||
| 123 | if (file == nullptr) { | 124 | if (file == nullptr) { |
| 124 | status = Loader::ResultStatus::ErrorNullFile; | 125 | status = Loader::ResultStatus::ErrorNullFile; |
| 125 | return; | 126 | return; |
| @@ -322,7 +323,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl | |||
| 322 | subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); | 323 | subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low}); |
| 323 | subsection_buckets.back().entries.push_back({size, {0}, 0}); | 324 | subsection_buckets.back().entries.push_back({size, {0}, 0}); |
| 324 | 325 | ||
| 325 | std::optional<Core::Crypto::Key128> key = {}; | 326 | std::optional<Core::Crypto::Key128> key; |
| 326 | if (encrypted) { | 327 | if (encrypted) { |
| 327 | if (has_rights_id) { | 328 | if (has_rights_id) { |
| 328 | status = Loader::ResultStatus::Success; | 329 | status = Loader::ResultStatus::Success; |
| @@ -441,18 +442,18 @@ std::optional<Core::Crypto::Key128> NCA::GetTitlekey() { | |||
| 441 | memcpy(rights_id.data(), header.rights_id.data(), 16); | 442 | memcpy(rights_id.data(), header.rights_id.data(), 16); |
| 442 | if (rights_id == u128{}) { | 443 | if (rights_id == u128{}) { |
| 443 | status = Loader::ResultStatus::ErrorInvalidRightsID; | 444 | status = Loader::ResultStatus::ErrorInvalidRightsID; |
| 444 | return {}; | 445 | return std::nullopt; |
| 445 | } | 446 | } |
| 446 | 447 | ||
| 447 | auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); | 448 | auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); |
| 448 | if (titlekey == Core::Crypto::Key128{}) { | 449 | if (titlekey == Core::Crypto::Key128{}) { |
| 449 | status = Loader::ResultStatus::ErrorMissingTitlekey; | 450 | status = Loader::ResultStatus::ErrorMissingTitlekey; |
| 450 | return {}; | 451 | return std::nullopt; |
| 451 | } | 452 | } |
| 452 | 453 | ||
| 453 | if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { | 454 | if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { |
| 454 | status = Loader::ResultStatus::ErrorMissingTitlekek; | 455 | status = Loader::ResultStatus::ErrorMissingTitlekek; |
| 455 | return {}; | 456 | return std::nullopt; |
| 456 | } | 457 | } |
| 457 | 458 | ||
| 458 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( | 459 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( |
| @@ -476,7 +477,7 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s | |||
| 476 | case NCASectionCryptoType::BKTR: | 477 | case NCASectionCryptoType::BKTR: |
| 477 | LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); | 478 | LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); |
| 478 | { | 479 | { |
| 479 | std::optional<Core::Crypto::Key128> key = {}; | 480 | std::optional<Core::Crypto::Key128> key; |
| 480 | if (has_rights_id) { | 481 | if (has_rights_id) { |
| 481 | status = Loader::ResultStatus::Success; | 482 | status = Loader::ResultStatus::Success; |
| 482 | key = GetTitlekey(); | 483 | key = GetTitlekey(); |
| @@ -495,9 +496,10 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s | |||
| 495 | 496 | ||
| 496 | auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key, | 497 | auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key, |
| 497 | starting_offset); | 498 | starting_offset); |
| 498 | std::vector<u8> iv(16); | 499 | Core::Crypto::CTREncryptionLayer::IVData iv{}; |
| 499 | for (u8 i = 0; i < 8; ++i) | 500 | for (std::size_t i = 0; i < 8; ++i) { |
| 500 | iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; | 501 | iv[i] = s_header.raw.section_ctr[8 - i - 1]; |
| 502 | } | ||
| 501 | out->SetIV(iv); | 503 | out->SetIV(iv); |
| 502 | return std::static_pointer_cast<VfsFile>(out); | 504 | return std::static_pointer_cast<VfsFile>(out); |
| 503 | } | 505 | } |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index d25cbcf91..69292232a 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -158,7 +158,7 @@ private: | |||
| 158 | bool encrypted = false; | 158 | bool encrypted = false; |
| 159 | bool is_update = false; | 159 | bool is_update = false; |
| 160 | 160 | ||
| 161 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); | 161 | Core::Crypto::KeyManager& keys; |
| 162 | }; | 162 | }; |
| 163 | 163 | ||
| 164 | } // namespace FileSys | 164 | } // namespace FileSys |
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index 63cd2eead..b0a130345 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "common/string_util.h" | 5 | #include "common/string_util.h" |
| 6 | #include "common/swap.h" | 6 | #include "common/swap.h" |
| 7 | #include "core/file_sys/control_metadata.h" | 7 | #include "core/file_sys/control_metadata.h" |
| 8 | #include "core/file_sys/vfs.h" | ||
| 8 | 9 | ||
| 9 | namespace FileSys { | 10 | namespace FileSys { |
| 10 | 11 | ||
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index e37b2fadf..403c4219a 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs_types.h" |
| 14 | 14 | ||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| @@ -83,7 +83,7 @@ enum class Language : u8 { | |||
| 83 | Italian = 7, | 83 | Italian = 7, |
| 84 | Dutch = 8, | 84 | Dutch = 8, |
| 85 | CanadianFrench = 9, | 85 | CanadianFrench = 9, |
| 86 | Portugese = 10, | 86 | Portuguese = 10, |
| 87 | Russian = 11, | 87 | Russian = 11, |
| 88 | Korean = 12, | 88 | Korean = 12, |
| 89 | Taiwanese = 13, | 89 | Taiwanese = 13, |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index a08a70efd..dd779310f 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -245,9 +245,11 @@ void IPSwitchCompiler::Parse() { | |||
| 245 | 245 | ||
| 246 | // Read rest of patch | 246 | // Read rest of patch |
| 247 | while (true) { | 247 | while (true) { |
| 248 | if (i + 1 >= lines.size()) | 248 | if (i + 1 >= lines.size()) { |
| 249 | break; | 249 | break; |
| 250 | const auto patch_line = lines[++i]; | 250 | } |
| 251 | |||
| 252 | const auto& patch_line = lines[++i]; | ||
| 251 | 253 | ||
| 252 | // Start of new patch | 254 | // Start of new patch |
| 253 | if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { | 255 | if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) { |
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp index 76313679d..ef93ef3ed 100644 --- a/src/core/file_sys/kernel_executable.cpp +++ b/src/core/file_sys/kernel_executable.cpp | |||
| @@ -2,9 +2,12 @@ | |||
| 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 <cstring> | ||
| 6 | |||
| 5 | #include "common/string_util.h" | 7 | #include "common/string_util.h" |
| 6 | #include "core/file_sys/kernel_executable.h" | 8 | #include "core/file_sys/kernel_executable.h" |
| 7 | #include "core/file_sys/vfs_offset.h" | 9 | #include "core/file_sys/vfs_offset.h" |
| 10 | #include "core/loader/loader.h" | ||
| 8 | 11 | ||
| 9 | namespace FileSys { | 12 | namespace FileSys { |
| 10 | 13 | ||
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h index 324a57384..044c554d3 100644 --- a/src/core/file_sys/kernel_executable.h +++ b/src/core/file_sys/kernel_executable.h | |||
| @@ -4,10 +4,17 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 7 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | ||
| 8 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 9 | #include "core/file_sys/vfs_types.h" | 13 | #include "core/file_sys/vfs_types.h" |
| 10 | #include "core/loader/loader.h" | 14 | |
| 15 | namespace Loader { | ||
| 16 | enum class ResultStatus : u16; | ||
| 17 | } | ||
| 11 | 18 | ||
| 12 | namespace FileSys { | 19 | namespace FileSys { |
| 13 | 20 | ||
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h index c95205668..2b4f21073 100644 --- a/src/core/file_sys/mode.h +++ b/src/core/file_sys/mode.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 8 | 9 | ||
| 9 | namespace FileSys { | 10 | namespace FileSys { |
| @@ -11,13 +12,11 @@ namespace FileSys { | |||
| 11 | enum class Mode : u32 { | 12 | enum class Mode : u32 { |
| 12 | Read = 1, | 13 | Read = 1, |
| 13 | Write = 2, | 14 | Write = 2, |
| 14 | ReadWrite = 3, | 15 | ReadWrite = Read | Write, |
| 15 | Append = 4, | 16 | Append = 4, |
| 16 | WriteAppend = 6, | 17 | WriteAppend = Write | Append, |
| 17 | }; | 18 | }; |
| 18 | 19 | ||
| 19 | inline u32 operator&(Mode lhs, Mode rhs) { | 20 | DECLARE_ENUM_FLAG_OPERATORS(Mode) |
| 20 | return static_cast<u32>(lhs) & static_cast<u32>(rhs); | ||
| 21 | } | ||
| 22 | 21 | ||
| 23 | } // namespace FileSys | 22 | } // namespace FileSys |
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 93d0df6b9..2d1476e3a 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/swap.h" | 8 | #include "common/swap.h" |
| 9 | #include "core/file_sys/nca_metadata.h" | 9 | #include "core/file_sys/nca_metadata.h" |
| 10 | #include "core/file_sys/vfs.h" | ||
| 10 | 11 | ||
| 11 | namespace FileSys { | 12 | namespace FileSys { |
| 12 | 13 | ||
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 1f82fff0a..53535e5f5 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs_types.h" |
| 14 | 14 | ||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | class CNMT; | 16 | class CNMT; |
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index 0090cc6c4..5990a2fd5 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.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 <array> | ||
| 6 | #include <cstddef> | 7 | #include <cstddef> |
| 7 | #include <cstring> | 8 | #include <cstring> |
| 8 | 9 | ||
| @@ -11,6 +12,49 @@ | |||
| 11 | #include "core/file_sys/nca_patch.h" | 12 | #include "core/file_sys/nca_patch.h" |
| 12 | 13 | ||
| 13 | namespace FileSys { | 14 | namespace FileSys { |
| 15 | namespace { | ||
| 16 | template <bool Subsection, typename BlockType, typename BucketType> | ||
| 17 | std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockType& block, | ||
| 18 | const BucketType& buckets) { | ||
| 19 | if constexpr (Subsection) { | ||
| 20 | const auto& last_bucket = buckets[block.number_buckets - 1]; | ||
| 21 | if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) { | ||
| 22 | return {block.number_buckets - 1, last_bucket.number_entries}; | ||
| 23 | } | ||
| 24 | } else { | ||
| 25 | ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); | ||
| 26 | } | ||
| 27 | |||
| 28 | std::size_t bucket_id = std::count_if( | ||
| 29 | block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets, | ||
| 30 | [&offset](u64 base_offset) { return base_offset <= offset; }); | ||
| 31 | |||
| 32 | const auto& bucket = buckets[bucket_id]; | ||
| 33 | |||
| 34 | if (bucket.number_entries == 1) { | ||
| 35 | return {bucket_id, 0}; | ||
| 36 | } | ||
| 37 | |||
| 38 | std::size_t low = 0; | ||
| 39 | std::size_t mid = 0; | ||
| 40 | std::size_t high = bucket.number_entries - 1; | ||
| 41 | while (low <= high) { | ||
| 42 | mid = (low + high) / 2; | ||
| 43 | if (bucket.entries[mid].address_patch > offset) { | ||
| 44 | high = mid - 1; | ||
| 45 | } else { | ||
| 46 | if (mid == bucket.number_entries - 1 || | ||
| 47 | bucket.entries[mid + 1].address_patch > offset) { | ||
| 48 | return {bucket_id, mid}; | ||
| 49 | } | ||
| 50 | |||
| 51 | low = mid + 1; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | UNREACHABLE_MSG("Offset could not be found in BKTR block."); | ||
| 56 | } | ||
| 57 | } // Anonymous namespace | ||
| 14 | 58 | ||
| 15 | BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, | 59 | BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_, |
| 16 | std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_, | 60 | std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_, |
| @@ -66,7 +110,7 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const { | |||
| 66 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR); | 110 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR); |
| 67 | 111 | ||
| 68 | // Calculate AES IV | 112 | // Calculate AES IV |
| 69 | std::vector<u8> iv(16); | 113 | std::array<u8, 16> iv{}; |
| 70 | auto subsection_ctr = subsection.ctr; | 114 | auto subsection_ctr = subsection.ctr; |
| 71 | auto offset_iv = section_offset + base_offset; | 115 | auto offset_iv = section_offset + base_offset; |
| 72 | for (std::size_t i = 0; i < section_ctr.size(); ++i) | 116 | for (std::size_t i = 0; i < section_ctr.size(); ++i) |
| @@ -109,46 +153,6 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const { | |||
| 109 | return raw_read; | 153 | return raw_read; |
| 110 | } | 154 | } |
| 111 | 155 | ||
| 112 | template <bool Subsection, typename BlockType, typename BucketType> | ||
| 113 | std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block, | ||
| 114 | BucketType buckets) const { | ||
| 115 | if constexpr (Subsection) { | ||
| 116 | const auto last_bucket = buckets[block.number_buckets - 1]; | ||
| 117 | if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) | ||
| 118 | return {block.number_buckets - 1, last_bucket.number_entries}; | ||
| 119 | } else { | ||
| 120 | ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); | ||
| 121 | } | ||
| 122 | |||
| 123 | std::size_t bucket_id = std::count_if( | ||
| 124 | block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets, | ||
| 125 | [&offset](u64 base_offset) { return base_offset <= offset; }); | ||
| 126 | |||
| 127 | const auto bucket = buckets[bucket_id]; | ||
| 128 | |||
| 129 | if (bucket.number_entries == 1) | ||
| 130 | return {bucket_id, 0}; | ||
| 131 | |||
| 132 | std::size_t low = 0; | ||
| 133 | std::size_t mid = 0; | ||
| 134 | std::size_t high = bucket.number_entries - 1; | ||
| 135 | while (low <= high) { | ||
| 136 | mid = (low + high) / 2; | ||
| 137 | if (bucket.entries[mid].address_patch > offset) { | ||
| 138 | high = mid - 1; | ||
| 139 | } else { | ||
| 140 | if (mid == bucket.number_entries - 1 || | ||
| 141 | bucket.entries[mid + 1].address_patch > offset) { | ||
| 142 | return {bucket_id, mid}; | ||
| 143 | } | ||
| 144 | |||
| 145 | low = mid + 1; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | UNREACHABLE_MSG("Offset could not be found in BKTR block."); | ||
| 150 | } | ||
| 151 | |||
| 152 | RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { | 156 | RelocationEntry BKTR::GetRelocationEntry(u64 offset) const { |
| 153 | const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets); | 157 | const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets); |
| 154 | return relocation_buckets[res.first].entries[res.second]; | 158 | return relocation_buckets[res.first].entries[res.second]; |
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h index 8e64e8378..60c544f8e 100644 --- a/src/core/file_sys/nca_patch.h +++ b/src/core/file_sys/nca_patch.h | |||
| @@ -117,10 +117,6 @@ public: | |||
| 117 | bool Rename(std::string_view name) override; | 117 | bool Rename(std::string_view name) override; |
| 118 | 118 | ||
| 119 | private: | 119 | private: |
| 120 | template <bool Subsection, typename BlockType, typename BucketType> | ||
| 121 | std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block, | ||
| 122 | BucketType buckets) const; | ||
| 123 | |||
| 124 | RelocationEntry GetRelocationEntry(u64 offset) const; | 120 | RelocationEntry GetRelocationEntry(u64 offset) const; |
| 125 | RelocationEntry GetNextRelocationEntry(u64 offset) const; | 121 | RelocationEntry GetNextRelocationEntry(u64 offset) const; |
| 126 | 122 | ||
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 846986736..48a2ed4d4 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -21,7 +21,7 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const { | |||
| 21 | magic == Common::MakeMagic('P', 'F', 'S', '0'); | 21 | magic == Common::MakeMagic('P', 'F', 'S', '0'); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | 24 | PartitionFilesystem::PartitionFilesystem(VirtualFile file) { |
| 25 | // At least be as large as the header | 25 | // At least be as large as the header |
| 26 | if (file->GetSize() < sizeof(Header)) { | 26 | if (file->GetSize() < sizeof(Header)) { |
| 27 | status = Loader::ResultStatus::ErrorBadPFSHeader; | 27 | status = Loader::ResultStatus::ErrorBadPFSHeader; |
| @@ -89,11 +89,11 @@ std::map<std::string, u64> PartitionFilesystem::GetFileSizes() const { | |||
| 89 | return sizes; | 89 | return sizes; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { | 92 | std::vector<VirtualFile> PartitionFilesystem::GetFiles() const { |
| 93 | return pfs_files; | 93 | return pfs_files; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { | 96 | std::vector<VirtualDir> PartitionFilesystem::GetSubdirectories() const { |
| 97 | return {}; | 97 | return {}; |
| 98 | } | 98 | } |
| 99 | 99 | ||
| @@ -101,7 +101,7 @@ std::string PartitionFilesystem::GetName() const { | |||
| 101 | return is_hfs ? "HFS0" : "PFS0"; | 101 | return is_hfs ? "HFS0" : "PFS0"; |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const { | 104 | VirtualDir PartitionFilesystem::GetParentDirectory() const { |
| 105 | // TODO(DarkLordZach): Add support for nested containers. | 105 | // TODO(DarkLordZach): Add support for nested containers. |
| 106 | return nullptr; | 106 | return nullptr; |
| 107 | } | 107 | } |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 279193b19..0f831148e 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -24,7 +24,7 @@ namespace FileSys { | |||
| 24 | */ | 24 | */ |
| 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { | 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { |
| 26 | public: | 26 | public: |
| 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); | 27 | explicit PartitionFilesystem(VirtualFile file); |
| 28 | ~PartitionFilesystem() override; | 28 | ~PartitionFilesystem() override; |
| 29 | 29 | ||
| 30 | Loader::ResultStatus GetStatus() const; | 30 | Loader::ResultStatus GetStatus() const; |
| @@ -32,10 +32,10 @@ public: | |||
| 32 | std::map<std::string, u64> GetFileOffsets() const; | 32 | std::map<std::string, u64> GetFileOffsets() const; |
| 33 | std::map<std::string, u64> GetFileSizes() const; | 33 | std::map<std::string, u64> GetFileSizes() const; |
| 34 | 34 | ||
| 35 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 35 | std::vector<VirtualFile> GetFiles() const override; |
| 36 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | 36 | std::vector<VirtualDir> GetSubdirectories() const override; |
| 37 | std::string GetName() const override; | 37 | std::string GetName() const override; |
| 38 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | 38 | VirtualDir GetParentDirectory() const override; |
| 39 | void PrintDebugInfo() const; | 39 | void PrintDebugInfo() const; |
| 40 | 40 | ||
| 41 | private: | 41 | private: |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index c47ff863e..b9c09b456 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include "core/settings.h" | 27 | #include "core/settings.h" |
| 28 | 28 | ||
| 29 | namespace FileSys { | 29 | namespace FileSys { |
| 30 | namespace { | ||
| 30 | 31 | ||
| 31 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | 32 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; |
| 32 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 33 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; |
| @@ -36,21 +37,29 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{ | |||
| 36 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", | 37 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9", |
| 37 | }; | 38 | }; |
| 38 | 39 | ||
| 39 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | 40 | enum class TitleVersionFormat : u8 { |
| 41 | ThreeElements, ///< vX.Y.Z | ||
| 42 | FourElements, ///< vX.Y.Z.W | ||
| 43 | }; | ||
| 44 | |||
| 45 | std::string FormatTitleVersion(u32 version, | ||
| 46 | TitleVersionFormat format = TitleVersionFormat::ThreeElements) { | ||
| 40 | std::array<u8, sizeof(u32)> bytes{}; | 47 | std::array<u8, sizeof(u32)> bytes{}; |
| 41 | bytes[0] = version % SINGLE_BYTE_MODULUS; | 48 | bytes[0] = static_cast<u8>(version % SINGLE_BYTE_MODULUS); |
| 42 | for (std::size_t i = 1; i < bytes.size(); ++i) { | 49 | for (std::size_t i = 1; i < bytes.size(); ++i) { |
| 43 | version /= SINGLE_BYTE_MODULUS; | 50 | version /= SINGLE_BYTE_MODULUS; |
| 44 | bytes[i] = version % SINGLE_BYTE_MODULUS; | 51 | bytes[i] = static_cast<u8>(version % SINGLE_BYTE_MODULUS); |
| 45 | } | 52 | } |
| 46 | 53 | ||
| 47 | if (format == TitleVersionFormat::FourElements) | 54 | if (format == TitleVersionFormat::FourElements) { |
| 48 | return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); | 55 | return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]); |
| 56 | } | ||
| 49 | return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); | 57 | return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); |
| 50 | } | 58 | } |
| 51 | 59 | ||
| 52 | std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir, | 60 | // Returns a directory with name matching name case-insensitive. Returns nullptr if directory |
| 53 | std::string_view name) { | 61 | // doesn't have a directory with name. |
| 62 | VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) { | ||
| 54 | #ifdef _WIN32 | 63 | #ifdef _WIN32 |
| 55 | return dir->GetSubdirectory(name); | 64 | return dir->GetSubdirectory(name); |
| 56 | #else | 65 | #else |
| @@ -66,6 +75,43 @@ std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<Vfs | |||
| 66 | #endif | 75 | #endif |
| 67 | } | 76 | } |
| 68 | 77 | ||
| 78 | std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( | ||
| 79 | u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) { | ||
| 80 | const auto build_id_raw = Common::HexToString(build_id_, upper); | ||
| 81 | const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); | ||
| 82 | const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); | ||
| 83 | |||
| 84 | if (file == nullptr) { | ||
| 85 | LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", | ||
| 86 | title_id, build_id); | ||
| 87 | return std::nullopt; | ||
| 88 | } | ||
| 89 | |||
| 90 | std::vector<u8> data(file->GetSize()); | ||
| 91 | if (file->Read(data.data(), data.size()) != data.size()) { | ||
| 92 | LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", | ||
| 93 | title_id, build_id); | ||
| 94 | return std::nullopt; | ||
| 95 | } | ||
| 96 | |||
| 97 | const Core::Memory::TextCheatParser parser; | ||
| 98 | return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size())); | ||
| 99 | } | ||
| 100 | |||
| 101 | void AppendCommaIfNotEmpty(std::string& to, std::string_view with) { | ||
| 102 | if (to.empty()) { | ||
| 103 | to += with; | ||
| 104 | } else { | ||
| 105 | to += ", "; | ||
| 106 | to += with; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||
| 111 | return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | ||
| 112 | } | ||
| 113 | } // Anonymous namespace | ||
| 114 | |||
| 69 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | 115 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} |
| 70 | 116 | ||
| 71 | PatchManager::~PatchManager() = default; | 117 | PatchManager::~PatchManager() = default; |
| @@ -246,7 +292,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | |||
| 246 | return out; | 292 | return out; |
| 247 | } | 293 | } |
| 248 | 294 | ||
| 249 | bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | 295 | bool PatchManager::HasNSOPatch(const BuildID& build_id_) const { |
| 250 | const auto build_id_raw = Common::HexToString(build_id_); | 296 | const auto build_id_raw = Common::HexToString(build_id_); |
| 251 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | 297 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); |
| 252 | 298 | ||
| @@ -266,36 +312,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 266 | return !CollectPatches(patch_dirs, build_id).empty(); | 312 | return !CollectPatches(patch_dirs, build_id).empty(); |
| 267 | } | 313 | } |
| 268 | 314 | ||
| 269 | namespace { | ||
| 270 | std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder( | ||
| 271 | const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_, | ||
| 272 | const VirtualDir& base_path, bool upper) { | ||
| 273 | const auto build_id_raw = Common::HexToString(build_id_, upper); | ||
| 274 | const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2); | ||
| 275 | const auto file = base_path->GetFile(fmt::format("{}.txt", build_id)); | ||
| 276 | |||
| 277 | if (file == nullptr) { | ||
| 278 | LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}", | ||
| 279 | title_id, build_id); | ||
| 280 | return std::nullopt; | ||
| 281 | } | ||
| 282 | |||
| 283 | std::vector<u8> data(file->GetSize()); | ||
| 284 | if (file->Read(data.data(), data.size()) != data.size()) { | ||
| 285 | LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}", | ||
| 286 | title_id, build_id); | ||
| 287 | return std::nullopt; | ||
| 288 | } | ||
| 289 | |||
| 290 | Core::Memory::TextCheatParser parser; | ||
| 291 | return parser.Parse( | ||
| 292 | system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size())); | ||
| 293 | } | ||
| 294 | |||
| 295 | } // Anonymous namespace | ||
| 296 | |||
| 297 | std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( | 315 | std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( |
| 298 | const Core::System& system, const std::array<u8, 32>& build_id_) const { | 316 | const Core::System& system, const BuildID& build_id_) const { |
| 299 | const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); | 317 | const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id); |
| 300 | if (load_dir == nullptr) { | 318 | if (load_dir == nullptr) { |
| 301 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | 319 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); |
| @@ -315,14 +333,12 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList( | |||
| 315 | 333 | ||
| 316 | auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); | 334 | auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats"); |
| 317 | if (cheats_dir != nullptr) { | 335 | if (cheats_dir != nullptr) { |
| 318 | auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true); | 336 | if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) { |
| 319 | if (res.has_value()) { | ||
| 320 | std::copy(res->begin(), res->end(), std::back_inserter(out)); | 337 | std::copy(res->begin(), res->end(), std::back_inserter(out)); |
| 321 | continue; | 338 | continue; |
| 322 | } | 339 | } |
| 323 | 340 | ||
| 324 | res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false); | 341 | if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) { |
| 325 | if (res.has_value()) { | ||
| 326 | std::copy(res->begin(), res->end(), std::back_inserter(out)); | 342 | std::copy(res->begin(), res->end(), std::back_inserter(out)); |
| 327 | } | 343 | } |
| 328 | } | 344 | } |
| @@ -436,21 +452,11 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content | |||
| 436 | return romfs; | 452 | return romfs; |
| 437 | } | 453 | } |
| 438 | 454 | ||
| 439 | static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { | 455 | PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const { |
| 440 | if (to.empty()) | 456 | if (title_id == 0) { |
| 441 | to += with; | ||
| 442 | else | ||
| 443 | to += ", " + with; | ||
| 444 | } | ||
| 445 | |||
| 446 | static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||
| 447 | return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | ||
| 448 | } | ||
| 449 | |||
| 450 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( | ||
| 451 | VirtualFile update_raw) const { | ||
| 452 | if (title_id == 0) | ||
| 453 | return {}; | 457 | return {}; |
| 458 | } | ||
| 459 | |||
| 454 | std::map<std::string, std::string, std::less<>> out; | 460 | std::map<std::string, std::string, std::less<>> out; |
| 455 | const auto& installed = Core::System::GetInstance().GetContentProvider(); | 461 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 456 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 462 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| @@ -473,8 +479,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 473 | if (meta_ver.value_or(0) == 0) { | 479 | if (meta_ver.value_or(0) == 0) { |
| 474 | out.insert_or_assign(update_label, ""); | 480 | out.insert_or_assign(update_label, ""); |
| 475 | } else { | 481 | } else { |
| 476 | out.insert_or_assign( | 482 | out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver)); |
| 477 | update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); | ||
| 478 | } | 483 | } |
| 479 | } else if (update_raw != nullptr) { | 484 | } else if (update_raw != nullptr) { |
| 480 | out.insert_or_assign(update_label, "PACKED"); | 485 | out.insert_or_assign(update_label, "PACKED"); |
| @@ -563,40 +568,46 @@ std::optional<u32> PatchManager::GetGameVersion() const { | |||
| 563 | return installed.GetEntryVersion(title_id); | 568 | return installed.GetEntryVersion(title_id); |
| 564 | } | 569 | } |
| 565 | 570 | ||
| 566 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { | 571 | PatchManager::Metadata PatchManager::GetControlMetadata() const { |
| 567 | const auto& installed = Core::System::GetInstance().GetContentProvider(); | 572 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 568 | 573 | ||
| 569 | const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); | 574 | const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control); |
| 570 | if (base_control_nca == nullptr) | 575 | if (base_control_nca == nullptr) { |
| 571 | return {}; | 576 | return {}; |
| 577 | } | ||
| 572 | 578 | ||
| 573 | return ParseControlNCA(*base_control_nca); | 579 | return ParseControlNCA(*base_control_nca); |
| 574 | } | 580 | } |
| 575 | 581 | ||
| 576 | std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const { | 582 | PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const { |
| 577 | const auto base_romfs = nca.GetRomFS(); | 583 | const auto base_romfs = nca.GetRomFS(); |
| 578 | if (base_romfs == nullptr) | 584 | if (base_romfs == nullptr) { |
| 579 | return {}; | 585 | return {}; |
| 586 | } | ||
| 580 | 587 | ||
| 581 | const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); | 588 | const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control); |
| 582 | if (romfs == nullptr) | 589 | if (romfs == nullptr) { |
| 583 | return {}; | 590 | return {}; |
| 591 | } | ||
| 584 | 592 | ||
| 585 | const auto extracted = ExtractRomFS(romfs); | 593 | const auto extracted = ExtractRomFS(romfs); |
| 586 | if (extracted == nullptr) | 594 | if (extracted == nullptr) { |
| 587 | return {}; | 595 | return {}; |
| 596 | } | ||
| 588 | 597 | ||
| 589 | auto nacp_file = extracted->GetFile("control.nacp"); | 598 | auto nacp_file = extracted->GetFile("control.nacp"); |
| 590 | if (nacp_file == nullptr) | 599 | if (nacp_file == nullptr) { |
| 591 | nacp_file = extracted->GetFile("Control.nacp"); | 600 | nacp_file = extracted->GetFile("Control.nacp"); |
| 601 | } | ||
| 592 | 602 | ||
| 593 | auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); | 603 | auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file); |
| 594 | 604 | ||
| 595 | VirtualFile icon_file; | 605 | VirtualFile icon_file; |
| 596 | for (const auto& language : FileSys::LANGUAGE_NAMES) { | 606 | for (const auto& language : FileSys::LANGUAGE_NAMES) { |
| 597 | icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat"); | 607 | icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat")); |
| 598 | if (icon_file != nullptr) | 608 | if (icon_file != nullptr) { |
| 599 | break; | 609 | break; |
| 610 | } | ||
| 600 | } | 611 | } |
| 601 | 612 | ||
| 602 | return {std::move(nacp), icon_file}; | 613 | return {std::move(nacp), icon_file}; |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index f4cb918dd..1f28c6241 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -6,10 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | #include <map> | 7 | #include <map> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <optional> | ||
| 9 | #include <string> | 10 | #include <string> |
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/nca_metadata.h" | 12 | #include "core/file_sys/nca_metadata.h" |
| 12 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs_types.h" |
| 13 | #include "core/memory/dmnt_cheat_types.h" | 14 | #include "core/memory/dmnt_cheat_types.h" |
| 14 | 15 | ||
| 15 | namespace Core { | 16 | namespace Core { |
| @@ -21,71 +22,62 @@ namespace FileSys { | |||
| 21 | class NCA; | 22 | class NCA; |
| 22 | class NACP; | 23 | class NACP; |
| 23 | 24 | ||
| 24 | enum class TitleVersionFormat : u8 { | ||
| 25 | ThreeElements, ///< vX.Y.Z | ||
| 26 | FourElements, ///< vX.Y.Z.W | ||
| 27 | }; | ||
| 28 | |||
| 29 | std::string FormatTitleVersion(u32 version, | ||
| 30 | TitleVersionFormat format = TitleVersionFormat::ThreeElements); | ||
| 31 | |||
| 32 | // Returns a directory with name matching name case-insensitive. Returns nullptr if directory | ||
| 33 | // doesn't have a directory with name. | ||
| 34 | std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir, | ||
| 35 | std::string_view name); | ||
| 36 | |||
| 37 | // A centralized class to manage patches to games. | 25 | // A centralized class to manage patches to games. |
| 38 | class PatchManager { | 26 | class PatchManager { |
| 39 | public: | 27 | public: |
| 28 | using BuildID = std::array<u8, 0x20>; | ||
| 29 | using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>; | ||
| 30 | using PatchVersionNames = std::map<std::string, std::string, std::less<>>; | ||
| 31 | |||
| 40 | explicit PatchManager(u64 title_id); | 32 | explicit PatchManager(u64 title_id); |
| 41 | ~PatchManager(); | 33 | ~PatchManager(); |
| 42 | 34 | ||
| 43 | u64 GetTitleID() const; | 35 | [[nodiscard]] u64 GetTitleID() const; |
| 44 | 36 | ||
| 45 | // Currently tracked ExeFS patches: | 37 | // Currently tracked ExeFS patches: |
| 46 | // - Game Updates | 38 | // - Game Updates |
| 47 | VirtualDir PatchExeFS(VirtualDir exefs) const; | 39 | [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const; |
| 48 | 40 | ||
| 49 | // Currently tracked NSO patches: | 41 | // Currently tracked NSO patches: |
| 50 | // - IPS | 42 | // - IPS |
| 51 | // - IPSwitch | 43 | // - IPSwitch |
| 52 | std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const; | 44 | [[nodiscard]] std::vector<u8> PatchNSO(const std::vector<u8>& nso, |
| 45 | const std::string& name) const; | ||
| 53 | 46 | ||
| 54 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. | 47 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. |
| 55 | // Used to prevent expensive copies in NSO loader. | 48 | // Used to prevent expensive copies in NSO loader. |
| 56 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; | 49 | [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const; |
| 57 | 50 | ||
| 58 | // Creates a CheatList object with all | 51 | // Creates a CheatList object with all |
| 59 | std::vector<Core::Memory::CheatEntry> CreateCheatList( | 52 | [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList( |
| 60 | const Core::System& system, const std::array<u8, 0x20>& build_id) const; | 53 | const Core::System& system, const BuildID& build_id) const; |
| 61 | 54 | ||
| 62 | // Currently tracked RomFS patches: | 55 | // Currently tracked RomFS patches: |
| 63 | // - Game Updates | 56 | // - Game Updates |
| 64 | // - LayeredFS | 57 | // - LayeredFS |
| 65 | VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, | 58 | [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, |
| 66 | ContentRecordType type = ContentRecordType::Program, | 59 | ContentRecordType type = ContentRecordType::Program, |
| 67 | VirtualFile update_raw = nullptr) const; | 60 | VirtualFile update_raw = nullptr) const; |
| 68 | 61 | ||
| 69 | // Returns a vector of pairs between patch names and patch versions. | 62 | // Returns a vector of pairs between patch names and patch versions. |
| 70 | // i.e. Update 3.2.2 will return {"Update", "3.2.2"} | 63 | // i.e. Update 3.2.2 will return {"Update", "3.2.2"} |
| 71 | std::map<std::string, std::string, std::less<>> GetPatchVersionNames( | 64 | [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const; |
| 72 | VirtualFile update_raw = nullptr) const; | ||
| 73 | 65 | ||
| 74 | // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, | 66 | // If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails, |
| 75 | // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be | 67 | // it will fallback to the Meta-type NCA of the base game. If that fails, the result will be |
| 76 | // std::nullopt | 68 | // std::nullopt |
| 77 | std::optional<u32> GetGameVersion() const; | 69 | [[nodiscard]] std::optional<u32> GetGameVersion() const; |
| 78 | 70 | ||
| 79 | // Given title_id of the program, attempts to get the control data of the update and parse | 71 | // Given title_id of the program, attempts to get the control data of the update and parse |
| 80 | // it, falling back to the base control data. | 72 | // it, falling back to the base control data. |
| 81 | std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const; | 73 | [[nodiscard]] Metadata GetControlMetadata() const; |
| 82 | 74 | ||
| 83 | // Version of GetControlMetadata that takes an arbitrary NCA | 75 | // Version of GetControlMetadata that takes an arbitrary NCA |
| 84 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; | 76 | [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const; |
| 85 | 77 | ||
| 86 | private: | 78 | private: |
| 87 | std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, | 79 | [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, |
| 88 | const std::string& build_id) const; | 80 | const std::string& build_id) const; |
| 89 | 81 | ||
| 90 | u64 title_id; | 82 | u64 title_id; |
| 91 | }; | 83 | }; |
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 43169bf9f..9cf49bf44 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/file_sys/program_metadata.h" | 9 | #include "core/file_sys/program_metadata.h" |
| 10 | #include "core/file_sys/vfs.h" | ||
| 10 | #include "core/loader/loader.h" | 11 | #include "core/loader/loader.h" |
| 11 | 12 | ||
| 12 | namespace FileSys { | 13 | namespace FileSys { |
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 35069972b..455532567 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "common/bit_field.h" | 9 | #include "common/bit_field.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs_types.h" |
| 13 | 13 | ||
| 14 | namespace Loader { | 14 | namespace Loader { |
| 15 | enum class ResultStatus : u16; | 15 | enum class ResultStatus : u16; |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 37351c561..da01002d5 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -257,8 +257,7 @@ std::vector<NcaID> PlaceholderCache::List() const { | |||
| 257 | for (const auto& sdir : dir->GetSubdirectories()) { | 257 | for (const auto& sdir : dir->GetSubdirectories()) { |
| 258 | for (const auto& file : sdir->GetFiles()) { | 258 | for (const auto& file : sdir->GetFiles()) { |
| 259 | const auto name = file->GetName(); | 259 | const auto name = file->GetName(); |
| 260 | if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && | 260 | if (name.length() == 36 && name.ends_with(".nca")) { |
| 261 | name[35] == 'a') { | ||
| 262 | out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); | 261 | out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); |
| 263 | } | 262 | } |
| 264 | } | 263 | } |
| @@ -344,15 +343,18 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { | |||
| 344 | 343 | ||
| 345 | static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id, | 344 | static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id, |
| 346 | ContentRecordType type) { | 345 | ContentRecordType type) { |
| 347 | if (map.find(title_id) == map.end()) | 346 | const auto cmnt_iter = map.find(title_id); |
| 348 | return {}; | 347 | if (cmnt_iter == map.cend()) { |
| 349 | 348 | return std::nullopt; | |
| 350 | const auto& cnmt = map.at(title_id); | 349 | } |
| 351 | 350 | ||
| 352 | const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(), | 351 | const auto& cnmt = cmnt_iter->second; |
| 352 | const auto& content_records = cnmt.GetContentRecords(); | ||
| 353 | const auto iter = std::find_if(content_records.cbegin(), content_records.cend(), | ||
| 353 | [type](const ContentRecord& rec) { return rec.type == type; }); | 354 | [type](const ContentRecord& rec) { return rec.type == type; }); |
| 354 | if (iter == cnmt.GetContentRecords().end()) | 355 | if (iter == content_records.cend()) { |
| 355 | return {}; | 356 | return std::nullopt; |
| 357 | } | ||
| 356 | 358 | ||
| 357 | return std::make_optional(iter->nca_id); | 359 | return std::make_optional(iter->nca_id); |
| 358 | } | 360 | } |
| @@ -467,14 +469,16 @@ VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType ty | |||
| 467 | 469 | ||
| 468 | std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { | 470 | std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { |
| 469 | const auto meta_iter = meta.find(title_id); | 471 | const auto meta_iter = meta.find(title_id); |
| 470 | if (meta_iter != meta.end()) | 472 | if (meta_iter != meta.cend()) { |
| 471 | return meta_iter->second.GetTitleVersion(); | 473 | return meta_iter->second.GetTitleVersion(); |
| 474 | } | ||
| 472 | 475 | ||
| 473 | const auto yuzu_meta_iter = yuzu_meta.find(title_id); | 476 | const auto yuzu_meta_iter = yuzu_meta.find(title_id); |
| 474 | if (yuzu_meta_iter != yuzu_meta.end()) | 477 | if (yuzu_meta_iter != yuzu_meta.cend()) { |
| 475 | return yuzu_meta_iter->second.GetTitleVersion(); | 478 | return yuzu_meta_iter->second.GetTitleVersion(); |
| 479 | } | ||
| 476 | 480 | ||
| 477 | return {}; | 481 | return std::nullopt; |
| 478 | } | 482 | } |
| 479 | 483 | ||
| 480 | VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { | 484 | VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const { |
| @@ -547,56 +551,6 @@ InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_ex | |||
| 547 | return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); | 551 | return InstallEntry(*xci.GetSecurePartitionNSP(), overwrite_if_exists, copy); |
| 548 | } | 552 | } |
| 549 | 553 | ||
| 550 | bool RegisteredCache::RemoveExistingEntry(u64 title_id) { | ||
| 551 | const auto delete_nca = [this](const NcaID& id) { | ||
| 552 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 553 | |||
| 554 | if (dir->GetFileRelative(path) == nullptr) { | ||
| 555 | return false; | ||
| 556 | } | ||
| 557 | |||
| 558 | Core::Crypto::SHA256Hash hash{}; | ||
| 559 | mbedtls_sha256_ret(id.data(), id.size(), hash.data(), 0); | ||
| 560 | const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||
| 561 | |||
| 562 | const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||
| 563 | |||
| 564 | const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||
| 565 | |||
| 566 | return res; | ||
| 567 | }; | ||
| 568 | |||
| 569 | // If an entry exists in the registered cache, remove it | ||
| 570 | if (HasEntry(title_id, ContentRecordType::Meta)) { | ||
| 571 | LOG_INFO(Loader, | ||
| 572 | "Previously installed entry (v{}) for title_id={:016X} detected! " | ||
| 573 | "Attempting to remove...", | ||
| 574 | GetEntryVersion(title_id).value_or(0), title_id); | ||
| 575 | // Get all the ncas associated with the current CNMT and delete them | ||
| 576 | const auto meta_old_id = | ||
| 577 | GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); | ||
| 578 | const auto program_id = | ||
| 579 | GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); | ||
| 580 | const auto data_id = | ||
| 581 | GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); | ||
| 582 | const auto control_id = | ||
| 583 | GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); | ||
| 584 | const auto html_id = | ||
| 585 | GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); | ||
| 586 | const auto legal_id = | ||
| 587 | GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); | ||
| 588 | |||
| 589 | delete_nca(meta_old_id); | ||
| 590 | delete_nca(program_id); | ||
| 591 | delete_nca(data_id); | ||
| 592 | delete_nca(control_id); | ||
| 593 | delete_nca(html_id); | ||
| 594 | delete_nca(legal_id); | ||
| 595 | return true; | ||
| 596 | } | ||
| 597 | return false; | ||
| 598 | } | ||
| 599 | |||
| 600 | InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, | 554 | InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_exists, |
| 601 | const VfsCopyFunction& copy) { | 555 | const VfsCopyFunction& copy) { |
| 602 | const auto ncas = nsp.GetNCAsCollapsed(); | 556 | const auto ncas = nsp.GetNCAsCollapsed(); |
| @@ -666,25 +620,25 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex | |||
| 666 | 620 | ||
| 667 | InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, | 621 | InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, |
| 668 | bool overwrite_if_exists, const VfsCopyFunction& copy) { | 622 | bool overwrite_if_exists, const VfsCopyFunction& copy) { |
| 669 | CNMTHeader header{ | 623 | const CNMTHeader header{ |
| 670 | nca.GetTitleId(), // Title ID | 624 | .title_id = nca.GetTitleId(), |
| 671 | 0, // Ignore/Default title version | 625 | .title_version = 0, |
| 672 | type, // Type | 626 | .type = type, |
| 673 | {}, // Padding | 627 | .reserved = {}, |
| 674 | 0x10, // Default table offset | 628 | .table_offset = 0x10, |
| 675 | 1, // 1 Content Entry | 629 | .number_content_entries = 1, |
| 676 | 0, // No Meta Entries | 630 | .number_meta_entries = 0, |
| 677 | {}, // Padding | 631 | .attributes = 0, |
| 678 | {}, // Reserved 1 | 632 | .reserved2 = {}, |
| 679 | 0, // Is committed | 633 | .is_committed = 0, |
| 680 | 0, // Required download system version | 634 | .required_download_system_version = 0, |
| 681 | {}, // Reserved 2 | 635 | .reserved3 = {}, |
| 682 | }; | 636 | }; |
| 683 | OptionalHeader opt_header{0, 0}; | 637 | const OptionalHeader opt_header{0, 0}; |
| 684 | ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; | 638 | ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}}; |
| 685 | const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); | 639 | const auto& data = nca.GetBaseFile()->ReadBytes(0x100000); |
| 686 | mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); | 640 | mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0); |
| 687 | memcpy(&c_rec.nca_id, &c_rec.hash, 16); | 641 | std::memcpy(&c_rec.nca_id, &c_rec.hash, 16); |
| 688 | const CNMT new_cnmt(header, opt_header, {c_rec}, {}); | 642 | const CNMT new_cnmt(header, opt_header, {c_rec}, {}); |
| 689 | if (!RawInstallYuzuMeta(new_cnmt)) { | 643 | if (!RawInstallYuzuMeta(new_cnmt)) { |
| 690 | return InstallResult::ErrorMetaFailed; | 644 | return InstallResult::ErrorMetaFailed; |
| @@ -692,6 +646,57 @@ InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type, | |||
| 692 | return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); | 646 | return RawInstallNCA(nca, copy, overwrite_if_exists, c_rec.nca_id); |
| 693 | } | 647 | } |
| 694 | 648 | ||
| 649 | bool RegisteredCache::RemoveExistingEntry(u64 title_id) const { | ||
| 650 | const auto delete_nca = [this](const NcaID& id) { | ||
| 651 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 652 | |||
| 653 | const bool isFile = dir->GetFileRelative(path) != nullptr; | ||
| 654 | const bool isDir = dir->GetDirectoryRelative(path) != nullptr; | ||
| 655 | |||
| 656 | if (isFile) { | ||
| 657 | return dir->DeleteFile(path); | ||
| 658 | } else if (isDir) { | ||
| 659 | return dir->DeleteSubdirectoryRecursive(path); | ||
| 660 | } | ||
| 661 | |||
| 662 | return false; | ||
| 663 | }; | ||
| 664 | |||
| 665 | // If an entry exists in the registered cache, remove it | ||
| 666 | if (HasEntry(title_id, ContentRecordType::Meta)) { | ||
| 667 | LOG_INFO(Loader, | ||
| 668 | "Previously installed entry (v{}) for title_id={:016X} detected! " | ||
| 669 | "Attempting to remove...", | ||
| 670 | GetEntryVersion(title_id).value_or(0), title_id); | ||
| 671 | |||
| 672 | // Get all the ncas associated with the current CNMT and delete them | ||
| 673 | const auto meta_old_id = | ||
| 674 | GetNcaIDFromMetadata(title_id, ContentRecordType::Meta).value_or(NcaID{}); | ||
| 675 | const auto program_id = | ||
| 676 | GetNcaIDFromMetadata(title_id, ContentRecordType::Program).value_or(NcaID{}); | ||
| 677 | const auto data_id = | ||
| 678 | GetNcaIDFromMetadata(title_id, ContentRecordType::Data).value_or(NcaID{}); | ||
| 679 | const auto control_id = | ||
| 680 | GetNcaIDFromMetadata(title_id, ContentRecordType::Control).value_or(NcaID{}); | ||
| 681 | const auto html_id = | ||
| 682 | GetNcaIDFromMetadata(title_id, ContentRecordType::HtmlDocument).value_or(NcaID{}); | ||
| 683 | const auto legal_id = | ||
| 684 | GetNcaIDFromMetadata(title_id, ContentRecordType::LegalInformation).value_or(NcaID{}); | ||
| 685 | |||
| 686 | const auto deleted_meta = delete_nca(meta_old_id); | ||
| 687 | const auto deleted_program = delete_nca(program_id); | ||
| 688 | const auto deleted_data = delete_nca(data_id); | ||
| 689 | const auto deleted_control = delete_nca(control_id); | ||
| 690 | const auto deleted_html = delete_nca(html_id); | ||
| 691 | const auto deleted_legal = delete_nca(legal_id); | ||
| 692 | |||
| 693 | return deleted_meta && (deleted_meta || deleted_program || deleted_data || | ||
| 694 | deleted_control || deleted_html || deleted_legal); | ||
| 695 | } | ||
| 696 | |||
| 697 | return false; | ||
| 698 | } | ||
| 699 | |||
| 695 | InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, | 700 | InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy, |
| 696 | bool overwrite_if_exists, | 701 | bool overwrite_if_exists, |
| 697 | std::optional<NcaID> override_id) { | 702 | std::optional<NcaID> override_id) { |
| @@ -722,7 +727,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti | |||
| 722 | LOG_WARNING(Loader, "Overwriting existing NCA..."); | 727 | LOG_WARNING(Loader, "Overwriting existing NCA..."); |
| 723 | VirtualDir c_dir; | 728 | VirtualDir c_dir; |
| 724 | { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); } | 729 | { c_dir = dir->GetFileRelative(path)->GetContainingDirectory(); } |
| 725 | c_dir->DeleteFile(FileUtil::GetFilename(path)); | 730 | c_dir->DeleteFile(Common::FS::GetFilename(path)); |
| 726 | } | 731 | } |
| 727 | 732 | ||
| 728 | auto out = dir->CreateFileRelative(path); | 733 | auto out = dir->CreateFileRelative(path); |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 29cf0d40c..5b414b0f0 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -133,9 +133,9 @@ public: | |||
| 133 | // Parsing function defines the conversion from raw file to NCA. If there are other steps | 133 | // Parsing function defines the conversion from raw file to NCA. If there are other steps |
| 134 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom | 134 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom |
| 135 | // parsing function. | 135 | // parsing function. |
| 136 | explicit RegisteredCache(VirtualDir dir, | 136 | explicit RegisteredCache( |
| 137 | ContentProviderParsingFunction parsing_function = | 137 | VirtualDir dir, ContentProviderParsingFunction parsing_function = |
| 138 | [](const VirtualFile& file, const NcaID& id) { return file; }); | 138 | [](const VirtualFile& file, const NcaID& id) { return file; }); |
| 139 | ~RegisteredCache() override; | 139 | ~RegisteredCache() override; |
| 140 | 140 | ||
| 141 | void Refresh() override; | 141 | void Refresh() override; |
| @@ -155,9 +155,6 @@ public: | |||
| 155 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, | 155 | std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, |
| 156 | std::optional<u64> title_id = {}) const override; | 156 | std::optional<u64> title_id = {}) const override; |
| 157 | 157 | ||
| 158 | // Removes an existing entry based on title id | ||
| 159 | bool RemoveExistingEntry(u64 title_id); | ||
| 160 | |||
| 161 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure | 158 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure |
| 162 | // there is a meta NCA and all of them are accessible. | 159 | // there is a meta NCA and all of them are accessible. |
| 163 | InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, | 160 | InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false, |
| @@ -172,6 +169,9 @@ public: | |||
| 172 | InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, | 169 | InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false, |
| 173 | const VfsCopyFunction& copy = &VfsRawCopy); | 170 | const VfsCopyFunction& copy = &VfsRawCopy); |
| 174 | 171 | ||
| 172 | // Removes an existing entry based on title id | ||
| 173 | bool RemoveExistingEntry(u64 title_id) const; | ||
| 174 | |||
| 175 | private: | 175 | private: |
| 176 | template <typename T> | 176 | template <typename T> |
| 177 | void IterateAllMetadata(std::vector<T>& out, | 177 | void IterateAllMetadata(std::vector<T>& out, |
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index 2fd07ed04..82e683782 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include "core/file_sys/vfs.h" | 7 | #include "core/file_sys/vfs.h" |
| 9 | 8 | ||
| 10 | namespace FileSys { | 9 | namespace FileSys { |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index 418a39a7e..e967a254e 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | ||
| 10 | #include "core/file_sys/card_image.h" | 9 | #include "core/file_sys/card_image.h" |
| 11 | #include "core/file_sys/content_archive.h" | 10 | #include "core/file_sys/content_archive.h" |
| 12 | #include "core/file_sys/nca_metadata.h" | 11 | #include "core/file_sys/nca_metadata.h" |
| @@ -19,7 +18,9 @@ | |||
| 19 | 18 | ||
| 20 | namespace FileSys { | 19 | namespace FileSys { |
| 21 | 20 | ||
| 22 | RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { | 21 | RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider, |
| 22 | Service::FileSystem::FileSystemController& controller) | ||
| 23 | : content_provider{provider}, filesystem_controller{controller} { | ||
| 23 | // Load the RomFS from the app | 24 | // Load the RomFS from the app |
| 24 | if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { | 25 | if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) { |
| 25 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | 26 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
| @@ -46,39 +47,38 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_titl | |||
| 46 | 47 | ||
| 47 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, | 48 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, |
| 48 | ContentRecordType type) const { | 49 | ContentRecordType type) const { |
| 49 | std::shared_ptr<NCA> res; | 50 | const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type); |
| 50 | |||
| 51 | switch (storage) { | ||
| 52 | case StorageId::None: | ||
| 53 | res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); | ||
| 54 | break; | ||
| 55 | case StorageId::NandSystem: | ||
| 56 | res = | ||
| 57 | Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry( | ||
| 58 | title_id, type); | ||
| 59 | break; | ||
| 60 | case StorageId::NandUser: | ||
| 61 | res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry( | ||
| 62 | title_id, type); | ||
| 63 | break; | ||
| 64 | case StorageId::SdCard: | ||
| 65 | res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry( | ||
| 66 | title_id, type); | ||
| 67 | break; | ||
| 68 | default: | ||
| 69 | UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); | ||
| 70 | } | ||
| 71 | |||
| 72 | if (res == nullptr) { | 51 | if (res == nullptr) { |
| 73 | // TODO(DarkLordZach): Find the right error code to use here | 52 | // TODO(DarkLordZach): Find the right error code to use here |
| 74 | return RESULT_UNKNOWN; | 53 | return RESULT_UNKNOWN; |
| 75 | } | 54 | } |
| 55 | |||
| 76 | const auto romfs = res->GetRomFS(); | 56 | const auto romfs = res->GetRomFS(); |
| 77 | if (romfs == nullptr) { | 57 | if (romfs == nullptr) { |
| 78 | // TODO(DarkLordZach): Find the right error code to use here | 58 | // TODO(DarkLordZach): Find the right error code to use here |
| 79 | return RESULT_UNKNOWN; | 59 | return RESULT_UNKNOWN; |
| 80 | } | 60 | } |
| 61 | |||
| 81 | return MakeResult<VirtualFile>(romfs); | 62 | return MakeResult<VirtualFile>(romfs); |
| 82 | } | 63 | } |
| 83 | 64 | ||
| 65 | std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage, | ||
| 66 | ContentRecordType type) const { | ||
| 67 | switch (storage) { | ||
| 68 | case StorageId::None: | ||
| 69 | return content_provider.GetEntry(title_id, type); | ||
| 70 | case StorageId::NandSystem: | ||
| 71 | return filesystem_controller.GetSystemNANDContents()->GetEntry(title_id, type); | ||
| 72 | case StorageId::NandUser: | ||
| 73 | return filesystem_controller.GetUserNANDContents()->GetEntry(title_id, type); | ||
| 74 | case StorageId::SdCard: | ||
| 75 | return filesystem_controller.GetSDMCContents()->GetEntry(title_id, type); | ||
| 76 | case StorageId::Host: | ||
| 77 | case StorageId::GameCard: | ||
| 78 | default: | ||
| 79 | UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); | ||
| 80 | return nullptr; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | } // namespace FileSys | 84 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index c5d40285c..ec704dfa8 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -13,8 +13,15 @@ namespace Loader { | |||
| 13 | class AppLoader; | 13 | class AppLoader; |
| 14 | } // namespace Loader | 14 | } // namespace Loader |
| 15 | 15 | ||
| 16 | namespace Service::FileSystem { | ||
| 17 | class FileSystemController; | ||
| 18 | } | ||
| 19 | |||
| 16 | namespace FileSys { | 20 | namespace FileSys { |
| 17 | 21 | ||
| 22 | class ContentProvider; | ||
| 23 | class NCA; | ||
| 24 | |||
| 18 | enum class ContentRecordType : u8; | 25 | enum class ContentRecordType : u8; |
| 19 | 26 | ||
| 20 | enum class StorageId : u8 { | 27 | enum class StorageId : u8 { |
| @@ -29,18 +36,26 @@ enum class StorageId : u8 { | |||
| 29 | /// File system interface to the RomFS archive | 36 | /// File system interface to the RomFS archive |
| 30 | class RomFSFactory { | 37 | class RomFSFactory { |
| 31 | public: | 38 | public: |
| 32 | explicit RomFSFactory(Loader::AppLoader& app_loader); | 39 | explicit RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider, |
| 40 | Service::FileSystem::FileSystemController& controller); | ||
| 33 | ~RomFSFactory(); | 41 | ~RomFSFactory(); |
| 34 | 42 | ||
| 35 | void SetPackedUpdate(VirtualFile update_raw); | 43 | void SetPackedUpdate(VirtualFile update_raw); |
| 36 | ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; | 44 | [[nodiscard]] ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const; |
| 37 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; | 45 | [[nodiscard]] ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, |
| 46 | ContentRecordType type) const; | ||
| 38 | 47 | ||
| 39 | private: | 48 | private: |
| 49 | [[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage, | ||
| 50 | ContentRecordType type) const; | ||
| 51 | |||
| 40 | VirtualFile file; | 52 | VirtualFile file; |
| 41 | VirtualFile update_raw; | 53 | VirtualFile update_raw; |
| 42 | bool updatable; | 54 | bool updatable; |
| 43 | u64 ivfc_offset; | 55 | u64 ivfc_offset; |
| 56 | |||
| 57 | ContentProvider& content_provider; | ||
| 58 | Service::FileSystem::FileSystemController& filesystem_controller; | ||
| 44 | }; | 59 | }; |
| 45 | 60 | ||
| 46 | } // namespace FileSys | 61 | } // namespace FileSys |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index adfd2c1a4..ba4efee3a 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -17,23 +17,23 @@ constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; | |||
| 17 | 17 | ||
| 18 | namespace { | 18 | namespace { |
| 19 | 19 | ||
| 20 | void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { | 20 | void PrintSaveDataAttributeWarnings(SaveDataAttribute meta) { |
| 21 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 21 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 22 | if (meta.zero_1 != 0) { | 22 | if (meta.zero_1 != 0) { |
| 23 | LOG_WARNING(Service_FS, | 23 | LOG_WARNING(Service_FS, |
| 24 | "Possibly incorrect SaveDataDescriptor, type is " | 24 | "Possibly incorrect SaveDataAttribute, type is " |
| 25 | "SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).", | 25 | "SystemSaveData||SaveData but offset 0x28 is non-zero ({:016X}).", |
| 26 | meta.zero_1); | 26 | meta.zero_1); |
| 27 | } | 27 | } |
| 28 | if (meta.zero_2 != 0) { | 28 | if (meta.zero_2 != 0) { |
| 29 | LOG_WARNING(Service_FS, | 29 | LOG_WARNING(Service_FS, |
| 30 | "Possibly incorrect SaveDataDescriptor, type is " | 30 | "Possibly incorrect SaveDataAttribute, type is " |
| 31 | "SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).", | 31 | "SystemSaveData||SaveData but offset 0x30 is non-zero ({:016X}).", |
| 32 | meta.zero_2); | 32 | meta.zero_2); |
| 33 | } | 33 | } |
| 34 | if (meta.zero_3 != 0) { | 34 | if (meta.zero_3 != 0) { |
| 35 | LOG_WARNING(Service_FS, | 35 | LOG_WARNING(Service_FS, |
| 36 | "Possibly incorrect SaveDataDescriptor, type is " | 36 | "Possibly incorrect SaveDataAttribute, type is " |
| 37 | "SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).", | 37 | "SystemSaveData||SaveData but offset 0x38 is non-zero ({:016X}).", |
| 38 | meta.zero_3); | 38 | meta.zero_3); |
| 39 | } | 39 | } |
| @@ -41,33 +41,32 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { | |||
| 41 | 41 | ||
| 42 | if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) { | 42 | if (meta.type == SaveDataType::SystemSaveData && meta.title_id != 0) { |
| 43 | LOG_WARNING(Service_FS, | 43 | LOG_WARNING(Service_FS, |
| 44 | "Possibly incorrect SaveDataDescriptor, type is SystemSaveData but title_id is " | 44 | "Possibly incorrect SaveDataAttribute, type is SystemSaveData but title_id is " |
| 45 | "non-zero ({:016X}).", | 45 | "non-zero ({:016X}).", |
| 46 | meta.title_id); | 46 | meta.title_id); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { | 49 | if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) { |
| 50 | LOG_WARNING(Service_FS, | 50 | LOG_WARNING(Service_FS, |
| 51 | "Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is " | 51 | "Possibly incorrect SaveDataAttribute, type is DeviceSaveData but user_id is " |
| 52 | "non-zero ({:016X}{:016X})", | 52 | "non-zero ({:016X}{:016X})", |
| 53 | meta.user_id[1], meta.user_id[0]); | 53 | meta.user_id[1], meta.user_id[0]); |
| 54 | } | 54 | } |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) { | 57 | bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataAttribute& attr) { |
| 58 | return desc.type == SaveDataType::CacheStorage || desc.type == SaveDataType::TemporaryStorage || | 58 | return attr.type == SaveDataType::CacheStorage || attr.type == SaveDataType::TemporaryStorage || |
| 59 | (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User | 59 | (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User |
| 60 | (desc.type == SaveDataType::SaveData || desc.type == SaveDataType::DeviceSaveData) && | 60 | (attr.type == SaveDataType::SaveData || attr.type == SaveDataType::DeviceSaveData) && |
| 61 | desc.title_id == 0 && desc.save_id == 0); | 61 | attr.title_id == 0 && attr.save_id == 0); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | } // Anonymous namespace | 64 | } // Anonymous namespace |
| 65 | 65 | ||
| 66 | std::string SaveDataDescriptor::DebugInfo() const { | 66 | std::string SaveDataAttribute::DebugInfo() const { |
| 67 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, " | 67 | return fmt::format("[title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, type={:02X}, " |
| 68 | "save_id={:016X}, " | ||
| 69 | "rank={}, index={}]", | 68 | "rank={}, index={}]", |
| 70 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | 69 | title_id, user_id[1], user_id[0], save_id, static_cast<u8>(type), |
| 71 | static_cast<u8>(rank), index); | 70 | static_cast<u8>(rank), index); |
| 72 | } | 71 | } |
| 73 | 72 | ||
| @@ -80,8 +79,8 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save | |||
| 80 | SaveDataFactory::~SaveDataFactory() = default; | 79 | SaveDataFactory::~SaveDataFactory() = default; |
| 81 | 80 | ||
| 82 | ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, | 81 | ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, |
| 83 | const SaveDataDescriptor& meta) const { | 82 | const SaveDataAttribute& meta) const { |
| 84 | PrintSaveDataDescriptorWarnings(meta); | 83 | PrintSaveDataAttributeWarnings(meta); |
| 85 | 84 | ||
| 86 | const auto save_directory = | 85 | const auto save_directory = |
| 87 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | 86 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); |
| @@ -98,7 +97,7 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, | |||
| 98 | } | 97 | } |
| 99 | 98 | ||
| 100 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, | 99 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, |
| 101 | const SaveDataDescriptor& meta) const { | 100 | const SaveDataAttribute& meta) const { |
| 102 | 101 | ||
| 103 | const auto save_directory = | 102 | const auto save_directory = |
| 104 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | 103 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 991e57aa1..6625bbbd8 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -21,6 +21,7 @@ enum class SaveDataSpaceId : u8 { | |||
| 21 | TemporaryStorage = 3, | 21 | TemporaryStorage = 3, |
| 22 | SdCardUser = 4, | 22 | SdCardUser = 4, |
| 23 | ProperSystem = 100, | 23 | ProperSystem = 100, |
| 24 | SafeMode = 101, | ||
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| 26 | enum class SaveDataType : u8 { | 27 | enum class SaveDataType : u8 { |
| @@ -30,28 +31,50 @@ enum class SaveDataType : u8 { | |||
| 30 | DeviceSaveData = 3, | 31 | DeviceSaveData = 3, |
| 31 | TemporaryStorage = 4, | 32 | TemporaryStorage = 4, |
| 32 | CacheStorage = 5, | 33 | CacheStorage = 5, |
| 34 | SystemBcat = 6, | ||
| 33 | }; | 35 | }; |
| 34 | 36 | ||
| 35 | enum class SaveDataRank : u8 { | 37 | enum class SaveDataRank : u8 { |
| 36 | Primary, | 38 | Primary = 0, |
| 37 | Secondary, | 39 | Secondary = 1, |
| 38 | }; | 40 | }; |
| 39 | 41 | ||
| 40 | struct SaveDataDescriptor { | 42 | enum class SaveDataFlags : u32 { |
| 41 | u64_le title_id; | 43 | None = (0 << 0), |
| 44 | KeepAfterResettingSystemSaveData = (1 << 0), | ||
| 45 | KeepAfterRefurbishment = (1 << 1), | ||
| 46 | KeepAfterResettingSystemSaveDataWithoutUserSaveData = (1 << 2), | ||
| 47 | NeedsSecureDelete = (1 << 3), | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct SaveDataAttribute { | ||
| 51 | u64 title_id; | ||
| 42 | u128 user_id; | 52 | u128 user_id; |
| 43 | u64_le save_id; | 53 | u64 save_id; |
| 44 | SaveDataType type; | 54 | SaveDataType type; |
| 45 | SaveDataRank rank; | 55 | SaveDataRank rank; |
| 46 | u16_le index; | 56 | u16 index; |
| 47 | INSERT_PADDING_BYTES(4); | 57 | INSERT_PADDING_BYTES(4); |
| 48 | u64_le zero_1; | 58 | u64 zero_1; |
| 49 | u64_le zero_2; | 59 | u64 zero_2; |
| 50 | u64_le zero_3; | 60 | u64 zero_3; |
| 51 | 61 | ||
| 52 | std::string DebugInfo() const; | 62 | std::string DebugInfo() const; |
| 53 | }; | 63 | }; |
| 54 | static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorrect size."); | 64 | static_assert(sizeof(SaveDataAttribute) == 0x40, "SaveDataAttribute has incorrect size."); |
| 65 | |||
| 66 | struct SaveDataExtraData { | ||
| 67 | SaveDataAttribute attr; | ||
| 68 | u64 owner_id; | ||
| 69 | s64 timestamp; | ||
| 70 | SaveDataFlags flags; | ||
| 71 | INSERT_PADDING_BYTES(4); | ||
| 72 | s64 available_size; | ||
| 73 | s64 journal_size; | ||
| 74 | s64 commit_id; | ||
| 75 | std::array<u8, 0x190> unused; | ||
| 76 | }; | ||
| 77 | static_assert(sizeof(SaveDataExtraData) == 0x200, "SaveDataExtraData has incorrect size."); | ||
| 55 | 78 | ||
| 56 | struct SaveDataSize { | 79 | struct SaveDataSize { |
| 57 | u64 normal; | 80 | u64 normal; |
| @@ -64,8 +87,8 @@ public: | |||
| 64 | explicit SaveDataFactory(VirtualDir dir); | 87 | explicit SaveDataFactory(VirtualDir dir); |
| 65 | ~SaveDataFactory(); | 88 | ~SaveDataFactory(); |
| 66 | 89 | ||
| 67 | ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | 90 | ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataAttribute& meta) const; |
| 68 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | 91 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataAttribute& meta) const; |
| 69 | 92 | ||
| 70 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; | 93 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; |
| 71 | 94 | ||
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index 6f732e4d8..cb56d8f2d 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include "core/file_sys/registered_cache.h" | 6 | #include "core/file_sys/registered_cache.h" |
| 7 | #include "core/file_sys/sdmc_factory.h" | 7 | #include "core/file_sys/sdmc_factory.h" |
| 8 | #include "core/file_sys/vfs.h" | ||
| 8 | #include "core/file_sys/xts_archive.h" | 9 | #include "core/file_sys/xts_archive.h" |
| 9 | #include "core/settings.h" | ||
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index 42dc4e08a..2bb92ba93 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include "core/file_sys/vfs.h" | 8 | #include "core/file_sys/vfs_types.h" |
| 9 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index a6637fa39..90641d23b 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -22,7 +22,7 @@ namespace FileSys { | |||
| 22 | 22 | ||
| 23 | NSP::NSP(VirtualFile file_) | 23 | NSP::NSP(VirtualFile file_) |
| 24 | : file(std::move(file_)), status{Loader::ResultStatus::Success}, | 24 | : file(std::move(file_)), status{Loader::ResultStatus::Success}, |
| 25 | pfs(std::make_shared<PartitionFilesystem>(file)) { | 25 | pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { |
| 26 | if (pfs->GetStatus() != Loader::ResultStatus::Success) { | 26 | if (pfs->GetStatus() != Loader::ResultStatus::Success) { |
| 27 | status = pfs->GetStatus(); | 27 | status = pfs->GetStatus(); |
| 28 | return; | 28 | return; |
| @@ -264,9 +264,9 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | |||
| 264 | } | 264 | } |
| 265 | 265 | ||
| 266 | const CNMT cnmt(inner_file); | 266 | const CNMT cnmt(inner_file); |
| 267 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | ||
| 268 | 267 | ||
| 269 | ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca; | 268 | ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca; |
| 269 | |||
| 270 | for (const auto& rec : cnmt.GetContentRecords()) { | 270 | for (const auto& rec : cnmt.GetContentRecords()) { |
| 271 | const auto id_string = Common::HexToString(rec.nca_id, false); | 271 | const auto id_string = Common::HexToString(rec.nca_id, false); |
| 272 | auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | 272 | auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); |
| @@ -283,13 +283,32 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | |||
| 283 | } | 283 | } |
| 284 | 284 | ||
| 285 | auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); | 285 | auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); |
| 286 | |||
| 286 | if (next_nca->GetType() == NCAContentType::Program) { | 287 | if (next_nca->GetType() == NCAContentType::Program) { |
| 287 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | 288 | program_status[next_nca->GetTitleId()] = next_nca->GetStatus(); |
| 288 | } | 289 | } |
| 289 | if (next_nca->GetStatus() == Loader::ResultStatus::Success || | 290 | |
| 290 | (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS && | 291 | if (next_nca->GetStatus() != Loader::ResultStatus::Success && |
| 291 | (cnmt.GetTitleID() & 0x800) != 0)) { | 292 | next_nca->GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { |
| 292 | ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca); | 293 | continue; |
| 294 | } | ||
| 295 | |||
| 296 | // If the last 3 hexadecimal digits of the CNMT TitleID is 0x800 or is missing the | ||
| 297 | // BKTRBaseRomFS, this is an update NCA. Otherwise, this is a base NCA. | ||
| 298 | if ((cnmt.GetTitleID() & 0x800) != 0 || | ||
| 299 | next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 300 | // If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and | ||
| 301 | // 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular | ||
| 302 | // update NCA. | ||
| 303 | if ((next_nca->GetTitleId() & 0x7FF) != 0 && | ||
| 304 | (next_nca->GetTitleId() & 0x800) == 0) { | ||
| 305 | ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = | ||
| 306 | std::move(next_nca); | ||
| 307 | } else { | ||
| 308 | ncas[cnmt.GetTitleID()][{cnmt.GetType(), rec.type}] = std::move(next_nca); | ||
| 309 | } | ||
| 310 | } else { | ||
| 311 | ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca); | ||
| 293 | } | 312 | } |
| 294 | } | 313 | } |
| 295 | 314 | ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 6d54bd807..c70a11b5b 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -10,6 +10,10 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/vfs.h" | 11 | #include "core/file_sys/vfs.h" |
| 12 | 12 | ||
| 13 | namespace Core::Crypto { | ||
| 14 | class KeyManager; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace Loader { | 17 | namespace Loader { |
| 14 | enum class ResultStatus : u16; | 18 | enum class ResultStatus : u16; |
| 15 | } | 19 | } |
| @@ -74,7 +78,7 @@ private: | |||
| 74 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; | 78 | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; |
| 75 | std::vector<VirtualFile> ticket_files; | 79 | std::vector<VirtualFile> ticket_files; |
| 76 | 80 | ||
| 77 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); | 81 | Core::Crypto::KeyManager& keys; |
| 78 | 82 | ||
| 79 | VirtualFile romfs; | 83 | VirtualFile romfs; |
| 80 | VirtualDir exefs; | 84 | VirtualDir exefs; |
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp index 61bb67945..d65c7d234 100644 --- a/src/core/file_sys/system_archive/mii_model.cpp +++ b/src/core/file_sys/system_archive/mii_model.cpp | |||
| @@ -27,18 +27,12 @@ VirtualDir MiiModel() { | |||
| 27 | auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, | 27 | auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, |
| 28 | std::vector<VirtualDir>{}, "data"); | 28 | std::vector<VirtualDir>{}, "data"); |
| 29 | 29 | ||
| 30 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( | 30 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); |
| 31 | MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); | 31 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); |
| 32 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( | 32 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); |
| 33 | MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); | 33 | out->AddFile(MakeArrayFile(MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat")); |
| 34 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( | 34 | out->AddFile(MakeArrayFile(MiiModelData::SHAPE_HIGH, "ShapeHigh.dat")); |
| 35 | MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); | 35 | out->AddFile(MakeArrayFile(MiiModelData::SHAPE_MID, "ShapeMid.dat")); |
| 36 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>( | ||
| 37 | MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat")); | ||
| 38 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>( | ||
| 39 | MiiModelData::SHAPE_HIGH, "ShapeHigh.dat")); | ||
| 40 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>( | ||
| 41 | MiiModelData::SHAPE_MID, "ShapeMid.dat")); | ||
| 42 | 36 | ||
| 43 | return out; | 37 | return out; |
| 44 | } | 38 | } |
diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp index f4443784d..100d3c5db 100644 --- a/src/core/file_sys/system_archive/ng_word.cpp +++ b/src/core/file_sys/system_archive/ng_word.cpp | |||
| @@ -24,19 +24,18 @@ constexpr std::array<u8, 30> WORD_TXT{ | |||
| 24 | } // namespace NgWord1Data | 24 | } // namespace NgWord1Data |
| 25 | 25 | ||
| 26 | VirtualDir NgWord1() { | 26 | VirtualDir NgWord1() { |
| 27 | std::vector<VirtualFile> files(NgWord1Data::NUMBER_WORD_TXT_FILES); | 27 | std::vector<VirtualFile> files; |
| 28 | files.reserve(NgWord1Data::NUMBER_WORD_TXT_FILES); | ||
| 28 | 29 | ||
| 29 | for (std::size_t i = 0; i < files.size(); ++i) { | 30 | for (std::size_t i = 0; i < files.size(); ++i) { |
| 30 | files[i] = std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( | 31 | files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, fmt::format("{}.txt", i))); |
| 31 | NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)); | ||
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::WORD_TXT.size()>>( | 34 | files.push_back(MakeArrayFile(NgWord1Data::WORD_TXT, "common.txt")); |
| 35 | NgWord1Data::WORD_TXT, "common.txt")); | 35 | files.push_back(MakeArrayFile(NgWord1Data::VERSION_DAT, "version.dat")); |
| 36 | files.push_back(std::make_shared<ArrayVfsFile<NgWord1Data::VERSION_DAT.size()>>( | ||
| 37 | NgWord1Data::VERSION_DAT, "version.dat")); | ||
| 38 | 36 | ||
| 39 | return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); | 37 | return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{}, |
| 38 | "data"); | ||
| 40 | } | 39 | } |
| 41 | 40 | ||
| 42 | namespace NgWord2Data { | 41 | namespace NgWord2Data { |
| @@ -55,27 +54,22 @@ constexpr std::array<u8, 0x2C> AC_NX_DATA{ | |||
| 55 | } // namespace NgWord2Data | 54 | } // namespace NgWord2Data |
| 56 | 55 | ||
| 57 | VirtualDir NgWord2() { | 56 | VirtualDir NgWord2() { |
| 58 | std::vector<VirtualFile> files(NgWord2Data::NUMBER_AC_NX_FILES * 3); | 57 | std::vector<VirtualFile> files; |
| 58 | files.reserve(NgWord2Data::NUMBER_AC_NX_FILES * 3); | ||
| 59 | 59 | ||
| 60 | for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { | 60 | for (std::size_t i = 0; i < NgWord2Data::NUMBER_AC_NX_FILES; ++i) { |
| 61 | files[3 * i] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 61 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i))); |
| 62 | NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b1_nx", i)); | 62 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i))); |
| 63 | files[3 * i + 1] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 63 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i))); |
| 64 | NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_b2_nx", i)); | ||
| 65 | files[3 * i + 2] = std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | ||
| 66 | NgWord2Data::AC_NX_DATA, fmt::format("ac_{}_not_b_nx", i)); | ||
| 67 | } | 64 | } |
| 68 | 65 | ||
| 69 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 66 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); |
| 70 | NgWord2Data::AC_NX_DATA, "ac_common_b1_nx")); | 67 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); |
| 71 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | 68 | files.push_back(MakeArrayFile(NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx")); |
| 72 | NgWord2Data::AC_NX_DATA, "ac_common_b2_nx")); | 69 | files.push_back(MakeArrayFile(NgWord2Data::VERSION_DAT, "version.dat")); |
| 73 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::AC_NX_DATA.size()>>( | ||
| 74 | NgWord2Data::AC_NX_DATA, "ac_common_not_b_nx")); | ||
| 75 | files.push_back(std::make_shared<ArrayVfsFile<NgWord2Data::VERSION_DAT.size()>>( | ||
| 76 | NgWord2Data::VERSION_DAT, "version.dat")); | ||
| 77 | 70 | ||
| 78 | return std::make_shared<VectorVfsDirectory>(files, std::vector<VirtualDir>{}, "data"); | 71 | return std::make_shared<VectorVfsDirectory>(std::move(files), std::vector<VirtualDir>{}, |
| 72 | "data"); | ||
| 79 | } | 73 | } |
| 80 | 74 | ||
| 81 | } // namespace FileSys::SystemArchive | 75 | } // namespace FileSys::SystemArchive |
diff --git a/src/core/file_sys/system_archive/time_zone_binary.cpp b/src/core/file_sys/system_archive/time_zone_binary.cpp index 9806bd197..8fd005012 100644 --- a/src/core/file_sys/system_archive/time_zone_binary.cpp +++ b/src/core/file_sys/system_archive/time_zone_binary.cpp | |||
| @@ -2,6 +2,9 @@ | |||
| 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 <array> | ||
| 6 | #include <vector> | ||
| 7 | |||
| 5 | #include "common/swap.h" | 8 | #include "common/swap.h" |
| 6 | #include "core/file_sys/system_archive/time_zone_binary.h" | 9 | #include "core/file_sys/system_archive/time_zone_binary.h" |
| 7 | #include "core/file_sys/vfs_vector.h" | 10 | #include "core/file_sys/vfs_vector.h" |
| @@ -615,43 +618,49 @@ static constexpr std::array<u8, 9633> LOCATION_NAMES{ | |||
| 615 | 0x0a}; | 618 | 0x0a}; |
| 616 | 619 | ||
| 617 | static VirtualFile GenerateDefaultTimeZoneFile() { | 620 | static VirtualFile GenerateDefaultTimeZoneFile() { |
| 618 | struct { | 621 | struct TimeZoneInfo { |
| 619 | s64_be at; | 622 | s64_be at; |
| 620 | INSERT_PADDING_BYTES(7); | 623 | std::array<u8, 7> padding1; |
| 621 | std::array<char, 4> time_zone_chars; | 624 | std::array<char, 4> time_zone_chars; |
| 622 | INSERT_PADDING_BYTES(2); | 625 | std::array<u8, 2> padding2; |
| 623 | std::array<char, 6> time_zone_name; | 626 | std::array<char, 6> time_zone_name; |
| 624 | } time_zone_info{}; | 627 | }; |
| 625 | 628 | ||
| 626 | const VirtualFile file{std::make_shared<VectorVfsFile>( | 629 | VirtualFile file{std::make_shared<VectorVfsFile>( |
| 627 | std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(time_zone_info)), | 630 | std::vector<u8>(sizeof(Service::Time::TimeZone::TzifHeader) + sizeof(TimeZoneInfo)), |
| 628 | "GMT")}; | 631 | "GMT")}; |
| 629 | 632 | ||
| 630 | Service::Time::TimeZone::TzifHeader header{}; | 633 | const Service::Time::TimeZone::TzifHeader header{ |
| 631 | header.magic = 0x545a6966; | 634 | .magic = 0x545a6966, |
| 632 | header.version = 0x32; | 635 | .version = 0x32, |
| 633 | header.ttis_gmt_count = 0x1; | 636 | .ttis_gmt_count = 1, |
| 634 | header.ttis_std_count = 0x1; | 637 | .ttis_std_count = 1, |
| 635 | header.time_count = 0x1; | 638 | .time_count = 1, |
| 636 | header.type_count = 0x1; | 639 | .type_count = 1, |
| 637 | header.char_count = 0x4; | 640 | .char_count = 4, |
| 641 | }; | ||
| 638 | file->WriteObject(header, 0); | 642 | file->WriteObject(header, 0); |
| 639 | 643 | ||
| 640 | time_zone_info.at = 0xf8; | 644 | const TimeZoneInfo time_zone_info{ |
| 641 | time_zone_info.time_zone_chars = {'G', 'M', 'T', '\0'}; | 645 | .at = 0xf8, |
| 642 | time_zone_info.time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'}; | 646 | .padding1 = {}, |
| 647 | .time_zone_chars = {'G', 'M', 'T', '\0'}, | ||
| 648 | .padding2 = {}, | ||
| 649 | .time_zone_name = {'\n', 'G', 'M', 'T', '0', '\n'}, | ||
| 650 | }; | ||
| 643 | file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader)); | 651 | file->WriteObject(time_zone_info, sizeof(Service::Time::TimeZone::TzifHeader)); |
| 644 | 652 | ||
| 645 | return file; | 653 | return file; |
| 646 | } | 654 | } |
| 647 | 655 | ||
| 648 | VirtualDir TimeZoneBinary() { | 656 | VirtualDir TimeZoneBinary() { |
| 649 | const std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( | 657 | std::vector<VirtualDir> root_dirs{std::make_shared<VectorVfsDirectory>( |
| 650 | std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, | 658 | std::vector<VirtualFile>{GenerateDefaultTimeZoneFile()}, std::vector<VirtualDir>{}, |
| 651 | "zoneinfo")}; | 659 | "zoneinfo")}; |
| 652 | const std::vector<VirtualFile> root_files{ | 660 | std::vector<VirtualFile> root_files{MakeArrayFile(LOCATION_NAMES, "binaryList.txt")}; |
| 653 | std::make_shared<ArrayVfsFile<LOCATION_NAMES.size()>>(LOCATION_NAMES, "binaryList.txt")}; | 661 | |
| 654 | return std::make_shared<VectorVfsDirectory>(root_files, root_dirs, "data"); | 662 | return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(root_dirs), |
| 663 | "data"); | ||
| 655 | } | 664 | } |
| 656 | 665 | ||
| 657 | } // namespace FileSys::SystemArchive | 666 | } // namespace FileSys::SystemArchive |
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index e33327ef0..b2f026b6d 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -30,7 +30,7 @@ bool VfsFilesystem::IsWritable() const { | |||
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { | 32 | VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { |
| 33 | const auto path = FileUtil::SanitizePath(path_); | 33 | const auto path = Common::FS::SanitizePath(path_); |
| 34 | if (root->GetFileRelative(path) != nullptr) | 34 | if (root->GetFileRelative(path) != nullptr) |
| 35 | return VfsEntryType::File; | 35 | return VfsEntryType::File; |
| 36 | if (root->GetDirectoryRelative(path) != nullptr) | 36 | if (root->GetDirectoryRelative(path) != nullptr) |
| @@ -40,22 +40,22 @@ VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const { | |||
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | 42 | VirtualFile VfsFilesystem::OpenFile(std::string_view path_, Mode perms) { |
| 43 | const auto path = FileUtil::SanitizePath(path_); | 43 | const auto path = Common::FS::SanitizePath(path_); |
| 44 | return root->GetFileRelative(path); | 44 | return root->GetFileRelative(path); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { | 47 | VirtualFile VfsFilesystem::CreateFile(std::string_view path_, Mode perms) { |
| 48 | const auto path = FileUtil::SanitizePath(path_); | 48 | const auto path = Common::FS::SanitizePath(path_); |
| 49 | return root->CreateFileRelative(path); | 49 | return root->CreateFileRelative(path); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { | 52 | VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { |
| 53 | const auto old_path = FileUtil::SanitizePath(old_path_); | 53 | const auto old_path = Common::FS::SanitizePath(old_path_); |
| 54 | const auto new_path = FileUtil::SanitizePath(new_path_); | 54 | const auto new_path = Common::FS::SanitizePath(new_path_); |
| 55 | 55 | ||
| 56 | // VfsDirectory impls are only required to implement copy across the current directory. | 56 | // VfsDirectory impls are only required to implement copy across the current directory. |
| 57 | if (FileUtil::GetParentPath(old_path) == FileUtil::GetParentPath(new_path)) { | 57 | if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) { |
| 58 | if (!root->Copy(FileUtil::GetFilename(old_path), FileUtil::GetFilename(new_path))) | 58 | if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path))) |
| 59 | return nullptr; | 59 | return nullptr; |
| 60 | return OpenFile(new_path, Mode::ReadWrite); | 60 | return OpenFile(new_path, Mode::ReadWrite); |
| 61 | } | 61 | } |
| @@ -76,8 +76,8 @@ VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view | |||
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { | 78 | VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) { |
| 79 | const auto sanitized_old_path = FileUtil::SanitizePath(old_path); | 79 | const auto sanitized_old_path = Common::FS::SanitizePath(old_path); |
| 80 | const auto sanitized_new_path = FileUtil::SanitizePath(new_path); | 80 | const auto sanitized_new_path = Common::FS::SanitizePath(new_path); |
| 81 | 81 | ||
| 82 | // Again, non-default impls are highly encouraged to provide a more optimized version of this. | 82 | // Again, non-default impls are highly encouraged to provide a more optimized version of this. |
| 83 | auto out = CopyFile(sanitized_old_path, sanitized_new_path); | 83 | auto out = CopyFile(sanitized_old_path, sanitized_new_path); |
| @@ -89,26 +89,26 @@ VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | bool VfsFilesystem::DeleteFile(std::string_view path_) { | 91 | bool VfsFilesystem::DeleteFile(std::string_view path_) { |
| 92 | const auto path = FileUtil::SanitizePath(path_); | 92 | const auto path = Common::FS::SanitizePath(path_); |
| 93 | auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); | 93 | auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); |
| 94 | if (parent == nullptr) | 94 | if (parent == nullptr) |
| 95 | return false; | 95 | return false; |
| 96 | return parent->DeleteFile(FileUtil::GetFilename(path)); | 96 | return parent->DeleteFile(Common::FS::GetFilename(path)); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { | 99 | VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { |
| 100 | const auto path = FileUtil::SanitizePath(path_); | 100 | const auto path = Common::FS::SanitizePath(path_); |
| 101 | return root->GetDirectoryRelative(path); | 101 | return root->GetDirectoryRelative(path); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { | 104 | VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { |
| 105 | const auto path = FileUtil::SanitizePath(path_); | 105 | const auto path = Common::FS::SanitizePath(path_); |
| 106 | return root->CreateDirectoryRelative(path); | 106 | return root->CreateDirectoryRelative(path); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { | 109 | VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) { |
| 110 | const auto old_path = FileUtil::SanitizePath(old_path_); | 110 | const auto old_path = Common::FS::SanitizePath(old_path_); |
| 111 | const auto new_path = FileUtil::SanitizePath(new_path_); | 111 | const auto new_path = Common::FS::SanitizePath(new_path_); |
| 112 | 112 | ||
| 113 | // Non-default impls are highly encouraged to provide a more optimized version of this. | 113 | // Non-default impls are highly encouraged to provide a more optimized version of this. |
| 114 | auto old_dir = OpenDirectory(old_path, Mode::Read); | 114 | auto old_dir = OpenDirectory(old_path, Mode::Read); |
| @@ -139,8 +139,8 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_ | |||
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { | 141 | VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) { |
| 142 | const auto sanitized_old_path = FileUtil::SanitizePath(old_path); | 142 | const auto sanitized_old_path = Common::FS::SanitizePath(old_path); |
| 143 | const auto sanitized_new_path = FileUtil::SanitizePath(new_path); | 143 | const auto sanitized_new_path = Common::FS::SanitizePath(new_path); |
| 144 | 144 | ||
| 145 | // Non-default impls are highly encouraged to provide a more optimized version of this. | 145 | // Non-default impls are highly encouraged to provide a more optimized version of this. |
| 146 | auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); | 146 | auto out = CopyDirectory(sanitized_old_path, sanitized_new_path); |
| @@ -152,28 +152,29 @@ VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_v | |||
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | bool VfsFilesystem::DeleteDirectory(std::string_view path_) { | 154 | bool VfsFilesystem::DeleteDirectory(std::string_view path_) { |
| 155 | const auto path = FileUtil::SanitizePath(path_); | 155 | const auto path = Common::FS::SanitizePath(path_); |
| 156 | auto parent = OpenDirectory(FileUtil::GetParentPath(path), Mode::Write); | 156 | auto parent = OpenDirectory(Common::FS::GetParentPath(path), Mode::Write); |
| 157 | if (parent == nullptr) | 157 | if (parent == nullptr) |
| 158 | return false; | 158 | return false; |
| 159 | return parent->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path)); | 159 | return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path)); |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | VfsFile::~VfsFile() = default; | 162 | VfsFile::~VfsFile() = default; |
| 163 | 163 | ||
| 164 | std::string VfsFile::GetExtension() const { | 164 | std::string VfsFile::GetExtension() const { |
| 165 | return std::string(FileUtil::GetExtensionFromFilename(GetName())); | 165 | return std::string(Common::FS::GetExtensionFromFilename(GetName())); |
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | VfsDirectory::~VfsDirectory() = default; | 168 | VfsDirectory::~VfsDirectory() = default; |
| 169 | 169 | ||
| 170 | std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { | 170 | std::optional<u8> VfsFile::ReadByte(std::size_t offset) const { |
| 171 | u8 out{}; | 171 | u8 out{}; |
| 172 | std::size_t size = Read(&out, 1, offset); | 172 | const std::size_t size = Read(&out, sizeof(u8), offset); |
| 173 | if (size == 1) | 173 | if (size == 1) { |
| 174 | return out; | 174 | return out; |
| 175 | } | ||
| 175 | 176 | ||
| 176 | return {}; | 177 | return std::nullopt; |
| 177 | } | 178 | } |
| 178 | 179 | ||
| 179 | std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { | 180 | std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { |
| @@ -203,7 +204,7 @@ std::string VfsFile::GetFullPath() const { | |||
| 203 | } | 204 | } |
| 204 | 205 | ||
| 205 | std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { | 206 | std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) const { |
| 206 | auto vec = FileUtil::SplitPathComponents(path); | 207 | auto vec = Common::FS::SplitPathComponents(path); |
| 207 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | 208 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), |
| 208 | vec.end()); | 209 | vec.end()); |
| 209 | if (vec.empty()) { | 210 | if (vec.empty()) { |
| @@ -239,7 +240,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(std::string_view path) co | |||
| 239 | } | 240 | } |
| 240 | 241 | ||
| 241 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const { | 242 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_view path) const { |
| 242 | auto vec = FileUtil::SplitPathComponents(path); | 243 | auto vec = Common::FS::SplitPathComponents(path); |
| 243 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | 244 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), |
| 244 | vec.end()); | 245 | vec.end()); |
| 245 | if (vec.empty()) { | 246 | if (vec.empty()) { |
| @@ -301,7 +302,7 @@ std::size_t VfsDirectory::GetSize() const { | |||
| 301 | } | 302 | } |
| 302 | 303 | ||
| 303 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) { | 304 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) { |
| 304 | auto vec = FileUtil::SplitPathComponents(path); | 305 | auto vec = Common::FS::SplitPathComponents(path); |
| 305 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | 306 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), |
| 306 | vec.end()); | 307 | vec.end()); |
| 307 | if (vec.empty()) { | 308 | if (vec.empty()) { |
| @@ -320,7 +321,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(std::string_view path) | |||
| 320 | } | 321 | } |
| 321 | } | 322 | } |
| 322 | 323 | ||
| 323 | return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); | 324 | return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path)); |
| 324 | } | 325 | } |
| 325 | 326 | ||
| 326 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) { | 327 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) { |
| @@ -332,7 +333,7 @@ std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(std::string_view path) | |||
| 332 | } | 333 | } |
| 333 | 334 | ||
| 334 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) { | 335 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_view path) { |
| 335 | auto vec = FileUtil::SplitPathComponents(path); | 336 | auto vec = Common::FS::SplitPathComponents(path); |
| 336 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | 337 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), |
| 337 | vec.end()); | 338 | vec.end()); |
| 338 | if (vec.empty()) { | 339 | if (vec.empty()) { |
| @@ -351,7 +352,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(std::string_ | |||
| 351 | } | 352 | } |
| 352 | } | 353 | } |
| 353 | 354 | ||
| 354 | return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); | 355 | return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path)); |
| 355 | } | 356 | } |
| 356 | 357 | ||
| 357 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { | 358 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(std::string_view path) { |
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp index d69952940..429d7bc8b 100644 --- a/src/core/file_sys/vfs_libzip.cpp +++ b/src/core/file_sys/vfs_libzip.cpp | |||
| @@ -49,7 +49,7 @@ VirtualDir ExtractZIP(VirtualFile file) { | |||
| 49 | if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size())) | 49 | if (zip_fread(file2.get(), buf.data(), buf.size()) != s64(buf.size())) |
| 50 | return nullptr; | 50 | return nullptr; |
| 51 | 51 | ||
| 52 | const auto parts = FileUtil::SplitPathComponents(stat.name); | 52 | const auto parts = Common::FS::SplitPathComponents(stat.name); |
| 53 | const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); | 53 | const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back()); |
| 54 | 54 | ||
| 55 | std::shared_ptr<VectorVfsDirectory> dtrv = out; | 55 | std::shared_ptr<VectorVfsDirectory> dtrv = out; |
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index c96f88488..7714d3de5 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp | |||
| @@ -58,10 +58,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { | 60 | std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { |
| 61 | if (r_offset < size) | 61 | if (r_offset >= size) { |
| 62 | return file->ReadByte(offset + r_offset); | 62 | return std::nullopt; |
| 63 | } | ||
| 63 | 64 | ||
| 64 | return {}; | 65 | return file->ReadByte(offset + r_offset); |
| 65 | } | 66 | } |
| 66 | 67 | ||
| 67 | std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { | 68 | std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 96ce5957c..488687ba9 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -14,24 +14,28 @@ | |||
| 14 | 14 | ||
| 15 | namespace FileSys { | 15 | namespace FileSys { |
| 16 | 16 | ||
| 17 | namespace FS = Common::FS; | ||
| 18 | |||
| 17 | static std::string ModeFlagsToString(Mode mode) { | 19 | static std::string ModeFlagsToString(Mode mode) { |
| 18 | std::string mode_str; | 20 | std::string mode_str; |
| 19 | 21 | ||
| 20 | // Calculate the correct open mode for the file. | 22 | // Calculate the correct open mode for the file. |
| 21 | if (mode & Mode::Read && mode & Mode::Write) { | 23 | if (True(mode & Mode::Read) && True(mode & Mode::Write)) { |
| 22 | if (mode & Mode::Append) | 24 | if (True(mode & Mode::Append)) { |
| 23 | mode_str = "a+"; | 25 | mode_str = "a+"; |
| 24 | else | 26 | } else { |
| 25 | mode_str = "r+"; | 27 | mode_str = "r+"; |
| 28 | } | ||
| 26 | } else { | 29 | } else { |
| 27 | if (mode & Mode::Read) | 30 | if (True(mode & Mode::Read)) { |
| 28 | mode_str = "r"; | 31 | mode_str = "r"; |
| 29 | else if (mode & Mode::Append) | 32 | } else if (True(mode & Mode::Append)) { |
| 30 | mode_str = "a"; | 33 | mode_str = "a"; |
| 31 | else if (mode & Mode::Write) | 34 | } else if (True(mode & Mode::Write)) { |
| 32 | mode_str = "w"; | 35 | mode_str = "w"; |
| 33 | else | 36 | } else { |
| 34 | UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); | 37 | UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); |
| 38 | } | ||
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | mode_str += "b"; | 41 | mode_str += "b"; |
| @@ -55,78 +59,82 @@ bool RealVfsFilesystem::IsWritable() const { | |||
| 55 | } | 59 | } |
| 56 | 60 | ||
| 57 | VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { | 61 | VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { |
| 58 | const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | 62 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 59 | if (!FileUtil::Exists(path)) | 63 | if (!FS::Exists(path)) { |
| 60 | return VfsEntryType::None; | 64 | return VfsEntryType::None; |
| 61 | if (FileUtil::IsDirectory(path)) | 65 | } |
| 66 | if (FS::IsDirectory(path)) { | ||
| 62 | return VfsEntryType::Directory; | 67 | return VfsEntryType::Directory; |
| 68 | } | ||
| 63 | 69 | ||
| 64 | return VfsEntryType::File; | 70 | return VfsEntryType::File; |
| 65 | } | 71 | } |
| 66 | 72 | ||
| 67 | VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | 73 | VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { |
| 68 | const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | 74 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 69 | if (cache.find(path) != cache.end()) { | 75 | |
| 70 | auto weak = cache[path]; | 76 | if (const auto weak_iter = cache.find(path); weak_iter != cache.cend()) { |
| 77 | const auto& weak = weak_iter->second; | ||
| 78 | |||
| 71 | if (!weak.expired()) { | 79 | if (!weak.expired()) { |
| 72 | return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); | 80 | return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, weak.lock(), path, perms)); |
| 73 | } | 81 | } |
| 74 | } | 82 | } |
| 75 | 83 | ||
| 76 | if (!FileUtil::Exists(path) && (perms & Mode::WriteAppend) != 0) | 84 | if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) { |
| 77 | FileUtil::CreateEmptyFile(path); | 85 | FS::CreateEmptyFile(path); |
| 86 | } | ||
| 78 | 87 | ||
| 79 | auto backing = std::make_shared<FileUtil::IOFile>(path, ModeFlagsToString(perms).c_str()); | 88 | auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str()); |
| 80 | cache[path] = backing; | 89 | cache.insert_or_assign(path, backing); |
| 81 | 90 | ||
| 82 | // Cannot use make_shared as RealVfsFile constructor is private | 91 | // Cannot use make_shared as RealVfsFile constructor is private |
| 83 | return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); | 92 | return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); |
| 84 | } | 93 | } |
| 85 | 94 | ||
| 86 | VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { | 95 | VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { |
| 87 | const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | 96 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 88 | const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); | 97 | const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash); |
| 89 | if (!FileUtil::Exists(path)) { | 98 | if (!FS::Exists(path)) { |
| 90 | FileUtil::CreateFullPath(path_fwd); | 99 | FS::CreateFullPath(path_fwd); |
| 91 | if (!FileUtil::CreateEmptyFile(path)) | 100 | if (!FS::CreateEmptyFile(path)) { |
| 92 | return nullptr; | 101 | return nullptr; |
| 102 | } | ||
| 93 | } | 103 | } |
| 94 | return OpenFile(path, perms); | 104 | return OpenFile(path, perms); |
| 95 | } | 105 | } |
| 96 | 106 | ||
| 97 | VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { | 107 | VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { |
| 98 | const auto old_path = | 108 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); |
| 99 | FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | 109 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); |
| 100 | const auto new_path = | ||
| 101 | FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||
| 102 | 110 | ||
| 103 | if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | 111 | if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) || |
| 104 | FileUtil::IsDirectory(old_path) || !FileUtil::Copy(old_path, new_path)) | 112 | !FS::Copy(old_path, new_path)) { |
| 105 | return nullptr; | 113 | return nullptr; |
| 114 | } | ||
| 106 | return OpenFile(new_path, Mode::ReadWrite); | 115 | return OpenFile(new_path, Mode::ReadWrite); |
| 107 | } | 116 | } |
| 108 | 117 | ||
| 109 | VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { | 118 | VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { |
| 110 | const auto old_path = | 119 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); |
| 111 | FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | 120 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); |
| 112 | const auto new_path = | 121 | const auto cached_file_iter = cache.find(old_path); |
| 113 | FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||
| 114 | 122 | ||
| 115 | if (cache.find(old_path) != cache.end()) { | 123 | if (cached_file_iter != cache.cend()) { |
| 116 | auto file = cache[old_path].lock(); | 124 | auto file = cached_file_iter->second.lock(); |
| 117 | 125 | ||
| 118 | if (!cache[old_path].expired()) { | 126 | if (!cached_file_iter->second.expired()) { |
| 119 | file->Close(); | 127 | file->Close(); |
| 120 | } | 128 | } |
| 121 | 129 | ||
| 122 | if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | 130 | if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) || |
| 123 | FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) { | 131 | !FS::Rename(old_path, new_path)) { |
| 124 | return nullptr; | 132 | return nullptr; |
| 125 | } | 133 | } |
| 126 | 134 | ||
| 127 | cache.erase(old_path); | 135 | cache.erase(old_path); |
| 128 | file->Open(new_path, "r+b"); | 136 | file->Open(new_path, "r+b"); |
| 129 | cache[new_path] = file; | 137 | cache.insert_or_assign(new_path, std::move(file)); |
| 130 | } else { | 138 | } else { |
| 131 | UNREACHABLE(); | 139 | UNREACHABLE(); |
| 132 | return nullptr; | 140 | return nullptr; |
| @@ -136,28 +144,33 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ | |||
| 136 | } | 144 | } |
| 137 | 145 | ||
| 138 | bool RealVfsFilesystem::DeleteFile(std::string_view path_) { | 146 | bool RealVfsFilesystem::DeleteFile(std::string_view path_) { |
| 139 | const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | 147 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 140 | if (cache.find(path) != cache.end()) { | 148 | const auto cached_iter = cache.find(path); |
| 141 | if (!cache[path].expired()) | 149 | |
| 142 | cache[path].lock()->Close(); | 150 | if (cached_iter != cache.cend()) { |
| 151 | if (!cached_iter->second.expired()) { | ||
| 152 | cached_iter->second.lock()->Close(); | ||
| 153 | } | ||
| 143 | cache.erase(path); | 154 | cache.erase(path); |
| 144 | } | 155 | } |
| 145 | return FileUtil::Delete(path); | 156 | |
| 157 | return FS::Delete(path); | ||
| 146 | } | 158 | } |
| 147 | 159 | ||
| 148 | VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { | 160 | VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { |
| 149 | const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | 161 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 150 | // Cannot use make_shared as RealVfsDirectory constructor is private | 162 | // Cannot use make_shared as RealVfsDirectory constructor is private |
| 151 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); | 163 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); |
| 152 | } | 164 | } |
| 153 | 165 | ||
| 154 | VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { | 166 | VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { |
| 155 | const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | 167 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 156 | const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); | 168 | const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash); |
| 157 | if (!FileUtil::Exists(path)) { | 169 | if (!FS::Exists(path)) { |
| 158 | FileUtil::CreateFullPath(path_fwd); | 170 | FS::CreateFullPath(path_fwd); |
| 159 | if (!FileUtil::CreateDir(path)) | 171 | if (!FS::CreateDir(path)) { |
| 160 | return nullptr; | 172 | return nullptr; |
| 173 | } | ||
| 161 | } | 174 | } |
| 162 | // Cannot use make_shared as RealVfsDirectory constructor is private | 175 | // Cannot use make_shared as RealVfsDirectory constructor is private |
| 163 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); | 176 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); |
| @@ -165,67 +178,75 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms | |||
| 165 | 178 | ||
| 166 | VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, | 179 | VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, |
| 167 | std::string_view new_path_) { | 180 | std::string_view new_path_) { |
| 168 | const auto old_path = | 181 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); |
| 169 | FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | 182 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); |
| 170 | const auto new_path = | 183 | if (!FS::Exists(old_path) || FS::Exists(new_path) || !FS::IsDirectory(old_path)) { |
| 171 | FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | ||
| 172 | if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | ||
| 173 | !FileUtil::IsDirectory(old_path)) | ||
| 174 | return nullptr; | 184 | return nullptr; |
| 175 | FileUtil::CopyDir(old_path, new_path); | 185 | } |
| 186 | FS::CopyDir(old_path, new_path); | ||
| 176 | return OpenDirectory(new_path, Mode::ReadWrite); | 187 | return OpenDirectory(new_path, Mode::ReadWrite); |
| 177 | } | 188 | } |
| 178 | 189 | ||
| 179 | VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, | 190 | VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, |
| 180 | std::string_view new_path_) { | 191 | std::string_view new_path_) { |
| 181 | const auto old_path = | 192 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); |
| 182 | FileUtil::SanitizePath(old_path_, FileUtil::DirectorySeparator::PlatformDefault); | 193 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); |
| 183 | const auto new_path = | 194 | |
| 184 | FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | 195 | if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) || |
| 185 | if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | 196 | !FS::Rename(old_path, new_path)) { |
| 186 | FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) | ||
| 187 | return nullptr; | 197 | return nullptr; |
| 198 | } | ||
| 188 | 199 | ||
| 189 | for (auto& kv : cache) { | 200 | for (auto& kv : cache) { |
| 190 | // Path in cache starts with old_path | 201 | // If the path in the cache doesn't start with old_path, then bail on this file. |
| 191 | if (kv.first.rfind(old_path, 0) == 0) { | 202 | if (kv.first.rfind(old_path, 0) != 0) { |
| 192 | const auto file_old_path = | 203 | continue; |
| 193 | FileUtil::SanitizePath(kv.first, FileUtil::DirectorySeparator::PlatformDefault); | 204 | } |
| 194 | const auto file_new_path = | 205 | |
| 195 | FileUtil::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), | 206 | const auto file_old_path = |
| 196 | FileUtil::DirectorySeparator::PlatformDefault); | 207 | FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); |
| 197 | auto cached = cache[file_old_path]; | 208 | auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), |
| 198 | if (!cached.expired()) { | 209 | FS::DirectorySeparator::PlatformDefault); |
| 199 | auto file = cached.lock(); | 210 | const auto& cached = cache[file_old_path]; |
| 200 | file->Open(file_new_path, "r+b"); | 211 | |
| 201 | cache.erase(file_old_path); | 212 | if (cached.expired()) { |
| 202 | cache[file_new_path] = file; | 213 | continue; |
| 203 | } | ||
| 204 | } | 214 | } |
| 215 | |||
| 216 | auto file = cached.lock(); | ||
| 217 | file->Open(file_new_path, "r+b"); | ||
| 218 | cache.erase(file_old_path); | ||
| 219 | cache.insert_or_assign(std::move(file_new_path), std::move(file)); | ||
| 205 | } | 220 | } |
| 206 | 221 | ||
| 207 | return OpenDirectory(new_path, Mode::ReadWrite); | 222 | return OpenDirectory(new_path, Mode::ReadWrite); |
| 208 | } | 223 | } |
| 209 | 224 | ||
| 210 | bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { | 225 | bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { |
| 211 | const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); | 226 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 227 | |||
| 212 | for (auto& kv : cache) { | 228 | for (auto& kv : cache) { |
| 213 | // Path in cache starts with old_path | 229 | // If the path in the cache doesn't start with path, then bail on this file. |
| 214 | if (kv.first.rfind(path, 0) == 0) { | 230 | if (kv.first.rfind(path, 0) != 0) { |
| 215 | if (!cache[kv.first].expired()) | 231 | continue; |
| 216 | cache[kv.first].lock()->Close(); | 232 | } |
| 217 | cache.erase(kv.first); | 233 | |
| 234 | const auto& entry = cache[kv.first]; | ||
| 235 | if (!entry.expired()) { | ||
| 236 | entry.lock()->Close(); | ||
| 218 | } | 237 | } |
| 238 | |||
| 239 | cache.erase(kv.first); | ||
| 219 | } | 240 | } |
| 220 | return FileUtil::DeleteDirRecursively(path); | 241 | |
| 242 | return FS::DeleteDirRecursively(path); | ||
| 221 | } | 243 | } |
| 222 | 244 | ||
| 223 | RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOFile> backing_, | 245 | RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_, |
| 224 | const std::string& path_, Mode perms_) | 246 | const std::string& path_, Mode perms_) |
| 225 | : base(base_), backing(std::move(backing_)), path(path_), | 247 | : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)), |
| 226 | parent_path(FileUtil::GetParentPath(path_)), | 248 | path_components(FS::SplitPathComponents(path_)), |
| 227 | path_components(FileUtil::SplitPathComponents(path_)), | 249 | parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)), |
| 228 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 229 | perms(perms_) {} | 250 | perms(perms_) {} |
| 230 | 251 | ||
| 231 | RealVfsFile::~RealVfsFile() = default; | 252 | RealVfsFile::~RealVfsFile() = default; |
| @@ -247,22 +268,24 @@ std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { | |||
| 247 | } | 268 | } |
| 248 | 269 | ||
| 249 | bool RealVfsFile::IsWritable() const { | 270 | bool RealVfsFile::IsWritable() const { |
| 250 | return (perms & Mode::WriteAppend) != 0; | 271 | return True(perms & Mode::WriteAppend); |
| 251 | } | 272 | } |
| 252 | 273 | ||
| 253 | bool RealVfsFile::IsReadable() const { | 274 | bool RealVfsFile::IsReadable() const { |
| 254 | return (perms & Mode::ReadWrite) != 0; | 275 | return True(perms & Mode::ReadWrite); |
| 255 | } | 276 | } |
| 256 | 277 | ||
| 257 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { | 278 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 258 | if (!backing->Seek(offset, SEEK_SET)) | 279 | if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { |
| 259 | return 0; | 280 | return 0; |
| 281 | } | ||
| 260 | return backing->ReadBytes(data, length); | 282 | return backing->ReadBytes(data, length); |
| 261 | } | 283 | } |
| 262 | 284 | ||
| 263 | std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { | 285 | std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 264 | if (!backing->Seek(offset, SEEK_SET)) | 286 | if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { |
| 265 | return 0; | 287 | return 0; |
| 288 | } | ||
| 266 | return backing->WriteBytes(data, length); | 289 | return backing->WriteBytes(data, length); |
| 267 | } | 290 | } |
| 268 | 291 | ||
| @@ -279,16 +302,18 @@ bool RealVfsFile::Close() { | |||
| 279 | 302 | ||
| 280 | template <> | 303 | template <> |
| 281 | std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { | 304 | std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const { |
| 282 | if (perms == Mode::Append) | 305 | if (perms == Mode::Append) { |
| 283 | return {}; | 306 | return {}; |
| 307 | } | ||
| 284 | 308 | ||
| 285 | std::vector<VirtualFile> out; | 309 | std::vector<VirtualFile> out; |
| 286 | FileUtil::ForeachDirectoryEntry( | 310 | FS::ForeachDirectoryEntry( |
| 287 | nullptr, path, | 311 | nullptr, path, |
| 288 | [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { | 312 | [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { |
| 289 | const std::string full_path = directory + DIR_SEP + filename; | 313 | const std::string full_path = directory + DIR_SEP + filename; |
| 290 | if (!FileUtil::IsDirectory(full_path)) | 314 | if (!FS::IsDirectory(full_path)) { |
| 291 | out.emplace_back(base.OpenFile(full_path, perms)); | 315 | out.emplace_back(base.OpenFile(full_path, perms)); |
| 316 | } | ||
| 292 | return true; | 317 | return true; |
| 293 | }); | 318 | }); |
| 294 | 319 | ||
| @@ -297,16 +322,18 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>( | |||
| 297 | 322 | ||
| 298 | template <> | 323 | template <> |
| 299 | std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { | 324 | std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const { |
| 300 | if (perms == Mode::Append) | 325 | if (perms == Mode::Append) { |
| 301 | return {}; | 326 | return {}; |
| 327 | } | ||
| 302 | 328 | ||
| 303 | std::vector<VirtualDir> out; | 329 | std::vector<VirtualDir> out; |
| 304 | FileUtil::ForeachDirectoryEntry( | 330 | FS::ForeachDirectoryEntry( |
| 305 | nullptr, path, | 331 | nullptr, path, |
| 306 | [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { | 332 | [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { |
| 307 | const std::string full_path = directory + DIR_SEP + filename; | 333 | const std::string full_path = directory + DIR_SEP + filename; |
| 308 | if (FileUtil::IsDirectory(full_path)) | 334 | if (FS::IsDirectory(full_path)) { |
| 309 | out.emplace_back(base.OpenDirectory(full_path, perms)); | 335 | out.emplace_back(base.OpenDirectory(full_path, perms)); |
| 336 | } | ||
| 310 | return true; | 337 | return true; |
| 311 | }); | 338 | }); |
| 312 | 339 | ||
| @@ -314,28 +341,30 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi | |||
| 314 | } | 341 | } |
| 315 | 342 | ||
| 316 | RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) | 343 | RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) |
| 317 | : base(base_), path(FileUtil::RemoveTrailingSlash(path_)), | 344 | : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), |
| 318 | parent_path(FileUtil::GetParentPath(path)), | 345 | path_components(FS::SplitPathComponents(path)), |
| 319 | path_components(FileUtil::SplitPathComponents(path)), | 346 | parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)), |
| 320 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 321 | perms(perms_) { | 347 | perms(perms_) { |
| 322 | if (!FileUtil::Exists(path) && perms & Mode::WriteAppend) | 348 | if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) { |
| 323 | FileUtil::CreateDir(path); | 349 | FS::CreateDir(path); |
| 350 | } | ||
| 324 | } | 351 | } |
| 325 | 352 | ||
| 326 | RealVfsDirectory::~RealVfsDirectory() = default; | 353 | RealVfsDirectory::~RealVfsDirectory() = default; |
| 327 | 354 | ||
| 328 | std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { | 355 | std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const { |
| 329 | const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | 356 | const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path)); |
| 330 | if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path)) | 357 | if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) { |
| 331 | return nullptr; | 358 | return nullptr; |
| 359 | } | ||
| 332 | return base.OpenFile(full_path, perms); | 360 | return base.OpenFile(full_path, perms); |
| 333 | } | 361 | } |
| 334 | 362 | ||
| 335 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { | 363 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetDirectoryRelative(std::string_view path) const { |
| 336 | const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | 364 | const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path)); |
| 337 | if (!FileUtil::Exists(full_path) || !FileUtil::IsDirectory(full_path)) | 365 | if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) { |
| 338 | return nullptr; | 366 | return nullptr; |
| 367 | } | ||
| 339 | return base.OpenDirectory(full_path, perms); | 368 | return base.OpenDirectory(full_path, perms); |
| 340 | } | 369 | } |
| 341 | 370 | ||
| @@ -348,17 +377,17 @@ std::shared_ptr<VfsDirectory> RealVfsDirectory::GetSubdirectory(std::string_view | |||
| 348 | } | 377 | } |
| 349 | 378 | ||
| 350 | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { | 379 | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFileRelative(std::string_view path) { |
| 351 | const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | 380 | const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path)); |
| 352 | return base.CreateFile(full_path, perms); | 381 | return base.CreateFile(full_path, perms); |
| 353 | } | 382 | } |
| 354 | 383 | ||
| 355 | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { | 384 | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateDirectoryRelative(std::string_view path) { |
| 356 | const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path)); | 385 | const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(path)); |
| 357 | return base.CreateDirectory(full_path, perms); | 386 | return base.CreateDirectory(full_path, perms); |
| 358 | } | 387 | } |
| 359 | 388 | ||
| 360 | bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { | 389 | bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { |
| 361 | auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(name)); | 390 | const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name)); |
| 362 | return base.DeleteDirectory(full_path); | 391 | return base.DeleteDirectory(full_path); |
| 363 | } | 392 | } |
| 364 | 393 | ||
| @@ -371,11 +400,11 @@ std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() | |||
| 371 | } | 400 | } |
| 372 | 401 | ||
| 373 | bool RealVfsDirectory::IsWritable() const { | 402 | bool RealVfsDirectory::IsWritable() const { |
| 374 | return (perms & Mode::WriteAppend) != 0; | 403 | return True(perms & Mode::WriteAppend); |
| 375 | } | 404 | } |
| 376 | 405 | ||
| 377 | bool RealVfsDirectory::IsReadable() const { | 406 | bool RealVfsDirectory::IsReadable() const { |
| 378 | return (perms & Mode::ReadWrite) != 0; | 407 | return True(perms & Mode::ReadWrite); |
| 379 | } | 408 | } |
| 380 | 409 | ||
| 381 | std::string RealVfsDirectory::GetName() const { | 410 | std::string RealVfsDirectory::GetName() const { |
| @@ -383,8 +412,9 @@ std::string RealVfsDirectory::GetName() const { | |||
| 383 | } | 412 | } |
| 384 | 413 | ||
| 385 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { | 414 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { |
| 386 | if (path_components.size() <= 1) | 415 | if (path_components.size() <= 1) { |
| 387 | return nullptr; | 416 | return nullptr; |
| 417 | } | ||
| 388 | 418 | ||
| 389 | return base.OpenDirectory(parent_path, perms); | 419 | return base.OpenDirectory(parent_path, perms); |
| 390 | } | 420 | } |
| @@ -421,16 +451,17 @@ std::string RealVfsDirectory::GetFullPath() const { | |||
| 421 | } | 451 | } |
| 422 | 452 | ||
| 423 | std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { | 453 | std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { |
| 424 | if (perms == Mode::Append) | 454 | if (perms == Mode::Append) { |
| 425 | return {}; | 455 | return {}; |
| 456 | } | ||
| 426 | 457 | ||
| 427 | std::map<std::string, VfsEntryType, std::less<>> out; | 458 | std::map<std::string, VfsEntryType, std::less<>> out; |
| 428 | FileUtil::ForeachDirectoryEntry( | 459 | FS::ForeachDirectoryEntry( |
| 429 | nullptr, path, | 460 | nullptr, path, |
| 430 | [&out](u64* entries_out, const std::string& directory, const std::string& filename) { | 461 | [&out](u64* entries_out, const std::string& directory, const std::string& filename) { |
| 431 | const std::string full_path = directory + DIR_SEP + filename; | 462 | const std::string full_path = directory + DIR_SEP + filename; |
| 432 | out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory | 463 | out.emplace(filename, |
| 433 | : VfsEntryType::File); | 464 | FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File); |
| 434 | return true; | 465 | return true; |
| 435 | }); | 466 | }); |
| 436 | 467 | ||
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index a0a857a31..0b537b22c 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include "core/file_sys/mode.h" | 9 | #include "core/file_sys/mode.h" |
| 10 | #include "core/file_sys/vfs.h" | 10 | #include "core/file_sys/vfs.h" |
| 11 | 11 | ||
| 12 | namespace FileUtil { | 12 | namespace Common::FS { |
| 13 | class IOFile; | 13 | class IOFile; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| @@ -36,7 +36,7 @@ public: | |||
| 36 | bool DeleteDirectory(std::string_view path) override; | 36 | bool DeleteDirectory(std::string_view path) override; |
| 37 | 37 | ||
| 38 | private: | 38 | private: |
| 39 | boost::container::flat_map<std::string, std::weak_ptr<FileUtil::IOFile>> cache; | 39 | boost::container::flat_map<std::string, std::weak_ptr<Common::FS::IOFile>> cache; |
| 40 | }; | 40 | }; |
| 41 | 41 | ||
| 42 | // An implmentation of VfsFile that represents a file on the user's computer. | 42 | // An implmentation of VfsFile that represents a file on the user's computer. |
| @@ -58,13 +58,13 @@ public: | |||
| 58 | bool Rename(std::string_view name) override; | 58 | bool Rename(std::string_view name) override; |
| 59 | 59 | ||
| 60 | private: | 60 | private: |
| 61 | RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing, | 61 | RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing, |
| 62 | const std::string& path, Mode perms = Mode::Read); | 62 | const std::string& path, Mode perms = Mode::Read); |
| 63 | 63 | ||
| 64 | bool Close(); | 64 | bool Close(); |
| 65 | 65 | ||
| 66 | RealVfsFilesystem& base; | 66 | RealVfsFilesystem& base; |
| 67 | std::shared_ptr<FileUtil::IOFile> backing; | 67 | std::shared_ptr<Common::FS::IOFile> backing; |
| 68 | std::string path; | 68 | std::string path; |
| 69 | std::string parent_path; | 69 | std::string parent_path; |
| 70 | std::vector<std::string> path_components; | 70 | std::vector<std::string> path_components; |
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h index 9f5a90b1b..8b27c30fa 100644 --- a/src/core/file_sys/vfs_static.h +++ b/src/core/file_sys/vfs_static.h | |||
| @@ -54,9 +54,11 @@ public: | |||
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | std::optional<u8> ReadByte(std::size_t offset) const override { | 56 | std::optional<u8> ReadByte(std::size_t offset) const override { |
| 57 | if (offset < size) | 57 | if (offset >= size) { |
| 58 | return value; | 58 | return std::nullopt; |
| 59 | return {}; | 59 | } |
| 60 | |||
| 61 | return value; | ||
| 60 | } | 62 | } |
| 61 | 63 | ||
| 62 | std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { | 64 | std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { |
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index ac36cb2ee..95d3da2f2 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h | |||
| @@ -4,7 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <cstring> | 8 | #include <cstring> |
| 9 | #include <memory> | ||
| 10 | #include <string> | ||
| 11 | #include <vector> | ||
| 8 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 9 | 13 | ||
| 10 | namespace FileSys { | 14 | namespace FileSys { |
| @@ -13,7 +17,8 @@ namespace FileSys { | |||
| 13 | template <std::size_t size> | 17 | template <std::size_t size> |
| 14 | class ArrayVfsFile : public VfsFile { | 18 | class ArrayVfsFile : public VfsFile { |
| 15 | public: | 19 | public: |
| 16 | ArrayVfsFile(std::array<u8, size> data, std::string name = "", VirtualDir parent = nullptr) | 20 | explicit ArrayVfsFile(const std::array<u8, size>& data, std::string name = "", |
| 21 | VirtualDir parent = nullptr) | ||
| 17 | : data(data), name(std::move(name)), parent(std::move(parent)) {} | 22 | : data(data), name(std::move(name)), parent(std::move(parent)) {} |
| 18 | 23 | ||
| 19 | std::string GetName() const override { | 24 | std::string GetName() const override { |
| @@ -61,6 +66,12 @@ private: | |||
| 61 | VirtualDir parent; | 66 | VirtualDir parent; |
| 62 | }; | 67 | }; |
| 63 | 68 | ||
| 69 | template <std::size_t Size, typename... Args> | ||
| 70 | std::shared_ptr<ArrayVfsFile<Size>> MakeArrayFile(const std::array<u8, Size>& data, | ||
| 71 | Args&&... args) { | ||
| 72 | return std::make_shared<ArrayVfsFile<Size>>(data, std::forward<Args>(args)...); | ||
| 73 | } | ||
| 74 | |||
| 64 | // An implementation of VfsFile that is backed by a vector optionally supplied upon construction | 75 | // An implementation of VfsFile that is backed by a vector optionally supplied upon construction |
| 65 | class VectorVfsFile : public VfsFile { | 76 | class VectorVfsFile : public VfsFile { |
| 66 | public: | 77 | public: |
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 86e06ccb9..24c58e7ae 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp | |||
| @@ -15,8 +15,9 @@ | |||
| 15 | #include "common/hex_util.h" | 15 | #include "common/hex_util.h" |
| 16 | #include "common/string_util.h" | 16 | #include "common/string_util.h" |
| 17 | #include "core/crypto/aes_util.h" | 17 | #include "core/crypto/aes_util.h" |
| 18 | #include "core/crypto/key_manager.h" | ||
| 18 | #include "core/crypto/xts_encryption_layer.h" | 19 | #include "core/crypto/xts_encryption_layer.h" |
| 19 | #include "core/file_sys/partition_filesystem.h" | 20 | #include "core/file_sys/content_archive.h" |
| 20 | #include "core/file_sys/vfs_offset.h" | 21 | #include "core/file_sys/vfs_offset.h" |
| 21 | #include "core/file_sys/xts_archive.h" | 22 | #include "core/file_sys/xts_archive.h" |
| 22 | #include "core/loader/loader.h" | 23 | #include "core/loader/loader.h" |
| @@ -43,8 +44,10 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t | |||
| 43 | return true; | 44 | return true; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { | 47 | NAX::NAX(VirtualFile file_) |
| 47 | std::string path = FileUtil::SanitizePath(file->GetFullPath()); | 48 | : header(std::make_unique<NAXHeader>()), |
| 49 | file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { | ||
| 50 | std::string path = Common::FS::SanitizePath(file->GetFullPath()); | ||
| 48 | static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", | 51 | static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", |
| 49 | std::regex_constants::ECMAScript | | 52 | std::regex_constants::ECMAScript | |
| 50 | std::regex_constants::icase); | 53 | std::regex_constants::icase); |
| @@ -60,7 +63,8 @@ NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::m | |||
| 60 | } | 63 | } |
| 61 | 64 | ||
| 62 | NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) | 65 | NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) |
| 63 | : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { | 66 | : header(std::make_unique<NAXHeader>()), |
| 67 | file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { | ||
| 64 | Core::Crypto::SHA256Hash hash{}; | 68 | Core::Crypto::SHA256Hash hash{}; |
| 65 | mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); | 69 | mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0); |
| 66 | status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], | 70 | status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], |
| @@ -70,14 +74,18 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) | |||
| 70 | NAX::~NAX() = default; | 74 | NAX::~NAX() = default; |
| 71 | 75 | ||
| 72 | Loader::ResultStatus NAX::Parse(std::string_view path) { | 76 | Loader::ResultStatus NAX::Parse(std::string_view path) { |
| 73 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) | 77 | if (file == nullptr) { |
| 78 | return Loader::ResultStatus::ErrorNullFile; | ||
| 79 | } | ||
| 80 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) { | ||
| 74 | return Loader::ResultStatus::ErrorBadNAXHeader; | 81 | return Loader::ResultStatus::ErrorBadNAXHeader; |
| 75 | 82 | } | |
| 76 | if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) | 83 | if (header->magic != Common::MakeMagic('N', 'A', 'X', '0')) { |
| 77 | return Loader::ResultStatus::ErrorBadNAXHeader; | 84 | return Loader::ResultStatus::ErrorBadNAXHeader; |
| 78 | 85 | } | |
| 79 | if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) | 86 | if (file->GetSize() < NAX_HEADER_PADDING_SIZE + header->file_size) { |
| 80 | return Loader::ResultStatus::ErrorIncorrectNAXFileSize; | 87 | return Loader::ResultStatus::ErrorIncorrectNAXFileSize; |
| 88 | } | ||
| 81 | 89 | ||
| 82 | keys.DeriveSDSeedLazy(); | 90 | keys.DeriveSDSeedLazy(); |
| 83 | std::array<Core::Crypto::Key256, 2> sd_keys{}; | 91 | std::array<Core::Crypto::Key256, 2> sd_keys{}; |
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 563531bb6..c472e226e 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h | |||
| @@ -9,12 +9,16 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 11 | #include "core/crypto/key_manager.h" | 11 | #include "core/crypto/key_manager.h" |
| 12 | #include "core/file_sys/content_archive.h" | ||
| 13 | #include "core/file_sys/vfs.h" | 12 | #include "core/file_sys/vfs.h" |
| 14 | #include "core/loader/loader.h" | 13 | |
| 14 | namespace Loader { | ||
| 15 | enum class ResultStatus : u16; | ||
| 16 | } | ||
| 15 | 17 | ||
| 16 | namespace FileSys { | 18 | namespace FileSys { |
| 17 | 19 | ||
| 20 | class NCA; | ||
| 21 | |||
| 18 | struct NAXHeader { | 22 | struct NAXHeader { |
| 19 | std::array<u8, 0x20> hmac; | 23 | std::array<u8, 0x20> hmac; |
| 20 | u64_le magic; | 24 | u64_le magic; |
| @@ -62,6 +66,6 @@ private: | |||
| 62 | 66 | ||
| 63 | VirtualFile dec_file; | 67 | VirtualFile dec_file; |
| 64 | 68 | ||
| 65 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); | 69 | Core::Crypto::KeyManager& keys; |
| 66 | }; | 70 | }; |
| 67 | } // namespace FileSys | 71 | } // namespace FileSys |
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp new file mode 100644 index 000000000..c5d65f2d0 --- /dev/null +++ b/src/core/frontend/applets/controller.cpp | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // Copyright 2020 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/frontend/applets/controller.h" | ||
| 8 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 9 | #include "core/hle/service/hid/hid.h" | ||
| 10 | #include "core/hle/service/sm/sm.h" | ||
| 11 | |||
| 12 | namespace Core::Frontend { | ||
| 13 | |||
| 14 | ControllerApplet::~ControllerApplet() = default; | ||
| 15 | |||
| 16 | DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_) | ||
| 17 | : service_manager{service_manager_} {} | ||
| 18 | |||
| 19 | DefaultControllerApplet::~DefaultControllerApplet() = default; | ||
| 20 | |||
| 21 | void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback, | ||
| 22 | ControllerParameters parameters) const { | ||
| 23 | LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!"); | ||
| 24 | |||
| 25 | auto& npad = | ||
| 26 | service_manager.GetService<Service::HID::Hid>("hid") | ||
| 27 | ->GetAppletResource() | ||
| 28 | ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad); | ||
| 29 | |||
| 30 | auto& players = Settings::values.players; | ||
| 31 | |||
| 32 | const std::size_t min_supported_players = | ||
| 33 | parameters.enable_single_mode ? 1 : parameters.min_players; | ||
| 34 | |||
| 35 | // Disconnect Handheld first. | ||
| 36 | npad.DisconnectNPadAtIndex(8); | ||
| 37 | |||
| 38 | // Deduce the best configuration based on the input parameters. | ||
| 39 | for (std::size_t index = 0; index < players.size() - 2; ++index) { | ||
| 40 | // First, disconnect all controllers regardless of the value of keep_controllers_connected. | ||
| 41 | // This makes it easy to connect the desired controllers. | ||
| 42 | npad.DisconnectNPadAtIndex(index); | ||
| 43 | |||
| 44 | // Only connect the minimum number of required players. | ||
| 45 | if (index >= min_supported_players) { | ||
| 46 | continue; | ||
| 47 | } | ||
| 48 | |||
| 49 | // Connect controllers based on the following priority list from highest to lowest priority: | ||
| 50 | // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld | ||
| 51 | if (parameters.allow_pro_controller) { | ||
| 52 | npad.AddNewControllerAt( | ||
| 53 | npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index); | ||
| 54 | } else if (parameters.allow_dual_joycons) { | ||
| 55 | npad.AddNewControllerAt( | ||
| 56 | npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index); | ||
| 57 | } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) { | ||
| 58 | // Assign left joycons to even player indices and right joycons to odd player indices. | ||
| 59 | // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and | ||
| 60 | // a right Joycon for Player 2 in 2 Player Assist mode. | ||
| 61 | if (index % 2 == 0) { | ||
| 62 | npad.AddNewControllerAt( | ||
| 63 | npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index); | ||
| 64 | } else { | ||
| 65 | npad.AddNewControllerAt( | ||
| 66 | npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index); | ||
| 67 | } | ||
| 68 | } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld && | ||
| 69 | !Settings::values.use_docked_mode) { | ||
| 70 | // We should *never* reach here under any normal circumstances. | ||
| 71 | npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld), | ||
| 72 | index); | ||
| 73 | } else { | ||
| 74 | UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!"); | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | callback(); | ||
| 79 | } | ||
| 80 | |||
| 81 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h new file mode 100644 index 000000000..3e49cdbb9 --- /dev/null +++ b/src/core/frontend/applets/controller.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | // Copyright 2020 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 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Service::SM { | ||
| 12 | class ServiceManager; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Core::Frontend { | ||
| 16 | |||
| 17 | using BorderColor = std::array<u8, 4>; | ||
| 18 | using ExplainText = std::array<char, 0x81>; | ||
| 19 | |||
| 20 | struct ControllerParameters { | ||
| 21 | s8 min_players{}; | ||
| 22 | s8 max_players{}; | ||
| 23 | bool keep_controllers_connected{}; | ||
| 24 | bool enable_single_mode{}; | ||
| 25 | bool enable_border_color{}; | ||
| 26 | std::vector<BorderColor> border_colors{}; | ||
| 27 | bool enable_explain_text{}; | ||
| 28 | std::vector<ExplainText> explain_text{}; | ||
| 29 | bool allow_pro_controller{}; | ||
| 30 | bool allow_handheld{}; | ||
| 31 | bool allow_dual_joycons{}; | ||
| 32 | bool allow_left_joycon{}; | ||
| 33 | bool allow_right_joycon{}; | ||
| 34 | }; | ||
| 35 | |||
| 36 | class ControllerApplet { | ||
| 37 | public: | ||
| 38 | virtual ~ControllerApplet(); | ||
| 39 | |||
| 40 | virtual void ReconfigureControllers(std::function<void()> callback, | ||
| 41 | ControllerParameters parameters) const = 0; | ||
| 42 | }; | ||
| 43 | |||
| 44 | class DefaultControllerApplet final : public ControllerApplet { | ||
| 45 | public: | ||
| 46 | explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_); | ||
| 47 | ~DefaultControllerApplet() override; | ||
| 48 | |||
| 49 | void ReconfigureControllers(std::function<void()> callback, | ||
| 50 | ControllerParameters parameters) const override; | ||
| 51 | |||
| 52 | private: | ||
| 53 | Service::SM::ServiceManager& service_manager; | ||
| 54 | }; | ||
| 55 | |||
| 56 | } // namespace Core::Frontend | ||
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 13aa14934..3e8780243 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h | |||
| @@ -39,7 +39,7 @@ public: | |||
| 39 | 39 | ||
| 40 | class Scoped { | 40 | class Scoped { |
| 41 | public: | 41 | public: |
| 42 | explicit Scoped(GraphicsContext& context_) : context(context_) { | 42 | [[nodiscard]] explicit Scoped(GraphicsContext& context_) : context(context_) { |
| 43 | context.MakeCurrent(); | 43 | context.MakeCurrent(); |
| 44 | } | 44 | } |
| 45 | ~Scoped() { | 45 | ~Scoped() { |
| @@ -52,7 +52,7 @@ public: | |||
| 52 | 52 | ||
| 53 | /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value | 53 | /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value |
| 54 | /// ends | 54 | /// ends |
| 55 | Scoped Acquire() { | 55 | [[nodiscard]] Scoped Acquire() { |
| 56 | return Scoped{*this}; | 56 | return Scoped{*this}; |
| 57 | } | 57 | } |
| 58 | }; | 58 | }; |
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 91ecc30ab..e2e3bbbb3 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | ||
| 7 | #include "common/math_util.h" | 8 | #include "common/math_util.h" |
| 8 | 9 | ||
| 9 | namespace Layout { | 10 | namespace Layout { |
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 2b098b7c6..277b70e53 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h | |||
| @@ -33,6 +33,9 @@ public: | |||
| 33 | virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { | 33 | virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const { |
| 34 | return {}; | 34 | return {}; |
| 35 | } | 35 | } |
| 36 | virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const { | ||
| 37 | return {}; | ||
| 38 | } | ||
| 36 | }; | 39 | }; |
| 37 | 40 | ||
| 38 | /// An abstract class template for a factory that can create input devices. | 41 | /// An abstract class template for a factory that can create input devices. |
| @@ -119,11 +122,11 @@ using ButtonDevice = InputDevice<bool>; | |||
| 119 | using AnalogDevice = InputDevice<std::tuple<float, float>>; | 122 | using AnalogDevice = InputDevice<std::tuple<float, float>>; |
| 120 | 123 | ||
| 121 | /** | 124 | /** |
| 122 | * A motion device is an input device that returns a tuple of accelerometer state vector and | 125 | * A motion status is an object that returns a tuple of accelerometer state vector, |
| 123 | * gyroscope state vector. | 126 | * gyroscope state vector, rotation state vector and orientation state matrix. |
| 124 | * | 127 | * |
| 125 | * For both vectors: | 128 | * For both vectors: |
| 126 | * x+ is the same direction as LEFT on D-pad. | 129 | * x+ is the same direction as RIGHT on D-pad. |
| 127 | * y+ is normal to the touch screen, pointing outward. | 130 | * y+ is normal to the touch screen, pointing outward. |
| 128 | * z+ is the same direction as UP on D-pad. | 131 | * z+ is the same direction as UP on D-pad. |
| 129 | * | 132 | * |
| @@ -133,8 +136,22 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>; | |||
| 133 | * For gyroscope state vector: | 136 | * For gyroscope state vector: |
| 134 | * Orientation is determined by right-hand rule. | 137 | * Orientation is determined by right-hand rule. |
| 135 | * Units: deg/sec | 138 | * Units: deg/sec |
| 139 | * | ||
| 140 | * For rotation state vector | ||
| 141 | * Units: rotations | ||
| 142 | * | ||
| 143 | * For orientation state matrix | ||
| 144 | * x vector | ||
| 145 | * y vector | ||
| 146 | * z vector | ||
| 147 | */ | ||
| 148 | using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>, | ||
| 149 | std::array<Common::Vec3f, 3>>; | ||
| 150 | |||
| 151 | /** | ||
| 152 | * A motion device is an input device that returns a motion status object | ||
| 136 | */ | 153 | */ |
| 137 | using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>; | 154 | using MotionDevice = InputDevice<MotionStatus>; |
| 138 | 155 | ||
| 139 | /** | 156 | /** |
| 140 | * A touch device is an input device that returns a tuple of two floats and a bool. The floats are | 157 | * A touch device is an input device that returns a tuple of two floats and a bool. The floats are |
diff --git a/src/core/hardware_interrupt_manager.cpp b/src/core/hardware_interrupt_manager.cpp index c629d9fa1..645f26e91 100644 --- a/src/core/hardware_interrupt_manager.cpp +++ b/src/core/hardware_interrupt_manager.cpp | |||
| @@ -11,19 +11,20 @@ | |||
| 11 | namespace Core::Hardware { | 11 | namespace Core::Hardware { |
| 12 | 12 | ||
| 13 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { | 13 | InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) { |
| 14 | gpu_interrupt_event = Core::Timing::CreateEvent("GPUInterrupt", [this](u64 message, s64) { | 14 | gpu_interrupt_event = Core::Timing::CreateEvent( |
| 15 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); | 15 | "GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) { |
| 16 | const u32 syncpt = static_cast<u32>(message >> 32); | 16 | auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv"); |
| 17 | const u32 value = static_cast<u32>(message); | 17 | const u32 syncpt = static_cast<u32>(message >> 32); |
| 18 | nvdrv->SignalGPUInterruptSyncpt(syncpt, value); | 18 | const u32 value = static_cast<u32>(message); |
| 19 | }); | 19 | nvdrv->SignalGPUInterruptSyncpt(syncpt, value); |
| 20 | }); | ||
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | InterruptManager::~InterruptManager() = default; | 23 | InterruptManager::~InterruptManager() = default; |
| 23 | 24 | ||
| 24 | void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { | 25 | void InterruptManager::GPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { |
| 25 | const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; | 26 | const u64 msg = (static_cast<u64>(syncpoint_id) << 32ULL) | value; |
| 26 | system.CoreTiming().ScheduleEvent(10, gpu_interrupt_event, msg); | 27 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{10}, gpu_interrupt_event, msg); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | } // namespace Core::Hardware | 30 | } // namespace Core::Hardware |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 0dc6a4a43..1b503331f 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -229,6 +229,8 @@ inline void ResponseBuilder::Push(u32 value) { | |||
| 229 | 229 | ||
| 230 | template <typename T> | 230 | template <typename T> |
| 231 | void ResponseBuilder::PushRaw(const T& value) { | 231 | void ResponseBuilder::PushRaw(const T& value) { |
| 232 | static_assert(std::is_trivially_copyable_v<T>, | ||
| 233 | "It's undefined behavior to use memcpy with non-trivially copyable objects"); | ||
| 232 | std::memcpy(cmdbuf + index, &value, sizeof(T)); | 234 | std::memcpy(cmdbuf + index, &value, sizeof(T)); |
| 233 | index += (sizeof(T) + 3) / 4; // round up to word length | 235 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 234 | } | 236 | } |
| @@ -384,6 +386,8 @@ inline s32 RequestParser::Pop() { | |||
| 384 | 386 | ||
| 385 | template <typename T> | 387 | template <typename T> |
| 386 | void RequestParser::PopRaw(T& value) { | 388 | void RequestParser::PopRaw(T& value) { |
| 389 | static_assert(std::is_trivially_copyable_v<T>, | ||
| 390 | "It's undefined behavior to use memcpy with non-trivially copyable objects"); | ||
| 387 | std::memcpy(&value, cmdbuf + index, sizeof(T)); | 391 | std::memcpy(&value, cmdbuf + index, sizeof(T)); |
| 388 | index += (sizeof(T) + 3) / 4; // round up to word length | 392 | index += (sizeof(T) + 3) / 4; // round up to word length |
| 389 | } | 393 | } |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index df0debe1b..b882eaa0f 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -81,7 +81,7 @@ ResultCode AddressArbiter::IncrementAndSignalToAddressIfEqual(VAddr address, s32 | |||
| 81 | do { | 81 | do { |
| 82 | current_value = monitor.ExclusiveRead32(current_core, address); | 82 | current_value = monitor.ExclusiveRead32(current_core, address); |
| 83 | 83 | ||
| 84 | if (current_value != value) { | 84 | if (current_value != static_cast<u32>(value)) { |
| 85 | return ERR_INVALID_STATE; | 85 | return ERR_INVALID_STATE; |
| 86 | } | 86 | } |
| 87 | current_value++; | 87 | current_value++; |
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 5ab204b9b..be9eba519 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp | |||
| @@ -48,14 +48,15 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, | 50 | ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, |
| 51 | Core::Memory::Memory& memory) { | 51 | Core::Memory::Memory& memory, |
| 52 | Core::Timing::CoreTiming& core_timing) { | ||
| 52 | // Keep ServerSession alive until we're done working with it. | 53 | // Keep ServerSession alive until we're done working with it. |
| 53 | if (!parent->Server()) { | 54 | if (!parent->Server()) { |
| 54 | return ERR_SESSION_CLOSED_BY_REMOTE; | 55 | return ERR_SESSION_CLOSED_BY_REMOTE; |
| 55 | } | 56 | } |
| 56 | 57 | ||
| 57 | // Signal the server session that new data is available | 58 | // Signal the server session that new data is available |
| 58 | return parent->Server()->HandleSyncRequest(std::move(thread), memory); | 59 | return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing); |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | } // namespace Kernel | 62 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index c5f760d7d..e5e0690c2 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h | |||
| @@ -16,6 +16,10 @@ namespace Core::Memory { | |||
| 16 | class Memory; | 16 | class Memory; |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Core::Timing { | ||
| 20 | class CoreTiming; | ||
| 21 | } | ||
| 22 | |||
| 19 | namespace Kernel { | 23 | namespace Kernel { |
| 20 | 24 | ||
| 21 | class KernelCore; | 25 | class KernelCore; |
| @@ -42,7 +46,8 @@ public: | |||
| 42 | return HANDLE_TYPE; | 46 | return HANDLE_TYPE; |
| 43 | } | 47 | } |
| 44 | 48 | ||
| 45 | ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | 49 | ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, |
| 50 | Core::Timing::CoreTiming& core_timing); | ||
| 46 | 51 | ||
| 47 | bool ShouldWait(const Thread* thread) const override; | 52 | bool ShouldWait(const Thread* thread) const override; |
| 48 | 53 | ||
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 9277b5d08..81f85643b 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -293,13 +293,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { | |||
| 293 | BufferDescriptorA()[buffer_index].Size()}; | 293 | BufferDescriptorA()[buffer_index].Size()}; |
| 294 | 294 | ||
| 295 | if (is_buffer_a) { | 295 | if (is_buffer_a) { |
| 296 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return buffer; }, | 296 | ASSERT_OR_EXECUTE_MSG( |
| 297 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 297 | BufferDescriptorA().size() > buffer_index, { return buffer; }, |
| 298 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | ||
| 298 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); | 299 | buffer.resize(BufferDescriptorA()[buffer_index].Size()); |
| 299 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); | 300 | memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 300 | } else { | 301 | } else { |
| 301 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return buffer; }, | 302 | ASSERT_OR_EXECUTE_MSG( |
| 302 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 303 | BufferDescriptorX().size() > buffer_index, { return buffer; }, |
| 304 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | ||
| 303 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); | 305 | buffer.resize(BufferDescriptorX()[buffer_index].Size()); |
| 304 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); | 306 | memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); |
| 305 | } | 307 | } |
| @@ -324,16 +326,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, | |||
| 324 | } | 326 | } |
| 325 | 327 | ||
| 326 | if (is_buffer_b) { | 328 | if (is_buffer_b) { |
| 327 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index && | 329 | ASSERT_OR_EXECUTE_MSG( |
| 328 | BufferDescriptorB()[buffer_index].Size() >= size, | 330 | BufferDescriptorB().size() > buffer_index && |
| 329 | { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", | 331 | BufferDescriptorB()[buffer_index].Size() >= size, |
| 330 | buffer_index, size); | 332 | { return 0; }, "BufferDescriptorB is invalid, index={}, size={}", buffer_index, size); |
| 331 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); | 333 | memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); |
| 332 | } else { | 334 | } else { |
| 333 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index && | 335 | ASSERT_OR_EXECUTE_MSG( |
| 334 | BufferDescriptorC()[buffer_index].Size() >= size, | 336 | BufferDescriptorC().size() > buffer_index && |
| 335 | { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", | 337 | BufferDescriptorC()[buffer_index].Size() >= size, |
| 336 | buffer_index, size); | 338 | { return 0; }, "BufferDescriptorC is invalid, index={}, size={}", buffer_index, size); |
| 337 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); | 339 | memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size); |
| 338 | } | 340 | } |
| 339 | 341 | ||
| @@ -344,12 +346,14 @@ std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const | |||
| 344 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && | 346 | const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && |
| 345 | BufferDescriptorA()[buffer_index].Size()}; | 347 | BufferDescriptorA()[buffer_index].Size()}; |
| 346 | if (is_buffer_a) { | 348 | if (is_buffer_a) { |
| 347 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorA().size() > buffer_index, { return 0; }, | 349 | ASSERT_OR_EXECUTE_MSG( |
| 348 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | 350 | BufferDescriptorA().size() > buffer_index, { return 0; }, |
| 351 | "BufferDescriptorA invalid buffer_index {}", buffer_index); | ||
| 349 | return BufferDescriptorA()[buffer_index].Size(); | 352 | return BufferDescriptorA()[buffer_index].Size(); |
| 350 | } else { | 353 | } else { |
| 351 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorX().size() > buffer_index, { return 0; }, | 354 | ASSERT_OR_EXECUTE_MSG( |
| 352 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | 355 | BufferDescriptorX().size() > buffer_index, { return 0; }, |
| 356 | "BufferDescriptorX invalid buffer_index {}", buffer_index); | ||
| 353 | return BufferDescriptorX()[buffer_index].Size(); | 357 | return BufferDescriptorX()[buffer_index].Size(); |
| 354 | } | 358 | } |
| 355 | } | 359 | } |
| @@ -358,12 +362,14 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons | |||
| 358 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && | 362 | const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && |
| 359 | BufferDescriptorB()[buffer_index].Size()}; | 363 | BufferDescriptorB()[buffer_index].Size()}; |
| 360 | if (is_buffer_b) { | 364 | if (is_buffer_b) { |
| 361 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorB().size() > buffer_index, { return 0; }, | 365 | ASSERT_OR_EXECUTE_MSG( |
| 362 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | 366 | BufferDescriptorB().size() > buffer_index, { return 0; }, |
| 367 | "BufferDescriptorB invalid buffer_index {}", buffer_index); | ||
| 363 | return BufferDescriptorB()[buffer_index].Size(); | 368 | return BufferDescriptorB()[buffer_index].Size(); |
| 364 | } else { | 369 | } else { |
| 365 | ASSERT_OR_EXECUTE_MSG(BufferDescriptorC().size() > buffer_index, { return 0; }, | 370 | ASSERT_OR_EXECUTE_MSG( |
| 366 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | 371 | BufferDescriptorC().size() > buffer_index, { return 0; }, |
| 372 | "BufferDescriptorC invalid buffer_index {}", buffer_index); | ||
| 367 | return BufferDescriptorC()[buffer_index].Size(); | 373 | return BufferDescriptorC()[buffer_index].Size(); |
| 368 | } | 374 | } |
| 369 | return 0; | 375 | return 0; |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b31673928..f3277b766 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | #include <boost/container/small_vector.hpp> | 14 | #include <boost/container/small_vector.hpp> |
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "common/concepts.h" | ||
| 16 | #include "common/swap.h" | 17 | #include "common/swap.h" |
| 17 | #include "core/hle/ipc.h" | 18 | #include "core/hle/ipc.h" |
| 18 | #include "core/hle/kernel/object.h" | 19 | #include "core/hle/kernel/object.h" |
| @@ -193,23 +194,24 @@ public: | |||
| 193 | 194 | ||
| 194 | /* Helper function to write a buffer using the appropriate buffer descriptor | 195 | /* Helper function to write a buffer using the appropriate buffer descriptor |
| 195 | * | 196 | * |
| 196 | * @tparam ContiguousContainer an arbitrary container that satisfies the | 197 | * @tparam T an arbitrary container that satisfies the |
| 197 | * ContiguousContainer concept in the C++ standard library. | 198 | * ContiguousContainer concept in the C++ standard library or a trivially copyable type. |
| 198 | * | 199 | * |
| 199 | * @param container The container to write the data of into a buffer. | 200 | * @param data The container/data to write into a buffer. |
| 200 | * @param buffer_index The buffer in particular to write to. | 201 | * @param buffer_index The buffer in particular to write to. |
| 201 | */ | 202 | */ |
| 202 | template <typename ContiguousContainer, | 203 | template <typename T, typename = std::enable_if_t<!std::is_pointer_v<T>>> |
| 203 | typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> | 204 | std::size_t WriteBuffer(const T& data, std::size_t buffer_index = 0) const { |
| 204 | std::size_t WriteBuffer(const ContiguousContainer& container, | 205 | if constexpr (Common::IsSTLContainer<T>) { |
| 205 | std::size_t buffer_index = 0) const { | 206 | using ContiguousType = typename T::value_type; |
| 206 | using ContiguousType = typename ContiguousContainer::value_type; | 207 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| 207 | 208 | "Container to WriteBuffer must contain trivially copyable objects"); | |
| 208 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 209 | return WriteBuffer(std::data(data), std::size(data) * sizeof(ContiguousType), |
| 209 | "Container to WriteBuffer must contain trivially copyable objects"); | 210 | buffer_index); |
| 210 | 211 | } else { | |
| 211 | return WriteBuffer(std::data(container), std::size(container) * sizeof(ContiguousType), | 212 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| 212 | buffer_index); | 213 | return WriteBuffer(&data, sizeof(T), buffer_index); |
| 214 | } | ||
| 213 | } | 215 | } |
| 214 | 216 | ||
| 215 | /// Helper function to get the size of the input buffer | 217 | /// Helper function to get the size of the input buffer |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index e1c7a0f3b..f2b0fe2fd 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -145,16 +145,18 @@ struct KernelCore::Impl { | |||
| 145 | 145 | ||
| 146 | void InitializePreemption(KernelCore& kernel) { | 146 | void InitializePreemption(KernelCore& kernel) { |
| 147 | preemption_event = Core::Timing::CreateEvent( | 147 | preemption_event = Core::Timing::CreateEvent( |
| 148 | "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) { | 148 | "PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) { |
| 149 | { | 149 | { |
| 150 | SchedulerLock lock(kernel); | 150 | SchedulerLock lock(kernel); |
| 151 | global_scheduler.PreemptThreads(); | 151 | global_scheduler.PreemptThreads(); |
| 152 | } | 152 | } |
| 153 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | 153 | const auto time_interval = std::chrono::nanoseconds{ |
| 154 | Core::Timing::msToCycles(std::chrono::milliseconds(10))}; | ||
| 154 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 155 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 155 | }); | 156 | }); |
| 156 | 157 | ||
| 157 | s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); | 158 | const auto time_interval = |
| 159 | std::chrono::nanoseconds{Core::Timing::msToCycles(std::chrono::milliseconds(10))}; | ||
| 158 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); | 160 | system.CoreTiming().ScheduleEvent(time_interval, preemption_event); |
| 159 | } | 161 | } |
| 160 | 162 | ||
| @@ -217,6 +219,7 @@ struct KernelCore::Impl { | |||
| 217 | return static_cast<u32>(system.GetCpuManager().CurrentCore()); | 219 | return static_cast<u32>(system.GetCpuManager().CurrentCore()); |
| 218 | } | 220 | } |
| 219 | } | 221 | } |
| 222 | std::unique_lock lock{register_thread_mutex}; | ||
| 220 | const auto it = host_thread_ids.find(this_id); | 223 | const auto it = host_thread_ids.find(this_id); |
| 221 | if (it == host_thread_ids.end()) { | 224 | if (it == host_thread_ids.end()) { |
| 222 | return Core::INVALID_HOST_THREAD_ID; | 225 | return Core::INVALID_HOST_THREAD_ID; |
| @@ -322,7 +325,7 @@ struct KernelCore::Impl { | |||
| 322 | std::unordered_map<std::thread::id, u32> host_thread_ids; | 325 | std::unordered_map<std::thread::id, u32> host_thread_ids; |
| 323 | u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; | 326 | u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; |
| 324 | std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; | 327 | std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; |
| 325 | std::mutex register_thread_mutex; | 328 | mutable std::mutex register_thread_mutex; |
| 326 | 329 | ||
| 327 | // Kernel memory management | 330 | // Kernel memory management |
| 328 | std::unique_ptr<Memory::MemoryManager> memory_manager; | 331 | std::unique_ptr<Memory::MemoryManager> memory_manager; |
diff --git a/src/core/hle/kernel/memory/page_table.cpp b/src/core/hle/kernel/memory/page_table.cpp index 5d6aac00f..a3fadb533 100644 --- a/src/core/hle/kernel/memory/page_table.cpp +++ b/src/core/hle/kernel/memory/page_table.cpp | |||
| @@ -604,7 +604,6 @@ ResultCode PageTable::MapPages(VAddr addr, const PageLinkedList& page_linked_lis | |||
| 604 | if (const auto result{ | 604 | if (const auto result{ |
| 605 | Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; | 605 | Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; |
| 606 | result.IsError()) { | 606 | result.IsError()) { |
| 607 | const MemoryInfo info{block_manager->FindBlock(cur_addr).GetMemoryInfo()}; | ||
| 608 | const std::size_t num_pages{(addr - cur_addr) / PageSize}; | 607 | const std::size_t num_pages{(addr - cur_addr) / PageSize}; |
| 609 | 608 | ||
| 610 | ASSERT( | 609 | ASSERT( |
| @@ -852,11 +851,12 @@ ResultCode PageTable::LockForDeviceAddressSpace(VAddr addr, std::size_t size) { | |||
| 852 | return result; | 851 | return result; |
| 853 | } | 852 | } |
| 854 | 853 | ||
| 855 | block_manager->UpdateLock(addr, size / PageSize, | 854 | block_manager->UpdateLock( |
| 856 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { | 855 | addr, size / PageSize, |
| 857 | block->ShareToDevice(perm); | 856 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { |
| 858 | }, | 857 | block->ShareToDevice(perm); |
| 859 | perm); | 858 | }, |
| 859 | perm); | ||
| 860 | 860 | ||
| 861 | return RESULT_SUCCESS; | 861 | return RESULT_SUCCESS; |
| 862 | } | 862 | } |
| @@ -874,11 +874,12 @@ ResultCode PageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size) | |||
| 874 | return result; | 874 | return result; |
| 875 | } | 875 | } |
| 876 | 876 | ||
| 877 | block_manager->UpdateLock(addr, size / PageSize, | 877 | block_manager->UpdateLock( |
| 878 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { | 878 | addr, size / PageSize, |
| 879 | block->UnshareToDevice(perm); | 879 | [](MemoryBlockManager::iterator block, MemoryPermission perm) { |
| 880 | }, | 880 | block->UnshareToDevice(perm); |
| 881 | perm); | 881 | }, |
| 882 | perm); | ||
| 882 | 883 | ||
| 883 | return RESULT_SUCCESS; | 884 | return RESULT_SUCCESS; |
| 884 | } | 885 | } |
diff --git a/src/core/hle/kernel/memory/system_control.cpp b/src/core/hle/kernel/memory/system_control.cpp index 2f98e9c4c..11d204bc2 100644 --- a/src/core/hle/kernel/memory/system_control.cpp +++ b/src/core/hle/kernel/memory/system_control.cpp | |||
| @@ -7,22 +7,15 @@ | |||
| 7 | #include "core/hle/kernel/memory/system_control.h" | 7 | #include "core/hle/kernel/memory/system_control.h" |
| 8 | 8 | ||
| 9 | namespace Kernel::Memory::SystemControl { | 9 | namespace Kernel::Memory::SystemControl { |
| 10 | 10 | namespace { | |
| 11 | u64 GenerateRandomU64ForInit() { | ||
| 12 | static std::random_device device; | ||
| 13 | static std::mt19937 gen(device()); | ||
| 14 | static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | ||
| 15 | return distribution(gen); | ||
| 16 | } | ||
| 17 | |||
| 18 | template <typename F> | 11 | template <typename F> |
| 19 | u64 GenerateUniformRange(u64 min, u64 max, F f) { | 12 | u64 GenerateUniformRange(u64 min, u64 max, F f) { |
| 20 | /* Handle the case where the difference is too large to represent. */ | 13 | // Handle the case where the difference is too large to represent. |
| 21 | if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { | 14 | if (max == std::numeric_limits<u64>::max() && min == std::numeric_limits<u64>::min()) { |
| 22 | return f(); | 15 | return f(); |
| 23 | } | 16 | } |
| 24 | 17 | ||
| 25 | /* Iterate until we get a value in range. */ | 18 | // Iterate until we get a value in range. |
| 26 | const u64 range_size = ((max + 1) - min); | 19 | const u64 range_size = ((max + 1) - min); |
| 27 | const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; | 20 | const u64 effective_max = (std::numeric_limits<u64>::max() / range_size) * range_size; |
| 28 | while (true) { | 21 | while (true) { |
| @@ -32,6 +25,14 @@ u64 GenerateUniformRange(u64 min, u64 max, F f) { | |||
| 32 | } | 25 | } |
| 33 | } | 26 | } |
| 34 | 27 | ||
| 28 | u64 GenerateRandomU64ForInit() { | ||
| 29 | static std::random_device device; | ||
| 30 | static std::mt19937 gen(device()); | ||
| 31 | static std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | ||
| 32 | return distribution(gen); | ||
| 33 | } | ||
| 34 | } // Anonymous namespace | ||
| 35 | |||
| 35 | u64 GenerateRandomRange(u64 min, u64 max) { | 36 | u64 GenerateRandomRange(u64 min, u64 max) { |
| 36 | return GenerateUniformRange(min, max, GenerateRandomU64ForInit); | 37 | return GenerateUniformRange(min, max, GenerateRandomU64ForInit); |
| 37 | } | 38 | } |
diff --git a/src/core/hle/kernel/memory/system_control.h b/src/core/hle/kernel/memory/system_control.h index 3fa93111d..19cab8cbc 100644 --- a/src/core/hle/kernel/memory/system_control.h +++ b/src/core/hle/kernel/memory/system_control.h | |||
| @@ -8,11 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | namespace Kernel::Memory::SystemControl { | 9 | namespace Kernel::Memory::SystemControl { |
| 10 | 10 | ||
| 11 | u64 GenerateRandomU64ForInit(); | ||
| 12 | |||
| 13 | template <typename F> | ||
| 14 | u64 GenerateUniformRange(u64 min, u64 max, F f); | ||
| 15 | |||
| 16 | u64 GenerateRandomRange(u64 min, u64 max); | 11 | u64 GenerateRandomRange(u64 min, u64 max); |
| 17 | 12 | ||
| 18 | } // namespace Kernel::Memory::SystemControl | 13 | } // namespace Kernel::Memory::SystemControl |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index f93e5e4b0..5cbd3b912 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -131,7 +131,8 @@ u32 GlobalScheduler::SelectThreads() { | |||
| 131 | u32 cores_needing_context_switch{}; | 131 | u32 cores_needing_context_switch{}; |
| 132 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 132 | for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 133 | Scheduler& sched = kernel.Scheduler(core); | 133 | Scheduler& sched = kernel.Scheduler(core); |
| 134 | ASSERT(top_threads[core] == nullptr || top_threads[core]->GetProcessorID() == core); | 134 | ASSERT(top_threads[core] == nullptr || |
| 135 | static_cast<u32>(top_threads[core]->GetProcessorID()) == core); | ||
| 135 | if (update_thread(top_threads[core], sched)) { | 136 | if (update_thread(top_threads[core], sched)) { |
| 136 | cores_needing_context_switch |= (1ul << core); | 137 | cores_needing_context_switch |= (1ul << core); |
| 137 | } | 138 | } |
| @@ -663,32 +664,26 @@ void Scheduler::Reload() { | |||
| 663 | } | 664 | } |
| 664 | 665 | ||
| 665 | void Scheduler::SwitchContextStep2() { | 666 | void Scheduler::SwitchContextStep2() { |
| 666 | Thread* previous_thread = current_thread_prev.get(); | ||
| 667 | Thread* new_thread = selected_thread.get(); | ||
| 668 | |||
| 669 | // Load context of new thread | 667 | // Load context of new thread |
| 670 | Process* const previous_process = | 668 | if (selected_thread) { |
| 671 | previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr; | 669 | ASSERT_MSG(selected_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, |
| 672 | |||
| 673 | if (new_thread) { | ||
| 674 | ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, | ||
| 675 | "Thread must be runnable."); | 670 | "Thread must be runnable."); |
| 676 | 671 | ||
| 677 | // Cancel any outstanding wakeup events for this thread | 672 | // Cancel any outstanding wakeup events for this thread |
| 678 | new_thread->SetIsRunning(true); | 673 | selected_thread->SetIsRunning(true); |
| 679 | new_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); | 674 | selected_thread->last_running_ticks = system.CoreTiming().GetCPUTicks(); |
| 680 | new_thread->SetWasRunning(false); | 675 | selected_thread->SetWasRunning(false); |
| 681 | 676 | ||
| 682 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | 677 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); |
| 683 | if (thread_owner_process != nullptr) { | 678 | if (thread_owner_process != nullptr) { |
| 684 | system.Kernel().MakeCurrentProcess(thread_owner_process); | 679 | system.Kernel().MakeCurrentProcess(thread_owner_process); |
| 685 | } | 680 | } |
| 686 | if (!new_thread->IsHLEThread()) { | 681 | if (!selected_thread->IsHLEThread()) { |
| 687 | Core::ARM_Interface& cpu_core = new_thread->ArmInterface(); | 682 | Core::ARM_Interface& cpu_core = selected_thread->ArmInterface(); |
| 688 | cpu_core.LoadContext(new_thread->GetContext32()); | 683 | cpu_core.LoadContext(selected_thread->GetContext32()); |
| 689 | cpu_core.LoadContext(new_thread->GetContext64()); | 684 | cpu_core.LoadContext(selected_thread->GetContext64()); |
| 690 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); | 685 | cpu_core.SetTlsAddress(selected_thread->GetTLSAddress()); |
| 691 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | 686 | cpu_core.SetTPIDR_EL0(selected_thread->GetTPIDR_EL0()); |
| 692 | cpu_core.ChangeProcessorID(this->core_id); | 687 | cpu_core.ChangeProcessorID(this->core_id); |
| 693 | cpu_core.ClearExclusiveState(); | 688 | cpu_core.ClearExclusiveState(); |
| 694 | } | 689 | } |
| @@ -761,7 +756,11 @@ void Scheduler::SwitchToCurrent() { | |||
| 761 | current_thread = selected_thread; | 756 | current_thread = selected_thread; |
| 762 | is_context_switch_pending = false; | 757 | is_context_switch_pending = false; |
| 763 | } | 758 | } |
| 764 | while (!is_context_switch_pending) { | 759 | const auto is_switch_pending = [this] { |
| 760 | std::scoped_lock lock{guard}; | ||
| 761 | return is_context_switch_pending; | ||
| 762 | }; | ||
| 763 | do { | ||
| 765 | if (current_thread != nullptr && !current_thread->IsHLEThread()) { | 764 | if (current_thread != nullptr && !current_thread->IsHLEThread()) { |
| 766 | current_thread->context_guard.lock(); | 765 | current_thread->context_guard.lock(); |
| 767 | if (!current_thread->IsRunnable()) { | 766 | if (!current_thread->IsRunnable()) { |
| @@ -780,7 +779,7 @@ void Scheduler::SwitchToCurrent() { | |||
| 780 | next_context = &idle_thread->GetHostContext(); | 779 | next_context = &idle_thread->GetHostContext(); |
| 781 | } | 780 | } |
| 782 | Common::Fiber::YieldTo(switch_fiber, *next_context); | 781 | Common::Fiber::YieldTo(switch_fiber, *next_context); |
| 783 | } | 782 | } while (!is_switch_pending()); |
| 784 | } | 783 | } |
| 785 | } | 784 | } |
| 786 | 785 | ||
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index b3b4b5169..b6f04dcea 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -188,7 +188,7 @@ private: | |||
| 188 | 188 | ||
| 189 | /// Scheduler lock mechanisms. | 189 | /// Scheduler lock mechanisms. |
| 190 | bool is_locked{}; | 190 | bool is_locked{}; |
| 191 | Common::SpinLock inner_lock{}; | 191 | std::mutex inner_lock; |
| 192 | std::atomic<s64> scope_lock{}; | 192 | std::atomic<s64> scope_lock{}; |
| 193 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; | 193 | Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; |
| 194 | 194 | ||
| @@ -289,7 +289,7 @@ private: | |||
| 289 | 289 | ||
| 290 | class SchedulerLock { | 290 | class SchedulerLock { |
| 291 | public: | 291 | public: |
| 292 | explicit SchedulerLock(KernelCore& kernel); | 292 | [[nodiscard]] explicit SchedulerLock(KernelCore& kernel); |
| 293 | ~SchedulerLock(); | 293 | ~SchedulerLock(); |
| 294 | 294 | ||
| 295 | protected: | 295 | protected: |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 7b23a6889..8c19f2534 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/core.h" | ||
| 12 | #include "core/core_timing.h" | 11 | #include "core/core_timing.h" |
| 13 | #include "core/hle/ipc_helpers.h" | 12 | #include "core/hle/ipc_helpers.h" |
| 14 | #include "core/hle/kernel/client_port.h" | 13 | #include "core/hle/kernel/client_port.h" |
| @@ -33,8 +32,10 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern | |||
| 33 | std::string name) { | 32 | std::string name) { |
| 34 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; | 33 | std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; |
| 35 | 34 | ||
| 36 | session->request_event = Core::Timing::CreateEvent( | 35 | session->request_event = |
| 37 | name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); }); | 36 | Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) { |
| 37 | session->CompleteSyncRequest(); | ||
| 38 | }); | ||
| 38 | session->name = std::move(name); | 39 | session->name = std::move(name); |
| 39 | session->parent = std::move(parent); | 40 | session->parent = std::move(parent); |
| 40 | 41 | ||
| @@ -183,10 +184,11 @@ ResultCode ServerSession::CompleteSyncRequest() { | |||
| 183 | } | 184 | } |
| 184 | 185 | ||
| 185 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | 186 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, |
| 186 | Core::Memory::Memory& memory) { | 187 | Core::Memory::Memory& memory, |
| 187 | ResultCode result = QueueSyncRequest(std::move(thread), memory); | 188 | Core::Timing::CoreTiming& core_timing) { |
| 188 | const u64 delay = kernel.IsMulticore() ? 0U : 20000U; | 189 | const ResultCode result = QueueSyncRequest(std::move(thread), memory); |
| 189 | Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {}); | 190 | const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000}; |
| 191 | core_timing.ScheduleEvent(delay, request_event, {}); | ||
| 190 | return result; | 192 | return result; |
| 191 | } | 193 | } |
| 192 | 194 | ||
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 403aaf10b..d23e9ec68 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h | |||
| @@ -18,8 +18,9 @@ class Memory; | |||
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | namespace Core::Timing { | 20 | namespace Core::Timing { |
| 21 | class CoreTiming; | ||
| 21 | struct EventType; | 22 | struct EventType; |
| 22 | } | 23 | } // namespace Core::Timing |
| 23 | 24 | ||
| 24 | namespace Kernel { | 25 | namespace Kernel { |
| 25 | 26 | ||
| @@ -87,12 +88,14 @@ public: | |||
| 87 | /** | 88 | /** |
| 88 | * Handle a sync request from the emulated application. | 89 | * Handle a sync request from the emulated application. |
| 89 | * | 90 | * |
| 90 | * @param thread Thread that initiated the request. | 91 | * @param thread Thread that initiated the request. |
| 91 | * @param memory Memory context to handle the sync request under. | 92 | * @param memory Memory context to handle the sync request under. |
| 93 | * @param core_timing Core timing context to schedule the request event under. | ||
| 92 | * | 94 | * |
| 93 | * @returns ResultCode from the operation. | 95 | * @returns ResultCode from the operation. |
| 94 | */ | 96 | */ |
| 95 | ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); | 97 | ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, |
| 98 | Core::Timing::CoreTiming& core_timing); | ||
| 96 | 99 | ||
| 97 | bool ShouldWait(const Thread* thread) const override; | 100 | bool ShouldWait(const Thread* thread) const override; |
| 98 | 101 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 01ae57053..bafd1ced7 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -346,7 +346,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { | |||
| 346 | SchedulerLock lock(system.Kernel()); | 346 | SchedulerLock lock(system.Kernel()); |
| 347 | thread->InvalidateHLECallback(); | 347 | thread->InvalidateHLECallback(); |
| 348 | thread->SetStatus(ThreadStatus::WaitIPC); | 348 | thread->SetStatus(ThreadStatus::WaitIPC); |
| 349 | session->SendSyncRequest(SharedFrom(thread), system.Memory()); | 349 | session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); |
| 350 | } | 350 | } |
| 351 | 351 | ||
| 352 | if (thread->HasHLECallback()) { | 352 | if (thread->HasHLECallback()) { |
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 941305e8e..95f2446c9 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp | |||
| @@ -16,14 +16,14 @@ namespace Kernel { | |||
| 16 | 16 | ||
| 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { | 17 | TimeManager::TimeManager(Core::System& system_) : system{system_} { |
| 18 | time_manager_event_type = Core::Timing::CreateEvent( | 18 | time_manager_event_type = Core::Timing::CreateEvent( |
| 19 | "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | 19 | "Kernel::TimeManagerCallback", |
| 20 | SchedulerLock lock(system.Kernel()); | 20 | [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { |
| 21 | Handle proper_handle = static_cast<Handle>(thread_handle); | 21 | const SchedulerLock lock(system.Kernel()); |
| 22 | const auto proper_handle = static_cast<Handle>(thread_handle); | ||
| 22 | if (cancelled_events[proper_handle]) { | 23 | if (cancelled_events[proper_handle]) { |
| 23 | return; | 24 | return; |
| 24 | } | 25 | } |
| 25 | std::shared_ptr<Thread> thread = | 26 | auto thread = this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); |
| 26 | this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | ||
| 27 | thread->OnWakeUp(); | 27 | thread->OnWakeUp(); |
| 28 | }); | 28 | }); |
| 29 | } | 29 | } |
| @@ -34,7 +34,8 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 | |||
| 34 | ASSERT(timetask); | 34 | ASSERT(timetask); |
| 35 | ASSERT(timetask->GetStatus() != ThreadStatus::Ready); | 35 | ASSERT(timetask->GetStatus() != ThreadStatus::Ready); |
| 36 | ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); | 36 | ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); |
| 37 | system.CoreTiming().ScheduleEvent(nanoseconds, time_manager_event_type, event_handle); | 37 | system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, |
| 38 | time_manager_event_type, event_handle); | ||
| 38 | } else { | 39 | } else { |
| 39 | event_handle = InvalidHandle; | 40 | event_handle = InvalidHandle; |
| 40 | } | 41 | } |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 450f61fea..b6bdbd988 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -342,8 +342,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) { | |||
| 342 | */ | 342 | */ |
| 343 | #define CASCADE_RESULT(target, source) \ | 343 | #define CASCADE_RESULT(target, source) \ |
| 344 | auto CONCAT2(check_result_L, __LINE__) = source; \ | 344 | auto CONCAT2(check_result_L, __LINE__) = source; \ |
| 345 | if (CONCAT2(check_result_L, __LINE__).Failed()) \ | 345 | if (CONCAT2(check_result_L, __LINE__).Failed()) { \ |
| 346 | return CONCAT2(check_result_L, __LINE__).Code(); \ | 346 | return CONCAT2(check_result_L, __LINE__).Code(); \ |
| 347 | } \ | ||
| 347 | target = std::move(*CONCAT2(check_result_L, __LINE__)) | 348 | target = std::move(*CONCAT2(check_result_L, __LINE__)) |
| 348 | 349 | ||
| 349 | /** | 350 | /** |
| @@ -351,6 +352,9 @@ ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) { | |||
| 351 | * non-success, or discarded otherwise. | 352 | * non-success, or discarded otherwise. |
| 352 | */ | 353 | */ |
| 353 | #define CASCADE_CODE(source) \ | 354 | #define CASCADE_CODE(source) \ |
| 354 | auto CONCAT2(check_result_L, __LINE__) = source; \ | 355 | do { \ |
| 355 | if (CONCAT2(check_result_L, __LINE__).IsError()) \ | 356 | auto CONCAT2(check_result_L, __LINE__) = source; \ |
| 356 | return CONCAT2(check_result_L, __LINE__); | 357 | if (CONCAT2(check_result_L, __LINE__).IsError()) { \ |
| 358 | return CONCAT2(check_result_L, __LINE__); \ | ||
| 359 | } \ | ||
| 360 | } while (false) | ||
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 8ac856ec3..6b1613510 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -35,7 +35,7 @@ constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30}; | |||
| 35 | constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; | 35 | constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; |
| 36 | 36 | ||
| 37 | static std::string GetImagePath(Common::UUID uuid) { | 37 | static std::string GetImagePath(Common::UUID uuid) { |
| 38 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 38 | return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + |
| 39 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | 39 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; |
| 40 | } | 40 | } |
| 41 | 41 | ||
| @@ -286,9 +286,7 @@ protected: | |||
| 286 | ProfileBase profile_base{}; | 286 | ProfileBase profile_base{}; |
| 287 | ProfileData data{}; | 287 | ProfileData data{}; |
| 288 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { | 288 | if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { |
| 289 | std::array<u8, sizeof(ProfileData)> raw_data; | 289 | ctx.WriteBuffer(data); |
| 290 | std::memcpy(raw_data.data(), &data, sizeof(ProfileData)); | ||
| 291 | ctx.WriteBuffer(raw_data); | ||
| 292 | IPC::ResponseBuilder rb{ctx, 16}; | 290 | IPC::ResponseBuilder rb{ctx, 16}; |
| 293 | rb.Push(RESULT_SUCCESS); | 291 | rb.Push(RESULT_SUCCESS); |
| 294 | rb.PushRaw(profile_base); | 292 | rb.PushRaw(profile_base); |
| @@ -320,7 +318,7 @@ protected: | |||
| 320 | IPC::ResponseBuilder rb{ctx, 3}; | 318 | IPC::ResponseBuilder rb{ctx, 3}; |
| 321 | rb.Push(RESULT_SUCCESS); | 319 | rb.Push(RESULT_SUCCESS); |
| 322 | 320 | ||
| 323 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | 321 | const Common::FS::IOFile image(GetImagePath(user_id), "rb"); |
| 324 | if (!image.IsOpen()) { | 322 | if (!image.IsOpen()) { |
| 325 | LOG_WARNING(Service_ACC, | 323 | LOG_WARNING(Service_ACC, |
| 326 | "Failed to load user provided image! Falling back to built-in backup..."); | 324 | "Failed to load user provided image! Falling back to built-in backup..."); |
| @@ -333,7 +331,7 @@ protected: | |||
| 333 | std::vector<u8> buffer(size); | 331 | std::vector<u8> buffer(size); |
| 334 | image.ReadBytes(buffer.data(), buffer.size()); | 332 | image.ReadBytes(buffer.data(), buffer.size()); |
| 335 | 333 | ||
| 336 | ctx.WriteBuffer(buffer.data(), buffer.size()); | 334 | ctx.WriteBuffer(buffer); |
| 337 | rb.Push<u32>(size); | 335 | rb.Push<u32>(size); |
| 338 | } | 336 | } |
| 339 | 337 | ||
| @@ -342,7 +340,7 @@ protected: | |||
| 342 | IPC::ResponseBuilder rb{ctx, 3}; | 340 | IPC::ResponseBuilder rb{ctx, 3}; |
| 343 | rb.Push(RESULT_SUCCESS); | 341 | rb.Push(RESULT_SUCCESS); |
| 344 | 342 | ||
| 345 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | 343 | const Common::FS::IOFile image(GetImagePath(user_id), "rb"); |
| 346 | 344 | ||
| 347 | if (!image.IsOpen()) { | 345 | if (!image.IsOpen()) { |
| 348 | LOG_WARNING(Service_ACC, | 346 | LOG_WARNING(Service_ACC, |
| @@ -407,7 +405,7 @@ protected: | |||
| 407 | ProfileData data; | 405 | ProfileData data; |
| 408 | std::memcpy(&data, user_data.data(), sizeof(ProfileData)); | 406 | std::memcpy(&data, user_data.data(), sizeof(ProfileData)); |
| 409 | 407 | ||
| 410 | FileUtil::IOFile image(GetImagePath(user_id), "wb"); | 408 | Common::FS::IOFile image(GetImagePath(user_id), "wb"); |
| 411 | 409 | ||
| 412 | if (!image.IsOpen() || !image.Resize(image_data.size()) || | 410 | if (!image.IsOpen() || !image.Resize(image_data.size()) || |
| 413 | image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || | 411 | image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || |
| @@ -776,6 +774,17 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) { | |||
| 776 | rb.Push(RESULT_SUCCESS); | 774 | rb.Push(RESULT_SUCCESS); |
| 777 | } | 775 | } |
| 778 | 776 | ||
| 777 | void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) { | ||
| 778 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 779 | |||
| 780 | // This is similar to GetBaasAccountManagerForApplication | ||
| 781 | // This command is used concurrently with ListOpenContextStoredUsers | ||
| 782 | // TODO: Find the differences between this and GetBaasAccountManagerForApplication | ||
| 783 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 784 | rb.Push(RESULT_SUCCESS); | ||
| 785 | rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser()); | ||
| 786 | } | ||
| 787 | |||
| 779 | void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { | 788 | void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) { |
| 780 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 789 | LOG_WARNING(Service_ACC, "(STUBBED) called"); |
| 781 | 790 | ||
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h index d4c6395c6..c611efd89 100644 --- a/src/core/hle/service/acc/acc.h +++ b/src/core/hle/service/acc/acc.h | |||
| @@ -34,6 +34,7 @@ public: | |||
| 34 | void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); | 34 | void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx); |
| 35 | void GetProfileEditor(Kernel::HLERequestContext& ctx); | 35 | void GetProfileEditor(Kernel::HLERequestContext& ctx); |
| 36 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); | 36 | void ListQualifiedUsers(Kernel::HLERequestContext& ctx); |
| 37 | void LoadOpenContext(Kernel::HLERequestContext& ctx); | ||
| 37 | void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); | 38 | void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx); |
| 38 | 39 | ||
| 39 | private: | 40 | private: |
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp index cb44e06b7..75a24f8f5 100644 --- a/src/core/hle/service/acc/acc_u0.cpp +++ b/src/core/hle/service/acc/acc_u0.cpp | |||
| @@ -29,7 +29,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p | |||
| 29 | {110, nullptr, "StoreSaveDataThumbnail"}, | 29 | {110, nullptr, "StoreSaveDataThumbnail"}, |
| 30 | {111, nullptr, "ClearSaveDataThumbnail"}, | 30 | {111, nullptr, "ClearSaveDataThumbnail"}, |
| 31 | {120, nullptr, "CreateGuestLoginRequest"}, | 31 | {120, nullptr, "CreateGuestLoginRequest"}, |
| 32 | {130, nullptr, "LoadOpenContext"}, // 5.0.0+ | 32 | {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+ |
| 33 | {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ | 33 | {131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+ |
| 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ | 34 | {140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+ |
| 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ | 35 | {141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+ |
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index eb8c81645..9b829e957 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace Service::Account { | 14 | namespace Service::Account { |
| 15 | 15 | ||
| 16 | namespace FS = Common::FS; | ||
| 16 | using Common::UUID; | 17 | using Common::UUID; |
| 17 | 18 | ||
| 18 | struct UserRaw { | 19 | struct UserRaw { |
| @@ -58,7 +59,7 @@ ProfileManager::~ProfileManager() { | |||
| 58 | /// internal management of the users profiles | 59 | /// internal management of the users profiles |
| 59 | std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { | 60 | std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { |
| 60 | if (user_count >= MAX_USERS) { | 61 | if (user_count >= MAX_USERS) { |
| 61 | return {}; | 62 | return std::nullopt; |
| 62 | } | 63 | } |
| 63 | profiles[user_count] = profile; | 64 | profiles[user_count] = profile; |
| 64 | return user_count++; | 65 | return user_count++; |
| @@ -101,13 +102,14 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern | |||
| 101 | [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) { | 102 | [&uuid](const ProfileInfo& profile) { return uuid == profile.user_uuid; })) { |
| 102 | return ERROR_USER_ALREADY_EXISTS; | 103 | return ERROR_USER_ALREADY_EXISTS; |
| 103 | } | 104 | } |
| 104 | ProfileInfo profile; | 105 | |
| 105 | profile.user_uuid = uuid; | 106 | return AddUser({ |
| 106 | profile.username = username; | 107 | .user_uuid = uuid, |
| 107 | profile.data = {}; | 108 | .username = username, |
| 108 | profile.creation_time = 0x0; | 109 | .creation_time = 0, |
| 109 | profile.is_open = false; | 110 | .data = {}, |
| 110 | return AddUser(profile); | 111 | .is_open = false, |
| 112 | }); | ||
| 111 | } | 113 | } |
| 112 | 114 | ||
| 113 | /// Creates a new user on the system. This function allows a much simpler method of registration | 115 | /// Creates a new user on the system. This function allows a much simpler method of registration |
| @@ -126,7 +128,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) | |||
| 126 | 128 | ||
| 127 | std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | 129 | std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { |
| 128 | if (index >= MAX_USERS) { | 130 | if (index >= MAX_USERS) { |
| 129 | return {}; | 131 | return std::nullopt; |
| 130 | } | 132 | } |
| 131 | 133 | ||
| 132 | return profiles[index].user_uuid; | 134 | return profiles[index].user_uuid; |
| @@ -135,13 +137,13 @@ std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | |||
| 135 | /// Returns a users profile index based on their user id. | 137 | /// Returns a users profile index based on their user id. |
| 136 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 138 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 137 | if (!uuid) { | 139 | if (!uuid) { |
| 138 | return {}; | 140 | return std::nullopt; |
| 139 | } | 141 | } |
| 140 | 142 | ||
| 141 | const auto iter = std::find_if(profiles.begin(), profiles.end(), | 143 | const auto iter = std::find_if(profiles.begin(), profiles.end(), |
| 142 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | 144 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); |
| 143 | if (iter == profiles.end()) { | 145 | if (iter == profiles.end()) { |
| 144 | return {}; | 146 | return std::nullopt; |
| 145 | } | 147 | } |
| 146 | 148 | ||
| 147 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); | 149 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); |
| @@ -317,9 +319,8 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& | |||
| 317 | } | 319 | } |
| 318 | 320 | ||
| 319 | void ProfileManager::ParseUserSaveFile() { | 321 | void ProfileManager::ParseUserSaveFile() { |
| 320 | FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 322 | const FS::IOFile save( |
| 321 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", | 323 | FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", "rb"); |
| 322 | "rb"); | ||
| 323 | 324 | ||
| 324 | if (!save.IsOpen()) { | 325 | if (!save.IsOpen()) { |
| 325 | LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " | 326 | LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " |
| @@ -339,7 +340,13 @@ void ProfileManager::ParseUserSaveFile() { | |||
| 339 | continue; | 340 | continue; |
| 340 | } | 341 | } |
| 341 | 342 | ||
| 342 | AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false}); | 343 | AddUser({ |
| 344 | .user_uuid = user.uuid, | ||
| 345 | .username = user.username, | ||
| 346 | .creation_time = user.timestamp, | ||
| 347 | .data = user.extra_data, | ||
| 348 | .is_open = false, | ||
| 349 | }); | ||
| 343 | } | 350 | } |
| 344 | 351 | ||
| 345 | std::stable_partition(profiles.begin(), profiles.end(), | 352 | std::stable_partition(profiles.begin(), profiles.end(), |
| @@ -350,29 +357,31 @@ void ProfileManager::WriteUserSaveFile() { | |||
| 350 | ProfileDataRaw raw{}; | 357 | ProfileDataRaw raw{}; |
| 351 | 358 | ||
| 352 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | 359 | for (std::size_t i = 0; i < MAX_USERS; ++i) { |
| 353 | raw.users[i].username = profiles[i].username; | 360 | raw.users[i] = { |
| 354 | raw.users[i].uuid2 = profiles[i].user_uuid; | 361 | .uuid = profiles[i].user_uuid, |
| 355 | raw.users[i].uuid = profiles[i].user_uuid; | 362 | .uuid2 = profiles[i].user_uuid, |
| 356 | raw.users[i].timestamp = profiles[i].creation_time; | 363 | .timestamp = profiles[i].creation_time, |
| 357 | raw.users[i].extra_data = profiles[i].data; | 364 | .username = profiles[i].username, |
| 365 | .extra_data = profiles[i].data, | ||
| 366 | }; | ||
| 358 | } | 367 | } |
| 359 | 368 | ||
| 360 | const auto raw_path = | 369 | const auto raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010"; |
| 361 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; | 370 | if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) { |
| 362 | if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) | 371 | FS::Delete(raw_path); |
| 363 | FileUtil::Delete(raw_path); | 372 | } |
| 364 | 373 | ||
| 365 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 374 | const auto path = |
| 366 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; | 375 | FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; |
| 367 | 376 | ||
| 368 | if (!FileUtil::CreateFullPath(path)) { | 377 | if (!FS::CreateFullPath(path)) { |
| 369 | LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " | 378 | LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " |
| 370 | "nand/system/save/8000000000000010/su/avators to mitigate this " | 379 | "nand/system/save/8000000000000010/su/avators to mitigate this " |
| 371 | "issue."); | 380 | "issue."); |
| 372 | return; | 381 | return; |
| 373 | } | 382 | } |
| 374 | 383 | ||
| 375 | FileUtil::IOFile save(path, "wb"); | 384 | FS::IOFile save(path, "wb"); |
| 376 | 385 | ||
| 377 | if (!save.IsOpen()) { | 386 | if (!save.IsOpen()) { |
| 378 | LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " | 387 | LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 4e7a0bec9..d7a81f64a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -378,7 +378,11 @@ void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& | |||
| 378 | } | 378 | } |
| 379 | 379 | ||
| 380 | void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { | 380 | void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) { |
| 381 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 381 | IPC::RequestParser rp{ctx}; |
| 382 | const auto permission = rp.PopEnum<ScreenshotPermission>(); | ||
| 383 | LOG_DEBUG(Service_AM, "called, permission={}", permission); | ||
| 384 | |||
| 385 | screenshot_permission = permission; | ||
| 382 | 386 | ||
| 383 | IPC::ResponseBuilder rb{ctx, 2}; | 387 | IPC::ResponseBuilder rb{ctx, 2}; |
| 384 | rb.Push(RESULT_SUCCESS); | 388 | rb.Push(RESULT_SUCCESS); |
| @@ -1188,7 +1192,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) | |||
| 1188 | {120, nullptr, "ExecuteProgram"}, | 1192 | {120, nullptr, "ExecuteProgram"}, |
| 1189 | {121, nullptr, "ClearUserChannel"}, | 1193 | {121, nullptr, "ClearUserChannel"}, |
| 1190 | {122, nullptr, "UnpopToUserChannel"}, | 1194 | {122, nullptr, "UnpopToUserChannel"}, |
| 1191 | {123, nullptr, "GetPreviousProgramIndex"}, | 1195 | {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"}, |
| 1192 | {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, | 1196 | {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, |
| 1193 | {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, | 1197 | {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, |
| 1194 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, | 1198 | {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, |
| @@ -1342,12 +1346,12 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | |||
| 1342 | 1346 | ||
| 1343 | LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); | 1347 | LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]); |
| 1344 | 1348 | ||
| 1345 | FileSys::SaveDataDescriptor descriptor{}; | 1349 | FileSys::SaveDataAttribute attribute{}; |
| 1346 | descriptor.title_id = system.CurrentProcess()->GetTitleID(); | 1350 | attribute.title_id = system.CurrentProcess()->GetTitleID(); |
| 1347 | descriptor.user_id = user_id; | 1351 | attribute.user_id = user_id; |
| 1348 | descriptor.type = FileSys::SaveDataType::SaveData; | 1352 | attribute.type = FileSys::SaveDataType::SaveData; |
| 1349 | const auto res = system.GetFileSystemController().CreateSaveData( | 1353 | const auto res = system.GetFileSystemController().CreateSaveData( |
| 1350 | FileSys::SaveDataSpaceId::NandUser, descriptor); | 1354 | FileSys::SaveDataSpaceId::NandUser, attribute); |
| 1351 | 1355 | ||
| 1352 | IPC::ResponseBuilder rb{ctx, 4}; | 1356 | IPC::ResponseBuilder rb{ctx, 4}; |
| 1353 | rb.Push(res.Code()); | 1357 | rb.Push(res.Code()); |
| @@ -1405,7 +1409,6 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { | |||
| 1405 | // Get supported languages from NACP, if possible | 1409 | // Get supported languages from NACP, if possible |
| 1406 | // Default to 0 (all languages supported) | 1410 | // Default to 0 (all languages supported) |
| 1407 | u32 supported_languages = 0; | 1411 | u32 supported_languages = 0; |
| 1408 | FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; | ||
| 1409 | 1412 | ||
| 1410 | const auto res = [this] { | 1413 | const auto res = [this] { |
| 1411 | const auto title_id = system.CurrentProcess()->GetTitleID(); | 1414 | const auto title_id = system.CurrentProcess()->GetTitleID(); |
| @@ -1551,6 +1554,14 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque | |||
| 1551 | rb.Push<u32>(0); | 1554 | rb.Push<u32>(0); |
| 1552 | } | 1555 | } |
| 1553 | 1556 | ||
| 1557 | void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) { | ||
| 1558 | LOG_WARNING(Service_AM, "(STUBBED) called"); | ||
| 1559 | |||
| 1560 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1561 | rb.Push(RESULT_SUCCESS); | ||
| 1562 | rb.Push<s32>(previous_program_index); | ||
| 1563 | } | ||
| 1564 | |||
| 1554 | void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { | 1565 | void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) { |
| 1555 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 1566 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 1556 | 1567 | ||
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 6cfb11b48..bcc06affe 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -149,6 +149,12 @@ private: | |||
| 149 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); | 149 | void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx); |
| 150 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); | 150 | void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx); |
| 151 | 151 | ||
| 152 | enum class ScreenshotPermission : u32 { | ||
| 153 | Inherit = 0, | ||
| 154 | Enable = 1, | ||
| 155 | Disable = 2, | ||
| 156 | }; | ||
| 157 | |||
| 152 | Core::System& system; | 158 | Core::System& system; |
| 153 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger; | 159 | std::shared_ptr<NVFlinger::NVFlinger> nvflinger; |
| 154 | Kernel::EventPair launchable_event; | 160 | Kernel::EventPair launchable_event; |
| @@ -157,6 +163,7 @@ private: | |||
| 157 | u32 idle_time_detection_extension = 0; | 163 | u32 idle_time_detection_extension = 0; |
| 158 | u64 num_fatal_sections_entered = 0; | 164 | u64 num_fatal_sections_entered = 0; |
| 159 | bool is_auto_sleep_disabled = false; | 165 | bool is_auto_sleep_disabled = false; |
| 166 | ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit; | ||
| 160 | }; | 167 | }; |
| 161 | 168 | ||
| 162 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { | 169 | class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { |
| @@ -281,11 +288,13 @@ private: | |||
| 281 | void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); | 288 | void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx); |
| 282 | void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); | 289 | void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx); |
| 283 | void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); | 290 | void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx); |
| 291 | void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); | ||
| 284 | void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); | 292 | void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); |
| 285 | void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); | 293 | void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); |
| 286 | 294 | ||
| 287 | bool launch_popped_application_specific = false; | 295 | bool launch_popped_application_specific = false; |
| 288 | bool launch_popped_account_preselect = false; | 296 | bool launch_popped_account_preselect = false; |
| 297 | s32 previous_program_index{-1}; | ||
| 289 | Kernel::EventPair gpu_error_detected_event; | 298 | Kernel::EventPair gpu_error_detected_event; |
| 290 | Kernel::EventPair friend_invitation_storage_channel_event; | 299 | Kernel::EventPair friend_invitation_storage_channel_event; |
| 291 | Core::System& system; | 300 | Core::System& system; |
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index c3261f3e6..2b626bb40 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <cstring> | 5 | #include <cstring> |
| 6 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/frontend/applets/controller.h" | ||
| 8 | #include "core/frontend/applets/error.h" | 9 | #include "core/frontend/applets/error.h" |
| 9 | #include "core/frontend/applets/general_frontend.h" | 10 | #include "core/frontend/applets/general_frontend.h" |
| 10 | #include "core/frontend/applets/profile_select.h" | 11 | #include "core/frontend/applets/profile_select.h" |
| @@ -15,6 +16,7 @@ | |||
| 15 | #include "core/hle/kernel/writable_event.h" | 16 | #include "core/hle/kernel/writable_event.h" |
| 16 | #include "core/hle/service/am/am.h" | 17 | #include "core/hle/service/am/am.h" |
| 17 | #include "core/hle/service/am/applets/applets.h" | 18 | #include "core/hle/service/am/applets/applets.h" |
| 19 | #include "core/hle/service/am/applets/controller.h" | ||
| 18 | #include "core/hle/service/am/applets/error.h" | 20 | #include "core/hle/service/am/applets/error.h" |
| 19 | #include "core/hle/service/am/applets/general_backend.h" | 21 | #include "core/hle/service/am/applets/general_backend.h" |
| 20 | #include "core/hle/service/am/applets/profile_select.h" | 22 | #include "core/hle/service/am/applets/profile_select.h" |
| @@ -140,14 +142,14 @@ void Applet::Initialize() { | |||
| 140 | 142 | ||
| 141 | AppletFrontendSet::AppletFrontendSet() = default; | 143 | AppletFrontendSet::AppletFrontendSet() = default; |
| 142 | 144 | ||
| 143 | AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, | 145 | AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, |
| 146 | ErrorApplet error, ParentalControlsApplet parental_controls, | ||
| 144 | PhotoViewer photo_viewer, ProfileSelect profile_select, | 147 | PhotoViewer photo_viewer, ProfileSelect profile_select, |
| 145 | SoftwareKeyboard software_keyboard, WebBrowser web_browser, | 148 | SoftwareKeyboard software_keyboard, WebBrowser web_browser) |
| 146 | ECommerceApplet e_commerce) | 149 | : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)}, |
| 147 | : parental_controls{std::move(parental_controls)}, error{std::move(error)}, | 150 | parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)}, |
| 148 | photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)}, | 151 | profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)}, |
| 149 | software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)}, | 152 | web_browser{std::move(web_browser)} {} |
| 150 | e_commerce{std::move(e_commerce)} {} | ||
| 151 | 153 | ||
| 152 | AppletFrontendSet::~AppletFrontendSet() = default; | 154 | AppletFrontendSet::~AppletFrontendSet() = default; |
| 153 | 155 | ||
| @@ -164,20 +166,37 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const { | |||
| 164 | } | 166 | } |
| 165 | 167 | ||
| 166 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { | 168 | void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { |
| 167 | if (set.parental_controls != nullptr) | 169 | if (set.controller != nullptr) { |
| 168 | frontend.parental_controls = std::move(set.parental_controls); | 170 | frontend.controller = std::move(set.controller); |
| 169 | if (set.error != nullptr) | 171 | } |
| 172 | |||
| 173 | if (set.e_commerce != nullptr) { | ||
| 174 | frontend.e_commerce = std::move(set.e_commerce); | ||
| 175 | } | ||
| 176 | |||
| 177 | if (set.error != nullptr) { | ||
| 170 | frontend.error = std::move(set.error); | 178 | frontend.error = std::move(set.error); |
| 171 | if (set.photo_viewer != nullptr) | 179 | } |
| 180 | |||
| 181 | if (set.parental_controls != nullptr) { | ||
| 182 | frontend.parental_controls = std::move(set.parental_controls); | ||
| 183 | } | ||
| 184 | |||
| 185 | if (set.photo_viewer != nullptr) { | ||
| 172 | frontend.photo_viewer = std::move(set.photo_viewer); | 186 | frontend.photo_viewer = std::move(set.photo_viewer); |
| 173 | if (set.profile_select != nullptr) | 187 | } |
| 188 | |||
| 189 | if (set.profile_select != nullptr) { | ||
| 174 | frontend.profile_select = std::move(set.profile_select); | 190 | frontend.profile_select = std::move(set.profile_select); |
| 175 | if (set.software_keyboard != nullptr) | 191 | } |
| 192 | |||
| 193 | if (set.software_keyboard != nullptr) { | ||
| 176 | frontend.software_keyboard = std::move(set.software_keyboard); | 194 | frontend.software_keyboard = std::move(set.software_keyboard); |
| 177 | if (set.web_browser != nullptr) | 195 | } |
| 196 | |||
| 197 | if (set.web_browser != nullptr) { | ||
| 178 | frontend.web_browser = std::move(set.web_browser); | 198 | frontend.web_browser = std::move(set.web_browser); |
| 179 | if (set.e_commerce != nullptr) | 199 | } |
| 180 | frontend.e_commerce = std::move(set.e_commerce); | ||
| 181 | } | 200 | } |
| 182 | 201 | ||
| 183 | void AppletManager::SetDefaultAppletFrontendSet() { | 202 | void AppletManager::SetDefaultAppletFrontendSet() { |
| @@ -186,15 +205,24 @@ void AppletManager::SetDefaultAppletFrontendSet() { | |||
| 186 | } | 205 | } |
| 187 | 206 | ||
| 188 | void AppletManager::SetDefaultAppletsIfMissing() { | 207 | void AppletManager::SetDefaultAppletsIfMissing() { |
| 189 | if (frontend.parental_controls == nullptr) { | 208 | if (frontend.controller == nullptr) { |
| 190 | frontend.parental_controls = | 209 | frontend.controller = |
| 191 | std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); | 210 | std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager()); |
| 211 | } | ||
| 212 | |||
| 213 | if (frontend.e_commerce == nullptr) { | ||
| 214 | frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>(); | ||
| 192 | } | 215 | } |
| 193 | 216 | ||
| 194 | if (frontend.error == nullptr) { | 217 | if (frontend.error == nullptr) { |
| 195 | frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); | 218 | frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>(); |
| 196 | } | 219 | } |
| 197 | 220 | ||
| 221 | if (frontend.parental_controls == nullptr) { | ||
| 222 | frontend.parental_controls = | ||
| 223 | std::make_unique<Core::Frontend::DefaultParentalControlsApplet>(); | ||
| 224 | } | ||
| 225 | |||
| 198 | if (frontend.photo_viewer == nullptr) { | 226 | if (frontend.photo_viewer == nullptr) { |
| 199 | frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); | 227 | frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>(); |
| 200 | } | 228 | } |
| @@ -211,10 +239,6 @@ void AppletManager::SetDefaultAppletsIfMissing() { | |||
| 211 | if (frontend.web_browser == nullptr) { | 239 | if (frontend.web_browser == nullptr) { |
| 212 | frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); | 240 | frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>(); |
| 213 | } | 241 | } |
| 214 | |||
| 215 | if (frontend.e_commerce == nullptr) { | ||
| 216 | frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>(); | ||
| 217 | } | ||
| 218 | } | 242 | } |
| 219 | 243 | ||
| 220 | void AppletManager::ClearAll() { | 244 | void AppletManager::ClearAll() { |
| @@ -225,6 +249,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const { | |||
| 225 | switch (id) { | 249 | switch (id) { |
| 226 | case AppletId::Auth: | 250 | case AppletId::Auth: |
| 227 | return std::make_shared<Auth>(system, *frontend.parental_controls); | 251 | return std::make_shared<Auth>(system, *frontend.parental_controls); |
| 252 | case AppletId::Controller: | ||
| 253 | return std::make_shared<Controller>(system, *frontend.controller); | ||
| 228 | case AppletId::Error: | 254 | case AppletId::Error: |
| 229 | return std::make_shared<Error>(system, *frontend.error); | 255 | return std::make_shared<Error>(system, *frontend.error); |
| 230 | case AppletId::ProfileSelect: | 256 | case AppletId::ProfileSelect: |
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h index e75be86a2..a1f4cf897 100644 --- a/src/core/hle/service/am/applets/applets.h +++ b/src/core/hle/service/am/applets/applets.h | |||
| @@ -17,6 +17,7 @@ class System; | |||
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | namespace Core::Frontend { | 19 | namespace Core::Frontend { |
| 20 | class ControllerApplet; | ||
| 20 | class ECommerceApplet; | 21 | class ECommerceApplet; |
| 21 | class ErrorApplet; | 22 | class ErrorApplet; |
| 22 | class ParentalControlsApplet; | 23 | class ParentalControlsApplet; |
| @@ -155,19 +156,20 @@ protected: | |||
| 155 | }; | 156 | }; |
| 156 | 157 | ||
| 157 | struct AppletFrontendSet { | 158 | struct AppletFrontendSet { |
| 158 | using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; | 159 | using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>; |
| 160 | using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>; | ||
| 159 | using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; | 161 | using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>; |
| 162 | using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>; | ||
| 160 | using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; | 163 | using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>; |
| 161 | using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; | 164 | using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>; |
| 162 | using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; | 165 | using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>; |
| 163 | using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; | 166 | using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>; |
| 164 | using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>; | ||
| 165 | 167 | ||
| 166 | AppletFrontendSet(); | 168 | AppletFrontendSet(); |
| 167 | AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error, | 169 | AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error, |
| 168 | PhotoViewer photo_viewer, ProfileSelect profile_select, | 170 | ParentalControlsApplet parental_controls, PhotoViewer photo_viewer, |
| 169 | SoftwareKeyboard software_keyboard, WebBrowser web_browser, | 171 | ProfileSelect profile_select, SoftwareKeyboard software_keyboard, |
| 170 | ECommerceApplet e_commerce); | 172 | WebBrowser web_browser); |
| 171 | ~AppletFrontendSet(); | 173 | ~AppletFrontendSet(); |
| 172 | 174 | ||
| 173 | AppletFrontendSet(const AppletFrontendSet&) = delete; | 175 | AppletFrontendSet(const AppletFrontendSet&) = delete; |
| @@ -176,13 +178,14 @@ struct AppletFrontendSet { | |||
| 176 | AppletFrontendSet(AppletFrontendSet&&) noexcept; | 178 | AppletFrontendSet(AppletFrontendSet&&) noexcept; |
| 177 | AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; | 179 | AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept; |
| 178 | 180 | ||
| 179 | ParentalControlsApplet parental_controls; | 181 | ControllerApplet controller; |
| 182 | ECommerceApplet e_commerce; | ||
| 180 | ErrorApplet error; | 183 | ErrorApplet error; |
| 184 | ParentalControlsApplet parental_controls; | ||
| 181 | PhotoViewer photo_viewer; | 185 | PhotoViewer photo_viewer; |
| 182 | ProfileSelect profile_select; | 186 | ProfileSelect profile_select; |
| 183 | SoftwareKeyboard software_keyboard; | 187 | SoftwareKeyboard software_keyboard; |
| 184 | WebBrowser web_browser; | 188 | WebBrowser web_browser; |
| 185 | ECommerceApplet e_commerce; | ||
| 186 | }; | 189 | }; |
| 187 | 190 | ||
| 188 | class AppletManager { | 191 | class AppletManager { |
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp new file mode 100644 index 000000000..2151da783 --- /dev/null +++ b/src/core/hle/service/am/applets/controller.cpp | |||
| @@ -0,0 +1,210 @@ | |||
| 1 | // Copyright 2020 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 <cstring> | ||
| 7 | |||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/logging/log.h" | ||
| 10 | #include "common/string_util.h" | ||
| 11 | #include "core/core.h" | ||
| 12 | #include "core/frontend/applets/controller.h" | ||
| 13 | #include "core/hle/result.h" | ||
| 14 | #include "core/hle/service/am/am.h" | ||
| 15 | #include "core/hle/service/am/applets/controller.h" | ||
| 16 | #include "core/hle/service/hid/controllers/npad.h" | ||
| 17 | |||
| 18 | namespace Service::AM::Applets { | ||
| 19 | |||
| 20 | // This error code (0x183ACA) is thrown when the applet fails to initialize. | ||
| 21 | [[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101}; | ||
| 22 | // This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2. | ||
| 23 | [[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102}; | ||
| 24 | |||
| 25 | static Core::Frontend::ControllerParameters ConvertToFrontendParameters( | ||
| 26 | ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text, | ||
| 27 | std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) { | ||
| 28 | HID::Controller_NPad::NPadType npad_style_set; | ||
| 29 | npad_style_set.raw = private_arg.style_set; | ||
| 30 | |||
| 31 | return { | ||
| 32 | .min_players = std::max(s8(1), header.player_count_min), | ||
| 33 | .max_players = header.player_count_max, | ||
| 34 | .keep_controllers_connected = header.enable_take_over_connection, | ||
| 35 | .enable_single_mode = header.enable_single_mode, | ||
| 36 | .enable_border_color = header.enable_identification_color, | ||
| 37 | .border_colors = identification_colors, | ||
| 38 | .enable_explain_text = enable_text, | ||
| 39 | .explain_text = text, | ||
| 40 | .allow_pro_controller = npad_style_set.pro_controller == 1, | ||
| 41 | .allow_handheld = npad_style_set.handheld == 1, | ||
| 42 | .allow_dual_joycons = npad_style_set.joycon_dual == 1, | ||
| 43 | .allow_left_joycon = npad_style_set.joycon_left == 1, | ||
| 44 | .allow_right_joycon = npad_style_set.joycon_right == 1, | ||
| 45 | }; | ||
| 46 | } | ||
| 47 | |||
| 48 | Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_) | ||
| 49 | : Applet{system_.Kernel()}, frontend(frontend_) {} | ||
| 50 | |||
| 51 | Controller::~Controller() = default; | ||
| 52 | |||
| 53 | void Controller::Initialize() { | ||
| 54 | Applet::Initialize(); | ||
| 55 | |||
| 56 | LOG_INFO(Service_HID, "Initializing Controller Applet."); | ||
| 57 | |||
| 58 | LOG_DEBUG(Service_HID, | ||
| 59 | "Initializing Applet with common_args: arg_version={}, lib_version={}, " | ||
| 60 | "play_startup_sound={}, size={}, system_tick={}, theme_color={}", | ||
| 61 | common_args.arguments_version, common_args.library_version, | ||
| 62 | common_args.play_startup_sound, common_args.size, common_args.system_tick, | ||
| 63 | common_args.theme_color); | ||
| 64 | |||
| 65 | library_applet_version = LibraryAppletVersion{common_args.library_version}; | ||
| 66 | |||
| 67 | const auto private_arg_storage = broker.PopNormalDataToApplet(); | ||
| 68 | ASSERT(private_arg_storage != nullptr); | ||
| 69 | |||
| 70 | const auto& private_arg = private_arg_storage->GetData(); | ||
| 71 | ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate)); | ||
| 72 | |||
| 73 | std::memcpy(&controller_private_arg, private_arg.data(), sizeof(ControllerSupportArgPrivate)); | ||
| 74 | ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate), | ||
| 75 | "Unknown ControllerSupportArgPrivate revision={} with size={}", | ||
| 76 | library_applet_version, controller_private_arg.arg_private_size); | ||
| 77 | |||
| 78 | switch (controller_private_arg.mode) { | ||
| 79 | case ControllerSupportMode::ShowControllerSupport: { | ||
| 80 | const auto user_arg_storage = broker.PopNormalDataToApplet(); | ||
| 81 | ASSERT(user_arg_storage != nullptr); | ||
| 82 | |||
| 83 | const auto& user_arg = user_arg_storage->GetData(); | ||
| 84 | switch (library_applet_version) { | ||
| 85 | case LibraryAppletVersion::Version3: | ||
| 86 | case LibraryAppletVersion::Version4: | ||
| 87 | case LibraryAppletVersion::Version5: | ||
| 88 | ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld)); | ||
| 89 | std::memcpy(&controller_user_arg_old, user_arg.data(), sizeof(ControllerSupportArgOld)); | ||
| 90 | break; | ||
| 91 | case LibraryAppletVersion::Version7: | ||
| 92 | ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew)); | ||
| 93 | std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); | ||
| 94 | break; | ||
| 95 | default: | ||
| 96 | UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}", | ||
| 97 | library_applet_version, controller_private_arg.arg_size); | ||
| 98 | ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew)); | ||
| 99 | std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew)); | ||
| 100 | break; | ||
| 101 | } | ||
| 102 | break; | ||
| 103 | } | ||
| 104 | case ControllerSupportMode::ShowControllerStrapGuide: | ||
| 105 | case ControllerSupportMode::ShowControllerFirmwareUpdate: | ||
| 106 | default: { | ||
| 107 | UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode); | ||
| 108 | break; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | bool Controller::TransactionComplete() const { | ||
| 114 | return complete; | ||
| 115 | } | ||
| 116 | |||
| 117 | ResultCode Controller::GetStatus() const { | ||
| 118 | return status; | ||
| 119 | } | ||
| 120 | |||
| 121 | void Controller::ExecuteInteractive() { | ||
| 122 | UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet."); | ||
| 123 | } | ||
| 124 | |||
| 125 | void Controller::Execute() { | ||
| 126 | switch (controller_private_arg.mode) { | ||
| 127 | case ControllerSupportMode::ShowControllerSupport: { | ||
| 128 | const auto parameters = [this] { | ||
| 129 | switch (library_applet_version) { | ||
| 130 | case LibraryAppletVersion::Version3: | ||
| 131 | case LibraryAppletVersion::Version4: | ||
| 132 | case LibraryAppletVersion::Version5: | ||
| 133 | return ConvertToFrontendParameters( | ||
| 134 | controller_private_arg, controller_user_arg_old.header, | ||
| 135 | controller_user_arg_old.enable_explain_text, | ||
| 136 | std::vector<IdentificationColor>( | ||
| 137 | controller_user_arg_old.identification_colors.begin(), | ||
| 138 | controller_user_arg_old.identification_colors.end()), | ||
| 139 | std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(), | ||
| 140 | controller_user_arg_old.explain_text.end())); | ||
| 141 | case LibraryAppletVersion::Version7: | ||
| 142 | default: | ||
| 143 | return ConvertToFrontendParameters( | ||
| 144 | controller_private_arg, controller_user_arg_new.header, | ||
| 145 | controller_user_arg_new.enable_explain_text, | ||
| 146 | std::vector<IdentificationColor>( | ||
| 147 | controller_user_arg_new.identification_colors.begin(), | ||
| 148 | controller_user_arg_new.identification_colors.end()), | ||
| 149 | std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(), | ||
| 150 | controller_user_arg_new.explain_text.end())); | ||
| 151 | } | ||
| 152 | }(); | ||
| 153 | |||
| 154 | is_single_mode = parameters.enable_single_mode; | ||
| 155 | |||
| 156 | LOG_DEBUG(Service_HID, | ||
| 157 | "Controller Parameters: min_players={}, max_players={}, " | ||
| 158 | "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, " | ||
| 159 | "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, " | ||
| 160 | "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}", | ||
| 161 | parameters.min_players, parameters.max_players, | ||
| 162 | parameters.keep_controllers_connected, parameters.enable_single_mode, | ||
| 163 | parameters.enable_border_color, parameters.enable_explain_text, | ||
| 164 | parameters.allow_pro_controller, parameters.allow_handheld, | ||
| 165 | parameters.allow_dual_joycons, parameters.allow_left_joycon, | ||
| 166 | parameters.allow_right_joycon); | ||
| 167 | |||
| 168 | frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters); | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | case ControllerSupportMode::ShowControllerStrapGuide: | ||
| 172 | case ControllerSupportMode::ShowControllerFirmwareUpdate: | ||
| 173 | default: { | ||
| 174 | ConfigurationComplete(); | ||
| 175 | break; | ||
| 176 | } | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | void Controller::ConfigurationComplete() { | ||
| 181 | ControllerSupportResultInfo result_info{}; | ||
| 182 | |||
| 183 | const auto& players = Settings::values.players; | ||
| 184 | |||
| 185 | // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters. | ||
| 186 | // Otherwise, only count connected players from P1-P8. | ||
| 187 | result_info.player_count = | ||
| 188 | is_single_mode ? 1 | ||
| 189 | : static_cast<s8>(std::count_if( | ||
| 190 | players.begin(), players.end() - 2, | ||
| 191 | [](Settings::PlayerInput player) { return player.connected; })); | ||
| 192 | |||
| 193 | result_info.selected_id = HID::Controller_NPad::IndexToNPad( | ||
| 194 | std::distance(players.begin(), | ||
| 195 | std::find_if(players.begin(), players.end(), | ||
| 196 | [](Settings::PlayerInput player) { return player.connected; }))); | ||
| 197 | |||
| 198 | result_info.result = 0; | ||
| 199 | |||
| 200 | LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}", | ||
| 201 | result_info.player_count, result_info.selected_id, result_info.result); | ||
| 202 | |||
| 203 | complete = true; | ||
| 204 | out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo)); | ||
| 205 | std::memcpy(out_data.data(), &result_info, out_data.size()); | ||
| 206 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data))); | ||
| 207 | broker.SignalStateChanged(); | ||
| 208 | } | ||
| 209 | |||
| 210 | } // namespace Service::AM::Applets | ||
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h new file mode 100644 index 000000000..f7bb3fba9 --- /dev/null +++ b/src/core/hle/service/am/applets/controller.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | // Copyright 2020 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 <array> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | #include "core/hle/service/am/applets/applets.h" | ||
| 14 | |||
| 15 | namespace Core { | ||
| 16 | class System; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Service::AM::Applets { | ||
| 20 | |||
| 21 | using IdentificationColor = std::array<u8, 4>; | ||
| 22 | using ExplainText = std::array<char, 0x81>; | ||
| 23 | |||
| 24 | enum class LibraryAppletVersion : u32_le { | ||
| 25 | Version3 = 0x3, // 1.0.0 - 2.3.0 | ||
| 26 | Version4 = 0x4, // 3.0.0 - 5.1.0 | ||
| 27 | Version5 = 0x5, // 6.0.0 - 7.0.1 | ||
| 28 | Version7 = 0x7, // 8.0.0+ | ||
| 29 | }; | ||
| 30 | |||
| 31 | enum class ControllerSupportMode : u8 { | ||
| 32 | ShowControllerSupport = 0, | ||
| 33 | ShowControllerStrapGuide = 1, | ||
| 34 | ShowControllerFirmwareUpdate = 2, | ||
| 35 | }; | ||
| 36 | |||
| 37 | enum class ControllerSupportCaller : u8 { | ||
| 38 | Application = 0, | ||
| 39 | System = 1, | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct ControllerSupportArgPrivate { | ||
| 43 | u32 arg_private_size{}; | ||
| 44 | u32 arg_size{}; | ||
| 45 | bool flag_0{}; | ||
| 46 | bool flag_1{}; | ||
| 47 | ControllerSupportMode mode{}; | ||
| 48 | ControllerSupportCaller caller{}; | ||
| 49 | u32 style_set{}; | ||
| 50 | u32 joy_hold_type{}; | ||
| 51 | }; | ||
| 52 | static_assert(sizeof(ControllerSupportArgPrivate) == 0x14, | ||
| 53 | "ControllerSupportArgPrivate has incorrect size."); | ||
| 54 | |||
| 55 | struct ControllerSupportArgHeader { | ||
| 56 | s8 player_count_min{}; | ||
| 57 | s8 player_count_max{}; | ||
| 58 | bool enable_take_over_connection{}; | ||
| 59 | bool enable_left_justify{}; | ||
| 60 | bool enable_permit_joy_dual{}; | ||
| 61 | bool enable_single_mode{}; | ||
| 62 | bool enable_identification_color{}; | ||
| 63 | }; | ||
| 64 | static_assert(sizeof(ControllerSupportArgHeader) == 0x7, | ||
| 65 | "ControllerSupportArgHeader has incorrect size."); | ||
| 66 | |||
| 67 | // LibraryAppletVersion 0x3, 0x4, 0x5 | ||
| 68 | struct ControllerSupportArgOld { | ||
| 69 | ControllerSupportArgHeader header{}; | ||
| 70 | std::array<IdentificationColor, 4> identification_colors{}; | ||
| 71 | bool enable_explain_text{}; | ||
| 72 | std::array<ExplainText, 4> explain_text{}; | ||
| 73 | }; | ||
| 74 | static_assert(sizeof(ControllerSupportArgOld) == 0x21C, | ||
| 75 | "ControllerSupportArgOld has incorrect size."); | ||
| 76 | |||
| 77 | // LibraryAppletVersion 0x7 | ||
| 78 | struct ControllerSupportArgNew { | ||
| 79 | ControllerSupportArgHeader header{}; | ||
| 80 | std::array<IdentificationColor, 8> identification_colors{}; | ||
| 81 | bool enable_explain_text{}; | ||
| 82 | std::array<ExplainText, 8> explain_text{}; | ||
| 83 | }; | ||
| 84 | static_assert(sizeof(ControllerSupportArgNew) == 0x430, | ||
| 85 | "ControllerSupportArgNew has incorrect size."); | ||
| 86 | |||
| 87 | struct ControllerSupportResultInfo { | ||
| 88 | s8 player_count{}; | ||
| 89 | INSERT_PADDING_BYTES(3); | ||
| 90 | u32 selected_id{}; | ||
| 91 | u32 result{}; | ||
| 92 | }; | ||
| 93 | static_assert(sizeof(ControllerSupportResultInfo) == 0xC, | ||
| 94 | "ControllerSupportResultInfo has incorrect size."); | ||
| 95 | |||
| 96 | class Controller final : public Applet { | ||
| 97 | public: | ||
| 98 | explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_); | ||
| 99 | ~Controller() override; | ||
| 100 | |||
| 101 | void Initialize() override; | ||
| 102 | |||
| 103 | bool TransactionComplete() const override; | ||
| 104 | ResultCode GetStatus() const override; | ||
| 105 | void ExecuteInteractive() override; | ||
| 106 | void Execute() override; | ||
| 107 | |||
| 108 | void ConfigurationComplete(); | ||
| 109 | |||
| 110 | private: | ||
| 111 | const Core::Frontend::ControllerApplet& frontend; | ||
| 112 | |||
| 113 | LibraryAppletVersion library_applet_version; | ||
| 114 | ControllerSupportArgPrivate controller_private_arg; | ||
| 115 | ControllerSupportArgOld controller_user_arg_old; | ||
| 116 | ControllerSupportArgNew controller_user_arg_new; | ||
| 117 | bool complete{false}; | ||
| 118 | ResultCode status{RESULT_SUCCESS}; | ||
| 119 | bool is_single_mode{false}; | ||
| 120 | std::vector<u8> out_data; | ||
| 121 | }; | ||
| 122 | |||
| 123 | } // namespace Service::AM::Applets | ||
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index fbe3686ae..bdeb0737a 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp | |||
| @@ -13,11 +13,23 @@ | |||
| 13 | 13 | ||
| 14 | namespace Service::AM::Applets { | 14 | namespace Service::AM::Applets { |
| 15 | 15 | ||
| 16 | namespace { | ||
| 17 | enum class Request : u32 { | ||
| 18 | Finalize = 0x4, | ||
| 19 | SetUserWordInfo = 0x6, | ||
| 20 | SetCustomizeDic = 0x7, | ||
| 21 | Calc = 0xa, | ||
| 22 | SetCustomizedDictionaries = 0xb, | ||
| 23 | UnsetCustomizedDictionaries = 0xc, | ||
| 24 | UnknownD = 0xd, | ||
| 25 | UnknownE = 0xe, | ||
| 26 | }; | ||
| 27 | constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8; | ||
| 16 | constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; | 28 | constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8; |
| 17 | constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; | 29 | constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4; |
| 18 | constexpr std::size_t DEFAULT_MAX_LENGTH = 500; | 30 | constexpr std::size_t DEFAULT_MAX_LENGTH = 500; |
| 19 | constexpr bool INTERACTIVE_STATUS_OK = false; | 31 | constexpr bool INTERACTIVE_STATUS_OK = false; |
| 20 | 32 | } // Anonymous namespace | |
| 21 | static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( | 33 | static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters( |
| 22 | KeyboardConfig config, std::u16string initial_text) { | 34 | KeyboardConfig config, std::u16string initial_text) { |
| 23 | Core::Frontend::SoftwareKeyboardParameters params{}; | 35 | Core::Frontend::SoftwareKeyboardParameters params{}; |
| @@ -47,6 +59,7 @@ SoftwareKeyboard::~SoftwareKeyboard() = default; | |||
| 47 | 59 | ||
| 48 | void SoftwareKeyboard::Initialize() { | 60 | void SoftwareKeyboard::Initialize() { |
| 49 | complete = false; | 61 | complete = false; |
| 62 | is_inline = false; | ||
| 50 | initial_text.clear(); | 63 | initial_text.clear(); |
| 51 | final_data.clear(); | 64 | final_data.clear(); |
| 52 | 65 | ||
| @@ -56,6 +69,11 @@ void SoftwareKeyboard::Initialize() { | |||
| 56 | ASSERT(keyboard_config_storage != nullptr); | 69 | ASSERT(keyboard_config_storage != nullptr); |
| 57 | const auto& keyboard_config = keyboard_config_storage->GetData(); | 70 | const auto& keyboard_config = keyboard_config_storage->GetData(); |
| 58 | 71 | ||
| 72 | if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) { | ||
| 73 | is_inline = true; | ||
| 74 | return; | ||
| 75 | } | ||
| 76 | |||
| 59 | ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); | 77 | ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig)); |
| 60 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); | 78 | std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig)); |
| 61 | 79 | ||
| @@ -87,16 +105,31 @@ void SoftwareKeyboard::ExecuteInteractive() { | |||
| 87 | const auto storage = broker.PopInteractiveDataToApplet(); | 105 | const auto storage = broker.PopInteractiveDataToApplet(); |
| 88 | ASSERT(storage != nullptr); | 106 | ASSERT(storage != nullptr); |
| 89 | const auto data = storage->GetData(); | 107 | const auto data = storage->GetData(); |
| 90 | const auto status = static_cast<bool>(data[0]); | 108 | if (!is_inline) { |
| 91 | 109 | const auto status = static_cast<bool>(data[0]); | |
| 92 | if (status == INTERACTIVE_STATUS_OK) { | 110 | if (status == INTERACTIVE_STATUS_OK) { |
| 93 | complete = true; | 111 | complete = true; |
| 112 | } else { | ||
| 113 | std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; | ||
| 114 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); | ||
| 115 | frontend.SendTextCheckDialog( | ||
| 116 | Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), | ||
| 117 | [this] { broker.SignalStateChanged(); }); | ||
| 118 | } | ||
| 94 | } else { | 119 | } else { |
| 95 | std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string; | 120 | Request request{}; |
| 96 | std::memcpy(string.data(), data.data() + 4, string.size() * 2); | 121 | std::memcpy(&request, data.data(), sizeof(Request)); |
| 97 | frontend.SendTextCheckDialog( | 122 | |
| 98 | Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()), | 123 | switch (request) { |
| 99 | [this] { broker.SignalStateChanged(); }); | 124 | case Request::Calc: { |
| 125 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::vector<u8>{1})); | ||
| 126 | broker.SignalStateChanged(); | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | default: | ||
| 130 | UNIMPLEMENTED_MSG("Request {:X} is not implemented", request); | ||
| 131 | break; | ||
| 132 | } | ||
| 100 | } | 133 | } |
| 101 | } | 134 | } |
| 102 | 135 | ||
| @@ -108,9 +141,10 @@ void SoftwareKeyboard::Execute() { | |||
| 108 | } | 141 | } |
| 109 | 142 | ||
| 110 | const auto parameters = ConvertToFrontendParameters(config, initial_text); | 143 | const auto parameters = ConvertToFrontendParameters(config, initial_text); |
| 111 | 144 | if (!is_inline) { | |
| 112 | frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, | 145 | frontend.RequestText( |
| 113 | parameters); | 146 | [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters); |
| 147 | } | ||
| 114 | } | 148 | } |
| 115 | 149 | ||
| 116 | void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { | 150 | void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) { |
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h index ef4801fc6..5a3824b5a 100644 --- a/src/core/hle/service/am/applets/software_keyboard.h +++ b/src/core/hle/service/am/applets/software_keyboard.h | |||
| @@ -78,6 +78,7 @@ private: | |||
| 78 | KeyboardConfig config; | 78 | KeyboardConfig config; |
| 79 | std::u16string initial_text; | 79 | std::u16string initial_text; |
| 80 | bool complete = false; | 80 | bool complete = false; |
| 81 | bool is_inline = false; | ||
| 81 | std::vector<u8> final_data; | 82 | std::vector<u8> final_data; |
| 82 | }; | 83 | }; |
| 83 | 84 | ||
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 9f30e167d..efe595c4f 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp | |||
| @@ -293,8 +293,8 @@ void WebBrowser::Finalize() { | |||
| 293 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); | 293 | broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(data))); |
| 294 | broker.SignalStateChanged(); | 294 | broker.SignalStateChanged(); |
| 295 | 295 | ||
| 296 | if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { | 296 | if (!temporary_dir.empty() && Common::FS::IsDirectory(temporary_dir)) { |
| 297 | FileUtil::DeleteDirRecursively(temporary_dir); | 297 | Common::FS::DeleteDirRecursively(temporary_dir); |
| 298 | } | 298 | } |
| 299 | } | 299 | } |
| 300 | 300 | ||
| @@ -452,10 +452,10 @@ void WebBrowser::InitializeOffline() { | |||
| 452 | }; | 452 | }; |
| 453 | 453 | ||
| 454 | temporary_dir = | 454 | temporary_dir = |
| 455 | FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + "web_applet_" + | 455 | Common::FS::SanitizePath(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + |
| 456 | WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], | 456 | "web_applet_" + WEB_SOURCE_NAMES[static_cast<u32>(source) - 1], |
| 457 | FileUtil::DirectorySeparator::PlatformDefault); | 457 | Common::FS::DirectorySeparator::PlatformDefault); |
| 458 | FileUtil::DeleteDirRecursively(temporary_dir); | 458 | Common::FS::DeleteDirRecursively(temporary_dir); |
| 459 | 459 | ||
| 460 | u64 title_id = 0; // 0 corresponds to current process | 460 | u64 title_id = 0; // 0 corresponds to current process |
| 461 | ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8); | 461 | ASSERT(args[WebArgTLVType::ApplicationID].size() >= 0x8); |
| @@ -492,8 +492,8 @@ void WebBrowser::InitializeOffline() { | |||
| 492 | } | 492 | } |
| 493 | 493 | ||
| 494 | filename = | 494 | filename = |
| 495 | FileUtil::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, | 495 | Common::FS::SanitizePath(temporary_dir + path_additional_directory + DIR_SEP + filename, |
| 496 | FileUtil::DirectorySeparator::PlatformDefault); | 496 | Common::FS::DirectorySeparator::PlatformDefault); |
| 497 | } | 497 | } |
| 498 | 498 | ||
| 499 | void WebBrowser::ExecuteShop() { | 499 | void WebBrowser::ExecuteShop() { |
| @@ -551,7 +551,8 @@ void WebBrowser::ExecuteShop() { | |||
| 551 | } | 551 | } |
| 552 | 552 | ||
| 553 | void WebBrowser::ExecuteOffline() { | 553 | void WebBrowser::ExecuteOffline() { |
| 554 | frontend.OpenPageLocal(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); | 554 | frontend.OpenPageLocal( |
| 555 | filename, [this] { UnpackRomFS(); }, [this] { Finalize(); }); | ||
| 555 | } | 556 | } |
| 556 | 557 | ||
| 557 | } // namespace Service::AM::Applets | 558 | } // namespace Service::AM::Applets |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 106e89743..9b4910e53 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -71,7 +71,7 @@ public: | |||
| 71 | 71 | ||
| 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, | 72 | stream = audio_core.OpenStream(system.CoreTiming(), audio_params.sample_rate, |
| 73 | audio_params.channel_count, std::move(unique_name), | 73 | audio_params.channel_count, std::move(unique_name), |
| 74 | [=]() { buffer_event.writable->Signal(); }); | 74 | [this] { buffer_event.writable->Signal(); }); |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | private: | 77 | private: |
| @@ -206,7 +206,7 @@ private: | |||
| 206 | AudioCore::StreamPtr stream; | 206 | AudioCore::StreamPtr stream; |
| 207 | std::string device_name; | 207 | std::string device_name; |
| 208 | 208 | ||
| 209 | [[maybe_unused]] AudoutParams audio_params {}; | 209 | [[maybe_unused]] AudoutParams audio_params{}; |
| 210 | 210 | ||
| 211 | /// This is the event handle used to check if the audio buffer was released | 211 | /// This is the event handle used to check if the audio buffer was released |
| 212 | Kernel::EventPair buffer_event; | 212 | Kernel::EventPair buffer_event; |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index d8359abaa..a2d3ded7b 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -26,7 +26,7 @@ namespace Service::Audio { | |||
| 26 | 26 | ||
| 27 | class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { | 27 | class IAudioRenderer final : public ServiceFramework<IAudioRenderer> { |
| 28 | public: | 28 | public: |
| 29 | explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params, | 29 | explicit IAudioRenderer(Core::System& system, AudioCommon::AudioRendererParameter audren_params, |
| 30 | const std::size_t instance_number) | 30 | const std::size_t instance_number) |
| 31 | : ServiceFramework("IAudioRenderer") { | 31 | : ServiceFramework("IAudioRenderer") { |
| 32 | // clang-format off | 32 | // clang-format off |
| @@ -94,14 +94,15 @@ private: | |||
| 94 | void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { | 94 | void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { |
| 95 | LOG_DEBUG(Service_Audio, "(STUBBED) called"); | 95 | LOG_DEBUG(Service_Audio, "(STUBBED) called"); |
| 96 | 96 | ||
| 97 | auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer()); | 97 | std::vector<u8> output_params(ctx.GetWriteBufferSize()); |
| 98 | auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); | ||
| 98 | 99 | ||
| 99 | if (result.Succeeded()) { | 100 | if (result.IsSuccess()) { |
| 100 | ctx.WriteBuffer(result.Unwrap()); | 101 | ctx.WriteBuffer(output_params); |
| 101 | } | 102 | } |
| 102 | 103 | ||
| 103 | IPC::ResponseBuilder rb{ctx, 2}; | 104 | IPC::ResponseBuilder rb{ctx, 2}; |
| 104 | rb.Push(result.Code()); | 105 | rb.Push(result); |
| 105 | } | 106 | } |
| 106 | 107 | ||
| 107 | void Start(Kernel::HLERequestContext& ctx) { | 108 | void Start(Kernel::HLERequestContext& ctx) { |
| @@ -346,7 +347,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) { | |||
| 346 | OpenAudioRendererImpl(ctx); | 347 | OpenAudioRendererImpl(ctx); |
| 347 | } | 348 | } |
| 348 | 349 | ||
| 349 | static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) { | 350 | static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) { |
| 350 | // +1 represents the final mix. | 351 | // +1 represents the final mix. |
| 351 | return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + | 352 | return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count + |
| 352 | 1; | 353 | 1; |
| @@ -375,7 +376,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 375 | constexpr u64 upsampler_manager_size = 0x48; | 376 | constexpr u64 upsampler_manager_size = 0x48; |
| 376 | 377 | ||
| 377 | // Calculates the part of the size that relates to mix buffers. | 378 | // Calculates the part of the size that relates to mix buffers. |
| 378 | const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) { | 379 | const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) { |
| 379 | // As of 8.0.0 this is the maximum on voice channels. | 380 | // As of 8.0.0 this is the maximum on voice channels. |
| 380 | constexpr u64 max_voice_channels = 6; | 381 | constexpr u64 max_voice_channels = 6; |
| 381 | 382 | ||
| @@ -397,7 +398,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 397 | }; | 398 | }; |
| 398 | 399 | ||
| 399 | // Calculates the portion of the size related to the mix data (and the sorting thereof). | 400 | // Calculates the portion of the size related to the mix data (and the sorting thereof). |
| 400 | const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) { | 401 | const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) { |
| 401 | // The size of the mixing info data structure. | 402 | // The size of the mixing info data structure. |
| 402 | constexpr u64 mix_info_size = 0x940; | 403 | constexpr u64 mix_info_size = 0x940; |
| 403 | 404 | ||
| @@ -447,7 +448,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 447 | }; | 448 | }; |
| 448 | 449 | ||
| 449 | // Calculates the part of the size related to voice channel info. | 450 | // Calculates the part of the size related to voice channel info. |
| 450 | const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) { | 451 | const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) { |
| 451 | constexpr u64 voice_info_size = 0x220; | 452 | constexpr u64 voice_info_size = 0x220; |
| 452 | constexpr u64 voice_resource_size = 0xD0; | 453 | constexpr u64 voice_resource_size = 0xD0; |
| 453 | 454 | ||
| @@ -461,7 +462,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 461 | }; | 462 | }; |
| 462 | 463 | ||
| 463 | // Calculates the part of the size related to memory pools. | 464 | // Calculates the part of the size related to memory pools. |
| 464 | const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) { | 465 | const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) { |
| 465 | const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); | 466 | const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count); |
| 466 | const u64 memory_pool_info_size = 0x20; | 467 | const u64 memory_pool_info_size = 0x20; |
| 467 | return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); | 468 | return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size); |
| @@ -469,7 +470,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 469 | 470 | ||
| 470 | // Calculates the part of the size related to the splitter context. | 471 | // Calculates the part of the size related to the splitter context. |
| 471 | const auto calculate_splitter_context_size = | 472 | const auto calculate_splitter_context_size = |
| 472 | [](const AudioCore::AudioRendererParameter& params) -> u64 { | 473 | [](const AudioCommon::AudioRendererParameter& params) -> u64 { |
| 473 | if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { | 474 | if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) { |
| 474 | return 0; | 475 | return 0; |
| 475 | } | 476 | } |
| @@ -488,27 +489,29 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 488 | }; | 489 | }; |
| 489 | 490 | ||
| 490 | // Calculates the part of the size related to the upsampler info. | 491 | // Calculates the part of the size related to the upsampler info. |
| 491 | const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) { | 492 | const auto calculate_upsampler_info_size = |
| 492 | constexpr u64 upsampler_info_size = 0x280; | 493 | [](const AudioCommon::AudioRendererParameter& params) { |
| 493 | // Yes, using the buffer size over info alignment size is intentional here. | 494 | constexpr u64 upsampler_info_size = 0x280; |
| 494 | return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count), | 495 | // Yes, using the buffer size over info alignment size is intentional here. |
| 495 | buffer_alignment_size); | 496 | return Common::AlignUp(upsampler_info_size * |
| 496 | }; | 497 | (u64{params.submix_count} + params.sink_count), |
| 498 | buffer_alignment_size); | ||
| 499 | }; | ||
| 497 | 500 | ||
| 498 | // Calculates the part of the size related to effect info. | 501 | // Calculates the part of the size related to effect info. |
| 499 | const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) { | 502 | const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) { |
| 500 | constexpr u64 effect_info_size = 0x2B0; | 503 | constexpr u64 effect_info_size = 0x2B0; |
| 501 | return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); | 504 | return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size); |
| 502 | }; | 505 | }; |
| 503 | 506 | ||
| 504 | // Calculates the part of the size related to audio sink info. | 507 | // Calculates the part of the size related to audio sink info. |
| 505 | const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) { | 508 | const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) { |
| 506 | const u64 sink_info_size = 0x170; | 509 | const u64 sink_info_size = 0x170; |
| 507 | return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); | 510 | return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size); |
| 508 | }; | 511 | }; |
| 509 | 512 | ||
| 510 | // Calculates the part of the size related to voice state info. | 513 | // Calculates the part of the size related to voice state info. |
| 511 | const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) { | 514 | const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) { |
| 512 | const u64 voice_state_size = 0x100; | 515 | const u64 voice_state_size = 0x100; |
| 513 | const u64 additional_size = buffer_alignment_size - 1; | 516 | const u64 additional_size = buffer_alignment_size - 1; |
| 514 | return Common::AlignUp(voice_state_size * params.voice_count + additional_size, | 517 | return Common::AlignUp(voice_state_size * params.voice_count + additional_size, |
| @@ -516,7 +519,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 516 | }; | 519 | }; |
| 517 | 520 | ||
| 518 | // Calculates the part of the size related to performance statistics. | 521 | // Calculates the part of the size related to performance statistics. |
| 519 | const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) { | 522 | const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) { |
| 520 | // Extra size value appended to the end of the calculation. | 523 | // Extra size value appended to the end of the calculation. |
| 521 | constexpr u64 appended = 128; | 524 | constexpr u64 appended = 128; |
| 522 | 525 | ||
| @@ -543,79 +546,81 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) { | |||
| 543 | }; | 546 | }; |
| 544 | 547 | ||
| 545 | // Calculates the part of the size that relates to the audio command buffer. | 548 | // Calculates the part of the size that relates to the audio command buffer. |
| 546 | const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) { | 549 | const auto calculate_command_buffer_size = |
| 547 | constexpr u64 alignment = (buffer_alignment_size - 1) * 2; | 550 | [](const AudioCommon::AudioRendererParameter& params) { |
| 551 | constexpr u64 alignment = (buffer_alignment_size - 1) * 2; | ||
| 548 | 552 | ||
| 549 | if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { | 553 | if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) { |
| 550 | constexpr u64 command_buffer_size = 0x18000; | 554 | constexpr u64 command_buffer_size = 0x18000; |
| 551 | 555 | ||
| 552 | return command_buffer_size + alignment; | 556 | return command_buffer_size + alignment; |
| 553 | } | 557 | } |
| 554 | 558 | ||
| 555 | // When the variadic command buffer is supported, this means | 559 | // When the variadic command buffer is supported, this means |
| 556 | // the command generator for the audio renderer can issue commands | 560 | // the command generator for the audio renderer can issue commands |
| 557 | // that are (as one would expect), variable in size. So what we need to do | 561 | // that are (as one would expect), variable in size. So what we need to do |
| 558 | // is determine the maximum possible size for a few command data structures | 562 | // is determine the maximum possible size for a few command data structures |
| 559 | // then multiply them by the amount of present commands indicated by the given | 563 | // then multiply them by the amount of present commands indicated by the given |
| 560 | // respective audio parameters. | 564 | // respective audio parameters. |
| 561 | 565 | ||
| 562 | constexpr u64 max_biquad_filters = 2; | 566 | constexpr u64 max_biquad_filters = 2; |
| 563 | constexpr u64 max_mix_buffers = 24; | 567 | constexpr u64 max_mix_buffers = 24; |
| 564 | 568 | ||
| 565 | constexpr u64 biquad_filter_command_size = 0x2C; | 569 | constexpr u64 biquad_filter_command_size = 0x2C; |
| 566 | 570 | ||
| 567 | constexpr u64 depop_mix_command_size = 0x24; | 571 | constexpr u64 depop_mix_command_size = 0x24; |
| 568 | constexpr u64 depop_setup_command_size = 0x50; | 572 | constexpr u64 depop_setup_command_size = 0x50; |
| 569 | 573 | ||
| 570 | constexpr u64 effect_command_max_size = 0x540; | 574 | constexpr u64 effect_command_max_size = 0x540; |
| 571 | 575 | ||
| 572 | constexpr u64 mix_command_size = 0x1C; | 576 | constexpr u64 mix_command_size = 0x1C; |
| 573 | constexpr u64 mix_ramp_command_size = 0x24; | 577 | constexpr u64 mix_ramp_command_size = 0x24; |
| 574 | constexpr u64 mix_ramp_grouped_command_size = 0x13C; | 578 | constexpr u64 mix_ramp_grouped_command_size = 0x13C; |
| 575 | 579 | ||
| 576 | constexpr u64 perf_command_size = 0x28; | 580 | constexpr u64 perf_command_size = 0x28; |
| 577 | 581 | ||
| 578 | constexpr u64 sink_command_size = 0x130; | 582 | constexpr u64 sink_command_size = 0x130; |
| 579 | 583 | ||
| 580 | constexpr u64 submix_command_max_size = | 584 | constexpr u64 submix_command_max_size = |
| 581 | depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; | 585 | depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers; |
| 582 | 586 | ||
| 583 | constexpr u64 volume_command_size = 0x1C; | 587 | constexpr u64 volume_command_size = 0x1C; |
| 584 | constexpr u64 volume_ramp_command_size = 0x20; | 588 | constexpr u64 volume_ramp_command_size = 0x20; |
| 585 | 589 | ||
| 586 | constexpr u64 voice_biquad_filter_command_size = | 590 | constexpr u64 voice_biquad_filter_command_size = |
| 587 | biquad_filter_command_size * max_biquad_filters; | 591 | biquad_filter_command_size * max_biquad_filters; |
| 588 | constexpr u64 voice_data_command_size = 0x9C; | 592 | constexpr u64 voice_data_command_size = 0x9C; |
| 589 | const u64 voice_command_max_size = | 593 | const u64 voice_command_max_size = |
| 590 | (params.splitter_count * depop_setup_command_size) + | 594 | (params.splitter_count * depop_setup_command_size) + |
| 591 | (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size + | 595 | (voice_data_command_size + voice_biquad_filter_command_size + |
| 592 | mix_ramp_grouped_command_size); | 596 | volume_ramp_command_size + mix_ramp_grouped_command_size); |
| 593 | 597 | ||
| 594 | // Now calculate the individual elements that comprise the size and add them together. | 598 | // Now calculate the individual elements that comprise the size and add them together. |
| 595 | const u64 effect_commands_size = params.effect_count * effect_command_max_size; | 599 | const u64 effect_commands_size = params.effect_count * effect_command_max_size; |
| 596 | 600 | ||
| 597 | const u64 final_mix_commands_size = | 601 | const u64 final_mix_commands_size = |
| 598 | depop_mix_command_size + volume_command_size * max_mix_buffers; | 602 | depop_mix_command_size + volume_command_size * max_mix_buffers; |
| 599 | 603 | ||
| 600 | const u64 perf_commands_size = | 604 | const u64 perf_commands_size = |
| 601 | perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); | 605 | perf_command_size * |
| 606 | (CalculateNumPerformanceEntries(params) + max_perf_detail_entries); | ||
| 602 | 607 | ||
| 603 | const u64 sink_commands_size = params.sink_count * sink_command_size; | 608 | const u64 sink_commands_size = params.sink_count * sink_command_size; |
| 604 | 609 | ||
| 605 | const u64 splitter_commands_size = | 610 | const u64 splitter_commands_size = |
| 606 | params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; | 611 | params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size; |
| 607 | 612 | ||
| 608 | const u64 submix_commands_size = params.submix_count * submix_command_max_size; | 613 | const u64 submix_commands_size = params.submix_count * submix_command_max_size; |
| 609 | 614 | ||
| 610 | const u64 voice_commands_size = params.voice_count * voice_command_max_size; | 615 | const u64 voice_commands_size = params.voice_count * voice_command_max_size; |
| 611 | 616 | ||
| 612 | return effect_commands_size + final_mix_commands_size + perf_commands_size + | 617 | return effect_commands_size + final_mix_commands_size + perf_commands_size + |
| 613 | sink_commands_size + splitter_commands_size + submix_commands_size + | 618 | sink_commands_size + splitter_commands_size + submix_commands_size + |
| 614 | voice_commands_size + alignment; | 619 | voice_commands_size + alignment; |
| 615 | }; | 620 | }; |
| 616 | 621 | ||
| 617 | IPC::RequestParser rp{ctx}; | 622 | IPC::RequestParser rp{ctx}; |
| 618 | const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); | 623 | const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); |
| 619 | 624 | ||
| 620 | u64 size = 0; | 625 | u64 size = 0; |
| 621 | size += calculate_mix_buffer_sizes(params); | 626 | size += calculate_mix_buffer_sizes(params); |
| @@ -681,7 +686,7 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c | |||
| 681 | 686 | ||
| 682 | void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { | 687 | void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) { |
| 683 | IPC::RequestParser rp{ctx}; | 688 | IPC::RequestParser rp{ctx}; |
| 684 | const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>(); | 689 | const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>(); |
| 685 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 690 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 686 | 691 | ||
| 687 | rb.Push(RESULT_SUCCESS); | 692 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index d19513cbb..f1d81602c 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -92,7 +92,7 @@ private: | |||
| 92 | if (performance) { | 92 | if (performance) { |
| 93 | rb.Push<u64>(*performance); | 93 | rb.Push<u64>(*performance); |
| 94 | } | 94 | } |
| 95 | ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16)); | 95 | ctx.WriteBuffer(samples); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, | 98 | bool DecodeOpusData(u32& consumed, u32& sample_count, const std::vector<u8>& input, |
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index d29e78d7e..ca021a99f 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -89,12 +89,12 @@ constexpr u32 TIMEOUT_SECONDS = 30; | |||
| 89 | 89 | ||
| 90 | std::string GetBINFilePath(u64 title_id) { | 90 | std::string GetBINFilePath(u64 title_id) { |
| 91 | return fmt::format("{}bcat/{:016X}/launchparam.bin", | 91 | return fmt::format("{}bcat/{:016X}/launchparam.bin", |
| 92 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); | 92 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id); |
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | std::string GetZIPFilePath(u64 title_id) { | 95 | std::string GetZIPFilePath(u64 title_id) { |
| 96 | return fmt::format("{}bcat/{:016X}/data.zip", | 96 | return fmt::format("{}bcat/{:016X}/data.zip", |
| 97 | FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id); | 97 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | // If the error is something the user should know about (build ID mismatch, bad client version), | 100 | // If the error is something the user should know about (build ID mismatch, bad client version), |
| @@ -205,8 +205,8 @@ private: | |||
| 205 | {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, | 205 | {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)}, |
| 206 | }; | 206 | }; |
| 207 | 207 | ||
| 208 | if (FileUtil::Exists(path)) { | 208 | if (Common::FS::Exists(path)) { |
| 209 | FileUtil::IOFile file{path, "rb"}; | 209 | Common::FS::IOFile file{path, "rb"}; |
| 210 | if (file.IsOpen()) { | 210 | if (file.IsOpen()) { |
| 211 | std::vector<u8> bytes(file.GetSize()); | 211 | std::vector<u8> bytes(file.GetSize()); |
| 212 | file.ReadBytes(bytes.data(), bytes.size()); | 212 | file.ReadBytes(bytes.data(), bytes.size()); |
| @@ -236,8 +236,8 @@ private: | |||
| 236 | return DownloadResult::InvalidContentType; | 236 | return DownloadResult::InvalidContentType; |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | FileUtil::CreateFullPath(path); | 239 | Common::FS::CreateFullPath(path); |
| 240 | FileUtil::IOFile file{path, "wb"}; | 240 | Common::FS::IOFile file{path, "wb"}; |
| 241 | if (!file.IsOpen()) | 241 | if (!file.IsOpen()) |
| 242 | return DownloadResult::GeneralFSError; | 242 | return DownloadResult::GeneralFSError; |
| 243 | if (!file.Resize(response->body.size())) | 243 | if (!file.Resize(response->body.size())) |
| @@ -290,7 +290,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe | |||
| 290 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); | 290 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); |
| 291 | 291 | ||
| 292 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { | 292 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { |
| 293 | FileUtil::Delete(zip_path); | 293 | Common::FS::Delete(zip_path); |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | HandleDownloadDisplayResult(applet_manager, res); | 296 | HandleDownloadDisplayResult(applet_manager, res); |
| @@ -300,7 +300,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe | |||
| 300 | 300 | ||
| 301 | progress.StartProcessingDataList(); | 301 | progress.StartProcessingDataList(); |
| 302 | 302 | ||
| 303 | FileUtil::IOFile zip{zip_path, "rb"}; | 303 | Common::FS::IOFile zip{zip_path, "rb"}; |
| 304 | const auto size = zip.GetSize(); | 304 | const auto size = zip.GetSize(); |
| 305 | std::vector<u8> bytes(size); | 305 | std::vector<u8> bytes(size); |
| 306 | if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { | 306 | if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { |
| @@ -365,8 +365,7 @@ bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) | |||
| 365 | 365 | ||
| 366 | std::thread([this, title, &progress] { | 366 | std::thread([this, title, &progress] { |
| 367 | SynchronizeInternal(applet_manager, dir_getter, title, progress); | 367 | SynchronizeInternal(applet_manager, dir_getter, title, progress); |
| 368 | }) | 368 | }).detach(); |
| 369 | .detach(); | ||
| 370 | 369 | ||
| 371 | return true; | 370 | return true; |
| 372 | } | 371 | } |
| @@ -377,8 +376,7 @@ bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, | |||
| 377 | 376 | ||
| 378 | std::thread([this, title, name, &progress] { | 377 | std::thread([this, title, name, &progress] { |
| 379 | SynchronizeInternal(applet_manager, dir_getter, title, progress, name); | 378 | SynchronizeInternal(applet_manager, dir_getter, title, progress, name); |
| 380 | }) | 379 | }).detach(); |
| 381 | .detach(); | ||
| 382 | 380 | ||
| 383 | return true; | 381 | return true; |
| 384 | } | 382 | } |
| @@ -422,7 +420,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) | |||
| 422 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); | 420 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); |
| 423 | 421 | ||
| 424 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { | 422 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { |
| 425 | FileUtil::Delete(path); | 423 | Common::FS::Delete(path); |
| 426 | } | 424 | } |
| 427 | 425 | ||
| 428 | HandleDownloadDisplayResult(applet_manager, res); | 426 | HandleDownloadDisplayResult(applet_manager, res); |
| @@ -430,7 +428,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) | |||
| 430 | } | 428 | } |
| 431 | } | 429 | } |
| 432 | 430 | ||
| 433 | FileUtil::IOFile bin{path, "rb"}; | 431 | Common::FS::IOFile bin{path, "rb"}; |
| 434 | const auto size = bin.GetSize(); | 432 | const auto size = bin.GetSize(); |
| 435 | std::vector<u8> bytes(size); | 433 | std::vector<u8> bytes(size); |
| 436 | if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { | 434 | if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { |
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 603b64d4f..db0e06ca1 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -112,7 +112,7 @@ private: | |||
| 112 | void GetImpl(Kernel::HLERequestContext& ctx) { | 112 | void GetImpl(Kernel::HLERequestContext& ctx) { |
| 113 | LOG_DEBUG(Service_BCAT, "called"); | 113 | LOG_DEBUG(Service_BCAT, "called"); |
| 114 | 114 | ||
| 115 | ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl)); | 115 | ctx.WriteBuffer(impl); |
| 116 | 116 | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2}; | 117 | IPC::ResponseBuilder rb{ctx, 2}; |
| 118 | rb.Push(RESULT_SUCCESS); | 118 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index ab17a187e..a0ee116fa 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 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 "common/logging/log.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 5 | #include "core/hle/service/caps/caps_c.h" | 7 | #include "core/hle/service/caps/caps_c.h" |
| 6 | 8 | ||
| 7 | namespace Service::Capture { | 9 | namespace Service::Capture { |
| @@ -47,7 +49,7 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") { | |||
| 47 | static const FunctionInfo functions[] = { | 49 | static const FunctionInfo functions[] = { |
| 48 | {1, nullptr, "CaptureRawImage"}, | 50 | {1, nullptr, "CaptureRawImage"}, |
| 49 | {2, nullptr, "CaptureRawImageWithTimeout"}, | 51 | {2, nullptr, "CaptureRawImageWithTimeout"}, |
| 50 | {33, nullptr, "Unknown33"}, | 52 | {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"}, |
| 51 | {1001, nullptr, "RequestTakingScreenShot"}, | 53 | {1001, nullptr, "RequestTakingScreenShot"}, |
| 52 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, | 54 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, |
| 53 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, | 55 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, |
| @@ -72,4 +74,16 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") { | |||
| 72 | 74 | ||
| 73 | CAPS_C::~CAPS_C() = default; | 75 | CAPS_C::~CAPS_C() = default; |
| 74 | 76 | ||
| 77 | void CAPS_C::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { | ||
| 78 | IPC::RequestParser rp{ctx}; | ||
| 79 | const auto library_version{rp.Pop<u64>()}; | ||
| 80 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 81 | |||
| 82 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", | ||
| 83 | library_version, applet_resource_user_id); | ||
| 84 | |||
| 85 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 86 | rb.Push(RESULT_SUCCESS); | ||
| 87 | } | ||
| 88 | |||
| 75 | } // namespace Service::Capture | 89 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index a9d028689..b110301d4 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h | |||
| @@ -16,6 +16,9 @@ class CAPS_C final : public ServiceFramework<CAPS_C> { | |||
| 16 | public: | 16 | public: |
| 17 | explicit CAPS_C(); | 17 | explicit CAPS_C(); |
| 18 | ~CAPS_C() override; | 18 | ~CAPS_C() override; |
| 19 | |||
| 20 | private: | ||
| 21 | void SetShimLibraryVersion(Kernel::HLERequestContext& ctx); | ||
| 19 | }; | 22 | }; |
| 20 | 23 | ||
| 21 | } // namespace Service::Capture | 24 | } // namespace Service::Capture |
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index fffb2ecf9..e386470f7 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp | |||
| @@ -25,7 +25,12 @@ CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") { | |||
| 25 | CAPS_SU::~CAPS_SU() = default; | 25 | CAPS_SU::~CAPS_SU() = default; |
| 26 | 26 | ||
| 27 | void CAPS_SU::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { | 27 | void CAPS_SU::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { |
| 28 | LOG_WARNING(Service_Capture, "(STUBBED) called"); | 28 | IPC::RequestParser rp{ctx}; |
| 29 | const auto library_version{rp.Pop<u64>()}; | ||
| 30 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 31 | |||
| 32 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", | ||
| 33 | library_version, applet_resource_user_id); | ||
| 29 | 34 | ||
| 30 | IPC::ResponseBuilder rb{ctx, 2}; | 35 | IPC::ResponseBuilder rb{ctx, 2}; |
| 31 | rb.Push(RESULT_SUCCESS); | 36 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index f36d8de2d..8e2b83629 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp | |||
| @@ -31,8 +31,7 @@ public: | |||
| 31 | CAPS_U::CAPS_U() : ServiceFramework("caps:u") { | 31 | CAPS_U::CAPS_U() : ServiceFramework("caps:u") { |
| 32 | // clang-format off | 32 | // clang-format off |
| 33 | static const FunctionInfo functions[] = { | 33 | static const FunctionInfo functions[] = { |
| 34 | {31, nullptr, "GetShimLibraryVersion"}, | 34 | {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"}, |
| 35 | {32, nullptr, "SetShimLibraryVersion"}, | ||
| 36 | {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, | 35 | {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, |
| 37 | {103, nullptr, "DeleteAlbumContentsFileForApplication"}, | 36 | {103, nullptr, "DeleteAlbumContentsFileForApplication"}, |
| 38 | {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, | 37 | {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, |
| @@ -53,6 +52,18 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") { | |||
| 53 | 52 | ||
| 54 | CAPS_U::~CAPS_U() = default; | 53 | CAPS_U::~CAPS_U() = default; |
| 55 | 54 | ||
| 55 | void CAPS_U::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) { | ||
| 56 | IPC::RequestParser rp{ctx}; | ||
| 57 | const auto library_version{rp.Pop<u64>()}; | ||
| 58 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 59 | |||
| 60 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", | ||
| 61 | library_version, applet_resource_user_id); | ||
| 62 | |||
| 63 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 64 | rb.Push(RESULT_SUCCESS); | ||
| 65 | } | ||
| 66 | |||
| 56 | void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) { | 67 | void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) { |
| 57 | // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an | 68 | // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an |
| 58 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total | 69 | // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total |
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index 689364de4..e04e56bbc 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h | |||
| @@ -18,6 +18,7 @@ public: | |||
| 18 | ~CAPS_U() override; | 18 | ~CAPS_U() override; |
| 19 | 19 | ||
| 20 | private: | 20 | private: |
| 21 | void SetShimLibraryVersion(Kernel::HLERequestContext& ctx); | ||
| 21 | void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); | 22 | void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); |
| 22 | }; | 23 | }; |
| 23 | 24 | ||
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index a41c73c48..c2737a365 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -160,7 +160,7 @@ private: | |||
| 160 | return; | 160 | return; |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | ctx.WriteBuffer(key.data(), key.size()); | 163 | ctx.WriteBuffer(key); |
| 164 | 164 | ||
| 165 | IPC::ResponseBuilder rb{ctx, 2}; | 165 | IPC::ResponseBuilder rb{ctx, 2}; |
| 166 | rb.Push(RESULT_SUCCESS); | 166 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index cadc03805..54a5fb84b 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -36,7 +36,7 @@ constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; | |||
| 36 | 36 | ||
| 37 | static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, | 37 | static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, |
| 38 | std::string_view dir_name_) { | 38 | std::string_view dir_name_) { |
| 39 | std::string dir_name(FileUtil::SanitizePath(dir_name_)); | 39 | std::string dir_name(Common::FS::SanitizePath(dir_name_)); |
| 40 | if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\") | 40 | if (dir_name.empty() || dir_name == "." || dir_name == "/" || dir_name == "\\") |
| 41 | return base; | 41 | return base; |
| 42 | 42 | ||
| @@ -53,9 +53,13 @@ std::string VfsDirectoryServiceWrapper::GetName() const { | |||
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { | 55 | ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 size) const { |
| 56 | std::string path(FileUtil::SanitizePath(path_)); | 56 | std::string path(Common::FS::SanitizePath(path_)); |
| 57 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | 57 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); |
| 58 | auto file = dir->CreateFile(FileUtil::GetFilename(path)); | 58 | // dir can be nullptr if path contains subdirectories, create those prior to creating the file. |
| 59 | if (dir == nullptr) { | ||
| 60 | dir = backing->CreateSubdirectory(Common::FS::GetParentPath(path)); | ||
| 61 | } | ||
| 62 | auto file = dir->CreateFile(Common::FS::GetFilename(path)); | ||
| 59 | if (file == nullptr) { | 63 | if (file == nullptr) { |
| 60 | // TODO(DarkLordZach): Find a better error code for this | 64 | // TODO(DarkLordZach): Find a better error code for this |
| 61 | return RESULT_UNKNOWN; | 65 | return RESULT_UNKNOWN; |
| @@ -68,17 +72,17 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64 | |||
| 68 | } | 72 | } |
| 69 | 73 | ||
| 70 | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { | 74 | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const { |
| 71 | std::string path(FileUtil::SanitizePath(path_)); | 75 | std::string path(Common::FS::SanitizePath(path_)); |
| 72 | if (path.empty()) { | 76 | if (path.empty()) { |
| 73 | // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... | 77 | // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... |
| 74 | return RESULT_SUCCESS; | 78 | return RESULT_SUCCESS; |
| 75 | } | 79 | } |
| 76 | 80 | ||
| 77 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | 81 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); |
| 78 | if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) { | 82 | if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) { |
| 79 | return FileSys::ERROR_PATH_NOT_FOUND; | 83 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 80 | } | 84 | } |
| 81 | if (!dir->DeleteFile(FileUtil::GetFilename(path))) { | 85 | if (!dir->DeleteFile(Common::FS::GetFilename(path))) { |
| 82 | // TODO(DarkLordZach): Find a better error code for this | 86 | // TODO(DarkLordZach): Find a better error code for this |
| 83 | return RESULT_UNKNOWN; | 87 | return RESULT_UNKNOWN; |
| 84 | } | 88 | } |
| @@ -87,11 +91,11 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons | |||
| 87 | } | 91 | } |
| 88 | 92 | ||
| 89 | ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { | 93 | ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const { |
| 90 | std::string path(FileUtil::SanitizePath(path_)); | 94 | std::string path(Common::FS::SanitizePath(path_)); |
| 91 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | 95 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); |
| 92 | if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) | 96 | if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) |
| 93 | dir = backing; | 97 | dir = backing; |
| 94 | auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); | 98 | auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path)); |
| 95 | if (new_dir == nullptr) { | 99 | if (new_dir == nullptr) { |
| 96 | // TODO(DarkLordZach): Find a better error code for this | 100 | // TODO(DarkLordZach): Find a better error code for this |
| 97 | return RESULT_UNKNOWN; | 101 | return RESULT_UNKNOWN; |
| @@ -100,9 +104,9 @@ ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) | |||
| 100 | } | 104 | } |
| 101 | 105 | ||
| 102 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { | 106 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) const { |
| 103 | std::string path(FileUtil::SanitizePath(path_)); | 107 | std::string path(Common::FS::SanitizePath(path_)); |
| 104 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | 108 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); |
| 105 | if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) { | 109 | if (!dir->DeleteSubdirectory(Common::FS::GetFilename(path))) { |
| 106 | // TODO(DarkLordZach): Find a better error code for this | 110 | // TODO(DarkLordZach): Find a better error code for this |
| 107 | return RESULT_UNKNOWN; | 111 | return RESULT_UNKNOWN; |
| 108 | } | 112 | } |
| @@ -110,9 +114,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path_) | |||
| 110 | } | 114 | } |
| 111 | 115 | ||
| 112 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { | 116 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path_) const { |
| 113 | std::string path(FileUtil::SanitizePath(path_)); | 117 | std::string path(Common::FS::SanitizePath(path_)); |
| 114 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | 118 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); |
| 115 | if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) { | 119 | if (!dir->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path))) { |
| 116 | // TODO(DarkLordZach): Find a better error code for this | 120 | // TODO(DarkLordZach): Find a better error code for this |
| 117 | return RESULT_UNKNOWN; | 121 | return RESULT_UNKNOWN; |
| 118 | } | 122 | } |
| @@ -120,10 +124,10 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str | |||
| 120 | } | 124 | } |
| 121 | 125 | ||
| 122 | ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { | 126 | ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const { |
| 123 | const std::string sanitized_path(FileUtil::SanitizePath(path)); | 127 | const std::string sanitized_path(Common::FS::SanitizePath(path)); |
| 124 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path)); | 128 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(sanitized_path)); |
| 125 | 129 | ||
| 126 | if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) { | 130 | if (!dir->CleanSubdirectoryRecursive(Common::FS::GetFilename(sanitized_path))) { |
| 127 | // TODO(DarkLordZach): Find a better error code for this | 131 | // TODO(DarkLordZach): Find a better error code for this |
| 128 | return RESULT_UNKNOWN; | 132 | return RESULT_UNKNOWN; |
| 129 | } | 133 | } |
| @@ -133,14 +137,14 @@ ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::stri | |||
| 133 | 137 | ||
| 134 | ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, | 138 | ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, |
| 135 | const std::string& dest_path_) const { | 139 | const std::string& dest_path_) const { |
| 136 | std::string src_path(FileUtil::SanitizePath(src_path_)); | 140 | std::string src_path(Common::FS::SanitizePath(src_path_)); |
| 137 | std::string dest_path(FileUtil::SanitizePath(dest_path_)); | 141 | std::string dest_path(Common::FS::SanitizePath(dest_path_)); |
| 138 | auto src = backing->GetFileRelative(src_path); | 142 | auto src = backing->GetFileRelative(src_path); |
| 139 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | 143 | if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { |
| 140 | // Use more-optimized vfs implementation rename. | 144 | // Use more-optimized vfs implementation rename. |
| 141 | if (src == nullptr) | 145 | if (src == nullptr) |
| 142 | return FileSys::ERROR_PATH_NOT_FOUND; | 146 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 143 | if (!src->Rename(FileUtil::GetFilename(dest_path))) { | 147 | if (!src->Rename(Common::FS::GetFilename(dest_path))) { |
| 144 | // TODO(DarkLordZach): Find a better error code for this | 148 | // TODO(DarkLordZach): Find a better error code for this |
| 145 | return RESULT_UNKNOWN; | 149 | return RESULT_UNKNOWN; |
| 146 | } | 150 | } |
| @@ -158,7 +162,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, | |||
| 158 | ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), | 162 | ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), |
| 159 | "Could not write all of the bytes but everything else has succeded."); | 163 | "Could not write all of the bytes but everything else has succeded."); |
| 160 | 164 | ||
| 161 | if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) { | 165 | if (!src->GetContainingDirectory()->DeleteFile(Common::FS::GetFilename(src_path))) { |
| 162 | // TODO(DarkLordZach): Find a better error code for this | 166 | // TODO(DarkLordZach): Find a better error code for this |
| 163 | return RESULT_UNKNOWN; | 167 | return RESULT_UNKNOWN; |
| 164 | } | 168 | } |
| @@ -168,14 +172,14 @@ ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_, | |||
| 168 | 172 | ||
| 169 | ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, | 173 | ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path_, |
| 170 | const std::string& dest_path_) const { | 174 | const std::string& dest_path_) const { |
| 171 | std::string src_path(FileUtil::SanitizePath(src_path_)); | 175 | std::string src_path(Common::FS::SanitizePath(src_path_)); |
| 172 | std::string dest_path(FileUtil::SanitizePath(dest_path_)); | 176 | std::string dest_path(Common::FS::SanitizePath(dest_path_)); |
| 173 | auto src = GetDirectoryRelativeWrapped(backing, src_path); | 177 | auto src = GetDirectoryRelativeWrapped(backing, src_path); |
| 174 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | 178 | if (Common::FS::GetParentPath(src_path) == Common::FS::GetParentPath(dest_path)) { |
| 175 | // Use more-optimized vfs implementation rename. | 179 | // Use more-optimized vfs implementation rename. |
| 176 | if (src == nullptr) | 180 | if (src == nullptr) |
| 177 | return FileSys::ERROR_PATH_NOT_FOUND; | 181 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 178 | if (!src->Rename(FileUtil::GetFilename(dest_path))) { | 182 | if (!src->Rename(Common::FS::GetFilename(dest_path))) { |
| 179 | // TODO(DarkLordZach): Find a better error code for this | 183 | // TODO(DarkLordZach): Find a better error code for this |
| 180 | return RESULT_UNKNOWN; | 184 | return RESULT_UNKNOWN; |
| 181 | } | 185 | } |
| @@ -194,7 +198,7 @@ ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_pa | |||
| 194 | 198 | ||
| 195 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, | 199 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path_, |
| 196 | FileSys::Mode mode) const { | 200 | FileSys::Mode mode) const { |
| 197 | const std::string path(FileUtil::SanitizePath(path_)); | 201 | const std::string path(Common::FS::SanitizePath(path_)); |
| 198 | std::string_view npath = path; | 202 | std::string_view npath = path; |
| 199 | while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { | 203 | while (!npath.empty() && (npath[0] == '/' || npath[0] == '\\')) { |
| 200 | npath.remove_prefix(1); | 204 | npath.remove_prefix(1); |
| @@ -214,7 +218,7 @@ ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std:: | |||
| 214 | } | 218 | } |
| 215 | 219 | ||
| 216 | ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { | 220 | ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path_) { |
| 217 | std::string path(FileUtil::SanitizePath(path_)); | 221 | std::string path(Common::FS::SanitizePath(path_)); |
| 218 | auto dir = GetDirectoryRelativeWrapped(backing, path); | 222 | auto dir = GetDirectoryRelativeWrapped(backing, path); |
| 219 | if (dir == nullptr) { | 223 | if (dir == nullptr) { |
| 220 | // TODO(DarkLordZach): Find a better error code for this | 224 | // TODO(DarkLordZach): Find a better error code for this |
| @@ -225,11 +229,11 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s | |||
| 225 | 229 | ||
| 226 | ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | 230 | ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( |
| 227 | const std::string& path_) const { | 231 | const std::string& path_) const { |
| 228 | std::string path(FileUtil::SanitizePath(path_)); | 232 | std::string path(Common::FS::SanitizePath(path_)); |
| 229 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | 233 | auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path)); |
| 230 | if (dir == nullptr) | 234 | if (dir == nullptr) |
| 231 | return FileSys::ERROR_PATH_NOT_FOUND; | 235 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 232 | auto filename = FileUtil::GetFilename(path); | 236 | auto filename = Common::FS::GetFilename(path); |
| 233 | // TODO(Subv): Some games use the '/' path, find out what this means. | 237 | // TODO(Subv): Some games use the '/' path, find out what this means. |
| 234 | if (filename.empty()) | 238 | if (filename.empty()) |
| 235 | return MakeResult(FileSys::EntryType::Directory); | 239 | return MakeResult(FileSys::EntryType::Directory); |
| @@ -307,7 +311,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS( | |||
| 307 | } | 311 | } |
| 308 | 312 | ||
| 309 | ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( | 313 | ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( |
| 310 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const { | 314 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const { |
| 311 | LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", | 315 | LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}", |
| 312 | static_cast<u8>(space), save_struct.DebugInfo()); | 316 | static_cast<u8>(space), save_struct.DebugInfo()); |
| 313 | 317 | ||
| @@ -319,15 +323,15 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData( | |||
| 319 | } | 323 | } |
| 320 | 324 | ||
| 321 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( | 325 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData( |
| 322 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const { | 326 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& attribute) const { |
| 323 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", | 327 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", |
| 324 | static_cast<u8>(space), descriptor.DebugInfo()); | 328 | static_cast<u8>(space), attribute.DebugInfo()); |
| 325 | 329 | ||
| 326 | if (save_data_factory == nullptr) { | 330 | if (save_data_factory == nullptr) { |
| 327 | return FileSys::ERROR_ENTITY_NOT_FOUND; | 331 | return FileSys::ERROR_ENTITY_NOT_FOUND; |
| 328 | } | 332 | } |
| 329 | 333 | ||
| 330 | return save_data_factory->Open(space, descriptor); | 334 | return save_data_factory->Open(space, attribute); |
| 331 | } | 335 | } |
| 332 | 336 | ||
| 333 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( | 337 | ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace( |
| @@ -375,7 +379,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage( | |||
| 375 | return FileSys::ERROR_ENTITY_NOT_FOUND; | 379 | return FileSys::ERROR_ENTITY_NOT_FOUND; |
| 376 | } | 380 | } |
| 377 | 381 | ||
| 378 | auto part = bis_factory->OpenPartitionStorage(id); | 382 | auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem()); |
| 379 | if (part == nullptr) { | 383 | if (part == nullptr) { |
| 380 | return FileSys::ERROR_INVALID_ARGUMENT; | 384 | return FileSys::ERROR_INVALID_ARGUMENT; |
| 381 | } | 385 | } |
| @@ -691,13 +695,13 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove | |||
| 691 | sdmc_factory = nullptr; | 695 | sdmc_factory = nullptr; |
| 692 | } | 696 | } |
| 693 | 697 | ||
| 694 | auto nand_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), | 698 | auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir), |
| 695 | FileSys::Mode::ReadWrite); | 699 | FileSys::Mode::ReadWrite); |
| 696 | auto sd_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), | 700 | auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir), |
| 697 | FileSys::Mode::ReadWrite); | 701 | FileSys::Mode::ReadWrite); |
| 698 | auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), | 702 | auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), |
| 699 | FileSys::Mode::ReadWrite); | 703 | FileSys::Mode::ReadWrite); |
| 700 | auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), | 704 | auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), |
| 701 | FileSys::Mode::ReadWrite); | 705 | FileSys::Mode::ReadWrite); |
| 702 | 706 | ||
| 703 | if (bis_factory == nullptr) { | 707 | if (bis_factory == nullptr) { |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 1b0a6a949..6dbbf0b2b 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -31,7 +31,7 @@ enum class SaveDataSpaceId : u8; | |||
| 31 | enum class SaveDataType : u8; | 31 | enum class SaveDataType : u8; |
| 32 | enum class StorageId : u8; | 32 | enum class StorageId : u8; |
| 33 | 33 | ||
| 34 | struct SaveDataDescriptor; | 34 | struct SaveDataAttribute; |
| 35 | struct SaveDataSize; | 35 | struct SaveDataSize; |
| 36 | } // namespace FileSys | 36 | } // namespace FileSys |
| 37 | 37 | ||
| @@ -69,9 +69,9 @@ public: | |||
| 69 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, | 69 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, |
| 70 | FileSys::ContentRecordType type) const; | 70 | FileSys::ContentRecordType type) const; |
| 71 | ResultVal<FileSys::VirtualDir> CreateSaveData( | 71 | ResultVal<FileSys::VirtualDir> CreateSaveData( |
| 72 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | 72 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; |
| 73 | ResultVal<FileSys::VirtualDir> OpenSaveData( | 73 | ResultVal<FileSys::VirtualDir> OpenSaveData( |
| 74 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const; | 74 | FileSys::SaveDataSpaceId space, const FileSys::SaveDataAttribute& save_struct) const; |
| 75 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; | 75 | ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const; |
| 76 | ResultVal<FileSys::VirtualDir> OpenSDMC() const; | 76 | ResultVal<FileSys::VirtualDir> OpenSDMC() const; |
| 77 | ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; | 77 | ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const; |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 20c331b77..649128be4 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -696,8 +696,8 @@ FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter) | |||
| 696 | {67, nullptr, "FindSaveDataWithFilter"}, | 696 | {67, nullptr, "FindSaveDataWithFilter"}, |
| 697 | {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, | 697 | {68, nullptr, "OpenSaveDataInfoReaderBySaveDataFilter"}, |
| 698 | {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, | 698 | {69, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataAttribute"}, |
| 699 | {70, nullptr, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, | 699 | {70, &FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute, "WriteSaveDataFileSystemExtraDataBySaveDataAttribute"}, |
| 700 | {71, nullptr, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, | 700 | {71, &FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute, "ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute"}, |
| 701 | {80, nullptr, "OpenSaveDataMetaFile"}, | 701 | {80, nullptr, "OpenSaveDataMetaFile"}, |
| 702 | {81, nullptr, "OpenSaveDataTransferManager"}, | 702 | {81, nullptr, "OpenSaveDataTransferManager"}, |
| 703 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, | 703 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, |
| @@ -812,7 +812,7 @@ void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 812 | void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | 812 | void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { |
| 813 | IPC::RequestParser rp{ctx}; | 813 | IPC::RequestParser rp{ctx}; |
| 814 | 814 | ||
| 815 | auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); | 815 | auto save_struct = rp.PopRaw<FileSys::SaveDataAttribute>(); |
| 816 | [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); | 816 | [[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); |
| 817 | u128 uid = rp.PopRaw<u128>(); | 817 | u128 uid = rp.PopRaw<u128>(); |
| 818 | 818 | ||
| @@ -826,31 +826,40 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | |||
| 826 | } | 826 | } |
| 827 | 827 | ||
| 828 | void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { | 828 | void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) { |
| 829 | LOG_INFO(Service_FS, "called."); | 829 | IPC::RequestParser rp{ctx}; |
| 830 | 830 | ||
| 831 | struct Parameters { | 831 | struct Parameters { |
| 832 | FileSys::SaveDataSpaceId save_data_space_id; | 832 | FileSys::SaveDataSpaceId space_id; |
| 833 | FileSys::SaveDataDescriptor descriptor; | 833 | FileSys::SaveDataAttribute attribute; |
| 834 | }; | 834 | }; |
| 835 | 835 | ||
| 836 | IPC::RequestParser rp{ctx}; | ||
| 837 | const auto parameters = rp.PopRaw<Parameters>(); | 836 | const auto parameters = rp.PopRaw<Parameters>(); |
| 838 | 837 | ||
| 839 | auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor); | 838 | LOG_INFO(Service_FS, "called."); |
| 839 | |||
| 840 | auto dir = fsc.OpenSaveData(parameters.space_id, parameters.attribute); | ||
| 840 | if (dir.Failed()) { | 841 | if (dir.Failed()) { |
| 841 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | 842 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; |
| 842 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); | 843 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); |
| 843 | return; | 844 | return; |
| 844 | } | 845 | } |
| 845 | 846 | ||
| 846 | FileSys::StorageId id; | 847 | FileSys::StorageId id{}; |
| 847 | if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) { | 848 | switch (parameters.space_id) { |
| 849 | case FileSys::SaveDataSpaceId::NandUser: | ||
| 848 | id = FileSys::StorageId::NandUser; | 850 | id = FileSys::StorageId::NandUser; |
| 849 | } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem || | 851 | break; |
| 850 | parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) { | 852 | case FileSys::SaveDataSpaceId::SdCardSystem: |
| 853 | case FileSys::SaveDataSpaceId::SdCardUser: | ||
| 851 | id = FileSys::StorageId::SdCard; | 854 | id = FileSys::StorageId::SdCard; |
| 852 | } else { | 855 | break; |
| 856 | case FileSys::SaveDataSpaceId::NandSystem: | ||
| 853 | id = FileSys::StorageId::NandSystem; | 857 | id = FileSys::StorageId::NandSystem; |
| 858 | break; | ||
| 859 | case FileSys::SaveDataSpaceId::TemporaryStorage: | ||
| 860 | case FileSys::SaveDataSpaceId::ProperSystem: | ||
| 861 | case FileSys::SaveDataSpaceId::SafeMode: | ||
| 862 | UNREACHABLE(); | ||
| 854 | } | 863 | } |
| 855 | 864 | ||
| 856 | auto filesystem = | 865 | auto filesystem = |
| @@ -876,22 +885,38 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& | |||
| 876 | rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); | 885 | rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc)); |
| 877 | } | 886 | } |
| 878 | 887 | ||
| 879 | void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 888 | void FSP_SRV::WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx) { |
| 880 | IPC::RequestParser rp{ctx}; | 889 | LOG_WARNING(Service_FS, "(STUBBED) called."); |
| 881 | log_mode = rp.PopEnum<LogMode>(); | ||
| 882 | |||
| 883 | LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); | ||
| 884 | 890 | ||
| 885 | IPC::ResponseBuilder rb{ctx, 2}; | 891 | IPC::ResponseBuilder rb{ctx, 2}; |
| 886 | rb.Push(RESULT_SUCCESS); | 892 | rb.Push(RESULT_SUCCESS); |
| 887 | } | 893 | } |
| 888 | 894 | ||
| 889 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 895 | void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute( |
| 890 | LOG_DEBUG(Service_FS, "called"); | 896 | Kernel::HLERequestContext& ctx) { |
| 897 | IPC::RequestParser rp{ctx}; | ||
| 898 | |||
| 899 | struct Parameters { | ||
| 900 | FileSys::SaveDataSpaceId space_id; | ||
| 901 | FileSys::SaveDataAttribute attribute; | ||
| 902 | }; | ||
| 903 | |||
| 904 | const auto parameters = rp.PopRaw<Parameters>(); | ||
| 905 | // Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData | ||
| 906 | constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None); | ||
| 907 | |||
| 908 | LOG_WARNING(Service_FS, | ||
| 909 | "(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n" | ||
| 910 | "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n" | ||
| 911 | "attribute.type={}, attribute.rank={}, attribute.index={}", | ||
| 912 | flags, static_cast<u32>(parameters.space_id), parameters.attribute.title_id, | ||
| 913 | parameters.attribute.user_id[1], parameters.attribute.user_id[0], | ||
| 914 | parameters.attribute.save_id, static_cast<u32>(parameters.attribute.type), | ||
| 915 | static_cast<u32>(parameters.attribute.rank), parameters.attribute.index); | ||
| 891 | 916 | ||
| 892 | IPC::ResponseBuilder rb{ctx, 3}; | 917 | IPC::ResponseBuilder rb{ctx, 3}; |
| 893 | rb.Push(RESULT_SUCCESS); | 918 | rb.Push(RESULT_SUCCESS); |
| 894 | rb.PushEnum(log_mode); | 919 | rb.Push(flags); |
| 895 | } | 920 | } |
| 896 | 921 | ||
| 897 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | 922 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { |
| @@ -966,6 +991,24 @@ void FSP_SRV::OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ct | |||
| 966 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); | 991 | rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); |
| 967 | } | 992 | } |
| 968 | 993 | ||
| 994 | void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||
| 995 | IPC::RequestParser rp{ctx}; | ||
| 996 | log_mode = rp.PopEnum<LogMode>(); | ||
| 997 | |||
| 998 | LOG_DEBUG(Service_FS, "called, log_mode={:08X}", static_cast<u32>(log_mode)); | ||
| 999 | |||
| 1000 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1001 | rb.Push(RESULT_SUCCESS); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||
| 1005 | LOG_DEBUG(Service_FS, "called"); | ||
| 1006 | |||
| 1007 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1008 | rb.Push(RESULT_SUCCESS); | ||
| 1009 | rb.PushEnum(log_mode); | ||
| 1010 | } | ||
| 1011 | |||
| 969 | void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { | 1012 | void FSP_SRV::OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx) { |
| 970 | const auto raw = ctx.ReadBuffer(); | 1013 | const auto raw = ctx.ReadBuffer(); |
| 971 | auto log = Common::StringFromFixedZeroTerminatedBuffer( | 1014 | auto log = Common::StringFromFixedZeroTerminatedBuffer( |
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index dfb3e395b..4964e874e 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -43,11 +43,13 @@ private: | |||
| 43 | void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); | 43 | void OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx); |
| 44 | void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); | 44 | void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); |
| 45 | void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); | 45 | void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); |
| 46 | void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | 46 | void WriteSaveDataFileSystemExtraDataBySaveDataAttribute(Kernel::HLERequestContext& ctx); |
| 47 | void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | 47 | void ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(Kernel::HLERequestContext& ctx); |
| 48 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 48 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 49 | void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); | 49 | void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); |
| 50 | void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 50 | void OpenPatchDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 51 | void SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | ||
| 52 | void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | ||
| 51 | void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); | 53 | void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); |
| 52 | void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); | 54 | void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); |
| 53 | void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); | 55 | void OpenMultiCommitManager(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h index 8bc69c372..f47a9e61c 100644 --- a/src/core/hle/service/hid/controllers/controller_base.h +++ b/src/core/hle/service/hid/controllers/controller_base.h | |||
| @@ -31,6 +31,10 @@ public: | |||
| 31 | virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 31 | virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 32 | std::size_t size) = 0; | 32 | std::size_t size) = 0; |
| 33 | 33 | ||
| 34 | // When the controller is requesting a motion update for the shared memory | ||
| 35 | virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | ||
| 36 | std::size_t size) {} | ||
| 37 | |||
| 34 | // Called when input devices should be loaded | 38 | // Called when input devices should be loaded |
| 35 | virtual void OnLoadInputDevices() = 0; | 39 | virtual void OnLoadInputDevices() = 0; |
| 36 | 40 | ||
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp index cb35919e9..ad251ed4a 100644 --- a/src/core/hle/service/hid/controllers/debug_pad.cpp +++ b/src/core/hle/service/hid/controllers/debug_pad.cpp | |||
| @@ -39,33 +39,36 @@ void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, | |||
| 39 | 39 | ||
| 40 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 40 | cur_entry.sampling_number = last_entry.sampling_number + 1; |
| 41 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 41 | cur_entry.sampling_number2 = cur_entry.sampling_number; |
| 42 | cur_entry.attribute.connected.Assign(1); | ||
| 43 | auto& pad = cur_entry.pad_state; | ||
| 44 | 42 | ||
| 45 | using namespace Settings::NativeButton; | 43 | if (Settings::values.debug_pad_enabled) { |
| 46 | pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); | 44 | cur_entry.attribute.connected.Assign(1); |
| 47 | pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); | 45 | auto& pad = cur_entry.pad_state; |
| 48 | pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 49 | pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 50 | pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 51 | pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 52 | pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 53 | pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 54 | pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 55 | pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 56 | pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 57 | pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 58 | pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 59 | pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 60 | 46 | ||
| 61 | const auto [stick_l_x_f, stick_l_y_f] = | 47 | using namespace Settings::NativeButton; |
| 62 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | 48 | pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); |
| 63 | const auto [stick_r_x_f, stick_r_y_f] = | 49 | pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); |
| 64 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | 50 | pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); |
| 65 | cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | 51 | pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); |
| 66 | cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | 52 | pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); |
| 67 | cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | 53 | pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); |
| 68 | cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | 54 | pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus()); |
| 55 | pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 56 | pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 57 | pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 58 | pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 59 | pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 60 | pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 61 | pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 62 | |||
| 63 | const auto [stick_l_x_f, stick_l_y_f] = | ||
| 64 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | ||
| 65 | const auto [stick_r_x_f, stick_r_y_f] = | ||
| 66 | analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | ||
| 67 | cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 68 | cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 69 | cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | ||
| 70 | cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | ||
| 71 | } | ||
| 69 | 72 | ||
| 70 | std::memcpy(data, &shared_memory, sizeof(SharedMemory)); | 73 | std::memcpy(data, &shared_memory, sizeof(SharedMemory)); |
| 71 | } | 74 | } |
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp index feae89525..0b896d5ad 100644 --- a/src/core/hle/service/hid/controllers/keyboard.cpp +++ b/src/core/hle/service/hid/controllers/keyboard.cpp | |||
| @@ -40,15 +40,16 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, | |||
| 40 | 40 | ||
| 41 | cur_entry.key.fill(0); | 41 | cur_entry.key.fill(0); |
| 42 | cur_entry.modifier = 0; | 42 | cur_entry.modifier = 0; |
| 43 | 43 | if (Settings::values.keyboard_enabled) { | |
| 44 | for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { | 44 | for (std::size_t i = 0; i < keyboard_keys.size(); ++i) { |
| 45 | cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); | 45 | cur_entry.key[i / KEYS_PER_BYTE] |= |
| 46 | } | 46 | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)); |
| 47 | 47 | } | |
| 48 | for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { | 48 | |
| 49 | cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); | 49 | for (std::size_t i = 0; i < keyboard_mods.size(); ++i) { |
| 50 | cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i); | ||
| 51 | } | ||
| 50 | } | 52 | } |
| 51 | |||
| 52 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); | 53 | std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory)); |
| 53 | } | 54 | } |
| 54 | 55 | ||
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index ef67ad690..2de4ed348 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff; | |||
| 24 | constexpr std::size_t NPAD_OFFSET = 0x9A00; | 24 | constexpr std::size_t NPAD_OFFSET = 0x9A00; |
| 25 | constexpr u32 BATTERY_FULL = 2; | 25 | constexpr u32 BATTERY_FULL = 2; |
| 26 | constexpr u32 MAX_NPAD_ID = 7; | 26 | constexpr u32 MAX_NPAD_ID = 7; |
| 27 | constexpr std::size_t HANDHELD_INDEX = 8; | ||
| 27 | constexpr std::array<u32, 10> npad_id_list{ | 28 | constexpr std::array<u32, 10> npad_id_list{ |
| 28 | 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, | 29 | 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN, |
| 29 | }; | 30 | }; |
| @@ -33,19 +34,41 @@ enum class JoystickId : std::size_t { | |||
| 33 | Joystick_Right, | 34 | Joystick_Right, |
| 34 | }; | 35 | }; |
| 35 | 36 | ||
| 36 | static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) { | 37 | Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad( |
| 38 | Settings::ControllerType type) { | ||
| 37 | switch (type) { | 39 | switch (type) { |
| 38 | case Settings::ControllerType::ProController: | 40 | case Settings::ControllerType::ProController: |
| 39 | return Controller_NPad::NPadControllerType::ProController; | 41 | return NPadControllerType::ProController; |
| 40 | case Settings::ControllerType::DualJoycon: | 42 | case Settings::ControllerType::DualJoyconDetached: |
| 41 | return Controller_NPad::NPadControllerType::JoyDual; | 43 | return NPadControllerType::JoyDual; |
| 42 | case Settings::ControllerType::LeftJoycon: | 44 | case Settings::ControllerType::LeftJoycon: |
| 43 | return Controller_NPad::NPadControllerType::JoyLeft; | 45 | return NPadControllerType::JoyLeft; |
| 44 | case Settings::ControllerType::RightJoycon: | 46 | case Settings::ControllerType::RightJoycon: |
| 45 | return Controller_NPad::NPadControllerType::JoyRight; | 47 | return NPadControllerType::JoyRight; |
| 48 | case Settings::ControllerType::Handheld: | ||
| 49 | return NPadControllerType::Handheld; | ||
| 46 | default: | 50 | default: |
| 47 | UNREACHABLE(); | 51 | UNREACHABLE(); |
| 48 | return Controller_NPad::NPadControllerType::JoyDual; | 52 | return NPadControllerType::ProController; |
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | Settings::ControllerType Controller_NPad::MapNPadToSettingsType( | ||
| 57 | Controller_NPad::NPadControllerType type) { | ||
| 58 | switch (type) { | ||
| 59 | case NPadControllerType::ProController: | ||
| 60 | return Settings::ControllerType::ProController; | ||
| 61 | case NPadControllerType::JoyDual: | ||
| 62 | return Settings::ControllerType::DualJoyconDetached; | ||
| 63 | case NPadControllerType::JoyLeft: | ||
| 64 | return Settings::ControllerType::LeftJoycon; | ||
| 65 | case NPadControllerType::JoyRight: | ||
| 66 | return Settings::ControllerType::RightJoycon; | ||
| 67 | case NPadControllerType::Handheld: | ||
| 68 | return Settings::ControllerType::Handheld; | ||
| 69 | default: | ||
| 70 | UNREACHABLE(); | ||
| 71 | return Settings::ControllerType::ProController; | ||
| 49 | } | 72 | } |
| 50 | } | 73 | } |
| 51 | 74 | ||
| @@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) { | |||
| 60 | case 6: | 83 | case 6: |
| 61 | case 7: | 84 | case 7: |
| 62 | return npad_id; | 85 | return npad_id; |
| 63 | case 8: | 86 | case HANDHELD_INDEX: |
| 64 | case NPAD_HANDHELD: | 87 | case NPAD_HANDHELD: |
| 65 | return 8; | 88 | return HANDHELD_INDEX; |
| 66 | case 9: | 89 | case 9: |
| 67 | case NPAD_UNKNOWN: | 90 | case NPAD_UNKNOWN: |
| 68 | return 9; | 91 | return 9; |
| @@ -83,38 +106,48 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) { | |||
| 83 | case 6: | 106 | case 6: |
| 84 | case 7: | 107 | case 7: |
| 85 | return static_cast<u32>(index); | 108 | return static_cast<u32>(index); |
| 86 | case 8: | 109 | case HANDHELD_INDEX: |
| 87 | return NPAD_HANDHELD; | 110 | return NPAD_HANDHELD; |
| 88 | case 9: | 111 | case 9: |
| 89 | return NPAD_UNKNOWN; | 112 | return NPAD_UNKNOWN; |
| 90 | default: | 113 | default: |
| 91 | UNIMPLEMENTED_MSG("Unknown npad index {}", index); | 114 | UNIMPLEMENTED_MSG("Unknown npad index {}", index); |
| 92 | return 0; | 115 | return 0; |
| 93 | }; | 116 | } |
| 94 | } | 117 | } |
| 95 | 118 | ||
| 96 | Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} | 119 | Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {} |
| 97 | Controller_NPad::~Controller_NPad() = default; | 120 | Controller_NPad::~Controller_NPad() = default; |
| 98 | 121 | ||
| 99 | void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | 122 | void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) { |
| 100 | const auto controller_type = connected_controllers[controller_idx].type; | 123 | const auto controller_type = connected_controllers[controller_idx].type; |
| 101 | auto& controller = shared_memory_entries[controller_idx]; | 124 | auto& controller = shared_memory_entries[controller_idx]; |
| 102 | if (controller_type == NPadControllerType::None) { | 125 | if (controller_type == NPadControllerType::None) { |
| 126 | styleset_changed_events[controller_idx].writable->Signal(); | ||
| 103 | return; | 127 | return; |
| 104 | } | 128 | } |
| 105 | controller.joy_styles.raw = 0; // Zero out | 129 | controller.joy_styles.raw = 0; // Zero out |
| 106 | controller.device_type.raw = 0; | 130 | controller.device_type.raw = 0; |
| 131 | controller.properties.raw = 0; | ||
| 107 | switch (controller_type) { | 132 | switch (controller_type) { |
| 108 | case NPadControllerType::None: | 133 | case NPadControllerType::None: |
| 109 | UNREACHABLE(); | 134 | UNREACHABLE(); |
| 110 | break; | 135 | break; |
| 136 | case NPadControllerType::ProController: | ||
| 137 | controller.joy_styles.pro_controller.Assign(1); | ||
| 138 | controller.device_type.pro_controller.Assign(1); | ||
| 139 | controller.properties.is_vertical.Assign(1); | ||
| 140 | controller.properties.use_plus.Assign(1); | ||
| 141 | controller.properties.use_minus.Assign(1); | ||
| 142 | controller.pad_assignment = NPadAssignments::Single; | ||
| 143 | break; | ||
| 111 | case NPadControllerType::Handheld: | 144 | case NPadControllerType::Handheld: |
| 112 | controller.joy_styles.handheld.Assign(1); | 145 | controller.joy_styles.handheld.Assign(1); |
| 113 | controller.device_type.handheld.Assign(1); | 146 | controller.device_type.handheld.Assign(1); |
| 114 | controller.pad_assignment = NPadAssignments::Dual; | ||
| 115 | controller.properties.is_vertical.Assign(1); | 147 | controller.properties.is_vertical.Assign(1); |
| 116 | controller.properties.use_plus.Assign(1); | 148 | controller.properties.use_plus.Assign(1); |
| 117 | controller.properties.use_minus.Assign(1); | 149 | controller.properties.use_minus.Assign(1); |
| 150 | controller.pad_assignment = NPadAssignments::Dual; | ||
| 118 | break; | 151 | break; |
| 119 | case NPadControllerType::JoyDual: | 152 | case NPadControllerType::JoyDual: |
| 120 | controller.joy_styles.joycon_dual.Assign(1); | 153 | controller.joy_styles.joycon_dual.Assign(1); |
| @@ -144,14 +177,6 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 144 | controller.device_type.pokeball.Assign(1); | 177 | controller.device_type.pokeball.Assign(1); |
| 145 | controller.pad_assignment = NPadAssignments::Single; | 178 | controller.pad_assignment = NPadAssignments::Single; |
| 146 | break; | 179 | break; |
| 147 | case NPadControllerType::ProController: | ||
| 148 | controller.joy_styles.pro_controller.Assign(1); | ||
| 149 | controller.device_type.pro_controller.Assign(1); | ||
| 150 | controller.properties.is_vertical.Assign(1); | ||
| 151 | controller.properties.use_plus.Assign(1); | ||
| 152 | controller.properties.use_minus.Assign(1); | ||
| 153 | controller.pad_assignment = NPadAssignments::Single; | ||
| 154 | break; | ||
| 155 | } | 180 | } |
| 156 | 181 | ||
| 157 | controller.single_color_error = ColorReadError::ReadOk; | 182 | controller.single_color_error = ColorReadError::ReadOk; |
| @@ -168,7 +193,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { | |||
| 168 | controller.battery_level[0] = BATTERY_FULL; | 193 | controller.battery_level[0] = BATTERY_FULL; |
| 169 | controller.battery_level[1] = BATTERY_FULL; | 194 | controller.battery_level[1] = BATTERY_FULL; |
| 170 | controller.battery_level[2] = BATTERY_FULL; | 195 | controller.battery_level[2] = BATTERY_FULL; |
| 171 | styleset_changed_events[controller_idx].writable->Signal(); | 196 | |
| 197 | SignalStyleSetChangedEvent(IndexToNPad(controller_idx)); | ||
| 172 | } | 198 | } |
| 173 | 199 | ||
| 174 | void Controller_NPad::OnInit() { | 200 | void Controller_NPad::OnInit() { |
| @@ -192,36 +218,25 @@ void Controller_NPad::OnInit() { | |||
| 192 | style.pokeball.Assign(1); | 218 | style.pokeball.Assign(1); |
| 193 | } | 219 | } |
| 194 | 220 | ||
| 195 | std::transform( | 221 | std::transform(Settings::values.players.begin(), Settings::values.players.end(), |
| 196 | Settings::values.players.begin(), Settings::values.players.end(), | 222 | connected_controllers.begin(), [](const Settings::PlayerInput& player) { |
| 197 | connected_controllers.begin(), [](const Settings::PlayerInput& player) { | 223 | return ControllerHolder{MapSettingsTypeToNPad(player.controller_type), |
| 198 | return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected}; | 224 | player.connected}; |
| 199 | }); | 225 | }); |
| 200 | |||
| 201 | std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8, | ||
| 202 | [](const ControllerHolder& holder) { return holder.is_connected; }); | ||
| 203 | 226 | ||
| 204 | // Account for handheld | 227 | // Account for handheld |
| 205 | if (connected_controllers[8].is_connected) | 228 | if (connected_controllers[HANDHELD_INDEX].is_connected) { |
| 206 | connected_controllers[8].type = NPadControllerType::Handheld; | 229 | connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld; |
| 230 | } | ||
| 207 | 231 | ||
| 208 | supported_npad_id_types.resize(npad_id_list.size()); | 232 | supported_npad_id_types.resize(npad_id_list.size()); |
| 209 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), | 233 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), |
| 210 | npad_id_list.size() * sizeof(u32)); | 234 | npad_id_list.size() * sizeof(u32)); |
| 211 | 235 | ||
| 212 | // Add a default dual joycon controller if none are present. | ||
| 213 | if (std::none_of(connected_controllers.begin(), connected_controllers.end(), | ||
| 214 | [](const ControllerHolder& controller) { return controller.is_connected; })) { | ||
| 215 | supported_npad_id_types.resize(npad_id_list.size()); | ||
| 216 | std::memcpy(supported_npad_id_types.data(), npad_id_list.data(), | ||
| 217 | npad_id_list.size() * sizeof(u32)); | ||
| 218 | AddNewController(NPadControllerType::JoyDual); | ||
| 219 | } | ||
| 220 | |||
| 221 | for (std::size_t i = 0; i < connected_controllers.size(); ++i) { | 236 | for (std::size_t i = 0; i < connected_controllers.size(); ++i) { |
| 222 | const auto& controller = connected_controllers[i]; | 237 | const auto& controller = connected_controllers[i]; |
| 223 | if (controller.is_connected) { | 238 | if (controller.is_connected) { |
| 224 | AddNewControllerAt(controller.type, IndexToNPad(i)); | 239 | AddNewControllerAt(controller.type, i); |
| 225 | } | 240 | } |
| 226 | } | 241 | } |
| 227 | } | 242 | } |
| @@ -235,6 +250,9 @@ void Controller_NPad::OnLoadInputDevices() { | |||
| 235 | std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | 250 | std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, |
| 236 | players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, | 251 | players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END, |
| 237 | sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); | 252 | sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>); |
| 253 | std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN, | ||
| 254 | players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END, | ||
| 255 | motions[i].begin(), Input::CreateDevice<Input::MotionDevice>); | ||
| 238 | } | 256 | } |
| 239 | } | 257 | } |
| 240 | 258 | ||
| @@ -242,7 +260,7 @@ void Controller_NPad::OnRelease() {} | |||
| 242 | 260 | ||
| 243 | void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | 261 | void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { |
| 244 | const auto controller_idx = NPadIdToIndex(npad_id); | 262 | const auto controller_idx = NPadIdToIndex(npad_id); |
| 245 | [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type; | 263 | const auto controller_type = connected_controllers[controller_idx].type; |
| 246 | if (!connected_controllers[controller_idx].is_connected) { | 264 | if (!connected_controllers[controller_idx].is_connected) { |
| 247 | return; | 265 | return; |
| 248 | } | 266 | } |
| @@ -251,66 +269,77 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) { | |||
| 251 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; | 269 | auto& rstick_entry = npad_pad_states[controller_idx].r_stick; |
| 252 | const auto& button_state = buttons[controller_idx]; | 270 | const auto& button_state = buttons[controller_idx]; |
| 253 | const auto& analog_state = sticks[controller_idx]; | 271 | const auto& analog_state = sticks[controller_idx]; |
| 272 | const auto& motion_state = motions[controller_idx]; | ||
| 254 | const auto [stick_l_x_f, stick_l_y_f] = | 273 | const auto [stick_l_x_f, stick_l_y_f] = |
| 255 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); | 274 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus(); |
| 256 | const auto [stick_r_x_f, stick_r_y_f] = | 275 | const auto [stick_r_x_f, stick_r_y_f] = |
| 257 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); | 276 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus(); |
| 258 | 277 | ||
| 259 | using namespace Settings::NativeButton; | 278 | using namespace Settings::NativeButton; |
| 260 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); | 279 | if (controller_type != NPadControllerType::JoyLeft) { |
| 261 | pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); | 280 | pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus()); |
| 262 | pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); | 281 | pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus()); |
| 263 | pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); | 282 | pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus()); |
| 264 | pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); | 283 | pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus()); |
| 265 | pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); | 284 | pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus()); |
| 266 | pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); | 285 | pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); |
| 267 | pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus()); | 286 | pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); |
| 268 | pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); | 287 | pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); |
| 269 | pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus()); | 288 | |
| 270 | pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus()); | 289 | pad_state.r_stick_right.Assign( |
| 271 | pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); | 290 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] |
| 272 | 291 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); | |
| 273 | pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); | 292 | pad_state.r_stick_left.Assign( |
| 274 | pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); | 293 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] |
| 275 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); | 294 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); |
| 276 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); | 295 | pad_state.r_stick_up.Assign( |
| 277 | 296 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | |
| 278 | pad_state.l_stick_right.Assign( | 297 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); |
| 279 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | 298 | pad_state.r_stick_down.Assign( |
| 280 | Input::AnalogDirection::RIGHT)); | 299 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] |
| 281 | pad_state.l_stick_left.Assign( | 300 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); |
| 282 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | 301 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); |
| 283 | Input::AnalogDirection::LEFT)); | 302 | rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); |
| 284 | pad_state.l_stick_up.Assign( | 303 | } |
| 285 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | 304 | |
| 286 | Input::AnalogDirection::UP)); | 305 | if (controller_type != NPadControllerType::JoyRight) { |
| 287 | pad_state.l_stick_down.Assign( | 306 | pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus()); |
| 288 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus( | 307 | pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus()); |
| 289 | Input::AnalogDirection::DOWN)); | 308 | pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus()); |
| 290 | 309 | pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus()); | |
| 291 | pad_state.r_stick_right.Assign( | 310 | pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus()); |
| 292 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | 311 | pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus()); |
| 293 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); | 312 | pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus()); |
| 294 | pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | 313 | pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus()); |
| 295 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); | 314 | |
| 296 | pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | 315 | pad_state.l_stick_right.Assign( |
| 297 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); | 316 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] |
| 298 | pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)] | 317 | ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT)); |
| 299 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); | 318 | pad_state.l_stick_left.Assign( |
| 300 | 319 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] | |
| 301 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); | 320 | ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT)); |
| 302 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); | 321 | pad_state.l_stick_up.Assign( |
| 303 | 322 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] | |
| 304 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | 323 | ->GetAnalogDirectionStatus(Input::AnalogDirection::UP)); |
| 305 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | 324 | pad_state.l_stick_down.Assign( |
| 306 | rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX); | 325 | analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)] |
| 307 | rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX); | 326 | ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN)); |
| 327 | lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX); | ||
| 328 | lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX); | ||
| 329 | } | ||
| 330 | |||
| 331 | if (controller_type == NPadControllerType::JoyLeft || | ||
| 332 | controller_type == NPadControllerType::JoyRight) { | ||
| 333 | pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 334 | pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus()); | ||
| 335 | } | ||
| 308 | } | 336 | } |
| 309 | 337 | ||
| 310 | void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | 338 | void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, |
| 311 | std::size_t data_len) { | 339 | std::size_t data_len) { |
| 312 | if (!IsControllerActivated()) | 340 | if (!IsControllerActivated()) { |
| 313 | return; | 341 | return; |
| 342 | } | ||
| 314 | for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { | 343 | for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { |
| 315 | auto& npad = shared_memory_entries[i]; | 344 | auto& npad = shared_memory_entries[i]; |
| 316 | const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, | 345 | const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states, |
| @@ -344,6 +373,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 344 | continue; | 373 | continue; |
| 345 | } | 374 | } |
| 346 | const u32 npad_index = static_cast<u32>(i); | 375 | const u32 npad_index = static_cast<u32>(i); |
| 376 | |||
| 347 | RequestPadStateUpdate(npad_index); | 377 | RequestPadStateUpdate(npad_index); |
| 348 | auto& pad_state = npad_pad_states[npad_index]; | 378 | auto& pad_state = npad_pad_states[npad_index]; |
| 349 | 379 | ||
| @@ -360,13 +390,37 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 360 | auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; | 390 | auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; |
| 361 | 391 | ||
| 362 | libnx_entry.connection_status.raw = 0; | 392 | libnx_entry.connection_status.raw = 0; |
| 393 | libnx_entry.connection_status.IsConnected.Assign(1); | ||
| 394 | auto& full_sixaxis_entry = | ||
| 395 | npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index]; | ||
| 396 | auto& handheld_sixaxis_entry = | ||
| 397 | npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index]; | ||
| 398 | auto& dual_left_sixaxis_entry = | ||
| 399 | npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index]; | ||
| 400 | auto& dual_right_sixaxis_entry = | ||
| 401 | npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index]; | ||
| 402 | auto& left_sixaxis_entry = | ||
| 403 | npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index]; | ||
| 404 | auto& right_sixaxis_entry = | ||
| 405 | npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index]; | ||
| 363 | 406 | ||
| 364 | switch (controller_type) { | 407 | switch (controller_type) { |
| 365 | case NPadControllerType::None: | 408 | case NPadControllerType::None: |
| 366 | UNREACHABLE(); | 409 | UNREACHABLE(); |
| 367 | break; | 410 | break; |
| 411 | case NPadControllerType::ProController: | ||
| 412 | main_controller.connection_status.raw = 0; | ||
| 413 | main_controller.connection_status.IsConnected.Assign(1); | ||
| 414 | main_controller.connection_status.IsWired.Assign(1); | ||
| 415 | main_controller.pad.pad_states.raw = pad_state.pad_states.raw; | ||
| 416 | main_controller.pad.l_stick = pad_state.l_stick; | ||
| 417 | main_controller.pad.r_stick = pad_state.r_stick; | ||
| 418 | |||
| 419 | libnx_entry.connection_status.IsWired.Assign(1); | ||
| 420 | break; | ||
| 368 | case NPadControllerType::Handheld: | 421 | case NPadControllerType::Handheld: |
| 369 | handheld_entry.connection_status.raw = 0; | 422 | handheld_entry.connection_status.raw = 0; |
| 423 | handheld_entry.connection_status.IsConnected.Assign(1); | ||
| 370 | handheld_entry.connection_status.IsWired.Assign(1); | 424 | handheld_entry.connection_status.IsWired.Assign(1); |
| 371 | handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); | 425 | handheld_entry.connection_status.IsLeftJoyConnected.Assign(1); |
| 372 | handheld_entry.connection_status.IsRightJoyConnected.Assign(1); | 426 | handheld_entry.connection_status.IsRightJoyConnected.Assign(1); |
| @@ -375,57 +429,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 375 | handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 429 | handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 376 | handheld_entry.pad.l_stick = pad_state.l_stick; | 430 | handheld_entry.pad.l_stick = pad_state.l_stick; |
| 377 | handheld_entry.pad.r_stick = pad_state.r_stick; | 431 | handheld_entry.pad.r_stick = pad_state.r_stick; |
| 432 | |||
| 433 | libnx_entry.connection_status.IsWired.Assign(1); | ||
| 434 | libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); | ||
| 435 | libnx_entry.connection_status.IsRightJoyConnected.Assign(1); | ||
| 436 | libnx_entry.connection_status.IsLeftJoyWired.Assign(1); | ||
| 437 | libnx_entry.connection_status.IsRightJoyWired.Assign(1); | ||
| 378 | break; | 438 | break; |
| 379 | case NPadControllerType::JoyDual: | 439 | case NPadControllerType::JoyDual: |
| 380 | dual_entry.connection_status.raw = 0; | 440 | dual_entry.connection_status.raw = 0; |
| 381 | 441 | dual_entry.connection_status.IsConnected.Assign(1); | |
| 382 | dual_entry.connection_status.IsLeftJoyConnected.Assign(1); | 442 | dual_entry.connection_status.IsLeftJoyConnected.Assign(1); |
| 383 | dual_entry.connection_status.IsRightJoyConnected.Assign(1); | 443 | dual_entry.connection_status.IsRightJoyConnected.Assign(1); |
| 384 | dual_entry.connection_status.IsConnected.Assign(1); | ||
| 385 | |||
| 386 | libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); | ||
| 387 | libnx_entry.connection_status.IsRightJoyConnected.Assign(1); | ||
| 388 | libnx_entry.connection_status.IsConnected.Assign(1); | ||
| 389 | |||
| 390 | dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 444 | dual_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 391 | dual_entry.pad.l_stick = pad_state.l_stick; | 445 | dual_entry.pad.l_stick = pad_state.l_stick; |
| 392 | dual_entry.pad.r_stick = pad_state.r_stick; | 446 | dual_entry.pad.r_stick = pad_state.r_stick; |
| 447 | |||
| 448 | libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); | ||
| 449 | libnx_entry.connection_status.IsRightJoyConnected.Assign(1); | ||
| 393 | break; | 450 | break; |
| 394 | case NPadControllerType::JoyLeft: | 451 | case NPadControllerType::JoyLeft: |
| 395 | left_entry.connection_status.raw = 0; | 452 | left_entry.connection_status.raw = 0; |
| 396 | |||
| 397 | left_entry.connection_status.IsConnected.Assign(1); | 453 | left_entry.connection_status.IsConnected.Assign(1); |
| 454 | left_entry.connection_status.IsLeftJoyConnected.Assign(1); | ||
| 398 | left_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 455 | left_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 399 | left_entry.pad.l_stick = pad_state.l_stick; | 456 | left_entry.pad.l_stick = pad_state.l_stick; |
| 400 | left_entry.pad.r_stick = pad_state.r_stick; | 457 | left_entry.pad.r_stick = pad_state.r_stick; |
| 458 | |||
| 459 | libnx_entry.connection_status.IsLeftJoyConnected.Assign(1); | ||
| 401 | break; | 460 | break; |
| 402 | case NPadControllerType::JoyRight: | 461 | case NPadControllerType::JoyRight: |
| 403 | right_entry.connection_status.raw = 0; | 462 | right_entry.connection_status.raw = 0; |
| 404 | |||
| 405 | right_entry.connection_status.IsConnected.Assign(1); | 463 | right_entry.connection_status.IsConnected.Assign(1); |
| 464 | right_entry.connection_status.IsRightJoyConnected.Assign(1); | ||
| 406 | right_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 465 | right_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 407 | right_entry.pad.l_stick = pad_state.l_stick; | 466 | right_entry.pad.l_stick = pad_state.l_stick; |
| 408 | right_entry.pad.r_stick = pad_state.r_stick; | 467 | right_entry.pad.r_stick = pad_state.r_stick; |
| 468 | |||
| 469 | libnx_entry.connection_status.IsRightJoyConnected.Assign(1); | ||
| 409 | break; | 470 | break; |
| 410 | case NPadControllerType::Pokeball: | 471 | case NPadControllerType::Pokeball: |
| 411 | pokeball_entry.connection_status.raw = 0; | 472 | pokeball_entry.connection_status.raw = 0; |
| 412 | |||
| 413 | pokeball_entry.connection_status.IsConnected.Assign(1); | 473 | pokeball_entry.connection_status.IsConnected.Assign(1); |
| 414 | pokeball_entry.connection_status.IsWired.Assign(1); | ||
| 415 | |||
| 416 | pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; | 474 | pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw; |
| 417 | pokeball_entry.pad.l_stick = pad_state.l_stick; | 475 | pokeball_entry.pad.l_stick = pad_state.l_stick; |
| 418 | pokeball_entry.pad.r_stick = pad_state.r_stick; | 476 | pokeball_entry.pad.r_stick = pad_state.r_stick; |
| 419 | break; | 477 | break; |
| 420 | case NPadControllerType::ProController: | ||
| 421 | main_controller.connection_status.raw = 0; | ||
| 422 | |||
| 423 | main_controller.connection_status.IsConnected.Assign(1); | ||
| 424 | main_controller.connection_status.IsWired.Assign(1); | ||
| 425 | main_controller.pad.pad_states.raw = pad_state.pad_states.raw; | ||
| 426 | main_controller.pad.l_stick = pad_state.l_stick; | ||
| 427 | main_controller.pad.r_stick = pad_state.r_stick; | ||
| 428 | break; | ||
| 429 | } | 478 | } |
| 430 | 479 | ||
| 431 | // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate | 480 | // LibNX exclusively uses this section, so we always update it since LibNX doesn't activate |
| @@ -440,6 +489,143 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* | |||
| 440 | shared_memory_entries.size() * sizeof(NPadEntry)); | 489 | shared_memory_entries.size() * sizeof(NPadEntry)); |
| 441 | } | 490 | } |
| 442 | 491 | ||
| 492 | void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | ||
| 493 | std::size_t data_len) { | ||
| 494 | if (!IsControllerActivated()) { | ||
| 495 | return; | ||
| 496 | } | ||
| 497 | for (std::size_t i = 0; i < shared_memory_entries.size(); i++) { | ||
| 498 | auto& npad = shared_memory_entries[i]; | ||
| 499 | |||
| 500 | const auto& controller_type = connected_controllers[i].type; | ||
| 501 | |||
| 502 | if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) { | ||
| 503 | continue; | ||
| 504 | } | ||
| 505 | |||
| 506 | const std::array<SixAxisGeneric*, 6> controller_sixaxes{ | ||
| 507 | &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left, | ||
| 508 | &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right, | ||
| 509 | }; | ||
| 510 | |||
| 511 | for (auto* sixaxis_sensor : controller_sixaxes) { | ||
| 512 | sixaxis_sensor->common.entry_count = 16; | ||
| 513 | sixaxis_sensor->common.total_entry_count = 17; | ||
| 514 | |||
| 515 | const auto& last_entry = | ||
| 516 | sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; | ||
| 517 | |||
| 518 | sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks(); | ||
| 519 | sixaxis_sensor->common.last_entry_index = | ||
| 520 | (sixaxis_sensor->common.last_entry_index + 1) % 17; | ||
| 521 | |||
| 522 | auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index]; | ||
| 523 | |||
| 524 | cur_entry.timestamp = last_entry.timestamp + 1; | ||
| 525 | cur_entry.timestamp2 = cur_entry.timestamp; | ||
| 526 | } | ||
| 527 | |||
| 528 | // Try to read sixaxis sensor states | ||
| 529 | std::array<MotionDevice, 2> motion_devices; | ||
| 530 | |||
| 531 | if (sixaxis_sensors_enabled && Settings::values.motion_enabled) { | ||
| 532 | sixaxis_at_rest = true; | ||
| 533 | for (std::size_t e = 0; e < motion_devices.size(); ++e) { | ||
| 534 | const auto& device = motions[i][e]; | ||
| 535 | if (device) { | ||
| 536 | std::tie(motion_devices[e].accel, motion_devices[e].gyro, | ||
| 537 | motion_devices[e].rotation, motion_devices[e].orientation) = | ||
| 538 | device->GetStatus(); | ||
| 539 | sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f; | ||
| 540 | } | ||
| 541 | } | ||
| 542 | } | ||
| 543 | |||
| 544 | auto& main_controller = | ||
| 545 | npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index]; | ||
| 546 | auto& handheld_entry = | ||
| 547 | npad.handheld_states.npad[npad.handheld_states.common.last_entry_index]; | ||
| 548 | auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index]; | ||
| 549 | auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index]; | ||
| 550 | auto& right_entry = | ||
| 551 | npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index]; | ||
| 552 | auto& pokeball_entry = | ||
| 553 | npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index]; | ||
| 554 | auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index]; | ||
| 555 | |||
| 556 | auto& full_sixaxis_entry = | ||
| 557 | npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index]; | ||
| 558 | auto& handheld_sixaxis_entry = | ||
| 559 | npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index]; | ||
| 560 | auto& dual_left_sixaxis_entry = | ||
| 561 | npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index]; | ||
| 562 | auto& dual_right_sixaxis_entry = | ||
| 563 | npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index]; | ||
| 564 | auto& left_sixaxis_entry = | ||
| 565 | npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index]; | ||
| 566 | auto& right_sixaxis_entry = | ||
| 567 | npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index]; | ||
| 568 | |||
| 569 | switch (controller_type) { | ||
| 570 | case NPadControllerType::None: | ||
| 571 | UNREACHABLE(); | ||
| 572 | break; | ||
| 573 | case NPadControllerType::ProController: | ||
| 574 | if (sixaxis_sensors_enabled && motions[i][0]) { | ||
| 575 | full_sixaxis_entry.accel = motion_devices[0].accel; | ||
| 576 | full_sixaxis_entry.gyro = motion_devices[0].gyro; | ||
| 577 | full_sixaxis_entry.rotation = motion_devices[0].rotation; | ||
| 578 | full_sixaxis_entry.orientation = motion_devices[0].orientation; | ||
| 579 | } | ||
| 580 | break; | ||
| 581 | case NPadControllerType::Handheld: | ||
| 582 | if (sixaxis_sensors_enabled && motions[i][0]) { | ||
| 583 | handheld_sixaxis_entry.accel = motion_devices[0].accel; | ||
| 584 | handheld_sixaxis_entry.gyro = motion_devices[0].gyro; | ||
| 585 | handheld_sixaxis_entry.rotation = motion_devices[0].rotation; | ||
| 586 | handheld_sixaxis_entry.orientation = motion_devices[0].orientation; | ||
| 587 | } | ||
| 588 | break; | ||
| 589 | case NPadControllerType::JoyDual: | ||
| 590 | if (sixaxis_sensors_enabled && motions[i][0]) { | ||
| 591 | // Set motion for the left joycon | ||
| 592 | dual_left_sixaxis_entry.accel = motion_devices[0].accel; | ||
| 593 | dual_left_sixaxis_entry.gyro = motion_devices[0].gyro; | ||
| 594 | dual_left_sixaxis_entry.rotation = motion_devices[0].rotation; | ||
| 595 | dual_left_sixaxis_entry.orientation = motion_devices[0].orientation; | ||
| 596 | } | ||
| 597 | if (sixaxis_sensors_enabled && motions[i][1]) { | ||
| 598 | // Set motion for the right joycon | ||
| 599 | dual_right_sixaxis_entry.accel = motion_devices[1].accel; | ||
| 600 | dual_right_sixaxis_entry.gyro = motion_devices[1].gyro; | ||
| 601 | dual_right_sixaxis_entry.rotation = motion_devices[1].rotation; | ||
| 602 | dual_right_sixaxis_entry.orientation = motion_devices[1].orientation; | ||
| 603 | } | ||
| 604 | break; | ||
| 605 | case NPadControllerType::JoyLeft: | ||
| 606 | if (sixaxis_sensors_enabled && motions[i][0]) { | ||
| 607 | left_sixaxis_entry.accel = motion_devices[0].accel; | ||
| 608 | left_sixaxis_entry.gyro = motion_devices[0].gyro; | ||
| 609 | left_sixaxis_entry.rotation = motion_devices[0].rotation; | ||
| 610 | left_sixaxis_entry.orientation = motion_devices[0].orientation; | ||
| 611 | } | ||
| 612 | break; | ||
| 613 | case NPadControllerType::JoyRight: | ||
| 614 | if (sixaxis_sensors_enabled && motions[i][1]) { | ||
| 615 | right_sixaxis_entry.accel = motion_devices[1].accel; | ||
| 616 | right_sixaxis_entry.gyro = motion_devices[1].gyro; | ||
| 617 | right_sixaxis_entry.rotation = motion_devices[1].rotation; | ||
| 618 | right_sixaxis_entry.orientation = motion_devices[1].orientation; | ||
| 619 | } | ||
| 620 | break; | ||
| 621 | case NPadControllerType::Pokeball: | ||
| 622 | break; | ||
| 623 | } | ||
| 624 | } | ||
| 625 | std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(), | ||
| 626 | shared_memory_entries.size() * sizeof(NPadEntry)); | ||
| 627 | } | ||
| 628 | |||
| 443 | void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { | 629 | void Controller_NPad::SetSupportedStyleSet(NPadType style_set) { |
| 444 | style.raw = style_set.raw; | 630 | style.raw = style_set.raw; |
| 445 | } | 631 | } |
| @@ -453,26 +639,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) { | |||
| 453 | supported_npad_id_types.clear(); | 639 | supported_npad_id_types.clear(); |
| 454 | supported_npad_id_types.resize(length / sizeof(u32)); | 640 | supported_npad_id_types.resize(length / sizeof(u32)); |
| 455 | std::memcpy(supported_npad_id_types.data(), data, length); | 641 | std::memcpy(supported_npad_id_types.data(), data, length); |
| 456 | for (std::size_t i = 0; i < connected_controllers.size(); i++) { | ||
| 457 | auto& controller = connected_controllers[i]; | ||
| 458 | if (!controller.is_connected) { | ||
| 459 | continue; | ||
| 460 | } | ||
| 461 | const auto requested_controller = | ||
| 462 | i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type) | ||
| 463 | : NPadControllerType::Handheld; | ||
| 464 | if (!IsControllerSupported(requested_controller)) { | ||
| 465 | const auto is_handheld = requested_controller == NPadControllerType::Handheld; | ||
| 466 | if (is_handheld) { | ||
| 467 | controller.type = NPadControllerType::None; | ||
| 468 | controller.is_connected = false; | ||
| 469 | AddNewController(requested_controller); | ||
| 470 | } else { | ||
| 471 | controller.type = requested_controller; | ||
| 472 | InitNewlyAddedControler(i); | ||
| 473 | } | ||
| 474 | } | ||
| 475 | } | ||
| 476 | } | 642 | } |
| 477 | 643 | ||
| 478 | void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { | 644 | void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) { |
| @@ -492,6 +658,14 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const { | |||
| 492 | return hold_type; | 658 | return hold_type; |
| 493 | } | 659 | } |
| 494 | 660 | ||
| 661 | void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) { | ||
| 662 | handheld_activation_mode = activation_mode; | ||
| 663 | } | ||
| 664 | |||
| 665 | Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const { | ||
| 666 | return handheld_activation_mode; | ||
| 667 | } | ||
| 668 | |||
| 495 | void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { | 669 | void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { |
| 496 | const std::size_t npad_index = NPadIdToIndex(npad_id); | 670 | const std::size_t npad_index = NPadIdToIndex(npad_id); |
| 497 | ASSERT(npad_index < shared_memory_entries.size()); | 671 | ASSERT(npad_index < shared_memory_entries.size()); |
| @@ -500,70 +674,86 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) | |||
| 500 | } | 674 | } |
| 501 | } | 675 | } |
| 502 | 676 | ||
| 503 | void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, | 677 | void Controller_NPad::VibrateController(const std::vector<u32>& controllers, |
| 504 | const std::vector<Vibration>& vibrations) { | 678 | const std::vector<Vibration>& vibrations) { |
| 505 | LOG_DEBUG(Service_HID, "(STUBBED) called"); | 679 | LOG_TRACE(Service_HID, "called"); |
| 506 | 680 | ||
| 507 | if (!can_controllers_vibrate) { | 681 | if (!Settings::values.vibration_enabled || !can_controllers_vibrate) { |
| 508 | return; | 682 | return; |
| 509 | } | 683 | } |
| 510 | for (std::size_t i = 0; i < controller_ids.size(); i++) { | 684 | bool success = true; |
| 511 | std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i)); | 685 | for (std::size_t i = 0; i < controllers.size(); ++i) { |
| 512 | if (connected_controllers[controller_pos].is_connected) { | 686 | if (!connected_controllers[i].is_connected) { |
| 513 | // TODO(ogniK): Vibrate the physical controller | 687 | continue; |
| 688 | } | ||
| 689 | using namespace Settings::NativeButton; | ||
| 690 | const auto& button_state = buttons[i]; | ||
| 691 | if (button_state[A - BUTTON_HID_BEGIN]) { | ||
| 692 | if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay( | ||
| 693 | vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high, | ||
| 694 | vibrations[0].freq_low)) { | ||
| 695 | success = false; | ||
| 696 | } | ||
| 514 | } | 697 | } |
| 515 | } | 698 | } |
| 516 | last_processed_vibration = vibrations.back(); | 699 | if (success) { |
| 700 | last_processed_vibration = vibrations.back(); | ||
| 701 | } | ||
| 702 | } | ||
| 703 | |||
| 704 | Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { | ||
| 705 | return last_processed_vibration; | ||
| 517 | } | 706 | } |
| 518 | 707 | ||
| 519 | std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { | 708 | std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const { |
| 520 | // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should | ||
| 521 | // be signalled at least once, and signaled after a new controller is connected? | ||
| 522 | const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; | 709 | const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)]; |
| 523 | return styleset_event.readable; | 710 | return styleset_event.readable; |
| 524 | } | 711 | } |
| 525 | 712 | ||
| 526 | Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { | 713 | void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const { |
| 527 | return last_processed_vibration; | 714 | styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal(); |
| 528 | } | 715 | } |
| 529 | 716 | ||
| 530 | void Controller_NPad::AddNewController(NPadControllerType controller) { | 717 | void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) { |
| 531 | controller = DecideBestController(controller); | 718 | UpdateControllerAt(controller, npad_index, true); |
| 532 | if (controller == NPadControllerType::Handheld) { | 719 | } |
| 533 | connected_controllers[8] = {controller, true}; | 720 | |
| 534 | InitNewlyAddedControler(8); | 721 | void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, |
| 535 | return; | 722 | bool connected) { |
| 536 | } | 723 | if (!connected) { |
| 537 | const auto pos = | 724 | DisconnectNPadAtIndex(npad_index); |
| 538 | std::find_if(connected_controllers.begin(), connected_controllers.end() - 2, | ||
| 539 | [](const ControllerHolder& holder) { return !holder.is_connected; }); | ||
| 540 | if (pos == connected_controllers.end() - 2) { | ||
| 541 | LOG_ERROR(Service_HID, "Cannot connect any more controllers!"); | ||
| 542 | return; | 725 | return; |
| 543 | } | 726 | } |
| 544 | const auto controller_id = std::distance(connected_controllers.begin(), pos); | ||
| 545 | connected_controllers[controller_id] = {controller, true}; | ||
| 546 | InitNewlyAddedControler(controller_id); | ||
| 547 | } | ||
| 548 | 727 | ||
| 549 | void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) { | ||
| 550 | controller = DecideBestController(controller); | ||
| 551 | if (controller == NPadControllerType::Handheld) { | 728 | if (controller == NPadControllerType::Handheld) { |
| 552 | connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true}; | 729 | Settings::values.players[HANDHELD_INDEX].controller_type = |
| 553 | InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD)); | 730 | MapNPadToSettingsType(controller); |
| 731 | Settings::values.players[HANDHELD_INDEX].connected = true; | ||
| 732 | connected_controllers[HANDHELD_INDEX] = {controller, true}; | ||
| 733 | InitNewlyAddedController(HANDHELD_INDEX); | ||
| 554 | return; | 734 | return; |
| 555 | } | 735 | } |
| 556 | 736 | ||
| 557 | connected_controllers[NPadIdToIndex(npad_id)] = {controller, true}; | 737 | Settings::values.players[npad_index].controller_type = MapNPadToSettingsType(controller); |
| 558 | InitNewlyAddedControler(NPadIdToIndex(npad_id)); | 738 | Settings::values.players[npad_index].connected = true; |
| 739 | connected_controllers[npad_index] = {controller, true}; | ||
| 740 | InitNewlyAddedController(npad_index); | ||
| 559 | } | 741 | } |
| 560 | 742 | ||
| 561 | void Controller_NPad::ConnectNPad(u32 npad_id) { | 743 | void Controller_NPad::DisconnectNPad(u32 npad_id) { |
| 562 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = true; | 744 | DisconnectNPadAtIndex(NPadIdToIndex(npad_id)); |
| 563 | } | 745 | } |
| 564 | 746 | ||
| 565 | void Controller_NPad::DisconnectNPad(u32 npad_id) { | 747 | void Controller_NPad::DisconnectNPadAtIndex(std::size_t npad_index) { |
| 566 | connected_controllers[NPadIdToIndex(npad_id)].is_connected = false; | 748 | Settings::values.players[npad_index].connected = false; |
| 749 | connected_controllers[npad_index].is_connected = false; | ||
| 750 | |||
| 751 | auto& controller = shared_memory_entries[npad_index]; | ||
| 752 | controller.joy_styles.raw = 0; // Zero out | ||
| 753 | controller.device_type.raw = 0; | ||
| 754 | controller.properties.raw = 0; | ||
| 755 | |||
| 756 | SignalStyleSetChangedEvent(IndexToNPad(npad_index)); | ||
| 567 | } | 757 | } |
| 568 | 758 | ||
| 569 | void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { | 759 | void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) { |
| @@ -574,6 +764,30 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo | |||
| 574 | return gyroscope_zero_drift_mode; | 764 | return gyroscope_zero_drift_mode; |
| 575 | } | 765 | } |
| 576 | 766 | ||
| 767 | bool Controller_NPad::IsSixAxisSensorAtRest() const { | ||
| 768 | return sixaxis_at_rest; | ||
| 769 | } | ||
| 770 | |||
| 771 | void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { | ||
| 772 | sixaxis_sensors_enabled = six_axis_status; | ||
| 773 | } | ||
| 774 | |||
| 775 | void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) { | ||
| 776 | const auto npad_index_1 = NPadIdToIndex(npad_id_1); | ||
| 777 | const auto npad_index_2 = NPadIdToIndex(npad_id_2); | ||
| 778 | |||
| 779 | // If the controllers at both npad indices form a pair of left and right joycons, merge them. | ||
| 780 | // Otherwise, do nothing. | ||
| 781 | if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft && | ||
| 782 | connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) || | ||
| 783 | (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft && | ||
| 784 | connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) { | ||
| 785 | // Disconnect the joycon at the second id and connect the dual joycon at the first index. | ||
| 786 | DisconnectNPad(npad_id_2); | ||
| 787 | AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1); | ||
| 788 | } | ||
| 789 | } | ||
| 790 | |||
| 577 | void Controller_NPad::StartLRAssignmentMode() { | 791 | void Controller_NPad::StartLRAssignmentMode() { |
| 578 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the | 792 | // Nothing internally is used for lr assignment mode. Since we have the ability to set the |
| 579 | // controller types from boot, it doesn't really matter about showing a selection screen | 793 | // controller types from boot, it doesn't really matter about showing a selection screen |
| @@ -599,8 +813,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) { | |||
| 599 | 813 | ||
| 600 | std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); | 814 | std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type); |
| 601 | 815 | ||
| 602 | InitNewlyAddedControler(npad_index_1); | 816 | AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1); |
| 603 | InitNewlyAddedControler(npad_index_2); | 817 | AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2); |
| 604 | 818 | ||
| 605 | return true; | 819 | return true; |
| 606 | } | 820 | } |
| @@ -614,11 +828,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { | |||
| 614 | case 0: | 828 | case 0: |
| 615 | return LedPattern{1, 0, 0, 0}; | 829 | return LedPattern{1, 0, 0, 0}; |
| 616 | case 1: | 830 | case 1: |
| 617 | return LedPattern{0, 1, 0, 0}; | 831 | return LedPattern{1, 1, 0, 0}; |
| 618 | case 2: | 832 | case 2: |
| 619 | return LedPattern{0, 0, 1, 0}; | 833 | return LedPattern{1, 1, 1, 0}; |
| 620 | case 3: | 834 | case 3: |
| 621 | return LedPattern{0, 0, 0, 1}; | 835 | return LedPattern{1, 1, 1, 1}; |
| 622 | case 4: | 836 | case 4: |
| 623 | return LedPattern{1, 0, 0, 1}; | 837 | return LedPattern{1, 0, 0, 1}; |
| 624 | case 5: | 838 | case 5: |
| @@ -628,9 +842,17 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) { | |||
| 628 | case 7: | 842 | case 7: |
| 629 | return LedPattern{0, 1, 1, 0}; | 843 | return LedPattern{0, 1, 1, 0}; |
| 630 | default: | 844 | default: |
| 631 | UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id); | ||
| 632 | return LedPattern{0, 0, 0, 0}; | 845 | return LedPattern{0, 0, 0, 0}; |
| 633 | }; | 846 | } |
| 847 | } | ||
| 848 | |||
| 849 | bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const { | ||
| 850 | return unintended_home_button_input_protection[NPadIdToIndex(npad_id)]; | ||
| 851 | } | ||
| 852 | |||
| 853 | void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, | ||
| 854 | u32 npad_id) { | ||
| 855 | unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled; | ||
| 634 | } | 856 | } |
| 635 | 857 | ||
| 636 | void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { | 858 | void Controller_NPad::SetVibrationEnabled(bool can_vibrate) { |
| @@ -651,13 +873,13 @@ void Controller_NPad::ClearAllConnectedControllers() { | |||
| 651 | } | 873 | } |
| 652 | 874 | ||
| 653 | void Controller_NPad::DisconnectAllConnectedControllers() { | 875 | void Controller_NPad::DisconnectAllConnectedControllers() { |
| 654 | for (ControllerHolder& controller : connected_controllers) { | 876 | for (auto& controller : connected_controllers) { |
| 655 | controller.is_connected = false; | 877 | controller.is_connected = false; |
| 656 | } | 878 | } |
| 657 | } | 879 | } |
| 658 | 880 | ||
| 659 | void Controller_NPad::ConnectAllDisconnectedControllers() { | 881 | void Controller_NPad::ConnectAllDisconnectedControllers() { |
| 660 | for (ControllerHolder& controller : connected_controllers) { | 882 | for (auto& controller : connected_controllers) { |
| 661 | if (controller.type != NPadControllerType::None && !controller.is_connected) { | 883 | if (controller.type != NPadControllerType::None && !controller.is_connected) { |
| 662 | controller.is_connected = true; | 884 | controller.is_connected = true; |
| 663 | } | 885 | } |
| @@ -665,7 +887,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() { | |||
| 665 | } | 887 | } |
| 666 | 888 | ||
| 667 | void Controller_NPad::ClearAllControllers() { | 889 | void Controller_NPad::ClearAllControllers() { |
| 668 | for (ControllerHolder& controller : connected_controllers) { | 890 | for (auto& controller : connected_controllers) { |
| 669 | controller.type = NPadControllerType::None; | 891 | controller.type = NPadControllerType::None; |
| 670 | controller.is_connected = false; | 892 | controller.is_connected = false; |
| 671 | } | 893 | } |
| @@ -713,92 +935,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const | |||
| 713 | return false; | 935 | return false; |
| 714 | } | 936 | } |
| 715 | 937 | ||
| 716 | Controller_NPad::NPadControllerType Controller_NPad::DecideBestController( | ||
| 717 | NPadControllerType priority) const { | ||
| 718 | if (IsControllerSupported(priority)) { | ||
| 719 | return priority; | ||
| 720 | } | ||
| 721 | const auto is_docked = Settings::values.use_docked_mode; | ||
| 722 | if (is_docked && priority == NPadControllerType::Handheld) { | ||
| 723 | priority = NPadControllerType::JoyDual; | ||
| 724 | if (IsControllerSupported(priority)) { | ||
| 725 | return priority; | ||
| 726 | } | ||
| 727 | } | ||
| 728 | std::vector<NPadControllerType> priority_list; | ||
| 729 | switch (priority) { | ||
| 730 | case NPadControllerType::ProController: | ||
| 731 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 732 | if (!is_docked) { | ||
| 733 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 734 | } | ||
| 735 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 736 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 737 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 738 | break; | ||
| 739 | case NPadControllerType::Handheld: | ||
| 740 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 741 | priority_list.push_back(NPadControllerType::ProController); | ||
| 742 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 743 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 744 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 745 | break; | ||
| 746 | case NPadControllerType::JoyDual: | ||
| 747 | if (!is_docked) { | ||
| 748 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 749 | } | ||
| 750 | priority_list.push_back(NPadControllerType::ProController); | ||
| 751 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 752 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 753 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 754 | break; | ||
| 755 | case NPadControllerType::JoyLeft: | ||
| 756 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 757 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 758 | if (!is_docked) { | ||
| 759 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 760 | } | ||
| 761 | priority_list.push_back(NPadControllerType::ProController); | ||
| 762 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 763 | break; | ||
| 764 | case NPadControllerType::JoyRight: | ||
| 765 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 766 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 767 | if (!is_docked) { | ||
| 768 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 769 | } | ||
| 770 | priority_list.push_back(NPadControllerType::ProController); | ||
| 771 | priority_list.push_back(NPadControllerType::Pokeball); | ||
| 772 | break; | ||
| 773 | case NPadControllerType::Pokeball: | ||
| 774 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 775 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 776 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 777 | if (!is_docked) { | ||
| 778 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 779 | } | ||
| 780 | priority_list.push_back(NPadControllerType::ProController); | ||
| 781 | break; | ||
| 782 | default: | ||
| 783 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 784 | if (!is_docked) { | ||
| 785 | priority_list.push_back(NPadControllerType::Handheld); | ||
| 786 | } | ||
| 787 | priority_list.push_back(NPadControllerType::ProController); | ||
| 788 | priority_list.push_back(NPadControllerType::JoyLeft); | ||
| 789 | priority_list.push_back(NPadControllerType::JoyRight); | ||
| 790 | priority_list.push_back(NPadControllerType::JoyDual); | ||
| 791 | break; | ||
| 792 | } | ||
| 793 | |||
| 794 | const auto iter = std::find_if(priority_list.begin(), priority_list.end(), | ||
| 795 | [this](auto type) { return IsControllerSupported(type); }); | ||
| 796 | if (iter == priority_list.end()) { | ||
| 797 | UNIMPLEMENTED_MSG("Could not find supported controller!"); | ||
| 798 | return priority; | ||
| 799 | } | ||
| 800 | |||
| 801 | return *iter; | ||
| 802 | } | ||
| 803 | |||
| 804 | } // namespace Service::HID | 938 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index 5d4c58a43..fd5c5a6eb 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h | |||
| @@ -32,6 +32,10 @@ public: | |||
| 32 | // When the controller is requesting an update for the shared memory | 32 | // When the controller is requesting an update for the shared memory |
| 33 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; | 33 | void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override; |
| 34 | 34 | ||
| 35 | // When the controller is requesting a motion update for the shared memory | ||
| 36 | void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, | ||
| 37 | std::size_t size) override; | ||
| 38 | |||
| 35 | // Called when input devices should be loaded | 39 | // Called when input devices should be loaded |
| 36 | void OnLoadInputDevices() override; | 40 | void OnLoadInputDevices() override; |
| 37 | 41 | ||
| @@ -74,6 +78,12 @@ public: | |||
| 74 | Single = 1, | 78 | Single = 1, |
| 75 | }; | 79 | }; |
| 76 | 80 | ||
| 81 | enum class NpadHandheldActivationMode : u64 { | ||
| 82 | Dual = 0, | ||
| 83 | Single = 1, | ||
| 84 | None = 2, | ||
| 85 | }; | ||
| 86 | |||
| 77 | enum class NPadControllerType { | 87 | enum class NPadControllerType { |
| 78 | None, | 88 | None, |
| 79 | ProController, | 89 | ProController, |
| @@ -110,22 +120,34 @@ public: | |||
| 110 | void SetHoldType(NpadHoldType joy_hold_type); | 120 | void SetHoldType(NpadHoldType joy_hold_type); |
| 111 | NpadHoldType GetHoldType() const; | 121 | NpadHoldType GetHoldType() const; |
| 112 | 122 | ||
| 123 | void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode); | ||
| 124 | NpadHandheldActivationMode GetNpadHandheldActivationMode() const; | ||
| 125 | |||
| 113 | void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); | 126 | void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode); |
| 114 | 127 | ||
| 115 | void VibrateController(const std::vector<u32>& controller_ids, | 128 | void VibrateController(const std::vector<u32>& controllers, |
| 116 | const std::vector<Vibration>& vibrations); | 129 | const std::vector<Vibration>& vibrations); |
| 117 | 130 | ||
| 118 | std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; | ||
| 119 | Vibration GetLastVibration() const; | 131 | Vibration GetLastVibration() const; |
| 120 | 132 | ||
| 121 | void AddNewController(NPadControllerType controller); | 133 | std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const; |
| 122 | void AddNewControllerAt(NPadControllerType controller, u32 npad_id); | 134 | void SignalStyleSetChangedEvent(u32 npad_id) const; |
| 135 | |||
| 136 | // Adds a new controller at an index. | ||
| 137 | void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index); | ||
| 138 | // Adds a new controller at an index with connection status. | ||
| 139 | void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected); | ||
| 123 | 140 | ||
| 124 | void ConnectNPad(u32 npad_id); | ||
| 125 | void DisconnectNPad(u32 npad_id); | 141 | void DisconnectNPad(u32 npad_id); |
| 142 | void DisconnectNPadAtIndex(std::size_t index); | ||
| 143 | |||
| 126 | void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); | 144 | void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode); |
| 127 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; | 145 | GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; |
| 146 | bool IsSixAxisSensorAtRest() const; | ||
| 147 | void SetSixAxisEnabled(bool six_axis_status); | ||
| 128 | LedPattern GetLedPattern(u32 npad_id); | 148 | LedPattern GetLedPattern(u32 npad_id); |
| 149 | bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; | ||
| 150 | void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); | ||
| 129 | void SetVibrationEnabled(bool can_vibrate); | 151 | void SetVibrationEnabled(bool can_vibrate); |
| 130 | bool IsVibrationEnabled() const; | 152 | bool IsVibrationEnabled() const; |
| 131 | void ClearAllConnectedControllers(); | 153 | void ClearAllConnectedControllers(); |
| @@ -133,6 +155,7 @@ public: | |||
| 133 | void ConnectAllDisconnectedControllers(); | 155 | void ConnectAllDisconnectedControllers(); |
| 134 | void ClearAllControllers(); | 156 | void ClearAllControllers(); |
| 135 | 157 | ||
| 158 | void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2); | ||
| 136 | void StartLRAssignmentMode(); | 159 | void StartLRAssignmentMode(); |
| 137 | void StopLRAssignmentMode(); | 160 | void StopLRAssignmentMode(); |
| 138 | bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); | 161 | bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2); |
| @@ -141,6 +164,8 @@ public: | |||
| 141 | // Specifically for cheat engine and other features. | 164 | // Specifically for cheat engine and other features. |
| 142 | u32 GetAndResetPressState(); | 165 | u32 GetAndResetPressState(); |
| 143 | 166 | ||
| 167 | static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type); | ||
| 168 | static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type); | ||
| 144 | static std::size_t NPadIdToIndex(u32 npad_id); | 169 | static std::size_t NPadIdToIndex(u32 npad_id); |
| 145 | static u32 IndexToNPad(std::size_t index); | 170 | static u32 IndexToNPad(std::size_t index); |
| 146 | 171 | ||
| @@ -244,6 +269,24 @@ private: | |||
| 244 | }; | 269 | }; |
| 245 | static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); | 270 | static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size"); |
| 246 | 271 | ||
| 272 | struct SixAxisStates { | ||
| 273 | s64_le timestamp{}; | ||
| 274 | INSERT_PADDING_WORDS(2); | ||
| 275 | s64_le timestamp2{}; | ||
| 276 | Common::Vec3f accel{}; | ||
| 277 | Common::Vec3f gyro{}; | ||
| 278 | Common::Vec3f rotation{}; | ||
| 279 | std::array<Common::Vec3f, 3> orientation{}; | ||
| 280 | s64_le always_one{1}; | ||
| 281 | }; | ||
| 282 | static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size"); | ||
| 283 | |||
| 284 | struct SixAxisGeneric { | ||
| 285 | CommonHeader common{}; | ||
| 286 | std::array<SixAxisStates, 17> sixaxis{}; | ||
| 287 | }; | ||
| 288 | static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size"); | ||
| 289 | |||
| 247 | enum class ColorReadError : u32_le { | 290 | enum class ColorReadError : u32_le { |
| 248 | ReadOk = 0, | 291 | ReadOk = 0, |
| 249 | ColorDoesntExist = 1, | 292 | ColorDoesntExist = 1, |
| @@ -273,6 +316,13 @@ private: | |||
| 273 | }; | 316 | }; |
| 274 | }; | 317 | }; |
| 275 | 318 | ||
| 319 | struct MotionDevice { | ||
| 320 | Common::Vec3f accel; | ||
| 321 | Common::Vec3f gyro; | ||
| 322 | Common::Vec3f rotation; | ||
| 323 | std::array<Common::Vec3f, 3> orientation; | ||
| 324 | }; | ||
| 325 | |||
| 276 | struct NPadEntry { | 326 | struct NPadEntry { |
| 277 | NPadType joy_styles; | 327 | NPadType joy_styles; |
| 278 | NPadAssignments pad_assignment; | 328 | NPadAssignments pad_assignment; |
| @@ -292,9 +342,12 @@ private: | |||
| 292 | NPadGeneric pokeball_states; | 342 | NPadGeneric pokeball_states; |
| 293 | NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be | 343 | NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be |
| 294 | // relying on this for the time being | 344 | // relying on this for the time being |
| 295 | INSERT_PADDING_BYTES( | 345 | SixAxisGeneric sixaxis_full; |
| 296 | 0x708 * | 346 | SixAxisGeneric sixaxis_handheld; |
| 297 | 6); // TODO(ogniK): SixAxis states, require more information before implementation | 347 | SixAxisGeneric sixaxis_dual_left; |
| 348 | SixAxisGeneric sixaxis_dual_right; | ||
| 349 | SixAxisGeneric sixaxis_left; | ||
| 350 | SixAxisGeneric sixaxis_right; | ||
| 298 | NPadDevice device_type; | 351 | NPadDevice device_type; |
| 299 | NPadProperties properties; | 352 | NPadProperties properties; |
| 300 | INSERT_PADDING_WORDS(1); | 353 | INSERT_PADDING_WORDS(1); |
| @@ -309,31 +362,38 @@ private: | |||
| 309 | bool is_connected; | 362 | bool is_connected; |
| 310 | }; | 363 | }; |
| 311 | 364 | ||
| 312 | void InitNewlyAddedControler(std::size_t controller_idx); | 365 | void InitNewlyAddedController(std::size_t controller_idx); |
| 313 | bool IsControllerSupported(NPadControllerType controller) const; | 366 | bool IsControllerSupported(NPadControllerType controller) const; |
| 314 | NPadControllerType DecideBestController(NPadControllerType priority) const; | ||
| 315 | void RequestPadStateUpdate(u32 npad_id); | 367 | void RequestPadStateUpdate(u32 npad_id); |
| 316 | 368 | ||
| 317 | u32 press_state{}; | 369 | u32 press_state{}; |
| 318 | 370 | ||
| 319 | NPadType style{}; | 371 | NPadType style{}; |
| 320 | std::array<NPadEntry, 10> shared_memory_entries{}; | 372 | std::array<NPadEntry, 10> shared_memory_entries{}; |
| 321 | std::array< | 373 | using ButtonArray = std::array< |
| 322 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, | 374 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>, |
| 323 | 10> | 375 | 10>; |
| 324 | buttons; | 376 | using StickArray = std::array< |
| 325 | std::array< | ||
| 326 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, | 377 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>, |
| 327 | 10> | 378 | 10>; |
| 328 | sticks; | 379 | using MotionArray = std::array< |
| 380 | std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTION_HID>, | ||
| 381 | 10>; | ||
| 382 | ButtonArray buttons; | ||
| 383 | StickArray sticks; | ||
| 384 | MotionArray motions; | ||
| 329 | std::vector<u32> supported_npad_id_types{}; | 385 | std::vector<u32> supported_npad_id_types{}; |
| 330 | NpadHoldType hold_type{NpadHoldType::Vertical}; | 386 | NpadHoldType hold_type{NpadHoldType::Vertical}; |
| 387 | NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual}; | ||
| 331 | // Each controller should have their own styleset changed event | 388 | // Each controller should have their own styleset changed event |
| 332 | std::array<Kernel::EventPair, 10> styleset_changed_events; | 389 | std::array<Kernel::EventPair, 10> styleset_changed_events; |
| 333 | Vibration last_processed_vibration{}; | 390 | Vibration last_processed_vibration{}; |
| 334 | std::array<ControllerHolder, 10> connected_controllers{}; | 391 | std::array<ControllerHolder, 10> connected_controllers{}; |
| 392 | std::array<bool, 10> unintended_home_button_input_protection{}; | ||
| 335 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; | 393 | GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard}; |
| 336 | bool can_controllers_vibrate{true}; | 394 | bool can_controllers_vibrate{true}; |
| 395 | bool sixaxis_sensors_enabled{true}; | ||
| 396 | bool sixaxis_at_rest{true}; | ||
| 337 | std::array<ControllerPad, 10> npad_pad_states{}; | 397 | std::array<ControllerPad, 10> npad_pad_states{}; |
| 338 | bool is_in_lr_assignment_mode{false}; | 398 | bool is_in_lr_assignment_mode{false}; |
| 339 | Core::System& system; | 399 | Core::System& system; |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp index e326f8f5c..0df395e85 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.cpp +++ b/src/core/hle/service/hid/controllers/touchscreen.cpp | |||
| @@ -40,9 +40,14 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 40 | cur_entry.sampling_number = last_entry.sampling_number + 1; | 40 | cur_entry.sampling_number = last_entry.sampling_number + 1; |
| 41 | cur_entry.sampling_number2 = cur_entry.sampling_number; | 41 | cur_entry.sampling_number2 = cur_entry.sampling_number; |
| 42 | 42 | ||
| 43 | const auto [x, y, pressed] = touch_device->GetStatus(); | 43 | bool pressed = false; |
| 44 | float x, y; | ||
| 45 | std::tie(x, y, pressed) = touch_device->GetStatus(); | ||
| 44 | auto& touch_entry = cur_entry.states[0]; | 46 | auto& touch_entry = cur_entry.states[0]; |
| 45 | touch_entry.attribute.raw = 0; | 47 | touch_entry.attribute.raw = 0; |
| 48 | if (!pressed && touch_btn_device) { | ||
| 49 | std::tie(x, y, pressed) = touch_btn_device->GetStatus(); | ||
| 50 | } | ||
| 46 | if (pressed && Settings::values.touchscreen.enabled) { | 51 | if (pressed && Settings::values.touchscreen.enabled) { |
| 47 | touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); | 52 | touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); |
| 48 | touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); | 53 | touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); |
| @@ -63,5 +68,10 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin | |||
| 63 | 68 | ||
| 64 | void Controller_Touchscreen::OnLoadInputDevices() { | 69 | void Controller_Touchscreen::OnLoadInputDevices() { |
| 65 | touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); | 70 | touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device); |
| 71 | if (Settings::values.use_touch_from_button) { | ||
| 72 | touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button"); | ||
| 73 | } else { | ||
| 74 | touch_btn_device.reset(); | ||
| 75 | } | ||
| 66 | } | 76 | } |
| 67 | } // namespace Service::HID | 77 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h index a1d97269e..4d9042adc 100644 --- a/src/core/hle/service/hid/controllers/touchscreen.h +++ b/src/core/hle/service/hid/controllers/touchscreen.h | |||
| @@ -68,6 +68,7 @@ private: | |||
| 68 | "TouchScreenSharedMemory is an invalid size"); | 68 | "TouchScreenSharedMemory is an invalid size"); |
| 69 | TouchScreenSharedMemory shared_memory{}; | 69 | TouchScreenSharedMemory shared_memory{}; |
| 70 | std::unique_ptr<Input::TouchDevice> touch_device; | 70 | std::unique_ptr<Input::TouchDevice> touch_device; |
| 71 | std::unique_ptr<Input::TouchDevice> touch_btn_device; | ||
| 71 | s64_le last_touch{}; | 72 | s64_le last_touch{}; |
| 72 | }; | 73 | }; |
| 73 | } // namespace Service::HID | 74 | } // namespace Service::HID |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e9020e0dc..71dbaba7f 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -38,10 +38,10 @@ | |||
| 38 | namespace Service::HID { | 38 | namespace Service::HID { |
| 39 | 39 | ||
| 40 | // Updating period for each HID device. | 40 | // Updating period for each HID device. |
| 41 | // TODO(ogniK): Find actual polling rate of hid | 41 | // HID is polled every 15ms, this value was derived from |
| 42 | constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66); | 42 | // https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet |
| 43 | [[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100); | 43 | constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz) |
| 44 | [[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100); | 44 | constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz) |
| 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; | 45 | constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; |
| 46 | 46 | ||
| 47 | IAppletResource::IAppletResource(Core::System& system) | 47 | IAppletResource::IAppletResource(Core::System& system) |
| @@ -75,14 +75,19 @@ IAppletResource::IAppletResource(Core::System& system) | |||
| 75 | GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); | 75 | GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); |
| 76 | 76 | ||
| 77 | // Register update callbacks | 77 | // Register update callbacks |
| 78 | pad_update_event = | 78 | pad_update_event = Core::Timing::CreateEvent( |
| 79 | Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) { | 79 | "HID::UpdatePadCallback", |
| 80 | UpdateControllers(userdata, ns_late); | 80 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 81 | UpdateControllers(user_data, ns_late); | ||
| 82 | }); | ||
| 83 | motion_update_event = Core::Timing::CreateEvent( | ||
| 84 | "HID::MotionPadCallback", | ||
| 85 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||
| 86 | UpdateMotion(user_data, ns_late); | ||
| 81 | }); | 87 | }); |
| 82 | 88 | ||
| 83 | // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) | 89 | system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event); |
| 84 | 90 | system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event); | |
| 85 | system.CoreTiming().ScheduleEvent(pad_update_ticks, pad_update_event); | ||
| 86 | 91 | ||
| 87 | ReloadInputDevices(); | 92 | ReloadInputDevices(); |
| 88 | } | 93 | } |
| @@ -107,7 +112,8 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | |||
| 107 | rb.PushCopyObjects(shared_mem); | 112 | rb.PushCopyObjects(shared_mem); |
| 108 | } | 113 | } |
| 109 | 114 | ||
| 110 | void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { | 115 | void IAppletResource::UpdateControllers(std::uintptr_t user_data, |
| 116 | std::chrono::nanoseconds ns_late) { | ||
| 111 | auto& core_timing = system.CoreTiming(); | 117 | auto& core_timing = system.CoreTiming(); |
| 112 | 118 | ||
| 113 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); | 119 | const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); |
| @@ -118,7 +124,17 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) { | |||
| 118 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); | 124 | controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); |
| 119 | } | 125 | } |
| 120 | 126 | ||
| 121 | core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event); | 127 | core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); |
| 128 | } | ||
| 129 | |||
| 130 | void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { | ||
| 131 | auto& core_timing = system.CoreTiming(); | ||
| 132 | |||
| 133 | for (const auto& controller : controllers) { | ||
| 134 | controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); | ||
| 135 | } | ||
| 136 | |||
| 137 | core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); | ||
| 122 | } | 138 | } |
| 123 | 139 | ||
| 124 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 140 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { |
| @@ -163,8 +179,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 163 | {56, nullptr, "ActivateJoyXpad"}, | 179 | {56, nullptr, "ActivateJoyXpad"}, |
| 164 | {58, nullptr, "GetJoyXpadLifoHandle"}, | 180 | {58, nullptr, "GetJoyXpadLifoHandle"}, |
| 165 | {59, nullptr, "GetJoyXpadIds"}, | 181 | {59, nullptr, "GetJoyXpadIds"}, |
| 166 | {60, nullptr, "ActivateSixAxisSensor"}, | 182 | {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"}, |
| 167 | {61, nullptr, "DeactivateSixAxisSensor"}, | 183 | {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"}, |
| 168 | {62, nullptr, "GetSixAxisSensorLifoHandle"}, | 184 | {62, nullptr, "GetSixAxisSensorLifoHandle"}, |
| 169 | {63, nullptr, "ActivateJoySixAxisSensor"}, | 185 | {63, nullptr, "ActivateJoySixAxisSensor"}, |
| 170 | {64, nullptr, "DeactivateJoySixAxisSensor"}, | 186 | {64, nullptr, "DeactivateJoySixAxisSensor"}, |
| @@ -172,7 +188,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 172 | {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, | 188 | {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, |
| 173 | {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, | 189 | {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, |
| 174 | {68, nullptr, "IsSixAxisSensorFusionEnabled"}, | 190 | {68, nullptr, "IsSixAxisSensorFusionEnabled"}, |
| 175 | {69, nullptr, "EnableSixAxisSensorFusion"}, | 191 | {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"}, |
| 176 | {70, nullptr, "SetSixAxisSensorFusionParameters"}, | 192 | {70, nullptr, "SetSixAxisSensorFusionParameters"}, |
| 177 | {71, nullptr, "GetSixAxisSensorFusionParameters"}, | 193 | {71, nullptr, "GetSixAxisSensorFusionParameters"}, |
| 178 | {72, nullptr, "ResetSixAxisSensorFusionParameters"}, | 194 | {72, nullptr, "ResetSixAxisSensorFusionParameters"}, |
| @@ -208,8 +224,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { | |||
| 208 | {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, | 224 | {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"}, |
| 209 | {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, | 225 | {129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"}, |
| 210 | {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, | 226 | {130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"}, |
| 211 | {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"}, | 227 | {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"}, |
| 212 | {132, nullptr, "EnableUnintendedHomeButtonInputProtection"}, | 228 | {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"}, |
| 213 | {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, | 229 | {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"}, |
| 214 | {134, nullptr, "SetNpadAnalogStickUseCenterClamp"}, | 230 | {134, nullptr, "SetNpadAnalogStickUseCenterClamp"}, |
| 215 | {135, nullptr, "SetNpadCaptureButtonAssignment"}, | 231 | {135, nullptr, "SetNpadCaptureButtonAssignment"}, |
| @@ -328,6 +344,31 @@ void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) { | |||
| 328 | rb.Push(0); | 344 | rb.Push(0); |
| 329 | } | 345 | } |
| 330 | 346 | ||
| 347 | void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 348 | IPC::RequestParser rp{ctx}; | ||
| 349 | const auto handle{rp.Pop<u32>()}; | ||
| 350 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 351 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true); | ||
| 352 | LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, | ||
| 353 | applet_resource_user_id); | ||
| 354 | |||
| 355 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 356 | rb.Push(RESULT_SUCCESS); | ||
| 357 | } | ||
| 358 | |||
| 359 | void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 360 | IPC::RequestParser rp{ctx}; | ||
| 361 | const auto handle{rp.Pop<u32>()}; | ||
| 362 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 363 | applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false); | ||
| 364 | |||
| 365 | LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, | ||
| 366 | applet_resource_user_id); | ||
| 367 | |||
| 368 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 369 | rb.Push(RESULT_SUCCESS); | ||
| 370 | } | ||
| 371 | |||
| 331 | void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { | 372 | void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) { |
| 332 | IPC::RequestParser rp{ctx}; | 373 | IPC::RequestParser rp{ctx}; |
| 333 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 374 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| @@ -432,6 +473,19 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | |||
| 432 | rb.Push(RESULT_SUCCESS); | 473 | rb.Push(RESULT_SUCCESS); |
| 433 | } | 474 | } |
| 434 | 475 | ||
| 476 | void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { | ||
| 477 | IPC::RequestParser rp{ctx}; | ||
| 478 | const auto enable{rp.Pop<bool>()}; | ||
| 479 | const auto handle{rp.Pop<u32>()}; | ||
| 480 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 481 | |||
| 482 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, | ||
| 483 | applet_resource_user_id); | ||
| 484 | |||
| 485 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 486 | rb.Push(RESULT_SUCCESS); | ||
| 487 | } | ||
| 488 | |||
| 435 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { | 489 | void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) { |
| 436 | IPC::RequestParser rp{ctx}; | 490 | IPC::RequestParser rp{ctx}; |
| 437 | const auto handle{rp.Pop<u32>()}; | 491 | const auto handle{rp.Pop<u32>()}; |
| @@ -483,13 +537,13 @@ void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) { | |||
| 483 | const auto handle{rp.Pop<u32>()}; | 537 | const auto handle{rp.Pop<u32>()}; |
| 484 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 538 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 485 | 539 | ||
| 486 | LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle, | 540 | LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle, |
| 487 | applet_resource_user_id); | 541 | applet_resource_user_id); |
| 488 | 542 | ||
| 489 | IPC::ResponseBuilder rb{ctx, 3}; | 543 | IPC::ResponseBuilder rb{ctx, 3}; |
| 490 | rb.Push(RESULT_SUCCESS); | 544 | rb.Push(RESULT_SUCCESS); |
| 491 | // TODO (Hexagon12): Properly implement reading gyroscope values from controllers. | 545 | rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 492 | rb.Push(true); | 546 | .IsSixAxisSensorAtRest()); |
| 493 | } | 547 | } |
| 494 | 548 | ||
| 495 | void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { | 549 | void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) { |
| @@ -670,13 +724,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) { | |||
| 670 | 724 | ||
| 671 | void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { | 725 | void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) { |
| 672 | IPC::RequestParser rp{ctx}; | 726 | IPC::RequestParser rp{ctx}; |
| 673 | const auto unknown_1{rp.Pop<u32>()}; | 727 | const auto npad_id_1{rp.Pop<u32>()}; |
| 674 | const auto unknown_2{rp.Pop<u32>()}; | 728 | const auto npad_id_2{rp.Pop<u32>()}; |
| 675 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 729 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 676 | 730 | ||
| 677 | LOG_WARNING(Service_HID, | 731 | LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}", |
| 678 | "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}", | 732 | npad_id_1, npad_id_2, applet_resource_user_id); |
| 679 | unknown_1, unknown_2, applet_resource_user_id); | 733 | |
| 734 | auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); | ||
| 735 | controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2); | ||
| 680 | 736 | ||
| 681 | IPC::ResponseBuilder rb{ctx, 2}; | 737 | IPC::ResponseBuilder rb{ctx, 2}; |
| 682 | rb.Push(RESULT_SUCCESS); | 738 | rb.Push(RESULT_SUCCESS); |
| @@ -711,8 +767,11 @@ void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { | |||
| 711 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 767 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 712 | const auto mode{rp.Pop<u64>()}; | 768 | const auto mode{rp.Pop<u64>()}; |
| 713 | 769 | ||
| 714 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}", | 770 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id, |
| 715 | applet_resource_user_id, mode); | 771 | mode); |
| 772 | |||
| 773 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 774 | .SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode}); | ||
| 716 | 775 | ||
| 717 | IPC::ResponseBuilder rb{ctx, 2}; | 776 | IPC::ResponseBuilder rb{ctx, 2}; |
| 718 | rb.Push(RESULT_SUCCESS); | 777 | rb.Push(RESULT_SUCCESS); |
| @@ -722,11 +781,13 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) { | |||
| 722 | IPC::RequestParser rp{ctx}; | 781 | IPC::RequestParser rp{ctx}; |
| 723 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 782 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 724 | 783 | ||
| 725 | LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", | 784 | LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); |
| 726 | applet_resource_user_id); | ||
| 727 | 785 | ||
| 728 | IPC::ResponseBuilder rb{ctx, 2}; | 786 | IPC::ResponseBuilder rb{ctx, 4}; |
| 729 | rb.Push(RESULT_SUCCESS); | 787 | rb.Push(RESULT_SUCCESS); |
| 788 | rb.Push<u64>( | ||
| 789 | static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad) | ||
| 790 | .GetNpadHandheldActivationMode())); | ||
| 730 | } | 791 | } |
| 731 | 792 | ||
| 732 | void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { | 793 | void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { |
| @@ -748,6 +809,40 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) { | |||
| 748 | } | 809 | } |
| 749 | } | 810 | } |
| 750 | 811 | ||
| 812 | void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) { | ||
| 813 | IPC::RequestParser rp{ctx}; | ||
| 814 | const auto npad_id{rp.Pop<u32>()}; | ||
| 815 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 816 | |||
| 817 | LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id, | ||
| 818 | applet_resource_user_id); | ||
| 819 | |||
| 820 | auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); | ||
| 821 | |||
| 822 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 823 | rb.Push(RESULT_SUCCESS); | ||
| 824 | rb.Push<bool>(controller.IsUnintendedHomeButtonInputProtectionEnabled(npad_id)); | ||
| 825 | } | ||
| 826 | |||
| 827 | void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) { | ||
| 828 | IPC::RequestParser rp{ctx}; | ||
| 829 | const auto unintended_home_button_input_protection{rp.Pop<bool>()}; | ||
| 830 | const auto npad_id{rp.Pop<u32>()}; | ||
| 831 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 832 | |||
| 833 | LOG_WARNING(Service_HID, | ||
| 834 | "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={}," | ||
| 835 | "applet_resource_user_id={}", | ||
| 836 | npad_id, unintended_home_button_input_protection, applet_resource_user_id); | ||
| 837 | |||
| 838 | auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad); | ||
| 839 | controller.SetUnintendedHomeButtonInputProtectionEnabled( | ||
| 840 | unintended_home_button_input_protection, npad_id); | ||
| 841 | |||
| 842 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 843 | rb.Push(RESULT_SUCCESS); | ||
| 844 | } | ||
| 845 | |||
| 751 | void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { | 846 | void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) { |
| 752 | IPC::RequestParser rp{ctx}; | 847 | IPC::RequestParser rp{ctx}; |
| 753 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 848 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| @@ -769,18 +864,18 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) { | |||
| 769 | 864 | ||
| 770 | void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { | 865 | void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) { |
| 771 | IPC::RequestParser rp{ctx}; | 866 | IPC::RequestParser rp{ctx}; |
| 772 | const auto controller_id{rp.Pop<u32>()}; | 867 | const auto controller{rp.Pop<u32>()}; |
| 773 | const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; | 868 | const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()}; |
| 774 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 869 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 775 | 870 | ||
| 776 | LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id, | 871 | LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller, |
| 777 | applet_resource_user_id); | 872 | applet_resource_user_id); |
| 778 | 873 | ||
| 779 | IPC::ResponseBuilder rb{ctx, 2}; | 874 | IPC::ResponseBuilder rb{ctx, 2}; |
| 780 | rb.Push(RESULT_SUCCESS); | 875 | rb.Push(RESULT_SUCCESS); |
| 781 | 876 | ||
| 782 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 877 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 783 | .VibrateController({controller_id}, {vibration_values}); | 878 | .VibrateController({controller}, {vibration_values}); |
| 784 | } | 879 | } |
| 785 | 880 | ||
| 786 | void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | 881 | void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { |
| @@ -798,8 +893,6 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) { | |||
| 798 | 893 | ||
| 799 | std::memcpy(controller_list.data(), controllers.data(), controllers.size()); | 894 | std::memcpy(controller_list.data(), controllers.data(), controllers.size()); |
| 800 | std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); | 895 | std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size()); |
| 801 | std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(), | ||
| 802 | [](u32 controller_id) { return controller_id - 3; }); | ||
| 803 | 896 | ||
| 804 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 897 | applet_resource->GetController<Controller_NPad>(HidController::NPad) |
| 805 | .VibrateController(controller_list, vibration_list); | 898 | .VibrateController(controller_list, vibration_list); |
| @@ -842,8 +935,7 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) { | |||
| 842 | void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { | 935 | void Hid::PermitVibration(Kernel::HLERequestContext& ctx) { |
| 843 | IPC::RequestParser rp{ctx}; | 936 | IPC::RequestParser rp{ctx}; |
| 844 | const auto can_vibrate{rp.Pop<bool>()}; | 937 | const auto can_vibrate{rp.Pop<bool>()}; |
| 845 | applet_resource->GetController<Controller_NPad>(HidController::NPad) | 938 | Settings::values.vibration_enabled = can_vibrate; |
| 846 | .SetVibrationEnabled(can_vibrate); | ||
| 847 | 939 | ||
| 848 | LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); | 940 | LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate); |
| 849 | 941 | ||
| @@ -856,8 +948,7 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) { | |||
| 856 | 948 | ||
| 857 | IPC::ResponseBuilder rb{ctx, 3}; | 949 | IPC::ResponseBuilder rb{ctx, 3}; |
| 858 | rb.Push(RESULT_SUCCESS); | 950 | rb.Push(RESULT_SUCCESS); |
| 859 | rb.Push( | 951 | rb.Push(Settings::values.vibration_enabled); |
| 860 | applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled()); | ||
| 861 | } | 952 | } |
| 862 | 953 | ||
| 863 | void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | 954 | void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 6fb048360..fd0372b18 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h | |||
| @@ -4,10 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/hid/controllers/controller_base.h" | 7 | #include <chrono> |
| 8 | #include "core/hle/service/service.h" | ||
| 9 | 8 | ||
| 10 | #include "controllers/controller_base.h" | 9 | #include "core/hle/service/hid/controllers/controller_base.h" |
| 11 | #include "core/hle/service/service.h" | 10 | #include "core/hle/service/service.h" |
| 12 | 11 | ||
| 13 | namespace Core::Timing { | 12 | namespace Core::Timing { |
| @@ -65,11 +64,13 @@ private: | |||
| 65 | } | 64 | } |
| 66 | 65 | ||
| 67 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); | 66 | void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); |
| 68 | void UpdateControllers(u64 userdata, s64 cycles_late); | 67 | void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 68 | void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 69 | 69 | ||
| 70 | std::shared_ptr<Kernel::SharedMemory> shared_mem; | 70 | std::shared_ptr<Kernel::SharedMemory> shared_mem; |
| 71 | 71 | ||
| 72 | std::shared_ptr<Core::Timing::EventType> pad_update_event; | 72 | std::shared_ptr<Core::Timing::EventType> pad_update_event; |
| 73 | std::shared_ptr<Core::Timing::EventType> motion_update_event; | ||
| 73 | Core::System& system; | 74 | Core::System& system; |
| 74 | 75 | ||
| 75 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> | 76 | std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> |
| @@ -87,6 +88,8 @@ private: | |||
| 87 | void CreateAppletResource(Kernel::HLERequestContext& ctx); | 88 | void CreateAppletResource(Kernel::HLERequestContext& ctx); |
| 88 | void ActivateXpad(Kernel::HLERequestContext& ctx); | 89 | void ActivateXpad(Kernel::HLERequestContext& ctx); |
| 89 | void GetXpadIDs(Kernel::HLERequestContext& ctx); | 90 | void GetXpadIDs(Kernel::HLERequestContext& ctx); |
| 91 | void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx); | ||
| 92 | void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx); | ||
| 90 | void ActivateDebugPad(Kernel::HLERequestContext& ctx); | 93 | void ActivateDebugPad(Kernel::HLERequestContext& ctx); |
| 91 | void ActivateTouchScreen(Kernel::HLERequestContext& ctx); | 94 | void ActivateTouchScreen(Kernel::HLERequestContext& ctx); |
| 92 | void ActivateMouse(Kernel::HLERequestContext& ctx); | 95 | void ActivateMouse(Kernel::HLERequestContext& ctx); |
| @@ -96,6 +99,7 @@ private: | |||
| 96 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); | 99 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx); |
| 97 | void StartSixAxisSensor(Kernel::HLERequestContext& ctx); | 100 | void StartSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 98 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); | 101 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx); |
| 102 | void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx); | ||
| 99 | void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | 103 | void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); |
| 100 | void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | 104 | void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); |
| 101 | void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); | 105 | void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx); |
| @@ -119,6 +123,8 @@ private: | |||
| 119 | void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); | 123 | void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); |
| 120 | void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); | 124 | void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx); |
| 121 | void SwapNpadAssignment(Kernel::HLERequestContext& ctx); | 125 | void SwapNpadAssignment(Kernel::HLERequestContext& ctx); |
| 126 | void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx); | ||
| 127 | void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx); | ||
| 122 | void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); | 128 | void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx); |
| 123 | void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); | 129 | void EndPermitVibrationSession(Kernel::HLERequestContext& ctx); |
| 124 | void SendVibrationValue(Kernel::HLERequestContext& ctx); | 130 | void SendVibrationValue(Kernel::HLERequestContext& ctx); |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 64a526b9e..d8cd10e31 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -310,7 +310,7 @@ public: | |||
| 310 | 310 | ||
| 311 | ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, | 311 | ResultVal<VAddr> MapProcessCodeMemory(Kernel::Process* process, VAddr baseAddress, |
| 312 | u64 size) const { | 312 | u64 size) const { |
| 313 | for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { | 313 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { |
| 314 | auto& page_table{process->PageTable()}; | 314 | auto& page_table{process->PageTable()}; |
| 315 | const VAddr addr{GetRandomMapRegion(page_table, size)}; | 315 | const VAddr addr{GetRandomMapRegion(page_table, size)}; |
| 316 | const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; | 316 | const ResultCode result{page_table.MapProcessCodeMemory(addr, baseAddress, size)}; |
| @@ -331,8 +331,7 @@ public: | |||
| 331 | 331 | ||
| 332 | ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, | 332 | ResultVal<VAddr> MapNro(Kernel::Process* process, VAddr nro_addr, std::size_t nro_size, |
| 333 | VAddr bss_addr, std::size_t bss_size, std::size_t size) const { | 333 | VAddr bss_addr, std::size_t bss_size, std::size_t size) const { |
| 334 | 334 | for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { | |
| 335 | for (int retry{}; retry < MAXIMUM_MAP_RETRIES; retry++) { | ||
| 336 | auto& page_table{process->PageTable()}; | 335 | auto& page_table{process->PageTable()}; |
| 337 | VAddr addr{}; | 336 | VAddr addr{}; |
| 338 | 337 | ||
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp index 4a1d1182e..4730070cb 100644 --- a/src/core/hle/service/mii/manager.cpp +++ b/src/core/hle/service/mii/manager.cpp | |||
| @@ -47,66 +47,67 @@ std::array<T, DestArraySize> ResizeArray(const std::array<T, SourceArraySize>& i | |||
| 47 | MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { | 47 | MiiInfo ConvertStoreDataToInfo(const MiiStoreData& data) { |
| 48 | MiiStoreBitFields bf; | 48 | MiiStoreBitFields bf; |
| 49 | std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); | 49 | std::memcpy(&bf, data.data.data.data(), sizeof(MiiStoreBitFields)); |
| 50 | MiiInfo info{}; | 50 | |
| 51 | info.name = ResizeArray<char16_t, 10, 11>(data.data.name); | 51 | return { |
| 52 | info.uuid = data.data.uuid; | 52 | .uuid = data.data.uuid, |
| 53 | info.font_region = static_cast<u8>(bf.font_region.Value()); | 53 | .name = ResizeArray<char16_t, 10, 11>(data.data.name), |
| 54 | info.favorite_color = static_cast<u8>(bf.favorite_color.Value()); | 54 | .font_region = static_cast<u8>(bf.font_region.Value()), |
| 55 | info.gender = static_cast<u8>(bf.gender.Value()); | 55 | .favorite_color = static_cast<u8>(bf.favorite_color.Value()), |
| 56 | info.height = static_cast<u8>(bf.height.Value()); | 56 | .gender = static_cast<u8>(bf.gender.Value()), |
| 57 | info.build = static_cast<u8>(bf.build.Value()); | 57 | .height = static_cast<u8>(bf.height.Value()), |
| 58 | info.type = static_cast<u8>(bf.type.Value()); | 58 | .build = static_cast<u8>(bf.build.Value()), |
| 59 | info.region_move = static_cast<u8>(bf.region_move.Value()); | 59 | .type = static_cast<u8>(bf.type.Value()), |
| 60 | info.faceline_type = static_cast<u8>(bf.faceline_type.Value()); | 60 | .region_move = static_cast<u8>(bf.region_move.Value()), |
| 61 | info.faceline_color = static_cast<u8>(bf.faceline_color.Value()); | 61 | .faceline_type = static_cast<u8>(bf.faceline_type.Value()), |
| 62 | info.faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()); | 62 | .faceline_color = static_cast<u8>(bf.faceline_color.Value()), |
| 63 | info.faceline_make = static_cast<u8>(bf.faceline_makeup.Value()); | 63 | .faceline_wrinkle = static_cast<u8>(bf.faceline_wrinkle.Value()), |
| 64 | info.hair_type = static_cast<u8>(bf.hair_type.Value()); | 64 | .faceline_make = static_cast<u8>(bf.faceline_makeup.Value()), |
| 65 | info.hair_color = static_cast<u8>(bf.hair_color.Value()); | 65 | .hair_type = static_cast<u8>(bf.hair_type.Value()), |
| 66 | info.hair_flip = static_cast<u8>(bf.hair_flip.Value()); | 66 | .hair_color = static_cast<u8>(bf.hair_color.Value()), |
| 67 | info.eye_type = static_cast<u8>(bf.eye_type.Value()); | 67 | .hair_flip = static_cast<u8>(bf.hair_flip.Value()), |
| 68 | info.eye_color = static_cast<u8>(bf.eye_color.Value()); | 68 | .eye_type = static_cast<u8>(bf.eye_type.Value()), |
| 69 | info.eye_scale = static_cast<u8>(bf.eye_scale.Value()); | 69 | .eye_color = static_cast<u8>(bf.eye_color.Value()), |
| 70 | info.eye_aspect = static_cast<u8>(bf.eye_aspect.Value()); | 70 | .eye_scale = static_cast<u8>(bf.eye_scale.Value()), |
| 71 | info.eye_rotate = static_cast<u8>(bf.eye_rotate.Value()); | 71 | .eye_aspect = static_cast<u8>(bf.eye_aspect.Value()), |
| 72 | info.eye_x = static_cast<u8>(bf.eye_x.Value()); | 72 | .eye_rotate = static_cast<u8>(bf.eye_rotate.Value()), |
| 73 | info.eye_y = static_cast<u8>(bf.eye_y.Value()); | 73 | .eye_x = static_cast<u8>(bf.eye_x.Value()), |
| 74 | info.eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()); | 74 | .eye_y = static_cast<u8>(bf.eye_y.Value()), |
| 75 | info.eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()); | 75 | .eyebrow_type = static_cast<u8>(bf.eyebrow_type.Value()), |
| 76 | info.eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()); | 76 | .eyebrow_color = static_cast<u8>(bf.eyebrow_color.Value()), |
| 77 | info.eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()); | 77 | .eyebrow_scale = static_cast<u8>(bf.eyebrow_scale.Value()), |
| 78 | info.eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()); | 78 | .eyebrow_aspect = static_cast<u8>(bf.eyebrow_aspect.Value()), |
| 79 | info.eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()); | 79 | .eyebrow_rotate = static_cast<u8>(bf.eyebrow_rotate.Value()), |
| 80 | info.eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3); | 80 | .eyebrow_x = static_cast<u8>(bf.eyebrow_x.Value()), |
| 81 | info.nose_type = static_cast<u8>(bf.nose_type.Value()); | 81 | .eyebrow_y = static_cast<u8>(bf.eyebrow_y.Value() + 3), |
| 82 | info.nose_scale = static_cast<u8>(bf.nose_scale.Value()); | 82 | .nose_type = static_cast<u8>(bf.nose_type.Value()), |
| 83 | info.nose_y = static_cast<u8>(bf.nose_y.Value()); | 83 | .nose_scale = static_cast<u8>(bf.nose_scale.Value()), |
| 84 | info.mouth_type = static_cast<u8>(bf.mouth_type.Value()); | 84 | .nose_y = static_cast<u8>(bf.nose_y.Value()), |
| 85 | info.mouth_color = static_cast<u8>(bf.mouth_color.Value()); | 85 | .mouth_type = static_cast<u8>(bf.mouth_type.Value()), |
| 86 | info.mouth_scale = static_cast<u8>(bf.mouth_scale.Value()); | 86 | .mouth_color = static_cast<u8>(bf.mouth_color.Value()), |
| 87 | info.mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()); | 87 | .mouth_scale = static_cast<u8>(bf.mouth_scale.Value()), |
| 88 | info.mouth_y = static_cast<u8>(bf.mouth_y.Value()); | 88 | .mouth_aspect = static_cast<u8>(bf.mouth_aspect.Value()), |
| 89 | info.beard_color = static_cast<u8>(bf.beard_color.Value()); | 89 | .mouth_y = static_cast<u8>(bf.mouth_y.Value()), |
| 90 | info.beard_type = static_cast<u8>(bf.beard_type.Value()); | 90 | .beard_color = static_cast<u8>(bf.beard_color.Value()), |
| 91 | info.mustache_type = static_cast<u8>(bf.mustache_type.Value()); | 91 | .beard_type = static_cast<u8>(bf.beard_type.Value()), |
| 92 | info.mustache_scale = static_cast<u8>(bf.mustache_scale.Value()); | 92 | .mustache_type = static_cast<u8>(bf.mustache_type.Value()), |
| 93 | info.mustache_y = static_cast<u8>(bf.mustache_y.Value()); | 93 | .mustache_scale = static_cast<u8>(bf.mustache_scale.Value()), |
| 94 | info.glasses_type = static_cast<u8>(bf.glasses_type.Value()); | 94 | .mustache_y = static_cast<u8>(bf.mustache_y.Value()), |
| 95 | info.glasses_color = static_cast<u8>(bf.glasses_color.Value()); | 95 | .glasses_type = static_cast<u8>(bf.glasses_type.Value()), |
| 96 | info.glasses_scale = static_cast<u8>(bf.glasses_scale.Value()); | 96 | .glasses_color = static_cast<u8>(bf.glasses_color.Value()), |
| 97 | info.glasses_y = static_cast<u8>(bf.glasses_y.Value()); | 97 | .glasses_scale = static_cast<u8>(bf.glasses_scale.Value()), |
| 98 | info.mole_type = static_cast<u8>(bf.mole_type.Value()); | 98 | .glasses_y = static_cast<u8>(bf.glasses_y.Value()), |
| 99 | info.mole_scale = static_cast<u8>(bf.mole_scale.Value()); | 99 | .mole_type = static_cast<u8>(bf.mole_type.Value()), |
| 100 | info.mole_x = static_cast<u8>(bf.mole_x.Value()); | 100 | .mole_scale = static_cast<u8>(bf.mole_scale.Value()), |
| 101 | info.mole_y = static_cast<u8>(bf.mole_y.Value()); | 101 | .mole_x = static_cast<u8>(bf.mole_x.Value()), |
| 102 | return info; | 102 | .mole_y = static_cast<u8>(bf.mole_y.Value()), |
| 103 | }; | ||
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | u16 GenerateCrc16(const void* data, std::size_t size) { | 106 | u16 GenerateCrc16(const void* data, std::size_t size) { |
| 106 | s32 crc{}; | 107 | s32 crc{}; |
| 107 | for (int i = 0; i < size; i++) { | 108 | for (std::size_t i = 0; i < size; i++) { |
| 108 | crc ^= reinterpret_cast<const u8*>(data)[i] << 8; | 109 | crc ^= static_cast<const u8*>(data)[i] << 8; |
| 109 | for (int j = 0; j < 8; j++) { | 110 | for (std::size_t j = 0; j < 8; j++) { |
| 110 | crc <<= 1; | 111 | crc <<= 1; |
| 111 | if ((crc & 0x10000) != 0) { | 112 | if ((crc & 0x10000) != 0) { |
| 112 | crc = (crc ^ 0x1021) & 0xFFFF; | 113 | crc = (crc ^ 0x1021) & 0xFFFF; |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 4b79eb81d..a0469ffbd 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 <array> | ||
| 5 | #include <atomic> | 6 | #include <atomic> |
| 6 | 7 | ||
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| @@ -72,10 +73,10 @@ private: | |||
| 72 | std::array<u8, 10> uuid; | 73 | std::array<u8, 10> uuid; |
| 73 | u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it | 74 | u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it |
| 74 | // mean something else | 75 | // mean something else |
| 75 | INSERT_PADDING_BYTES(0x15); | 76 | std::array<u8, 0x15> padding_1; |
| 76 | u32_le protocol; | 77 | u32_le protocol; |
| 77 | u32_le tag_type; | 78 | u32_le tag_type; |
| 78 | INSERT_PADDING_BYTES(0x2c); | 79 | std::array<u8, 0x2c> padding_2; |
| 79 | }; | 80 | }; |
| 80 | static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); | 81 | static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size"); |
| 81 | 82 | ||
| @@ -127,7 +128,7 @@ private: | |||
| 127 | const u32 array_size = rp.Pop<u32>(); | 128 | const u32 array_size = rp.Pop<u32>(); |
| 128 | LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); | 129 | LOG_DEBUG(Service_NFP, "called, array_size={}", array_size); |
| 129 | 130 | ||
| 130 | ctx.WriteBuffer(&device_handle, sizeof(device_handle)); | 131 | ctx.WriteBuffer(device_handle); |
| 131 | 132 | ||
| 132 | IPC::ResponseBuilder rb{ctx, 3}; | 133 | IPC::ResponseBuilder rb{ctx, 3}; |
| 133 | rb.Push(RESULT_SUCCESS); | 134 | rb.Push(RESULT_SUCCESS); |
| @@ -213,14 +214,16 @@ private: | |||
| 213 | LOG_DEBUG(Service_NFP, "called"); | 214 | LOG_DEBUG(Service_NFP, "called"); |
| 214 | 215 | ||
| 215 | IPC::ResponseBuilder rb{ctx, 2}; | 216 | IPC::ResponseBuilder rb{ctx, 2}; |
| 216 | auto amiibo = nfp_interface.GetAmiiboBuffer(); | 217 | const auto& amiibo = nfp_interface.GetAmiiboBuffer(); |
| 217 | TagInfo tag_info{}; | 218 | const TagInfo tag_info{ |
| 218 | tag_info.uuid = amiibo.uuid; | 219 | .uuid = amiibo.uuid, |
| 219 | tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size()); | 220 | .uuid_length = static_cast<u8>(tag_info.uuid.size()), |
| 220 | 221 | .padding_1 = {}, | |
| 221 | tag_info.protocol = 1; // TODO(ogniK): Figure out actual values | 222 | .protocol = 1, // TODO(ogniK): Figure out actual values |
| 222 | tag_info.tag_type = 2; | 223 | .tag_type = 2, |
| 223 | ctx.WriteBuffer(&tag_info, sizeof(TagInfo)); | 224 | .padding_2 = {}, |
| 225 | }; | ||
| 226 | ctx.WriteBuffer(tag_info); | ||
| 224 | rb.Push(RESULT_SUCCESS); | 227 | rb.Push(RESULT_SUCCESS); |
| 225 | } | 228 | } |
| 226 | 229 | ||
| @@ -236,8 +239,8 @@ private: | |||
| 236 | LOG_DEBUG(Service_NFP, "called"); | 239 | LOG_DEBUG(Service_NFP, "called"); |
| 237 | 240 | ||
| 238 | IPC::ResponseBuilder rb{ctx, 2}; | 241 | IPC::ResponseBuilder rb{ctx, 2}; |
| 239 | auto amiibo = nfp_interface.GetAmiiboBuffer(); | 242 | const auto& amiibo = nfp_interface.GetAmiiboBuffer(); |
| 240 | ctx.WriteBuffer(&amiibo.model_info, sizeof(amiibo.model_info)); | 243 | ctx.WriteBuffer(amiibo.model_info); |
| 241 | rb.Push(RESULT_SUCCESS); | 244 | rb.Push(RESULT_SUCCESS); |
| 242 | } | 245 | } |
| 243 | 246 | ||
| @@ -283,7 +286,7 @@ private: | |||
| 283 | 286 | ||
| 284 | CommonInfo common_info{}; | 287 | CommonInfo common_info{}; |
| 285 | common_info.application_area_size = 0; | 288 | common_info.application_area_size = 0; |
| 286 | ctx.WriteBuffer(&common_info, sizeof(CommonInfo)); | 289 | ctx.WriteBuffer(common_info); |
| 287 | 290 | ||
| 288 | IPC::ResponseBuilder rb{ctx, 2}; | 291 | IPC::ResponseBuilder rb{ctx, 2}; |
| 289 | rb.Push(RESULT_SUCCESS); | 292 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 01ddcdbd6..2e9d95195 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "core/hle/kernel/writable_event.h" | 9 | #include "core/hle/kernel/writable_event.h" |
| 10 | #include "core/hle/service/nifm/nifm.h" | 10 | #include "core/hle/service/nifm/nifm.h" |
| 11 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 12 | #include "core/network/network.h" | ||
| 12 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| 13 | 14 | ||
| 14 | namespace Service::NIFM { | 15 | namespace Service::NIFM { |
| @@ -174,6 +175,16 @@ private: | |||
| 174 | IPC::ResponseBuilder rb{ctx, 2}; | 175 | IPC::ResponseBuilder rb{ctx, 2}; |
| 175 | rb.Push(RESULT_SUCCESS); | 176 | rb.Push(RESULT_SUCCESS); |
| 176 | } | 177 | } |
| 178 | void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { | ||
| 179 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 180 | |||
| 181 | const auto [ipv4, error] = Network::GetHostIPv4Address(); | ||
| 182 | UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); | ||
| 183 | |||
| 184 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 185 | rb.Push(RESULT_SUCCESS); | ||
| 186 | rb.PushRaw(ipv4); | ||
| 187 | } | ||
| 177 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { | 188 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 178 | LOG_DEBUG(Service_NIFM, "called"); | 189 | LOG_DEBUG(Service_NIFM, "called"); |
| 179 | 190 | ||
| @@ -235,7 +246,7 @@ IGeneralService::IGeneralService(Core::System& system) | |||
| 235 | {9, nullptr, "SetNetworkProfile"}, | 246 | {9, nullptr, "SetNetworkProfile"}, |
| 236 | {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"}, | 247 | {10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"}, |
| 237 | {11, nullptr, "GetScanDataOld"}, | 248 | {11, nullptr, "GetScanDataOld"}, |
| 238 | {12, nullptr, "GetCurrentIpAddress"}, | 249 | {12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"}, |
| 239 | {13, nullptr, "GetCurrentAccessPointOld"}, | 250 | {13, nullptr, "GetCurrentAccessPointOld"}, |
| 240 | {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, | 251 | {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, |
| 241 | {15, nullptr, "GetCurrentIpConfigInfo"}, | 252 | {15, nullptr, "GetCurrentIpConfigInfo"}, |
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp index 886450be2..58ee1f712 100644 --- a/src/core/hle/service/ns/ns.cpp +++ b/src/core/hle/service/ns/ns.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "core/file_sys/control_metadata.h" | 6 | #include "core/file_sys/control_metadata.h" |
| 7 | #include "core/file_sys/patch_manager.h" | 7 | #include "core/file_sys/patch_manager.h" |
| 8 | #include "core/file_sys/vfs.h" | ||
| 8 | #include "core/hle/ipc_helpers.h" | 9 | #include "core/hle/ipc_helpers.h" |
| 9 | #include "core/hle/kernel/hle_ipc.h" | 10 | #include "core/hle/kernel/hle_ipc.h" |
| 10 | #include "core/hle/service/ns/errors.h" | 11 | #include "core/hle/service/ns/errors.h" |
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 1b52511a5..0240d6643 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h | |||
| @@ -21,8 +21,9 @@ namespace Service::Nvidia::Devices { | |||
| 21 | /// implement the ioctl interface. | 21 | /// implement the ioctl interface. |
| 22 | class nvdevice { | 22 | class nvdevice { |
| 23 | public: | 23 | public: |
| 24 | explicit nvdevice(Core::System& system) : system{system} {}; | 24 | explicit nvdevice(Core::System& system) : system{system} {} |
| 25 | virtual ~nvdevice() = default; | 25 | virtual ~nvdevice() = default; |
| 26 | |||
| 26 | union Ioctl { | 27 | union Ioctl { |
| 27 | u32_le raw; | 28 | u32_le raw; |
| 28 | BitField<0, 8, u32> cmd; | 29 | BitField<0, 8, u32> cmd; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 195421cc0..39bd2a45b 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -16,11 +16,12 @@ | |||
| 16 | #include "video_core/renderer_base.h" | 16 | #include "video_core/renderer_base.h" |
| 17 | 17 | ||
| 18 | namespace Service::Nvidia::Devices { | 18 | namespace Service::Nvidia::Devices { |
| 19 | |||
| 19 | namespace NvErrCodes { | 20 | namespace NvErrCodes { |
| 20 | enum { | 21 | constexpr u32 Success{}; |
| 21 | InvalidNmapHandle = -22, | 22 | constexpr u32 OutOfMemory{static_cast<u32>(-12)}; |
| 22 | }; | 23 | constexpr u32 InvalidInput{static_cast<u32>(-22)}; |
| 23 | } | 24 | } // namespace NvErrCodes |
| 24 | 25 | ||
| 25 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | 26 | nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) |
| 26 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | 27 | : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} |
| @@ -49,8 +50,9 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std: | |||
| 49 | break; | 50 | break; |
| 50 | } | 51 | } |
| 51 | 52 | ||
| 52 | if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) | 53 | if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) { |
| 53 | return Remap(input, output); | 54 | return Remap(input, output); |
| 55 | } | ||
| 54 | 56 | ||
| 55 | UNIMPLEMENTED_MSG("Unimplemented ioctl command"); | 57 | UNIMPLEMENTED_MSG("Unimplemented ioctl command"); |
| 56 | return 0; | 58 | return 0; |
| @@ -59,6 +61,7 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std: | |||
| 59 | u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { | 61 | u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) { |
| 60 | IoctlInitalizeEx params{}; | 62 | IoctlInitalizeEx params{}; |
| 61 | std::memcpy(¶ms, input.data(), input.size()); | 63 | std::memcpy(¶ms, input.data(), input.size()); |
| 64 | |||
| 62 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); | 65 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size); |
| 63 | 66 | ||
| 64 | return 0; | 67 | return 0; |
| @@ -67,53 +70,61 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 67 | u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { | 70 | u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) { |
| 68 | IoctlAllocSpace params{}; | 71 | IoctlAllocSpace params{}; |
| 69 | std::memcpy(¶ms, input.data(), input.size()); | 72 | std::memcpy(¶ms, input.data(), input.size()); |
| 73 | |||
| 70 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, | 74 | LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, |
| 71 | params.page_size, params.flags); | 75 | params.page_size, params.flags); |
| 72 | 76 | ||
| 73 | auto& gpu = system.GPU(); | 77 | const auto size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; |
| 74 | const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; | 78 | if ((params.flags & AddressSpaceFlags::FixedOffset) != AddressSpaceFlags::None) { |
| 75 | if (params.flags & 1) { | 79 | params.offset = *system.GPU().MemoryManager().AllocateFixed(params.offset, size); |
| 76 | params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1); | ||
| 77 | } else { | 80 | } else { |
| 78 | params.offset = gpu.MemoryManager().AllocateSpace(size, params.align); | 81 | params.offset = system.GPU().MemoryManager().Allocate(size, params.align); |
| 82 | } | ||
| 83 | |||
| 84 | auto result{NvErrCodes::Success}; | ||
| 85 | if (!params.offset) { | ||
| 86 | LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size); | ||
| 87 | result = NvErrCodes::OutOfMemory; | ||
| 79 | } | 88 | } |
| 80 | 89 | ||
| 81 | std::memcpy(output.data(), ¶ms, output.size()); | 90 | std::memcpy(output.data(), ¶ms, output.size()); |
| 82 | return 0; | 91 | return result; |
| 83 | } | 92 | } |
| 84 | 93 | ||
| 85 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { | 94 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { |
| 86 | std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry); | 95 | const auto num_entries = input.size() / sizeof(IoctlRemapEntry); |
| 87 | 96 | ||
| 88 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); | 97 | LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries); |
| 89 | 98 | ||
| 99 | auto result{NvErrCodes::Success}; | ||
| 90 | std::vector<IoctlRemapEntry> entries(num_entries); | 100 | std::vector<IoctlRemapEntry> entries(num_entries); |
| 91 | std::memcpy(entries.data(), input.data(), input.size()); | 101 | std::memcpy(entries.data(), input.data(), input.size()); |
| 92 | 102 | ||
| 93 | auto& gpu = system.GPU(); | ||
| 94 | for (const auto& entry : entries) { | 103 | for (const auto& entry : entries) { |
| 95 | LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", | 104 | LOG_DEBUG(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", |
| 96 | entry.offset, entry.nvmap_handle, entry.pages); | 105 | entry.offset, entry.nvmap_handle, entry.pages); |
| 97 | GPUVAddr offset = static_cast<GPUVAddr>(entry.offset) << 0x10; | 106 | |
| 98 | auto object = nvmap_dev->GetObject(entry.nvmap_handle); | 107 | const auto object{nvmap_dev->GetObject(entry.nvmap_handle)}; |
| 99 | if (!object) { | 108 | if (!object) { |
| 100 | LOG_CRITICAL(Service_NVDRV, "nvmap {} is an invalid handle!", entry.nvmap_handle); | 109 | LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle); |
| 101 | std::memcpy(output.data(), entries.data(), output.size()); | 110 | result = NvErrCodes::InvalidInput; |
| 102 | return static_cast<u32>(NvErrCodes::InvalidNmapHandle); | 111 | break; |
| 103 | } | 112 | } |
| 104 | 113 | ||
| 105 | ASSERT(object->status == nvmap::Object::Status::Allocated); | 114 | const auto offset{static_cast<GPUVAddr>(entry.offset) << 0x10}; |
| 115 | const auto size{static_cast<u64>(entry.pages) << 0x10}; | ||
| 116 | const auto map_offset{static_cast<u64>(entry.map_offset) << 0x10}; | ||
| 117 | const auto addr{system.GPU().MemoryManager().Map(object->addr + map_offset, offset, size)}; | ||
| 106 | 118 | ||
| 107 | const u64 size = static_cast<u64>(entry.pages) << 0x10; | 119 | if (!addr) { |
| 108 | ASSERT(size <= object->size); | 120 | LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!"); |
| 109 | const u64 map_offset = static_cast<u64>(entry.map_offset) << 0x10; | 121 | result = NvErrCodes::InvalidInput; |
| 110 | 122 | break; | |
| 111 | const GPUVAddr returned = | 123 | } |
| 112 | gpu.MemoryManager().MapBufferEx(object->addr + map_offset, offset, size); | ||
| 113 | ASSERT(returned == offset); | ||
| 114 | } | 124 | } |
| 125 | |||
| 115 | std::memcpy(output.data(), entries.data(), output.size()); | 126 | std::memcpy(output.data(), entries.data(), output.size()); |
| 116 | return 0; | 127 | return result; |
| 117 | } | 128 | } |
| 118 | 129 | ||
| 119 | u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | 130 | u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -126,44 +137,76 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 126 | params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, | 137 | params.flags, params.nvmap_handle, params.buffer_offset, params.mapping_size, |
| 127 | params.offset); | 138 | params.offset); |
| 128 | 139 | ||
| 129 | if (!params.nvmap_handle) { | 140 | const auto object{nvmap_dev->GetObject(params.nvmap_handle)}; |
| 130 | return 0; | 141 | if (!object) { |
| 142 | LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle); | ||
| 143 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 144 | return NvErrCodes::InvalidInput; | ||
| 131 | } | 145 | } |
| 132 | 146 | ||
| 133 | auto object = nvmap_dev->GetObject(params.nvmap_handle); | ||
| 134 | ASSERT(object); | ||
| 135 | |||
| 136 | // We can only map objects that have already been assigned a CPU address. | ||
| 137 | ASSERT(object->status == nvmap::Object::Status::Allocated); | ||
| 138 | |||
| 139 | ASSERT(params.buffer_offset == 0); | ||
| 140 | |||
| 141 | // The real nvservices doesn't make a distinction between handles and ids, and | 147 | // The real nvservices doesn't make a distinction between handles and ids, and |
| 142 | // object can only have one handle and it will be the same as its id. Assert that this is the | 148 | // object can only have one handle and it will be the same as its id. Assert that this is the |
| 143 | // case to prevent unexpected behavior. | 149 | // case to prevent unexpected behavior. |
| 144 | ASSERT(object->id == params.nvmap_handle); | 150 | ASSERT(object->id == params.nvmap_handle); |
| 145 | |||
| 146 | auto& gpu = system.GPU(); | 151 | auto& gpu = system.GPU(); |
| 147 | 152 | ||
| 148 | if (params.flags & 1) { | 153 | u64 page_size{params.page_size}; |
| 149 | params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size); | 154 | if (!page_size) { |
| 150 | } else { | 155 | page_size = object->align; |
| 151 | params.offset = gpu.MemoryManager().MapBufferEx(object->addr, object->size); | 156 | } |
| 157 | |||
| 158 | if ((params.flags & AddressSpaceFlags::Remap) != AddressSpaceFlags::None) { | ||
| 159 | if (const auto buffer_map{FindBufferMap(params.offset)}; buffer_map) { | ||
| 160 | const auto cpu_addr{static_cast<VAddr>(buffer_map->CpuAddr() + params.buffer_offset)}; | ||
| 161 | const auto gpu_addr{static_cast<GPUVAddr>(params.offset + params.buffer_offset)}; | ||
| 162 | |||
| 163 | if (!gpu.MemoryManager().Map(cpu_addr, gpu_addr, params.mapping_size)) { | ||
| 164 | LOG_CRITICAL(Service_NVDRV, | ||
| 165 | "remap failed, flags={:X}, nvmap_handle={:X}, buffer_offset={}, " | ||
| 166 | "mapping_size = {}, offset={}", | ||
| 167 | params.flags, params.nvmap_handle, params.buffer_offset, | ||
| 168 | params.mapping_size, params.offset); | ||
| 169 | |||
| 170 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 171 | return NvErrCodes::InvalidInput; | ||
| 172 | } | ||
| 173 | |||
| 174 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 175 | return NvErrCodes::Success; | ||
| 176 | } else { | ||
| 177 | LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset); | ||
| 178 | |||
| 179 | std::memcpy(output.data(), ¶ms, output.size()); | ||
| 180 | return NvErrCodes::InvalidInput; | ||
| 181 | } | ||
| 152 | } | 182 | } |
| 153 | 183 | ||
| 154 | // Create a new mapping entry for this operation. | 184 | // We can only map objects that have already been assigned a CPU address. |
| 155 | ASSERT_MSG(buffer_mappings.find(params.offset) == buffer_mappings.end(), | 185 | ASSERT(object->status == nvmap::Object::Status::Allocated); |
| 156 | "Offset is already mapped"); | 186 | |
| 187 | const auto physical_address{object->addr + params.buffer_offset}; | ||
| 188 | u64 size{params.mapping_size}; | ||
| 189 | if (!size) { | ||
| 190 | size = object->size; | ||
| 191 | } | ||
| 157 | 192 | ||
| 158 | BufferMapping mapping{}; | 193 | const bool is_alloc{(params.flags & AddressSpaceFlags::FixedOffset) == AddressSpaceFlags::None}; |
| 159 | mapping.nvmap_handle = params.nvmap_handle; | 194 | if (is_alloc) { |
| 160 | mapping.offset = params.offset; | 195 | params.offset = gpu.MemoryManager().MapAllocate(physical_address, size, page_size); |
| 161 | mapping.size = object->size; | 196 | } else { |
| 197 | params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size); | ||
| 198 | } | ||
| 162 | 199 | ||
| 163 | buffer_mappings[params.offset] = mapping; | 200 | auto result{NvErrCodes::Success}; |
| 201 | if (!params.offset) { | ||
| 202 | LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size); | ||
| 203 | result = NvErrCodes::InvalidInput; | ||
| 204 | } else { | ||
| 205 | AddBufferMap(params.offset, size, physical_address, is_alloc); | ||
| 206 | } | ||
| 164 | 207 | ||
| 165 | std::memcpy(output.data(), ¶ms, output.size()); | 208 | std::memcpy(output.data(), ¶ms, output.size()); |
| 166 | return 0; | 209 | return result; |
| 167 | } | 210 | } |
| 168 | 211 | ||
| 169 | u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | 212 | u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { |
| @@ -172,24 +215,20 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 172 | 215 | ||
| 173 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); | 216 | LOG_DEBUG(Service_NVDRV, "called, offset=0x{:X}", params.offset); |
| 174 | 217 | ||
| 175 | const auto itr = buffer_mappings.find(params.offset); | 218 | if (const auto size{RemoveBufferMap(params.offset)}; size) { |
| 176 | if (itr == buffer_mappings.end()) { | 219 | system.GPU().MemoryManager().Unmap(params.offset, *size); |
| 177 | LOG_WARNING(Service_NVDRV, "Tried to unmap an invalid offset 0x{:X}", params.offset); | 220 | } else { |
| 178 | // Hardware tests shows that unmapping an already unmapped buffer always returns successful | 221 | LOG_ERROR(Service_NVDRV, "invalid offset=0x{:X}", params.offset); |
| 179 | // and doesn't fail. | ||
| 180 | return 0; | ||
| 181 | } | 222 | } |
| 182 | 223 | ||
| 183 | params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size); | ||
| 184 | buffer_mappings.erase(itr->second.offset); | ||
| 185 | |||
| 186 | std::memcpy(output.data(), ¶ms, output.size()); | 224 | std::memcpy(output.data(), ¶ms, output.size()); |
| 187 | return 0; | 225 | return NvErrCodes::Success; |
| 188 | } | 226 | } |
| 189 | 227 | ||
| 190 | u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { | 228 | u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) { |
| 191 | IoctlBindChannel params{}; | 229 | IoctlBindChannel params{}; |
| 192 | std::memcpy(¶ms, input.data(), input.size()); | 230 | std::memcpy(¶ms, input.data(), input.size()); |
| 231 | |||
| 193 | LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); | 232 | LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd); |
| 194 | 233 | ||
| 195 | channel = params.fd; | 234 | channel = params.fd; |
| @@ -199,6 +238,7 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou | |||
| 199 | u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { | 238 | u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) { |
| 200 | IoctlGetVaRegions params{}; | 239 | IoctlGetVaRegions params{}; |
| 201 | std::memcpy(¶ms, input.data(), input.size()); | 240 | std::memcpy(¶ms, input.data(), input.size()); |
| 241 | |||
| 202 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, | 242 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr, |
| 203 | params.buf_size); | 243 | params.buf_size); |
| 204 | 244 | ||
| @@ -210,9 +250,43 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o | |||
| 210 | params.regions[1].offset = 0x04000000; | 250 | params.regions[1].offset = 0x04000000; |
| 211 | params.regions[1].page_size = 0x10000; | 251 | params.regions[1].page_size = 0x10000; |
| 212 | params.regions[1].pages = 0x1bffff; | 252 | params.regions[1].pages = 0x1bffff; |
| 253 | |||
| 213 | // TODO(ogniK): This probably can stay stubbed but should add support way way later | 254 | // TODO(ogniK): This probably can stay stubbed but should add support way way later |
| 255 | |||
| 214 | std::memcpy(output.data(), ¶ms, output.size()); | 256 | std::memcpy(output.data(), ¶ms, output.size()); |
| 215 | return 0; | 257 | return 0; |
| 216 | } | 258 | } |
| 217 | 259 | ||
| 260 | std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const { | ||
| 261 | const auto end{buffer_mappings.upper_bound(gpu_addr)}; | ||
| 262 | for (auto iter{buffer_mappings.begin()}; iter != end; ++iter) { | ||
| 263 | if (gpu_addr >= iter->second.StartAddr() && gpu_addr < iter->second.EndAddr()) { | ||
| 264 | return iter->second; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | return std::nullopt; | ||
| 269 | } | ||
| 270 | |||
| 271 | void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, | ||
| 272 | bool is_allocated) { | ||
| 273 | buffer_mappings[gpu_addr] = {gpu_addr, size, cpu_addr, is_allocated}; | ||
| 274 | } | ||
| 275 | |||
| 276 | std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) { | ||
| 277 | if (const auto iter{buffer_mappings.find(gpu_addr)}; iter != buffer_mappings.end()) { | ||
| 278 | std::size_t size{}; | ||
| 279 | |||
| 280 | if (iter->second.IsAllocated()) { | ||
| 281 | size = iter->second.Size(); | ||
| 282 | } | ||
| 283 | |||
| 284 | buffer_mappings.erase(iter); | ||
| 285 | |||
| 286 | return size; | ||
| 287 | } | ||
| 288 | |||
| 289 | return std::nullopt; | ||
| 290 | } | ||
| 291 | |||
| 218 | } // namespace Service::Nvidia::Devices | 292 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index f79fcc065..9a0cdff0c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h | |||
| @@ -4,9 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <unordered_map> | 9 | #include <optional> |
| 9 | #include <vector> | 10 | #include <vector> |
| 11 | |||
| 12 | #include "common/common_funcs.h" | ||
| 10 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 12 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 15 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| @@ -15,6 +18,13 @@ namespace Service::Nvidia::Devices { | |||
| 15 | 18 | ||
| 16 | class nvmap; | 19 | class nvmap; |
| 17 | 20 | ||
| 21 | enum class AddressSpaceFlags : u32 { | ||
| 22 | None = 0x0, | ||
| 23 | FixedOffset = 0x1, | ||
| 24 | Remap = 0x100, | ||
| 25 | }; | ||
| 26 | DECLARE_ENUM_FLAG_OPERATORS(AddressSpaceFlags); | ||
| 27 | |||
| 18 | class nvhost_as_gpu final : public nvdevice { | 28 | class nvhost_as_gpu final : public nvdevice { |
| 19 | public: | 29 | public: |
| 20 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | 30 | explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); |
| @@ -25,6 +35,45 @@ public: | |||
| 25 | IoctlVersion version) override; | 35 | IoctlVersion version) override; |
| 26 | 36 | ||
| 27 | private: | 37 | private: |
| 38 | class BufferMap final { | ||
| 39 | public: | ||
| 40 | constexpr BufferMap() = default; | ||
| 41 | |||
| 42 | constexpr BufferMap(GPUVAddr start_addr, std::size_t size) | ||
| 43 | : start_addr{start_addr}, end_addr{start_addr + size} {} | ||
| 44 | |||
| 45 | constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr, | ||
| 46 | bool is_allocated) | ||
| 47 | : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr}, | ||
| 48 | is_allocated{is_allocated} {} | ||
| 49 | |||
| 50 | constexpr VAddr StartAddr() const { | ||
| 51 | return start_addr; | ||
| 52 | } | ||
| 53 | |||
| 54 | constexpr VAddr EndAddr() const { | ||
| 55 | return end_addr; | ||
| 56 | } | ||
| 57 | |||
| 58 | constexpr std::size_t Size() const { | ||
| 59 | return end_addr - start_addr; | ||
| 60 | } | ||
| 61 | |||
| 62 | constexpr VAddr CpuAddr() const { | ||
| 63 | return cpu_addr; | ||
| 64 | } | ||
| 65 | |||
| 66 | constexpr bool IsAllocated() const { | ||
| 67 | return is_allocated; | ||
| 68 | } | ||
| 69 | |||
| 70 | private: | ||
| 71 | GPUVAddr start_addr{}; | ||
| 72 | GPUVAddr end_addr{}; | ||
| 73 | VAddr cpu_addr{}; | ||
| 74 | bool is_allocated{}; | ||
| 75 | }; | ||
| 76 | |||
| 28 | enum class IoctlCommand : u32_le { | 77 | enum class IoctlCommand : u32_le { |
| 29 | IocInitalizeExCommand = 0x40284109, | 78 | IocInitalizeExCommand = 0x40284109, |
| 30 | IocAllocateSpaceCommand = 0xC0184102, | 79 | IocAllocateSpaceCommand = 0xC0184102, |
| @@ -49,7 +98,7 @@ private: | |||
| 49 | struct IoctlAllocSpace { | 98 | struct IoctlAllocSpace { |
| 50 | u32_le pages; | 99 | u32_le pages; |
| 51 | u32_le page_size; | 100 | u32_le page_size; |
| 52 | u32_le flags; | 101 | AddressSpaceFlags flags; |
| 53 | INSERT_PADDING_WORDS(1); | 102 | INSERT_PADDING_WORDS(1); |
| 54 | union { | 103 | union { |
| 55 | u64_le offset; | 104 | u64_le offset; |
| @@ -69,18 +118,18 @@ private: | |||
| 69 | static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); | 118 | static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); |
| 70 | 119 | ||
| 71 | struct IoctlMapBufferEx { | 120 | struct IoctlMapBufferEx { |
| 72 | u32_le flags; // bit0: fixed_offset, bit2: cacheable | 121 | AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable |
| 73 | u32_le kind; // -1 is default | 122 | u32_le kind; // -1 is default |
| 74 | u32_le nvmap_handle; | 123 | u32_le nvmap_handle; |
| 75 | u32_le page_size; // 0 means don't care | 124 | u32_le page_size; // 0 means don't care |
| 76 | u64_le buffer_offset; | 125 | s64_le buffer_offset; |
| 77 | u64_le mapping_size; | 126 | u64_le mapping_size; |
| 78 | u64_le offset; | 127 | s64_le offset; |
| 79 | }; | 128 | }; |
| 80 | static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); | 129 | static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size"); |
| 81 | 130 | ||
| 82 | struct IoctlUnmapBuffer { | 131 | struct IoctlUnmapBuffer { |
| 83 | u64_le offset; | 132 | s64_le offset; |
| 84 | }; | 133 | }; |
| 85 | static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); | 134 | static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size"); |
| 86 | 135 | ||
| @@ -106,15 +155,6 @@ private: | |||
| 106 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, | 155 | static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2, |
| 107 | "IoctlGetVaRegions is incorrect size"); | 156 | "IoctlGetVaRegions is incorrect size"); |
| 108 | 157 | ||
| 109 | struct BufferMapping { | ||
| 110 | u64 offset; | ||
| 111 | u64 size; | ||
| 112 | u32 nvmap_handle; | ||
| 113 | }; | ||
| 114 | |||
| 115 | /// Map containing the nvmap object mappings in GPU memory. | ||
| 116 | std::unordered_map<u64, BufferMapping> buffer_mappings; | ||
| 117 | |||
| 118 | u32 channel{}; | 158 | u32 channel{}; |
| 119 | 159 | ||
| 120 | u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); | 160 | u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); |
| @@ -125,7 +165,14 @@ private: | |||
| 125 | u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); | 165 | u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); |
| 126 | u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); | 166 | u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); |
| 127 | 167 | ||
| 168 | std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const; | ||
| 169 | void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated); | ||
| 170 | std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr); | ||
| 171 | |||
| 128 | std::shared_ptr<nvmap> nvmap_dev; | 172 | std::shared_ptr<nvmap> nvmap_dev; |
| 173 | |||
| 174 | // This is expected to be ordered, therefore we must use a map, not unordered_map | ||
| 175 | std::map<GPUVAddr, BufferMap> buffer_mappings; | ||
| 129 | }; | 176 | }; |
| 130 | 177 | ||
| 131 | } // namespace Service::Nvidia::Devices | 178 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index bdae8b887..fcb612864 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp | |||
| @@ -22,6 +22,18 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std:: | |||
| 22 | switch (static_cast<IoctlCommand>(command.raw)) { | 22 | switch (static_cast<IoctlCommand>(command.raw)) { |
| 23 | case IoctlCommand::IocSetNVMAPfdCommand: | 23 | case IoctlCommand::IocSetNVMAPfdCommand: |
| 24 | return SetNVMAPfd(input, output); | 24 | return SetNVMAPfd(input, output); |
| 25 | case IoctlCommand::IocSubmit: | ||
| 26 | return Submit(input, output); | ||
| 27 | case IoctlCommand::IocGetSyncpoint: | ||
| 28 | return GetSyncpoint(input, output); | ||
| 29 | case IoctlCommand::IocGetWaitbase: | ||
| 30 | return GetWaitbase(input, output); | ||
| 31 | case IoctlCommand::IocMapBuffer: | ||
| 32 | return MapBuffer(input, output); | ||
| 33 | case IoctlCommand::IocMapBufferEx: | ||
| 34 | return MapBufferEx(input, output); | ||
| 35 | case IoctlCommand::IocUnmapBufferEx: | ||
| 36 | return UnmapBufferEx(input, output); | ||
| 25 | } | 37 | } |
| 26 | 38 | ||
| 27 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | 39 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); |
| @@ -30,11 +42,67 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std:: | |||
| 30 | 42 | ||
| 31 | u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { | 43 | u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { |
| 32 | IoctlSetNvmapFD params{}; | 44 | IoctlSetNvmapFD params{}; |
| 33 | std::memcpy(¶ms, input.data(), input.size()); | 45 | std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD)); |
| 34 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); | 46 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); |
| 35 | 47 | ||
| 36 | nvmap_fd = params.nvmap_fd; | 48 | nvmap_fd = params.nvmap_fd; |
| 37 | return 0; | 49 | return 0; |
| 38 | } | 50 | } |
| 39 | 51 | ||
| 52 | u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 53 | IoctlSubmit params{}; | ||
| 54 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); | ||
| 55 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | ||
| 56 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); | ||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 61 | IoctlGetSyncpoint params{}; | ||
| 62 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); | ||
| 63 | LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); | ||
| 64 | params.value = 0; // Seems to be hard coded at 0 | ||
| 65 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); | ||
| 66 | return 0; | ||
| 67 | } | ||
| 68 | |||
| 69 | u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 70 | IoctlGetWaitbase params{}; | ||
| 71 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | ||
| 72 | LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); | ||
| 73 | params.value = 0; // Seems to be hard coded at 0 | ||
| 74 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 79 | IoctlMapBuffer params{}; | ||
| 80 | std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); | ||
| 81 | LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, | ||
| 82 | params.address_1); | ||
| 83 | params.address_1 = 0; | ||
| 84 | params.address_2 = 0; | ||
| 85 | std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 90 | IoctlMapBufferEx params{}; | ||
| 91 | std::memcpy(¶ms, input.data(), sizeof(IoctlMapBufferEx)); | ||
| 92 | LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, | ||
| 93 | params.address_1); | ||
| 94 | params.address_1 = 0; | ||
| 95 | params.address_2 = 0; | ||
| 96 | std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBufferEx)); | ||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 101 | IoctlUnmapBufferEx params{}; | ||
| 102 | std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx)); | ||
| 103 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | ||
| 104 | std::memcpy(output.data(), ¶ms, sizeof(IoctlUnmapBufferEx)); | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 40 | } // namespace Service::Nvidia::Devices | 108 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index cbdac8069..4332db118 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h | |||
| @@ -23,16 +23,66 @@ public: | |||
| 23 | private: | 23 | private: |
| 24 | enum class IoctlCommand : u32_le { | 24 | enum class IoctlCommand : u32_le { |
| 25 | IocSetNVMAPfdCommand = 0x40044801, | 25 | IocSetNVMAPfdCommand = 0x40044801, |
| 26 | IocSubmit = 0xC0400001, | ||
| 27 | IocGetSyncpoint = 0xC0080002, | ||
| 28 | IocGetWaitbase = 0xC0080003, | ||
| 29 | IocMapBuffer = 0xC01C0009, | ||
| 30 | IocMapBufferEx = 0xC0A40009, | ||
| 31 | IocUnmapBufferEx = 0xC0A4000A, | ||
| 26 | }; | 32 | }; |
| 27 | 33 | ||
| 28 | struct IoctlSetNvmapFD { | 34 | struct IoctlSetNvmapFD { |
| 29 | u32_le nvmap_fd; | 35 | u32_le nvmap_fd; |
| 30 | }; | 36 | }; |
| 31 | static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); | 37 | static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size"); |
| 38 | |||
| 39 | struct IoctlSubmit { | ||
| 40 | INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure | ||
| 41 | }; | ||
| 42 | static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size"); | ||
| 43 | |||
| 44 | struct IoctlGetSyncpoint { | ||
| 45 | u32 unknown; // seems to be ignored? Nintendo added this | ||
| 46 | u32 value; | ||
| 47 | }; | ||
| 48 | static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size"); | ||
| 49 | |||
| 50 | struct IoctlGetWaitbase { | ||
| 51 | u32 unknown; // seems to be ignored? Nintendo added this | ||
| 52 | u32 value; | ||
| 53 | }; | ||
| 54 | static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size"); | ||
| 55 | |||
| 56 | struct IoctlMapBuffer { | ||
| 57 | u32 unknown; | ||
| 58 | u32 address_1; | ||
| 59 | u32 address_2; | ||
| 60 | INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size"); | ||
| 63 | |||
| 64 | struct IoctlMapBufferEx { | ||
| 65 | u32 unknown; | ||
| 66 | u32 address_1; | ||
| 67 | u32 address_2; | ||
| 68 | INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure | ||
| 69 | }; | ||
| 70 | static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size"); | ||
| 71 | |||
| 72 | struct IoctlUnmapBufferEx { | ||
| 73 | INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure | ||
| 74 | }; | ||
| 75 | static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size"); | ||
| 32 | 76 | ||
| 33 | u32_le nvmap_fd{}; | 77 | u32_le nvmap_fd{}; |
| 34 | 78 | ||
| 35 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); | 79 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); |
| 80 | u32 Submit(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 81 | u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 82 | u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 83 | u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 84 | u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 85 | u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 36 | }; | 86 | }; |
| 37 | 87 | ||
| 38 | } // namespace Service::Nvidia::Devices | 88 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index c695b8863..9da19ad56 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp | |||
| @@ -22,6 +22,18 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve | |||
| 22 | switch (static_cast<IoctlCommand>(command.raw)) { | 22 | switch (static_cast<IoctlCommand>(command.raw)) { |
| 23 | case IoctlCommand::IocSetNVMAPfdCommand: | 23 | case IoctlCommand::IocSetNVMAPfdCommand: |
| 24 | return SetNVMAPfd(input, output); | 24 | return SetNVMAPfd(input, output); |
| 25 | case IoctlCommand::IocSubmit: | ||
| 26 | return Submit(input, output); | ||
| 27 | case IoctlCommand::IocGetSyncpoint: | ||
| 28 | return GetSyncpoint(input, output); | ||
| 29 | case IoctlCommand::IocGetWaitbase: | ||
| 30 | return GetWaitbase(input, output); | ||
| 31 | case IoctlCommand::IocMapBuffer: | ||
| 32 | return MapBuffer(input, output); | ||
| 33 | case IoctlCommand::IocMapBufferEx: | ||
| 34 | return MapBuffer(input, output); | ||
| 35 | case IoctlCommand::IocUnmapBufferEx: | ||
| 36 | return UnmapBufferEx(input, output); | ||
| 25 | } | 37 | } |
| 26 | 38 | ||
| 27 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); | 39 | UNIMPLEMENTED_MSG("Unimplemented ioctl"); |
| @@ -30,11 +42,71 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve | |||
| 30 | 42 | ||
| 31 | u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { | 43 | u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) { |
| 32 | IoctlSetNvmapFD params{}; | 44 | IoctlSetNvmapFD params{}; |
| 33 | std::memcpy(¶ms, input.data(), input.size()); | 45 | std::memcpy(¶ms, input.data(), sizeof(IoctlSetNvmapFD)); |
| 34 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); | 46 | LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd); |
| 35 | 47 | ||
| 36 | nvmap_fd = params.nvmap_fd; | 48 | nvmap_fd = params.nvmap_fd; |
| 37 | return 0; | 49 | return 0; |
| 38 | } | 50 | } |
| 39 | 51 | ||
| 52 | u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 53 | IoctlSubmit params{}; | ||
| 54 | std::memcpy(¶ms, input.data(), sizeof(IoctlSubmit)); | ||
| 55 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | ||
| 56 | |||
| 57 | // Workaround for Luigi's Mansion 3, as nvhost_vic is not implemented for asynch GPU | ||
| 58 | params.command_buffer = {}; | ||
| 59 | |||
| 60 | std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit)); | ||
| 61 | return 0; | ||
| 62 | } | ||
| 63 | |||
| 64 | u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 65 | IoctlGetSyncpoint params{}; | ||
| 66 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetSyncpoint)); | ||
| 67 | LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); | ||
| 68 | params.value = 0; // Seems to be hard coded at 0 | ||
| 69 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetSyncpoint)); | ||
| 70 | return 0; | ||
| 71 | } | ||
| 72 | |||
| 73 | u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 74 | IoctlGetWaitbase params{}; | ||
| 75 | std::memcpy(¶ms, input.data(), sizeof(IoctlGetWaitbase)); | ||
| 76 | LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown); | ||
| 77 | params.value = 0; // Seems to be hard coded at 0 | ||
| 78 | std::memcpy(output.data(), ¶ms, sizeof(IoctlGetWaitbase)); | ||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 83 | IoctlMapBuffer params{}; | ||
| 84 | std::memcpy(¶ms, input.data(), sizeof(IoctlMapBuffer)); | ||
| 85 | LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, | ||
| 86 | params.address_1); | ||
| 87 | params.address_1 = 0; | ||
| 88 | params.address_2 = 0; | ||
| 89 | std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBuffer)); | ||
| 90 | return 0; | ||
| 91 | } | ||
| 92 | |||
| 93 | u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 94 | IoctlMapBufferEx params{}; | ||
| 95 | std::memcpy(¶ms, input.data(), sizeof(IoctlMapBufferEx)); | ||
| 96 | LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2, | ||
| 97 | params.address_1); | ||
| 98 | params.address_1 = 0; | ||
| 99 | params.address_2 = 0; | ||
| 100 | std::memcpy(output.data(), ¶ms, sizeof(IoctlMapBufferEx)); | ||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { | ||
| 105 | IoctlUnmapBufferEx params{}; | ||
| 106 | std::memcpy(¶ms, input.data(), sizeof(IoctlUnmapBufferEx)); | ||
| 107 | LOG_WARNING(Service_NVDRV, "(STUBBED) called"); | ||
| 108 | std::memcpy(output.data(), ¶ms, sizeof(IoctlUnmapBufferEx)); | ||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 40 | } // namespace Service::Nvidia::Devices | 112 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index bec32bea1..a7bb7bbd5 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <vector> | 8 | #include <vector> |
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| @@ -23,6 +24,12 @@ public: | |||
| 23 | private: | 24 | private: |
| 24 | enum class IoctlCommand : u32_le { | 25 | enum class IoctlCommand : u32_le { |
| 25 | IocSetNVMAPfdCommand = 0x40044801, | 26 | IocSetNVMAPfdCommand = 0x40044801, |
| 27 | IocSubmit = 0xC0400001, | ||
| 28 | IocGetSyncpoint = 0xC0080002, | ||
| 29 | IocGetWaitbase = 0xC0080003, | ||
| 30 | IocMapBuffer = 0xC01C0009, | ||
| 31 | IocMapBufferEx = 0xC03C0009, | ||
| 32 | IocUnmapBufferEx = 0xC03C000A, | ||
| 26 | }; | 33 | }; |
| 27 | 34 | ||
| 28 | struct IoctlSetNvmapFD { | 35 | struct IoctlSetNvmapFD { |
| @@ -30,9 +37,65 @@ private: | |||
| 30 | }; | 37 | }; |
| 31 | static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); | 38 | static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size"); |
| 32 | 39 | ||
| 40 | struct IoctlSubmitCommandBuffer { | ||
| 41 | u32 id; | ||
| 42 | u32 offset; | ||
| 43 | u32 count; | ||
| 44 | }; | ||
| 45 | static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC, | ||
| 46 | "IoctlSubmitCommandBuffer is incorrect size"); | ||
| 47 | |||
| 48 | struct IoctlSubmit { | ||
| 49 | u32 command_buffer_count; | ||
| 50 | u32 relocations_count; | ||
| 51 | u32 syncpt_count; | ||
| 52 | u32 wait_count; | ||
| 53 | std::array<IoctlSubmitCommandBuffer, 4> command_buffer; | ||
| 54 | }; | ||
| 55 | static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit is incorrect size"); | ||
| 56 | |||
| 57 | struct IoctlGetSyncpoint { | ||
| 58 | u32 unknown; // seems to be ignored? Nintendo added this | ||
| 59 | u32 value; | ||
| 60 | }; | ||
| 61 | static_assert(sizeof(IoctlGetSyncpoint) == 0x8, "IoctlGetSyncpoint is incorrect size"); | ||
| 62 | |||
| 63 | struct IoctlGetWaitbase { | ||
| 64 | u32 unknown; // seems to be ignored? Nintendo added this | ||
| 65 | u32 value; | ||
| 66 | }; | ||
| 67 | static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size"); | ||
| 68 | |||
| 69 | struct IoctlMapBuffer { | ||
| 70 | u32 unknown; | ||
| 71 | u32 address_1; | ||
| 72 | u32 address_2; | ||
| 73 | INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure | ||
| 74 | }; | ||
| 75 | static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size"); | ||
| 76 | |||
| 77 | struct IoctlMapBufferEx { | ||
| 78 | u32 unknown; | ||
| 79 | u32 address_1; | ||
| 80 | u32 address_2; | ||
| 81 | INSERT_PADDING_BYTES(0x30); // TODO(DarkLordZach): RE this structure | ||
| 82 | }; | ||
| 83 | static_assert(sizeof(IoctlMapBufferEx) == 0x3C, "IoctlMapBufferEx is incorrect size"); | ||
| 84 | |||
| 85 | struct IoctlUnmapBufferEx { | ||
| 86 | INSERT_PADDING_BYTES(0x3C); // TODO(DarkLordZach): RE this structure | ||
| 87 | }; | ||
| 88 | static_assert(sizeof(IoctlUnmapBufferEx) == 0x3C, "IoctlUnmapBufferEx is incorrect size"); | ||
| 89 | |||
| 33 | u32_le nvmap_fd{}; | 90 | u32_le nvmap_fd{}; |
| 34 | 91 | ||
| 35 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); | 92 | u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output); |
| 93 | u32 Submit(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 94 | u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 95 | u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 96 | u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 97 | u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 98 | u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); | ||
| 36 | }; | 99 | }; |
| 37 | 100 | ||
| 38 | } // namespace Service::Nvidia::Devices | 101 | } // namespace Service::Nvidia::Devices |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 8c742316c..9436e16ad 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp | |||
| @@ -18,7 +18,12 @@ enum { | |||
| 18 | }; | 18 | }; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | nvmap::nvmap(Core::System& system) : nvdevice(system) {} | 21 | nvmap::nvmap(Core::System& system) : nvdevice(system) { |
| 22 | // Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to | ||
| 23 | // represent this. | ||
| 24 | CreateObject(0); | ||
| 25 | } | ||
| 26 | |||
| 22 | nvmap::~nvmap() = default; | 27 | nvmap::~nvmap() = default; |
| 23 | 28 | ||
| 24 | VAddr nvmap::GetObjectAddress(u32 handle) const { | 29 | VAddr nvmap::GetObjectAddress(u32 handle) const { |
| @@ -50,6 +55,21 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector< | |||
| 50 | return 0; | 55 | return 0; |
| 51 | } | 56 | } |
| 52 | 57 | ||
| 58 | u32 nvmap::CreateObject(u32 size) { | ||
| 59 | // Create a new nvmap object and obtain a handle to it. | ||
| 60 | auto object = std::make_shared<Object>(); | ||
| 61 | object->id = next_id++; | ||
| 62 | object->size = size; | ||
| 63 | object->status = Object::Status::Created; | ||
| 64 | object->refcount = 1; | ||
| 65 | |||
| 66 | const u32 handle = next_handle++; | ||
| 67 | |||
| 68 | handles.insert_or_assign(handle, std::move(object)); | ||
| 69 | |||
| 70 | return handle; | ||
| 71 | } | ||
| 72 | |||
| 53 | u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | 73 | u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { |
| 54 | IocCreateParams params; | 74 | IocCreateParams params; |
| 55 | std::memcpy(¶ms, input.data(), sizeof(params)); | 75 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| @@ -59,17 +79,8 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) { | |||
| 59 | LOG_ERROR(Service_NVDRV, "Size is 0"); | 79 | LOG_ERROR(Service_NVDRV, "Size is 0"); |
| 60 | return static_cast<u32>(NvErrCodes::InvalidValue); | 80 | return static_cast<u32>(NvErrCodes::InvalidValue); |
| 61 | } | 81 | } |
| 62 | // Create a new nvmap object and obtain a handle to it. | ||
| 63 | auto object = std::make_shared<Object>(); | ||
| 64 | object->id = next_id++; | ||
| 65 | object->size = params.size; | ||
| 66 | object->status = Object::Status::Created; | ||
| 67 | object->refcount = 1; | ||
| 68 | |||
| 69 | u32 handle = next_handle++; | ||
| 70 | handles[handle] = std::move(object); | ||
| 71 | 82 | ||
| 72 | params.handle = handle; | 83 | params.handle = CreateObject(params.size); |
| 73 | 84 | ||
| 74 | std::memcpy(output.data(), ¶ms, sizeof(params)); | 85 | std::memcpy(output.data(), ¶ms, sizeof(params)); |
| 75 | return 0; | 86 | return 0; |
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 73c2e8809..84624be00 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h | |||
| @@ -49,10 +49,10 @@ public: | |||
| 49 | 49 | ||
| 50 | private: | 50 | private: |
| 51 | /// Id to use for the next handle that is created. | 51 | /// Id to use for the next handle that is created. |
| 52 | u32 next_handle = 1; | 52 | u32 next_handle = 0; |
| 53 | 53 | ||
| 54 | /// Id to use for the next object that is created. | 54 | /// Id to use for the next object that is created. |
| 55 | u32 next_id = 1; | 55 | u32 next_id = 0; |
| 56 | 56 | ||
| 57 | /// Mapping of currently allocated handles to the objects they represent. | 57 | /// Mapping of currently allocated handles to the objects they represent. |
| 58 | std::unordered_map<u32, std::shared_ptr<Object>> handles; | 58 | std::unordered_map<u32, std::shared_ptr<Object>> handles; |
| @@ -119,6 +119,8 @@ private: | |||
| 119 | }; | 119 | }; |
| 120 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); | 120 | static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size"); |
| 121 | 121 | ||
| 122 | u32 CreateObject(u32 size); | ||
| 123 | |||
| 122 | u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); | 124 | u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output); |
| 123 | u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); | 125 | u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output); |
| 124 | u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); | 126 | u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output); |
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index deaf0808b..88fbfa9b0 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp | |||
| @@ -60,24 +60,24 @@ void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) { | |||
| 60 | 60 | ||
| 61 | if (ctrl.must_delay) { | 61 | if (ctrl.must_delay) { |
| 62 | ctrl.fresh_call = false; | 62 | ctrl.fresh_call = false; |
| 63 | ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout, | 63 | ctx.SleepClientThread( |
| 64 | [=](std::shared_ptr<Kernel::Thread> thread, | 64 | "NVServices::DelayedResponse", ctrl.timeout, |
| 65 | Kernel::HLERequestContext& ctx, | 65 | [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_, |
| 66 | Kernel::ThreadWakeupReason reason) { | 66 | Kernel::ThreadWakeupReason reason) { |
| 67 | IoctlCtrl ctrl2{ctrl}; | 67 | IoctlCtrl ctrl2{ctrl}; |
| 68 | std::vector<u8> tmp_output = output; | 68 | std::vector<u8> tmp_output = output; |
| 69 | std::vector<u8> tmp_output2 = output2; | 69 | std::vector<u8> tmp_output2 = output2; |
| 70 | u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, | 70 | const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output, |
| 71 | tmp_output2, ctrl2, version); | 71 | tmp_output2, ctrl2, version); |
| 72 | ctx.WriteBuffer(tmp_output, 0); | 72 | ctx_.WriteBuffer(tmp_output, 0); |
| 73 | if (version == IoctlVersion::Version3) { | 73 | if (version == IoctlVersion::Version3) { |
| 74 | ctx.WriteBuffer(tmp_output2, 1); | 74 | ctx_.WriteBuffer(tmp_output2, 1); |
| 75 | } | 75 | } |
| 76 | IPC::ResponseBuilder rb{ctx, 3}; | 76 | IPC::ResponseBuilder rb{ctx_, 3}; |
| 77 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 78 | rb.Push(result); | 78 | rb.Push(ioctl_result); |
| 79 | }, | 79 | }, |
| 80 | nvdrv->GetEventWriteable(ctrl.event_id)); | 80 | nvdrv->GetEventWriteable(ctrl.event_id)); |
| 81 | } else { | 81 | } else { |
| 82 | ctx.WriteBuffer(output); | 82 | ctx.WriteBuffer(output); |
| 83 | if (version == IoctlVersion::Version3) { | 83 | if (version == IoctlVersion::Version3) { |
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index d7a1bef91..7706a5590 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h | |||
| @@ -54,7 +54,7 @@ struct EventInterface { | |||
| 54 | } | 54 | } |
| 55 | mask = mask >> 1; | 55 | mask = mask >> 1; |
| 56 | } | 56 | } |
| 57 | return {}; | 57 | return std::nullopt; |
| 58 | } | 58 | } |
| 59 | void SetEventStatus(const u32 event_id, EventState new_status) { | 59 | void SetEventStatus(const u32 event_id, EventState new_status) { |
| 60 | EventState old_status = status[event_id]; | 60 | EventState old_status = status[event_id]; |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index caca80dde..637b310d7 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp | |||
| @@ -24,13 +24,13 @@ BufferQueue::~BufferQueue() = default; | |||
| 24 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { | 24 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { |
| 25 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); | 25 | LOG_WARNING(Service, "Adding graphics buffer {}", slot); |
| 26 | 26 | ||
| 27 | Buffer buffer{}; | ||
| 28 | buffer.slot = slot; | ||
| 29 | buffer.igbp_buffer = igbp_buffer; | ||
| 30 | buffer.status = Buffer::Status::Free; | ||
| 31 | free_buffers.push_back(slot); | 27 | free_buffers.push_back(slot); |
| 28 | queue.push_back({ | ||
| 29 | .slot = slot, | ||
| 30 | .status = Buffer::Status::Free, | ||
| 31 | .igbp_buffer = igbp_buffer, | ||
| 32 | }); | ||
| 32 | 33 | ||
| 33 | queue.emplace_back(buffer); | ||
| 34 | buffer_wait_event.writable->Signal(); | 34 | buffer_wait_event.writable->Signal(); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| @@ -38,7 +38,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue | |||
| 38 | u32 height) { | 38 | u32 height) { |
| 39 | 39 | ||
| 40 | if (free_buffers.empty()) { | 40 | if (free_buffers.empty()) { |
| 41 | return {}; | 41 | return std::nullopt; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | auto f_itr = free_buffers.begin(); | 44 | auto f_itr = free_buffers.begin(); |
| @@ -69,7 +69,7 @@ std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::Dequeue | |||
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | if (itr == queue.end()) { | 71 | if (itr == queue.end()) { |
| 72 | return {}; | 72 | return std::nullopt; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | itr->status = Buffer::Status::Dequeued; | 75 | itr->status = Buffer::Status::Dequeued; |
| @@ -103,14 +103,15 @@ std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::Ac | |||
| 103 | auto itr = queue.end(); | 103 | auto itr = queue.end(); |
| 104 | // Iterate to find a queued buffer matching the requested slot. | 104 | // Iterate to find a queued buffer matching the requested slot. |
| 105 | while (itr == queue.end() && !queue_sequence.empty()) { | 105 | while (itr == queue.end() && !queue_sequence.empty()) { |
| 106 | u32 slot = queue_sequence.front(); | 106 | const u32 slot = queue_sequence.front(); |
| 107 | itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { | 107 | itr = std::find_if(queue.begin(), queue.end(), [&slot](const Buffer& buffer) { |
| 108 | return buffer.status == Buffer::Status::Queued && buffer.slot == slot; | 108 | return buffer.status == Buffer::Status::Queued && buffer.slot == slot; |
| 109 | }); | 109 | }); |
| 110 | queue_sequence.pop_front(); | 110 | queue_sequence.pop_front(); |
| 111 | } | 111 | } |
| 112 | if (itr == queue.end()) | 112 | if (itr == queue.end()) { |
| 113 | return {}; | 113 | return std::nullopt; |
| 114 | } | ||
| 114 | itr->status = Buffer::Status::Acquired; | 115 | itr->status = Buffer::Status::Acquired; |
| 115 | return *itr; | 116 | return *itr; |
| 116 | } | 117 | } |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 2f44d3779..c64673dba 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -28,8 +28,7 @@ | |||
| 28 | 28 | ||
| 29 | namespace Service::NVFlinger { | 29 | namespace Service::NVFlinger { |
| 30 | 30 | ||
| 31 | constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60); | 31 | constexpr auto frame_ns = std::chrono::nanoseconds{1000000000 / 60}; |
| 32 | constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30); | ||
| 33 | 32 | ||
| 34 | void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { | 33 | void NVFlinger::VSyncThread(NVFlinger& nv_flinger) { |
| 35 | nv_flinger.SplitVSync(); | 34 | nv_flinger.SplitVSync(); |
| @@ -67,20 +66,24 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) { | |||
| 67 | guard = std::make_shared<std::mutex>(); | 66 | guard = std::make_shared<std::mutex>(); |
| 68 | 67 | ||
| 69 | // Schedule the screen composition events | 68 | // Schedule the screen composition events |
| 70 | composition_event = | 69 | composition_event = Core::Timing::CreateEvent( |
| 71 | Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) { | 70 | "ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) { |
| 72 | Lock(); | 71 | const auto guard = Lock(); |
| 73 | Compose(); | 72 | Compose(); |
| 74 | const auto ticks = GetNextTicks(); | 73 | |
| 75 | this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late), | 74 | const auto ticks = std::chrono::nanoseconds{GetNextTicks()}; |
| 76 | composition_event); | 75 | const auto ticks_delta = ticks - ns_late; |
| 76 | const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta); | ||
| 77 | |||
| 78 | this->system.CoreTiming().ScheduleEvent(future_ns, composition_event); | ||
| 77 | }); | 79 | }); |
| 80 | |||
| 78 | if (system.IsMulticore()) { | 81 | if (system.IsMulticore()) { |
| 79 | is_running = true; | 82 | is_running = true; |
| 80 | wait_event = std::make_unique<Common::Event>(); | 83 | wait_event = std::make_unique<Common::Event>(); |
| 81 | vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); | 84 | vsync_thread = std::make_unique<std::thread>(VSyncThread, std::ref(*this)); |
| 82 | } else { | 85 | } else { |
| 83 | system.CoreTiming().ScheduleEvent(frame_ticks, composition_event); | 86 | system.CoreTiming().ScheduleEvent(frame_ns, composition_event); |
| 84 | } | 87 | } |
| 85 | } | 88 | } |
| 86 | 89 | ||
| @@ -111,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) { | |||
| 111 | [&](const VI::Display& display) { return display.GetName() == name; }); | 114 | [&](const VI::Display& display) { return display.GetName() == name; }); |
| 112 | 115 | ||
| 113 | if (itr == displays.end()) { | 116 | if (itr == displays.end()) { |
| 114 | return {}; | 117 | return std::nullopt; |
| 115 | } | 118 | } |
| 116 | 119 | ||
| 117 | return itr->GetID(); | 120 | return itr->GetID(); |
| @@ -121,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | |||
| 121 | auto* const display = FindDisplay(display_id); | 124 | auto* const display = FindDisplay(display_id); |
| 122 | 125 | ||
| 123 | if (display == nullptr) { | 126 | if (display == nullptr) { |
| 124 | return {}; | 127 | return std::nullopt; |
| 125 | } | 128 | } |
| 126 | 129 | ||
| 127 | const u64 layer_id = next_layer_id++; | 130 | const u64 layer_id = next_layer_id++; |
| @@ -141,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co | |||
| 141 | const auto* const layer = FindLayer(display_id, layer_id); | 144 | const auto* const layer = FindLayer(display_id, layer_id); |
| 142 | 145 | ||
| 143 | if (layer == nullptr) { | 146 | if (layer == nullptr) { |
| 144 | return {}; | 147 | return std::nullopt; |
| 145 | } | 148 | } |
| 146 | 149 | ||
| 147 | return layer->GetBufferQueue().GetId(); | 150 | return layer->GetBufferQueue().GetId(); |
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index e4959a9af..1ebe949c0 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -54,12 +54,12 @@ public: | |||
| 54 | /// Opens the specified display and returns the ID. | 54 | /// Opens the specified display and returns the ID. |
| 55 | /// | 55 | /// |
| 56 | /// If an invalid display name is provided, then an empty optional is returned. | 56 | /// If an invalid display name is provided, then an empty optional is returned. |
| 57 | std::optional<u64> OpenDisplay(std::string_view name); | 57 | [[nodiscard]] std::optional<u64> OpenDisplay(std::string_view name); |
| 58 | 58 | ||
| 59 | /// Creates a layer on the specified display and returns the layer ID. | 59 | /// Creates a layer on the specified display and returns the layer ID. |
| 60 | /// | 60 | /// |
| 61 | /// If an invalid display ID is specified, then an empty optional is returned. | 61 | /// If an invalid display ID is specified, then an empty optional is returned. |
| 62 | std::optional<u64> CreateLayer(u64 display_id); | 62 | [[nodiscard]] std::optional<u64> CreateLayer(u64 display_id); |
| 63 | 63 | ||
| 64 | /// Closes a layer on all displays for the given layer ID. | 64 | /// Closes a layer on all displays for the given layer ID. |
| 65 | void CloseLayer(u64 layer_id); | 65 | void CloseLayer(u64 layer_id); |
| @@ -67,41 +67,41 @@ public: | |||
| 67 | /// Finds the buffer queue ID of the specified layer in the specified display. | 67 | /// Finds the buffer queue ID of the specified layer in the specified display. |
| 68 | /// | 68 | /// |
| 69 | /// If an invalid display ID or layer ID is provided, then an empty optional is returned. | 69 | /// If an invalid display ID or layer ID is provided, then an empty optional is returned. |
| 70 | std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; | 70 | [[nodiscard]] std::optional<u32> FindBufferQueueId(u64 display_id, u64 layer_id) const; |
| 71 | 71 | ||
| 72 | /// Gets the vsync event for the specified display. | 72 | /// Gets the vsync event for the specified display. |
| 73 | /// | 73 | /// |
| 74 | /// If an invalid display ID is provided, then nullptr is returned. | 74 | /// If an invalid display ID is provided, then nullptr is returned. |
| 75 | std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; | 75 | [[nodiscard]] std::shared_ptr<Kernel::ReadableEvent> FindVsyncEvent(u64 display_id) const; |
| 76 | 76 | ||
| 77 | /// Obtains a buffer queue identified by the ID. | 77 | /// Obtains a buffer queue identified by the ID. |
| 78 | BufferQueue& FindBufferQueue(u32 id); | 78 | [[nodiscard]] BufferQueue& FindBufferQueue(u32 id); |
| 79 | 79 | ||
| 80 | /// Obtains a buffer queue identified by the ID. | 80 | /// Obtains a buffer queue identified by the ID. |
| 81 | const BufferQueue& FindBufferQueue(u32 id) const; | 81 | [[nodiscard]] const BufferQueue& FindBufferQueue(u32 id) const; |
| 82 | 82 | ||
| 83 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when | 83 | /// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when |
| 84 | /// finished. | 84 | /// finished. |
| 85 | void Compose(); | 85 | void Compose(); |
| 86 | 86 | ||
| 87 | s64 GetNextTicks() const; | 87 | [[nodiscard]] s64 GetNextTicks() const; |
| 88 | 88 | ||
| 89 | std::unique_lock<std::mutex> Lock() { | 89 | [[nodiscard]] std::unique_lock<std::mutex> Lock() const { |
| 90 | return std::unique_lock{*guard}; | 90 | return std::unique_lock{*guard}; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | private: | 93 | private: |
| 94 | /// Finds the display identified by the specified ID. | 94 | /// Finds the display identified by the specified ID. |
| 95 | VI::Display* FindDisplay(u64 display_id); | 95 | [[nodiscard]] VI::Display* FindDisplay(u64 display_id); |
| 96 | 96 | ||
| 97 | /// Finds the display identified by the specified ID. | 97 | /// Finds the display identified by the specified ID. |
| 98 | const VI::Display* FindDisplay(u64 display_id) const; | 98 | [[nodiscard]] const VI::Display* FindDisplay(u64 display_id) const; |
| 99 | 99 | ||
| 100 | /// Finds the layer identified by the specified ID in the desired display. | 100 | /// Finds the layer identified by the specified ID in the desired display. |
| 101 | VI::Layer* FindLayer(u64 display_id, u64 layer_id); | 101 | [[nodiscard]] VI::Layer* FindLayer(u64 display_id, u64 layer_id); |
| 102 | 102 | ||
| 103 | /// Finds the layer identified by the specified ID in the desired display. | 103 | /// Finds the layer identified by the specified ID in the desired display. |
| 104 | const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; | 104 | [[nodiscard]] const VI::Layer* FindLayer(u64 display_id, u64 layer_id) const; |
| 105 | 105 | ||
| 106 | static void VSyncThread(NVFlinger& nv_flinger); | 106 | static void VSyncThread(NVFlinger& nv_flinger); |
| 107 | 107 | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index fa5347af9..ba9159ee0 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -89,8 +89,6 @@ namespace Service { | |||
| 89 | return function_string; | 89 | return function_string; |
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 93 | |||
| 94 | ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, | 92 | ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions, |
| 95 | InvokerFn* handler_invoker) | 93 | InvokerFn* handler_invoker) |
| 96 | : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} | 94 | : service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {} |
| @@ -105,10 +103,9 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) | |||
| 105 | port_installed = true; | 103 | port_installed = true; |
| 106 | } | 104 | } |
| 107 | 105 | ||
| 108 | void ServiceFrameworkBase::InstallAsNamedPort() { | 106 | void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) { |
| 109 | ASSERT(!port_installed); | 107 | ASSERT(!port_installed); |
| 110 | 108 | ||
| 111 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 112 | auto [server_port, client_port] = | 109 | auto [server_port, client_port] = |
| 113 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); | 110 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); |
| 114 | server_port->SetHleHandler(shared_from_this()); | 111 | server_port->SetHleHandler(shared_from_this()); |
| @@ -116,10 +113,9 @@ void ServiceFrameworkBase::InstallAsNamedPort() { | |||
| 116 | port_installed = true; | 113 | port_installed = true; |
| 117 | } | 114 | } |
| 118 | 115 | ||
| 119 | std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { | 116 | std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) { |
| 120 | ASSERT(!port_installed); | 117 | ASSERT(!port_installed); |
| 121 | 118 | ||
| 122 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 123 | auto [server_port, client_port] = | 119 | auto [server_port, client_port] = |
| 124 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); | 120 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); |
| 125 | auto port = MakeResult(std::move(server_port)).Unwrap(); | 121 | auto port = MakeResult(std::move(server_port)).Unwrap(); |
| @@ -191,9 +187,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co | |||
| 191 | return RESULT_SUCCESS; | 187 | return RESULT_SUCCESS; |
| 192 | } | 188 | } |
| 193 | 189 | ||
| 194 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 195 | // Module interface | ||
| 196 | |||
| 197 | /// Initialize ServiceManager | 190 | /// Initialize ServiceManager |
| 198 | void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | 191 | void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { |
| 199 | // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it | 192 | // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it |
| @@ -246,7 +239,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | |||
| 246 | PSC::InstallInterfaces(*sm); | 239 | PSC::InstallInterfaces(*sm); |
| 247 | PSM::InstallInterfaces(*sm); | 240 | PSM::InstallInterfaces(*sm); |
| 248 | Set::InstallInterfaces(*sm); | 241 | Set::InstallInterfaces(*sm); |
| 249 | Sockets::InstallInterfaces(*sm); | 242 | Sockets::InstallInterfaces(*sm, system); |
| 250 | SPL::InstallInterfaces(*sm); | 243 | SPL::InstallInterfaces(*sm); |
| 251 | SSL::InstallInterfaces(*sm); | 244 | SSL::InstallInterfaces(*sm); |
| 252 | Time::InstallInterfaces(system); | 245 | Time::InstallInterfaces(system); |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 022d885b6..a01ef3353 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -63,9 +63,9 @@ public: | |||
| 63 | /// Creates a port pair and registers this service with the given ServiceManager. | 63 | /// Creates a port pair and registers this service with the given ServiceManager. |
| 64 | void InstallAsService(SM::ServiceManager& service_manager); | 64 | void InstallAsService(SM::ServiceManager& service_manager); |
| 65 | /// Creates a port pair and registers it on the kernel's global port registry. | 65 | /// Creates a port pair and registers it on the kernel's global port registry. |
| 66 | void InstallAsNamedPort(); | 66 | void InstallAsNamedPort(Kernel::KernelCore& kernel); |
| 67 | /// Creates and returns an unregistered port for the service. | 67 | /// Creates and returns an unregistered port for the service. |
| 68 | std::shared_ptr<Kernel::ClientPort> CreatePort(); | 68 | std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel); |
| 69 | 69 | ||
| 70 | void InvokeRequest(Kernel::HLERequestContext& ctx); | 70 | void InvokeRequest(Kernel::HLERequestContext& ctx); |
| 71 | 71 | ||
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 34fe2fd82..e64777668 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -106,7 +106,7 @@ void GetKeyCodeMapImpl(Kernel::HLERequestContext& ctx) { | |||
| 106 | 106 | ||
| 107 | IPC::ResponseBuilder rb{ctx, 2}; | 107 | IPC::ResponseBuilder rb{ctx, 2}; |
| 108 | rb.Push(RESULT_SUCCESS); | 108 | rb.Push(RESULT_SUCCESS); |
| 109 | ctx.WriteBuffer(&layout, sizeof(KeyboardLayout)); | 109 | ctx.WriteBuffer(layout); |
| 110 | } | 110 | } |
| 111 | } // Anonymous namespace | 111 | } // Anonymous namespace |
| 112 | 112 | ||
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index d872de16c..9c1da361b 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -19,7 +19,7 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); | |||
| 19 | constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); | 19 | constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); |
| 20 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); | 20 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); |
| 21 | 21 | ||
| 22 | ServiceManager::ServiceManager() = default; | 22 | ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {} |
| 23 | ServiceManager::~ServiceManager() = default; | 23 | ServiceManager::~ServiceManager() = default; |
| 24 | 24 | ||
| 25 | void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | 25 | void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { |
| @@ -27,11 +27,11 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | |||
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | static ResultCode ValidateServiceName(const std::string& name) { | 29 | static ResultCode ValidateServiceName(const std::string& name) { |
| 30 | if (name.size() <= 0 || name.size() > 8) { | 30 | if (name.empty() || name.size() > 8) { |
| 31 | LOG_ERROR(Service_SM, "Invalid service name! service={}", name); | 31 | LOG_ERROR(Service_SM, "Invalid service name! service={}", name); |
| 32 | return ERR_INVALID_NAME; | 32 | return ERR_INVALID_NAME; |
| 33 | } | 33 | } |
| 34 | if (name.find('\0') != std::string::npos) { | 34 | if (name.rfind('\0') != std::string::npos) { |
| 35 | LOG_ERROR(Service_SM, "A non null terminated service was passed"); | 35 | LOG_ERROR(Service_SM, "A non null terminated service was passed"); |
| 36 | return ERR_INVALID_NAME; | 36 | return ERR_INVALID_NAME; |
| 37 | } | 37 | } |
| @@ -43,13 +43,13 @@ void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self, | |||
| 43 | ASSERT(self->sm_interface.expired()); | 43 | ASSERT(self->sm_interface.expired()); |
| 44 | 44 | ||
| 45 | auto sm = std::make_shared<SM>(self, kernel); | 45 | auto sm = std::make_shared<SM>(self, kernel); |
| 46 | sm->InstallAsNamedPort(); | 46 | sm->InstallAsNamedPort(kernel); |
| 47 | self->sm_interface = sm; | 47 | self->sm_interface = sm; |
| 48 | self->controller_interface = std::make_unique<Controller>(); | 48 | self->controller_interface = std::make_unique<Controller>(); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( | 51 | ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(std::string name, |
| 52 | std::string name, unsigned int max_sessions) { | 52 | u32 max_sessions) { |
| 53 | 53 | ||
| 54 | CASCADE_CODE(ValidateServiceName(name)); | 54 | CASCADE_CODE(ValidateServiceName(name)); |
| 55 | 55 | ||
| @@ -58,7 +58,6 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService( | |||
| 58 | return ERR_ALREADY_REGISTERED; | 58 | return ERR_ALREADY_REGISTERED; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 62 | auto [server_port, client_port] = | 61 | auto [server_port, client_port] = |
| 63 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); | 62 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); |
| 64 | 63 | ||
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index b06d2f103..6790c86f0 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <type_traits> | 9 | #include <type_traits> |
| 10 | #include <unordered_map> | 10 | #include <unordered_map> |
| 11 | 11 | ||
| 12 | #include "common/concepts.h" | ||
| 12 | #include "core/hle/kernel/client_port.h" | 13 | #include "core/hle/kernel/client_port.h" |
| 13 | #include "core/hle/kernel/object.h" | 14 | #include "core/hle/kernel/object.h" |
| 14 | #include "core/hle/kernel/server_port.h" | 15 | #include "core/hle/kernel/server_port.h" |
| @@ -47,19 +48,17 @@ class ServiceManager { | |||
| 47 | public: | 48 | public: |
| 48 | static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel); | 49 | static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel); |
| 49 | 50 | ||
| 50 | ServiceManager(); | 51 | explicit ServiceManager(Kernel::KernelCore& kernel_); |
| 51 | ~ServiceManager(); | 52 | ~ServiceManager(); |
| 52 | 53 | ||
| 53 | ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name, | 54 | ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name, |
| 54 | unsigned int max_sessions); | 55 | u32 max_sessions); |
| 55 | ResultCode UnregisterService(const std::string& name); | 56 | ResultCode UnregisterService(const std::string& name); |
| 56 | ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); | 57 | ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name); |
| 57 | ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); | 58 | ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name); |
| 58 | 59 | ||
| 59 | template <typename T> | 60 | template <Common::DerivedFrom<Kernel::SessionRequestHandler> T> |
| 60 | std::shared_ptr<T> GetService(const std::string& service_name) const { | 61 | std::shared_ptr<T> GetService(const std::string& service_name) const { |
| 61 | static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>, | ||
| 62 | "Not a base of ServiceFrameworkBase"); | ||
| 63 | auto service = registered_services.find(service_name); | 62 | auto service = registered_services.find(service_name); |
| 64 | if (service == registered_services.end()) { | 63 | if (service == registered_services.end()) { |
| 65 | LOG_DEBUG(Service, "Can't find service: {}", service_name); | 64 | LOG_DEBUG(Service, "Can't find service: {}", service_name); |
| @@ -80,6 +79,9 @@ private: | |||
| 80 | 79 | ||
| 81 | /// Map of registered services, retrieved using GetServicePort or ConnectToService. | 80 | /// Map of registered services, retrieved using GetServicePort or ConnectToService. |
| 82 | std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services; | 81 | std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services; |
| 82 | |||
| 83 | /// Kernel context | ||
| 84 | Kernel::KernelCore& kernel; | ||
| 83 | }; | 85 | }; |
| 84 | 86 | ||
| 85 | } // namespace Service::SM | 87 | } // namespace Service::SM |
diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h new file mode 100644 index 000000000..2d53e52b6 --- /dev/null +++ b/src/core/hle/service/sockets/blocking_worker.h | |||
| @@ -0,0 +1,161 @@ | |||
| 1 | // Copyright 2020 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 <atomic> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <string_view> | ||
| 11 | #include <thread> | ||
| 12 | #include <variant> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include <fmt/format.h> | ||
| 16 | |||
| 17 | #include "common/assert.h" | ||
| 18 | #include "common/microprofile.h" | ||
| 19 | #include "common/thread.h" | ||
| 20 | #include "core/core.h" | ||
| 21 | #include "core/hle/kernel/hle_ipc.h" | ||
| 22 | #include "core/hle/kernel/kernel.h" | ||
| 23 | #include "core/hle/kernel/thread.h" | ||
| 24 | #include "core/hle/kernel/writable_event.h" | ||
| 25 | |||
| 26 | namespace Service::Sockets { | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Worker abstraction to execute blocking calls on host without blocking the guest thread | ||
| 30 | * | ||
| 31 | * @tparam Service Service where the work is executed | ||
| 32 | * @tparam Types Types of work to execute | ||
| 33 | */ | ||
| 34 | template <class Service, class... Types> | ||
| 35 | class BlockingWorker { | ||
| 36 | using This = BlockingWorker<Service, Types...>; | ||
| 37 | using WorkVariant = std::variant<std::monostate, Types...>; | ||
| 38 | |||
| 39 | public: | ||
| 40 | /// Create a new worker | ||
| 41 | static std::unique_ptr<This> Create(Core::System& system, Service* service, | ||
| 42 | std::string_view name) { | ||
| 43 | return std::unique_ptr<This>(new This(system, service, name)); | ||
| 44 | } | ||
| 45 | |||
| 46 | ~BlockingWorker() { | ||
| 47 | while (!is_available.load(std::memory_order_relaxed)) { | ||
| 48 | // Busy wait until work is finished | ||
| 49 | std::this_thread::yield(); | ||
| 50 | } | ||
| 51 | // Monostate means to exit the thread | ||
| 52 | work = std::monostate{}; | ||
| 53 | work_event.Set(); | ||
| 54 | thread.join(); | ||
| 55 | } | ||
| 56 | |||
| 57 | /** | ||
| 58 | * Try to capture the worker to send work after a success | ||
| 59 | * @returns True when the worker has been successfully captured | ||
| 60 | */ | ||
| 61 | bool TryCapture() { | ||
| 62 | bool expected = true; | ||
| 63 | return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed, | ||
| 64 | std::memory_order_relaxed); | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Send work to this worker abstraction | ||
| 69 | * @see TryCapture must be called before attempting to call this function | ||
| 70 | */ | ||
| 71 | template <class Work> | ||
| 72 | void SendWork(Work new_work) { | ||
| 73 | ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured"); | ||
| 74 | work = std::move(new_work); | ||
| 75 | work_event.Set(); | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Generate a callback for @see SleepClientThread | ||
| 79 | template <class Work> | ||
| 80 | auto Callback() { | ||
| 81 | return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx, | ||
| 82 | Kernel::ThreadWakeupReason reason) { | ||
| 83 | ASSERT(reason == Kernel::ThreadWakeupReason::Signal); | ||
| 84 | std::get<Work>(work).Response(ctx); | ||
| 85 | is_available.store(true); | ||
| 86 | }; | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Get kernel event that will be signalled by the worker when the host operation finishes | ||
| 90 | std::shared_ptr<Kernel::WritableEvent> KernelEvent() const { | ||
| 91 | return kernel_event; | ||
| 92 | } | ||
| 93 | |||
| 94 | private: | ||
| 95 | explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) { | ||
| 96 | auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name)); | ||
| 97 | kernel_event = std::move(pair.writable); | ||
| 98 | thread = std::thread([this, &system, service, name] { Run(system, service, name); }); | ||
| 99 | } | ||
| 100 | |||
| 101 | void Run(Core::System& system, Service* service, std::string_view name) { | ||
| 102 | system.RegisterHostThread(); | ||
| 103 | |||
| 104 | const std::string thread_name = fmt::format("yuzu:{}", name); | ||
| 105 | MicroProfileOnThreadCreate(thread_name.c_str()); | ||
| 106 | Common::SetCurrentThreadName(thread_name.c_str()); | ||
| 107 | |||
| 108 | bool keep_running = true; | ||
| 109 | while (keep_running) { | ||
| 110 | work_event.Wait(); | ||
| 111 | |||
| 112 | const auto visit_fn = [service, &keep_running]<typename T>(T&& w) { | ||
| 113 | if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) { | ||
| 114 | keep_running = false; | ||
| 115 | } else { | ||
| 116 | w.Execute(service); | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | std::visit(visit_fn, work); | ||
| 120 | |||
| 121 | kernel_event->Signal(); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | std::thread thread; | ||
| 126 | WorkVariant work; | ||
| 127 | Common::Event work_event; | ||
| 128 | std::shared_ptr<Kernel::WritableEvent> kernel_event; | ||
| 129 | std::atomic_bool is_available{true}; | ||
| 130 | }; | ||
| 131 | |||
| 132 | template <class Service, class... Types> | ||
| 133 | class BlockingWorkerPool { | ||
| 134 | using Worker = BlockingWorker<Service, Types...>; | ||
| 135 | |||
| 136 | public: | ||
| 137 | explicit BlockingWorkerPool(Core::System& system_, Service* service_) | ||
| 138 | : system{system_}, service{service_} {} | ||
| 139 | |||
| 140 | /// Returns a captured worker thread, creating new ones if necessary | ||
| 141 | Worker* CaptureWorker() { | ||
| 142 | for (auto& worker : workers) { | ||
| 143 | if (worker->TryCapture()) { | ||
| 144 | return worker.get(); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size())); | ||
| 148 | [[maybe_unused]] const bool success = new_worker->TryCapture(); | ||
| 149 | ASSERT(success); | ||
| 150 | |||
| 151 | return workers.emplace_back(std::move(new_worker)).get(); | ||
| 152 | } | ||
| 153 | |||
| 154 | private: | ||
| 155 | Core::System& system; | ||
| 156 | Service* const service; | ||
| 157 | |||
| 158 | std::vector<std::unique_ptr<Worker>> workers; | ||
| 159 | }; | ||
| 160 | |||
| 161 | } // namespace Service::Sockets | ||
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 8d4952c0e..a74be9370 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp | |||
| @@ -2,18 +2,138 @@ | |||
| 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 <array> | ||
| 6 | #include <memory> | ||
| 7 | #include <string> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include <fmt/format.h> | ||
| 12 | |||
| 13 | #include "common/microprofile.h" | ||
| 14 | #include "common/thread.h" | ||
| 5 | #include "core/hle/ipc_helpers.h" | 15 | #include "core/hle/ipc_helpers.h" |
| 16 | #include "core/hle/kernel/thread.h" | ||
| 6 | #include "core/hle/service/sockets/bsd.h" | 17 | #include "core/hle/service/sockets/bsd.h" |
| 18 | #include "core/hle/service/sockets/sockets_translate.h" | ||
| 19 | #include "core/network/network.h" | ||
| 20 | #include "core/network/sockets.h" | ||
| 7 | 21 | ||
| 8 | namespace Service::Sockets { | 22 | namespace Service::Sockets { |
| 9 | 23 | ||
| 24 | namespace { | ||
| 25 | |||
| 26 | bool IsConnectionBased(Type type) { | ||
| 27 | switch (type) { | ||
| 28 | case Type::STREAM: | ||
| 29 | return true; | ||
| 30 | case Type::DGRAM: | ||
| 31 | return false; | ||
| 32 | default: | ||
| 33 | UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type)); | ||
| 34 | return false; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | } // Anonymous namespace | ||
| 39 | |||
| 40 | void BSD::PollWork::Execute(BSD* bsd) { | ||
| 41 | std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout); | ||
| 42 | } | ||
| 43 | |||
| 44 | void BSD::PollWork::Response(Kernel::HLERequestContext& ctx) { | ||
| 45 | ctx.WriteBuffer(write_buffer); | ||
| 46 | |||
| 47 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 48 | rb.Push(RESULT_SUCCESS); | ||
| 49 | rb.Push<s32>(ret); | ||
| 50 | rb.PushEnum(bsd_errno); | ||
| 51 | } | ||
| 52 | |||
| 53 | void BSD::AcceptWork::Execute(BSD* bsd) { | ||
| 54 | std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer); | ||
| 55 | } | ||
| 56 | |||
| 57 | void BSD::AcceptWork::Response(Kernel::HLERequestContext& ctx) { | ||
| 58 | ctx.WriteBuffer(write_buffer); | ||
| 59 | |||
| 60 | IPC::ResponseBuilder rb{ctx, 5}; | ||
| 61 | rb.Push(RESULT_SUCCESS); | ||
| 62 | rb.Push<s32>(ret); | ||
| 63 | rb.PushEnum(bsd_errno); | ||
| 64 | rb.Push<u32>(static_cast<u32>(write_buffer.size())); | ||
| 65 | } | ||
| 66 | |||
| 67 | void BSD::ConnectWork::Execute(BSD* bsd) { | ||
| 68 | bsd_errno = bsd->ConnectImpl(fd, addr); | ||
| 69 | } | ||
| 70 | |||
| 71 | void BSD::ConnectWork::Response(Kernel::HLERequestContext& ctx) { | ||
| 72 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 73 | rb.Push(RESULT_SUCCESS); | ||
| 74 | rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1); | ||
| 75 | rb.PushEnum(bsd_errno); | ||
| 76 | } | ||
| 77 | |||
| 78 | void BSD::RecvWork::Execute(BSD* bsd) { | ||
| 79 | std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message); | ||
| 80 | } | ||
| 81 | |||
| 82 | void BSD::RecvWork::Response(Kernel::HLERequestContext& ctx) { | ||
| 83 | ctx.WriteBuffer(message); | ||
| 84 | |||
| 85 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 86 | rb.Push(RESULT_SUCCESS); | ||
| 87 | rb.Push<s32>(ret); | ||
| 88 | rb.PushEnum(bsd_errno); | ||
| 89 | } | ||
| 90 | |||
| 91 | void BSD::RecvFromWork::Execute(BSD* bsd) { | ||
| 92 | std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr); | ||
| 93 | } | ||
| 94 | |||
| 95 | void BSD::RecvFromWork::Response(Kernel::HLERequestContext& ctx) { | ||
| 96 | ctx.WriteBuffer(message, 0); | ||
| 97 | if (!addr.empty()) { | ||
| 98 | ctx.WriteBuffer(addr, 1); | ||
| 99 | } | ||
| 100 | |||
| 101 | IPC::ResponseBuilder rb{ctx, 5}; | ||
| 102 | rb.Push(RESULT_SUCCESS); | ||
| 103 | rb.Push<s32>(ret); | ||
| 104 | rb.PushEnum(bsd_errno); | ||
| 105 | rb.Push<u32>(static_cast<u32>(addr.size())); | ||
| 106 | } | ||
| 107 | |||
| 108 | void BSD::SendWork::Execute(BSD* bsd) { | ||
| 109 | std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message); | ||
| 110 | } | ||
| 111 | |||
| 112 | void BSD::SendWork::Response(Kernel::HLERequestContext& ctx) { | ||
| 113 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 114 | rb.Push(RESULT_SUCCESS); | ||
| 115 | rb.Push<s32>(ret); | ||
| 116 | rb.PushEnum(bsd_errno); | ||
| 117 | } | ||
| 118 | |||
| 119 | void BSD::SendToWork::Execute(BSD* bsd) { | ||
| 120 | std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr); | ||
| 121 | } | ||
| 122 | |||
| 123 | void BSD::SendToWork::Response(Kernel::HLERequestContext& ctx) { | ||
| 124 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 125 | rb.Push(RESULT_SUCCESS); | ||
| 126 | rb.Push<s32>(ret); | ||
| 127 | rb.PushEnum(bsd_errno); | ||
| 128 | } | ||
| 129 | |||
| 10 | void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { | 130 | void BSD::RegisterClient(Kernel::HLERequestContext& ctx) { |
| 11 | LOG_WARNING(Service, "(STUBBED) called"); | 131 | LOG_WARNING(Service, "(STUBBED) called"); |
| 12 | 132 | ||
| 13 | IPC::ResponseBuilder rb{ctx, 3}; | 133 | IPC::ResponseBuilder rb{ctx, 3}; |
| 14 | 134 | ||
| 15 | rb.Push(RESULT_SUCCESS); | 135 | rb.Push(RESULT_SUCCESS); |
| 16 | rb.Push<u32>(0); // bsd errno | 136 | rb.Push<s32>(0); // bsd errno |
| 17 | } | 137 | } |
| 18 | 138 | ||
| 19 | void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { | 139 | void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { |
| @@ -26,20 +146,19 @@ void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) { | |||
| 26 | 146 | ||
| 27 | void BSD::Socket(Kernel::HLERequestContext& ctx) { | 147 | void BSD::Socket(Kernel::HLERequestContext& ctx) { |
| 28 | IPC::RequestParser rp{ctx}; | 148 | IPC::RequestParser rp{ctx}; |
| 149 | const u32 domain = rp.Pop<u32>(); | ||
| 150 | const u32 type = rp.Pop<u32>(); | ||
| 151 | const u32 protocol = rp.Pop<u32>(); | ||
| 29 | 152 | ||
| 30 | u32 domain = rp.Pop<u32>(); | 153 | LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol); |
| 31 | u32 type = rp.Pop<u32>(); | ||
| 32 | u32 protocol = rp.Pop<u32>(); | ||
| 33 | |||
| 34 | LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol); | ||
| 35 | 154 | ||
| 36 | u32 fd = next_fd++; | 155 | const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type), |
| 156 | static_cast<Protocol>(protocol)); | ||
| 37 | 157 | ||
| 38 | IPC::ResponseBuilder rb{ctx, 4}; | 158 | IPC::ResponseBuilder rb{ctx, 4}; |
| 39 | |||
| 40 | rb.Push(RESULT_SUCCESS); | 159 | rb.Push(RESULT_SUCCESS); |
| 41 | rb.Push<u32>(fd); | 160 | rb.Push<s32>(fd); |
| 42 | rb.Push<u32>(0); // bsd errno | 161 | rb.PushEnum(bsd_errno); |
| 43 | } | 162 | } |
| 44 | 163 | ||
| 45 | void BSD::Select(Kernel::HLERequestContext& ctx) { | 164 | void BSD::Select(Kernel::HLERequestContext& ctx) { |
| @@ -52,67 +171,664 @@ void BSD::Select(Kernel::HLERequestContext& ctx) { | |||
| 52 | rb.Push<u32>(0); // bsd errno | 171 | rb.Push<u32>(0); // bsd errno |
| 53 | } | 172 | } |
| 54 | 173 | ||
| 174 | void BSD::Poll(Kernel::HLERequestContext& ctx) { | ||
| 175 | IPC::RequestParser rp{ctx}; | ||
| 176 | const s32 nfds = rp.Pop<s32>(); | ||
| 177 | const s32 timeout = rp.Pop<s32>(); | ||
| 178 | |||
| 179 | LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout); | ||
| 180 | |||
| 181 | ExecuteWork(ctx, "BSD:Poll", timeout != 0, | ||
| 182 | PollWork{ | ||
| 183 | .nfds = nfds, | ||
| 184 | .timeout = timeout, | ||
| 185 | .read_buffer = ctx.ReadBuffer(), | ||
| 186 | .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), | ||
| 187 | }); | ||
| 188 | } | ||
| 189 | |||
| 190 | void BSD::Accept(Kernel::HLERequestContext& ctx) { | ||
| 191 | IPC::RequestParser rp{ctx}; | ||
| 192 | const s32 fd = rp.Pop<s32>(); | ||
| 193 | |||
| 194 | LOG_DEBUG(Service, "called. fd={}", fd); | ||
| 195 | |||
| 196 | ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd), | ||
| 197 | AcceptWork{ | ||
| 198 | .fd = fd, | ||
| 199 | .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()), | ||
| 200 | }); | ||
| 201 | } | ||
| 202 | |||
| 55 | void BSD::Bind(Kernel::HLERequestContext& ctx) { | 203 | void BSD::Bind(Kernel::HLERequestContext& ctx) { |
| 56 | LOG_WARNING(Service, "(STUBBED) called"); | 204 | IPC::RequestParser rp{ctx}; |
| 205 | const s32 fd = rp.Pop<s32>(); | ||
| 57 | 206 | ||
| 58 | IPC::ResponseBuilder rb{ctx, 4}; | 207 | LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); |
| 59 | 208 | ||
| 60 | rb.Push(RESULT_SUCCESS); | 209 | BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer())); |
| 61 | rb.Push<u32>(0); // ret | ||
| 62 | rb.Push<u32>(0); // bsd errno | ||
| 63 | } | 210 | } |
| 64 | 211 | ||
| 65 | void BSD::Connect(Kernel::HLERequestContext& ctx) { | 212 | void BSD::Connect(Kernel::HLERequestContext& ctx) { |
| 66 | LOG_WARNING(Service, "(STUBBED) called"); | 213 | IPC::RequestParser rp{ctx}; |
| 214 | const s32 fd = rp.Pop<s32>(); | ||
| 67 | 215 | ||
| 68 | IPC::ResponseBuilder rb{ctx, 4}; | 216 | LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize()); |
| 217 | |||
| 218 | ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd), | ||
| 219 | ConnectWork{ | ||
| 220 | .fd = fd, | ||
| 221 | .addr = ctx.ReadBuffer(), | ||
| 222 | }); | ||
| 223 | } | ||
| 224 | |||
| 225 | void BSD::GetPeerName(Kernel::HLERequestContext& ctx) { | ||
| 226 | IPC::RequestParser rp{ctx}; | ||
| 227 | const s32 fd = rp.Pop<s32>(); | ||
| 228 | |||
| 229 | LOG_DEBUG(Service, "called. fd={}", fd); | ||
| 69 | 230 | ||
| 231 | std::vector<u8> write_buffer(ctx.GetWriteBufferSize()); | ||
| 232 | const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer); | ||
| 233 | |||
| 234 | ctx.WriteBuffer(write_buffer); | ||
| 235 | |||
| 236 | IPC::ResponseBuilder rb{ctx, 5}; | ||
| 70 | rb.Push(RESULT_SUCCESS); | 237 | rb.Push(RESULT_SUCCESS); |
| 71 | rb.Push<u32>(0); // ret | 238 | rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0); |
| 72 | rb.Push<u32>(0); // bsd errno | 239 | rb.PushEnum(bsd_errno); |
| 240 | rb.Push<u32>(static_cast<u32>(write_buffer.size())); | ||
| 241 | } | ||
| 242 | |||
| 243 | void BSD::GetSockName(Kernel::HLERequestContext& ctx) { | ||
| 244 | IPC::RequestParser rp{ctx}; | ||
| 245 | const s32 fd = rp.Pop<s32>(); | ||
| 246 | |||
| 247 | LOG_DEBUG(Service, "called. fd={}", fd); | ||
| 248 | |||
| 249 | std::vector<u8> write_buffer(ctx.GetWriteBufferSize()); | ||
| 250 | const Errno bsd_errno = GetSockNameImpl(fd, write_buffer); | ||
| 251 | |||
| 252 | ctx.WriteBuffer(write_buffer); | ||
| 253 | |||
| 254 | IPC::ResponseBuilder rb{ctx, 5}; | ||
| 255 | rb.Push(RESULT_SUCCESS); | ||
| 256 | rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0); | ||
| 257 | rb.PushEnum(bsd_errno); | ||
| 258 | rb.Push<u32>(static_cast<u32>(write_buffer.size())); | ||
| 73 | } | 259 | } |
| 74 | 260 | ||
| 75 | void BSD::Listen(Kernel::HLERequestContext& ctx) { | 261 | void BSD::Listen(Kernel::HLERequestContext& ctx) { |
| 76 | LOG_WARNING(Service, "(STUBBED) called"); | 262 | IPC::RequestParser rp{ctx}; |
| 263 | const s32 fd = rp.Pop<s32>(); | ||
| 264 | const s32 backlog = rp.Pop<s32>(); | ||
| 77 | 265 | ||
| 78 | IPC::ResponseBuilder rb{ctx, 4}; | 266 | LOG_DEBUG(Service, "called. fd={} backlog={}", fd, backlog); |
| 267 | |||
| 268 | BuildErrnoResponse(ctx, ListenImpl(fd, backlog)); | ||
| 269 | } | ||
| 270 | |||
| 271 | void BSD::Fcntl(Kernel::HLERequestContext& ctx) { | ||
| 272 | IPC::RequestParser rp{ctx}; | ||
| 273 | const s32 fd = rp.Pop<s32>(); | ||
| 274 | const s32 cmd = rp.Pop<s32>(); | ||
| 275 | const s32 arg = rp.Pop<s32>(); | ||
| 79 | 276 | ||
| 277 | LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg); | ||
| 278 | |||
| 279 | const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg); | ||
| 280 | |||
| 281 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 80 | rb.Push(RESULT_SUCCESS); | 282 | rb.Push(RESULT_SUCCESS); |
| 81 | rb.Push<u32>(0); // ret | 283 | rb.Push<s32>(ret); |
| 82 | rb.Push<u32>(0); // bsd errno | 284 | rb.PushEnum(bsd_errno); |
| 83 | } | 285 | } |
| 84 | 286 | ||
| 85 | void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) { | 287 | void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) { |
| 86 | LOG_WARNING(Service, "(STUBBED) called"); | 288 | IPC::RequestParser rp{ctx}; |
| 87 | 289 | ||
| 88 | IPC::ResponseBuilder rb{ctx, 4}; | 290 | const s32 fd = rp.Pop<s32>(); |
| 291 | const u32 level = rp.Pop<u32>(); | ||
| 292 | const OptName optname = static_cast<OptName>(rp.Pop<u32>()); | ||
| 89 | 293 | ||
| 90 | rb.Push(RESULT_SUCCESS); | 294 | const std::vector<u8> buffer = ctx.ReadBuffer(); |
| 91 | rb.Push<u32>(0); // ret | 295 | const u8* optval = buffer.empty() ? nullptr : buffer.data(); |
| 92 | rb.Push<u32>(0); // bsd errno | 296 | size_t optlen = buffer.size(); |
| 297 | |||
| 298 | std::array<u64, 2> values; | ||
| 299 | if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) { | ||
| 300 | std::memcpy(values.data(), buffer.data(), sizeof(values)); | ||
| 301 | optlen = sizeof(values); | ||
| 302 | optval = reinterpret_cast<const u8*>(values.data()); | ||
| 303 | } | ||
| 304 | |||
| 305 | LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level, | ||
| 306 | static_cast<u32>(optname), optlen); | ||
| 307 | |||
| 308 | BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval)); | ||
| 309 | } | ||
| 310 | |||
| 311 | void BSD::Shutdown(Kernel::HLERequestContext& ctx) { | ||
| 312 | IPC::RequestParser rp{ctx}; | ||
| 313 | |||
| 314 | const s32 fd = rp.Pop<s32>(); | ||
| 315 | const s32 how = rp.Pop<s32>(); | ||
| 316 | |||
| 317 | LOG_DEBUG(Service, "called. fd={} how={}", fd, how); | ||
| 318 | |||
| 319 | BuildErrnoResponse(ctx, ShutdownImpl(fd, how)); | ||
| 320 | } | ||
| 321 | |||
| 322 | void BSD::Recv(Kernel::HLERequestContext& ctx) { | ||
| 323 | IPC::RequestParser rp{ctx}; | ||
| 324 | |||
| 325 | const s32 fd = rp.Pop<s32>(); | ||
| 326 | const u32 flags = rp.Pop<u32>(); | ||
| 327 | |||
| 328 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize()); | ||
| 329 | |||
| 330 | ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd), | ||
| 331 | RecvWork{ | ||
| 332 | .fd = fd, | ||
| 333 | .flags = flags, | ||
| 334 | .message = std::vector<u8>(ctx.GetWriteBufferSize()), | ||
| 335 | }); | ||
| 336 | } | ||
| 337 | |||
| 338 | void BSD::RecvFrom(Kernel::HLERequestContext& ctx) { | ||
| 339 | IPC::RequestParser rp{ctx}; | ||
| 340 | |||
| 341 | const s32 fd = rp.Pop<s32>(); | ||
| 342 | const u32 flags = rp.Pop<u32>(); | ||
| 343 | |||
| 344 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags, | ||
| 345 | ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1)); | ||
| 346 | |||
| 347 | ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd), | ||
| 348 | RecvFromWork{ | ||
| 349 | .fd = fd, | ||
| 350 | .flags = flags, | ||
| 351 | .message = std::vector<u8>(ctx.GetWriteBufferSize(0)), | ||
| 352 | .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)), | ||
| 353 | }); | ||
| 354 | } | ||
| 355 | |||
| 356 | void BSD::Send(Kernel::HLERequestContext& ctx) { | ||
| 357 | IPC::RequestParser rp{ctx}; | ||
| 358 | |||
| 359 | const s32 fd = rp.Pop<s32>(); | ||
| 360 | const u32 flags = rp.Pop<u32>(); | ||
| 361 | |||
| 362 | LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize()); | ||
| 363 | |||
| 364 | ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd), | ||
| 365 | SendWork{ | ||
| 366 | .fd = fd, | ||
| 367 | .flags = flags, | ||
| 368 | .message = ctx.ReadBuffer(), | ||
| 369 | }); | ||
| 93 | } | 370 | } |
| 94 | 371 | ||
| 95 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { | 372 | void BSD::SendTo(Kernel::HLERequestContext& ctx) { |
| 96 | LOG_WARNING(Service, "(STUBBED) called"); | 373 | IPC::RequestParser rp{ctx}; |
| 374 | const s32 fd = rp.Pop<s32>(); | ||
| 375 | const u32 flags = rp.Pop<u32>(); | ||
| 376 | |||
| 377 | LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags, | ||
| 378 | ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1)); | ||
| 379 | |||
| 380 | ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd), | ||
| 381 | SendToWork{ | ||
| 382 | .fd = fd, | ||
| 383 | .flags = flags, | ||
| 384 | .message = ctx.ReadBuffer(0), | ||
| 385 | .addr = ctx.ReadBuffer(1), | ||
| 386 | }); | ||
| 387 | } | ||
| 97 | 388 | ||
| 98 | IPC::ResponseBuilder rb{ctx, 4}; | 389 | void BSD::Write(Kernel::HLERequestContext& ctx) { |
| 390 | IPC::RequestParser rp{ctx}; | ||
| 391 | const s32 fd = rp.Pop<s32>(); | ||
| 99 | 392 | ||
| 100 | rb.Push(RESULT_SUCCESS); | 393 | LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize()); |
| 101 | rb.Push<u32>(0); // ret | 394 | |
| 102 | rb.Push<u32>(0); // bsd errno | 395 | ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd), |
| 396 | SendWork{ | ||
| 397 | .fd = fd, | ||
| 398 | .flags = 0, | ||
| 399 | .message = ctx.ReadBuffer(), | ||
| 400 | }); | ||
| 103 | } | 401 | } |
| 104 | 402 | ||
| 105 | void BSD::Close(Kernel::HLERequestContext& ctx) { | 403 | void BSD::Close(Kernel::HLERequestContext& ctx) { |
| 106 | LOG_WARNING(Service, "(STUBBED) called"); | 404 | IPC::RequestParser rp{ctx}; |
| 405 | const s32 fd = rp.Pop<s32>(); | ||
| 406 | |||
| 407 | LOG_DEBUG(Service, "called. fd={}", fd); | ||
| 408 | |||
| 409 | BuildErrnoResponse(ctx, CloseImpl(fd)); | ||
| 410 | } | ||
| 411 | |||
| 412 | template <typename Work> | ||
| 413 | void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, | ||
| 414 | bool is_blocking, Work work) { | ||
| 415 | if (!is_blocking) { | ||
| 416 | work.Execute(this); | ||
| 417 | work.Response(ctx); | ||
| 418 | return; | ||
| 419 | } | ||
| 420 | |||
| 421 | // Signal a dummy response to make IPC validation happy | ||
| 422 | // This will be overwritten by the SleepClientThread callback | ||
| 423 | work.Response(ctx); | ||
| 424 | |||
| 425 | auto worker = worker_pool.CaptureWorker(); | ||
| 426 | |||
| 427 | ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(), | ||
| 428 | worker->Callback<Work>(), worker->KernelEvent()); | ||
| 429 | |||
| 430 | worker->SendWork(std::move(work)); | ||
| 431 | } | ||
| 432 | |||
| 433 | std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) { | ||
| 434 | if (type == Type::SEQPACKET) { | ||
| 435 | UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management"); | ||
| 436 | } else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) { | ||
| 437 | UNIMPLEMENTED_MSG("SOCK_RAW errno management"); | ||
| 438 | } | ||
| 439 | |||
| 440 | [[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0; | ||
| 441 | UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type"); | ||
| 442 | type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000); | ||
| 443 | |||
| 444 | const s32 fd = FindFreeFileDescriptorHandle(); | ||
| 445 | if (fd < 0) { | ||
| 446 | LOG_ERROR(Service, "No more file descriptors available"); | ||
| 447 | return {-1, Errno::MFILE}; | ||
| 448 | } | ||
| 449 | |||
| 450 | FileDescriptor& descriptor = file_descriptors[fd].emplace(); | ||
| 451 | // ENONMEM might be thrown here | ||
| 452 | |||
| 453 | LOG_INFO(Service, "New socket fd={}", fd); | ||
| 454 | |||
| 455 | descriptor.socket = std::make_unique<Network::Socket>(); | ||
| 456 | descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); | ||
| 457 | descriptor.is_connection_based = IsConnectionBased(type); | ||
| 458 | |||
| 459 | return {fd, Errno::SUCCESS}; | ||
| 460 | } | ||
| 107 | 461 | ||
| 462 | std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, | ||
| 463 | s32 nfds, s32 timeout) { | ||
| 464 | if (write_buffer.size() < nfds * sizeof(PollFD)) { | ||
| 465 | return {-1, Errno::INVAL}; | ||
| 466 | } | ||
| 467 | |||
| 468 | if (nfds == 0) { | ||
| 469 | // When no entries are provided, -1 is returned with errno zero | ||
| 470 | return {-1, Errno::SUCCESS}; | ||
| 471 | } | ||
| 472 | |||
| 473 | const size_t length = std::min(read_buffer.size(), write_buffer.size()); | ||
| 474 | std::vector<PollFD> fds(nfds); | ||
| 475 | std::memcpy(fds.data(), read_buffer.data(), length); | ||
| 476 | |||
| 477 | if (timeout >= 0) { | ||
| 478 | const s64 seconds = timeout / 1000; | ||
| 479 | const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000); | ||
| 480 | |||
| 481 | if (seconds < 0) { | ||
| 482 | return {-1, Errno::INVAL}; | ||
| 483 | } | ||
| 484 | if (nanoseconds > 999'999'999) { | ||
| 485 | return {-1, Errno::INVAL}; | ||
| 486 | } | ||
| 487 | } else if (timeout != -1) { | ||
| 488 | return {-1, Errno::INVAL}; | ||
| 489 | } | ||
| 490 | |||
| 491 | for (PollFD& pollfd : fds) { | ||
| 492 | ASSERT(pollfd.revents == 0); | ||
| 493 | |||
| 494 | if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) { | ||
| 495 | LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd); | ||
| 496 | pollfd.revents = 0; | ||
| 497 | return {0, Errno::SUCCESS}; | ||
| 498 | } | ||
| 499 | |||
| 500 | const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd]; | ||
| 501 | if (!descriptor) { | ||
| 502 | LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd); | ||
| 503 | pollfd.revents = POLL_NVAL; | ||
| 504 | return {0, Errno::SUCCESS}; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | std::vector<Network::PollFD> host_pollfds(fds.size()); | ||
| 509 | std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) { | ||
| 510 | Network::PollFD result; | ||
| 511 | result.socket = file_descriptors[pollfd.fd]->socket.get(); | ||
| 512 | result.events = TranslatePollEventsToHost(pollfd.events); | ||
| 513 | result.revents = 0; | ||
| 514 | return result; | ||
| 515 | }); | ||
| 516 | |||
| 517 | const auto result = Network::Poll(host_pollfds, timeout); | ||
| 518 | |||
| 519 | const size_t num = host_pollfds.size(); | ||
| 520 | for (size_t i = 0; i < num; ++i) { | ||
| 521 | fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents); | ||
| 522 | } | ||
| 523 | std::memcpy(write_buffer.data(), fds.data(), length); | ||
| 524 | |||
| 525 | return Translate(result); | ||
| 526 | } | ||
| 527 | |||
| 528 | std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) { | ||
| 529 | if (!IsFileDescriptorValid(fd)) { | ||
| 530 | return {-1, Errno::BADF}; | ||
| 531 | } | ||
| 532 | |||
| 533 | const s32 new_fd = FindFreeFileDescriptorHandle(); | ||
| 534 | if (new_fd < 0) { | ||
| 535 | LOG_ERROR(Service, "No more file descriptors available"); | ||
| 536 | return {-1, Errno::MFILE}; | ||
| 537 | } | ||
| 538 | |||
| 539 | FileDescriptor& descriptor = *file_descriptors[fd]; | ||
| 540 | auto [result, bsd_errno] = descriptor.socket->Accept(); | ||
| 541 | if (bsd_errno != Network::Errno::SUCCESS) { | ||
| 542 | return {-1, Translate(bsd_errno)}; | ||
| 543 | } | ||
| 544 | |||
| 545 | FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace(); | ||
| 546 | new_descriptor.socket = std::move(result.socket); | ||
| 547 | new_descriptor.is_connection_based = descriptor.is_connection_based; | ||
| 548 | |||
| 549 | ASSERT(write_buffer.size() == sizeof(SockAddrIn)); | ||
| 550 | const SockAddrIn guest_addr_in = Translate(result.sockaddr_in); | ||
| 551 | std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in)); | ||
| 552 | |||
| 553 | return {new_fd, Errno::SUCCESS}; | ||
| 554 | } | ||
| 555 | |||
| 556 | Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) { | ||
| 557 | if (!IsFileDescriptorValid(fd)) { | ||
| 558 | return Errno::BADF; | ||
| 559 | } | ||
| 560 | ASSERT(addr.size() == sizeof(SockAddrIn)); | ||
| 561 | SockAddrIn addr_in; | ||
| 562 | std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); | ||
| 563 | |||
| 564 | return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in))); | ||
| 565 | } | ||
| 566 | |||
| 567 | Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) { | ||
| 568 | if (!IsFileDescriptorValid(fd)) { | ||
| 569 | return Errno::BADF; | ||
| 570 | } | ||
| 571 | |||
| 572 | UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn)); | ||
| 573 | SockAddrIn addr_in; | ||
| 574 | std::memcpy(&addr_in, addr.data(), sizeof(addr_in)); | ||
| 575 | |||
| 576 | return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in))); | ||
| 577 | } | ||
| 578 | |||
| 579 | Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) { | ||
| 580 | if (!IsFileDescriptorValid(fd)) { | ||
| 581 | return Errno::BADF; | ||
| 582 | } | ||
| 583 | |||
| 584 | const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName(); | ||
| 585 | if (bsd_errno != Network::Errno::SUCCESS) { | ||
| 586 | return Translate(bsd_errno); | ||
| 587 | } | ||
| 588 | const SockAddrIn guest_addrin = Translate(addr_in); | ||
| 589 | |||
| 590 | ASSERT(write_buffer.size() == sizeof(guest_addrin)); | ||
| 591 | std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); | ||
| 592 | return Translate(bsd_errno); | ||
| 593 | } | ||
| 594 | |||
| 595 | Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) { | ||
| 596 | if (!IsFileDescriptorValid(fd)) { | ||
| 597 | return Errno::BADF; | ||
| 598 | } | ||
| 599 | |||
| 600 | const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName(); | ||
| 601 | if (bsd_errno != Network::Errno::SUCCESS) { | ||
| 602 | return Translate(bsd_errno); | ||
| 603 | } | ||
| 604 | const SockAddrIn guest_addrin = Translate(addr_in); | ||
| 605 | |||
| 606 | ASSERT(write_buffer.size() == sizeof(guest_addrin)); | ||
| 607 | std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); | ||
| 608 | return Translate(bsd_errno); | ||
| 609 | } | ||
| 610 | |||
| 611 | Errno BSD::ListenImpl(s32 fd, s32 backlog) { | ||
| 612 | if (!IsFileDescriptorValid(fd)) { | ||
| 613 | return Errno::BADF; | ||
| 614 | } | ||
| 615 | return Translate(file_descriptors[fd]->socket->Listen(backlog)); | ||
| 616 | } | ||
| 617 | |||
| 618 | std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { | ||
| 619 | if (!IsFileDescriptorValid(fd)) { | ||
| 620 | return {-1, Errno::BADF}; | ||
| 621 | } | ||
| 622 | |||
| 623 | FileDescriptor& descriptor = *file_descriptors[fd]; | ||
| 624 | |||
| 625 | switch (cmd) { | ||
| 626 | case FcntlCmd::GETFL: | ||
| 627 | ASSERT(arg == 0); | ||
| 628 | return {descriptor.flags, Errno::SUCCESS}; | ||
| 629 | case FcntlCmd::SETFL: { | ||
| 630 | const bool enable = (arg & FLAG_O_NONBLOCK) != 0; | ||
| 631 | const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable)); | ||
| 632 | if (bsd_errno != Errno::SUCCESS) { | ||
| 633 | return {-1, bsd_errno}; | ||
| 634 | } | ||
| 635 | descriptor.flags = arg; | ||
| 636 | return {0, Errno::SUCCESS}; | ||
| 637 | } | ||
| 638 | default: | ||
| 639 | UNIMPLEMENTED_MSG("Unimplemented cmd={}", static_cast<int>(cmd)); | ||
| 640 | return {-1, Errno::SUCCESS}; | ||
| 641 | } | ||
| 642 | } | ||
| 643 | |||
| 644 | Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { | ||
| 645 | UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET | ||
| 646 | |||
| 647 | if (!IsFileDescriptorValid(fd)) { | ||
| 648 | return Errno::BADF; | ||
| 649 | } | ||
| 650 | |||
| 651 | Network::Socket* const socket = file_descriptors[fd]->socket.get(); | ||
| 652 | |||
| 653 | if (optname == OptName::LINGER) { | ||
| 654 | ASSERT(optlen == sizeof(Linger)); | ||
| 655 | Linger linger; | ||
| 656 | std::memcpy(&linger, optval, sizeof(linger)); | ||
| 657 | ASSERT(linger.onoff == 0 || linger.onoff == 1); | ||
| 658 | |||
| 659 | return Translate(socket->SetLinger(linger.onoff != 0, linger.linger)); | ||
| 660 | } | ||
| 661 | |||
| 662 | ASSERT(optlen == sizeof(u32)); | ||
| 663 | u32 value; | ||
| 664 | std::memcpy(&value, optval, sizeof(value)); | ||
| 665 | |||
| 666 | switch (optname) { | ||
| 667 | case OptName::REUSEADDR: | ||
| 668 | ASSERT(value == 0 || value == 1); | ||
| 669 | return Translate(socket->SetReuseAddr(value != 0)); | ||
| 670 | case OptName::BROADCAST: | ||
| 671 | ASSERT(value == 0 || value == 1); | ||
| 672 | return Translate(socket->SetBroadcast(value != 0)); | ||
| 673 | case OptName::SNDBUF: | ||
| 674 | return Translate(socket->SetSndBuf(value)); | ||
| 675 | case OptName::RCVBUF: | ||
| 676 | return Translate(socket->SetRcvBuf(value)); | ||
| 677 | case OptName::SNDTIMEO: | ||
| 678 | return Translate(socket->SetSndTimeo(value)); | ||
| 679 | case OptName::RCVTIMEO: | ||
| 680 | return Translate(socket->SetRcvTimeo(value)); | ||
| 681 | default: | ||
| 682 | UNIMPLEMENTED_MSG("Unimplemented optname={}", static_cast<int>(optname)); | ||
| 683 | return Errno::SUCCESS; | ||
| 684 | } | ||
| 685 | } | ||
| 686 | |||
| 687 | Errno BSD::ShutdownImpl(s32 fd, s32 how) { | ||
| 688 | if (!IsFileDescriptorValid(fd)) { | ||
| 689 | return Errno::BADF; | ||
| 690 | } | ||
| 691 | const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how)); | ||
| 692 | return Translate(file_descriptors[fd]->socket->Shutdown(host_how)); | ||
| 693 | } | ||
| 694 | |||
| 695 | std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) { | ||
| 696 | if (!IsFileDescriptorValid(fd)) { | ||
| 697 | return {-1, Errno::BADF}; | ||
| 698 | } | ||
| 699 | return Translate(file_descriptors[fd]->socket->Recv(flags, message)); | ||
| 700 | } | ||
| 701 | |||
| 702 | std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, | ||
| 703 | std::vector<u8>& addr) { | ||
| 704 | if (!IsFileDescriptorValid(fd)) { | ||
| 705 | return {-1, Errno::BADF}; | ||
| 706 | } | ||
| 707 | |||
| 708 | FileDescriptor& descriptor = *file_descriptors[fd]; | ||
| 709 | |||
| 710 | Network::SockAddrIn addr_in{}; | ||
| 711 | Network::SockAddrIn* p_addr_in = nullptr; | ||
| 712 | if (descriptor.is_connection_based) { | ||
| 713 | // Connection based file descriptors (e.g. TCP) zero addr | ||
| 714 | addr.clear(); | ||
| 715 | } else { | ||
| 716 | p_addr_in = &addr_in; | ||
| 717 | } | ||
| 718 | |||
| 719 | // Apply flags | ||
| 720 | if ((flags & FLAG_MSG_DONTWAIT) != 0) { | ||
| 721 | flags &= ~FLAG_MSG_DONTWAIT; | ||
| 722 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { | ||
| 723 | descriptor.socket->SetNonBlock(true); | ||
| 724 | } | ||
| 725 | } | ||
| 726 | |||
| 727 | const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in)); | ||
| 728 | |||
| 729 | // Restore original state | ||
| 730 | if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) { | ||
| 731 | descriptor.socket->SetNonBlock(false); | ||
| 732 | } | ||
| 733 | |||
| 734 | if (p_addr_in) { | ||
| 735 | if (ret < 0) { | ||
| 736 | addr.clear(); | ||
| 737 | } else { | ||
| 738 | ASSERT(addr.size() == sizeof(SockAddrIn)); | ||
| 739 | const SockAddrIn result = Translate(addr_in); | ||
| 740 | std::memcpy(addr.data(), &result, sizeof(result)); | ||
| 741 | } | ||
| 742 | } | ||
| 743 | |||
| 744 | return {ret, bsd_errno}; | ||
| 745 | } | ||
| 746 | |||
| 747 | std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) { | ||
| 748 | if (!IsFileDescriptorValid(fd)) { | ||
| 749 | return {-1, Errno::BADF}; | ||
| 750 | } | ||
| 751 | return Translate(file_descriptors[fd]->socket->Send(message, flags)); | ||
| 752 | } | ||
| 753 | |||
| 754 | std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, | ||
| 755 | const std::vector<u8>& addr) { | ||
| 756 | if (!IsFileDescriptorValid(fd)) { | ||
| 757 | return {-1, Errno::BADF}; | ||
| 758 | } | ||
| 759 | |||
| 760 | Network::SockAddrIn addr_in; | ||
| 761 | Network::SockAddrIn* p_addr_in = nullptr; | ||
| 762 | if (!addr.empty()) { | ||
| 763 | ASSERT(addr.size() == sizeof(SockAddrIn)); | ||
| 764 | SockAddrIn guest_addr_in; | ||
| 765 | std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in)); | ||
| 766 | addr_in = Translate(guest_addr_in); | ||
| 767 | p_addr_in = &addr_in; | ||
| 768 | } | ||
| 769 | |||
| 770 | return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in)); | ||
| 771 | } | ||
| 772 | |||
| 773 | Errno BSD::CloseImpl(s32 fd) { | ||
| 774 | if (!IsFileDescriptorValid(fd)) { | ||
| 775 | return Errno::BADF; | ||
| 776 | } | ||
| 777 | |||
| 778 | const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close()); | ||
| 779 | if (bsd_errno != Errno::SUCCESS) { | ||
| 780 | return bsd_errno; | ||
| 781 | } | ||
| 782 | |||
| 783 | LOG_INFO(Service, "Close socket fd={}", fd); | ||
| 784 | |||
| 785 | file_descriptors[fd].reset(); | ||
| 786 | return bsd_errno; | ||
| 787 | } | ||
| 788 | |||
| 789 | s32 BSD::FindFreeFileDescriptorHandle() noexcept { | ||
| 790 | for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) { | ||
| 791 | if (!file_descriptors[fd]) { | ||
| 792 | return fd; | ||
| 793 | } | ||
| 794 | } | ||
| 795 | return -1; | ||
| 796 | } | ||
| 797 | |||
| 798 | bool BSD::IsFileDescriptorValid(s32 fd) const noexcept { | ||
| 799 | if (fd > static_cast<s32>(MAX_FD) || fd < 0) { | ||
| 800 | LOG_ERROR(Service, "Invalid file descriptor handle={}", fd); | ||
| 801 | return false; | ||
| 802 | } | ||
| 803 | if (!file_descriptors[fd]) { | ||
| 804 | LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd); | ||
| 805 | return false; | ||
| 806 | } | ||
| 807 | return true; | ||
| 808 | } | ||
| 809 | |||
| 810 | bool BSD::IsBlockingSocket(s32 fd) const noexcept { | ||
| 811 | // Inform invalid sockets as non-blocking | ||
| 812 | // This way we avoid using a worker thread as it will fail without blocking host | ||
| 813 | if (fd > static_cast<s32>(MAX_FD) || fd < 0) { | ||
| 814 | return false; | ||
| 815 | } | ||
| 816 | if (!file_descriptors[fd]) { | ||
| 817 | return false; | ||
| 818 | } | ||
| 819 | return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0; | ||
| 820 | } | ||
| 821 | |||
| 822 | void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept { | ||
| 108 | IPC::ResponseBuilder rb{ctx, 4}; | 823 | IPC::ResponseBuilder rb{ctx, 4}; |
| 109 | 824 | ||
| 110 | rb.Push(RESULT_SUCCESS); | 825 | rb.Push(RESULT_SUCCESS); |
| 111 | rb.Push<u32>(0); // ret | 826 | rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1); |
| 112 | rb.Push<u32>(0); // bsd errno | 827 | rb.PushEnum(bsd_errno); |
| 113 | } | 828 | } |
| 114 | 829 | ||
| 115 | BSD::BSD(const char* name) : ServiceFramework(name) { | 830 | BSD::BSD(Core::System& system, const char* name) |
| 831 | : ServiceFramework(name), worker_pool{system, this} { | ||
| 116 | // clang-format off | 832 | // clang-format off |
| 117 | static const FunctionInfo functions[] = { | 833 | static const FunctionInfo functions[] = { |
| 118 | {0, &BSD::RegisterClient, "RegisterClient"}, | 834 | {0, &BSD::RegisterClient, "RegisterClient"}, |
| @@ -121,25 +837,25 @@ BSD::BSD(const char* name) : ServiceFramework(name) { | |||
| 121 | {3, nullptr, "SocketExempt"}, | 837 | {3, nullptr, "SocketExempt"}, |
| 122 | {4, nullptr, "Open"}, | 838 | {4, nullptr, "Open"}, |
| 123 | {5, &BSD::Select, "Select"}, | 839 | {5, &BSD::Select, "Select"}, |
| 124 | {6, nullptr, "Poll"}, | 840 | {6, &BSD::Poll, "Poll"}, |
| 125 | {7, nullptr, "Sysctl"}, | 841 | {7, nullptr, "Sysctl"}, |
| 126 | {8, nullptr, "Recv"}, | 842 | {8, &BSD::Recv, "Recv"}, |
| 127 | {9, nullptr, "RecvFrom"}, | 843 | {9, &BSD::RecvFrom, "RecvFrom"}, |
| 128 | {10, nullptr, "Send"}, | 844 | {10, &BSD::Send, "Send"}, |
| 129 | {11, &BSD::SendTo, "SendTo"}, | 845 | {11, &BSD::SendTo, "SendTo"}, |
| 130 | {12, nullptr, "Accept"}, | 846 | {12, &BSD::Accept, "Accept"}, |
| 131 | {13, &BSD::Bind, "Bind"}, | 847 | {13, &BSD::Bind, "Bind"}, |
| 132 | {14, &BSD::Connect, "Connect"}, | 848 | {14, &BSD::Connect, "Connect"}, |
| 133 | {15, nullptr, "GetPeerName"}, | 849 | {15, &BSD::GetPeerName, "GetPeerName"}, |
| 134 | {16, nullptr, "GetSockName"}, | 850 | {16, &BSD::GetSockName, "GetSockName"}, |
| 135 | {17, nullptr, "GetSockOpt"}, | 851 | {17, nullptr, "GetSockOpt"}, |
| 136 | {18, &BSD::Listen, "Listen"}, | 852 | {18, &BSD::Listen, "Listen"}, |
| 137 | {19, nullptr, "Ioctl"}, | 853 | {19, nullptr, "Ioctl"}, |
| 138 | {20, nullptr, "Fcntl"}, | 854 | {20, &BSD::Fcntl, "Fcntl"}, |
| 139 | {21, &BSD::SetSockOpt, "SetSockOpt"}, | 855 | {21, &BSD::SetSockOpt, "SetSockOpt"}, |
| 140 | {22, nullptr, "Shutdown"}, | 856 | {22, &BSD::Shutdown, "Shutdown"}, |
| 141 | {23, nullptr, "ShutdownAllSockets"}, | 857 | {23, nullptr, "ShutdownAllSockets"}, |
| 142 | {24, nullptr, "Write"}, | 858 | {24, &BSD::Write, "Write"}, |
| 143 | {25, nullptr, "Read"}, | 859 | {25, nullptr, "Read"}, |
| 144 | {26, &BSD::Close, "Close"}, | 860 | {26, &BSD::Close, "Close"}, |
| 145 | {27, nullptr, "DuplicateSocket"}, | 861 | {27, nullptr, "DuplicateSocket"}, |
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 3098e3baf..357531951 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h | |||
| @@ -4,30 +4,174 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 8 | #include <string_view> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "common/common_types.h" | ||
| 7 | #include "core/hle/kernel/hle_ipc.h" | 12 | #include "core/hle/kernel/hle_ipc.h" |
| 8 | #include "core/hle/service/service.h" | 13 | #include "core/hle/service/service.h" |
| 14 | #include "core/hle/service/sockets/blocking_worker.h" | ||
| 15 | #include "core/hle/service/sockets/sockets.h" | ||
| 16 | |||
| 17 | namespace Core { | ||
| 18 | class System; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Network { | ||
| 22 | class Socket; | ||
| 23 | } | ||
| 9 | 24 | ||
| 10 | namespace Service::Sockets { | 25 | namespace Service::Sockets { |
| 11 | 26 | ||
| 12 | class BSD final : public ServiceFramework<BSD> { | 27 | class BSD final : public ServiceFramework<BSD> { |
| 13 | public: | 28 | public: |
| 14 | explicit BSD(const char* name); | 29 | explicit BSD(Core::System& system, const char* name); |
| 15 | ~BSD() override; | 30 | ~BSD() override; |
| 16 | 31 | ||
| 17 | private: | 32 | private: |
| 33 | /// Maximum number of file descriptors | ||
| 34 | static constexpr size_t MAX_FD = 128; | ||
| 35 | |||
| 36 | struct FileDescriptor { | ||
| 37 | std::unique_ptr<Network::Socket> socket; | ||
| 38 | s32 flags = 0; | ||
| 39 | bool is_connection_based = false; | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct PollWork { | ||
| 43 | void Execute(BSD* bsd); | ||
| 44 | void Response(Kernel::HLERequestContext& ctx); | ||
| 45 | |||
| 46 | s32 nfds; | ||
| 47 | s32 timeout; | ||
| 48 | std::vector<u8> read_buffer; | ||
| 49 | std::vector<u8> write_buffer; | ||
| 50 | s32 ret{}; | ||
| 51 | Errno bsd_errno{}; | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct AcceptWork { | ||
| 55 | void Execute(BSD* bsd); | ||
| 56 | void Response(Kernel::HLERequestContext& ctx); | ||
| 57 | |||
| 58 | s32 fd; | ||
| 59 | std::vector<u8> write_buffer; | ||
| 60 | s32 ret{}; | ||
| 61 | Errno bsd_errno{}; | ||
| 62 | }; | ||
| 63 | |||
| 64 | struct ConnectWork { | ||
| 65 | void Execute(BSD* bsd); | ||
| 66 | void Response(Kernel::HLERequestContext& ctx); | ||
| 67 | |||
| 68 | s32 fd; | ||
| 69 | std::vector<u8> addr; | ||
| 70 | Errno bsd_errno{}; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct RecvWork { | ||
| 74 | void Execute(BSD* bsd); | ||
| 75 | void Response(Kernel::HLERequestContext& ctx); | ||
| 76 | |||
| 77 | s32 fd; | ||
| 78 | u32 flags; | ||
| 79 | std::vector<u8> message; | ||
| 80 | s32 ret{}; | ||
| 81 | Errno bsd_errno{}; | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct RecvFromWork { | ||
| 85 | void Execute(BSD* bsd); | ||
| 86 | void Response(Kernel::HLERequestContext& ctx); | ||
| 87 | |||
| 88 | s32 fd; | ||
| 89 | u32 flags; | ||
| 90 | std::vector<u8> message; | ||
| 91 | std::vector<u8> addr; | ||
| 92 | s32 ret{}; | ||
| 93 | Errno bsd_errno{}; | ||
| 94 | }; | ||
| 95 | |||
| 96 | struct SendWork { | ||
| 97 | void Execute(BSD* bsd); | ||
| 98 | void Response(Kernel::HLERequestContext& ctx); | ||
| 99 | |||
| 100 | s32 fd; | ||
| 101 | u32 flags; | ||
| 102 | std::vector<u8> message; | ||
| 103 | s32 ret{}; | ||
| 104 | Errno bsd_errno{}; | ||
| 105 | }; | ||
| 106 | |||
| 107 | struct SendToWork { | ||
| 108 | void Execute(BSD* bsd); | ||
| 109 | void Response(Kernel::HLERequestContext& ctx); | ||
| 110 | |||
| 111 | s32 fd; | ||
| 112 | u32 flags; | ||
| 113 | std::vector<u8> message; | ||
| 114 | std::vector<u8> addr; | ||
| 115 | s32 ret{}; | ||
| 116 | Errno bsd_errno{}; | ||
| 117 | }; | ||
| 118 | |||
| 18 | void RegisterClient(Kernel::HLERequestContext& ctx); | 119 | void RegisterClient(Kernel::HLERequestContext& ctx); |
| 19 | void StartMonitoring(Kernel::HLERequestContext& ctx); | 120 | void StartMonitoring(Kernel::HLERequestContext& ctx); |
| 20 | void Socket(Kernel::HLERequestContext& ctx); | 121 | void Socket(Kernel::HLERequestContext& ctx); |
| 21 | void Select(Kernel::HLERequestContext& ctx); | 122 | void Select(Kernel::HLERequestContext& ctx); |
| 123 | void Poll(Kernel::HLERequestContext& ctx); | ||
| 124 | void Accept(Kernel::HLERequestContext& ctx); | ||
| 22 | void Bind(Kernel::HLERequestContext& ctx); | 125 | void Bind(Kernel::HLERequestContext& ctx); |
| 23 | void Connect(Kernel::HLERequestContext& ctx); | 126 | void Connect(Kernel::HLERequestContext& ctx); |
| 127 | void GetPeerName(Kernel::HLERequestContext& ctx); | ||
| 128 | void GetSockName(Kernel::HLERequestContext& ctx); | ||
| 24 | void Listen(Kernel::HLERequestContext& ctx); | 129 | void Listen(Kernel::HLERequestContext& ctx); |
| 130 | void Fcntl(Kernel::HLERequestContext& ctx); | ||
| 25 | void SetSockOpt(Kernel::HLERequestContext& ctx); | 131 | void SetSockOpt(Kernel::HLERequestContext& ctx); |
| 132 | void Shutdown(Kernel::HLERequestContext& ctx); | ||
| 133 | void Recv(Kernel::HLERequestContext& ctx); | ||
| 134 | void RecvFrom(Kernel::HLERequestContext& ctx); | ||
| 135 | void Send(Kernel::HLERequestContext& ctx); | ||
| 26 | void SendTo(Kernel::HLERequestContext& ctx); | 136 | void SendTo(Kernel::HLERequestContext& ctx); |
| 137 | void Write(Kernel::HLERequestContext& ctx); | ||
| 27 | void Close(Kernel::HLERequestContext& ctx); | 138 | void Close(Kernel::HLERequestContext& ctx); |
| 28 | 139 | ||
| 29 | /// Id to use for the next open file descriptor. | 140 | template <typename Work> |
| 30 | u32 next_fd = 1; | 141 | void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason, |
| 142 | bool is_blocking, Work work); | ||
| 143 | |||
| 144 | std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol); | ||
| 145 | std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer, | ||
| 146 | s32 nfds, s32 timeout); | ||
| 147 | std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer); | ||
| 148 | Errno BindImpl(s32 fd, const std::vector<u8>& addr); | ||
| 149 | Errno ConnectImpl(s32 fd, const std::vector<u8>& addr); | ||
| 150 | Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer); | ||
| 151 | Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); | ||
| 152 | Errno ListenImpl(s32 fd, s32 backlog); | ||
| 153 | std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); | ||
| 154 | Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); | ||
| 155 | Errno ShutdownImpl(s32 fd, s32 how); | ||
| 156 | std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); | ||
| 157 | std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, | ||
| 158 | std::vector<u8>& addr); | ||
| 159 | std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message); | ||
| 160 | std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message, | ||
| 161 | const std::vector<u8>& addr); | ||
| 162 | Errno CloseImpl(s32 fd); | ||
| 163 | |||
| 164 | s32 FindFreeFileDescriptorHandle() noexcept; | ||
| 165 | bool IsFileDescriptorValid(s32 fd) const noexcept; | ||
| 166 | bool IsBlockingSocket(s32 fd) const noexcept; | ||
| 167 | |||
| 168 | void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept; | ||
| 169 | |||
| 170 | std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors; | ||
| 171 | |||
| 172 | BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork, | ||
| 173 | SendToWork> | ||
| 174 | worker_pool; | ||
| 31 | }; | 175 | }; |
| 32 | 176 | ||
| 33 | class BSDCFG final : public ServiceFramework<BSDCFG> { | 177 | class BSDCFG final : public ServiceFramework<BSDCFG> { |
diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp index 08d2d306a..1d27f7906 100644 --- a/src/core/hle/service/sockets/sockets.cpp +++ b/src/core/hle/service/sockets/sockets.cpp | |||
| @@ -10,9 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | namespace Service::Sockets { | 11 | namespace Service::Sockets { |
| 12 | 12 | ||
| 13 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 13 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { |
| 14 | std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager); | 14 | std::make_shared<BSD>(system, "bsd:s")->InstallAsService(service_manager); |
| 15 | std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager); | 15 | std::make_shared<BSD>(system, "bsd:u")->InstallAsService(service_manager); |
| 16 | std::make_shared<BSDCFG>()->InstallAsService(service_manager); | 16 | std::make_shared<BSDCFG>()->InstallAsService(service_manager); |
| 17 | 17 | ||
| 18 | std::make_shared<ETHC_C>()->InstallAsService(service_manager); | 18 | std::make_shared<ETHC_C>()->InstallAsService(service_manager); |
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index ca8a6a7e0..89a410076 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h | |||
| @@ -4,11 +4,94 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | 9 | ||
| 10 | namespace Core { | ||
| 11 | class System; | ||
| 12 | } | ||
| 13 | |||
| 9 | namespace Service::Sockets { | 14 | namespace Service::Sockets { |
| 10 | 15 | ||
| 16 | enum class Errno : u32 { | ||
| 17 | SUCCESS = 0, | ||
| 18 | BADF = 9, | ||
| 19 | AGAIN = 11, | ||
| 20 | INVAL = 22, | ||
| 21 | MFILE = 24, | ||
| 22 | NOTCONN = 107, | ||
| 23 | }; | ||
| 24 | |||
| 25 | enum class Domain : u32 { | ||
| 26 | INET = 2, | ||
| 27 | }; | ||
| 28 | |||
| 29 | enum class Type : u32 { | ||
| 30 | STREAM = 1, | ||
| 31 | DGRAM = 2, | ||
| 32 | RAW = 3, | ||
| 33 | SEQPACKET = 5, | ||
| 34 | }; | ||
| 35 | |||
| 36 | enum class Protocol : u32 { | ||
| 37 | UNSPECIFIED = 0, | ||
| 38 | ICMP = 1, | ||
| 39 | TCP = 6, | ||
| 40 | UDP = 17, | ||
| 41 | }; | ||
| 42 | |||
| 43 | enum class OptName : u32 { | ||
| 44 | REUSEADDR = 0x4, | ||
| 45 | BROADCAST = 0x20, | ||
| 46 | LINGER = 0x80, | ||
| 47 | SNDBUF = 0x1001, | ||
| 48 | RCVBUF = 0x1002, | ||
| 49 | SNDTIMEO = 0x1005, | ||
| 50 | RCVTIMEO = 0x1006, | ||
| 51 | }; | ||
| 52 | |||
| 53 | enum class ShutdownHow : s32 { | ||
| 54 | RD = 0, | ||
| 55 | WR = 1, | ||
| 56 | RDWR = 2, | ||
| 57 | }; | ||
| 58 | |||
| 59 | enum class FcntlCmd : s32 { | ||
| 60 | GETFL = 3, | ||
| 61 | SETFL = 4, | ||
| 62 | }; | ||
| 63 | |||
| 64 | struct SockAddrIn { | ||
| 65 | u8 len; | ||
| 66 | u8 family; | ||
| 67 | u16 portno; | ||
| 68 | std::array<u8, 4> ip; | ||
| 69 | std::array<u8, 8> zeroes; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct PollFD { | ||
| 73 | s32 fd; | ||
| 74 | u16 events; | ||
| 75 | u16 revents; | ||
| 76 | }; | ||
| 77 | |||
| 78 | struct Linger { | ||
| 79 | u32 onoff; | ||
| 80 | u32 linger; | ||
| 81 | }; | ||
| 82 | |||
| 83 | constexpr u16 POLL_IN = 0x01; | ||
| 84 | constexpr u16 POLL_PRI = 0x02; | ||
| 85 | constexpr u16 POLL_OUT = 0x04; | ||
| 86 | constexpr u16 POLL_ERR = 0x08; | ||
| 87 | constexpr u16 POLL_HUP = 0x10; | ||
| 88 | constexpr u16 POLL_NVAL = 0x20; | ||
| 89 | |||
| 90 | constexpr u32 FLAG_MSG_DONTWAIT = 0x80; | ||
| 91 | |||
| 92 | constexpr u32 FLAG_O_NONBLOCK = 0x800; | ||
| 93 | |||
| 11 | /// Registers all Sockets services with the specified service manager. | 94 | /// Registers all Sockets services with the specified service manager. |
| 12 | void InstallInterfaces(SM::ServiceManager& service_manager); | 95 | void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system); |
| 13 | 96 | ||
| 14 | } // namespace Service::Sockets | 97 | } // namespace Service::Sockets |
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp new file mode 100644 index 000000000..139743e1d --- /dev/null +++ b/src/core/hle/service/sockets/sockets_translate.cpp | |||
| @@ -0,0 +1,165 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <utility> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/service/sockets/sockets.h" | ||
| 10 | #include "core/hle/service/sockets/sockets_translate.h" | ||
| 11 | #include "core/network/network.h" | ||
| 12 | |||
| 13 | namespace Service::Sockets { | ||
| 14 | |||
| 15 | Errno Translate(Network::Errno value) { | ||
| 16 | switch (value) { | ||
| 17 | case Network::Errno::SUCCESS: | ||
| 18 | return Errno::SUCCESS; | ||
| 19 | case Network::Errno::BADF: | ||
| 20 | return Errno::BADF; | ||
| 21 | case Network::Errno::AGAIN: | ||
| 22 | return Errno::AGAIN; | ||
| 23 | case Network::Errno::INVAL: | ||
| 24 | return Errno::INVAL; | ||
| 25 | case Network::Errno::MFILE: | ||
| 26 | return Errno::MFILE; | ||
| 27 | case Network::Errno::NOTCONN: | ||
| 28 | return Errno::NOTCONN; | ||
| 29 | default: | ||
| 30 | UNIMPLEMENTED_MSG("Unimplemented errno={}", static_cast<int>(value)); | ||
| 31 | return Errno::SUCCESS; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) { | ||
| 36 | return {value.first, Translate(value.second)}; | ||
| 37 | } | ||
| 38 | |||
| 39 | Network::Domain Translate(Domain domain) { | ||
| 40 | switch (domain) { | ||
| 41 | case Domain::INET: | ||
| 42 | return Network::Domain::INET; | ||
| 43 | default: | ||
| 44 | UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain)); | ||
| 45 | return {}; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | Domain Translate(Network::Domain domain) { | ||
| 50 | switch (domain) { | ||
| 51 | case Network::Domain::INET: | ||
| 52 | return Domain::INET; | ||
| 53 | default: | ||
| 54 | UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain)); | ||
| 55 | return {}; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | Network::Type Translate(Type type) { | ||
| 60 | switch (type) { | ||
| 61 | case Type::STREAM: | ||
| 62 | return Network::Type::STREAM; | ||
| 63 | case Type::DGRAM: | ||
| 64 | return Network::Type::DGRAM; | ||
| 65 | default: | ||
| 66 | UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type)); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | Network::Protocol Translate(Type type, Protocol protocol) { | ||
| 71 | switch (protocol) { | ||
| 72 | case Protocol::UNSPECIFIED: | ||
| 73 | LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type"); | ||
| 74 | switch (type) { | ||
| 75 | case Type::DGRAM: | ||
| 76 | return Network::Protocol::UDP; | ||
| 77 | case Type::STREAM: | ||
| 78 | return Network::Protocol::TCP; | ||
| 79 | default: | ||
| 80 | return Network::Protocol::TCP; | ||
| 81 | } | ||
| 82 | case Protocol::TCP: | ||
| 83 | return Network::Protocol::TCP; | ||
| 84 | case Protocol::UDP: | ||
| 85 | return Network::Protocol::UDP; | ||
| 86 | default: | ||
| 87 | UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol)); | ||
| 88 | return Network::Protocol::TCP; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | u16 TranslatePollEventsToHost(u16 flags) { | ||
| 93 | u16 result = 0; | ||
| 94 | const auto translate = [&result, &flags](u16 from, u16 to) { | ||
| 95 | if ((flags & from) != 0) { | ||
| 96 | flags &= ~from; | ||
| 97 | result |= to; | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | translate(POLL_IN, Network::POLL_IN); | ||
| 101 | translate(POLL_PRI, Network::POLL_PRI); | ||
| 102 | translate(POLL_OUT, Network::POLL_OUT); | ||
| 103 | translate(POLL_ERR, Network::POLL_ERR); | ||
| 104 | translate(POLL_HUP, Network::POLL_HUP); | ||
| 105 | translate(POLL_NVAL, Network::POLL_NVAL); | ||
| 106 | |||
| 107 | UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags); | ||
| 108 | return result; | ||
| 109 | } | ||
| 110 | |||
| 111 | u16 TranslatePollEventsToGuest(u16 flags) { | ||
| 112 | u16 result = 0; | ||
| 113 | const auto translate = [&result, &flags](u16 from, u16 to) { | ||
| 114 | if ((flags & from) != 0) { | ||
| 115 | flags &= ~from; | ||
| 116 | result |= to; | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | |||
| 120 | translate(Network::POLL_IN, POLL_IN); | ||
| 121 | translate(Network::POLL_PRI, POLL_PRI); | ||
| 122 | translate(Network::POLL_OUT, POLL_OUT); | ||
| 123 | translate(Network::POLL_ERR, POLL_ERR); | ||
| 124 | translate(Network::POLL_HUP, POLL_HUP); | ||
| 125 | translate(Network::POLL_NVAL, POLL_NVAL); | ||
| 126 | |||
| 127 | UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags); | ||
| 128 | return result; | ||
| 129 | } | ||
| 130 | |||
| 131 | Network::SockAddrIn Translate(SockAddrIn value) { | ||
| 132 | ASSERT(value.len == 0 || value.len == sizeof(value)); | ||
| 133 | |||
| 134 | return { | ||
| 135 | .family = Translate(static_cast<Domain>(value.family)), | ||
| 136 | .ip = value.ip, | ||
| 137 | .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8), | ||
| 138 | }; | ||
| 139 | } | ||
| 140 | |||
| 141 | SockAddrIn Translate(Network::SockAddrIn value) { | ||
| 142 | return { | ||
| 143 | .len = sizeof(SockAddrIn), | ||
| 144 | .family = static_cast<u8>(Translate(value.family)), | ||
| 145 | .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8), | ||
| 146 | .ip = value.ip, | ||
| 147 | .zeroes = {}, | ||
| 148 | }; | ||
| 149 | } | ||
| 150 | |||
| 151 | Network::ShutdownHow Translate(ShutdownHow how) { | ||
| 152 | switch (how) { | ||
| 153 | case ShutdownHow::RD: | ||
| 154 | return Network::ShutdownHow::RD; | ||
| 155 | case ShutdownHow::WR: | ||
| 156 | return Network::ShutdownHow::WR; | ||
| 157 | case ShutdownHow::RDWR: | ||
| 158 | return Network::ShutdownHow::RDWR; | ||
| 159 | default: | ||
| 160 | UNIMPLEMENTED_MSG("Unimplemented how={}", static_cast<int>(how)); | ||
| 161 | return {}; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | } // namespace Service::Sockets | ||
diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h new file mode 100644 index 000000000..8ed041e31 --- /dev/null +++ b/src/core/hle/service/sockets/sockets_translate.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2020 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 <utility> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/hle/service/sockets/sockets.h" | ||
| 11 | #include "core/network/network.h" | ||
| 12 | |||
| 13 | namespace Service::Sockets { | ||
| 14 | |||
| 15 | /// Translate abstract errno to guest errno | ||
| 16 | Errno Translate(Network::Errno value); | ||
| 17 | |||
| 18 | /// Translate abstract return value errno pair to guest return value errno pair | ||
| 19 | std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value); | ||
| 20 | |||
| 21 | /// Translate guest domain to abstract domain | ||
| 22 | Network::Domain Translate(Domain domain); | ||
| 23 | |||
| 24 | /// Translate abstract domain to guest domain | ||
| 25 | Domain Translate(Network::Domain domain); | ||
| 26 | |||
| 27 | /// Translate guest type to abstract type | ||
| 28 | Network::Type Translate(Type type); | ||
| 29 | |||
| 30 | /// Translate guest protocol to abstract protocol | ||
| 31 | Network::Protocol Translate(Type type, Protocol protocol); | ||
| 32 | |||
| 33 | /// Translate abstract poll event flags to guest poll event flags | ||
| 34 | u16 TranslatePollEventsToHost(u16 flags); | ||
| 35 | |||
| 36 | /// Translate guest poll event flags to abstract poll event flags | ||
| 37 | u16 TranslatePollEventsToGuest(u16 flags); | ||
| 38 | |||
| 39 | /// Translate guest socket address structure to abstract socket address structure | ||
| 40 | Network::SockAddrIn Translate(SockAddrIn value); | ||
| 41 | |||
| 42 | /// Translate abstract socket address structure to guest socket address structure | ||
| 43 | SockAddrIn Translate(Network::SockAddrIn value); | ||
| 44 | |||
| 45 | /// Translate guest shutdown mode to abstract shutdown mode | ||
| 46 | Network::ShutdownHow Translate(ShutdownHow how); | ||
| 47 | |||
| 48 | } // namespace Service::Sockets | ||
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 13e4b3818..ee4fa4b48 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp | |||
| @@ -290,7 +290,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) { | |||
| 290 | 290 | ||
| 291 | IPC::ResponseBuilder rb{ctx, 2}; | 291 | IPC::ResponseBuilder rb{ctx, 2}; |
| 292 | rb.Push(RESULT_SUCCESS); | 292 | rb.Push(RESULT_SUCCESS); |
| 293 | ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); | 293 | ctx.WriteBuffer(clock_snapshot); |
| 294 | } | 294 | } |
| 295 | 295 | ||
| 296 | void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) { | 296 | void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx) { |
| @@ -313,7 +313,7 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques | |||
| 313 | 313 | ||
| 314 | IPC::ResponseBuilder rb{ctx, 2}; | 314 | IPC::ResponseBuilder rb{ctx, 2}; |
| 315 | rb.Push(RESULT_SUCCESS); | 315 | rb.Push(RESULT_SUCCESS); |
| 316 | ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); | 316 | ctx.WriteBuffer(clock_snapshot); |
| 317 | } | 317 | } |
| 318 | 318 | ||
| 319 | void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( | 319 | void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser( |
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp index c070d6e97..320672add 100644 --- a/src/core/hle/service/time/time_zone_content_manager.cpp +++ b/src/core/hle/service/time/time_zone_content_manager.cpp | |||
| @@ -73,10 +73,8 @@ TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core:: | |||
| 73 | 73 | ||
| 74 | std::string location_name; | 74 | std::string location_name; |
| 75 | const auto timezone_setting = Settings::GetTimeZoneString(); | 75 | const auto timezone_setting = Settings::GetTimeZoneString(); |
| 76 | if (timezone_setting == "auto") { | 76 | if (timezone_setting == "auto" || timezone_setting == "default") { |
| 77 | location_name = Common::TimeZone::GetDefaultTimeZone(); | 77 | location_name = Common::TimeZone::GetDefaultTimeZone(); |
| 78 | } else if (timezone_setting == "default") { | ||
| 79 | location_name = location_name; | ||
| 80 | } else { | 78 | } else { |
| 81 | location_name = timezone_setting; | 79 | location_name = timezone_setting; |
| 82 | } | 80 | } |
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp index db57ae069..ff3a10b3e 100644 --- a/src/core/hle/service/time/time_zone_service.cpp +++ b/src/core/hle/service/time/time_zone_service.cpp | |||
| @@ -142,7 +142,7 @@ void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) { | |||
| 142 | IPC::ResponseBuilder rb{ctx, 3}; | 142 | IPC::ResponseBuilder rb{ctx, 3}; |
| 143 | rb.Push(RESULT_SUCCESS); | 143 | rb.Push(RESULT_SUCCESS); |
| 144 | rb.PushRaw<u32>(1); // Number of times we're returning | 144 | rb.PushRaw<u32>(1); // Number of times we're returning |
| 145 | ctx.WriteBuffer(&posix_time, sizeof(s64)); | 145 | ctx.WriteBuffer(posix_time); |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { | 148 | void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { |
| @@ -164,7 +164,7 @@ void ITimeZoneService::ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) { | |||
| 164 | IPC::ResponseBuilder rb{ctx, 3}; | 164 | IPC::ResponseBuilder rb{ctx, 3}; |
| 165 | rb.Push(RESULT_SUCCESS); | 165 | rb.Push(RESULT_SUCCESS); |
| 166 | rb.PushRaw<u32>(1); // Number of times we're returning | 166 | rb.PushRaw<u32>(1); // Number of times we're returning |
| 167 | ctx.WriteBuffer(&posix_time, sizeof(s64)); | 167 | ctx.WriteBuffer(posix_time); |
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | } // namespace Service::Time | 170 | } // namespace Service::Time |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index ea7b4ae13..480d34725 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -511,7 +511,7 @@ private: | |||
| 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, | 511 | LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id, |
| 512 | static_cast<u32>(transaction), flags); | 512 | static_cast<u32>(transaction), flags); |
| 513 | 513 | ||
| 514 | nv_flinger->Lock(); | 514 | const auto guard = nv_flinger->Lock(); |
| 515 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 515 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 516 | 516 | ||
| 517 | switch (transaction) { | 517 | switch (transaction) { |
| @@ -548,10 +548,10 @@ private: | |||
| 548 | // Wait the current thread until a buffer becomes available | 548 | // Wait the current thread until a buffer becomes available |
| 549 | ctx.SleepClientThread( | 549 | ctx.SleepClientThread( |
| 550 | "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, | 550 | "IHOSBinderDriver::DequeueBuffer", UINT64_MAX, |
| 551 | [=](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, | 551 | [=, this](std::shared_ptr<Kernel::Thread> thread, |
| 552 | Kernel::ThreadWakeupReason reason) { | 552 | Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { |
| 553 | // Repeat TransactParcel DequeueBuffer when a buffer is available | 553 | // Repeat TransactParcel DequeueBuffer when a buffer is available |
| 554 | nv_flinger->Lock(); | 554 | const auto guard = nv_flinger->Lock(); |
| 555 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); | 555 | auto& buffer_queue = nv_flinger->FindBufferQueue(id); |
| 556 | auto result = buffer_queue.DequeueBuffer(width, height); | 556 | auto result = buffer_queue.DequeueBuffer(width, height); |
| 557 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); | 557 | ASSERT_MSG(result != std::nullopt, "Could not dequeue buffer."); |
| @@ -1199,6 +1199,23 @@ private: | |||
| 1199 | } | 1199 | } |
| 1200 | } | 1200 | } |
| 1201 | 1201 | ||
| 1202 | void GetIndirectLayerImageRequiredMemoryInfo(Kernel::HLERequestContext& ctx) { | ||
| 1203 | IPC::RequestParser rp{ctx}; | ||
| 1204 | const auto width = rp.Pop<u64>(); | ||
| 1205 | const auto height = rp.Pop<u64>(); | ||
| 1206 | LOG_DEBUG(Service_VI, "called width={}, height={}", width, height); | ||
| 1207 | |||
| 1208 | constexpr std::size_t base_size = 0x20000; | ||
| 1209 | constexpr std::size_t alignment = 0x1000; | ||
| 1210 | const auto texture_size = width * height * 4; | ||
| 1211 | const auto out_size = (texture_size + base_size - 1) / base_size * base_size; | ||
| 1212 | |||
| 1213 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 1214 | rb.Push(RESULT_SUCCESS); | ||
| 1215 | rb.Push(out_size); | ||
| 1216 | rb.Push(alignment); | ||
| 1217 | } | ||
| 1218 | |||
| 1202 | static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) { | 1219 | static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) { |
| 1203 | switch (mode) { | 1220 | switch (mode) { |
| 1204 | case NintendoScaleMode::None: | 1221 | case NintendoScaleMode::None: |
| @@ -1243,7 +1260,8 @@ IApplicationDisplayService::IApplicationDisplayService( | |||
| 1243 | {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, | 1260 | {2102, &IApplicationDisplayService::ConvertScalingMode, "ConvertScalingMode"}, |
| 1244 | {2450, nullptr, "GetIndirectLayerImageMap"}, | 1261 | {2450, nullptr, "GetIndirectLayerImageMap"}, |
| 1245 | {2451, nullptr, "GetIndirectLayerImageCropMap"}, | 1262 | {2451, nullptr, "GetIndirectLayerImageCropMap"}, |
| 1246 | {2460, nullptr, "GetIndirectLayerImageRequiredMemoryInfo"}, | 1263 | {2460, &IApplicationDisplayService::GetIndirectLayerImageRequiredMemoryInfo, |
| 1264 | "GetIndirectLayerImageRequiredMemoryInfo"}, | ||
| 1247 | {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, | 1265 | {5202, &IApplicationDisplayService::GetDisplayVsyncEvent, "GetDisplayVsyncEvent"}, |
| 1248 | {5203, nullptr, "GetDisplayVsyncEventForDebug"}, | 1266 | {5203, nullptr, "GetDisplayVsyncEventForDebug"}, |
| 1249 | }; | 1267 | }; |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 134e83412..394a1bf26 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -89,7 +89,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load( | 91 | AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load( |
| 92 | Kernel::Process& process) { | 92 | Kernel::Process& process, Core::System& system) { |
| 93 | if (is_loaded) { | 93 | if (is_loaded) { |
| 94 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 94 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 95 | } | 95 | } |
| @@ -141,9 +141,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 141 | continue; | 141 | continue; |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; | 144 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 145 | const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( | 145 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( |
| 146 | process, *module_file, code_size, should_pass_arguments, false)}; | 146 | process, system, *module_file, code_size, should_pass_arguments, false); |
| 147 | if (!tentative_next_load_addr) { | 147 | if (!tentative_next_load_addr) { |
| 148 | return {ResultStatus::ErrorLoadingNSO, {}}; | 148 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 149 | } | 149 | } |
| @@ -168,9 +168,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | const VAddr load_addr{next_load_addr}; | 170 | const VAddr load_addr{next_load_addr}; |
| 171 | const bool should_pass_arguments{std::strcmp(module, "rtld") == 0}; | 171 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 172 | const auto tentative_next_load_addr{AppLoader_NSO::LoadModule( | 172 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( |
| 173 | process, *module_file, load_addr, should_pass_arguments, true, pm)}; | 173 | process, system, *module_file, load_addr, should_pass_arguments, true, pm); |
| 174 | if (!tentative_next_load_addr) { | 174 | if (!tentative_next_load_addr) { |
| 175 | return {ResultStatus::ErrorLoadingNSO, {}}; | 175 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 176 | } | 176 | } |
| @@ -192,8 +192,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 192 | // Register the RomFS if a ".romfs" file was found | 192 | // Register the RomFS if a ".romfs" file was found |
| 193 | if (romfs_iter != files.end() && *romfs_iter != nullptr) { | 193 | if (romfs_iter != files.end() && *romfs_iter != nullptr) { |
| 194 | romfs = *romfs_iter; | 194 | romfs = *romfs_iter; |
| 195 | Core::System::GetInstance().GetFileSystemController().RegisterRomFS( | 195 | system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( |
| 196 | std::make_unique<FileSys::RomFSFactory>(*this)); | 196 | *this, system.GetContentProvider(), system.GetFileSystemController())); |
| 197 | } | 197 | } |
| 198 | 198 | ||
| 199 | is_loaded = true; | 199 | is_loaded = true; |
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 1c0a354a4..35d340317 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -9,6 +9,10 @@ | |||
| 9 | #include "core/file_sys/program_metadata.h" | 9 | #include "core/file_sys/program_metadata.h" |
| 10 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 11 | 11 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace Loader { | 16 | namespace Loader { |
| 13 | 17 | ||
| 14 | /** | 18 | /** |
| @@ -37,7 +41,7 @@ public: | |||
| 37 | return IdentifyType(file); | 41 | return IdentifyType(file); |
| 38 | } | 42 | } |
| 39 | 43 | ||
| 40 | LoadResult Load(Kernel::Process& process) override; | 44 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 41 | 45 | ||
| 42 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 46 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 43 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 47 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 8f7615115..dca1fcb18 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -383,7 +383,8 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 383 | return FileType::Error; | 383 | return FileType::Error; |
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) { | 386 | AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process, |
| 387 | [[maybe_unused]] Core::System& system) { | ||
| 387 | if (is_loaded) { | 388 | if (is_loaded) { |
| 388 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 389 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 389 | } | 390 | } |
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 7ef7770a6..3527933ad 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/loader/loader.h" | 9 | #include "core/loader/loader.h" |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace Loader { | 15 | namespace Loader { |
| 12 | 16 | ||
| 13 | /// Loads an ELF/AXF file | 17 | /// Loads an ELF/AXF file |
| @@ -26,7 +30,7 @@ public: | |||
| 26 | return IdentifyType(file); | 30 | return IdentifyType(file); |
| 27 | } | 31 | } |
| 28 | 32 | ||
| 29 | LoadResult Load(Kernel::Process& process) override; | 33 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 30 | }; | 34 | }; |
| 31 | 35 | ||
| 32 | } // namespace Loader | 36 | } // namespace Loader |
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 40fa03ad1..5981bcd21 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp | |||
| @@ -43,7 +43,8 @@ FileType AppLoader_KIP::GetFileType() const { | |||
| 43 | : FileType::Error; | 43 | : FileType::Error; |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) { | 46 | AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process, |
| 47 | [[maybe_unused]] Core::System& system) { | ||
| 47 | if (is_loaded) { | 48 | if (is_loaded) { |
| 48 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 49 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 49 | } | 50 | } |
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h index 12ca40269..dee05a7b5 100644 --- a/src/core/loader/kip.h +++ b/src/core/loader/kip.h | |||
| @@ -6,6 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include "core/loader/loader.h" | 7 | #include "core/loader/loader.h" |
| 8 | 8 | ||
| 9 | namespace Core { | ||
| 10 | class System; | ||
| 11 | } | ||
| 12 | |||
| 9 | namespace FileSys { | 13 | namespace FileSys { |
| 10 | class KIP; | 14 | class KIP; |
| 11 | } | 15 | } |
| @@ -26,7 +30,7 @@ public: | |||
| 26 | 30 | ||
| 27 | FileType GetFileType() const override; | 31 | FileType GetFileType() const override; |
| 28 | 32 | ||
| 29 | LoadResult Load(Kernel::Process& process) override; | 33 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 30 | 34 | ||
| 31 | private: | 35 | private: |
| 32 | std::unique_ptr<FileSys::KIP> kip; | 36 | std::unique_ptr<FileSys::KIP> kip; |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 59ca7091a..9bc3a8840 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -3,8 +3,10 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <optional> | ||
| 6 | #include <ostream> | 7 | #include <ostream> |
| 7 | #include <string> | 8 | #include <string> |
| 9 | #include "common/concepts.h" | ||
| 8 | #include "common/file_util.h" | 10 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | 12 | #include "common/string_util.h" |
| @@ -21,27 +23,41 @@ | |||
| 21 | 23 | ||
| 22 | namespace Loader { | 24 | namespace Loader { |
| 23 | 25 | ||
| 24 | FileType IdentifyFile(FileSys::VirtualFile file) { | 26 | namespace { |
| 25 | FileType type; | ||
| 26 | |||
| 27 | #define CHECK_TYPE(loader) \ | ||
| 28 | type = AppLoader_##loader::IdentifyType(file); \ | ||
| 29 | if (FileType::Error != type) \ | ||
| 30 | return type; | ||
| 31 | 27 | ||
| 32 | CHECK_TYPE(DeconstructedRomDirectory) | 28 | template <Common::DerivedFrom<AppLoader> T> |
| 33 | CHECK_TYPE(ELF) | 29 | std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) { |
| 34 | CHECK_TYPE(NSO) | 30 | const auto file_type = T::IdentifyType(file); |
| 35 | CHECK_TYPE(NRO) | 31 | if (file_type != FileType::Error) { |
| 36 | CHECK_TYPE(NCA) | 32 | return file_type; |
| 37 | CHECK_TYPE(XCI) | 33 | } |
| 38 | CHECK_TYPE(NAX) | 34 | return std::nullopt; |
| 39 | CHECK_TYPE(NSP) | 35 | } |
| 40 | CHECK_TYPE(KIP) | ||
| 41 | 36 | ||
| 42 | #undef CHECK_TYPE | 37 | } // namespace |
| 43 | 38 | ||
| 44 | return FileType::Unknown; | 39 | FileType IdentifyFile(FileSys::VirtualFile file) { |
| 40 | if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) { | ||
| 41 | return *romdir_type; | ||
| 42 | } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) { | ||
| 43 | return *elf_type; | ||
| 44 | } else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) { | ||
| 45 | return *nso_type; | ||
| 46 | } else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) { | ||
| 47 | return *nro_type; | ||
| 48 | } else if (const auto nca_type = IdentifyFileLoader<AppLoader_NCA>(file)) { | ||
| 49 | return *nca_type; | ||
| 50 | } else if (const auto xci_type = IdentifyFileLoader<AppLoader_XCI>(file)) { | ||
| 51 | return *xci_type; | ||
| 52 | } else if (const auto nax_type = IdentifyFileLoader<AppLoader_NAX>(file)) { | ||
| 53 | return *nax_type; | ||
| 54 | } else if (const auto nsp_type = IdentifyFileLoader<AppLoader_NSP>(file)) { | ||
| 55 | return *nsp_type; | ||
| 56 | } else if (const auto kip_type = IdentifyFileLoader<AppLoader_KIP>(file)) { | ||
| 57 | return *kip_type; | ||
| 58 | } else { | ||
| 59 | return FileType::Unknown; | ||
| 60 | } | ||
| 45 | } | 61 | } |
| 46 | 62 | ||
| 47 | FileType GuessFromFilename(const std::string& name) { | 63 | FileType GuessFromFilename(const std::string& name) { |
| @@ -51,7 +67,7 @@ FileType GuessFromFilename(const std::string& name) { | |||
| 51 | return FileType::NCA; | 67 | return FileType::NCA; |
| 52 | 68 | ||
| 53 | const std::string extension = | 69 | const std::string extension = |
| 54 | Common::ToLower(std::string(FileUtil::GetExtensionFromFilename(name))); | 70 | Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name))); |
| 55 | 71 | ||
| 56 | if (extension == "elf") | 72 | if (extension == "elf") |
| 57 | return FileType::ELF; | 73 | return FileType::ELF; |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 227ecc704..ac60b097a 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -15,6 +15,10 @@ | |||
| 15 | #include "core/file_sys/control_metadata.h" | 15 | #include "core/file_sys/control_metadata.h" |
| 16 | #include "core/file_sys/vfs.h" | 16 | #include "core/file_sys/vfs.h" |
| 17 | 17 | ||
| 18 | namespace Core { | ||
| 19 | class System; | ||
| 20 | } | ||
| 21 | |||
| 18 | namespace FileSys { | 22 | namespace FileSys { |
| 19 | class NACP; | 23 | class NACP; |
| 20 | } // namespace FileSys | 24 | } // namespace FileSys |
| @@ -154,9 +158,10 @@ public: | |||
| 154 | /** | 158 | /** |
| 155 | * Load the application and return the created Process instance | 159 | * Load the application and return the created Process instance |
| 156 | * @param process The newly created process. | 160 | * @param process The newly created process. |
| 161 | * @param system The system that this process is being loaded under. | ||
| 157 | * @return The status result of the operation. | 162 | * @return The status result of the operation. |
| 158 | */ | 163 | */ |
| 159 | virtual LoadResult Load(Kernel::Process& process) = 0; | 164 | virtual LoadResult Load(Kernel::Process& process, Core::System& system) = 0; |
| 160 | 165 | ||
| 161 | /** | 166 | /** |
| 162 | * Get the code (typically .code section) of the application | 167 | * Get the code (typically .code section) of the application |
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index a152981a0..49028177b 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp | |||
| @@ -41,7 +41,8 @@ FileType AppLoader_NAX::GetFileType() const { | |||
| 41 | return IdentifyTypeImpl(*nax); | 41 | return IdentifyTypeImpl(*nax); |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) { | 44 | AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process, |
| 45 | [[maybe_unused]] Core::System& system) { | ||
| 45 | if (is_loaded) { | 46 | if (is_loaded) { |
| 46 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 47 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 47 | } | 48 | } |
| @@ -65,7 +66,7 @@ AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) { | |||
| 65 | return {nca_status, {}}; | 66 | return {nca_status, {}}; |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | const auto result = nca_loader->Load(process); | 69 | const auto result = nca_loader->Load(process, system); |
| 69 | if (result.first != ResultStatus::Success) { | 70 | if (result.first != ResultStatus::Success) { |
| 70 | return result; | 71 | return result; |
| 71 | } | 72 | } |
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index eaec9bf58..c2b7722b5 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h | |||
| @@ -8,10 +8,12 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/loader/loader.h" | 9 | #include "core/loader/loader.h" |
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace Core { |
| 12 | class System; | ||
| 13 | } | ||
| 12 | 14 | ||
| 15 | namespace FileSys { | ||
| 13 | class NAX; | 16 | class NAX; |
| 14 | |||
| 15 | } // namespace FileSys | 17 | } // namespace FileSys |
| 16 | 18 | ||
| 17 | namespace Loader { | 19 | namespace Loader { |
| @@ -33,7 +35,7 @@ public: | |||
| 33 | 35 | ||
| 34 | FileType GetFileType() const override; | 36 | FileType GetFileType() const override; |
| 35 | 37 | ||
| 36 | LoadResult Load(Kernel::Process& process) override; | 38 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 37 | 39 | ||
| 38 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 39 | u64 ReadRomFSIVFCOffset() const override; | 41 | u64 ReadRomFSIVFCOffset() const override; |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 5a0469978..fa694de37 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -31,7 +31,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 31 | return FileType::Error; | 31 | return FileType::Error; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { | 34 | AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process, Core::System& system) { |
| 35 | if (is_loaded) { | 35 | if (is_loaded) { |
| 36 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 36 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 37 | } | 37 | } |
| @@ -52,14 +52,14 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) { | |||
| 52 | 52 | ||
| 53 | directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); | 53 | directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true); |
| 54 | 54 | ||
| 55 | const auto load_result = directory_loader->Load(process); | 55 | const auto load_result = directory_loader->Load(process, system); |
| 56 | if (load_result.first != ResultStatus::Success) { | 56 | if (load_result.first != ResultStatus::Success) { |
| 57 | return load_result; | 57 | return load_result; |
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { | 60 | if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { |
| 61 | Core::System::GetInstance().GetFileSystemController().RegisterRomFS( | 61 | system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( |
| 62 | std::make_unique<FileSys::RomFSFactory>(*this)); | 62 | *this, system.GetContentProvider(), system.GetFileSystemController())); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | is_loaded = true; | 65 | is_loaded = true; |
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index e47dc0e47..711070294 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -8,6 +8,10 @@ | |||
| 8 | #include "core/file_sys/vfs.h" | 8 | #include "core/file_sys/vfs.h" |
| 9 | #include "core/loader/loader.h" | 9 | #include "core/loader/loader.h" |
| 10 | 10 | ||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 11 | namespace FileSys { | 15 | namespace FileSys { |
| 12 | class NCA; | 16 | class NCA; |
| 13 | } | 17 | } |
| @@ -33,7 +37,7 @@ public: | |||
| 33 | return IdentifyType(file); | 37 | return IdentifyType(file); |
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | LoadResult Load(Kernel::Process& process) override; | 40 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 37 | 41 | ||
| 38 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 42 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 39 | u64 ReadRomFSIVFCOffset() const override; | 43 | u64 ReadRomFSIVFCOffset() const override; |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 906544bc9..9fb5eddad 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -208,7 +208,7 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi | |||
| 208 | return LoadNroImpl(process, file.ReadAllBytes(), file.GetName()); | 208 | return LoadNroImpl(process, file.ReadAllBytes(), file.GetName()); |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { | 211 | AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::System& system) { |
| 212 | if (is_loaded) { | 212 | if (is_loaded) { |
| 213 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 213 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 214 | } | 214 | } |
| @@ -218,8 +218,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { | |||
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | if (romfs != nullptr) { | 220 | if (romfs != nullptr) { |
| 221 | Core::System::GetInstance().GetFileSystemController().RegisterRomFS( | 221 | system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>( |
| 222 | std::make_unique<FileSys::RomFSFactory>(*this)); | 222 | *this, system.GetContentProvider(), system.GetFileSystemController())); |
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | is_loaded = true; | 225 | is_loaded = true; |
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 4593d48fb..a2aab2ecc 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -10,6 +10,10 @@ | |||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/loader/loader.h" | 11 | #include "core/loader/loader.h" |
| 12 | 12 | ||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 13 | namespace FileSys { | 17 | namespace FileSys { |
| 14 | class NACP; | 18 | class NACP; |
| 15 | } | 19 | } |
| @@ -37,7 +41,7 @@ public: | |||
| 37 | return IdentifyType(file); | 41 | return IdentifyType(file); |
| 38 | } | 42 | } |
| 39 | 43 | ||
| 40 | LoadResult Load(Kernel::Process& process) override; | 44 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 41 | 45 | ||
| 42 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 46 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 43 | ResultStatus ReadProgramId(u64& out_program_id) override; | 47 | ResultStatus ReadProgramId(u64& out_program_id) override; |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 575330a86..1e70f6e11 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -71,21 +71,21 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 71 | return FileType::NSO; | 71 | return FileType::NSO; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | 74 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::System& system, |
| 75 | const FileSys::VfsFile& file, VAddr load_base, | 75 | const FileSys::VfsFile& file, VAddr load_base, |
| 76 | bool should_pass_arguments, bool load_into_process, | 76 | bool should_pass_arguments, bool load_into_process, |
| 77 | std::optional<FileSys::PatchManager> pm) { | 77 | std::optional<FileSys::PatchManager> pm) { |
| 78 | if (file.GetSize() < sizeof(NSOHeader)) { | 78 | if (file.GetSize() < sizeof(NSOHeader)) { |
| 79 | return {}; | 79 | return std::nullopt; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | NSOHeader nso_header{}; | 82 | NSOHeader nso_header{}; |
| 83 | if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { | 83 | if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) { |
| 84 | return {}; | 84 | return std::nullopt; |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { | 87 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { |
| 88 | return {}; | 88 | return std::nullopt; |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | // Build program image | 91 | // Build program image |
| @@ -148,7 +148,6 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 148 | 148 | ||
| 149 | // Apply cheats if they exist and the program has a valid title ID | 149 | // Apply cheats if they exist and the program has a valid title ID |
| 150 | if (pm) { | 150 | if (pm) { |
| 151 | auto& system = Core::System::GetInstance(); | ||
| 152 | system.SetCurrentProcessBuildID(nso_header.build_id); | 151 | system.SetCurrentProcessBuildID(nso_header.build_id); |
| 153 | const auto cheats = pm->CreateCheatList(system, nso_header.build_id); | 152 | const auto cheats = pm->CreateCheatList(system, nso_header.build_id); |
| 154 | if (!cheats.empty()) { | 153 | if (!cheats.empty()) { |
| @@ -166,7 +165,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, | |||
| 166 | return load_base + image_size; | 165 | return load_base + image_size; |
| 167 | } | 166 | } |
| 168 | 167 | ||
| 169 | AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { | 168 | AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::System& system) { |
| 170 | if (is_loaded) { | 169 | if (is_loaded) { |
| 171 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 170 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 172 | } | 171 | } |
| @@ -175,7 +174,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) { | |||
| 175 | 174 | ||
| 176 | // Load module | 175 | // Load module |
| 177 | const VAddr base_address = process.PageTable().GetCodeRegionStart(); | 176 | const VAddr base_address = process.PageTable().GetCodeRegionStart(); |
| 178 | if (!LoadModule(process, *file, base_address, true, true)) { | 177 | if (!LoadModule(process, system, *file, base_address, true, true)) { |
| 179 | return {ResultStatus::ErrorLoadingNSO, {}}; | 178 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 180 | } | 179 | } |
| 181 | 180 | ||
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index b210830f0..4bd47787d 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -12,6 +12,10 @@ | |||
| 12 | #include "core/file_sys/patch_manager.h" | 12 | #include "core/file_sys/patch_manager.h" |
| 13 | #include "core/loader/loader.h" | 13 | #include "core/loader/loader.h" |
| 14 | 14 | ||
| 15 | namespace Core { | ||
| 16 | class System; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Kernel { | 19 | namespace Kernel { |
| 16 | class Process; | 20 | class Process; |
| 17 | } | 21 | } |
| @@ -80,12 +84,12 @@ public: | |||
| 80 | return IdentifyType(file); | 84 | return IdentifyType(file); |
| 81 | } | 85 | } |
| 82 | 86 | ||
| 83 | static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file, | 87 | static std::optional<VAddr> LoadModule(Kernel::Process& process, Core::System& system, |
| 84 | VAddr load_base, bool should_pass_arguments, | 88 | const FileSys::VfsFile& file, VAddr load_base, |
| 85 | bool load_into_process, | 89 | bool should_pass_arguments, bool load_into_process, |
| 86 | std::optional<FileSys::PatchManager> pm = {}); | 90 | std::optional<FileSys::PatchManager> pm = {}); |
| 87 | 91 | ||
| 88 | LoadResult Load(Kernel::Process& process) override; | 92 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 89 | 93 | ||
| 90 | ResultStatus ReadNSOModules(Modules& modules) override; | 94 | ResultStatus ReadNSOModules(Modules& modules) override; |
| 91 | 95 | ||
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 13950fc08..15e528fa8 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -71,7 +71,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 71 | return FileType::Error; | 71 | return FileType::Error; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { | 74 | AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process, Core::System& system) { |
| 75 | if (is_loaded) { | 75 | if (is_loaded) { |
| 76 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 76 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 77 | } | 77 | } |
| @@ -99,15 +99,14 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) { | |||
| 99 | return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; | 99 | return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | const auto result = secondary_loader->Load(process); | 102 | const auto result = secondary_loader->Load(process, system); |
| 103 | if (result.first != ResultStatus::Success) { | 103 | if (result.first != ResultStatus::Success) { |
| 104 | return result; | 104 | return result; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | FileSys::VirtualFile update_raw; | 107 | FileSys::VirtualFile update_raw; |
| 108 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { | 108 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { |
| 109 | Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( | 109 | system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); |
| 110 | std::move(update_raw)); | ||
| 111 | } | 110 | } |
| 112 | 111 | ||
| 113 | is_loaded = true; | 112 | is_loaded = true; |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 868b028d3..b27deb686 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -9,6 +9,10 @@ | |||
| 9 | #include "core/file_sys/vfs.h" | 9 | #include "core/file_sys/vfs.h" |
| 10 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 11 | 11 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace FileSys { | 16 | namespace FileSys { |
| 13 | class NACP; | 17 | class NACP; |
| 14 | class NSP; | 18 | class NSP; |
| @@ -35,7 +39,7 @@ public: | |||
| 35 | return IdentifyType(file); | 39 | return IdentifyType(file); |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | LoadResult Load(Kernel::Process& process) override; | 42 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 39 | 43 | ||
| 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; | 44 | ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; |
| 41 | u64 ReadRomFSIVFCOffset() const override; | 45 | u64 ReadRomFSIVFCOffset() const override; |
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 7186ad1ff..25e83af0f 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -49,7 +49,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 49 | return FileType::Error; | 49 | return FileType::Error; |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { | 52 | AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process, Core::System& system) { |
| 53 | if (is_loaded) { | 53 | if (is_loaded) { |
| 54 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 54 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 55 | } | 55 | } |
| @@ -66,15 +66,14 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) { | |||
| 66 | return {ResultStatus::ErrorMissingProductionKeyFile, {}}; | 66 | return {ResultStatus::ErrorMissingProductionKeyFile, {}}; |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | const auto result = nca_loader->Load(process); | 69 | const auto result = nca_loader->Load(process, system); |
| 70 | if (result.first != ResultStatus::Success) { | 70 | if (result.first != ResultStatus::Success) { |
| 71 | return result; | 71 | return result; |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | FileSys::VirtualFile update_raw; | 74 | FileSys::VirtualFile update_raw; |
| 75 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { | 75 | if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { |
| 76 | Core::System::GetInstance().GetFileSystemController().SetPackedUpdate( | 76 | system.GetFileSystemController().SetPackedUpdate(std::move(update_raw)); |
| 77 | std::move(update_raw)); | ||
| 78 | } | 77 | } |
| 79 | 78 | ||
| 80 | is_loaded = true; | 79 | is_loaded = true; |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 618ae2f47..04aea286f 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -9,6 +9,10 @@ | |||
| 9 | #include "core/file_sys/vfs.h" | 9 | #include "core/file_sys/vfs.h" |
| 10 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 11 | 11 | ||
| 12 | namespace Core { | ||
| 13 | class System; | ||
| 14 | } | ||
| 15 | |||
| 12 | namespace FileSys { | 16 | namespace FileSys { |
| 13 | class NACP; | 17 | class NACP; |
| 14 | class XCI; | 18 | class XCI; |
| @@ -35,7 +39,7 @@ public: | |||
| 35 | return IdentifyType(file); | 39 | return IdentifyType(file); |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | LoadResult Load(Kernel::Process& process) override; | 42 | LoadResult Load(Kernel::Process& process, Core::System& system) override; |
| 39 | 43 | ||
| 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; | 44 | ResultStatus ReadRomFS(FileSys::VirtualFile& file) override; |
| 41 | u64 ReadRomFSIVFCOffset() const override; | 45 | u64 ReadRomFSIVFCOffset() const override; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 2c5588933..c3f4829d7 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -567,7 +567,7 @@ struct Memory::Impl { | |||
| 567 | * @param page_table The page table to use to perform the mapping. | 567 | * @param page_table The page table to use to perform the mapping. |
| 568 | * @param base The base address to begin mapping at. | 568 | * @param base The base address to begin mapping at. |
| 569 | * @param size The total size of the range in bytes. | 569 | * @param size The total size of the range in bytes. |
| 570 | * @param memory The memory to map. | 570 | * @param target The target address to begin mapping from. |
| 571 | * @param type The page type to map the memory as. | 571 | * @param type The page type to map the memory as. |
| 572 | */ | 572 | */ |
| 573 | void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, | 573 | void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target, |
| @@ -704,7 +704,7 @@ struct Memory::Impl { | |||
| 704 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 704 | u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |
| 705 | if (page_pointer != nullptr) { | 705 | if (page_pointer != nullptr) { |
| 706 | // NOTE: Avoid adding any extra logic to this fast-path block | 706 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 707 | T volatile* pointer = reinterpret_cast<T volatile*>(&page_pointer[vaddr]); | 707 | auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]); |
| 708 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 708 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 709 | } | 709 | } |
| 710 | 710 | ||
| @@ -720,9 +720,8 @@ struct Memory::Impl { | |||
| 720 | case Common::PageType::RasterizerCachedMemory: { | 720 | case Common::PageType::RasterizerCachedMemory: { |
| 721 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | 721 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; |
| 722 | system.GPU().InvalidateRegion(vaddr, sizeof(T)); | 722 | system.GPU().InvalidateRegion(vaddr, sizeof(T)); |
| 723 | T volatile* pointer = reinterpret_cast<T volatile*>(&host_ptr); | 723 | auto* pointer = reinterpret_cast<volatile T*>(&host_ptr); |
| 724 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 724 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 725 | break; | ||
| 726 | } | 725 | } |
| 727 | default: | 726 | default: |
| 728 | UNREACHABLE(); | 727 | UNREACHABLE(); |
| @@ -734,7 +733,7 @@ struct Memory::Impl { | |||
| 734 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; | 733 | u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; |
| 735 | if (page_pointer != nullptr) { | 734 | if (page_pointer != nullptr) { |
| 736 | // NOTE: Avoid adding any extra logic to this fast-path block | 735 | // NOTE: Avoid adding any extra logic to this fast-path block |
| 737 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&page_pointer[vaddr]); | 736 | auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]); |
| 738 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 737 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 739 | } | 738 | } |
| 740 | 739 | ||
| @@ -750,9 +749,8 @@ struct Memory::Impl { | |||
| 750 | case Common::PageType::RasterizerCachedMemory: { | 749 | case Common::PageType::RasterizerCachedMemory: { |
| 751 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; | 750 | u8* host_ptr{GetPointerFromRasterizerCachedMemory(vaddr)}; |
| 752 | system.GPU().InvalidateRegion(vaddr, sizeof(u128)); | 751 | system.GPU().InvalidateRegion(vaddr, sizeof(u128)); |
| 753 | u64 volatile* pointer = reinterpret_cast<u64 volatile*>(&host_ptr); | 752 | auto* pointer = reinterpret_cast<volatile u64*>(&host_ptr); |
| 754 | return Common::AtomicCompareAndSwap(pointer, data, expected); | 753 | return Common::AtomicCompareAndSwap(pointer, data, expected); |
| 755 | break; | ||
| 756 | } | 754 | } |
| 757 | default: | 755 | default: |
| 758 | UNREACHABLE(); | 756 | UNREACHABLE(); |
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 53d27859b..29284a42d 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp | |||
| @@ -19,10 +19,24 @@ | |||
| 19 | #include "core/memory/cheat_engine.h" | 19 | #include "core/memory/cheat_engine.h" |
| 20 | 20 | ||
| 21 | namespace Core::Memory { | 21 | namespace Core::Memory { |
| 22 | 22 | namespace { | |
| 23 | constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12); | 23 | constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12}; |
| 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; | 24 | constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; |
| 25 | 25 | ||
| 26 | std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) { | ||
| 27 | auto end_index = start_index; | ||
| 28 | while (data[end_index] != match) { | ||
| 29 | ++end_index; | ||
| 30 | if (end_index > data.size() || | ||
| 31 | (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) { | ||
| 32 | return {}; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | return data.substr(start_index, end_index - start_index); | ||
| 37 | } | ||
| 38 | } // Anonymous namespace | ||
| 39 | |||
| 26 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) | 40 | StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) |
| 27 | : metadata(metadata), system(system) {} | 41 | : metadata(metadata), system(system) {} |
| 28 | 42 | ||
| @@ -42,7 +56,7 @@ u64 StandardVmCallbacks::HidKeysDown() { | |||
| 42 | if (applet_resource == nullptr) { | 56 | if (applet_resource == nullptr) { |
| 43 | LOG_WARNING(CheatEngine, | 57 | LOG_WARNING(CheatEngine, |
| 44 | "Attempted to read input state, but applet resource is not initialized!"); | 58 | "Attempted to read input state, but applet resource is not initialized!"); |
| 45 | return false; | 59 | return 0; |
| 46 | } | 60 | } |
| 47 | 61 | ||
| 48 | const auto press_state = | 62 | const auto press_state = |
| @@ -82,26 +96,9 @@ CheatParser::~CheatParser() = default; | |||
| 82 | 96 | ||
| 83 | TextCheatParser::~TextCheatParser() = default; | 97 | TextCheatParser::~TextCheatParser() = default; |
| 84 | 98 | ||
| 85 | namespace { | 99 | std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const { |
| 86 | template <char match> | ||
| 87 | std::string_view ExtractName(std::string_view data, std::size_t start_index) { | ||
| 88 | auto end_index = start_index; | ||
| 89 | while (data[end_index] != match) { | ||
| 90 | ++end_index; | ||
| 91 | if (end_index > data.size() || | ||
| 92 | (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) { | ||
| 93 | return {}; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | return data.substr(start_index, end_index - start_index); | ||
| 98 | } | ||
| 99 | } // Anonymous namespace | ||
| 100 | |||
| 101 | std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, | ||
| 102 | std::string_view data) const { | ||
| 103 | std::vector<CheatEntry> out(1); | 100 | std::vector<CheatEntry> out(1); |
| 104 | std::optional<u64> current_entry = std::nullopt; | 101 | std::optional<u64> current_entry; |
| 105 | 102 | ||
| 106 | for (std::size_t i = 0; i < data.size(); ++i) { | 103 | for (std::size_t i = 0; i < data.size(); ++i) { |
| 107 | if (::isspace(data[i])) { | 104 | if (::isspace(data[i])) { |
| @@ -115,7 +112,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, | |||
| 115 | return {}; | 112 | return {}; |
| 116 | } | 113 | } |
| 117 | 114 | ||
| 118 | const auto name = ExtractName<'}'>(data, i + 1); | 115 | const auto name = ExtractName(data, i + 1, '}'); |
| 119 | if (name.empty()) { | 116 | if (name.empty()) { |
| 120 | return {}; | 117 | return {}; |
| 121 | } | 118 | } |
| @@ -132,7 +129,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system, | |||
| 132 | current_entry = out.size(); | 129 | current_entry = out.size(); |
| 133 | out.emplace_back(); | 130 | out.emplace_back(); |
| 134 | 131 | ||
| 135 | const auto name = ExtractName<']'>(data, i + 1); | 132 | const auto name = ExtractName(data, i + 1, ']'); |
| 136 | if (name.empty()) { | 133 | if (name.empty()) { |
| 137 | return {}; | 134 | return {}; |
| 138 | } | 135 | } |
| @@ -190,24 +187,38 @@ CheatEngine::~CheatEngine() { | |||
| 190 | void CheatEngine::Initialize() { | 187 | void CheatEngine::Initialize() { |
| 191 | event = Core::Timing::CreateEvent( | 188 | event = Core::Timing::CreateEvent( |
| 192 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), | 189 | "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), |
| 193 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); | 190 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 194 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); | 191 | FrameCallback(user_data, ns_late); |
| 192 | }); | ||
| 193 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event); | ||
| 195 | 194 | ||
| 196 | metadata.process_id = system.CurrentProcess()->GetProcessID(); | 195 | metadata.process_id = system.CurrentProcess()->GetProcessID(); |
| 197 | metadata.title_id = system.CurrentProcess()->GetTitleID(); | 196 | metadata.title_id = system.CurrentProcess()->GetTitleID(); |
| 198 | 197 | ||
| 199 | const auto& page_table = system.CurrentProcess()->PageTable(); | 198 | const auto& page_table = system.CurrentProcess()->PageTable(); |
| 200 | metadata.heap_extents = {page_table.GetHeapRegionStart(), page_table.GetHeapRegionSize()}; | 199 | metadata.heap_extents = { |
| 201 | metadata.address_space_extents = {page_table.GetAddressSpaceStart(), | 200 | .base = page_table.GetHeapRegionStart(), |
| 202 | page_table.GetAddressSpaceSize()}; | 201 | .size = page_table.GetHeapRegionSize(), |
| 203 | metadata.alias_extents = {page_table.GetAliasCodeRegionStart(), | 202 | }; |
| 204 | page_table.GetAliasCodeRegionSize()}; | 203 | |
| 204 | metadata.address_space_extents = { | ||
| 205 | .base = page_table.GetAddressSpaceStart(), | ||
| 206 | .size = page_table.GetAddressSpaceSize(), | ||
| 207 | }; | ||
| 208 | |||
| 209 | metadata.alias_extents = { | ||
| 210 | .base = page_table.GetAliasCodeRegionStart(), | ||
| 211 | .size = page_table.GetAliasCodeRegionSize(), | ||
| 212 | }; | ||
| 205 | 213 | ||
| 206 | is_pending_reload.exchange(true); | 214 | is_pending_reload.exchange(true); |
| 207 | } | 215 | } |
| 208 | 216 | ||
| 209 | void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { | 217 | void CheatEngine::SetMainMemoryParameters(VAddr main_region_begin, u64 main_region_size) { |
| 210 | metadata.main_nso_extents = {main_region_begin, main_region_size}; | 218 | metadata.main_nso_extents = { |
| 219 | .base = main_region_begin, | ||
| 220 | .size = main_region_size, | ||
| 221 | }; | ||
| 211 | } | 222 | } |
| 212 | 223 | ||
| 213 | void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | 224 | void CheatEngine::Reload(std::vector<CheatEntry> cheats) { |
| @@ -217,7 +228,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) { | |||
| 217 | 228 | ||
| 218 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); | 229 | MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); |
| 219 | 230 | ||
| 220 | void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { | 231 | void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { |
| 221 | if (is_pending_reload.exchange(false)) { | 232 | if (is_pending_reload.exchange(false)) { |
| 222 | vm.LoadProgram(cheats); | 233 | vm.LoadProgram(cheats); |
| 223 | } | 234 | } |
| @@ -230,7 +241,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) { | |||
| 230 | 241 | ||
| 231 | vm.Execute(metadata); | 242 | vm.Execute(metadata); |
| 232 | 243 | ||
| 233 | core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event); | 244 | core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event); |
| 234 | } | 245 | } |
| 235 | 246 | ||
| 236 | } // namespace Core::Memory | 247 | } // namespace Core::Memory |
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index 2649423f8..a31002346 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <chrono> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| @@ -46,8 +47,7 @@ class CheatParser { | |||
| 46 | public: | 47 | public: |
| 47 | virtual ~CheatParser(); | 48 | virtual ~CheatParser(); |
| 48 | 49 | ||
| 49 | virtual std::vector<CheatEntry> Parse(const Core::System& system, | 50 | [[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0; |
| 50 | std::string_view data) const = 0; | ||
| 51 | }; | 51 | }; |
| 52 | 52 | ||
| 53 | // CheatParser implementation that parses text files | 53 | // CheatParser implementation that parses text files |
| @@ -55,7 +55,7 @@ class TextCheatParser final : public CheatParser { | |||
| 55 | public: | 55 | public: |
| 56 | ~TextCheatParser() override; | 56 | ~TextCheatParser() override; |
| 57 | 57 | ||
| 58 | std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override; | 58 | [[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override; |
| 59 | }; | 59 | }; |
| 60 | 60 | ||
| 61 | // Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming | 61 | // Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming |
| @@ -71,7 +71,7 @@ public: | |||
| 71 | void Reload(std::vector<CheatEntry> cheats); | 71 | void Reload(std::vector<CheatEntry> cheats); |
| 72 | 72 | ||
| 73 | private: | 73 | private: |
| 74 | void FrameCallback(u64 userdata, s64 cycles_late); | 74 | void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); |
| 75 | 75 | ||
| 76 | DmntCheatVm vm; | 76 | DmntCheatVm vm; |
| 77 | CheatProcessMetadata metadata; | 77 | CheatProcessMetadata metadata; |
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index 2e7da23fe..48be80c12 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp | |||
| @@ -313,30 +313,32 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 313 | 313 | ||
| 314 | switch (opcode_type) { | 314 | switch (opcode_type) { |
| 315 | case CheatVmOpcodeType::StoreStatic: { | 315 | case CheatVmOpcodeType::StoreStatic: { |
| 316 | StoreStaticOpcode store_static{}; | ||
| 317 | // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) | 316 | // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) |
| 318 | // Read additional words. | 317 | // Read additional words. |
| 319 | const u32 second_dword = GetNextDword(); | 318 | const u32 second_dword = GetNextDword(); |
| 320 | store_static.bit_width = (first_dword >> 24) & 0xF; | 319 | const u32 bit_width = (first_dword >> 24) & 0xF; |
| 321 | store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | 320 | |
| 322 | store_static.offset_register = ((first_dword >> 16) & 0xF); | 321 | opcode.opcode = StoreStaticOpcode{ |
| 323 | store_static.rel_address = | 322 | .bit_width = bit_width, |
| 324 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | 323 | .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), |
| 325 | store_static.value = GetNextVmInt(store_static.bit_width); | 324 | .offset_register = (first_dword >> 16) & 0xF, |
| 326 | opcode.opcode = store_static; | 325 | .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, |
| 326 | .value = GetNextVmInt(bit_width), | ||
| 327 | }; | ||
| 327 | } break; | 328 | } break; |
| 328 | case CheatVmOpcodeType::BeginConditionalBlock: { | 329 | case CheatVmOpcodeType::BeginConditionalBlock: { |
| 329 | BeginConditionalOpcode begin_cond{}; | ||
| 330 | // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) | 330 | // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) |
| 331 | // Read additional words. | 331 | // Read additional words. |
| 332 | const u32 second_dword = GetNextDword(); | 332 | const u32 second_dword = GetNextDword(); |
| 333 | begin_cond.bit_width = (first_dword >> 24) & 0xF; | 333 | const u32 bit_width = (first_dword >> 24) & 0xF; |
| 334 | begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | 334 | |
| 335 | begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); | 335 | opcode.opcode = BeginConditionalOpcode{ |
| 336 | begin_cond.rel_address = | 336 | .bit_width = bit_width, |
| 337 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | 337 | .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), |
| 338 | begin_cond.value = GetNextVmInt(begin_cond.bit_width); | 338 | .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF), |
| 339 | opcode.opcode = begin_cond; | 339 | .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, |
| 340 | .value = GetNextVmInt(bit_width), | ||
| 341 | }; | ||
| 340 | } break; | 342 | } break; |
| 341 | case CheatVmOpcodeType::EndConditionalBlock: { | 343 | case CheatVmOpcodeType::EndConditionalBlock: { |
| 342 | // 20000000 | 344 | // 20000000 |
| @@ -344,12 +346,14 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 344 | opcode.opcode = EndConditionalOpcode{}; | 346 | opcode.opcode = EndConditionalOpcode{}; |
| 345 | } break; | 347 | } break; |
| 346 | case CheatVmOpcodeType::ControlLoop: { | 348 | case CheatVmOpcodeType::ControlLoop: { |
| 347 | ControlLoopOpcode ctrl_loop{}; | ||
| 348 | // 300R0000 VVVVVVVV | 349 | // 300R0000 VVVVVVVV |
| 349 | // 310R0000 | 350 | // 310R0000 |
| 350 | // Parse register, whether loop start or loop end. | 351 | // Parse register, whether loop start or loop end. |
| 351 | ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0; | 352 | ControlLoopOpcode ctrl_loop{ |
| 352 | ctrl_loop.reg_index = ((first_dword >> 20) & 0xF); | 353 | .start_loop = ((first_dword >> 24) & 0xF) == 0, |
| 354 | .reg_index = (first_dword >> 20) & 0xF, | ||
| 355 | .num_iters = 0, | ||
| 356 | }; | ||
| 353 | 357 | ||
| 354 | // Read number of iters if loop start. | 358 | // Read number of iters if loop start. |
| 355 | if (ctrl_loop.start_loop) { | 359 | if (ctrl_loop.start_loop) { |
| @@ -358,66 +362,65 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 358 | opcode.opcode = ctrl_loop; | 362 | opcode.opcode = ctrl_loop; |
| 359 | } break; | 363 | } break; |
| 360 | case CheatVmOpcodeType::LoadRegisterStatic: { | 364 | case CheatVmOpcodeType::LoadRegisterStatic: { |
| 361 | LoadRegisterStaticOpcode ldr_static{}; | ||
| 362 | // 400R0000 VVVVVVVV VVVVVVVV | 365 | // 400R0000 VVVVVVVV VVVVVVVV |
| 363 | // Read additional words. | 366 | // Read additional words. |
| 364 | ldr_static.reg_index = ((first_dword >> 16) & 0xF); | 367 | opcode.opcode = LoadRegisterStaticOpcode{ |
| 365 | ldr_static.value = | 368 | .reg_index = (first_dword >> 16) & 0xF, |
| 366 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | 369 | .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(), |
| 367 | opcode.opcode = ldr_static; | 370 | }; |
| 368 | } break; | 371 | } break; |
| 369 | case CheatVmOpcodeType::LoadRegisterMemory: { | 372 | case CheatVmOpcodeType::LoadRegisterMemory: { |
| 370 | LoadRegisterMemoryOpcode ldr_memory{}; | ||
| 371 | // 5TMRI0AA AAAAAAAA | 373 | // 5TMRI0AA AAAAAAAA |
| 372 | // Read additional words. | 374 | // Read additional words. |
| 373 | const u32 second_dword = GetNextDword(); | 375 | const u32 second_dword = GetNextDword(); |
| 374 | ldr_memory.bit_width = (first_dword >> 24) & 0xF; | 376 | opcode.opcode = LoadRegisterMemoryOpcode{ |
| 375 | ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF); | 377 | .bit_width = (first_dword >> 24) & 0xF, |
| 376 | ldr_memory.reg_index = ((first_dword >> 16) & 0xF); | 378 | .mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF), |
| 377 | ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0; | 379 | .reg_index = ((first_dword >> 16) & 0xF), |
| 378 | ldr_memory.rel_address = | 380 | .load_from_reg = ((first_dword >> 12) & 0xF) != 0, |
| 379 | (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword); | 381 | .rel_address = (static_cast<u64>(first_dword & 0xFF) << 32) | second_dword, |
| 380 | opcode.opcode = ldr_memory; | 382 | }; |
| 381 | } break; | 383 | } break; |
| 382 | case CheatVmOpcodeType::StoreStaticToAddress: { | 384 | case CheatVmOpcodeType::StoreStaticToAddress: { |
| 383 | StoreStaticToAddressOpcode str_static{}; | ||
| 384 | // 6T0RIor0 VVVVVVVV VVVVVVVV | 385 | // 6T0RIor0 VVVVVVVV VVVVVVVV |
| 385 | // Read additional words. | 386 | // Read additional words. |
| 386 | str_static.bit_width = (first_dword >> 24) & 0xF; | 387 | opcode.opcode = StoreStaticToAddressOpcode{ |
| 387 | str_static.reg_index = ((first_dword >> 16) & 0xF); | 388 | .bit_width = (first_dword >> 24) & 0xF, |
| 388 | str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0; | 389 | .reg_index = (first_dword >> 16) & 0xF, |
| 389 | str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0; | 390 | .increment_reg = ((first_dword >> 12) & 0xF) != 0, |
| 390 | str_static.offset_reg_index = ((first_dword >> 4) & 0xF); | 391 | .add_offset_reg = ((first_dword >> 8) & 0xF) != 0, |
| 391 | str_static.value = | 392 | .offset_reg_index = (first_dword >> 4) & 0xF, |
| 392 | (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword()); | 393 | .value = (static_cast<u64>(GetNextDword()) << 32) | GetNextDword(), |
| 393 | opcode.opcode = str_static; | 394 | }; |
| 394 | } break; | 395 | } break; |
| 395 | case CheatVmOpcodeType::PerformArithmeticStatic: { | 396 | case CheatVmOpcodeType::PerformArithmeticStatic: { |
| 396 | PerformArithmeticStaticOpcode perform_math_static{}; | ||
| 397 | // 7T0RC000 VVVVVVVV | 397 | // 7T0RC000 VVVVVVVV |
| 398 | // Read additional words. | 398 | // Read additional words. |
| 399 | perform_math_static.bit_width = (first_dword >> 24) & 0xF; | 399 | opcode.opcode = PerformArithmeticStaticOpcode{ |
| 400 | perform_math_static.reg_index = ((first_dword >> 16) & 0xF); | 400 | .bit_width = (first_dword >> 24) & 0xF, |
| 401 | perform_math_static.math_type = | 401 | .reg_index = ((first_dword >> 16) & 0xF), |
| 402 | static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF); | 402 | .math_type = static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF), |
| 403 | perform_math_static.value = GetNextDword(); | 403 | .value = GetNextDword(), |
| 404 | opcode.opcode = perform_math_static; | 404 | }; |
| 405 | } break; | 405 | } break; |
| 406 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: { | 406 | case CheatVmOpcodeType::BeginKeypressConditionalBlock: { |
| 407 | BeginKeypressConditionalOpcode begin_keypress_cond{}; | ||
| 408 | // 8kkkkkkk | 407 | // 8kkkkkkk |
| 409 | // Just parse the mask. | 408 | // Just parse the mask. |
| 410 | begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF; | 409 | opcode.opcode = BeginKeypressConditionalOpcode{ |
| 411 | opcode.opcode = begin_keypress_cond; | 410 | .key_mask = first_dword & 0x0FFFFFFF, |
| 411 | }; | ||
| 412 | } break; | 412 | } break; |
| 413 | case CheatVmOpcodeType::PerformArithmeticRegister: { | 413 | case CheatVmOpcodeType::PerformArithmeticRegister: { |
| 414 | PerformArithmeticRegisterOpcode perform_math_reg{}; | ||
| 415 | // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) | 414 | // 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) |
| 416 | perform_math_reg.bit_width = (first_dword >> 24) & 0xF; | 415 | PerformArithmeticRegisterOpcode perform_math_reg{ |
| 417 | perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF); | 416 | .bit_width = (first_dword >> 24) & 0xF, |
| 418 | perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF); | 417 | .math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF), |
| 419 | perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF); | 418 | .dst_reg_index = (first_dword >> 16) & 0xF, |
| 420 | perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0; | 419 | .src_reg_1_index = (first_dword >> 12) & 0xF, |
| 420 | .src_reg_2_index = 0, | ||
| 421 | .has_immediate = ((first_dword >> 8) & 0xF) != 0, | ||
| 422 | .value = {}, | ||
| 423 | }; | ||
| 421 | if (perform_math_reg.has_immediate) { | 424 | if (perform_math_reg.has_immediate) { |
| 422 | perform_math_reg.src_reg_2_index = 0; | 425 | perform_math_reg.src_reg_2_index = 0; |
| 423 | perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); | 426 | perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width); |
| @@ -427,7 +430,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 427 | opcode.opcode = perform_math_reg; | 430 | opcode.opcode = perform_math_reg; |
| 428 | } break; | 431 | } break; |
| 429 | case CheatVmOpcodeType::StoreRegisterToAddress: { | 432 | case CheatVmOpcodeType::StoreRegisterToAddress: { |
| 430 | StoreRegisterToAddressOpcode str_register{}; | ||
| 431 | // ATSRIOxa (aaaaaaaa) | 433 | // ATSRIOxa (aaaaaaaa) |
| 432 | // A = opcode 10 | 434 | // A = opcode 10 |
| 433 | // T = bit width | 435 | // T = bit width |
| @@ -439,20 +441,23 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 439 | // Relative Address | 441 | // Relative Address |
| 440 | // x = offset register (for offset type 1), memory type (for offset type 3) | 442 | // x = offset register (for offset type 1), memory type (for offset type 3) |
| 441 | // a = relative address (for offset type 2+3) | 443 | // a = relative address (for offset type 2+3) |
| 442 | str_register.bit_width = (first_dword >> 24) & 0xF; | 444 | StoreRegisterToAddressOpcode str_register{ |
| 443 | str_register.str_reg_index = ((first_dword >> 20) & 0xF); | 445 | .bit_width = (first_dword >> 24) & 0xF, |
| 444 | str_register.addr_reg_index = ((first_dword >> 16) & 0xF); | 446 | .str_reg_index = (first_dword >> 20) & 0xF, |
| 445 | str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0; | 447 | .addr_reg_index = (first_dword >> 16) & 0xF, |
| 446 | str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)); | 448 | .increment_reg = ((first_dword >> 12) & 0xF) != 0, |
| 447 | str_register.ofs_reg_index = ((first_dword >> 4) & 0xF); | 449 | .ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF)), |
| 450 | .mem_type = MemoryAccessType::MainNso, | ||
| 451 | .ofs_reg_index = (first_dword >> 4) & 0xF, | ||
| 452 | .rel_address = 0, | ||
| 453 | }; | ||
| 448 | switch (str_register.ofs_type) { | 454 | switch (str_register.ofs_type) { |
| 449 | case StoreRegisterOffsetType::None: | 455 | case StoreRegisterOffsetType::None: |
| 450 | case StoreRegisterOffsetType::Reg: | 456 | case StoreRegisterOffsetType::Reg: |
| 451 | // Nothing more to do | 457 | // Nothing more to do |
| 452 | break; | 458 | break; |
| 453 | case StoreRegisterOffsetType::Imm: | 459 | case StoreRegisterOffsetType::Imm: |
| 454 | str_register.rel_address = | 460 | str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 455 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 456 | break; | 461 | break; |
| 457 | case StoreRegisterOffsetType::MemReg: | 462 | case StoreRegisterOffsetType::MemReg: |
| 458 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 463 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| @@ -460,8 +465,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 460 | case StoreRegisterOffsetType::MemImm: | 465 | case StoreRegisterOffsetType::MemImm: |
| 461 | case StoreRegisterOffsetType::MemImmReg: | 466 | case StoreRegisterOffsetType::MemImmReg: |
| 462 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 467 | str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 463 | str_register.rel_address = | 468 | str_register.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 464 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 465 | break; | 469 | break; |
| 466 | default: | 470 | default: |
| 467 | str_register.ofs_type = StoreRegisterOffsetType::None; | 471 | str_register.ofs_type = StoreRegisterOffsetType::None; |
| @@ -470,7 +474,6 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 470 | opcode.opcode = str_register; | 474 | opcode.opcode = str_register; |
| 471 | } break; | 475 | } break; |
| 472 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: { | 476 | case CheatVmOpcodeType::BeginRegisterConditionalBlock: { |
| 473 | BeginRegisterConditionalOpcode begin_reg_cond{}; | ||
| 474 | // C0TcSX## | 477 | // C0TcSX## |
| 475 | // C0TcS0Ma aaaaaaaa | 478 | // C0TcS0Ma aaaaaaaa |
| 476 | // C0TcS1Mr | 479 | // C0TcS1Mr |
| @@ -492,11 +495,19 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 492 | // r = offset register. | 495 | // r = offset register. |
| 493 | // X = other register. | 496 | // X = other register. |
| 494 | // V = value. | 497 | // V = value. |
| 495 | begin_reg_cond.bit_width = (first_dword >> 20) & 0xF; | 498 | |
| 496 | begin_reg_cond.cond_type = | 499 | BeginRegisterConditionalOpcode begin_reg_cond{ |
| 497 | static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF); | 500 | .bit_width = (first_dword >> 20) & 0xF, |
| 498 | begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF); | 501 | .cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF), |
| 499 | begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF); | 502 | .val_reg_index = (first_dword >> 12) & 0xF, |
| 503 | .comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF), | ||
| 504 | .mem_type = MemoryAccessType::MainNso, | ||
| 505 | .addr_reg_index = 0, | ||
| 506 | .other_reg_index = 0, | ||
| 507 | .ofs_reg_index = 0, | ||
| 508 | .rel_address = 0, | ||
| 509 | .value = {}, | ||
| 510 | }; | ||
| 500 | 511 | ||
| 501 | switch (begin_reg_cond.comp_type) { | 512 | switch (begin_reg_cond.comp_type) { |
| 502 | case CompareRegisterValueType::StaticValue: | 513 | case CompareRegisterValueType::StaticValue: |
| @@ -508,26 +519,25 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 508 | case CompareRegisterValueType::MemoryRelAddr: | 519 | case CompareRegisterValueType::MemoryRelAddr: |
| 509 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 520 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 510 | begin_reg_cond.rel_address = | 521 | begin_reg_cond.rel_address = |
| 511 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | 522 | (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 512 | break; | 523 | break; |
| 513 | case CompareRegisterValueType::MemoryOfsReg: | 524 | case CompareRegisterValueType::MemoryOfsReg: |
| 514 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 525 | begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 515 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | 526 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); |
| 516 | break; | 527 | break; |
| 517 | case CompareRegisterValueType::RegisterRelAddr: | 528 | case CompareRegisterValueType::RegisterRelAddr: |
| 518 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | 529 | begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF; |
| 519 | begin_reg_cond.rel_address = | 530 | begin_reg_cond.rel_address = |
| 520 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | 531 | (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 521 | break; | 532 | break; |
| 522 | case CompareRegisterValueType::RegisterOfsReg: | 533 | case CompareRegisterValueType::RegisterOfsReg: |
| 523 | begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF); | 534 | begin_reg_cond.addr_reg_index = (first_dword >> 4) & 0xF; |
| 524 | begin_reg_cond.ofs_reg_index = (first_dword & 0xF); | 535 | begin_reg_cond.ofs_reg_index = first_dword & 0xF; |
| 525 | break; | 536 | break; |
| 526 | } | 537 | } |
| 527 | opcode.opcode = begin_reg_cond; | 538 | opcode.opcode = begin_reg_cond; |
| 528 | } break; | 539 | } break; |
| 529 | case CheatVmOpcodeType::SaveRestoreRegister: { | 540 | case CheatVmOpcodeType::SaveRestoreRegister: { |
| 530 | SaveRestoreRegisterOpcode save_restore_reg{}; | ||
| 531 | // C10D0Sx0 | 541 | // C10D0Sx0 |
| 532 | // C1 = opcode 0xC1 | 542 | // C1 = opcode 0xC1 |
| 533 | // D = destination index. | 543 | // D = destination index. |
| @@ -535,36 +545,37 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 535 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring | 545 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring |
| 536 | // a register. | 546 | // a register. |
| 537 | // NOTE: If we add more save slots later, current encoding is backwards compatible. | 547 | // NOTE: If we add more save slots later, current encoding is backwards compatible. |
| 538 | save_restore_reg.dst_index = (first_dword >> 16) & 0xF; | 548 | opcode.opcode = SaveRestoreRegisterOpcode{ |
| 539 | save_restore_reg.src_index = (first_dword >> 8) & 0xF; | 549 | .dst_index = (first_dword >> 16) & 0xF, |
| 540 | save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF); | 550 | .src_index = (first_dword >> 8) & 0xF, |
| 541 | opcode.opcode = save_restore_reg; | 551 | .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF), |
| 552 | }; | ||
| 542 | } break; | 553 | } break; |
| 543 | case CheatVmOpcodeType::SaveRestoreRegisterMask: { | 554 | case CheatVmOpcodeType::SaveRestoreRegisterMask: { |
| 544 | SaveRestoreRegisterMaskOpcode save_restore_regmask{}; | ||
| 545 | // C2x0XXXX | 555 | // C2x0XXXX |
| 546 | // C2 = opcode 0xC2 | 556 | // C2 = opcode 0xC2 |
| 547 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. | 557 | // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. |
| 548 | // X = 16-bit bitmask, bit i --> save or restore register i. | 558 | // X = 16-bit bitmask, bit i --> save or restore register i. |
| 549 | save_restore_regmask.op_type = | 559 | SaveRestoreRegisterMaskOpcode save_restore_regmask{ |
| 550 | static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF); | 560 | .op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF), |
| 561 | .should_operate = {}, | ||
| 562 | }; | ||
| 551 | for (std::size_t i = 0; i < NumRegisters; i++) { | 563 | for (std::size_t i = 0; i < NumRegisters; i++) { |
| 552 | save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0; | 564 | save_restore_regmask.should_operate[i] = (first_dword & (1U << i)) != 0; |
| 553 | } | 565 | } |
| 554 | opcode.opcode = save_restore_regmask; | 566 | opcode.opcode = save_restore_regmask; |
| 555 | } break; | 567 | } break; |
| 556 | case CheatVmOpcodeType::ReadWriteStaticRegister: { | 568 | case CheatVmOpcodeType::ReadWriteStaticRegister: { |
| 557 | ReadWriteStaticRegisterOpcode rw_static_reg{}; | ||
| 558 | // C3000XXx | 569 | // C3000XXx |
| 559 | // C3 = opcode 0xC3. | 570 | // C3 = opcode 0xC3. |
| 560 | // XX = static register index. | 571 | // XX = static register index. |
| 561 | // x = register index. | 572 | // x = register index. |
| 562 | rw_static_reg.static_idx = ((first_dword >> 4) & 0xFF); | 573 | opcode.opcode = ReadWriteStaticRegisterOpcode{ |
| 563 | rw_static_reg.idx = (first_dword & 0xF); | 574 | .static_idx = (first_dword >> 4) & 0xFF, |
| 564 | opcode.opcode = rw_static_reg; | 575 | .idx = first_dword & 0xF, |
| 576 | }; | ||
| 565 | } break; | 577 | } break; |
| 566 | case CheatVmOpcodeType::DebugLog: { | 578 | case CheatVmOpcodeType::DebugLog: { |
| 567 | DebugLogOpcode debug_log{}; | ||
| 568 | // FFFTIX## | 579 | // FFFTIX## |
| 569 | // FFFTI0Ma aaaaaaaa | 580 | // FFFTI0Ma aaaaaaaa |
| 570 | // FFFTI1Mr | 581 | // FFFTI1Mr |
| @@ -583,31 +594,36 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 583 | // a = relative address. | 594 | // a = relative address. |
| 584 | // r = offset register. | 595 | // r = offset register. |
| 585 | // X = value register. | 596 | // X = value register. |
| 586 | debug_log.bit_width = (first_dword >> 16) & 0xF; | 597 | DebugLogOpcode debug_log{ |
| 587 | debug_log.log_id = ((first_dword >> 12) & 0xF); | 598 | .bit_width = (first_dword >> 16) & 0xF, |
| 588 | debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF); | 599 | .log_id = (first_dword >> 12) & 0xF, |
| 600 | .val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF), | ||
| 601 | .mem_type = MemoryAccessType::MainNso, | ||
| 602 | .addr_reg_index = 0, | ||
| 603 | .val_reg_index = 0, | ||
| 604 | .ofs_reg_index = 0, | ||
| 605 | .rel_address = 0, | ||
| 606 | }; | ||
| 589 | 607 | ||
| 590 | switch (debug_log.val_type) { | 608 | switch (debug_log.val_type) { |
| 591 | case DebugLogValueType::RegisterValue: | 609 | case DebugLogValueType::RegisterValue: |
| 592 | debug_log.val_reg_index = ((first_dword >> 4) & 0xF); | 610 | debug_log.val_reg_index = (first_dword >> 4) & 0xF; |
| 593 | break; | 611 | break; |
| 594 | case DebugLogValueType::MemoryRelAddr: | 612 | case DebugLogValueType::MemoryRelAddr: |
| 595 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 613 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 596 | debug_log.rel_address = | 614 | debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 597 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 598 | break; | 615 | break; |
| 599 | case DebugLogValueType::MemoryOfsReg: | 616 | case DebugLogValueType::MemoryOfsReg: |
| 600 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); | 617 | debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF); |
| 601 | debug_log.ofs_reg_index = (first_dword & 0xF); | 618 | debug_log.ofs_reg_index = first_dword & 0xF; |
| 602 | break; | 619 | break; |
| 603 | case DebugLogValueType::RegisterRelAddr: | 620 | case DebugLogValueType::RegisterRelAddr: |
| 604 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | 621 | debug_log.addr_reg_index = (first_dword >> 4) & 0xF; |
| 605 | debug_log.rel_address = | 622 | debug_log.rel_address = (static_cast<u64>(first_dword & 0xF) << 32) | GetNextDword(); |
| 606 | ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword())); | ||
| 607 | break; | 623 | break; |
| 608 | case DebugLogValueType::RegisterOfsReg: | 624 | case DebugLogValueType::RegisterOfsReg: |
| 609 | debug_log.addr_reg_index = ((first_dword >> 4) & 0xF); | 625 | debug_log.addr_reg_index = (first_dword >> 4) & 0xF; |
| 610 | debug_log.ofs_reg_index = (first_dword & 0xF); | 626 | debug_log.ofs_reg_index = first_dword & 0xF; |
| 611 | break; | 627 | break; |
| 612 | } | 628 | } |
| 613 | opcode.opcode = debug_log; | 629 | opcode.opcode = debug_log; |
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp new file mode 100644 index 000000000..56d173b5e --- /dev/null +++ b/src/core/network/network.cpp | |||
| @@ -0,0 +1,654 @@ | |||
| 1 | // Copyright 2020 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cstring> | ||
| 7 | #include <limits> | ||
| 8 | #include <utility> | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #ifdef _WIN32 | ||
| 12 | #define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname | ||
| 13 | #include <winsock2.h> | ||
| 14 | #elif __unix__ | ||
| 15 | #include <errno.h> | ||
| 16 | #include <fcntl.h> | ||
| 17 | #include <netdb.h> | ||
| 18 | #include <netinet/in.h> | ||
| 19 | #include <poll.h> | ||
| 20 | #include <sys/socket.h> | ||
| 21 | #include <unistd.h> | ||
| 22 | #else | ||
| 23 | #error "Unimplemented platform" | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #include "common/assert.h" | ||
| 27 | #include "common/common_types.h" | ||
| 28 | #include "common/logging/log.h" | ||
| 29 | #include "core/network/network.h" | ||
| 30 | #include "core/network/sockets.h" | ||
| 31 | |||
| 32 | namespace Network { | ||
| 33 | |||
| 34 | namespace { | ||
| 35 | |||
| 36 | #ifdef _WIN32 | ||
| 37 | |||
| 38 | using socklen_t = int; | ||
| 39 | |||
| 40 | void Initialize() { | ||
| 41 | WSADATA wsa_data; | ||
| 42 | (void)WSAStartup(MAKEWORD(2, 2), &wsa_data); | ||
| 43 | } | ||
| 44 | |||
| 45 | void Finalize() { | ||
| 46 | WSACleanup(); | ||
| 47 | } | ||
| 48 | |||
| 49 | constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||
| 50 | auto& bytes = addr.S_un.S_un_b; | ||
| 51 | return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4}; | ||
| 52 | } | ||
| 53 | |||
| 54 | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | ||
| 55 | sockaddr_in result; | ||
| 56 | |||
| 57 | #ifdef __unix__ | ||
| 58 | result.sin_len = sizeof(result); | ||
| 59 | #endif | ||
| 60 | |||
| 61 | switch (static_cast<Domain>(input.family)) { | ||
| 62 | case Domain::INET: | ||
| 63 | result.sin_family = AF_INET; | ||
| 64 | break; | ||
| 65 | default: | ||
| 66 | UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family)); | ||
| 67 | result.sin_family = AF_INET; | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | |||
| 71 | result.sin_port = htons(input.portno); | ||
| 72 | |||
| 73 | auto& ip = result.sin_addr.S_un.S_un_b; | ||
| 74 | ip.s_b1 = input.ip[0]; | ||
| 75 | ip.s_b2 = input.ip[1]; | ||
| 76 | ip.s_b3 = input.ip[2]; | ||
| 77 | ip.s_b4 = input.ip[3]; | ||
| 78 | |||
| 79 | sockaddr addr; | ||
| 80 | std::memcpy(&addr, &result, sizeof(addr)); | ||
| 81 | return addr; | ||
| 82 | } | ||
| 83 | |||
| 84 | LINGER MakeLinger(bool enable, u32 linger_value) { | ||
| 85 | ASSERT(linger_value <= std::numeric_limits<u_short>::max()); | ||
| 86 | |||
| 87 | LINGER value; | ||
| 88 | value.l_onoff = enable ? 1 : 0; | ||
| 89 | value.l_linger = static_cast<u_short>(linger_value); | ||
| 90 | return value; | ||
| 91 | } | ||
| 92 | |||
| 93 | int LastError() { | ||
| 94 | return WSAGetLastError(); | ||
| 95 | } | ||
| 96 | |||
| 97 | bool EnableNonBlock(SOCKET fd, bool enable) { | ||
| 98 | u_long value = enable ? 1 : 0; | ||
| 99 | return ioctlsocket(fd, FIONBIO, &value) != SOCKET_ERROR; | ||
| 100 | } | ||
| 101 | |||
| 102 | #elif __unix__ // ^ _WIN32 v __unix__ | ||
| 103 | |||
| 104 | using SOCKET = int; | ||
| 105 | using WSAPOLLFD = pollfd; | ||
| 106 | using ULONG = u64; | ||
| 107 | |||
| 108 | constexpr SOCKET INVALID_SOCKET = -1; | ||
| 109 | constexpr SOCKET SOCKET_ERROR = -1; | ||
| 110 | |||
| 111 | constexpr int WSAEWOULDBLOCK = EAGAIN; | ||
| 112 | constexpr int WSAENOTCONN = ENOTCONN; | ||
| 113 | |||
| 114 | constexpr int SD_RECEIVE = SHUT_RD; | ||
| 115 | constexpr int SD_SEND = SHUT_WR; | ||
| 116 | constexpr int SD_BOTH = SHUT_RDWR; | ||
| 117 | |||
| 118 | void Initialize() {} | ||
| 119 | |||
| 120 | void Finalize() {} | ||
| 121 | |||
| 122 | constexpr IPv4Address TranslateIPv4(in_addr addr) { | ||
| 123 | const u32 bytes = addr.s_addr; | ||
| 124 | return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8), | ||
| 125 | static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)}; | ||
| 126 | } | ||
| 127 | |||
| 128 | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | ||
| 129 | sockaddr_in result; | ||
| 130 | |||
| 131 | switch (static_cast<Domain>(input.family)) { | ||
| 132 | case Domain::INET: | ||
| 133 | result.sin_family = AF_INET; | ||
| 134 | break; | ||
| 135 | default: | ||
| 136 | UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", static_cast<int>(input.family)); | ||
| 137 | result.sin_family = AF_INET; | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | |||
| 141 | result.sin_port = htons(input.portno); | ||
| 142 | |||
| 143 | result.sin_addr.s_addr = input.ip[0] | input.ip[1] << 8 | input.ip[2] << 16 | input.ip[3] << 24; | ||
| 144 | |||
| 145 | sockaddr addr; | ||
| 146 | std::memcpy(&addr, &result, sizeof(addr)); | ||
| 147 | return addr; | ||
| 148 | } | ||
| 149 | |||
| 150 | int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) { | ||
| 151 | return poll(fds, nfds, timeout); | ||
| 152 | } | ||
| 153 | |||
| 154 | int closesocket(SOCKET fd) { | ||
| 155 | return close(fd); | ||
| 156 | } | ||
| 157 | |||
| 158 | linger MakeLinger(bool enable, u32 linger_value) { | ||
| 159 | linger value; | ||
| 160 | value.l_onoff = enable ? 1 : 0; | ||
| 161 | value.l_linger = linger_value; | ||
| 162 | return value; | ||
| 163 | } | ||
| 164 | |||
| 165 | int LastError() { | ||
| 166 | return errno; | ||
| 167 | } | ||
| 168 | |||
| 169 | bool EnableNonBlock(int fd, bool enable) { | ||
| 170 | int flags = fcntl(fd, F_GETFD); | ||
| 171 | if (flags == -1) { | ||
| 172 | return false; | ||
| 173 | } | ||
| 174 | if (enable) { | ||
| 175 | flags |= O_NONBLOCK; | ||
| 176 | } else { | ||
| 177 | flags &= ~O_NONBLOCK; | ||
| 178 | } | ||
| 179 | return fcntl(fd, F_SETFD, flags) == 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | #endif | ||
| 183 | |||
| 184 | int TranslateDomain(Domain domain) { | ||
| 185 | switch (domain) { | ||
| 186 | case Domain::INET: | ||
| 187 | return AF_INET; | ||
| 188 | default: | ||
| 189 | UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain)); | ||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | int TranslateType(Type type) { | ||
| 195 | switch (type) { | ||
| 196 | case Type::STREAM: | ||
| 197 | return SOCK_STREAM; | ||
| 198 | case Type::DGRAM: | ||
| 199 | return SOCK_DGRAM; | ||
| 200 | default: | ||
| 201 | UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type)); | ||
| 202 | return 0; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | int TranslateProtocol(Protocol protocol) { | ||
| 207 | switch (protocol) { | ||
| 208 | case Protocol::TCP: | ||
| 209 | return IPPROTO_TCP; | ||
| 210 | case Protocol::UDP: | ||
| 211 | return IPPROTO_UDP; | ||
| 212 | default: | ||
| 213 | UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol)); | ||
| 214 | return 0; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | SockAddrIn TranslateToSockAddrIn(sockaddr input_) { | ||
| 219 | sockaddr_in input; | ||
| 220 | std::memcpy(&input, &input_, sizeof(input)); | ||
| 221 | |||
| 222 | SockAddrIn result; | ||
| 223 | |||
| 224 | switch (input.sin_family) { | ||
| 225 | case AF_INET: | ||
| 226 | result.family = Domain::INET; | ||
| 227 | break; | ||
| 228 | default: | ||
| 229 | UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); | ||
| 230 | result.family = Domain::INET; | ||
| 231 | break; | ||
| 232 | } | ||
| 233 | |||
| 234 | result.portno = ntohs(input.sin_port); | ||
| 235 | |||
| 236 | result.ip = TranslateIPv4(input.sin_addr); | ||
| 237 | |||
| 238 | return result; | ||
| 239 | } | ||
| 240 | |||
| 241 | u16 TranslatePollEvents(u16 events) { | ||
| 242 | u16 result = 0; | ||
| 243 | |||
| 244 | if (events & POLL_IN) { | ||
| 245 | events &= ~POLL_IN; | ||
| 246 | result |= POLLIN; | ||
| 247 | } | ||
| 248 | if (events & POLL_PRI) { | ||
| 249 | events &= ~POLL_PRI; | ||
| 250 | #ifdef _WIN32 | ||
| 251 | LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); | ||
| 252 | #else | ||
| 253 | result |= POLL_PRI; | ||
| 254 | #endif | ||
| 255 | } | ||
| 256 | if (events & POLL_OUT) { | ||
| 257 | events &= ~POLL_OUT; | ||
| 258 | result |= POLLOUT; | ||
| 259 | } | ||
| 260 | |||
| 261 | UNIMPLEMENTED_IF_MSG(events != 0, "Unhandled guest events=0x{:x}", events); | ||
| 262 | |||
| 263 | return result; | ||
| 264 | } | ||
| 265 | |||
| 266 | u16 TranslatePollRevents(u16 revents) { | ||
| 267 | u16 result = 0; | ||
| 268 | const auto translate = [&result, &revents](int host, unsigned guest) { | ||
| 269 | if (revents & host) { | ||
| 270 | revents &= ~host; | ||
| 271 | result |= guest; | ||
| 272 | } | ||
| 273 | }; | ||
| 274 | |||
| 275 | translate(POLLIN, POLL_IN); | ||
| 276 | translate(POLLPRI, POLL_PRI); | ||
| 277 | translate(POLLOUT, POLL_OUT); | ||
| 278 | translate(POLLERR, POLL_ERR); | ||
| 279 | translate(POLLHUP, POLL_HUP); | ||
| 280 | |||
| 281 | UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); | ||
| 282 | |||
| 283 | return result; | ||
| 284 | } | ||
| 285 | |||
| 286 | template <typename T> | ||
| 287 | Errno SetSockOpt(SOCKET fd, int option, T value) { | ||
| 288 | const int result = | ||
| 289 | setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); | ||
| 290 | if (result != SOCKET_ERROR) { | ||
| 291 | return Errno::SUCCESS; | ||
| 292 | } | ||
| 293 | const int ec = LastError(); | ||
| 294 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 295 | return Errno::SUCCESS; | ||
| 296 | } | ||
| 297 | |||
| 298 | } // Anonymous namespace | ||
| 299 | |||
| 300 | NetworkInstance::NetworkInstance() { | ||
| 301 | Initialize(); | ||
| 302 | } | ||
| 303 | |||
| 304 | NetworkInstance::~NetworkInstance() { | ||
| 305 | Finalize(); | ||
| 306 | } | ||
| 307 | |||
| 308 | std::pair<IPv4Address, Errno> GetHostIPv4Address() { | ||
| 309 | std::array<char, 256> name{}; | ||
| 310 | if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { | ||
| 311 | UNIMPLEMENTED_MSG("Unhandled gethostname error"); | ||
| 312 | return {IPv4Address{}, Errno::SUCCESS}; | ||
| 313 | } | ||
| 314 | |||
| 315 | hostent* const ent = gethostbyname(name.data()); | ||
| 316 | if (!ent) { | ||
| 317 | UNIMPLEMENTED_MSG("Unhandled gethostbyname error"); | ||
| 318 | return {IPv4Address{}, Errno::SUCCESS}; | ||
| 319 | } | ||
| 320 | if (ent->h_addr_list == nullptr) { | ||
| 321 | UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); | ||
| 322 | return {IPv4Address{}, Errno::SUCCESS}; | ||
| 323 | } | ||
| 324 | if (ent->h_length != sizeof(in_addr)) { | ||
| 325 | UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length); | ||
| 326 | } | ||
| 327 | |||
| 328 | in_addr addr; | ||
| 329 | std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); | ||
| 330 | return {TranslateIPv4(addr), Errno::SUCCESS}; | ||
| 331 | } | ||
| 332 | |||
| 333 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | ||
| 334 | const size_t num = pollfds.size(); | ||
| 335 | |||
| 336 | std::vector<WSAPOLLFD> host_pollfds(pollfds.size()); | ||
| 337 | std::transform(pollfds.begin(), pollfds.end(), host_pollfds.begin(), [](PollFD fd) { | ||
| 338 | WSAPOLLFD result; | ||
| 339 | result.fd = fd.socket->fd; | ||
| 340 | result.events = TranslatePollEvents(fd.events); | ||
| 341 | result.revents = 0; | ||
| 342 | return result; | ||
| 343 | }); | ||
| 344 | |||
| 345 | const int result = WSAPoll(host_pollfds.data(), static_cast<ULONG>(num), timeout); | ||
| 346 | if (result == 0) { | ||
| 347 | ASSERT(std::all_of(host_pollfds.begin(), host_pollfds.end(), | ||
| 348 | [](WSAPOLLFD fd) { return fd.revents == 0; })); | ||
| 349 | return {0, Errno::SUCCESS}; | ||
| 350 | } | ||
| 351 | |||
| 352 | for (size_t i = 0; i < num; ++i) { | ||
| 353 | pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents); | ||
| 354 | } | ||
| 355 | |||
| 356 | if (result > 0) { | ||
| 357 | return {result, Errno::SUCCESS}; | ||
| 358 | } | ||
| 359 | |||
| 360 | ASSERT(result == SOCKET_ERROR); | ||
| 361 | |||
| 362 | const int ec = LastError(); | ||
| 363 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 364 | return {-1, Errno::SUCCESS}; | ||
| 365 | } | ||
| 366 | |||
| 367 | Socket::~Socket() { | ||
| 368 | if (fd == INVALID_SOCKET) { | ||
| 369 | return; | ||
| 370 | } | ||
| 371 | (void)closesocket(fd); | ||
| 372 | fd = INVALID_SOCKET; | ||
| 373 | } | ||
| 374 | |||
| 375 | Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {} | ||
| 376 | |||
| 377 | Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { | ||
| 378 | fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); | ||
| 379 | if (fd != INVALID_SOCKET) { | ||
| 380 | return Errno::SUCCESS; | ||
| 381 | } | ||
| 382 | |||
| 383 | const int ec = LastError(); | ||
| 384 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 385 | return Errno::SUCCESS; | ||
| 386 | } | ||
| 387 | |||
| 388 | std::pair<Socket::AcceptResult, Errno> Socket::Accept() { | ||
| 389 | sockaddr addr; | ||
| 390 | socklen_t addrlen = sizeof(addr); | ||
| 391 | const SOCKET new_socket = accept(fd, &addr, &addrlen); | ||
| 392 | |||
| 393 | if (new_socket == INVALID_SOCKET) { | ||
| 394 | const int ec = LastError(); | ||
| 395 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 396 | return {AcceptResult{}, Errno::SUCCESS}; | ||
| 397 | } | ||
| 398 | |||
| 399 | AcceptResult result; | ||
| 400 | result.socket = std::make_unique<Socket>(); | ||
| 401 | result.socket->fd = new_socket; | ||
| 402 | |||
| 403 | ASSERT(addrlen == sizeof(sockaddr_in)); | ||
| 404 | result.sockaddr_in = TranslateToSockAddrIn(addr); | ||
| 405 | |||
| 406 | return {std::move(result), Errno::SUCCESS}; | ||
| 407 | } | ||
| 408 | |||
| 409 | Errno Socket::Connect(SockAddrIn addr_in) { | ||
| 410 | const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in); | ||
| 411 | if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != INVALID_SOCKET) { | ||
| 412 | return Errno::SUCCESS; | ||
| 413 | } | ||
| 414 | |||
| 415 | switch (const int ec = LastError()) { | ||
| 416 | case WSAEWOULDBLOCK: | ||
| 417 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 418 | return Errno::AGAIN; | ||
| 419 | default: | ||
| 420 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 421 | return Errno::SUCCESS; | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | std::pair<SockAddrIn, Errno> Socket::GetPeerName() { | ||
| 426 | sockaddr addr; | ||
| 427 | socklen_t addrlen = sizeof(addr); | ||
| 428 | if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { | ||
| 429 | const int ec = LastError(); | ||
| 430 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 431 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 432 | } | ||
| 433 | |||
| 434 | ASSERT(addrlen == sizeof(sockaddr_in)); | ||
| 435 | return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; | ||
| 436 | } | ||
| 437 | |||
| 438 | std::pair<SockAddrIn, Errno> Socket::GetSockName() { | ||
| 439 | sockaddr addr; | ||
| 440 | socklen_t addrlen = sizeof(addr); | ||
| 441 | if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { | ||
| 442 | const int ec = LastError(); | ||
| 443 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 444 | return {SockAddrIn{}, Errno::SUCCESS}; | ||
| 445 | } | ||
| 446 | |||
| 447 | ASSERT(addrlen == sizeof(sockaddr_in)); | ||
| 448 | return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; | ||
| 449 | } | ||
| 450 | |||
| 451 | Errno Socket::Bind(SockAddrIn addr) { | ||
| 452 | const sockaddr addr_in = TranslateFromSockAddrIn(addr); | ||
| 453 | if (bind(fd, &addr_in, sizeof(addr_in)) != SOCKET_ERROR) { | ||
| 454 | return Errno::SUCCESS; | ||
| 455 | } | ||
| 456 | |||
| 457 | const int ec = LastError(); | ||
| 458 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 459 | return Errno::SUCCESS; | ||
| 460 | } | ||
| 461 | |||
| 462 | Errno Socket::Listen(s32 backlog) { | ||
| 463 | if (listen(fd, backlog) != SOCKET_ERROR) { | ||
| 464 | return Errno::SUCCESS; | ||
| 465 | } | ||
| 466 | |||
| 467 | const int ec = LastError(); | ||
| 468 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 469 | return Errno::SUCCESS; | ||
| 470 | } | ||
| 471 | |||
| 472 | Errno Socket::Shutdown(ShutdownHow how) { | ||
| 473 | int host_how = 0; | ||
| 474 | switch (how) { | ||
| 475 | case ShutdownHow::RD: | ||
| 476 | host_how = SD_RECEIVE; | ||
| 477 | break; | ||
| 478 | case ShutdownHow::WR: | ||
| 479 | host_how = SD_SEND; | ||
| 480 | break; | ||
| 481 | case ShutdownHow::RDWR: | ||
| 482 | host_how = SD_BOTH; | ||
| 483 | break; | ||
| 484 | default: | ||
| 485 | UNIMPLEMENTED_MSG("Unimplemented flag how={}", static_cast<int>(how)); | ||
| 486 | return Errno::SUCCESS; | ||
| 487 | } | ||
| 488 | if (shutdown(fd, host_how) != SOCKET_ERROR) { | ||
| 489 | return Errno::SUCCESS; | ||
| 490 | } | ||
| 491 | |||
| 492 | switch (const int ec = LastError()) { | ||
| 493 | case WSAENOTCONN: | ||
| 494 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 495 | return Errno::NOTCONN; | ||
| 496 | default: | ||
| 497 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 498 | return Errno::SUCCESS; | ||
| 499 | } | ||
| 500 | } | ||
| 501 | |||
| 502 | std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { | ||
| 503 | ASSERT(flags == 0); | ||
| 504 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 505 | |||
| 506 | const int result = | ||
| 507 | recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0); | ||
| 508 | if (result != SOCKET_ERROR) { | ||
| 509 | return {result, Errno::SUCCESS}; | ||
| 510 | } | ||
| 511 | |||
| 512 | switch (const int ec = LastError()) { | ||
| 513 | case WSAEWOULDBLOCK: | ||
| 514 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 515 | return {-1, Errno::AGAIN}; | ||
| 516 | case WSAENOTCONN: | ||
| 517 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 518 | return {-1, Errno::NOTCONN}; | ||
| 519 | default: | ||
| 520 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 521 | return {0, Errno::SUCCESS}; | ||
| 522 | } | ||
| 523 | } | ||
| 524 | |||
| 525 | std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { | ||
| 526 | ASSERT(flags == 0); | ||
| 527 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 528 | |||
| 529 | sockaddr addr_in{}; | ||
| 530 | socklen_t addrlen = sizeof(addr_in); | ||
| 531 | socklen_t* const p_addrlen = addr ? &addrlen : nullptr; | ||
| 532 | sockaddr* const p_addr_in = addr ? &addr_in : nullptr; | ||
| 533 | |||
| 534 | const int result = recvfrom(fd, reinterpret_cast<char*>(message.data()), | ||
| 535 | static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); | ||
| 536 | if (result != SOCKET_ERROR) { | ||
| 537 | if (addr) { | ||
| 538 | ASSERT(addrlen == sizeof(addr_in)); | ||
| 539 | *addr = TranslateToSockAddrIn(addr_in); | ||
| 540 | } | ||
| 541 | return {result, Errno::SUCCESS}; | ||
| 542 | } | ||
| 543 | |||
| 544 | switch (const int ec = LastError()) { | ||
| 545 | case WSAEWOULDBLOCK: | ||
| 546 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 547 | return {-1, Errno::AGAIN}; | ||
| 548 | case WSAENOTCONN: | ||
| 549 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 550 | return {-1, Errno::NOTCONN}; | ||
| 551 | default: | ||
| 552 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 553 | return {-1, Errno::SUCCESS}; | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) { | ||
| 558 | ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); | ||
| 559 | ASSERT(flags == 0); | ||
| 560 | |||
| 561 | const int result = send(fd, reinterpret_cast<const char*>(message.data()), | ||
| 562 | static_cast<int>(message.size()), 0); | ||
| 563 | if (result != SOCKET_ERROR) { | ||
| 564 | return {result, Errno::SUCCESS}; | ||
| 565 | } | ||
| 566 | |||
| 567 | const int ec = LastError(); | ||
| 568 | switch (ec) { | ||
| 569 | case WSAEWOULDBLOCK: | ||
| 570 | LOG_DEBUG(Service, "EAGAIN generated"); | ||
| 571 | return {-1, Errno::AGAIN}; | ||
| 572 | case WSAENOTCONN: | ||
| 573 | LOG_ERROR(Service, "ENOTCONN generated"); | ||
| 574 | return {-1, Errno::NOTCONN}; | ||
| 575 | default: | ||
| 576 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 577 | return {-1, Errno::SUCCESS}; | ||
| 578 | } | ||
| 579 | } | ||
| 580 | |||
| 581 | std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message, | ||
| 582 | const SockAddrIn* addr) { | ||
| 583 | ASSERT(flags == 0); | ||
| 584 | |||
| 585 | const sockaddr* to = nullptr; | ||
| 586 | const int tolen = addr ? 0 : sizeof(sockaddr); | ||
| 587 | sockaddr host_addr_in; | ||
| 588 | |||
| 589 | if (addr) { | ||
| 590 | host_addr_in = TranslateFromSockAddrIn(*addr); | ||
| 591 | to = &host_addr_in; | ||
| 592 | } | ||
| 593 | |||
| 594 | const int result = sendto(fd, reinterpret_cast<const char*>(message.data()), | ||
| 595 | static_cast<int>(message.size()), 0, to, tolen); | ||
| 596 | if (result != SOCKET_ERROR) { | ||
| 597 | return {result, Errno::SUCCESS}; | ||
| 598 | } | ||
| 599 | |||
| 600 | const int ec = LastError(); | ||
| 601 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 602 | return {-1, Errno::SUCCESS}; | ||
| 603 | } | ||
| 604 | |||
| 605 | Errno Socket::Close() { | ||
| 606 | [[maybe_unused]] const int result = closesocket(fd); | ||
| 607 | ASSERT(result == 0); | ||
| 608 | fd = INVALID_SOCKET; | ||
| 609 | |||
| 610 | return Errno::SUCCESS; | ||
| 611 | } | ||
| 612 | |||
| 613 | Errno Socket::SetLinger(bool enable, u32 linger) { | ||
| 614 | return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); | ||
| 615 | } | ||
| 616 | |||
| 617 | Errno Socket::SetReuseAddr(bool enable) { | ||
| 618 | return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0); | ||
| 619 | } | ||
| 620 | |||
| 621 | Errno Socket::SetBroadcast(bool enable) { | ||
| 622 | return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); | ||
| 623 | } | ||
| 624 | |||
| 625 | Errno Socket::SetSndBuf(u32 value) { | ||
| 626 | return SetSockOpt(fd, SO_SNDBUF, value); | ||
| 627 | } | ||
| 628 | |||
| 629 | Errno Socket::SetRcvBuf(u32 value) { | ||
| 630 | return SetSockOpt(fd, SO_RCVBUF, value); | ||
| 631 | } | ||
| 632 | |||
| 633 | Errno Socket::SetSndTimeo(u32 value) { | ||
| 634 | return SetSockOpt(fd, SO_SNDTIMEO, value); | ||
| 635 | } | ||
| 636 | |||
| 637 | Errno Socket::SetRcvTimeo(u32 value) { | ||
| 638 | return SetSockOpt(fd, SO_RCVTIMEO, value); | ||
| 639 | } | ||
| 640 | |||
| 641 | Errno Socket::SetNonBlock(bool enable) { | ||
| 642 | if (EnableNonBlock(fd, enable)) { | ||
| 643 | return Errno::SUCCESS; | ||
| 644 | } | ||
| 645 | const int ec = LastError(); | ||
| 646 | UNREACHABLE_MSG("Unhandled host socket error={}", ec); | ||
| 647 | return Errno::SUCCESS; | ||
| 648 | } | ||
| 649 | |||
| 650 | bool Socket::IsOpened() const { | ||
| 651 | return fd != INVALID_SOCKET; | ||
| 652 | } | ||
| 653 | |||
| 654 | } // namespace Network | ||
diff --git a/src/core/network/network.h b/src/core/network/network.h new file mode 100644 index 000000000..0622e4593 --- /dev/null +++ b/src/core/network/network.h | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2020 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 <array> | ||
| 8 | #include <utility> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Network { | ||
| 13 | |||
| 14 | class Socket; | ||
| 15 | |||
| 16 | /// Error code for network functions | ||
| 17 | enum class Errno { | ||
| 18 | SUCCESS, | ||
| 19 | BADF, | ||
| 20 | INVAL, | ||
| 21 | MFILE, | ||
| 22 | NOTCONN, | ||
| 23 | AGAIN, | ||
| 24 | }; | ||
| 25 | |||
| 26 | /// Address families | ||
| 27 | enum class Domain { | ||
| 28 | INET, ///< Address family for IPv4 | ||
| 29 | }; | ||
| 30 | |||
| 31 | /// Socket types | ||
| 32 | enum class Type { | ||
| 33 | STREAM, | ||
| 34 | DGRAM, | ||
| 35 | RAW, | ||
| 36 | SEQPACKET, | ||
| 37 | }; | ||
| 38 | |||
| 39 | /// Protocol values for sockets | ||
| 40 | enum class Protocol { | ||
| 41 | ICMP, | ||
| 42 | TCP, | ||
| 43 | UDP, | ||
| 44 | }; | ||
| 45 | |||
| 46 | /// Shutdown mode | ||
| 47 | enum class ShutdownHow { | ||
| 48 | RD, | ||
| 49 | WR, | ||
| 50 | RDWR, | ||
| 51 | }; | ||
| 52 | |||
| 53 | /// Array of IPv4 address | ||
| 54 | using IPv4Address = std::array<u8, 4>; | ||
| 55 | |||
| 56 | /// Cross-platform sockaddr structure | ||
| 57 | struct SockAddrIn { | ||
| 58 | Domain family; | ||
| 59 | IPv4Address ip; | ||
| 60 | u16 portno; | ||
| 61 | }; | ||
| 62 | |||
| 63 | /// Cross-platform poll fd structure | ||
| 64 | struct PollFD { | ||
| 65 | Socket* socket; | ||
| 66 | u16 events; | ||
| 67 | u16 revents; | ||
| 68 | }; | ||
| 69 | |||
| 70 | constexpr u16 POLL_IN = 1 << 0; | ||
| 71 | constexpr u16 POLL_PRI = 1 << 1; | ||
| 72 | constexpr u16 POLL_OUT = 1 << 2; | ||
| 73 | constexpr u16 POLL_ERR = 1 << 3; | ||
| 74 | constexpr u16 POLL_HUP = 1 << 4; | ||
| 75 | constexpr u16 POLL_NVAL = 1 << 5; | ||
| 76 | |||
| 77 | class NetworkInstance { | ||
| 78 | public: | ||
| 79 | explicit NetworkInstance(); | ||
| 80 | ~NetworkInstance(); | ||
| 81 | }; | ||
| 82 | |||
| 83 | /// @brief Returns host's IPv4 address | ||
| 84 | /// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code | ||
| 85 | std::pair<IPv4Address, Errno> GetHostIPv4Address(); | ||
| 86 | |||
| 87 | } // namespace Network | ||
diff --git a/src/core/network/sockets.h b/src/core/network/sockets.h new file mode 100644 index 000000000..7bdff0fe4 --- /dev/null +++ b/src/core/network/sockets.h | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | // Copyright 2020 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 <memory> | ||
| 8 | #include <utility> | ||
| 9 | |||
| 10 | #if defined(_WIN32) | ||
| 11 | #include <winsock.h> | ||
| 12 | #elif !defined(__unix__) | ||
| 13 | #error "Platform not implemented" | ||
| 14 | #endif | ||
| 15 | |||
| 16 | #include "common/common_types.h" | ||
| 17 | #include "core/network/network.h" | ||
| 18 | |||
| 19 | // TODO: C++20 Replace std::vector usages with std::span | ||
| 20 | |||
| 21 | namespace Network { | ||
| 22 | |||
| 23 | class Socket { | ||
| 24 | public: | ||
| 25 | struct AcceptResult { | ||
| 26 | std::unique_ptr<Socket> socket; | ||
| 27 | SockAddrIn sockaddr_in; | ||
| 28 | }; | ||
| 29 | |||
| 30 | explicit Socket() = default; | ||
| 31 | ~Socket(); | ||
| 32 | |||
| 33 | Socket(const Socket&) = delete; | ||
| 34 | Socket& operator=(const Socket&) = delete; | ||
| 35 | |||
| 36 | Socket(Socket&& rhs) noexcept; | ||
| 37 | |||
| 38 | // Avoid closing sockets implicitly | ||
| 39 | Socket& operator=(Socket&&) noexcept = delete; | ||
| 40 | |||
| 41 | Errno Initialize(Domain domain, Type type, Protocol protocol); | ||
| 42 | |||
| 43 | Errno Close(); | ||
| 44 | |||
| 45 | std::pair<AcceptResult, Errno> Accept(); | ||
| 46 | |||
| 47 | Errno Connect(SockAddrIn addr_in); | ||
| 48 | |||
| 49 | std::pair<SockAddrIn, Errno> GetPeerName(); | ||
| 50 | |||
| 51 | std::pair<SockAddrIn, Errno> GetSockName(); | ||
| 52 | |||
| 53 | Errno Bind(SockAddrIn addr); | ||
| 54 | |||
| 55 | Errno Listen(s32 backlog); | ||
| 56 | |||
| 57 | Errno Shutdown(ShutdownHow how); | ||
| 58 | |||
| 59 | std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message); | ||
| 60 | |||
| 61 | std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr); | ||
| 62 | |||
| 63 | std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags); | ||
| 64 | |||
| 65 | std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr); | ||
| 66 | |||
| 67 | Errno SetLinger(bool enable, u32 linger); | ||
| 68 | |||
| 69 | Errno SetReuseAddr(bool enable); | ||
| 70 | |||
| 71 | Errno SetBroadcast(bool enable); | ||
| 72 | |||
| 73 | Errno SetSndBuf(u32 value); | ||
| 74 | |||
| 75 | Errno SetRcvBuf(u32 value); | ||
| 76 | |||
| 77 | Errno SetSndTimeo(u32 value); | ||
| 78 | |||
| 79 | Errno SetRcvTimeo(u32 value); | ||
| 80 | |||
| 81 | Errno SetNonBlock(bool enable); | ||
| 82 | |||
| 83 | bool IsOpened() const; | ||
| 84 | |||
| 85 | #if defined(_WIN32) | ||
| 86 | SOCKET fd = INVALID_SOCKET; | ||
| 87 | #elif defined(__unix__) | ||
| 88 | int fd = -1; | ||
| 89 | #endif | ||
| 90 | }; | ||
| 91 | |||
| 92 | std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout); | ||
| 93 | |||
| 94 | } // namespace Network | ||
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 29339ead7..b93396a80 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -38,11 +38,11 @@ PerfStats::~PerfStats() { | |||
| 38 | std::ostringstream stream; | 38 | std::ostringstream stream; |
| 39 | std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, | 39 | std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, |
| 40 | std::ostream_iterator<double>(stream, "\n")); | 40 | std::ostream_iterator<double>(stream, "\n")); |
| 41 | const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); | 41 | const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir); |
| 42 | // %F Date format expanded is "%Y-%m-%d" | 42 | // %F Date format expanded is "%Y-%m-%d" |
| 43 | const std::string filename = | 43 | const std::string filename = |
| 44 | fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); | 44 | fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); |
| 45 | FileUtil::IOFile file(filename, "w"); | 45 | Common::FS::IOFile file(filename, "w"); |
| 46 | file.WriteString(stream.str()); | 46 | file.WriteString(stream.str()); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| @@ -74,15 +74,16 @@ void PerfStats::EndGameFrame() { | |||
| 74 | game_frames += 1; | 74 | game_frames += 1; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | double PerfStats::GetMeanFrametime() { | 77 | double PerfStats::GetMeanFrametime() const { |
| 78 | std::lock_guard lock{object_mutex}; | 78 | std::lock_guard lock{object_mutex}; |
| 79 | 79 | ||
| 80 | if (current_index <= IgnoreFrames) { | 80 | if (current_index <= IgnoreFrames) { |
| 81 | return 0; | 81 | return 0; |
| 82 | } | 82 | } |
| 83 | |||
| 83 | const double sum = std::accumulate(perf_history.begin() + IgnoreFrames, | 84 | const double sum = std::accumulate(perf_history.begin() + IgnoreFrames, |
| 84 | perf_history.begin() + current_index, 0.0); | 85 | perf_history.begin() + current_index, 0.0); |
| 85 | return sum / (current_index - IgnoreFrames); | 86 | return sum / static_cast<double>(current_index - IgnoreFrames); |
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { | 89 | PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { |
| @@ -94,12 +95,13 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us | |||
| 94 | 95 | ||
| 95 | const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; | 96 | const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval; |
| 96 | 97 | ||
| 97 | PerfStatsResults results{}; | 98 | const PerfStatsResults results{ |
| 98 | results.system_fps = static_cast<double>(system_frames) / interval; | 99 | .system_fps = static_cast<double>(system_frames) / interval, |
| 99 | results.game_fps = static_cast<double>(game_frames) / interval; | 100 | .game_fps = static_cast<double>(game_frames) / interval, |
| 100 | results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / | 101 | .frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() / |
| 101 | static_cast<double>(system_frames); | 102 | static_cast<double>(system_frames), |
| 102 | results.emulation_speed = system_us_per_second.count() / 1'000'000.0; | 103 | .emulation_speed = system_us_per_second.count() / 1'000'000.0, |
| 104 | }; | ||
| 103 | 105 | ||
| 104 | // Reset counters | 106 | // Reset counters |
| 105 | reset_point = now; | 107 | reset_point = now; |
| @@ -111,7 +113,7 @@ PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us | |||
| 111 | return results; | 113 | return results; |
| 112 | } | 114 | } |
| 113 | 115 | ||
| 114 | double PerfStats::GetLastFrameTimeScale() { | 116 | double PerfStats::GetLastFrameTimeScale() const { |
| 115 | std::lock_guard lock{object_mutex}; | 117 | std::lock_guard lock{object_mutex}; |
| 116 | 118 | ||
| 117 | constexpr double FRAME_LENGTH = 1.0 / 60; | 119 | constexpr double FRAME_LENGTH = 1.0 / 60; |
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index d9a64f072..69256b960 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h | |||
| @@ -30,7 +30,6 @@ struct PerfStatsResults { | |||
| 30 | class PerfStats { | 30 | class PerfStats { |
| 31 | public: | 31 | public: |
| 32 | explicit PerfStats(u64 title_id); | 32 | explicit PerfStats(u64 title_id); |
| 33 | |||
| 34 | ~PerfStats(); | 33 | ~PerfStats(); |
| 35 | 34 | ||
| 36 | using Clock = std::chrono::high_resolution_clock; | 35 | using Clock = std::chrono::high_resolution_clock; |
| @@ -42,18 +41,18 @@ public: | |||
| 42 | PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); | 41 | PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); |
| 43 | 42 | ||
| 44 | /** | 43 | /** |
| 45 | * Returns the Arthimetic Mean of all frametime values stored in the performance history. | 44 | * Returns the arithmetic mean of all frametime values stored in the performance history. |
| 46 | */ | 45 | */ |
| 47 | double GetMeanFrametime(); | 46 | double GetMeanFrametime() const; |
| 48 | 47 | ||
| 49 | /** | 48 | /** |
| 50 | * Gets the ratio between walltime and the emulated time of the previous system frame. This is | 49 | * Gets the ratio between walltime and the emulated time of the previous system frame. This is |
| 51 | * useful for scaling inputs or outputs moving between the two time domains. | 50 | * useful for scaling inputs or outputs moving between the two time domains. |
| 52 | */ | 51 | */ |
| 53 | double GetLastFrameTimeScale(); | 52 | double GetLastFrameTimeScale() const; |
| 54 | 53 | ||
| 55 | private: | 54 | private: |
| 56 | std::mutex object_mutex{}; | 55 | mutable std::mutex object_mutex; |
| 57 | 56 | ||
| 58 | /// Title ID for the game that is running. 0 if there is no game running yet | 57 | /// Title ID for the game that is running. 0 if there is no game running yet |
| 59 | u64 title_id{0}; | 58 | u64 title_id{0}; |
| @@ -61,7 +60,7 @@ private: | |||
| 61 | std::size_t current_index{0}; | 60 | std::size_t current_index{0}; |
| 62 | /// Stores an hour of historical frametime data useful for processing and tracking performance | 61 | /// Stores an hour of historical frametime data useful for processing and tracking performance |
| 63 | /// regressions with code changes. | 62 | /// regressions with code changes. |
| 64 | std::array<double, 216000> perf_history = {}; | 63 | std::array<double, 216000> perf_history{}; |
| 65 | 64 | ||
| 66 | /// Point when the cumulative counters were reset | 65 | /// Point when the cumulative counters were reset |
| 67 | Clock::time_point reset_point = Clock::now(); | 66 | Clock::time_point reset_point = Clock::now(); |
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 76cfa5a17..0becdf642 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp | |||
| @@ -28,8 +28,9 @@ | |||
| 28 | namespace { | 28 | namespace { |
| 29 | 29 | ||
| 30 | std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { | 30 | std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { |
| 31 | return fmt::format("{}{}/{:016X}_{}.json", FileUtil::GetUserPath(FileUtil::UserPath::LogDir), | 31 | return fmt::format("{}{}/{:016X}_{}.json", |
| 32 | type, title_id, timestamp); | 32 | Common::FS::GetUserPath(Common::FS::UserPath::LogDir), type, title_id, |
| 33 | timestamp); | ||
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | std::string GetTimestamp() { | 36 | std::string GetTimestamp() { |
| @@ -40,13 +41,13 @@ std::string GetTimestamp() { | |||
| 40 | using namespace nlohmann; | 41 | using namespace nlohmann; |
| 41 | 42 | ||
| 42 | void SaveToFile(json json, const std::string& filename) { | 43 | void SaveToFile(json json, const std::string& filename) { |
| 43 | if (!FileUtil::CreateFullPath(filename)) { | 44 | if (!Common::FS::CreateFullPath(filename)) { |
| 44 | LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); | 45 | LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); |
| 45 | return; | 46 | return; |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | std::ofstream file( | 49 | std::ofstream file( |
| 49 | FileUtil::SanitizePath(filename, FileUtil::DirectorySeparator::PlatformDefault)); | 50 | Common::FS::SanitizePath(filename, Common::FS::DirectorySeparator::PlatformDefault)); |
| 50 | file << std::setw(4) << json << std::endl; | 51 | file << std::setw(4) << json << std::endl; |
| 51 | } | 52 | } |
| 52 | 53 | ||
diff --git a/src/core/settings.cpp b/src/core/settings.cpp index e8a6f2a6e..28d3f9099 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp | |||
| @@ -13,56 +13,6 @@ | |||
| 13 | 13 | ||
| 14 | namespace Settings { | 14 | namespace Settings { |
| 15 | 15 | ||
| 16 | namespace NativeButton { | ||
| 17 | const std::array<const char*, NumButtons> mapping = {{ | ||
| 18 | "button_a", | ||
| 19 | "button_b", | ||
| 20 | "button_x", | ||
| 21 | "button_y", | ||
| 22 | "button_lstick", | ||
| 23 | "button_rstick", | ||
| 24 | "button_l", | ||
| 25 | "button_r", | ||
| 26 | "button_zl", | ||
| 27 | "button_zr", | ||
| 28 | "button_plus", | ||
| 29 | "button_minus", | ||
| 30 | "button_dleft", | ||
| 31 | "button_dup", | ||
| 32 | "button_dright", | ||
| 33 | "button_ddown", | ||
| 34 | "button_lstick_left", | ||
| 35 | "button_lstick_up", | ||
| 36 | "button_lstick_right", | ||
| 37 | "button_lstick_down", | ||
| 38 | "button_rstick_left", | ||
| 39 | "button_rstick_up", | ||
| 40 | "button_rstick_right", | ||
| 41 | "button_rstick_down", | ||
| 42 | "button_sl", | ||
| 43 | "button_sr", | ||
| 44 | "button_home", | ||
| 45 | "button_screenshot", | ||
| 46 | }}; | ||
| 47 | } | ||
| 48 | |||
| 49 | namespace NativeAnalog { | ||
| 50 | const std::array<const char*, NumAnalogs> mapping = {{ | ||
| 51 | "lstick", | ||
| 52 | "rstick", | ||
| 53 | }}; | ||
| 54 | } | ||
| 55 | |||
| 56 | namespace NativeMouseButton { | ||
| 57 | const std::array<const char*, NumMouseButtons> mapping = {{ | ||
| 58 | "left", | ||
| 59 | "right", | ||
| 60 | "middle", | ||
| 61 | "forward", | ||
| 62 | "back", | ||
| 63 | }}; | ||
| 64 | } | ||
| 65 | |||
| 66 | Values values = {}; | 16 | Values values = {}; |
| 67 | bool configuring_global = true; | 17 | bool configuring_global = true; |
| 68 | 18 | ||
| @@ -115,13 +65,14 @@ void LogSettings() { | |||
| 115 | values.use_asynchronous_gpu_emulation.GetValue()); | 65 | values.use_asynchronous_gpu_emulation.GetValue()); |
| 116 | log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); | 66 | log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); |
| 117 | log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); | 67 | log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); |
| 68 | log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); | ||
| 118 | log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); | 69 | log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); |
| 119 | log_setting("Audio_OutputEngine", values.sink_id); | 70 | log_setting("Audio_OutputEngine", values.sink_id); |
| 120 | log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); | 71 | log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); |
| 121 | log_setting("Audio_OutputDevice", values.audio_device_id); | 72 | log_setting("Audio_OutputDevice", values.audio_device_id); |
| 122 | log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); | 73 | log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); |
| 123 | log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); | 74 | log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)); |
| 124 | log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); | 75 | log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)); |
| 125 | log_setting("Debugging_UseGdbstub", values.use_gdbstub); | 76 | log_setting("Debugging_UseGdbstub", values.use_gdbstub); |
| 126 | log_setting("Debugging_GdbstubPort", values.gdbstub_port); | 77 | log_setting("Debugging_GdbstubPort", values.gdbstub_port); |
| 127 | log_setting("Debugging_ProgramArgs", values.program_args); | 78 | log_setting("Debugging_ProgramArgs", values.program_args); |
| @@ -170,8 +121,8 @@ void RestoreGlobalState() { | |||
| 170 | values.use_asynchronous_gpu_emulation.SetGlobal(true); | 121 | values.use_asynchronous_gpu_emulation.SetGlobal(true); |
| 171 | values.use_vsync.SetGlobal(true); | 122 | values.use_vsync.SetGlobal(true); |
| 172 | values.use_assembly_shaders.SetGlobal(true); | 123 | values.use_assembly_shaders.SetGlobal(true); |
| 124 | values.use_asynchronous_shaders.SetGlobal(true); | ||
| 173 | values.use_fast_gpu_time.SetGlobal(true); | 125 | values.use_fast_gpu_time.SetGlobal(true); |
| 174 | values.force_30fps_mode.SetGlobal(true); | ||
| 175 | values.bg_red.SetGlobal(true); | 126 | values.bg_red.SetGlobal(true); |
| 176 | values.bg_green.SetGlobal(true); | 127 | values.bg_green.SetGlobal(true); |
| 177 | values.bg_blue.SetGlobal(true); | 128 | values.bg_blue.SetGlobal(true); |
diff --git a/src/core/settings.h b/src/core/settings.h index a64debd25..9834f44bb 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -12,340 +12,10 @@ | |||
| 12 | #include <string> | 12 | #include <string> |
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "input_common/settings.h" | ||
| 15 | 16 | ||
| 16 | namespace Settings { | 17 | namespace Settings { |
| 17 | 18 | ||
| 18 | namespace NativeButton { | ||
| 19 | enum Values { | ||
| 20 | A, | ||
| 21 | B, | ||
| 22 | X, | ||
| 23 | Y, | ||
| 24 | LStick, | ||
| 25 | RStick, | ||
| 26 | L, | ||
| 27 | R, | ||
| 28 | ZL, | ||
| 29 | ZR, | ||
| 30 | Plus, | ||
| 31 | Minus, | ||
| 32 | |||
| 33 | DLeft, | ||
| 34 | DUp, | ||
| 35 | DRight, | ||
| 36 | DDown, | ||
| 37 | |||
| 38 | LStick_Left, | ||
| 39 | LStick_Up, | ||
| 40 | LStick_Right, | ||
| 41 | LStick_Down, | ||
| 42 | |||
| 43 | RStick_Left, | ||
| 44 | RStick_Up, | ||
| 45 | RStick_Right, | ||
| 46 | RStick_Down, | ||
| 47 | |||
| 48 | SL, | ||
| 49 | SR, | ||
| 50 | |||
| 51 | Home, | ||
| 52 | Screenshot, | ||
| 53 | |||
| 54 | NumButtons, | ||
| 55 | }; | ||
| 56 | |||
| 57 | constexpr int BUTTON_HID_BEGIN = A; | ||
| 58 | constexpr int BUTTON_NS_BEGIN = Home; | ||
| 59 | |||
| 60 | constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN; | ||
| 61 | constexpr int BUTTON_NS_END = NumButtons; | ||
| 62 | |||
| 63 | constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; | ||
| 64 | constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; | ||
| 65 | |||
| 66 | extern const std::array<const char*, NumButtons> mapping; | ||
| 67 | |||
| 68 | } // namespace NativeButton | ||
| 69 | |||
| 70 | namespace NativeAnalog { | ||
| 71 | enum Values { | ||
| 72 | LStick, | ||
| 73 | RStick, | ||
| 74 | |||
| 75 | NumAnalogs, | ||
| 76 | }; | ||
| 77 | |||
| 78 | constexpr int STICK_HID_BEGIN = LStick; | ||
| 79 | constexpr int STICK_HID_END = NumAnalogs; | ||
| 80 | constexpr int NUM_STICKS_HID = NumAnalogs; | ||
| 81 | |||
| 82 | extern const std::array<const char*, NumAnalogs> mapping; | ||
| 83 | } // namespace NativeAnalog | ||
| 84 | |||
| 85 | namespace NativeMouseButton { | ||
| 86 | enum Values { | ||
| 87 | Left, | ||
| 88 | Right, | ||
| 89 | Middle, | ||
| 90 | Forward, | ||
| 91 | Back, | ||
| 92 | |||
| 93 | NumMouseButtons, | ||
| 94 | }; | ||
| 95 | |||
| 96 | constexpr int MOUSE_HID_BEGIN = Left; | ||
| 97 | constexpr int MOUSE_HID_END = NumMouseButtons; | ||
| 98 | constexpr int NUM_MOUSE_HID = NumMouseButtons; | ||
| 99 | |||
| 100 | extern const std::array<const char*, NumMouseButtons> mapping; | ||
| 101 | } // namespace NativeMouseButton | ||
| 102 | |||
| 103 | namespace NativeKeyboard { | ||
| 104 | enum Keys { | ||
| 105 | None, | ||
| 106 | Error, | ||
| 107 | |||
| 108 | A = 4, | ||
| 109 | B, | ||
| 110 | C, | ||
| 111 | D, | ||
| 112 | E, | ||
| 113 | F, | ||
| 114 | G, | ||
| 115 | H, | ||
| 116 | I, | ||
| 117 | J, | ||
| 118 | K, | ||
| 119 | L, | ||
| 120 | M, | ||
| 121 | N, | ||
| 122 | O, | ||
| 123 | P, | ||
| 124 | Q, | ||
| 125 | R, | ||
| 126 | S, | ||
| 127 | T, | ||
| 128 | U, | ||
| 129 | V, | ||
| 130 | W, | ||
| 131 | X, | ||
| 132 | Y, | ||
| 133 | Z, | ||
| 134 | N1, | ||
| 135 | N2, | ||
| 136 | N3, | ||
| 137 | N4, | ||
| 138 | N5, | ||
| 139 | N6, | ||
| 140 | N7, | ||
| 141 | N8, | ||
| 142 | N9, | ||
| 143 | N0, | ||
| 144 | Enter, | ||
| 145 | Escape, | ||
| 146 | Backspace, | ||
| 147 | Tab, | ||
| 148 | Space, | ||
| 149 | Minus, | ||
| 150 | Equal, | ||
| 151 | LeftBrace, | ||
| 152 | RightBrace, | ||
| 153 | Backslash, | ||
| 154 | Tilde, | ||
| 155 | Semicolon, | ||
| 156 | Apostrophe, | ||
| 157 | Grave, | ||
| 158 | Comma, | ||
| 159 | Dot, | ||
| 160 | Slash, | ||
| 161 | CapsLockKey, | ||
| 162 | |||
| 163 | F1, | ||
| 164 | F2, | ||
| 165 | F3, | ||
| 166 | F4, | ||
| 167 | F5, | ||
| 168 | F6, | ||
| 169 | F7, | ||
| 170 | F8, | ||
| 171 | F9, | ||
| 172 | F10, | ||
| 173 | F11, | ||
| 174 | F12, | ||
| 175 | |||
| 176 | SystemRequest, | ||
| 177 | ScrollLockKey, | ||
| 178 | Pause, | ||
| 179 | Insert, | ||
| 180 | Home, | ||
| 181 | PageUp, | ||
| 182 | Delete, | ||
| 183 | End, | ||
| 184 | PageDown, | ||
| 185 | Right, | ||
| 186 | Left, | ||
| 187 | Down, | ||
| 188 | Up, | ||
| 189 | |||
| 190 | NumLockKey, | ||
| 191 | KPSlash, | ||
| 192 | KPAsterisk, | ||
| 193 | KPMinus, | ||
| 194 | KPPlus, | ||
| 195 | KPEnter, | ||
| 196 | KP1, | ||
| 197 | KP2, | ||
| 198 | KP3, | ||
| 199 | KP4, | ||
| 200 | KP5, | ||
| 201 | KP6, | ||
| 202 | KP7, | ||
| 203 | KP8, | ||
| 204 | KP9, | ||
| 205 | KP0, | ||
| 206 | KPDot, | ||
| 207 | |||
| 208 | Key102, | ||
| 209 | Compose, | ||
| 210 | Power, | ||
| 211 | KPEqual, | ||
| 212 | |||
| 213 | F13, | ||
| 214 | F14, | ||
| 215 | F15, | ||
| 216 | F16, | ||
| 217 | F17, | ||
| 218 | F18, | ||
| 219 | F19, | ||
| 220 | F20, | ||
| 221 | F21, | ||
| 222 | F22, | ||
| 223 | F23, | ||
| 224 | F24, | ||
| 225 | |||
| 226 | Open, | ||
| 227 | Help, | ||
| 228 | Properties, | ||
| 229 | Front, | ||
| 230 | Stop, | ||
| 231 | Repeat, | ||
| 232 | Undo, | ||
| 233 | Cut, | ||
| 234 | Copy, | ||
| 235 | Paste, | ||
| 236 | Find, | ||
| 237 | Mute, | ||
| 238 | VolumeUp, | ||
| 239 | VolumeDown, | ||
| 240 | CapsLockActive, | ||
| 241 | NumLockActive, | ||
| 242 | ScrollLockActive, | ||
| 243 | KPComma, | ||
| 244 | |||
| 245 | KPLeftParenthesis, | ||
| 246 | KPRightParenthesis, | ||
| 247 | |||
| 248 | LeftControlKey = 0xE0, | ||
| 249 | LeftShiftKey, | ||
| 250 | LeftAltKey, | ||
| 251 | LeftMetaKey, | ||
| 252 | RightControlKey, | ||
| 253 | RightShiftKey, | ||
| 254 | RightAltKey, | ||
| 255 | RightMetaKey, | ||
| 256 | |||
| 257 | MediaPlayPause, | ||
| 258 | MediaStopCD, | ||
| 259 | MediaPrevious, | ||
| 260 | MediaNext, | ||
| 261 | MediaEject, | ||
| 262 | MediaVolumeUp, | ||
| 263 | MediaVolumeDown, | ||
| 264 | MediaMute, | ||
| 265 | MediaWebsite, | ||
| 266 | MediaBack, | ||
| 267 | MediaForward, | ||
| 268 | MediaStop, | ||
| 269 | MediaFind, | ||
| 270 | MediaScrollUp, | ||
| 271 | MediaScrollDown, | ||
| 272 | MediaEdit, | ||
| 273 | MediaSleep, | ||
| 274 | MediaCoffee, | ||
| 275 | MediaRefresh, | ||
| 276 | MediaCalculator, | ||
| 277 | |||
| 278 | NumKeyboardKeys, | ||
| 279 | }; | ||
| 280 | |||
| 281 | static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys."); | ||
| 282 | |||
| 283 | enum Modifiers { | ||
| 284 | LeftControl, | ||
| 285 | LeftShift, | ||
| 286 | LeftAlt, | ||
| 287 | LeftMeta, | ||
| 288 | RightControl, | ||
| 289 | RightShift, | ||
| 290 | RightAlt, | ||
| 291 | RightMeta, | ||
| 292 | CapsLock, | ||
| 293 | ScrollLock, | ||
| 294 | NumLock, | ||
| 295 | |||
| 296 | NumKeyboardMods, | ||
| 297 | }; | ||
| 298 | |||
| 299 | constexpr int KEYBOARD_KEYS_HID_BEGIN = None; | ||
| 300 | constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys; | ||
| 301 | constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys; | ||
| 302 | |||
| 303 | constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl; | ||
| 304 | constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods; | ||
| 305 | constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods; | ||
| 306 | |||
| 307 | } // namespace NativeKeyboard | ||
| 308 | |||
| 309 | using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>; | ||
| 310 | using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>; | ||
| 311 | using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>; | ||
| 312 | using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>; | ||
| 313 | using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>; | ||
| 314 | |||
| 315 | constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28; | ||
| 316 | constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A; | ||
| 317 | constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6; | ||
| 318 | constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E; | ||
| 319 | |||
| 320 | enum class ControllerType { | ||
| 321 | ProController, | ||
| 322 | DualJoycon, | ||
| 323 | RightJoycon, | ||
| 324 | LeftJoycon, | ||
| 325 | }; | ||
| 326 | |||
| 327 | struct PlayerInput { | ||
| 328 | bool connected; | ||
| 329 | ControllerType type; | ||
| 330 | ButtonsRaw buttons; | ||
| 331 | AnalogsRaw analogs; | ||
| 332 | |||
| 333 | u32 body_color_right; | ||
| 334 | u32 button_color_right; | ||
| 335 | u32 body_color_left; | ||
| 336 | u32 button_color_left; | ||
| 337 | }; | ||
| 338 | |||
| 339 | struct TouchscreenInput { | ||
| 340 | bool enabled; | ||
| 341 | std::string device; | ||
| 342 | |||
| 343 | u32 finger; | ||
| 344 | u32 diameter_x; | ||
| 345 | u32 diameter_y; | ||
| 346 | u32 rotation_angle; | ||
| 347 | }; | ||
| 348 | |||
| 349 | enum class RendererBackend { | 19 | enum class RendererBackend { |
| 350 | OpenGL = 0, | 20 | OpenGL = 0, |
| 351 | Vulkan = 1, | 21 | Vulkan = 1, |
| @@ -359,7 +29,8 @@ enum class GPUAccuracy : u32 { | |||
| 359 | 29 | ||
| 360 | enum class CPUAccuracy { | 30 | enum class CPUAccuracy { |
| 361 | Accurate = 0, | 31 | Accurate = 0, |
| 362 | DebugMode = 1, | 32 | Unsafe = 1, |
| 33 | DebugMode = 2, | ||
| 363 | }; | 34 | }; |
| 364 | 35 | ||
| 365 | extern bool configuring_global; | 36 | extern bool configuring_global; |
| @@ -396,6 +67,11 @@ private: | |||
| 396 | Type local{}; | 67 | Type local{}; |
| 397 | }; | 68 | }; |
| 398 | 69 | ||
| 70 | struct TouchFromButtonMap { | ||
| 71 | std::string name; | ||
| 72 | std::vector<std::string> buttons; | ||
| 73 | }; | ||
| 74 | |||
| 399 | struct Values { | 75 | struct Values { |
| 400 | // Audio | 76 | // Audio |
| 401 | std::string audio_device_id; | 77 | std::string audio_device_id; |
| @@ -419,6 +95,9 @@ struct Values { | |||
| 419 | bool cpuopt_misc_ir; | 95 | bool cpuopt_misc_ir; |
| 420 | bool cpuopt_reduce_misalign_checks; | 96 | bool cpuopt_reduce_misalign_checks; |
| 421 | 97 | ||
| 98 | bool cpuopt_unsafe_unfuse_fma; | ||
| 99 | bool cpuopt_unsafe_reduce_fp_error; | ||
| 100 | |||
| 422 | // Renderer | 101 | // Renderer |
| 423 | Setting<RendererBackend> renderer_backend; | 102 | Setting<RendererBackend> renderer_backend; |
| 424 | bool renderer_debug; | 103 | bool renderer_debug; |
| @@ -434,7 +113,7 @@ struct Values { | |||
| 434 | Setting<bool> use_asynchronous_gpu_emulation; | 113 | Setting<bool> use_asynchronous_gpu_emulation; |
| 435 | Setting<bool> use_vsync; | 114 | Setting<bool> use_vsync; |
| 436 | Setting<bool> use_assembly_shaders; | 115 | Setting<bool> use_assembly_shaders; |
| 437 | Setting<bool> force_30fps_mode; | 116 | Setting<bool> use_asynchronous_shaders; |
| 438 | Setting<bool> use_fast_gpu_time; | 117 | Setting<bool> use_fast_gpu_time; |
| 439 | 118 | ||
| 440 | Setting<float> bg_red; | 119 | Setting<float> bg_red; |
| @@ -457,6 +136,8 @@ struct Values { | |||
| 457 | // Controls | 136 | // Controls |
| 458 | std::array<PlayerInput, 10> players; | 137 | std::array<PlayerInput, 10> players; |
| 459 | 138 | ||
| 139 | bool use_docked_mode; | ||
| 140 | |||
| 460 | bool mouse_enabled; | 141 | bool mouse_enabled; |
| 461 | std::string mouse_device; | 142 | std::string mouse_device; |
| 462 | MouseButtonsRaw mouse_buttons; | 143 | MouseButtonsRaw mouse_buttons; |
| @@ -469,14 +150,19 @@ struct Values { | |||
| 469 | ButtonsRaw debug_pad_buttons; | 150 | ButtonsRaw debug_pad_buttons; |
| 470 | AnalogsRaw debug_pad_analogs; | 151 | AnalogsRaw debug_pad_analogs; |
| 471 | 152 | ||
| 153 | bool vibration_enabled; | ||
| 154 | |||
| 155 | bool motion_enabled; | ||
| 472 | std::string motion_device; | 156 | std::string motion_device; |
| 157 | std::string touch_device; | ||
| 473 | TouchscreenInput touchscreen; | 158 | TouchscreenInput touchscreen; |
| 474 | std::atomic_bool is_device_reload_pending{true}; | 159 | std::atomic_bool is_device_reload_pending{true}; |
| 160 | bool use_touch_from_button; | ||
| 161 | int touch_from_button_map_index; | ||
| 475 | std::string udp_input_address; | 162 | std::string udp_input_address; |
| 476 | u16 udp_input_port; | 163 | u16 udp_input_port; |
| 477 | u8 udp_pad_index; | 164 | u8 udp_pad_index; |
| 478 | 165 | std::vector<TouchFromButtonMap> touch_from_button_maps; | |
| 479 | bool use_docked_mode; | ||
| 480 | 166 | ||
| 481 | // Data Storage | 167 | // Data Storage |
| 482 | bool use_virtual_sd; | 168 | bool use_virtual_sd; |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 78915e6db..da09c0dbc 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -25,6 +25,8 @@ | |||
| 25 | 25 | ||
| 26 | namespace Core { | 26 | namespace Core { |
| 27 | 27 | ||
| 28 | namespace Telemetry = Common::Telemetry; | ||
| 29 | |||
| 28 | static u64 GenerateTelemetryId() { | 30 | static u64 GenerateTelemetryId() { |
| 29 | u64 telemetry_id{}; | 31 | u64 telemetry_id{}; |
| 30 | 32 | ||
| @@ -70,12 +72,12 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) { | |||
| 70 | 72 | ||
| 71 | u64 GetTelemetryId() { | 73 | u64 GetTelemetryId() { |
| 72 | u64 telemetry_id{}; | 74 | u64 telemetry_id{}; |
| 73 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + | 75 | const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + |
| 74 | "telemetry_id"}; | 76 | "telemetry_id"}; |
| 75 | 77 | ||
| 76 | bool generate_new_id = !FileUtil::Exists(filename); | 78 | bool generate_new_id = !Common::FS::Exists(filename); |
| 77 | if (!generate_new_id) { | 79 | if (!generate_new_id) { |
| 78 | FileUtil::IOFile file(filename, "rb"); | 80 | Common::FS::IOFile file(filename, "rb"); |
| 79 | if (!file.IsOpen()) { | 81 | if (!file.IsOpen()) { |
| 80 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); | 82 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); |
| 81 | return {}; | 83 | return {}; |
| @@ -88,7 +90,7 @@ u64 GetTelemetryId() { | |||
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | if (generate_new_id) { | 92 | if (generate_new_id) { |
| 91 | FileUtil::IOFile file(filename, "wb"); | 93 | Common::FS::IOFile file(filename, "wb"); |
| 92 | if (!file.IsOpen()) { | 94 | if (!file.IsOpen()) { |
| 93 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); | 95 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); |
| 94 | return {}; | 96 | return {}; |
| @@ -102,10 +104,10 @@ u64 GetTelemetryId() { | |||
| 102 | 104 | ||
| 103 | u64 RegenerateTelemetryId() { | 105 | u64 RegenerateTelemetryId() { |
| 104 | const u64 new_telemetry_id{GenerateTelemetryId()}; | 106 | const u64 new_telemetry_id{GenerateTelemetryId()}; |
| 105 | const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + | 107 | const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + |
| 106 | "telemetry_id"}; | 108 | "telemetry_id"}; |
| 107 | 109 | ||
| 108 | FileUtil::IOFile file(filename, "wb"); | 110 | Common::FS::IOFile file(filename, "wb"); |
| 109 | if (!file.IsOpen()) { | 111 | if (!file.IsOpen()) { |
| 110 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); | 112 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); |
| 111 | return {}; | 113 | return {}; |
| @@ -207,6 +209,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { | |||
| 207 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); | 209 | AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue()); |
| 208 | AddField(field_type, "Renderer_UseAssemblyShaders", | 210 | AddField(field_type, "Renderer_UseAssemblyShaders", |
| 209 | Settings::values.use_assembly_shaders.GetValue()); | 211 | Settings::values.use_assembly_shaders.GetValue()); |
| 212 | AddField(field_type, "Renderer_UseAsynchronousShaders", | ||
| 213 | Settings::values.use_asynchronous_shaders.GetValue()); | ||
| 210 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); | 214 | AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode); |
| 211 | } | 215 | } |
| 212 | 216 | ||
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 17ac22377..66789d4bd 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h | |||
| @@ -52,7 +52,7 @@ public: | |||
| 52 | * @param value Value for the field to add. | 52 | * @param value Value for the field to add. |
| 53 | */ | 53 | */ |
| 54 | template <typename T> | 54 | template <typename T> |
| 55 | void AddField(Telemetry::FieldType type, const char* name, T value) { | 55 | void AddField(Common::Telemetry::FieldType type, const char* name, T value) { |
| 56 | field_collection.AddField(type, name, std::move(value)); | 56 | field_collection.AddField(type, name, std::move(value)); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| @@ -63,7 +63,8 @@ public: | |||
| 63 | bool SubmitTestcase(); | 63 | bool SubmitTestcase(); |
| 64 | 64 | ||
| 65 | private: | 65 | private: |
| 66 | Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session | 66 | /// Tracks all added fields for the session |
| 67 | Common::Telemetry::FieldCollection field_collection; | ||
| 67 | }; | 68 | }; |
| 68 | 69 | ||
| 69 | /** | 70 | /** |
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp index 8b0c50d11..5c674a099 100644 --- a/src/core/tools/freezer.cpp +++ b/src/core/tools/freezer.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | namespace Tools { | 14 | namespace Tools { |
| 15 | namespace { | 15 | namespace { |
| 16 | 16 | ||
| 17 | constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60); | 17 | constexpr auto memory_freezer_ns = std::chrono::nanoseconds{1000000000 / 60}; |
| 18 | 18 | ||
| 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { | 19 | u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { |
| 20 | switch (width) { | 20 | switch (width) { |
| @@ -57,8 +57,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m | |||
| 57 | : core_timing{core_timing_}, memory{memory_} { | 57 | : core_timing{core_timing_}, memory{memory_} { |
| 58 | event = Core::Timing::CreateEvent( | 58 | event = Core::Timing::CreateEvent( |
| 59 | "MemoryFreezer::FrameCallback", | 59 | "MemoryFreezer::FrameCallback", |
| 60 | [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); }); | 60 | [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { |
| 61 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | 61 | FrameCallback(user_data, ns_late); |
| 62 | }); | ||
| 63 | core_timing.ScheduleEvent(memory_freezer_ns, event); | ||
| 62 | } | 64 | } |
| 63 | 65 | ||
| 64 | Freezer::~Freezer() { | 66 | Freezer::~Freezer() { |
| @@ -68,7 +70,7 @@ Freezer::~Freezer() { | |||
| 68 | void Freezer::SetActive(bool active) { | 70 | void Freezer::SetActive(bool active) { |
| 69 | if (!this->active.exchange(active)) { | 71 | if (!this->active.exchange(active)) { |
| 70 | FillEntryReads(); | 72 | FillEntryReads(); |
| 71 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); | 73 | core_timing.ScheduleEvent(memory_freezer_ns, event); |
| 72 | LOG_DEBUG(Common_Memory, "Memory freezer activated!"); | 74 | LOG_DEBUG(Common_Memory, "Memory freezer activated!"); |
| 73 | } else { | 75 | } else { |
| 74 | LOG_DEBUG(Common_Memory, "Memory freezer deactivated!"); | 76 | LOG_DEBUG(Common_Memory, "Memory freezer deactivated!"); |
| @@ -105,28 +107,21 @@ void Freezer::Unfreeze(VAddr address) { | |||
| 105 | 107 | ||
| 106 | LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); | 108 | LOG_DEBUG(Common_Memory, "Unfreezing memory for address={:016X}", address); |
| 107 | 109 | ||
| 108 | entries.erase( | 110 | std::erase_if(entries, [address](const Entry& entry) { return entry.address == address; }); |
| 109 | std::remove_if(entries.begin(), entries.end(), | ||
| 110 | [&address](const Entry& entry) { return entry.address == address; }), | ||
| 111 | entries.end()); | ||
| 112 | } | 111 | } |
| 113 | 112 | ||
| 114 | bool Freezer::IsFrozen(VAddr address) const { | 113 | bool Freezer::IsFrozen(VAddr address) const { |
| 115 | std::lock_guard lock{entries_mutex}; | 114 | std::lock_guard lock{entries_mutex}; |
| 116 | 115 | ||
| 117 | return std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { | 116 | return FindEntry(address) != entries.cend(); |
| 118 | return entry.address == address; | ||
| 119 | }) != entries.end(); | ||
| 120 | } | 117 | } |
| 121 | 118 | ||
| 122 | void Freezer::SetFrozenValue(VAddr address, u64 value) { | 119 | void Freezer::SetFrozenValue(VAddr address, u64 value) { |
| 123 | std::lock_guard lock{entries_mutex}; | 120 | std::lock_guard lock{entries_mutex}; |
| 124 | 121 | ||
| 125 | const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { | 122 | const auto iter = FindEntry(address); |
| 126 | return entry.address == address; | ||
| 127 | }); | ||
| 128 | 123 | ||
| 129 | if (iter == entries.end()) { | 124 | if (iter == entries.cend()) { |
| 130 | LOG_ERROR(Common_Memory, | 125 | LOG_ERROR(Common_Memory, |
| 131 | "Tried to set freeze value for address={:016X} that is not frozen!", address); | 126 | "Tried to set freeze value for address={:016X} that is not frozen!", address); |
| 132 | return; | 127 | return; |
| @@ -141,11 +136,9 @@ void Freezer::SetFrozenValue(VAddr address, u64 value) { | |||
| 141 | std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { | 136 | std::optional<Freezer::Entry> Freezer::GetEntry(VAddr address) const { |
| 142 | std::lock_guard lock{entries_mutex}; | 137 | std::lock_guard lock{entries_mutex}; |
| 143 | 138 | ||
| 144 | const auto iter = std::find_if(entries.begin(), entries.end(), [&address](const Entry& entry) { | 139 | const auto iter = FindEntry(address); |
| 145 | return entry.address == address; | ||
| 146 | }); | ||
| 147 | 140 | ||
| 148 | if (iter == entries.end()) { | 141 | if (iter == entries.cend()) { |
| 149 | return std::nullopt; | 142 | return std::nullopt; |
| 150 | } | 143 | } |
| 151 | 144 | ||
| @@ -158,7 +151,17 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const { | |||
| 158 | return entries; | 151 | return entries; |
| 159 | } | 152 | } |
| 160 | 153 | ||
| 161 | void Freezer::FrameCallback(u64 userdata, s64 ns_late) { | 154 | Freezer::Entries::iterator Freezer::FindEntry(VAddr address) { |
| 155 | return std::find_if(entries.begin(), entries.end(), | ||
| 156 | [address](const Entry& entry) { return entry.address == address; }); | ||
| 157 | } | ||
| 158 | |||
| 159 | Freezer::Entries::const_iterator Freezer::FindEntry(VAddr address) const { | ||
| 160 | return std::find_if(entries.begin(), entries.end(), | ||
| 161 | [address](const Entry& entry) { return entry.address == address; }); | ||
| 162 | } | ||
| 163 | |||
| 164 | void Freezer::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late) { | ||
| 162 | if (!IsActive()) { | 165 | if (!IsActive()) { |
| 163 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); | 166 | LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); |
| 164 | return; | 167 | return; |
| @@ -173,7 +176,7 @@ void Freezer::FrameCallback(u64 userdata, s64 ns_late) { | |||
| 173 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); | 176 | MemoryWriteWidth(memory, entry.width, entry.address, entry.value); |
| 174 | } | 177 | } |
| 175 | 178 | ||
| 176 | core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event); | 179 | core_timing.ScheduleEvent(memory_freezer_ns - ns_late, event); |
| 177 | } | 180 | } |
| 178 | 181 | ||
| 179 | void Freezer::FillEntryReads() { | 182 | void Freezer::FillEntryReads() { |
diff --git a/src/core/tools/freezer.h b/src/core/tools/freezer.h index 62fc6aa6c..0fdb701a7 100644 --- a/src/core/tools/freezer.h +++ b/src/core/tools/freezer.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <chrono> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <mutex> | 10 | #include <mutex> |
| 10 | #include <optional> | 11 | #include <optional> |
| @@ -72,13 +73,18 @@ public: | |||
| 72 | std::vector<Entry> GetEntries() const; | 73 | std::vector<Entry> GetEntries() const; |
| 73 | 74 | ||
| 74 | private: | 75 | private: |
| 75 | void FrameCallback(u64 userdata, s64 cycles_late); | 76 | using Entries = std::vector<Entry>; |
| 77 | |||
| 78 | Entries::iterator FindEntry(VAddr address); | ||
| 79 | Entries::const_iterator FindEntry(VAddr address) const; | ||
| 80 | |||
| 81 | void FrameCallback(std::uintptr_t user_data, std::chrono::nanoseconds ns_late); | ||
| 76 | void FillEntryReads(); | 82 | void FillEntryReads(); |
| 77 | 83 | ||
| 78 | std::atomic_bool active{false}; | 84 | std::atomic_bool active{false}; |
| 79 | 85 | ||
| 80 | mutable std::mutex entries_mutex; | 86 | mutable std::mutex entries_mutex; |
| 81 | std::vector<Entry> entries; | 87 | Entries entries; |
| 82 | 88 | ||
| 83 | std::shared_ptr<Core::Timing::EventType> event; | 89 | std::shared_ptr<Core::Timing::EventType> event; |
| 84 | Core::Timing::CoreTiming& core_timing; | 90 | Core::Timing::CoreTiming& core_timing; |