summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt21
-rw-r--r--src/core/announce_multiplayer_session.cpp164
-rw-r--r--src/core/announce_multiplayer_session.h98
-rw-r--r--src/core/arm/arm_interface.h16
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp24
-rw-r--r--src/core/arm/cpu_interrupt_handler.h39
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp51
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h5
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp34
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp36
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h7
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core.h5
-rw-r--r--src/core/core_timing.cpp159
-rw-r--r--src/core/core_timing.h31
-rw-r--r--src/core/debugger/debugger.cpp9
-rw-r--r--src/core/file_sys/errors.h5
-rw-r--r--src/core/file_sys/ips_layer.cpp7
-rw-r--r--src/core/file_sys/patch_manager.cpp14
-rw-r--r--src/core/file_sys/system_archive/shared_font.cpp2
-rw-r--r--src/core/frontend/applets/software_keyboard.h2
-rw-r--r--src/core/frontend/emu_window.cpp5
-rw-r--r--src/core/frontend/emu_window.h5
-rw-r--r--src/core/hid/emulated_controller.cpp80
-rw-r--r--src/core/hid/emulated_controller.h8
-rw-r--r--src/core/hid/hid_types.h12
-rw-r--r--src/core/hid/input_converter.cpp3
-rw-r--r--src/core/hle/ipc.h5
-rw-r--r--src/core/hle/ipc_helpers.h5
-rw-r--r--src/core/hle/kernel/k_client_port.cpp5
-rw-r--r--src/core/hle/kernel/k_client_port.h5
-rw-r--r--src/core/hle/kernel/k_process.cpp5
-rw-r--r--src/core/hle/kernel/k_process.h5
-rw-r--r--src/core/hle/kernel/k_shared_memory.cpp5
-rw-r--r--src/core/hle/kernel/k_shared_memory.h5
-rw-r--r--src/core/hle/kernel/kernel.cpp41
-rw-r--r--src/core/hle/kernel/kernel.h8
-rw-r--r--src/core/hle/kernel/physical_core.cpp29
-rw-r--r--src/core/hle/kernel/physical_core.h17
-rw-r--r--src/core/hle/result.h7
-rw-r--r--src/core/hle/service/acc/acc.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_software_keyboard.cpp6
-rw-r--r--src/core/hle/service/am/applets/applet_web_browser.cpp2
-rw-r--r--src/core/hle/service/apm/apm_controller.cpp2
-rw-r--r--src/core/hle/service/audio/audout_u.cpp3
-rw-r--r--src/core/hle/service/audio/audren_u.cpp31
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp77
-rw-r--r--src/core/hle/service/hid/hid.cpp20
-rw-r--r--src/core/hle/service/hid/irs.cpp2
-rw-r--r--src/core/hle/service/hid/irs_ring_lifo.h47
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.cpp239
-rw-r--r--src/core/hle/service/hid/irsensor/clustering_processor.h38
-rw-r--r--src/core/hle/service/ldn/errors.h12
-rw-r--r--src/core/hle/service/ldn/ldn.cpp436
-rw-r--r--src/core/hle/service/ldn/ldn.h6
-rw-r--r--src/core/hle/service/ldn/ldn_results.h27
-rw-r--r--src/core/hle/service/ldn/ldn_types.h292
-rw-r--r--src/core/hle/service/mm/mm_u.cpp10
-rw-r--r--src/core/hle/service/nifm/nifm.cpp342
-rw-r--r--src/core/hle/service/nifm/nifm.h27
-rw-r--r--src/core/hle/service/ns/iplatform_service_manager.cpp (renamed from src/core/hle/service/ns/pl_u.cpp)34
-rw-r--r--src/core/hle/service/ns/iplatform_service_manager.h (renamed from src/core/hle/service/ns/pl_u.h)6
-rw-r--r--src/core/hle/service/ns/ns.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp7
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp4
-rw-r--r--src/core/hle/service/pcv/pcv.cpp93
-rw-r--r--src/core/hle/service/pcv/pcv.h91
-rw-r--r--src/core/hle/service/service.cpp7
-rw-r--r--src/core/hle/service/sockets/bsd.cpp46
-rw-r--r--src/core/hle/service/sockets/bsd.h13
-rw-r--r--src/core/hle/service/sockets/sockets.h6
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp2
-rw-r--r--src/core/internal_network/network.cpp62
-rw-r--r--src/core/internal_network/network.h43
-rw-r--r--src/core/internal_network/socket_proxy.cpp290
-rw-r--r--src/core/internal_network/socket_proxy.h97
-rw-r--r--src/core/internal_network/sockets.h132
-rw-r--r--src/core/loader/elf.cpp263
-rw-r--r--src/core/loader/elf.h36
-rw-r--r--src/core/loader/kip.cpp2
-rw-r--r--src/core/loader/loader.cpp11
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/memory.cpp86
-rw-r--r--src/core/memory.h11
-rw-r--r--src/core/perf_stats.cpp5
-rw-r--r--src/core/perf_stats.h5
-rw-r--r--src/core/telemetry_session.cpp5
-rw-r--r--src/core/telemetry_session.h5
93 files changed, 2580 insertions, 1426 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c1cc62a45..806e7ff6c 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,10 +1,9 @@
+# SPDX-FileCopyrightText: 2018 yuzu Emulator Project
+# SPDX-License-Identifier: GPL-2.0-or-later
+
add_library(core STATIC
- announce_multiplayer_session.cpp
- announce_multiplayer_session.h
arm/arm_interface.h
arm/arm_interface.cpp
- arm/cpu_interrupt_handler.cpp
- arm/cpu_interrupt_handler.h
arm/dynarmic/arm_dynarmic_32.cpp
arm/dynarmic/arm_dynarmic_32.h
arm/dynarmic/arm_dynarmic_64.cpp
@@ -448,6 +447,7 @@ add_library(core STATIC
hle/service/hid/hidbus.h
hle/service/hid/irs.cpp
hle/service/hid/irs.h
+ hle/service/hid/irs_ring_lifo.h
hle/service/hid/ring_lifo.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
@@ -500,9 +500,10 @@ add_library(core STATIC
hle/service/jit/jit.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
- hle/service/ldn/errors.h
+ hle/service/ldn/ldn_results.h
hle/service/ldn/ldn.cpp
hle/service/ldn/ldn.h
+ hle/service/ldn/ldn_types.h
hle/service/ldr/ldr.cpp
hle/service/ldr/ldr.h
hle/service/lm/lm.cpp
@@ -537,14 +538,14 @@ add_library(core STATIC
hle/service/npns/npns.cpp
hle/service/npns/npns.h
hle/service/ns/errors.h
+ hle/service/ns/iplatform_service_manager.cpp
+ hle/service/ns/iplatform_service_manager.h
hle/service/ns/language.cpp
hle/service/ns/language.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pdm_qry.cpp
hle/service/ns/pdm_qry.h
- hle/service/ns/pl_u.cpp
- hle/service/ns/pl_u.h
hle/service/nvdrv/devices/nvdevice.h
hle/service/nvdrv/devices/nvdisp_disp0.cpp
hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -721,10 +722,10 @@ add_library(core STATIC
internal_network/network_interface.cpp
internal_network/network_interface.h
internal_network/sockets.h
+ internal_network/socket_proxy.cpp
+ internal_network/socket_proxy.h
loader/deconstructed_rom_directory.cpp
loader/deconstructed_rom_directory.h
- loader/elf.cpp
- loader/elf.h
loader/kip.cpp
loader/kip.h
loader/loader.cpp
@@ -783,7 +784,7 @@ endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core)
-target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::Opus)
+target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus)
if (MINGW)
target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
endif()
diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp
deleted file mode 100644
index d73a488cf..000000000
--- a/src/core/announce_multiplayer_session.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <chrono>
-#include <future>
-#include <vector>
-#include "announce_multiplayer_session.h"
-#include "common/announce_multiplayer_room.h"
-#include "common/assert.h"
-#include "common/settings.h"
-#include "network/network.h"
-
-#ifdef ENABLE_WEB_SERVICE
-#include "web_service/announce_room_json.h"
-#endif
-
-namespace Core {
-
-// Time between room is announced to web_service
-static constexpr std::chrono::seconds announce_time_interval(15);
-
-AnnounceMultiplayerSession::AnnounceMultiplayerSession(Network::RoomNetwork& room_network_)
- : room_network{room_network_} {
-#ifdef ENABLE_WEB_SERVICE
- backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
- Settings::values.yuzu_username.GetValue(),
- Settings::values.yuzu_token.GetValue());
-#else
- backend = std::make_unique<AnnounceMultiplayerRoom::NullBackend>();
-#endif
-}
-
-WebService::WebResult AnnounceMultiplayerSession::Register() {
- std::shared_ptr<Network::Room> room = room_network.GetRoom().lock();
- if (!room) {
- return WebService::WebResult{WebService::WebResult::Code::LibError,
- "Network is not initialized", ""};
- }
- if (room->GetState() != Network::Room::State::Open) {
- return WebService::WebResult{WebService::WebResult::Code::LibError, "Room is not open", ""};
- }
- UpdateBackendData(room);
- WebService::WebResult result = backend->Register();
- if (result.result_code != WebService::WebResult::Code::Success) {
- return result;
- }
- LOG_INFO(WebService, "Room has been registered");
- room->SetVerifyUID(result.returned_data);
- registered = true;
- return WebService::WebResult{WebService::WebResult::Code::Success, "", ""};
-}
-
-void AnnounceMultiplayerSession::Start() {
- if (announce_multiplayer_thread) {
- Stop();
- }
- shutdown_event.Reset();
- announce_multiplayer_thread =
- std::make_unique<std::thread>(&AnnounceMultiplayerSession::AnnounceMultiplayerLoop, this);
-}
-
-void AnnounceMultiplayerSession::Stop() {
- if (announce_multiplayer_thread) {
- shutdown_event.Set();
- announce_multiplayer_thread->join();
- announce_multiplayer_thread.reset();
- backend->Delete();
- registered = false;
- }
-}
-
-AnnounceMultiplayerSession::CallbackHandle AnnounceMultiplayerSession::BindErrorCallback(
- std::function<void(const WebService::WebResult&)> function) {
- std::lock_guard lock(callback_mutex);
- auto handle = std::make_shared<std::function<void(const WebService::WebResult&)>>(function);
- error_callbacks.insert(handle);
- return handle;
-}
-
-void AnnounceMultiplayerSession::UnbindErrorCallback(CallbackHandle handle) {
- std::lock_guard lock(callback_mutex);
- error_callbacks.erase(handle);
-}
-
-AnnounceMultiplayerSession::~AnnounceMultiplayerSession() {
- Stop();
-}
-
-void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr<Network::Room> room) {
- Network::RoomInformation room_information = room->GetRoomInformation();
- std::vector<AnnounceMultiplayerRoom::Member> memberlist = room->GetRoomMemberList();
- backend->SetRoomInformation(room_information.name, room_information.description,
- room_information.port, room_information.member_slots,
- Network::network_version, room->HasPassword(),
- room_information.preferred_game);
- backend->ClearPlayers();
- for (const auto& member : memberlist) {
- backend->AddPlayer(member);
- }
-}
-
-void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() {
- // Invokes all current bound error callbacks.
- const auto ErrorCallback = [this](WebService::WebResult result) {
- std::lock_guard<std::mutex> lock(callback_mutex);
- for (auto callback : error_callbacks) {
- (*callback)(result);
- }
- };
-
- if (!registered) {
- WebService::WebResult result = Register();
- if (result.result_code != WebService::WebResult::Code::Success) {
- ErrorCallback(result);
- return;
- }
- }
-
- auto update_time = std::chrono::steady_clock::now();
- std::future<WebService::WebResult> future;
- while (!shutdown_event.WaitUntil(update_time)) {
- update_time += announce_time_interval;
- std::shared_ptr<Network::Room> room = room_network.GetRoom().lock();
- if (!room) {
- break;
- }
- if (room->GetState() != Network::Room::State::Open) {
- break;
- }
- UpdateBackendData(room);
- WebService::WebResult result = backend->Update();
- if (result.result_code != WebService::WebResult::Code::Success) {
- ErrorCallback(result);
- }
- if (result.result_string == "404") {
- registered = false;
- // Needs to register the room again
- WebService::WebResult register_result = Register();
- if (register_result.result_code != WebService::WebResult::Code::Success) {
- ErrorCallback(register_result);
- }
- }
- }
-}
-
-AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() {
- return backend->GetRoomList();
-}
-
-bool AnnounceMultiplayerSession::IsRunning() const {
- return announce_multiplayer_thread != nullptr;
-}
-
-void AnnounceMultiplayerSession::UpdateCredentials() {
- ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running");
-
-#ifdef ENABLE_WEB_SERVICE
- backend = std::make_unique<WebService::RoomJson>(Settings::values.web_api_url.GetValue(),
- Settings::values.yuzu_username.GetValue(),
- Settings::values.yuzu_token.GetValue());
-#endif
-}
-
-} // namespace Core
diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h
deleted file mode 100644
index db790f7d2..000000000
--- a/src/core/announce_multiplayer_session.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <atomic>
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <set>
-#include <thread>
-#include "common/announce_multiplayer_room.h"
-#include "common/common_types.h"
-#include "common/thread.h"
-
-namespace Network {
-class Room;
-class RoomNetwork;
-} // namespace Network
-
-namespace Core {
-
-/**
- * Instruments AnnounceMultiplayerRoom::Backend.
- * Creates a thread that regularly updates the room information and submits them
- * An async get of room information is also possible
- */
-class AnnounceMultiplayerSession {
-public:
- using CallbackHandle = std::shared_ptr<std::function<void(const WebService::WebResult&)>>;
- AnnounceMultiplayerSession(Network::RoomNetwork& room_network_);
- ~AnnounceMultiplayerSession();
-
- /**
- * Allows to bind a function that will get called if the announce encounters an error
- * @param function The function that gets called
- * @return A handle that can be used the unbind the function
- */
- CallbackHandle BindErrorCallback(std::function<void(const WebService::WebResult&)> function);
-
- /**
- * Unbind a function from the error callbacks
- * @param handle The handle for the function that should get unbind
- */
- void UnbindErrorCallback(CallbackHandle handle);
-
- /**
- * Registers a room to web services
- * @return The result of the registration attempt.
- */
- WebService::WebResult Register();
-
- /**
- * Starts the announce of a room to web services
- */
- void Start();
-
- /**
- * Stops the announce to web services
- */
- void Stop();
-
- /**
- * Returns a list of all room information the backend got
- * @param func A function that gets executed when the async get finished, e.g. a signal
- * @return a list of rooms received from the web service
- */
- AnnounceMultiplayerRoom::RoomList GetRoomList();
-
- /**
- * Whether the announce session is still running
- */
- bool IsRunning() const;
-
- /**
- * Recreates the backend, updating the credentials.
- * This can only be used when the announce session is not running.
- */
- void UpdateCredentials();
-
-private:
- void UpdateBackendData(std::shared_ptr<Network::Room> room);
- void AnnounceMultiplayerLoop();
-
- Common::Event shutdown_event;
- std::mutex callback_mutex;
- std::set<CallbackHandle> error_callbacks;
- std::unique_ptr<std::thread> announce_multiplayer_thread;
-
- /// Backend interface that logs fields
- std::unique_ptr<AnnounceMultiplayerRoom::Backend> backend;
-
- std::atomic_bool registered = false; ///< Whether the room has been registered
-
- Network::RoomNetwork& room_network;
-};
-
-} // namespace Core
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index c092db9ff..7d62d030e 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -28,7 +27,6 @@ namespace Core {
class System;
class CPUInterruptHandler;
-using CPUInterrupts = std::array<CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>;
using WatchpointArray = std::array<Kernel::DebugWatchpoint, Core::Hardware::NUM_WATCHPOINTS>;
/// Generic ARMv8 CPU interface
@@ -37,10 +35,8 @@ public:
YUZU_NON_COPYABLE(ARM_Interface);
YUZU_NON_MOVEABLE(ARM_Interface);
- explicit ARM_Interface(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_)
- : system{system_}, interrupt_handlers{interrupt_handlers_}, uses_wall_clock{
- uses_wall_clock_} {}
+ explicit ARM_Interface(System& system_, bool uses_wall_clock_)
+ : system{system_}, uses_wall_clock{uses_wall_clock_} {}
virtual ~ARM_Interface() = default;
struct ThreadContext32 {
@@ -182,6 +178,9 @@ public:
/// Signal an interrupt and ask the core to halt as soon as possible.
virtual void SignalInterrupt() = 0;
+ /// Clear a previous interrupt.
+ virtual void ClearInterrupt() = 0;
+
struct BacktraceEntry {
std::string module;
u64 address;
@@ -209,7 +208,6 @@ public:
protected:
/// System context that this ARM interface is running under.
System& system;
- CPUInterrupts& interrupt_handlers;
const WatchpointArray* watchpoints;
bool uses_wall_clock;
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
deleted file mode 100644
index 77b6194d7..000000000
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "common/thread.h"
-#include "core/arm/cpu_interrupt_handler.h"
-
-namespace Core {
-
-CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
-
-CPUInterruptHandler::~CPUInterruptHandler() = default;
-
-void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
- if (is_interrupted_) {
- interrupt_event->Set();
- }
- is_interrupted = is_interrupted_;
-}
-
-void CPUInterruptHandler::AwaitInterrupt() {
- interrupt_event->Wait();
-}
-
-} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
deleted file mode 100644
index 286e12e53..000000000
--- a/src/core/arm/cpu_interrupt_handler.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <atomic>
-#include <memory>
-
-namespace Common {
-class Event;
-}
-
-namespace Core {
-
-class CPUInterruptHandler {
-public:
- CPUInterruptHandler();
- ~CPUInterruptHandler();
-
- CPUInterruptHandler(const CPUInterruptHandler&) = delete;
- CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
-
- CPUInterruptHandler(CPUInterruptHandler&&) = delete;
- CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
-
- bool IsInterrupted() const {
- return is_interrupted;
- }
-
- void SetInterrupt(bool is_interrupted);
-
- void AwaitInterrupt();
-
-private:
- std::unique_ptr<Common::Event> interrupt_event;
- std::atomic_bool is_interrupted{false};
-};
-
-} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index b8d2ce224..d1e70f19d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -11,7 +11,6 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
@@ -125,7 +124,9 @@ public:
}
void AddTicks(u64 ticks) override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ return;
+ }
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -142,7 +143,12 @@ public:
}
u64 GetTicksRemaining() override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ if (!IsInterrupted()) {
+ return minimum_run_cycles;
+ }
+ return 0U;
+ }
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -168,11 +174,15 @@ public:
parent.jit.load()->HaltExecution(hr);
}
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_32& parent;
Core::Memory::Memory& memory;
std::size_t num_interpreted_instructions{};
bool debugger_enabled{};
- static constexpr u64 minimum_run_cycles = 1000U;
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable* page_table) const {
@@ -180,19 +190,21 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
config.callbacks = cb.get();
config.coprocessors[15] = cp15;
config.define_unpredictable_behaviour = true;
- static constexpr std::size_t PAGE_BITS = 12;
- static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - PAGE_BITS);
+ static constexpr std::size_t YUZU_PAGEBITS = 12;
+ static constexpr std::size_t NUM_PAGE_TABLE_ENTRIES = 1 << (32 - YUZU_PAGEBITS);
if (page_table) {
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
page_table->pointers.data());
+ config.absolute_offset_page_table = true;
+ config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
+ config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
+ config.only_detect_misalignment_via_page_table_on_page_boundary = true;
+
config.fastmem_pointer = page_table->fastmem_arena;
+
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
+ config.recompile_on_exclusive_fastmem_failure = true;
}
- config.absolute_offset_page_table = true;
- config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
- config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
- config.only_detect_misalignment_via_page_table_on_page_boundary = true;
- config.fastmem_exclusive_access = true;
- config.recompile_on_exclusive_fastmem_failure = true;
// Multi-process state
config.processor_id = core_index;
@@ -200,7 +212,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
- config.enable_cycle_counting = !uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
@@ -244,6 +256,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
if (!Settings::values.cpuopt_fastmem_exclusives) {
config.fastmem_exclusive_access = false;
@@ -311,11 +324,9 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() {
LoadContext(breakpoint_context);
}
-ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
- cb(std::make_unique<DynarmicCallbacks32>(*this)),
+ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {}
@@ -394,6 +405,10 @@ void ARM_Dynarmic_32::SignalInterrupt() {
jit.load()->HaltExecution(break_loop);
}
+void ARM_Dynarmic_32::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
+}
+
void ARM_Dynarmic_32::ClearInstructionCache() {
jit.load()->ClearCache();
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index 346e9abf8..d24ba2289 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -28,8 +28,8 @@ class System;
class ARM_Dynarmic_32 final : public ARM_Interface {
public:
- ARM_Dynarmic_32(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_32() override;
void SetPC(u64 pc) override;
@@ -56,6 +56,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override {}
void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 1a4d37cbc..1d46f6d40 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -10,7 +10,6 @@
#include "common/logging/log.h"
#include "common/page_table.h"
#include "common/settings.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
@@ -166,7 +165,9 @@ public:
}
void AddTicks(u64 ticks) override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ return;
+ }
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
@@ -181,7 +182,12 @@ public:
}
u64 GetTicksRemaining() override {
- ASSERT_MSG(!parent.uses_wall_clock, "This should never happen - dynarmic ticking disabled");
+ if (parent.uses_wall_clock) {
+ if (!IsInterrupted()) {
+ return minimum_run_cycles;
+ }
+ return 0U;
+ }
return std::max<s64>(parent.system.CoreTiming().GetDowncount(), 0);
}
@@ -211,12 +217,16 @@ public:
parent.jit.load()->HaltExecution(hr);
}
+ bool IsInterrupted() {
+ return parent.system.Kernel().PhysicalCore(parent.core_index).IsInterrupted();
+ }
+
ARM_Dynarmic_64& parent;
Core::Memory::Memory& memory;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
bool debugger_enabled{};
- static constexpr u64 minimum_run_cycles = 1000U;
+ static constexpr u64 minimum_run_cycles = 10000U;
};
std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable* page_table,
@@ -240,7 +250,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
config.fastmem_address_space_bits = address_space_bits;
config.silently_mirror_fastmem = false;
- config.fastmem_exclusive_access = true;
+ config.fastmem_exclusive_access = config.fastmem_pointer != nullptr;
config.recompile_on_exclusive_fastmem_failure = true;
}
@@ -260,7 +270,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
// Timing
config.wall_clock_cntpct = uses_wall_clock;
- config.enable_cycle_counting = !uses_wall_clock;
+ config.enable_cycle_counting = true;
// Code cache size
config.code_cache_size = 512_MiB;
@@ -304,6 +314,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable*
}
if (!Settings::values.cpuopt_fastmem) {
config.fastmem_pointer = nullptr;
+ config.fastmem_exclusive_access = false;
}
if (!Settings::values.cpuopt_fastmem_exclusives) {
config.fastmem_exclusive_access = false;
@@ -371,10 +382,9 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() {
LoadContext(breakpoint_context);
}
-ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
- bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
- std::size_t core_index_)
- : ARM_Interface{system_, interrupt_handlers_, uses_wall_clock_},
+ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_,
+ ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_)
+ : ARM_Interface{system_, uses_wall_clock_},
cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_},
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)},
null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {}
@@ -461,6 +471,10 @@ void ARM_Dynarmic_64::SignalInterrupt() {
jit.load()->HaltExecution(break_loop);
}
+void ARM_Dynarmic_64::ClearInterrupt() {
+ jit.load()->ClearHalt(break_loop);
+}
+
void ARM_Dynarmic_64::ClearInstructionCache() {
jit.load()->ClearCache();
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index c77a83ad7..ed1a5eb96 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -20,14 +20,13 @@ class Memory;
namespace Core {
class DynarmicCallbacks64;
-class CPUInterruptHandler;
class DynarmicExclusiveMonitor;
class System;
class ARM_Dynarmic_64 final : public ARM_Interface {
public:
- ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_, bool uses_wall_clock_,
- ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_);
+ ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_,
+ std::size_t core_index_);
~ARM_Dynarmic_64() override;
void SetPC(u64 pc) override;
@@ -50,6 +49,7 @@ public:
void LoadContext(const ThreadContext64& ctx) override;
void SignalInterrupt() override;
+ void ClearInterrupt() override;
void ClearExclusiveState() override;
void ClearInstructionCache() override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 6aae79c48..200efe4db 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <fmt/format.h>
#include "common/logging/log.h"
@@ -9,6 +8,10 @@
#include "core/core.h"
#include "core/core_timing.h"
+#ifdef _MSC_VER
+#include <intrin.h>
+#endif
+
using Callback = Dynarmic::A32::Coprocessor::Callback;
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
@@ -48,12 +51,31 @@ CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1
switch (opc2) {
case 4:
// CP15_DATA_SYNC_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+ _mm_lfence();
+#else
+ asm volatile("mfence\n\tlfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
case 5:
// CP15_DATA_MEMORY_BARRIER
- // This is a dummy write, we ignore the value written here.
- return &dummy_value;
+ return Callback{
+ [](Dynarmic::A32::Jit*, void*, std::uint32_t, std::uint32_t) -> std::uint64_t {
+#ifdef _MSC_VER
+ _mm_mfence();
+#else
+ asm volatile("mfence\n\t" : : : "memory");
+#endif
+ return 0;
+ },
+ std::nullopt,
+ };
}
}
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index f271b2070..d90b3e568 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -36,6 +35,8 @@ public:
ARM_Dynarmic_32& parent;
u32 uprw = 0;
u32 uro = 0;
+
+ friend class ARM_Dynarmic_32;
};
} // namespace Core
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 95791a07f..121092868 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include <atomic>
@@ -142,8 +141,6 @@ struct System::Impl {
core_timing.SyncPause(false);
is_paused = false;
- audio_core->PauseSinks(false);
-
return status;
}
@@ -151,8 +148,6 @@ struct System::Impl {
std::unique_lock<std::mutex> lk(suspend_guard);
status = SystemResultStatus::Success;
- audio_core->PauseSinks(true);
-
core_timing.SyncPause(true);
kernel.Suspend(true);
is_paused = true;
@@ -320,10 +315,19 @@ struct System::Impl {
if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result);
}
+
+ std::string title_version;
+ const FileSys::PatchManager pm(program_id, system.GetFileSystemController(),
+ system.GetContentProvider());
+ const auto metadata = pm.GetControlMetadata();
+ if (metadata.first != nullptr) {
+ title_version = metadata.first->GetVersionString();
+ }
if (auto room_member = room_network.GetRoomMember().lock()) {
Network::GameInfo game_info;
game_info.name = name;
game_info.id = program_id;
+ game_info.version = title_version;
room_member->SendGameInfo(game_info);
}
diff --git a/src/core/core.h b/src/core/core.h
index 13122dd61..0ce3b1d60 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 5425637f5..5375a5d59 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -6,9 +6,7 @@
#include <string>
#include <tuple>
-#include "common/logging/log.h"
#include "common/microprofile.h"
-#include "common/thread.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hardware_properties.h"
@@ -44,10 +42,10 @@ CoreTiming::CoreTiming()
CoreTiming::~CoreTiming() = default;
-void CoreTiming::ThreadEntry(CoreTiming& instance, size_t id) {
- const std::string name = "yuzu:HostTiming_" + std::to_string(id);
- MicroProfileOnThreadCreate(name.c_str());
- Common::SetCurrentThreadName(name.c_str());
+void CoreTiming::ThreadEntry(CoreTiming& instance) {
+ constexpr char name[] = "yuzu:HostTiming";
+ MicroProfileOnThreadCreate(name);
+ Common::SetCurrentThreadName(name);
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
instance.on_thread_init();
instance.ThreadLoop();
@@ -63,127 +61,91 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
if (is_multicore) {
- worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0);
+ timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
}
}
void CoreTiming::Shutdown() {
- is_paused = true;
+ paused = true;
shutting_down = true;
- std::atomic_thread_fence(std::memory_order_release);
-
- event_cv.notify_all();
- wait_pause_cv.notify_all();
- for (auto& thread : worker_threads) {
- thread.join();
+ pause_event.Set();
+ event.Set();
+ if (timer_thread) {
+ timer_thread->join();
}
- worker_threads.clear();
- pause_callbacks.clear();
ClearPendingEvents();
+ timer_thread.reset();
has_started = false;
}
-void CoreTiming::Pause(bool is_paused_) {
- std::unique_lock main_lock(event_mutex);
- if (is_paused_ == paused_state.load(std::memory_order_relaxed)) {
- return;
- }
- if (is_multicore) {
- is_paused = is_paused_;
- event_cv.notify_all();
- if (!is_paused_) {
- wait_pause_cv.notify_all();
- }
- }
- paused_state.store(is_paused_, std::memory_order_relaxed);
+void CoreTiming::Pause(bool is_paused) {
+ paused = is_paused;
+ pause_event.Set();
- if (!is_paused_) {
+ if (!is_paused) {
pause_end_time = GetGlobalTimeNs().count();
}
-
- for (auto& cb : pause_callbacks) {
- cb(is_paused_);
- }
}
-void CoreTiming::SyncPause(bool is_paused_) {
- std::unique_lock main_lock(event_mutex);
- if (is_paused_ == paused_state.load(std::memory_order_relaxed)) {
+void CoreTiming::SyncPause(bool is_paused) {
+ if (is_paused == paused && paused_set == paused) {
return;
}
- if (is_multicore) {
- is_paused = is_paused_;
- event_cv.notify_all();
- if (!is_paused_) {
- wait_pause_cv.notify_all();
- }
- }
- paused_state.store(is_paused_, std::memory_order_relaxed);
- if (is_multicore) {
- if (is_paused_) {
- wait_signal_cv.wait(main_lock, [this] { return pause_count == worker_threads.size(); });
- } else {
- wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });
+ Pause(is_paused);
+ if (timer_thread) {
+ if (!is_paused) {
+ pause_event.Set();
}
+ event.Set();
+ while (paused_set != is_paused)
+ ;
}
- if (!is_paused_) {
+ if (!is_paused) {
pause_end_time = GetGlobalTimeNs().count();
}
-
- for (auto& cb : pause_callbacks) {
- cb(is_paused_);
- }
}
bool CoreTiming::IsRunning() const {
- return !paused_state.load(std::memory_order_acquire);
+ return !paused_set;
}
bool CoreTiming::HasPendingEvents() const {
- std::unique_lock main_lock(event_mutex);
- return !event_queue.empty() || pending_events.load(std::memory_order_relaxed) != 0;
+ return !(wait_set && event_queue.empty());
}
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) {
+ {
+ std::scoped_lock scope{basic_lock};
+ const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
- std::unique_lock main_lock(event_mutex);
- const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
-
- event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
- pending_events.fetch_add(1, std::memory_order_relaxed);
-
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
-
- if (is_multicore) {
- event_cv.notify_one();
+ event_queue.emplace_back(
+ Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
+ std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
+
+ event.Set();
}
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) {
- std::unique_lock main_lock(event_mutex);
+ std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
event_queue.emplace_back(
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
- pending_events.fetch_add(1, std::memory_order_relaxed);
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
-
- if (is_multicore) {
- event_cv.notify_one();
- }
}
void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data) {
- std::unique_lock main_lock(event_mutex);
+ std::scoped_lock scope{basic_lock};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type.lock().get() == event_type.get() && e.user_data == user_data;
});
@@ -192,7 +154,6 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
if (itr != event_queue.end()) {
event_queue.erase(itr, event_queue.end());
std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
- pending_events.fetch_sub(1, std::memory_order_relaxed);
}
}
@@ -232,12 +193,11 @@ u64 CoreTiming::GetClockTicks() const {
}
void CoreTiming::ClearPendingEvents() {
- std::unique_lock main_lock(event_mutex);
event_queue.clear();
}
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
- std::unique_lock main_lock(event_mutex);
+ std::scoped_lock lock{basic_lock};
const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
return e.type.lock().get() == event_type.get();
@@ -250,29 +210,23 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
}
}
-void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
- std::unique_lock main_lock(event_mutex);
- pause_callbacks.emplace_back(std::move(callback));
-}
-
std::optional<s64> CoreTiming::Advance() {
+ std::scoped_lock lock{advance_lock, basic_lock};
global_timer = GetGlobalTimeNs().count();
- std::unique_lock main_lock(event_mutex);
while (!event_queue.empty() && event_queue.front().time <= global_timer) {
Event evt = std::move(event_queue.front());
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
event_queue.pop_back();
if (const auto event_type{evt.type.lock()}) {
- event_mutex.unlock();
+ basic_lock.unlock();
const auto new_schedule_time{event_type->callback(
evt.user_data, evt.time,
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
- event_mutex.lock();
- pending_events.fetch_sub(1, std::memory_order_relaxed);
+ basic_lock.lock();
if (evt.reschedule_time != 0) {
// If this event was scheduled into a pause, its time now is going to be way behind.
@@ -285,9 +239,9 @@ std::optional<s64> CoreTiming::Advance() {
const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count()
: evt.reschedule_time};
+
event_queue.emplace_back(
Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
- pending_events.fetch_add(1, std::memory_order_relaxed);
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
@@ -304,34 +258,27 @@ std::optional<s64> CoreTiming::Advance() {
}
void CoreTiming::ThreadLoop() {
- const auto predicate = [this] { return !event_queue.empty() || is_paused; };
has_started = true;
while (!shutting_down) {
- while (!is_paused && !shutting_down) {
+ while (!paused) {
+ paused_set = false;
const auto next_time = Advance();
if (next_time) {
if (*next_time > 0) {
std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
- std::unique_lock main_lock(event_mutex);
- event_cv.wait_for(main_lock, next_time_ns, predicate);
+ event.WaitFor(next_time_ns);
}
} else {
- std::unique_lock main_lock(event_mutex);
- event_cv.wait(main_lock, predicate);
+ wait_set = true;
+ event.Wait();
}
+ wait_set = false;
}
- std::unique_lock main_lock(event_mutex);
- pause_count++;
- if (pause_count == worker_threads.size()) {
- clock->Pause(true);
- wait_signal_cv.notify_all();
- }
- wait_pause_cv.wait(main_lock, [this] { return !is_paused || shutting_down; });
- pause_count--;
- if (pause_count == 0) {
- clock->Pause(false);
- wait_signal_cv.notify_all();
- }
+
+ paused_set = true;
+ clock->Pause(true);
+ pause_event.Wait();
+ clock->Pause(false);
}
}
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 09b6ed81a..3259397b2 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -5,7 +5,6 @@
#include <atomic>
#include <chrono>
-#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
@@ -15,6 +14,7 @@
#include <vector>
#include "common/common_types.h"
+#include "common/thread.h"
#include "common/wall_clock.h"
namespace Core::Timing {
@@ -22,7 +22,6 @@ namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
-using PauseCallback = std::function<void(bool paused)>;
/// Contains the characteristics of a particular event.
struct EventType {
@@ -134,16 +133,13 @@ public:
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
std::optional<s64> Advance();
- /// Register a callback function to be called when coretiming pauses.
- void RegisterPauseCallback(PauseCallback&& callback);
-
private:
struct Event;
/// Clear all pending events. This should ONLY be done on exit.
void ClearPendingEvents();
- static void ThreadEntry(CoreTiming& instance, size_t id);
+ static void ThreadEntry(CoreTiming& instance);
void ThreadLoop();
std::unique_ptr<Common::WallClock> clock;
@@ -156,31 +152,26 @@ private:
// accomodated by the standard adaptor class.
std::vector<Event> event_queue;
u64 event_fifo_id = 0;
- std::atomic<size_t> pending_events{};
std::shared_ptr<EventType> ev_lost;
+ Common::Event event{};
+ Common::Event pause_event{};
+ std::mutex basic_lock;
+ std::mutex advance_lock;
+ std::unique_ptr<std::thread> timer_thread;
+ std::atomic<bool> paused{};
+ std::atomic<bool> paused_set{};
+ std::atomic<bool> wait_set{};
+ std::atomic<bool> shutting_down{};
std::atomic<bool> has_started{};
std::function<void()> on_thread_init{};
- std::vector<std::thread> worker_threads;
-
- std::condition_variable event_cv;
- std::condition_variable wait_pause_cv;
- std::condition_variable wait_signal_cv;
- mutable std::mutex event_mutex;
-
- std::atomic<bool> paused_state{};
- bool is_paused{};
- bool shutting_down{};
bool is_multicore{};
- size_t pause_count{};
s64 pause_end_time{};
/// Cycle timing
u64 ticks{};
s64 downcount{};
-
- std::vector<PauseCallback> pause_callbacks{};
};
/// Creates a core timing event with the given name and callback.
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index ac64d2f9d..e42bdd17d 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -15,6 +15,7 @@
#include "core/debugger/debugger_interface.h"
#include "core/debugger/gdbstub.h"
#include "core/hle/kernel/global_scheduler_context.h"
+#include "core/hle/kernel/k_scheduler.h"
template <typename Readable, typename Buffer, typename Callback>
static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) {
@@ -230,13 +231,12 @@ private:
}
void PauseEmulation() {
+ Kernel::KScopedSchedulerLock sl{system.Kernel()};
+
// Put all threads to sleep on next scheduler round.
for (auto* thread : ThreadList()) {
thread->RequestSuspend(Kernel::SuspendType::Debug);
}
-
- // Signal an interrupt so that scheduler will fire.
- system.Kernel().InterruptAllPhysicalCores();
}
void ResumeEmulation(Kernel::KThread* except = nullptr) {
@@ -253,7 +253,8 @@ private:
template <typename Callback>
void MarkResumed(Callback&& cb) {
- std::scoped_lock lk{connection_lock};
+ Kernel::KScopedSchedulerLock sl{system.Kernel()};
+ std::scoped_lock cl{connection_lock};
stopped = false;
cb();
}
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index ff15b3e23..7cee0c7df 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index 4b35ca82f..5aab428bb 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -217,9 +217,7 @@ void IPSwitchCompiler::Parse() {
break;
} else if (StartsWith(line, "@nsobid-")) {
// NSO Build ID Specifier
- auto raw_build_id = line.substr(8);
- if (raw_build_id.size() != 0x40)
- raw_build_id.resize(0x40, '0');
+ const auto raw_build_id = fmt::format("{:0<64}", line.substr(8));
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
} else if (StartsWith(line, "#")) {
// Mandatory Comment
@@ -287,7 +285,8 @@ void IPSwitchCompiler::Parse() {
std::copy(value.begin(), value.end(), std::back_inserter(replace));
} else {
// hex replacement
- const auto value = patch_line.substr(9);
+ const auto value =
+ patch_line.substr(9, patch_line.find_first_of(" /\r\n", 9) - 9);
replace = Common::HexStringToVector(value, is_little_endian);
}
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index bd525b26c..4c80e13a9 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -191,6 +191,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) const {
const auto& disabled = Settings::values.disabled_addons[title_id];
+ const auto nso_build_id = fmt::format("{:0<64}", build_id);
std::vector<VirtualFile> out;
out.reserve(patch_dirs.size());
@@ -203,21 +204,18 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips") {
auto name = file->GetName();
- const auto p1 = name.substr(0, name.find('.'));
- const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
- if (build_id == this_build_id)
+ const auto this_build_id =
+ fmt::format("{:0<64}", name.substr(0, name.find('.')));
+ if (nso_build_id == this_build_id)
out.push_back(file);
} else if (file->GetExtension() == "pchtxt") {
IPSwitchCompiler compiler{file};
if (!compiler.IsValid())
continue;
- auto this_build_id = Common::HexToString(compiler.GetBuildID());
- this_build_id =
- this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
-
- if (build_id == this_build_id)
+ const auto this_build_id = Common::HexToString(compiler.GetBuildID());
+ if (nso_build_id == this_build_id)
out.push_back(file);
}
}
diff --git a/src/core/file_sys/system_archive/shared_font.cpp b/src/core/file_sys/system_archive/shared_font.cpp
index f841988ff..3210583f0 100644
--- a/src/core/file_sys/system_archive/shared_font.cpp
+++ b/src/core/file_sys/system_archive/shared_font.cpp
@@ -9,7 +9,7 @@
#include "core/file_sys/system_archive/data/font_standard.h"
#include "core/file_sys/system_archive/shared_font.h"
#include "core/file_sys/vfs_vector.h"
-#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
namespace FileSys::SystemArchive {
diff --git a/src/core/frontend/applets/software_keyboard.h b/src/core/frontend/applets/software_keyboard.h
index a405e3c94..094d1e713 100644
--- a/src/core/frontend/applets/software_keyboard.h
+++ b/src/core/frontend/applets/software_keyboard.h
@@ -17,6 +17,8 @@ struct KeyboardInitializeParameters {
std::u16string sub_text;
std::u16string guide_text;
std::u16string initial_text;
+ char16_t left_optional_symbol_key;
+ char16_t right_optional_symbol_key;
u32 max_text_length;
u32 min_text_length;
s32 initial_cursor_position;
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 57c6ffc43..1be2dccb0 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "core/frontend/emu_window.h"
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index b3bffecb2..ac1906d5e 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 8c3895937..01c43be93 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/thread.h"
#include "core/hid/emulated_controller.h"
#include "core/hid/input_converter.h"
@@ -84,23 +85,26 @@ void EmulatedController::ReloadFromSettings() {
motion_params[index] = Common::ParamPackage(player.motions[index]);
}
+ controller.colors_state.fullkey = {
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
+ };
controller.colors_state.left = {
- .body = player.body_color_left,
- .button = player.button_color_left,
+ .body = GetNpadColor(player.body_color_left),
+ .button = GetNpadColor(player.button_color_left),
};
-
- controller.colors_state.right = {
- .body = player.body_color_right,
- .button = player.button_color_right,
+ controller.colors_state.left = {
+ .body = GetNpadColor(player.body_color_right),
+ .button = GetNpadColor(player.button_color_right),
};
- controller.colors_state.fullkey = controller.colors_state.left;
-
// Other or debug controller should always be a pro controller
if (npad_id_type != NpadIdType::Other) {
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
+ original_npad_type = npad_type;
} else {
SetNpadStyleIndex(NpadStyleIndex::ProController);
+ original_npad_type = npad_type;
}
if (player.connected) {
@@ -352,6 +356,7 @@ void EmulatedController::DisableConfiguration() {
Disconnect();
}
SetNpadStyleIndex(tmp_npad_type);
+ original_npad_type = tmp_npad_type;
}
// Apply temporary connected status to the real controller
@@ -557,6 +562,16 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
return;
}
+ // GC controllers have triggers not buttons
+ if (npad_type == NpadStyleIndex::GameCube) {
+ if (index == Settings::NativeButton::ZR) {
+ return;
+ }
+ if (index == Settings::NativeButton::ZL) {
+ return;
+ }
+ }
+
switch (index) {
case Settings::NativeButton::A:
controller.npad_button_state.a.Assign(current_status.value);
@@ -733,6 +748,11 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
return;
}
+ // Only GC controllers have analog triggers
+ if (npad_type != NpadStyleIndex::GameCube) {
+ return;
+ }
+
const auto& trigger = controller.trigger_values[index];
switch (index) {
@@ -949,6 +969,9 @@ bool EmulatedController::TestVibration(std::size_t device_index) {
// Send a slight vibration to test for rumble support
output_devices[device_index]->SetVibration(test_vibration);
+ // Wait for about 15ms to ensure the controller is ready for the stop command
+ std::this_thread::sleep_for(std::chrono::milliseconds(15));
+
// Stop any vibration and return the result
return output_devices[device_index]->SetVibration(zero_vibration) ==
Common::Input::VibrationError::None;
@@ -999,13 +1022,27 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
if (!is_connected) {
return;
}
+
+ // Attempt to reconnect with the original type
+ if (npad_type != original_npad_type) {
+ Disconnect();
+ const auto current_npad_type = npad_type;
+ SetNpadStyleIndex(original_npad_type);
+ if (IsControllerSupported()) {
+ Connect();
+ return;
+ }
+ SetNpadStyleIndex(current_npad_type);
+ Connect();
+ }
+
if (IsControllerSupported()) {
return;
}
Disconnect();
- // Fallback fullkey controllers to Pro controllers
+ // Fallback Fullkey controllers to Pro controllers
if (IsControllerFullkey() && supported_style_tag.fullkey) {
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
SetNpadStyleIndex(NpadStyleIndex::ProController);
@@ -1013,6 +1050,22 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
return;
}
+ // Fallback Dual joycon controllers to Pro controllers
+ if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
+ LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
+ SetNpadStyleIndex(NpadStyleIndex::ProController);
+ Connect();
+ return;
+ }
+
+ // Fallback Pro controllers to Dual joycon
+ if (npad_type == NpadStyleIndex::ProController && supported_style_tag.joycon_dual) {
+ LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
+ SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
+ Connect();
+ return;
+ }
+
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
npad_type);
}
@@ -1310,6 +1363,15 @@ const CameraState& EmulatedController::GetCamera() const {
return controller.camera_state;
}
+NpadColor EmulatedController::GetNpadColor(u32 color) {
+ return {
+ .r = static_cast<u8>((color >> 16) & 0xFF),
+ .g = static_cast<u8>((color >> 8) & 0xFF),
+ .b = static_cast<u8>(color & 0xFF),
+ .a = 0xff,
+ };
+}
+
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
std::scoped_lock lock{callback_mutex};
for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 823c1700c..c3aa8f9d3 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -425,6 +425,13 @@ private:
void SetCamera(const Common::Input::CallbackStatus& callback);
/**
+ * Converts a color format from bgra to rgba
+ * @param color in bgra format
+ * @return NpadColor in rgba format
+ */
+ NpadColor GetNpadColor(u32 color);
+
+ /**
* Triggers a callback that something has changed on the controller status
* @param type Input type of the event to trigger
* @param is_service_update indicates if this event should only be sent to HID services
@@ -433,6 +440,7 @@ private:
const NpadIdType npad_id_type;
NpadStyleIndex npad_type{NpadStyleIndex::None};
+ NpadStyleIndex original_npad_type{NpadStyleIndex::None};
NpadStyleTag supported_style_tag{NpadStyleSet::All};
bool is_connected{false};
bool is_configuring{false};
diff --git a/src/core/hid/hid_types.h b/src/core/hid/hid_types.h
index e49223016..e3b1cfbc6 100644
--- a/src/core/hid/hid_types.h
+++ b/src/core/hid/hid_types.h
@@ -327,10 +327,18 @@ struct TouchState {
};
static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size");
+struct NpadColor {
+ u8 r{};
+ u8 g{};
+ u8 b{};
+ u8 a{};
+};
+static_assert(sizeof(NpadColor) == 4, "NpadColor is an invalid size");
+
// This is nn::hid::NpadControllerColor
struct NpadControllerColor {
- u32 body{};
- u32 button{};
+ NpadColor body{};
+ NpadColor button{};
};
static_assert(sizeof(NpadControllerColor) == 8, "NpadControllerColor is an invalid size");
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 68d143a01..52fb69e9c 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -52,6 +52,9 @@ Common::Input::ButtonStatus TransformToButton(const Common::Input::CallbackStatu
Common::Input::ButtonStatus status{};
switch (callback.type) {
case Common::Input::InputType::Analog:
+ status.value = TransformToTrigger(callback).pressed.value;
+ status.toggle = callback.analog_status.properties.toggle;
+ break;
case Common::Input::InputType::Trigger:
status.value = TransformToTrigger(callback).pressed.value;
break;
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h
index 602e12606..416da15ec 100644
--- a/src/core/hle/ipc.h
+++ b/src/core/hle/ipc.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 004bb2005..d631c0357 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_client_port.cpp b/src/core/hle/kernel/k_client_port.cpp
index d63e77d15..3cb22ff4d 100644
--- a/src/core/hle/kernel/k_client_port.cpp
+++ b/src/core/hle/kernel/k_client_port.cpp
@@ -1,6 +1,5 @@
-// Copyright 2021 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2021 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/scope_exit.h"
#include "core/hle/kernel/hle_ipc.h"
diff --git a/src/core/hle/kernel/k_client_port.h b/src/core/hle/kernel/k_client_port.h
index ef8583efc..e17eff28f 100644
--- a/src/core/hle/kernel/k_client_port.h
+++ b/src/core/hle/kernel/k_client_port.h
@@ -1,6 +1,5 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2016 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp
index b662788b3..d3e99665f 100644
--- a/src/core/hle/kernel/k_process.cpp
+++ b/src/core/hle/kernel/k_process.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <bitset>
diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h
index 5e3e22ad8..d56d73bab 100644
--- a/src/core/hle/kernel/k_process.h
+++ b/src/core/hle/kernel/k_process.h
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp
index b77735736..8ff1545b6 100644
--- a/src/core/hle/kernel/k_shared_memory.cpp
+++ b/src/core/hle/kernel/k_shared_memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "core/core.h"
diff --git a/src/core/hle/kernel/k_shared_memory.h b/src/core/hle/kernel/k_shared_memory.h
index 2c1db0e70..34cb98456 100644
--- a/src/core/hle/kernel/k_shared_memory.h
+++ b/src/core/hle/kernel/k_shared_memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f4072e1c3..ce7fa8275 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -17,7 +17,6 @@
#include "common/thread.h"
#include "common/thread_worker.h"
#include "core/arm/arm_interface.h"
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
@@ -82,7 +81,7 @@ struct KernelCore::Impl {
void InitializeCores() {
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- cores[core_id].Initialize((*current_process).Is64BitProcess());
+ cores[core_id]->Initialize((*current_process).Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
@@ -100,7 +99,9 @@ struct KernelCore::Impl {
next_user_process_id = KProcess::ProcessIDMin;
next_thread_id = 1;
- cores.clear();
+ for (auto& core : cores) {
+ core = nullptr;
+ }
global_handle_table->Finalize();
global_handle_table.reset();
@@ -199,7 +200,7 @@ struct KernelCore::Impl {
const s32 core{static_cast<s32>(i)};
schedulers[i] = std::make_unique<Kernel::KScheduler>(system.Kernel());
- cores.emplace_back(i, system, *schedulers[i], interrupts);
+ cores[i] = std::make_unique<Kernel::PhysicalCore>(i, system, *schedulers[i]);
auto* main_thread{Kernel::KThread::Create(system.Kernel())};
main_thread->SetName(fmt::format("MainThread:{}", core));
@@ -761,7 +762,7 @@ struct KernelCore::Impl {
std::unordered_set<KAutoObject*> registered_in_use_objects;
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
- std::vector<Kernel::PhysicalCore> cores;
+ std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
@@ -785,7 +786,6 @@ struct KernelCore::Impl {
Common::ThreadWorker service_threads_manager;
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
- std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{};
@@ -874,11 +874,11 @@ const Kernel::KScheduler& KernelCore::Scheduler(std::size_t id) const {
}
Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) {
- return impl->cores[id];
+ return *impl->cores[id];
}
const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
- return impl->cores[id];
+ return *impl->cores[id];
}
size_t KernelCore::CurrentPhysicalCoreIndex() const {
@@ -890,11 +890,11 @@ size_t KernelCore::CurrentPhysicalCoreIndex() const {
}
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
- return impl->cores[CurrentPhysicalCoreIndex()];
+ return *impl->cores[CurrentPhysicalCoreIndex()];
}
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
- return impl->cores[CurrentPhysicalCoreIndex()];
+ return *impl->cores[CurrentPhysicalCoreIndex()];
}
Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -906,15 +906,6 @@ Kernel::KScheduler* KernelCore::CurrentScheduler() {
return impl->schedulers[core_id].get();
}
-std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts() {
- return impl->interrupts;
-}
-
-const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& KernelCore::Interrupts()
- const {
- return impl->interrupts;
-}
-
Kernel::TimeManager& KernelCore::TimeManager() {
return impl->time_manager;
}
@@ -939,24 +930,18 @@ const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
return *impl->global_object_list_container;
}
-void KernelCore::InterruptAllPhysicalCores() {
- for (auto& physical_core : impl->cores) {
- physical_core.Interrupt();
- }
-}
-
void KernelCore::InvalidateAllInstructionCaches() {
for (auto& physical_core : impl->cores) {
- physical_core.ArmInterface().ClearInstructionCache();
+ physical_core->ArmInterface().ClearInstructionCache();
}
}
void KernelCore::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
for (auto& physical_core : impl->cores) {
- if (!physical_core.IsInitialized()) {
+ if (!physical_core->IsInitialized()) {
continue;
}
- physical_core.ArmInterface().InvalidateCacheRange(addr, size);
+ physical_core->ArmInterface().InvalidateCacheRange(addr, size);
}
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 6c7cf6af2..bcf016a97 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -9,14 +9,12 @@
#include <string>
#include <unordered_map>
#include <vector>
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/k_auto_object.h"
#include "core/hle/kernel/k_slab_heap.h"
#include "core/hle/kernel/svc_common.h"
namespace Core {
-class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -183,12 +181,6 @@ public:
const KAutoObjectWithListContainer& ObjectListContainer() const;
- std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts();
-
- const std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES>& Interrupts() const;
-
- void InterruptAllPhysicalCores();
-
void InvalidateAllInstructionCaches();
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index 6e7dacf97..d4375962f 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "core/arm/cpu_interrupt_handler.h"
#include "core/arm/dynarmic/arm_dynarmic_32.h"
#include "core/arm/dynarmic/arm_dynarmic_64.h"
#include "core/core.h"
@@ -11,16 +10,14 @@
namespace Kernel {
-PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
- Core::CPUInterrupts& interrupts_)
- : core_index{core_index_}, system{system_}, scheduler{scheduler_},
- interrupts{interrupts_}, guard{std::make_unique<std::mutex>()} {
+PhysicalCore::PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_)
+ : core_index{core_index_}, system{system_}, scheduler{scheduler_} {
#ifdef ARCHITECTURE_x86_64
// TODO(bunnei): Initialization relies on a core being available. We may later replace this with
// a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager.
auto& kernel = system.Kernel();
arm_interface = std::make_unique<Core::ARM_Dynarmic_64>(
- system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
+ system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
#else
#error Platform not supported yet.
#endif
@@ -34,7 +31,7 @@ void PhysicalCore::Initialize([[maybe_unused]] bool is_64_bit) {
if (!is_64_bit) {
// We already initialized a 64-bit core, replace with a 32-bit one.
arm_interface = std::make_unique<Core::ARM_Dynarmic_32>(
- system, interrupts, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
+ system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), core_index);
}
#else
#error Platform not supported yet.
@@ -47,24 +44,26 @@ void PhysicalCore::Run() {
}
void PhysicalCore::Idle() {
- interrupts[core_index].AwaitInterrupt();
+ std::unique_lock lk{guard};
+ on_interrupt.wait(lk, [this] { return is_interrupted; });
}
bool PhysicalCore::IsInterrupted() const {
- return interrupts[core_index].IsInterrupted();
+ return is_interrupted;
}
void PhysicalCore::Interrupt() {
- guard->lock();
- interrupts[core_index].SetInterrupt(true);
+ std::unique_lock lk{guard};
+ is_interrupted = true;
arm_interface->SignalInterrupt();
- guard->unlock();
+ on_interrupt.notify_all();
}
void PhysicalCore::ClearInterrupt() {
- guard->lock();
- interrupts[core_index].SetInterrupt(false);
- guard->unlock();
+ std::unique_lock lk{guard};
+ is_interrupted = false;
+ arm_interface->ClearInterrupt();
+ on_interrupt.notify_all();
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 898d1e5db..2fc8d4be2 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -14,7 +14,6 @@ class KScheduler;
} // namespace Kernel
namespace Core {
-class CPUInterruptHandler;
class ExclusiveMonitor;
class System;
} // namespace Core
@@ -23,15 +22,11 @@ namespace Kernel {
class PhysicalCore {
public:
- PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_,
- Core::CPUInterrupts& interrupts_);
+ PhysicalCore(std::size_t core_index_, Core::System& system_, KScheduler& scheduler_);
~PhysicalCore();
- PhysicalCore(const PhysicalCore&) = delete;
- PhysicalCore& operator=(const PhysicalCore&) = delete;
-
- PhysicalCore(PhysicalCore&&) = default;
- PhysicalCore& operator=(PhysicalCore&&) = delete;
+ YUZU_NON_COPYABLE(PhysicalCore);
+ YUZU_NON_MOVEABLE(PhysicalCore);
/// Initialize the core for the specified parameters.
void Initialize(bool is_64_bit);
@@ -86,9 +81,11 @@ private:
const std::size_t core_index;
Core::System& system;
Kernel::KScheduler& scheduler;
- Core::CPUInterrupts& interrupts;
- std::unique_ptr<std::mutex> guard;
+
+ std::mutex guard;
+ std::condition_variable on_interrupt;
std::unique_ptr<Core::ARM_Interface> arm_interface;
+ bool is_interrupted;
};
} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index aa9e5b89d..47a1b829b 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -118,6 +117,7 @@ union Result {
BitField<0, 9, ErrorModule> module;
BitField<9, 13, u32> description;
+ Result() = default;
constexpr explicit Result(u32 raw_) : raw(raw_) {}
constexpr Result(ErrorModule module_, u32 description_)
@@ -131,6 +131,7 @@ union Result {
return !IsSuccess();
}
};
+static_assert(std::is_trivial_v<Result>);
[[nodiscard]] constexpr bool operator==(const Result& a, const Result& b) {
return a.raw == b.raw;
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index def105832..bb838e285 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -534,7 +534,7 @@ public:
private:
void CheckAvailability(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_ACC, "(STUBBED) called");
+ LOG_DEBUG(Service_ACC, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push(false); // TODO: Check when this is supposed to return true and when not
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 118f226e4..6fb7e198e 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -754,7 +754,7 @@ void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ LOG_DEBUG(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/am/applets/applet_software_keyboard.cpp b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
index faa092957..c18236045 100644
--- a/src/core/hle/service/am/applets/applet_software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/applet_software_keyboard.cpp
@@ -536,6 +536,8 @@ void SoftwareKeyboard::InitializeFrontendNormalKeyboard() {
.sub_text{std::move(sub_text)},
.guide_text{std::move(guide_text)},
.initial_text{initial_text},
+ .left_optional_symbol_key{swkbd_config_common.left_optional_symbol_key},
+ .right_optional_symbol_key{swkbd_config_common.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -591,6 +593,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardOld() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
@@ -632,6 +636,8 @@ void SoftwareKeyboard::InitializeFrontendInlineKeyboardNew() {
.sub_text{},
.guide_text{},
.initial_text{current_text},
+ .left_optional_symbol_key{appear_arg.left_optional_symbol_key},
+ .right_optional_symbol_key{appear_arg.right_optional_symbol_key},
.max_text_length{max_text_length},
.min_text_length{min_text_length},
.initial_cursor_position{initial_cursor_position},
diff --git a/src/core/hle/service/am/applets/applet_web_browser.cpp b/src/core/hle/service/am/applets/applet_web_browser.cpp
index 4b804b78c..14aa6f69e 100644
--- a/src/core/hle/service/am/applets/applet_web_browser.cpp
+++ b/src/core/hle/service/am/applets/applet_web_browser.cpp
@@ -21,7 +21,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applet_web_browser.h"
#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
#include "core/loader/loader.h"
namespace Service::AM::Applets {
diff --git a/src/core/hle/service/apm/apm_controller.cpp b/src/core/hle/service/apm/apm_controller.cpp
index 4e710491b..d6de84066 100644
--- a/src/core/hle/service/apm/apm_controller.cpp
+++ b/src/core/hle/service/apm/apm_controller.cpp
@@ -80,7 +80,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
}
void Controller::SetClockSpeed(u32 mhz) {
- LOG_INFO(Service_APM, "called, mhz={:08X}", mhz);
+ LOG_DEBUG(Service_APM, "called, mhz={:08X}", mhz);
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
// TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
}
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index a44dd842a..49c092301 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -246,9 +246,8 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
const auto write_count =
static_cast<u32>(ctx.GetWriteBufferSize() / sizeof(AudioDevice::AudioDeviceName));
std::vector<AudioDevice::AudioDeviceName> device_names{};
- std::string print_names{};
if (write_count > 0) {
- device_names.push_back(AudioDevice::AudioDeviceName("DeviceOut"));
+ device_names.emplace_back("DeviceOut");
LOG_DEBUG(Service_Audio, "called. \nName=DeviceOut");
} else {
LOG_DEBUG(Service_Audio, "called. Empty buffer passed in.");
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 381a66ba5..6fb07c37d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -50,7 +50,7 @@ public:
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
{8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
{9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
- {10, nullptr, "RequestUpdateAuto"},
+ {10, &IAudioRenderer::RequestUpdate, "RequestUpdateAuto"},
{11, nullptr, "ExecuteAudioRendererRendering"},
};
// clang-format on
@@ -113,15 +113,30 @@ private:
// These buffers are written manually to avoid an issue with WriteBuffer throwing errors for
// checking size 0. Performance size is 0 for most games.
- const auto buffers{ctx.BufferDescriptorB()};
- std::vector<u8> output(buffers[0].Size(), 0);
- std::vector<u8> performance(buffers[1].Size(), 0);
+
+ std::vector<u8> output{};
+ std::vector<u8> performance{};
+ auto is_buffer_b{ctx.BufferDescriptorB()[0].Size() != 0};
+ if (is_buffer_b) {
+ const auto buffersB{ctx.BufferDescriptorB()};
+ output.resize(buffersB[0].Size(), 0);
+ performance.resize(buffersB[1].Size(), 0);
+ } else {
+ const auto buffersC{ctx.BufferDescriptorC()};
+ output.resize(buffersC[0].Size(), 0);
+ performance.resize(buffersC[1].Size(), 0);
+ }
auto result = impl->RequestUpdate(input, performance, output);
if (result.IsSuccess()) {
- ctx.WriteBufferB(output.data(), output.size(), 0);
- ctx.WriteBufferB(performance.data(), performance.size(), 1);
+ if (is_buffer_b) {
+ ctx.WriteBufferB(output.data(), output.size(), 0);
+ ctx.WriteBufferB(performance.data(), performance.size(), 1);
+ } else {
+ ctx.WriteBufferC(output.data(), output.size(), 0);
+ ctx.WriteBufferC(performance.data(), performance.size(), 1);
+ }
} else {
LOG_ERROR(Service_Audio, "RequestUpdate failed error 0x{:02X}!", result.description);
}
@@ -237,7 +252,7 @@ private:
std::vector<AudioDevice::AudioDeviceName> out_names{};
- u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
+ const u32 out_count = impl->ListAudioDeviceName(out_names, in_count);
std::string out{};
for (u32 i = 0; i < out_count; i++) {
@@ -350,7 +365,7 @@ private:
std::vector<AudioDevice::AudioDeviceName> out_names{};
- u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
+ const u32 out_count = impl->ListAudioOutputDeviceName(out_names, in_count);
std::string out{};
for (u32 i = 0; i < out_count; i++) {
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index fae6e5aff..e23eae36a 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -246,7 +246,8 @@ static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vec
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
- entries.emplace_back(new_entry->GetName(), type, new_entry->GetSize());
+ entries.emplace_back(new_entry->GetName(), type,
+ type == FileSys::EntryType::Directory ? 0 : new_entry->GetSize());
}
}
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 3c28dee76..cb29004e8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -163,28 +163,51 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
}
LOG_DEBUG(Service_HID, "Npad connected {}", npad_id);
const auto controller_type = controller.device->GetNpadStyleIndex();
+ const auto& body_colors = controller.device->GetColors();
+ const auto& battery_level = controller.device->GetBattery();
auto* shared_memory = controller.shared_memory;
if (controller_type == Core::HID::NpadStyleIndex::None) {
controller.styleset_changed_event->GetWritableEvent().Signal();
return;
}
+
+ // Reset memory values
shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None;
shared_memory->device_type.raw = 0;
shared_memory->system_properties.raw = 0;
+ shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+ shared_memory->joycon_color.attribute = ColorAttribute::NoController;
+ shared_memory->fullkey_color = {};
+ shared_memory->joycon_color.left = {};
+ shared_memory->joycon_color.right = {};
+ shared_memory->battery_level_dual = {};
+ shared_memory->battery_level_left = {};
+ shared_memory->battery_level_right = {};
+
switch (controller_type) {
case Core::HID::NpadStyleIndex::None:
ASSERT(false);
break;
case Core::HID::NpadStyleIndex::ProController:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+ shared_memory->battery_level_dual = battery_level.dual.battery_level;
shared_memory->style_tag.fullkey.Assign(1);
shared_memory->device_type.fullkey.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.dual.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::SwitchProController;
shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::Handheld:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->fullkey_color.fullkey = body_colors.fullkey;
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->joycon_color.right = body_colors.right;
shared_memory->style_tag.handheld.Assign(1);
shared_memory->device_type.handheld_left.Assign(1);
shared_memory->device_type.handheld_right.Assign(1);
@@ -192,47 +215,86 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory->system_properties.use_plus.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
shared_memory->system_properties.use_directional_buttons.Assign(1);
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory->applet_nfc_xcd.applet_footer.type =
AppletFooterUiType::HandheldJoyConLeftJoyConRight;
shared_memory->sixaxis_handheld_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconDual:
+ shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
shared_memory->style_tag.joycon_dual.Assign(1);
if (controller.is_dual_left_connected) {
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->battery_level_left = battery_level.left.battery_level;
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
shared_memory->sixaxis_dual_left_properties.is_newly_assigned.Assign(1);
}
if (controller.is_dual_right_connected) {
+ shared_memory->joycon_color.right = body_colors.right;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
shared_memory->sixaxis_dual_right_properties.is_newly_assigned.Assign(1);
}
shared_memory->system_properties.use_directional_buttons.Assign(1);
shared_memory->system_properties.is_vertical.Assign(1);
shared_memory->assignment_mode = NpadJoyAssignmentMode::Dual;
+
if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDual;
+ shared_memory->fullkey_color.fullkey = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
} else if (controller.is_dual_left_connected) {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+ shared_memory->fullkey_color.fullkey = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.left.is_charging);
} else {
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+ shared_memory->fullkey_color.fullkey = body_colors.right;
+ shared_memory->battery_level_dual = battery_level.right.battery_level;
+ shared_memory->system_properties.is_charging_joy_dual.Assign(
+ battery_level.right.is_charging);
}
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.left = body_colors.left;
+ shared_memory->battery_level_dual = battery_level.left.battery_level;
shared_memory->style_tag.joycon_left.Assign(1);
shared_memory->device_type.joycon_left.Assign(1);
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_minus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_left.Assign(
+ battery_level.left.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
break;
case Core::HID::NpadStyleIndex::JoyconRight:
+ shared_memory->joycon_color.attribute = ColorAttribute::Ok;
+ shared_memory->joycon_color.right = body_colors.right;
+ shared_memory->battery_level_right = battery_level.right.battery_level;
shared_memory->style_tag.joycon_right.Assign(1);
shared_memory->device_type.joycon_right.Assign(1);
shared_memory->system_properties.is_horizontal.Assign(1);
shared_memory->system_properties.use_plus.Assign(1);
+ shared_memory->system_properties.is_charging_joy_right.Assign(
+ battery_level.right.is_charging);
shared_memory->applet_nfc_xcd.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
break;
@@ -269,21 +331,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
break;
}
- const auto& body_colors = controller.device->GetColors();
-
- shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
- shared_memory->fullkey_color.fullkey = body_colors.fullkey;
-
- shared_memory->joycon_color.attribute = ColorAttribute::Ok;
- shared_memory->joycon_color.left = body_colors.left;
- shared_memory->joycon_color.right = body_colors.right;
-
- // TODO: Investigate when we should report all batery types
- const auto& battery_level = controller.device->GetBattery();
- shared_memory->battery_level_dual = battery_level.dual.battery_level;
- shared_memory->battery_level_left = battery_level.left.battery_level;
- shared_memory->battery_level_right = battery_level.right.battery_level;
-
controller.is_connected = true;
controller.device->Connect();
SignalStyleSetChangedEvent(npad_id);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 5ecbddf94..3d3457160 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -819,12 +819,12 @@ void Hid::EnableSixAxisSensorUnalteredPassthrough(Kernel::HLERequestContext& ctx
const auto result = controller.EnableSixAxisSensorUnalteredPassthrough(
parameters.sixaxis_handle, parameters.enabled);
- LOG_WARNING(Service_HID,
- "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
- "applet_resource_user_id={}",
- parameters.enabled, parameters.sixaxis_handle.npad_type,
- parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
- parameters.applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "(STUBBED) called, enabled={}, npad_type={}, npad_id={}, device_index={}, "
+ "applet_resource_user_id={}",
+ parameters.enabled, parameters.sixaxis_handle.npad_type,
+ parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -846,7 +846,7 @@ void Hid::IsSixAxisSensorUnalteredPassthroughEnabled(Kernel::HLERequestContext&
const auto result = controller.IsSixAxisSensorUnalteredPassthroughEnabled(
parameters.sixaxis_handle, is_unaltered_sisxaxis_enabled);
- LOG_WARNING(
+ LOG_DEBUG(
Service_HID,
"(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
@@ -2146,12 +2146,18 @@ public:
{324, nullptr, "GetUniquePadButtonSet"},
{325, nullptr, "GetUniquePadColor"},
{326, nullptr, "GetUniquePadAppletDetailedUiType"},
+ {327, nullptr, "GetAbstractedPadIdDataFromNpad"},
+ {328, nullptr, "AttachAbstractedPadToNpad"},
+ {329, nullptr, "DetachAbstractedPadAll"},
+ {330, nullptr, "CheckAbstractedPadConnection"},
{500, nullptr, "SetAppletResourceUserId"},
{501, nullptr, "RegisterAppletResourceUserId"},
{502, nullptr, "UnregisterAppletResourceUserId"},
{503, nullptr, "EnableAppletToGetInput"},
{504, nullptr, "SetAruidValidForVibration"},
{505, nullptr, "EnableAppletToGetSixAxisSensor"},
+ {506, nullptr, "EnableAppletToGetPadInput"},
+ {507, nullptr, "EnableAppletToGetTouchScreen"},
{510, nullptr, "SetVibrationMasterVolume"},
{511, nullptr, "GetVibrationMasterVolume"},
{512, nullptr, "BeginPermitVibrationSession"},
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index d5107e41f..c4b44cbf9 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -166,7 +166,7 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
if (result.IsSuccess()) {
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
- MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device);
+ MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device);
auto& image_transfer_processor =
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
image_transfer_processor.SetConfig(parameters.processor_config);
diff --git a/src/core/hle/service/hid/irs_ring_lifo.h b/src/core/hle/service/hid/irs_ring_lifo.h
new file mode 100644
index 000000000..255d1d296
--- /dev/null
+++ b/src/core/hle/service/hid/irs_ring_lifo.h
@@ -0,0 +1,47 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <array>
+
+#include "common/common_types.h"
+
+namespace Service::IRS {
+
+template <typename State, std::size_t max_buffer_size>
+struct Lifo {
+ s64 sampling_number{};
+ s64 buffer_count{};
+ std::array<State, max_buffer_size> entries{};
+
+ const State& ReadCurrentEntry() const {
+ return entries[GetBufferTail()];
+ }
+
+ const State& ReadPreviousEntry() const {
+ return entries[GetPreviousEntryIndex()];
+ }
+
+ s64 GetBufferTail() const {
+ return sampling_number % max_buffer_size;
+ }
+
+ std::size_t GetPreviousEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + max_buffer_size - 1) % max_buffer_size);
+ }
+
+ std::size_t GetNextEntryIndex() const {
+ return static_cast<size_t>((GetBufferTail() + 1) % max_buffer_size);
+ }
+
+ void WriteNextEntry(const State& new_state) {
+ if (buffer_count < static_cast<s64>(max_buffer_size)) {
+ buffer_count++;
+ }
+ sampling_number++;
+ entries[GetBufferTail()] = new_state;
+ }
+};
+
+} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.cpp b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
index 6479af212..e2f4ae876 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.cpp
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.cpp
@@ -1,34 +1,265 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
+#include <queue>
+
+#include "core/hid/emulated_controller.h"
+#include "core/hid/hid_core.h"
#include "core/hle/service/hid/irsensor/clustering_processor.h"
namespace Service::IRS {
-ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format)
- : device(device_format) {
+ClusteringProcessor::ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index)
+ : device{device_format} {
+ npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
+
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
+ SetDefaultConfig();
+
+ shared_memory = std::construct_at(
+ reinterpret_cast<ClusteringSharedMemory*>(&device_format.state.processor_raw_data));
+
+ Core::HID::ControllerUpdateCallback engine_callback{
+ .on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
+ .is_npad_service = true,
+ };
+ callback_key = npad_device->SetCallback(engine_callback);
}
-ClusteringProcessor::~ClusteringProcessor() = default;
+ClusteringProcessor::~ClusteringProcessor() {
+ npad_device->DeleteCallback(callback_key);
+};
-void ClusteringProcessor::StartProcessor() {}
+void ClusteringProcessor::StartProcessor() {
+ device.camera_status = Core::IrSensor::IrCameraStatus::Available;
+ device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
+}
void ClusteringProcessor::SuspendProcessor() {}
void ClusteringProcessor::StopProcessor() {}
+void ClusteringProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
+ if (type != Core::HID::ControllerTriggerType::IrSensor) {
+ return;
+ }
+
+ next_state = {};
+ const auto camera_data = npad_device->GetCamera();
+ auto filtered_image = camera_data.data;
+
+ RemoveLowIntensityData(filtered_image);
+
+ const auto window_start_x = static_cast<std::size_t>(current_config.window_of_interest.x);
+ const auto window_start_y = static_cast<std::size_t>(current_config.window_of_interest.y);
+ const auto window_end_x =
+ window_start_x + static_cast<std::size_t>(current_config.window_of_interest.width);
+ const auto window_end_y =
+ window_start_y + static_cast<std::size_t>(current_config.window_of_interest.height);
+
+ for (std::size_t y = window_start_y; y < window_end_y; y++) {
+ for (std::size_t x = window_start_x; x < window_end_x; x++) {
+ u8 pixel = GetPixel(filtered_image, x, y);
+ if (pixel == 0) {
+ continue;
+ }
+ const auto cluster = GetClusterProperties(filtered_image, x, y);
+ if (cluster.pixel_count > current_config.pixel_count_max) {
+ continue;
+ }
+ if (cluster.pixel_count < current_config.pixel_count_min) {
+ continue;
+ }
+ // Cluster object limit reached
+ if (next_state.object_count >= next_state.data.size()) {
+ continue;
+ }
+ next_state.data[next_state.object_count] = cluster;
+ next_state.object_count++;
+ }
+ }
+
+ next_state.sampling_number = camera_data.sample;
+ next_state.timestamp = next_state.timestamp + 131;
+ next_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
+ shared_memory->clustering_lifo.WriteNextEntry(next_state);
+
+ if (!IsProcessorActive()) {
+ StartProcessor();
+ }
+}
+
+void ClusteringProcessor::RemoveLowIntensityData(std::vector<u8>& data) {
+ for (u8& pixel : data) {
+ if (pixel < current_config.pixel_count_min) {
+ pixel = 0;
+ }
+ }
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetClusterProperties(std::vector<u8>& data,
+ std::size_t x,
+ std::size_t y) {
+ using DataPoint = Common::Point<std::size_t>;
+ std::queue<DataPoint> search_points{};
+ ClusteringData current_cluster = GetPixelProperties(data, x, y);
+ SetPixel(data, x, y, 0);
+ search_points.emplace<DataPoint>({x, y});
+
+ while (!search_points.empty()) {
+ const auto point = search_points.front();
+ search_points.pop();
+
+ // Avoid negative numbers
+ if (point.x == 0 || point.y == 0) {
+ continue;
+ }
+
+ std::array<DataPoint, 4> new_points{
+ DataPoint{point.x - 1, point.y},
+ {point.x, point.y - 1},
+ {point.x + 1, point.y},
+ {point.x, point.y + 1},
+ };
+
+ for (const auto new_point : new_points) {
+ if (new_point.x >= width) {
+ continue;
+ }
+ if (new_point.y >= height) {
+ continue;
+ }
+ if (GetPixel(data, new_point.x, new_point.y) < current_config.object_intensity_min) {
+ continue;
+ }
+ const ClusteringData cluster = GetPixelProperties(data, new_point.x, new_point.y);
+ current_cluster = MergeCluster(current_cluster, cluster);
+ SetPixel(data, new_point.x, new_point.y, 0);
+ search_points.emplace<DataPoint>({new_point.x, new_point.y});
+ }
+ }
+
+ return current_cluster;
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::GetPixelProperties(
+ const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ return {
+ .average_intensity = GetPixel(data, x, y) / 255.0f,
+ .centroid =
+ {
+ .x = static_cast<f32>(x),
+ .y = static_cast<f32>(y),
+
+ },
+ .pixel_count = 1,
+ .bound =
+ {
+ .x = static_cast<s16>(x),
+ .y = static_cast<s16>(y),
+ .width = 1,
+ .height = 1,
+ },
+ };
+}
+
+ClusteringProcessor::ClusteringData ClusteringProcessor::MergeCluster(
+ const ClusteringData a, const ClusteringData b) const {
+ const f32 a_pixel_count = static_cast<f32>(a.pixel_count);
+ const f32 b_pixel_count = static_cast<f32>(b.pixel_count);
+ const f32 pixel_count = a_pixel_count + b_pixel_count;
+ const f32 average_intensity =
+ (a.average_intensity * a_pixel_count + b.average_intensity * b_pixel_count) / pixel_count;
+ const Core::IrSensor::IrsCentroid centroid = {
+ .x = (a.centroid.x * a_pixel_count + b.centroid.x * b_pixel_count) / pixel_count,
+ .y = (a.centroid.y * a_pixel_count + b.centroid.y * b_pixel_count) / pixel_count,
+ };
+ s16 bound_start_x = a.bound.x < b.bound.x ? a.bound.x : b.bound.x;
+ s16 bound_start_y = a.bound.y < b.bound.y ? a.bound.y : b.bound.y;
+ s16 a_bound_end_x = a.bound.x + a.bound.width;
+ s16 a_bound_end_y = a.bound.y + a.bound.height;
+ s16 b_bound_end_x = b.bound.x + b.bound.width;
+ s16 b_bound_end_y = b.bound.y + b.bound.height;
+
+ const Core::IrSensor::IrsRect bound = {
+ .x = bound_start_x,
+ .y = bound_start_y,
+ .width = a_bound_end_x > b_bound_end_x ? static_cast<s16>(a_bound_end_x - bound_start_x)
+ : static_cast<s16>(b_bound_end_x - bound_start_x),
+ .height = a_bound_end_y > b_bound_end_y ? static_cast<s16>(a_bound_end_y - bound_start_y)
+ : static_cast<s16>(b_bound_end_y - bound_start_y),
+ };
+
+ return {
+ .average_intensity = average_intensity,
+ .centroid = centroid,
+ .pixel_count = static_cast<u32>(pixel_count),
+ .bound = bound,
+ };
+}
+
+u8 ClusteringProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
+ if ((y * width) + x > data.size()) {
+ return 0;
+ }
+ return data[(y * width) + x];
+}
+
+void ClusteringProcessor::SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value) {
+ if ((y * width) + x > data.size()) {
+ return;
+ }
+ data[(y * width) + x] = value;
+}
+
+void ClusteringProcessor::SetDefaultConfig() {
+ using namespace std::literals::chrono_literals;
+ current_config.camera_config.exposure_time = std::chrono::microseconds(200ms).count();
+ current_config.camera_config.gain = 2;
+ current_config.camera_config.is_negative_used = false;
+ current_config.camera_config.light_target = Core::IrSensor::CameraLightTarget::BrightLeds;
+ current_config.window_of_interest = {
+ .x = 0,
+ .y = 0,
+ .width = width,
+ .height = height,
+ };
+ current_config.pixel_count_min = 3;
+ current_config.pixel_count_max = static_cast<u32>(GetDataSize(format));
+ current_config.is_external_light_filter_enabled = true;
+ current_config.object_intensity_min = 150;
+
+ npad_device->SetCameraFormat(format);
+}
+
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
current_config.camera_config.gain = config.camera_config.gain;
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
current_config.camera_config.light_target =
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
+ current_config.window_of_interest = config.window_of_interest;
current_config.pixel_count_min = config.pixel_count_min;
current_config.pixel_count_max = config.pixel_count_max;
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
current_config.object_intensity_min = config.object_intensity_min;
+
+ LOG_INFO(Service_IRS,
+ "Processor config, exposure_time={}, gain={}, is_negative_used={}, "
+ "light_target={}, window_of_interest=({}, {}, {}, {}), pixel_count_min={}, "
+ "pixel_count_max={}, is_external_light_filter_enabled={}, object_intensity_min={}",
+ current_config.camera_config.exposure_time, current_config.camera_config.gain,
+ current_config.camera_config.is_negative_used,
+ current_config.camera_config.light_target, current_config.window_of_interest.x,
+ current_config.window_of_interest.y, current_config.window_of_interest.width,
+ current_config.window_of_interest.height, current_config.pixel_count_min,
+ current_config.pixel_count_max, current_config.is_external_light_filter_enabled,
+ current_config.object_intensity_min);
+
+ npad_device->SetCameraFormat(format);
}
} // namespace Service::IRS
diff --git a/src/core/hle/service/hid/irsensor/clustering_processor.h b/src/core/hle/service/hid/irsensor/clustering_processor.h
index 6e2ba8846..dc01a8ea7 100644
--- a/src/core/hle/service/hid/irsensor/clustering_processor.h
+++ b/src/core/hle/service/hid/irsensor/clustering_processor.h
@@ -5,12 +5,19 @@
#include "common/common_types.h"
#include "core/hid/irs_types.h"
+#include "core/hle/service/hid/irs_ring_lifo.h"
#include "core/hle/service/hid/irsensor/processor_base.h"
+namespace Core::HID {
+class EmulatedController;
+} // namespace Core::HID
+
namespace Service::IRS {
class ClusteringProcessor final : public ProcessorBase {
public:
- explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format);
+ explicit ClusteringProcessor(Core::HID::HIDCore& hid_core_,
+ Core::IrSensor::DeviceFormat& device_format,
+ std::size_t npad_index);
~ClusteringProcessor() override;
// Called when the processor is initialized
@@ -26,6 +33,10 @@ public:
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
private:
+ static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size320x240;
+ static constexpr std::size_t width = 320;
+ static constexpr std::size_t height = 240;
+
// This is nn::irsensor::ClusteringProcessorConfig
struct ClusteringProcessorConfig {
Core::IrSensor::CameraConfig camera_config;
@@ -68,7 +79,32 @@ private:
static_assert(sizeof(ClusteringProcessorState) == 0x198,
"ClusteringProcessorState is an invalid size");
+ struct ClusteringSharedMemory {
+ Service::IRS::Lifo<ClusteringProcessorState, 6> clustering_lifo;
+ static_assert(sizeof(clustering_lifo) == 0x9A0, "clustering_lifo is an invalid size");
+ INSERT_PADDING_WORDS(0x11F);
+ };
+ static_assert(sizeof(ClusteringSharedMemory) == 0xE20,
+ "ClusteringSharedMemory is an invalid size");
+
+ void OnControllerUpdate(Core::HID::ControllerTriggerType type);
+ void RemoveLowIntensityData(std::vector<u8>& data);
+ ClusteringData GetClusterProperties(std::vector<u8>& data, std::size_t x, std::size_t y);
+ ClusteringData GetPixelProperties(const std::vector<u8>& data, std::size_t x,
+ std::size_t y) const;
+ ClusteringData MergeCluster(const ClusteringData a, const ClusteringData b) const;
+ u8 GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const;
+ void SetPixel(std::vector<u8>& data, std::size_t x, std::size_t y, u8 value);
+
+ // Sets config parameters of the camera
+ void SetDefaultConfig();
+
+ ClusteringSharedMemory* shared_memory = nullptr;
+ ClusteringProcessorState next_state{};
+
ClusteringProcessorConfig current_config{};
Core::IrSensor::DeviceFormat& device;
+ Core::HID::EmulatedController* npad_device;
+ int callback_key{};
};
} // namespace Service::IRS
diff --git a/src/core/hle/service/ldn/errors.h b/src/core/hle/service/ldn/errors.h
deleted file mode 100644
index 972a74806..000000000
--- a/src/core/hle/service/ldn/errors.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/hle/result.h"
-
-namespace Service::LDN {
-
-constexpr Result ERROR_DISABLED{ErrorModule::LDN, 22};
-
-} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 125d4dc4c..c11daff54 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -3,11 +3,15 @@
#include <memory>
-#include "core/hle/ipc_helpers.h"
-#include "core/hle/result.h"
-#include "core/hle/service/ldn/errors.h"
+#include "core/core.h"
#include "core/hle/service/ldn/ldn.h"
-#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/ldn/ldn_results.h"
+#include "core/hle/service/ldn/ldn_types.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+
+// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
+#undef CreateEvent
namespace Service::LDN {
@@ -100,74 +104,418 @@ class IUserLocalCommunicationService final
: public ServiceFramework<IUserLocalCommunicationService> {
public:
explicit IUserLocalCommunicationService(Core::System& system_)
- : ServiceFramework{system_, "IUserLocalCommunicationService"} {
+ : ServiceFramework{system_, "IUserLocalCommunicationService", ServiceThreadType::CreateNew},
+ service_context{system, "IUserLocalCommunicationService"}, room_network{
+ system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IUserLocalCommunicationService::GetState, "GetState"},
- {1, nullptr, "GetNetworkInfo"},
+ {1, &IUserLocalCommunicationService::GetNetworkInfo, "GetNetworkInfo"},
{2, nullptr, "GetIpv4Address"},
- {3, nullptr, "GetDisconnectReason"},
- {4, nullptr, "GetSecurityParameter"},
- {5, nullptr, "GetNetworkConfig"},
- {100, nullptr, "AttachStateChangeEvent"},
- {101, nullptr, "GetNetworkInfoLatestUpdate"},
- {102, nullptr, "Scan"},
- {103, nullptr, "ScanPrivate"},
+ {3, &IUserLocalCommunicationService::GetDisconnectReason, "GetDisconnectReason"},
+ {4, &IUserLocalCommunicationService::GetSecurityParameter, "GetSecurityParameter"},
+ {5, &IUserLocalCommunicationService::GetNetworkConfig, "GetNetworkConfig"},
+ {100, &IUserLocalCommunicationService::AttachStateChangeEvent, "AttachStateChangeEvent"},
+ {101, &IUserLocalCommunicationService::GetNetworkInfoLatestUpdate, "GetNetworkInfoLatestUpdate"},
+ {102, &IUserLocalCommunicationService::Scan, "Scan"},
+ {103, &IUserLocalCommunicationService::ScanPrivate, "ScanPrivate"},
{104, nullptr, "SetWirelessControllerRestriction"},
- {200, nullptr, "OpenAccessPoint"},
- {201, nullptr, "CloseAccessPoint"},
- {202, nullptr, "CreateNetwork"},
- {203, nullptr, "CreateNetworkPrivate"},
- {204, nullptr, "DestroyNetwork"},
+ {200, &IUserLocalCommunicationService::OpenAccessPoint, "OpenAccessPoint"},
+ {201, &IUserLocalCommunicationService::CloseAccessPoint, "CloseAccessPoint"},
+ {202, &IUserLocalCommunicationService::CreateNetwork, "CreateNetwork"},
+ {203, &IUserLocalCommunicationService::CreateNetworkPrivate, "CreateNetworkPrivate"},
+ {204, &IUserLocalCommunicationService::DestroyNetwork, "DestroyNetwork"},
{205, nullptr, "Reject"},
- {206, nullptr, "SetAdvertiseData"},
- {207, nullptr, "SetStationAcceptPolicy"},
- {208, nullptr, "AddAcceptFilterEntry"},
+ {206, &IUserLocalCommunicationService::SetAdvertiseData, "SetAdvertiseData"},
+ {207, &IUserLocalCommunicationService::SetStationAcceptPolicy, "SetStationAcceptPolicy"},
+ {208, &IUserLocalCommunicationService::AddAcceptFilterEntry, "AddAcceptFilterEntry"},
{209, nullptr, "ClearAcceptFilter"},
- {300, nullptr, "OpenStation"},
- {301, nullptr, "CloseStation"},
- {302, nullptr, "Connect"},
+ {300, &IUserLocalCommunicationService::OpenStation, "OpenStation"},
+ {301, &IUserLocalCommunicationService::CloseStation, "CloseStation"},
+ {302, &IUserLocalCommunicationService::Connect, "Connect"},
{303, nullptr, "ConnectPrivate"},
- {304, nullptr, "Disconnect"},
- {400, nullptr, "Initialize"},
- {401, nullptr, "Finalize"},
- {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"}, // 7.0.0+
+ {304, &IUserLocalCommunicationService::Disconnect, "Disconnect"},
+ {400, &IUserLocalCommunicationService::Initialize, "Initialize"},
+ {401, &IUserLocalCommunicationService::Finalize, "Finalize"},
+ {402, &IUserLocalCommunicationService::Initialize2, "Initialize2"},
};
// clang-format on
RegisterHandlers(functions);
+
+ state_change_event =
+ service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
+ }
+
+ ~IUserLocalCommunicationService() {
+ service_context.CloseEvent(state_change_event);
+ }
+
+ void OnEventFired() {
+ state_change_event->GetWritableEvent().Signal();
}
void GetState(Kernel::HLERequestContext& ctx) {
+ State state = State::Error;
+ LOG_WARNING(Service_LDN, "(STUBBED) called, state = {}", state);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(state);
+ }
+
+ void GetNetworkInfo(Kernel::HLERequestContext& ctx) {
+ const auto write_buffer_size = ctx.GetWriteBufferSize();
+
+ if (write_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", write_buffer_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo network_info{};
+ const auto rc = ResultSuccess;
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
+ network_info.common.ssid.GetStringValue(), network_info.ldn.node_count);
+
+ ctx.WriteBuffer<NetworkInfo>(network_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ }
+
+ void GetDisconnectReason(Kernel::HLERequestContext& ctx) {
+ const auto disconnect_reason = DisconnectReason::None;
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, disconnect_reason={}", disconnect_reason);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(disconnect_reason);
+ }
+
+ void GetSecurityParameter(Kernel::HLERequestContext& ctx) {
+ SecurityParameter security_parameter{};
+ NetworkInfo info{};
+ const Result rc = ResultSuccess;
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ security_parameter.session_id = info.network_id.session_id;
+ std::memcpy(security_parameter.data.data(), info.ldn.security_parameter.data(),
+ sizeof(SecurityParameter::data));
+
LOG_WARNING(Service_LDN, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<SecurityParameter>(security_parameter);
+ }
+
+ void GetNetworkConfig(Kernel::HLERequestContext& ctx) {
+ NetworkConfig config{};
+ NetworkInfo info{};
+ const Result rc = ResultSuccess;
+
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkConfig is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ config.intent_id = info.network_id.intent_id;
+ config.channel = info.common.channel;
+ config.node_count_max = info.ldn.node_count_max;
+ config.local_communication_version = info.ldn.nodes[0].local_communication_version;
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, intent_id={}/{}, channel={}, node_count_max={}, "
+ "local_communication_version={}",
+ config.intent_id.local_communication_id, config.intent_id.scene_id,
+ config.channel, config.node_count_max, config.local_communication_version);
+
+ IPC::ResponseBuilder rb{ctx, 10};
+ rb.Push(rc);
+ rb.PushRaw<NetworkConfig>(config);
+ }
+
+ void AttachStateChangeEvent(Kernel::HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(state_change_event->GetReadableEvent());
+ }
+
+ void GetNetworkInfoLatestUpdate(Kernel::HLERequestContext& ctx) {
+ const std::size_t network_buffer_size = ctx.GetWriteBufferSize(0);
+ const std::size_t node_buffer_count = ctx.GetWriteBufferSize(1) / sizeof(NodeLatestUpdate);
+
+ if (node_buffer_count == 0 || network_buffer_size != sizeof(NetworkInfo)) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}, {}", network_buffer_size,
+ node_buffer_count);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ NetworkInfo info;
+ std::vector<NodeLatestUpdate> latest_update(node_buffer_count);
+
+ const auto rc = ResultSuccess;
+ if (rc.IsError()) {
+ LOG_ERROR(Service_LDN, "NetworkInfo is not valid {}", rc.raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ return;
+ }
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, ssid='{}', nodes={}",
+ info.common.ssid.GetStringValue(), info.ldn.node_count);
+
+ ctx.WriteBuffer(info, 0);
+ ctx.WriteBuffer(latest_update, 1);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void Scan(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx);
+ }
+
+ void ScanPrivate(Kernel::HLERequestContext& ctx) {
+ ScanImpl(ctx, true);
+ }
+
+ void ScanImpl(Kernel::HLERequestContext& ctx, bool is_private = false) {
+ IPC::RequestParser rp{ctx};
+ const auto channel{rp.PopEnum<WifiChannel>()};
+ const auto scan_filter{rp.PopRaw<ScanFilter>()};
+
+ const std::size_t network_info_size = ctx.GetWriteBufferSize() / sizeof(NetworkInfo);
+
+ if (network_info_size == 0) {
+ LOG_ERROR(Service_LDN, "Invalid buffer size {}", network_info_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ u16 count = 0;
+ std::vector<NetworkInfo> network_infos(network_info_size);
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, channel={}, filter_scan_flag={}, filter_network_type={}",
+ channel, scan_filter.flag, scan_filter.network_type);
+
+ ctx.WriteBuffer(network_infos);
+
IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(count);
+ }
+
+ void OpenAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CloseAccessPoint(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CreateNetwork(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ INSERT_PADDING_WORDS_NOINIT(1);
+ NetworkConfig network_config;
+ };
+ static_assert(sizeof(Parameters) == 0x98, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ parameters.security_config.passphrase_size,
+ parameters.security_config.security_mode,
+ parameters.network_config.local_communication_version);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CreateNetworkPrivate(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ SecurityConfig security_config;
+ SecurityParameter security_parameter;
+ UserConfig user_config;
+ NetworkConfig network_config;
+ };
+ static_assert(sizeof(Parameters) == 0xB8, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ parameters.security_config.passphrase_size,
+ parameters.security_config.security_mode,
+ parameters.network_config.local_communication_version);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void DestroyNetwork(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void SetAdvertiseData(Kernel::HLERequestContext& ctx) {
+ std::vector<u8> read_buffer = ctx.ReadBuffer();
+
+ LOG_WARNING(Service_LDN, "(STUBBED) called, size {}", read_buffer.size());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void SetStationAcceptPolicy(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
- // Indicate a network error, as we do not actually emulate LDN
- rb.Push(static_cast<u32>(State::Error));
+ void AddAcceptFilterEntry(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void OpenStation(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void CloseStation(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void Connect(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ u32 local_communication_version;
+ u32 option;
+ };
+ static_assert(sizeof(Parameters) == 0x7C, "Parameters has incorrect size.");
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_LDN,
+ "(STUBBED) called, passphrase_size={}, security_mode={}, "
+ "local_communication_version={}",
+ parameters.security_config.passphrase_size,
+ parameters.security_config.security_mode,
+ parameters.local_communication_version);
+
+ const std::vector<u8> read_buffer = ctx.ReadBuffer();
+ NetworkInfo network_info{};
+
+ if (read_buffer.size() != sizeof(NetworkInfo)) {
+ LOG_ERROR(Frontend, "NetworkInfo doesn't match read_buffer size!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultBadInput);
+ return;
+ }
+
+ std::memcpy(&network_info, read_buffer.data(), read_buffer.size());
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void Disconnect(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+ void Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ const auto rc = InitializeImpl(ctx);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(rc);
+ }
+
+ void Finalize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ is_initialized = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Initialize2(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_LDN, "called");
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
- is_initialized = true;
+ const auto rc = InitializeImpl(ctx);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(rc);
+ }
+
+ Result InitializeImpl(Kernel::HLERequestContext& ctx) {
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ if (!network_interface) {
+ LOG_ERROR(Service_LDN, "No network interface is set");
+ return ResultAirplaneModeEnabled;
+ }
+
+ is_initialized = true;
+ // TODO (flTobi): Change this to ResultSuccess when LDN is fully implemented
+ return ResultAirplaneModeEnabled;
}
-private:
- enum class State {
- None,
- Initialized,
- AccessPointOpened,
- AccessPointCreated,
- StationOpened,
- StationConnected,
- Error,
- };
+ KernelHelpers::ServiceContext service_context;
+ Kernel::KEvent* state_change_event;
+ Network::RoomNetwork& room_network;
bool is_initialized{};
};
@@ -273,7 +621,7 @@ public:
LOG_WARNING(Service_LDN, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ERROR_DISABLED);
+ rb.Push(ResultDisabled);
}
};
diff --git a/src/core/hle/service/ldn/ldn.h b/src/core/hle/service/ldn/ldn.h
index a0031ac71..6afe2ea6f 100644
--- a/src/core/hle/service/ldn/ldn.h
+++ b/src/core/hle/service/ldn/ldn.h
@@ -3,6 +3,12 @@
#pragma once
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/k_event.h"
+#include "core/hle/result.h"
+#include "core/hle/service/kernel_helpers.h"
+#include "core/hle/service/sm/sm.h"
+
namespace Core {
class System;
}
diff --git a/src/core/hle/service/ldn/ldn_results.h b/src/core/hle/service/ldn/ldn_results.h
new file mode 100644
index 000000000..f340bda42
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_results.h
@@ -0,0 +1,27 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::LDN {
+
+constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10};
+constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20};
+constexpr Result ResultDisabled{ErrorModule::LDN, 22};
+constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23};
+constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30};
+constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31};
+constexpr Result ResultBadState{ErrorModule::LDN, 32};
+constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33};
+constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50};
+constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65};
+constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66};
+constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67};
+constexpr Result ResultBadInput{ErrorModule::LDN, 96};
+constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97};
+constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113};
+constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114};
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/ldn/ldn_types.h b/src/core/hle/service/ldn/ldn_types.h
new file mode 100644
index 000000000..6231e936d
--- /dev/null
+++ b/src/core/hle/service/ldn/ldn_types.h
@@ -0,0 +1,292 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <fmt/format.h>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "network/network.h"
+
+namespace Service::LDN {
+
+constexpr size_t SsidLengthMax = 32;
+constexpr size_t AdvertiseDataSizeMax = 384;
+constexpr size_t UserNameBytesMax = 32;
+constexpr int NodeCountMax = 8;
+constexpr int StationCountMax = NodeCountMax - 1;
+constexpr size_t PassphraseLengthMax = 64;
+
+enum class SecurityMode : u16 {
+ All,
+ Retail,
+ Debug,
+};
+
+enum class NodeStateChange : u8 {
+ None,
+ Connect,
+ Disconnect,
+ DisconnectAndConnect,
+};
+
+enum class ScanFilterFlag : u32 {
+ None = 0,
+ LocalCommunicationId = 1 << 0,
+ SessionId = 1 << 1,
+ NetworkType = 1 << 2,
+ Ssid = 1 << 4,
+ SceneId = 1 << 5,
+ IntentId = LocalCommunicationId | SceneId,
+ NetworkId = IntentId | SessionId,
+};
+
+enum class NetworkType : u32 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class PackedNetworkType : u8 {
+ None,
+ General,
+ Ldn,
+ All,
+};
+
+enum class State : u32 {
+ None,
+ Initialized,
+ AccessPointOpened,
+ AccessPointCreated,
+ StationOpened,
+ StationConnected,
+ Error,
+};
+
+enum class DisconnectReason : s16 {
+ Unknown = -1,
+ None,
+ DisconnectedByUser,
+ DisconnectedBySystem,
+ DestroyedByUser,
+ DestroyedBySystem,
+ Rejected,
+ SignalLost,
+};
+
+enum class NetworkError {
+ Unknown = -1,
+ None = 0,
+ PortUnreachable,
+ TooManyPlayers,
+ VersionTooLow,
+ VersionTooHigh,
+ ConnectFailure,
+ ConnectNotFound,
+ ConnectTimeout,
+ ConnectRejected,
+ RejectFailed,
+};
+
+enum class AcceptPolicy : u8 {
+ AcceptAll,
+ RejectAll,
+ BlackList,
+ WhiteList,
+};
+
+enum class WifiChannel : s16 {
+ Default = 0,
+ wifi24_1 = 1,
+ wifi24_6 = 6,
+ wifi24_11 = 11,
+ wifi50_36 = 36,
+ wifi50_40 = 40,
+ wifi50_44 = 44,
+ wifi50_48 = 48,
+};
+
+enum class LinkLevel : s8 {
+ Bad,
+ Low,
+ Good,
+ Excellent,
+};
+
+struct NodeLatestUpdate {
+ NodeStateChange state_change;
+ INSERT_PADDING_BYTES(0x7); // Unknown
+};
+static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size");
+
+struct SessionId {
+ u64 high;
+ u64 low;
+
+ bool operator==(const SessionId&) const = default;
+};
+static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");
+
+struct IntentId {
+ u64 local_communication_id;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 scene_id;
+ INSERT_PADDING_BYTES(0x4); // Reserved
+};
+static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");
+
+struct NetworkId {
+ IntentId intent_id;
+ SessionId session_id;
+};
+static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");
+
+struct Ssid {
+ u8 length{};
+ std::array<char, SsidLengthMax + 1> raw{};
+
+ Ssid() = default;
+
+ explicit Ssid(std::string_view data) {
+ length = static_cast<u8>(std::min(data.size(), SsidLengthMax));
+ data.copy(raw.data(), length);
+ raw[length] = 0;
+ }
+
+ std::string GetStringValue() const {
+ return std::string(raw.data());
+ }
+};
+static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");
+
+struct Ipv4Address {
+ union {
+ u32 raw{};
+ std::array<u8, 4> bytes;
+ };
+
+ std::string GetStringValue() const {
+ return fmt::format("{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
+ }
+};
+static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");
+
+struct MacAddress {
+ std::array<u8, 6> raw{};
+
+ friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
+};
+static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");
+
+struct ScanFilter {
+ NetworkId network_id;
+ NetworkType network_type;
+ MacAddress mac_address;
+ Ssid ssid;
+ INSERT_PADDING_BYTES(0x10);
+ ScanFilterFlag flag;
+};
+static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size");
+
+struct CommonNetworkInfo {
+ MacAddress bssid;
+ Ssid ssid;
+ WifiChannel channel;
+ LinkLevel link_level;
+ PackedNetworkType network_type;
+ INSERT_PADDING_BYTES(0x4);
+};
+static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");
+
+struct NodeInfo {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ s8 node_id;
+ u8 is_connected;
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ s16 local_communication_version;
+ INSERT_PADDING_BYTES(0x10); // Reserved
+};
+static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");
+
+struct LdnNetworkInfo {
+ std::array<u8, 0x10> security_parameter;
+ SecurityMode security_mode;
+ AcceptPolicy station_accept_policy;
+ u8 has_action_frame;
+ INSERT_PADDING_BYTES(0x2); // Padding
+ u8 node_count_max;
+ u8 node_count;
+ std::array<NodeInfo, NodeCountMax> nodes;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+ u16 advertise_data_size;
+ std::array<u8, AdvertiseDataSizeMax> advertise_data;
+ INSERT_PADDING_BYTES(0x8C); // Reserved
+ u64 random_authentication_id;
+};
+static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");
+
+struct NetworkInfo {
+ NetworkId network_id;
+ CommonNetworkInfo common;
+ LdnNetworkInfo ldn;
+};
+static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
+
+struct SecurityConfig {
+ SecurityMode security_mode;
+ u16 passphrase_size;
+ std::array<u8, PassphraseLengthMax> passphrase;
+};
+static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size");
+
+struct UserConfig {
+ std::array<u8, UserNameBytesMax + 1> user_name;
+ INSERT_PADDING_BYTES(0xF); // Reserved
+};
+static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size");
+
+#pragma pack(push, 4)
+struct ConnectRequest {
+ SecurityConfig security_config;
+ UserConfig user_config;
+ u32 local_communication_version;
+ u32 option_unknown;
+ NetworkInfo network_info;
+};
+static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size");
+#pragma pack(pop)
+
+struct SecurityParameter {
+ std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig
+ SessionId session_id;
+};
+static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size");
+
+struct NetworkConfig {
+ IntentId intent_id;
+ WifiChannel channel;
+ u8 node_count_max;
+ INSERT_PADDING_BYTES(0x1); // Reserved
+ u16 local_communication_version;
+ INSERT_PADDING_BYTES(0xA); // Reserved
+};
+static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size");
+
+struct AddressEntry {
+ Ipv4Address ipv4_address;
+ MacAddress mac_address;
+ INSERT_PADDING_BYTES(0x2); // Reserved
+};
+static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size");
+
+struct AddressList {
+ std::array<AddressEntry, 0x8> addresses;
+};
+static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");
+
+} // namespace Service::LDN
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index 5ebb124a7..ba8c0e230 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -46,7 +46,7 @@ private:
IPC::RequestParser rp{ctx};
min = rp.Pop<u32>();
max = rp.Pop<u32>();
- LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
+ LOG_DEBUG(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
current = min;
IPC::ResponseBuilder rb{ctx, 2};
@@ -54,7 +54,7 @@ private:
}
void GetOld(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_MM, "(STUBBED) called");
+ LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
@@ -81,8 +81,8 @@ private:
u32 input_id = rp.Pop<u32>();
min = rp.Pop<u32>();
max = rp.Pop<u32>();
- LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
- input_id, min, max);
+ LOG_DEBUG(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}", input_id,
+ min, max);
current = min;
IPC::ResponseBuilder rb{ctx, 2};
@@ -90,7 +90,7 @@ private:
}
void Get(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_MM, "(STUBBED) called");
+ LOG_DEBUG(Service_MM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 2889973e4..e3ef06481 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -6,7 +6,6 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nifm/nifm.h"
-#include "core/hle/service/service.h"
namespace {
@@ -271,213 +270,228 @@ public:
}
};
-class IGeneralService final : public ServiceFramework<IGeneralService> {
-public:
- explicit IGeneralService(Core::System& system_);
+void IGeneralService::GetClientId(Kernel::HLERequestContext& ctx) {
+ static constexpr u32 client_id = 1;
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
-private:
- void GetClientId(Kernel::HLERequestContext& ctx) {
- static constexpr u32 client_id = 1;
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
+}
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
- }
+void IGeneralService::CreateScanRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- void CreateScanRequest(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IScanRequest>(system);
+}
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IScanRequest>(system);
- }
+void IGeneralService::CreateRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- void CreateRequest(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IRequest>(system);
+}
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<IRequest>(system);
- }
+void IGeneralService::GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ const auto net_iface = Network::GetSelectedNetworkInterface();
- const auto net_iface = Network::GetSelectedNetworkInterface();
-
- const SfNetworkProfileData network_profile_data = [&net_iface] {
- if (!net_iface) {
- return SfNetworkProfileData{};
- }
-
- return SfNetworkProfileData{
- .ip_setting_data{
- .ip_address_setting{
- .is_automatic{true},
- .current_address{Network::TranslateIPv4(net_iface->ip_address)},
- .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
- .gateway{Network::TranslateIPv4(net_iface->gateway)},
- },
- .dns_setting{
- .is_automatic{true},
- .primary_dns{1, 1, 1, 1},
- .secondary_dns{1, 0, 0, 1},
- },
- .proxy_setting{
- .enabled{false},
- .port{},
- .proxy_server{},
- .automatic_auth_enabled{},
- .user{},
- .password{},
- },
- .mtu{1500},
+ SfNetworkProfileData network_profile_data = [&net_iface] {
+ if (!net_iface) {
+ return SfNetworkProfileData{};
+ }
+
+ return SfNetworkProfileData{
+ .ip_setting_data{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{Network::TranslateIPv4(net_iface->ip_address)},
+ .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
+ .gateway{Network::TranslateIPv4(net_iface->gateway)},
},
- .uuid{0xdeadbeef, 0xdeadbeef},
- .network_name{"yuzu Network"},
- .wireless_setting_data{
- .ssid_length{12},
- .ssid{"yuzu Network"},
- .passphrase{"yuzupassword"},
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
},
- };
- }();
-
- ctx.WriteBuffer(network_profile_data);
+ .proxy_setting{
+ .enabled{false},
+ .port{},
+ .proxy_server{},
+ .automatic_auth_enabled{},
+ .user{},
+ .password{},
+ },
+ .mtu{1500},
+ },
+ .uuid{0xdeadbeef, 0xdeadbeef},
+ .network_name{"yuzu Network"},
+ .wireless_setting_data{
+ .ssid_length{12},
+ .ssid{"yuzu Network"},
+ .passphrase{"yuzupassword"},
+ },
+ };
+ }();
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ network_profile_data.ip_setting_data.ip_address_setting.current_address =
+ room_member->GetFakeIpAddress();
+ }
}
- void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ ctx.WriteBuffer(network_profile_data);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultSuccess);
- }
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
- void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- auto ipv4 = Network::GetHostIPv4Address();
- if (!ipv4) {
- LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
- ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
- }
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(*ipv4);
+void IGeneralService::GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ auto ipv4 = Network::GetHostIPv4Address();
+ if (!ipv4) {
+ LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
+ ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
- void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NIFM, "called");
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ ipv4 = room_member->GetFakeIpAddress();
+ }
+ }
- ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c,
- "SfNetworkProfileData is not the correct size");
- u128 uuid{};
- auto buffer = ctx.ReadBuffer();
- std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(*ipv4);
+}
- IPC::ResponseBuilder rb{ctx, 6, 0, 1};
+void IGeneralService::CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
- rb.Push(ResultSuccess);
- rb.PushIpcInterface<INetworkProfile>(system);
- rb.PushRaw<u128>(uuid);
- }
+ ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "SfNetworkProfileData is not the correct size");
+ u128 uuid{};
+ auto buffer = ctx.ReadBuffer();
+ std::memcpy(&uuid, buffer.data() + 8, sizeof(u128));
- void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 6, 0, 1};
- struct IpConfigInfo {
- IpAddressSetting ip_address_setting{};
- DnsSetting dns_setting{};
- };
- static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
- "IpConfigInfo has incorrect size.");
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<INetworkProfile>(system);
+ rb.PushRaw<u128>(uuid);
+}
- const auto net_iface = Network::GetSelectedNetworkInterface();
+void IGeneralService::GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- const IpConfigInfo ip_config_info = [&net_iface] {
- if (!net_iface) {
- return IpConfigInfo{};
- }
+ struct IpConfigInfo {
+ IpAddressSetting ip_address_setting{};
+ DnsSetting dns_setting{};
+ };
+ static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
+ "IpConfigInfo has incorrect size.");
- return IpConfigInfo{
- .ip_address_setting{
- .is_automatic{true},
- .current_address{Network::TranslateIPv4(net_iface->ip_address)},
- .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
- .gateway{Network::TranslateIPv4(net_iface->gateway)},
- },
- .dns_setting{
- .is_automatic{true},
- .primary_dns{1, 1, 1, 1},
- .secondary_dns{1, 0, 0, 1},
- },
- };
- }();
+ const auto net_iface = Network::GetSelectedNetworkInterface();
- IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
- rb.Push(ResultSuccess);
- rb.PushRaw<IpConfigInfo>(ip_config_info);
- }
+ IpConfigInfo ip_config_info = [&net_iface] {
+ if (!net_iface) {
+ return IpConfigInfo{};
+ }
- void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ return IpConfigInfo{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{Network::TranslateIPv4(net_iface->ip_address)},
+ .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
+ .gateway{Network::TranslateIPv4(net_iface->gateway)},
+ },
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
+ },
+ };
+ }();
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.Push<u8>(0);
+ // When we're connected to a room, spoof the hosts IP address
+ if (auto room_member = network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ ip_config_info.ip_address_setting.current_address = room_member->GetFakeIpAddress();
+ }
}
- void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
+ rb.Push(ResultSuccess);
+ rb.PushRaw<IpConfigInfo>(ip_config_info);
+}
- struct Output {
- InternetConnectionType type{InternetConnectionType::WiFi};
- u8 wifi_strength{3};
- InternetConnectionStatus state{InternetConnectionStatus::Connected};
- };
- static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
+void IGeneralService::IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- constexpr Output out{};
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u8>(1);
+}
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- rb.PushRaw(out);
- }
+void IGeneralService::GetInternetConnectionStatus(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
- void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+ struct Output {
+ InternetConnectionType type{InternetConnectionType::WiFi};
+ u8 wifi_strength{3};
+ InternetConnectionStatus state{InternetConnectionStatus::Connected};
+ };
+ static_assert(sizeof(Output) == 0x3, "Output has incorrect size.");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- if (Network::GetHostIPv4Address().has_value()) {
- rb.Push<u8>(1);
- } else {
- rb.Push<u8>(0);
- }
+ constexpr Output out{};
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushRaw(out);
+}
+
+void IGeneralService::IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ if (Network::GetHostIPv4Address().has_value()) {
+ rb.Push<u8>(1);
+ } else {
+ rb.Push<u8>(0);
}
+}
- void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_NIFM, "(STUBBED) called");
+void IGeneralService::IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
+ LOG_ERROR(Service_NIFM, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(ResultSuccess);
- if (Network::GetHostIPv4Address().has_value()) {
- rb.Push<u8>(1);
- } else {
- rb.Push<u8>(0);
- }
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ if (Network::GetHostIPv4Address().has_value()) {
+ rb.Push<u8>(1);
+ } else {
+ rb.Push<u8>(0);
}
-};
+}
IGeneralService::IGeneralService(Core::System& system_)
- : ServiceFramework{system_, "IGeneralService"} {
+ : ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IGeneralService::GetClientId, "GetClientId"},
@@ -528,6 +542,8 @@ IGeneralService::IGeneralService(Core::System& system_)
RegisterHandlers(functions);
}
+IGeneralService::~IGeneralService() = default;
+
class NetworkInterface final : public ServiceFramework<NetworkInterface> {
public:
explicit NetworkInterface(const char* name, Core::System& system_)
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 5f62d0014..48161be28 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -3,6 +3,11 @@
#pragma once
+#include "core/hle/service/service.h"
+#include "network/network.h"
+#include "network/room.h"
+#include "network/room_member.h"
+
namespace Core {
class System;
}
@@ -16,4 +21,26 @@ namespace Service::NIFM {
/// Registers all NIFM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
+class IGeneralService final : public ServiceFramework<IGeneralService> {
+public:
+ explicit IGeneralService(Core::System& system_);
+ ~IGeneralService() override;
+
+private:
+ void GetClientId(Kernel::HLERequestContext& ctx);
+ void CreateScanRequest(Kernel::HLERequestContext& ctx);
+ void CreateRequest(Kernel::HLERequestContext& ctx);
+ void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx);
+ void RemoveNetworkProfile(Kernel::HLERequestContext& ctx);
+ void GetCurrentIpAddress(Kernel::HLERequestContext& ctx);
+ void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx);
+ void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx);
+ void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx);
+ void GetInternetConnectionStatus(Kernel::HLERequestContext& ctx);
+ void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx);
+ void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx);
+
+ Network::RoomNetwork& network;
+};
+
} // namespace Service::NIFM
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/iplatform_service_manager.cpp
index cc11f3e08..fd047ff26 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/iplatform_service_manager.cpp
@@ -20,7 +20,7 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/physical_memory.h"
#include "core/hle/service/filesystem/filesystem.h"
-#include "core/hle/service/ns/pl_u.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
namespace Service::NS {
@@ -99,7 +99,7 @@ static u32 GetU32Swapped(const u8* data) {
return Common::swap32(value);
}
-struct PL_U::Impl {
+struct IPlatformServiceManager::Impl {
const FontRegion& GetSharedFontRegion(std::size_t index) const {
if (index >= shared_font_regions.size() || shared_font_regions.empty()) {
// No font fallback
@@ -134,16 +134,16 @@ struct PL_U::Impl {
std::vector<FontRegion> shared_font_regions;
};
-PL_U::PL_U(Core::System& system_)
- : ServiceFramework{system_, "pl:u"}, impl{std::make_unique<Impl>()} {
+IPlatformServiceManager::IPlatformServiceManager(Core::System& system_, const char* service_name_)
+ : ServiceFramework{system_, service_name_}, impl{std::make_unique<Impl>()} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &PL_U::RequestLoad, "RequestLoad"},
- {1, &PL_U::GetLoadState, "GetLoadState"},
- {2, &PL_U::GetSize, "GetSize"},
- {3, &PL_U::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
- {4, &PL_U::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
- {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
+ {0, &IPlatformServiceManager::RequestLoad, "RequestLoad"},
+ {1, &IPlatformServiceManager::GetLoadState, "GetLoadState"},
+ {2, &IPlatformServiceManager::GetSize, "GetSize"},
+ {3, &IPlatformServiceManager::GetSharedMemoryAddressOffset, "GetSharedMemoryAddressOffset"},
+ {4, &IPlatformServiceManager::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
+ {5, &IPlatformServiceManager::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
{6, nullptr, "GetSharedFontInOrderOfPriorityForSystem"},
{100, nullptr, "RequestApplicationFunctionAuthorization"},
{101, nullptr, "RequestApplicationFunctionAuthorizationByProcessId"},
@@ -206,9 +206,9 @@ PL_U::PL_U(Core::System& system_)
}
}
-PL_U::~PL_U() = default;
+IPlatformServiceManager::~IPlatformServiceManager() = default;
-void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::RequestLoad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 shared_font_type{rp.Pop<u32>()};
// Games don't call this so all fonts should be loaded
@@ -218,7 +218,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetLoadState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
@@ -228,7 +228,7 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(LoadState::Done));
}
-void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
@@ -238,7 +238,7 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(impl->GetSharedFontRegion(font_id).size);
}
-void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
@@ -248,7 +248,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset);
}
-void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
LOG_DEBUG(Service_NS, "called");
@@ -261,7 +261,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
rb.PushCopyObjects(&kernel.GetFontSharedMem());
}
-void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
+void IPlatformServiceManager::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/iplatform_service_manager.h
index 07d0ac934..ed6eda89f 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/iplatform_service_manager.h
@@ -36,10 +36,10 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
void DecryptSharedFontToTTF(const std::vector<u32>& input, std::vector<u8>& output);
void EncryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, std::size_t& offset);
-class PL_U final : public ServiceFramework<PL_U> {
+class IPlatformServiceManager final : public ServiceFramework<IPlatformServiceManager> {
public:
- explicit PL_U(Core::System& system_);
- ~PL_U() override;
+ explicit IPlatformServiceManager(Core::System& system_, const char* service_name_);
+ ~IPlatformServiceManager() override;
private:
void RequestLoad(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index aafc8fe03..f7318c3cb 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -9,10 +9,10 @@
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/ns/errors.h"
+#include "core/hle/service/ns/iplatform_service_manager.h"
#include "core/hle/service/ns/language.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/ns/pdm_qry.h"
-#include "core/hle/service/ns/pl_u.h"
#include "core/hle/service/set/set.h"
namespace Service::NS {
@@ -764,7 +764,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
std::make_shared<PDM_QRY>(system)->InstallAsService(service_manager);
- std::make_shared<PL_U>(system)->InstallAsService(service_manager);
+ std::make_shared<IPlatformServiceManager>(system, "pl:s")->InstallAsService(service_manager);
+ std::make_shared<IPlatformServiceManager>(system, "pl:u")->InstallAsService(service_manager);
}
} // namespace Service::NS
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 2a5128c60..a7385fce8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "audio_core/audio_core.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -65,7 +66,10 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>&
return NvResult::NotImplemented;
}
-void nvhost_nvdec::OnOpen(DeviceFD fd) {}
+void nvhost_nvdec::OnOpen(DeviceFD fd) {
+ LOG_INFO(Service_NVDRV, "NVDEC video stream started");
+ system.AudioCore().SetNVDECActive(true);
+}
void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
@@ -73,6 +77,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) {
if (iter != fd_to_id.end()) {
system.GPU().ClearCdmaInstance(iter->second);
}
+ system.AudioCore().SetNVDECActive(false);
}
} // 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 728bfa00b..d8518149d 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -198,7 +198,7 @@ NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output)
IocParamParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_WARNING(Service_NVDRV, "(STUBBED) called type={}", params.param);
+ LOG_DEBUG(Service_NVDRV, "(STUBBED) called type={}", params.param);
auto object = GetObject(params.handle);
if (!object) {
@@ -243,7 +243,7 @@ NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
IocFreeParams params;
std::memcpy(&params, input.data(), sizeof(params));
- LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ LOG_DEBUG(Service_NVDRV, "(STUBBED) called");
auto itr = handles.find(params.handle);
if (itr == handles.end()) {
diff --git a/src/core/hle/service/pcv/pcv.cpp b/src/core/hle/service/pcv/pcv.cpp
index 0989474be..f7a497a14 100644
--- a/src/core/hle/service/pcv/pcv.cpp
+++ b/src/core/hle/service/pcv/pcv.cpp
@@ -3,6 +3,7 @@
#include <memory>
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/pcv/pcv.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -77,10 +78,102 @@ public:
}
};
+class IClkrstSession final : public ServiceFramework<IClkrstSession> {
+public:
+ explicit IClkrstSession(Core::System& system_, DeviceCode deivce_code_)
+ : ServiceFramework{system_, "IClkrstSession"}, deivce_code(deivce_code_) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "SetClockEnabled"},
+ {1, nullptr, "SetClockDisabled"},
+ {2, nullptr, "SetResetAsserted"},
+ {3, nullptr, "SetResetDeasserted"},
+ {4, nullptr, "SetPowerEnabled"},
+ {5, nullptr, "SetPowerDisabled"},
+ {6, nullptr, "GetState"},
+ {7, &IClkrstSession::SetClockRate, "SetClockRate"},
+ {8, &IClkrstSession::GetClockRate, "GetClockRate"},
+ {9, nullptr, "SetMinVClockRate"},
+ {10, nullptr, "GetPossibleClockRates"},
+ {11, nullptr, "GetDvfsTable"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+ }
+
+private:
+ void SetClockRate(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ clock_rate = rp.Pop<u32>();
+ LOG_DEBUG(Service_PCV, "(STUBBED) called, clock_rate={}", clock_rate);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetClockRate(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PCV, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u32>(clock_rate);
+ }
+
+ DeviceCode deivce_code;
+ u32 clock_rate{};
+};
+
+class CLKRST final : public ServiceFramework<CLKRST> {
+public:
+ explicit CLKRST(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &CLKRST::OpenSession, "OpenSession"},
+ {1, nullptr, "GetTemperatureThresholds"},
+ {2, nullptr, "SetTemperature"},
+ {3, nullptr, "GetModuleStateTable"},
+ {4, nullptr, "GetModuleStateTableEvent"},
+ {5, nullptr, "GetModuleStateTableMaxCount"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void OpenSession(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto device_code = static_cast<DeviceCode>(rp.Pop<u32>());
+ const auto unkonwn_input = rp.Pop<u32>();
+
+ LOG_DEBUG(Service_PCV, "called, device_code={}, input={}", device_code, unkonwn_input);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IClkrstSession>(system, device_code);
+ }
+};
+
+class CLKRST_A final : public ServiceFramework<CLKRST_A> {
+public:
+ explicit CLKRST_A(Core::System& system_) : ServiceFramework{system_, "clkrst:a"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "ReleaseControl"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+};
+
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
std::make_shared<PCV>(system)->InstallAsService(sm);
std::make_shared<PCV_ARB>(system)->InstallAsService(sm);
std::make_shared<PCV_IMM>(system)->InstallAsService(sm);
+ std::make_shared<CLKRST>(system, "clkrst")->InstallAsService(sm);
+ std::make_shared<CLKRST>(system, "clkrst:i")->InstallAsService(sm);
+ std::make_shared<CLKRST_A>(system)->InstallAsService(sm);
}
} // namespace Service::PCV
diff --git a/src/core/hle/service/pcv/pcv.h b/src/core/hle/service/pcv/pcv.h
index a42e6f8f6..6b26b6fa7 100644
--- a/src/core/hle/service/pcv/pcv.h
+++ b/src/core/hle/service/pcv/pcv.h
@@ -13,6 +13,97 @@ class ServiceManager;
namespace Service::PCV {
+enum class DeviceCode : u32 {
+ Cpu = 0x40000001,
+ Gpu = 0x40000002,
+ I2s1 = 0x40000003,
+ I2s2 = 0x40000004,
+ I2s3 = 0x40000005,
+ Pwm = 0x40000006,
+ I2c1 = 0x02000001,
+ I2c2 = 0x02000002,
+ I2c3 = 0x02000003,
+ I2c4 = 0x02000004,
+ I2c5 = 0x02000005,
+ I2c6 = 0x02000006,
+ Spi1 = 0x07000000,
+ Spi2 = 0x07000001,
+ Spi3 = 0x07000002,
+ Spi4 = 0x07000003,
+ Disp1 = 0x40000011,
+ Disp2 = 0x40000012,
+ Isp = 0x40000013,
+ Vi = 0x40000014,
+ Sdmmc1 = 0x40000015,
+ Sdmmc2 = 0x40000016,
+ Sdmmc3 = 0x40000017,
+ Sdmmc4 = 0x40000018,
+ Owr = 0x40000019,
+ Csite = 0x4000001A,
+ Tsec = 0x4000001B,
+ Mselect = 0x4000001C,
+ Hda2codec2x = 0x4000001D,
+ Actmon = 0x4000001E,
+ I2cSlow = 0x4000001F,
+ Sor1 = 0x40000020,
+ Sata = 0x40000021,
+ Hda = 0x40000022,
+ XusbCoreHostSrc = 0x40000023,
+ XusbFalconSrc = 0x40000024,
+ XusbFsSrc = 0x40000025,
+ XusbCoreDevSrc = 0x40000026,
+ XusbSsSrc = 0x40000027,
+ UartA = 0x03000001,
+ UartB = 0x35000405,
+ UartC = 0x3500040F,
+ UartD = 0x37000001,
+ Host1x = 0x4000002C,
+ Entropy = 0x4000002D,
+ SocTherm = 0x4000002E,
+ Vic = 0x4000002F,
+ Nvenc = 0x40000030,
+ Nvjpg = 0x40000031,
+ Nvdec = 0x40000032,
+ Qspi = 0x40000033,
+ ViI2c = 0x40000034,
+ Tsecb = 0x40000035,
+ Ape = 0x40000036,
+ AudioDsp = 0x40000037,
+ AudioUart = 0x40000038,
+ Emc = 0x40000039,
+ Plle = 0x4000003A,
+ PlleHwSeq = 0x4000003B,
+ Dsi = 0x4000003C,
+ Maud = 0x4000003D,
+ Dpaux1 = 0x4000003E,
+ MipiCal = 0x4000003F,
+ UartFstMipiCal = 0x40000040,
+ Osc = 0x40000041,
+ SysBus = 0x40000042,
+ SorSafe = 0x40000043,
+ XusbSs = 0x40000044,
+ XusbHost = 0x40000045,
+ XusbDevice = 0x40000046,
+ Extperiph1 = 0x40000047,
+ Ahub = 0x40000048,
+ Hda2hdmicodec = 0x40000049,
+ Gpuaux = 0x4000004A,
+ UsbD = 0x4000004B,
+ Usb2 = 0x4000004C,
+ Pcie = 0x4000004D,
+ Afi = 0x4000004E,
+ PciExClk = 0x4000004F,
+ PExUsbPhy = 0x40000050,
+ XUsbPadCtl = 0x40000051,
+ Apbdma = 0x40000052,
+ Usb2TrkClk = 0x40000053,
+ XUsbIoPll = 0x40000054,
+ XUsbIoPllHwSeq = 0x40000055,
+ Cec = 0x40000056,
+ Extperiph2 = 0x40000057,
+ OscClk = 0x40000080
+};
+
void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
} // namespace Service::PCV
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index c64291e7f..dadaf897f 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -194,13 +194,16 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
Kernel::HLERequestContext& ctx) {
const auto guard = LockService();
+ Result result = ResultSuccess;
+
switch (ctx.GetCommandType()) {
case IPC::CommandType::Close:
case IPC::CommandType::TIPC_Close: {
session.Close();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
- return IPC::ERR_REMOTE_PROCESS_DEAD;
+ result = IPC::ERR_REMOTE_PROCESS_DEAD;
+ break;
}
case IPC::CommandType::ControlWithContext:
case IPC::CommandType::Control: {
@@ -227,7 +230,7 @@ Result ServiceFrameworkBase::HandleSyncRequest(Kernel::KServerSession& session,
ctx.WriteToOutgoingCommandBuffer(ctx.GetThread());
}
- return ResultSuccess;
+ return result;
}
/// Initialize Services
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index c7194731e..cc679cc81 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -9,12 +9,16 @@
#include <fmt/format.h>
#include "common/microprofile.h"
+#include "common/socket_types.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/sockets/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h"
#include "core/internal_network/network.h"
+#include "core/internal_network/socket_proxy.h"
#include "core/internal_network/sockets.h"
+#include "network/network.h"
namespace Service::Sockets {
@@ -472,7 +476,13 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
LOG_INFO(Service, "New socket fd={}", fd);
- descriptor.socket = std::make_unique<Network::Socket>();
+ auto room_member = room_network.GetRoomMember().lock();
+ if (room_member && room_member->IsConnected()) {
+ descriptor.socket = std::make_unique<Network::ProxySocket>(room_network);
+ } else {
+ descriptor.socket = std::make_unique<Network::Socket>();
+ }
+
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
descriptor.is_connection_based = IsConnectionBased(type);
@@ -648,7 +658,7 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
ASSERT(arg == 0);
return {descriptor.flags, Errno::SUCCESS};
case FcntlCmd::SETFL: {
- const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
+ const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
if (bsd_errno != Errno::SUCCESS) {
return {-1, bsd_errno};
@@ -669,7 +679,7 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con
return Errno::BADF;
}
- Network::Socket* const socket = file_descriptors[fd]->socket.get();
+ Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
if (optname == OptName::LINGER) {
ASSERT(optlen == sizeof(Linger));
@@ -724,6 +734,8 @@ std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message)
FileDescriptor& descriptor = *file_descriptors[fd];
// Apply flags
+ using Network::FLAG_MSG_DONTWAIT;
+ using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -759,6 +771,8 @@ std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& mess
}
// Apply flags
+ using Network::FLAG_MSG_DONTWAIT;
+ using Network::FLAG_O_NONBLOCK;
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
flags &= ~FLAG_MSG_DONTWAIT;
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
@@ -857,8 +871,19 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
rb.PushEnum(bsd_errno);
}
+void BSD::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
+ for (auto& optional_descriptor : file_descriptors) {
+ if (!optional_descriptor.has_value()) {
+ continue;
+ }
+ FileDescriptor& descriptor = *optional_descriptor;
+ descriptor.socket.get()->HandleProxyPacket(packet);
+ }
+}
+
BSD::BSD(Core::System& system_, const char* name)
- : ServiceFramework{system_, name, ServiceThreadType::CreateNew} {
+ : ServiceFramework{system_, name, ServiceThreadType::CreateNew}, room_network{
+ system_.GetRoomNetwork()} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@@ -899,9 +924,20 @@ BSD::BSD(Core::System& system_, const char* name)
// clang-format on
RegisterHandlers(functions);
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ proxy_packet_received = room_member->BindOnProxyPacketReceived(
+ [this](const Network::ProxyPacket& packet) { OnProxyPacketReceived(packet); });
+ } else {
+ LOG_ERROR(Service, "Network isn't initalized");
+ }
}
-BSD::~BSD() = default;
+BSD::~BSD() {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ room_member->Unbind(proxy_packet_received);
+ }
+}
BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} {
// clang-format off
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 9ea36428d..81e855e0f 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -7,14 +7,17 @@
#include <vector>
#include "common/common_types.h"
+#include "common/socket_types.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sockets/sockets.h"
+#include "network/network.h"
namespace Core {
class System;
}
namespace Network {
+class SocketBase;
class Socket;
} // namespace Network
@@ -30,7 +33,7 @@ private:
static constexpr size_t MAX_FD = 128;
struct FileDescriptor {
- std::unique_ptr<Network::Socket> socket;
+ std::unique_ptr<Network::SocketBase> socket;
s32 flags = 0;
bool is_connection_based = false;
};
@@ -165,6 +168,14 @@ private:
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
+
+ Network::RoomNetwork& room_network;
+
+ /// Callback to parse and handle a received wifi packet.
+ void OnProxyPacketReceived(const Network::ProxyPacket& packet);
+
+ // Callback identifier for the OnProxyPacketReceived event.
+ Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index b735b00fc..31b7dad33 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -22,7 +22,9 @@ enum class Errno : u32 {
AGAIN = 11,
INVAL = 22,
MFILE = 24,
+ MSGSIZE = 90,
NOTCONN = 107,
+ TIMEDOUT = 110,
};
enum class Domain : u32 {
@@ -96,10 +98,6 @@ struct Linger {
u32 linger;
};
-constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
-
-constexpr u32 FLAG_O_NONBLOCK = 0x800;
-
/// Registers all Sockets services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
index 2db10ec81..023aa0486 100644
--- a/src/core/hle/service/sockets/sockets_translate.cpp
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -25,6 +25,8 @@ Errno Translate(Network::Errno value) {
return Errno::MFILE;
case Network::Errno::NOTCONN:
return Errno::NOTCONN;
+ case Network::Errno::TIMEDOUT:
+ return Errno::TIMEDOUT;
default:
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
return Errno::SUCCESS;
diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp
index 36c43cc8f..cdf38a2a4 100644
--- a/src/core/internal_network/network.cpp
+++ b/src/core/internal_network/network.cpp
@@ -32,6 +32,7 @@
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"
#include "core/internal_network/sockets.h"
+#include "network/network.h"
namespace Network {
@@ -114,7 +115,10 @@ Errno TranslateNativeError(int e) {
return Errno::NETDOWN;
case WSAENETUNREACH:
return Errno::NETUNREACH;
+ case WSAEMSGSIZE:
+ return Errno::MSGSIZE;
default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
return Errno::OTHER;
}
}
@@ -125,7 +129,6 @@ using SOCKET = int;
using WSAPOLLFD = pollfd;
using ULONG = u64;
-constexpr SOCKET INVALID_SOCKET = -1;
constexpr SOCKET SOCKET_ERROR = -1;
constexpr int SD_RECEIVE = SHUT_RD;
@@ -206,7 +209,10 @@ Errno TranslateNativeError(int e) {
return Errno::NETDOWN;
case ENETUNREACH:
return Errno::NETUNREACH;
+ case EMSGSIZE:
+ return Errno::MSGSIZE;
default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", e);
return Errno::OTHER;
}
}
@@ -329,16 +335,6 @@ PollEvents TranslatePollRevents(short revents) {
return result;
}
-template <typename T>
-Errno SetSockOpt(SOCKET fd, int option, T value) {
- const int result =
- setsockopt(fd, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
- if (result != SOCKET_ERROR) {
- return Errno::SUCCESS;
- }
- return GetAndLogLastError();
-}
-
} // Anonymous namespace
NetworkInstance::NetworkInstance() {
@@ -350,26 +346,16 @@ NetworkInstance::~NetworkInstance() {
}
std::optional<IPv4Address> GetHostIPv4Address() {
- const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
- const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
- if (network_interfaces.size() == 0) {
- LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
+ const auto network_interface = Network::GetSelectedNetworkInterface();
+ if (!network_interface.has_value()) {
+ LOG_ERROR(Network, "GetSelectedNetworkInterface returned no interface");
return {};
}
- const auto res =
- std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
- return iface.name == selected_network_interface;
- });
-
- if (res != network_interfaces.end()) {
- char ip_addr[16] = {};
- ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
- return TranslateIPv4(res->ip_address);
- } else {
- LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
- return {};
- }
+ std::array<char, 16> ip_addr = {};
+ ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) !=
+ nullptr);
+ return TranslateIPv4(network_interface->ip_address);
}
std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
@@ -412,7 +398,19 @@ Socket::~Socket() {
fd = INVALID_SOCKET;
}
-Socket::Socket(Socket&& rhs) noexcept : fd{std::exchange(rhs.fd, INVALID_SOCKET)} {}
+Socket::Socket(Socket&& rhs) noexcept {
+ fd = std::exchange(rhs.fd, INVALID_SOCKET);
+}
+
+template <typename T>
+Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) {
+ const int result =
+ setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value));
+ if (result != SOCKET_ERROR) {
+ return Errno::SUCCESS;
+ }
+ return GetAndLogLastError();
+}
Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol));
@@ -423,7 +421,7 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) {
return GetAndLogLastError();
}
-std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
+std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() {
sockaddr addr;
socklen_t addrlen = sizeof(addr);
const SOCKET new_socket = accept(fd, &addr, &addrlen);
@@ -634,4 +632,8 @@ bool Socket::IsOpened() const {
return fd != INVALID_SOCKET;
}
+void Socket::HandleProxyPacket(const ProxyPacket& packet) {
+ LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!");
+}
+
} // namespace Network
diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h
index 10e5ef10d..36994c22e 100644
--- a/src/core/internal_network/network.h
+++ b/src/core/internal_network/network.h
@@ -8,6 +8,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
+#include "common/socket_types.h"
#ifdef _WIN32
#include <winsock2.h>
@@ -17,6 +18,7 @@
namespace Network {
+class SocketBase;
class Socket;
/// Error code for network functions
@@ -31,46 +33,11 @@ enum class Errno {
HOSTUNREACH,
NETDOWN,
NETUNREACH,
+ TIMEDOUT,
+ MSGSIZE,
OTHER,
};
-/// Address families
-enum class Domain {
- INET, ///< Address family for IPv4
-};
-
-/// Socket types
-enum class Type {
- STREAM,
- DGRAM,
- RAW,
- SEQPACKET,
-};
-
-/// Protocol values for sockets
-enum class Protocol {
- ICMP,
- TCP,
- UDP,
-};
-
-/// Shutdown mode
-enum class ShutdownHow {
- RD,
- WR,
- RDWR,
-};
-
-/// Array of IPv4 address
-using IPv4Address = std::array<u8, 4>;
-
-/// Cross-platform sockaddr structure
-struct SockAddrIn {
- Domain family;
- IPv4Address ip;
- u16 portno;
-};
-
/// Cross-platform poll fd structure
enum class PollEvents : u16 {
@@ -86,7 +53,7 @@ enum class PollEvents : u16 {
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
struct PollFD {
- Socket* socket;
+ SocketBase* socket;
PollEvents events;
PollEvents revents;
};
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp
new file mode 100644
index 000000000..0c746bd82
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.cpp
@@ -0,0 +1,290 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <chrono>
+#include <thread>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/internal_network/network.h"
+#include "core/internal_network/network_interface.h"
+#include "core/internal_network/socket_proxy.h"
+
+namespace Network {
+
+ProxySocket::ProxySocket(RoomNetwork& room_network_) noexcept : room_network{room_network_} {}
+
+ProxySocket::~ProxySocket() {
+ if (fd == INVALID_SOCKET) {
+ return;
+ }
+ fd = INVALID_SOCKET;
+}
+
+void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
+ if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
+ closed) {
+ return;
+ }
+
+ if (!broadcast && packet.broadcast) {
+ LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode");
+ return;
+ }
+
+ std::lock_guard guard(packets_mutex);
+ received_packets.push(packet);
+}
+
+template <typename T>
+Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
+ LOG_DEBUG(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
+ protocol = socket_protocol;
+ SetSockOpt(fd, SO_TYPE, type);
+
+ return Errno::SUCCESS;
+}
+
+std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {AcceptResult{}, Errno::SUCCESS};
+}
+
+Errno ProxySocket::Connect(SockAddrIn addr_in) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {SockAddrIn{}, Errno::SUCCESS};
+}
+
+std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return {SockAddrIn{}, Errno::SUCCESS};
+}
+
+Errno ProxySocket::Bind(SockAddrIn addr) {
+ if (is_bound) {
+ LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
+ return Errno::SUCCESS;
+ }
+ local_endpoint = addr;
+ is_bound = true;
+
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Listen(s32 backlog) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::Shutdown(ShutdownHow how) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ return Errno::SUCCESS;
+}
+
+std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ ASSERT(flags == 0);
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+
+ return {static_cast<s32>(0), Errno::SUCCESS};
+}
+
+std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) {
+ ASSERT(flags == 0);
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+
+ // TODO (flTobi): Verify the timeout behavior and break when connection is lost
+ const auto timestamp = std::chrono::steady_clock::now();
+ // When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a
+ // packet arrives. In order to prevent lost packets from hanging the emulation thread, we set
+ // the timeout to 5s instead
+ const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout;
+ while (true) {
+ {
+ std::lock_guard guard(packets_mutex);
+ if (received_packets.size() > 0) {
+ return ReceivePacket(flags, message, addr, message.size());
+ }
+ }
+
+ if (!blocking) {
+ return {-1, Errno::AGAIN};
+ }
+
+ std::this_thread::yield();
+
+ const auto time_diff = std::chrono::steady_clock::now() - timestamp;
+ const auto time_diff_ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
+
+ if (time_diff_ms > timeout) {
+ return {-1, Errno::TIMEDOUT};
+ }
+ }
+}
+
+std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message,
+ SockAddrIn* addr, std::size_t max_length) {
+ ProxyPacket& packet = received_packets.front();
+ if (addr) {
+ addr->family = Domain::INET;
+ addr->ip = packet.local_endpoint.ip; // The senders ip address
+ addr->portno = packet.local_endpoint.portno; // The senders port number
+ }
+
+ bool peek = (flags & FLAG_MSG_PEEK) != 0;
+ std::size_t read_bytes;
+ if (packet.data.size() > max_length) {
+ read_bytes = max_length;
+ message.clear();
+ std::copy(packet.data.begin(), packet.data.begin() + read_bytes,
+ std::back_inserter(message));
+ message.resize(max_length);
+
+ if (protocol == Protocol::UDP) {
+ if (!peek) {
+ received_packets.pop();
+ }
+ return {-1, Errno::MSGSIZE};
+ } else if (protocol == Protocol::TCP) {
+ std::vector<u8> numArray(packet.data.size() - max_length);
+ std::copy(packet.data.begin() + max_length, packet.data.end(),
+ std::back_inserter(numArray));
+ packet.data = numArray;
+ }
+ } else {
+ read_bytes = packet.data.size();
+ message.clear();
+ std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message));
+ message.resize(max_length);
+ if (!peek) {
+ received_packets.pop();
+ }
+ }
+
+ return {static_cast<u32>(read_bytes), Errno::SUCCESS};
+}
+
+std::pair<s32, Errno> ProxySocket::Send(const std::vector<u8>& message, int flags) {
+ LOG_WARNING(Network, "(STUBBED) called");
+ ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
+ ASSERT(flags == 0);
+
+ return {static_cast<s32>(0), Errno::SUCCESS};
+}
+
+void ProxySocket::SendPacket(ProxyPacket& packet) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (room_member->IsConnected()) {
+ room_member->SendProxyPacket(packet);
+ }
+ }
+}
+
+std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) {
+ ASSERT(flags == 0);
+
+ if (!is_bound) {
+ LOG_ERROR(Network, "ProxySocket is not bound!");
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+ }
+
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ if (!room_member->IsConnected()) {
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+ }
+ }
+
+ ProxyPacket packet;
+ packet.local_endpoint = local_endpoint;
+ packet.remote_endpoint = *addr;
+ packet.protocol = protocol;
+ packet.broadcast = broadcast && packet.remote_endpoint.ip[3] == 255;
+
+ auto& ip = local_endpoint.ip;
+ auto ipv4 = Network::GetHostIPv4Address();
+ // If the ip is all zeroes (INADDR_ANY) or if it matches the hosts ip address,
+ // replace it with a "fake" routing address
+ if (std::all_of(ip.begin(), ip.end(), [](u8 i) { return i == 0; }) || (ipv4 && ipv4 == ip)) {
+ if (auto room_member = room_network.GetRoomMember().lock()) {
+ packet.local_endpoint.ip = room_member->GetFakeIpAddress();
+ }
+ }
+
+ packet.data.clear();
+ std::copy(message.begin(), message.end(), std::back_inserter(packet.data));
+
+ SendPacket(packet);
+
+ return {static_cast<s32>(message.size()), Errno::SUCCESS};
+}
+
+Errno ProxySocket::Close() {
+ fd = INVALID_SOCKET;
+ closed = true;
+
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::SetLinger(bool enable, u32 linger) {
+ struct Linger {
+ u16 linger_enable;
+ u16 linger_time;
+ } values;
+ values.linger_enable = enable ? 1 : 0;
+ values.linger_time = static_cast<u16>(linger);
+
+ return SetSockOpt(fd, SO_LINGER, values);
+}
+
+Errno ProxySocket::SetReuseAddr(bool enable) {
+ return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
+}
+
+Errno ProxySocket::SetBroadcast(bool enable) {
+ broadcast = enable;
+ return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
+}
+
+Errno ProxySocket::SetSndBuf(u32 value) {
+ return SetSockOpt(fd, SO_SNDBUF, value);
+}
+
+Errno ProxySocket::SetKeepAlive(bool enable) {
+ return Errno::SUCCESS;
+}
+
+Errno ProxySocket::SetRcvBuf(u32 value) {
+ return SetSockOpt(fd, SO_RCVBUF, value);
+}
+
+Errno ProxySocket::SetSndTimeo(u32 value) {
+ send_timeout = value;
+ return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
+}
+
+Errno ProxySocket::SetRcvTimeo(u32 value) {
+ receive_timeout = value;
+ return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
+}
+
+Errno ProxySocket::SetNonBlock(bool enable) {
+ blocking = !enable;
+ return Errno::SUCCESS;
+}
+
+bool ProxySocket::IsOpened() const {
+ return fd != INVALID_SOCKET;
+}
+
+} // namespace Network
diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h
new file mode 100644
index 000000000..f12b5f567
--- /dev/null
+++ b/src/core/internal_network/socket_proxy.h
@@ -0,0 +1,97 @@
+// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <mutex>
+#include <vector>
+#include <queue>
+
+#include "common/common_funcs.h"
+#include "core/internal_network/sockets.h"
+#include "network/network.h"
+
+namespace Network {
+
+class ProxySocket : public SocketBase {
+public:
+ YUZU_NON_COPYABLE(ProxySocket);
+ YUZU_NON_MOVEABLE(ProxySocket);
+
+ explicit ProxySocket(RoomNetwork& room_network_) noexcept;
+ ~ProxySocket() override;
+
+ void HandleProxyPacket(const ProxyPacket& packet) override;
+
+ Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
+
+ Errno Close() override;
+
+ std::pair<AcceptResult, Errno> Accept() override;
+
+ Errno Connect(SockAddrIn addr_in) override;
+
+ std::pair<SockAddrIn, Errno> GetPeerName() override;
+
+ std::pair<SockAddrIn, Errno> GetSockName() override;
+
+ Errno Bind(SockAddrIn addr) override;
+
+ Errno Listen(s32 backlog) override;
+
+ Errno Shutdown(ShutdownHow how) override;
+
+ std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
+
+ std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
+
+ std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr,
+ std::size_t max_length);
+
+ std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
+
+ void SendPacket(ProxyPacket& packet);
+
+ std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) override;
+
+ Errno SetLinger(bool enable, u32 linger) override;
+
+ Errno SetReuseAddr(bool enable) override;
+
+ Errno SetBroadcast(bool enable) override;
+
+ Errno SetKeepAlive(bool enable) override;
+
+ Errno SetSndBuf(u32 value) override;
+
+ Errno SetRcvBuf(u32 value) override;
+
+ Errno SetSndTimeo(u32 value) override;
+
+ Errno SetRcvTimeo(u32 value) override;
+
+ Errno SetNonBlock(bool enable) override;
+
+ template <typename T>
+ Errno SetSockOpt(SOCKET fd, int option, T value);
+
+ bool IsOpened() const override;
+
+private:
+ bool broadcast = false;
+ bool closed = false;
+ u32 send_timeout = 0;
+ u32 receive_timeout = 0;
+ bool is_bound = false;
+ SockAddrIn local_endpoint{};
+ bool blocking = true;
+ std::queue<ProxyPacket> received_packets;
+ Protocol protocol;
+
+ std::mutex packets_mutex;
+
+ RoomNetwork& room_network;
+};
+
+} // namespace Network
diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h
index 77e27e928..a70429b19 100644
--- a/src/core/internal_network/sockets.h
+++ b/src/core/internal_network/sockets.h
@@ -14,20 +14,88 @@
#include "common/common_types.h"
#include "core/internal_network/network.h"
+#include "network/network.h"
// TODO: C++20 Replace std::vector usages with std::span
namespace Network {
-class Socket {
+class SocketBase {
public:
+#ifdef YUZU_UNIX
+ using SOCKET = int;
+ static constexpr SOCKET INVALID_SOCKET = -1;
+ static constexpr SOCKET SOCKET_ERROR = -1;
+#endif
+
struct AcceptResult {
- std::unique_ptr<Socket> socket;
+ std::unique_ptr<SocketBase> socket;
SockAddrIn sockaddr_in;
};
+ virtual ~SocketBase() = default;
+
+ virtual SocketBase& operator=(const SocketBase&) = delete;
+
+ // Avoid closing sockets implicitly
+ virtual SocketBase& operator=(SocketBase&&) noexcept = delete;
+
+ virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
+
+ virtual Errno Close() = 0;
+
+ virtual std::pair<AcceptResult, Errno> Accept() = 0;
+
+ virtual Errno Connect(SockAddrIn addr_in) = 0;
+
+ virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
+
+ virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
+
+ virtual Errno Bind(SockAddrIn addr) = 0;
+
+ virtual Errno Listen(s32 backlog) = 0;
+
+ virtual Errno Shutdown(ShutdownHow how) = 0;
+
+ virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0;
+
+ virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message,
+ SockAddrIn* addr) = 0;
+
+ virtual std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) = 0;
+
+ virtual std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) = 0;
+
+ virtual Errno SetLinger(bool enable, u32 linger) = 0;
- explicit Socket() = default;
- ~Socket();
+ virtual Errno SetReuseAddr(bool enable) = 0;
+
+ virtual Errno SetKeepAlive(bool enable) = 0;
+
+ virtual Errno SetBroadcast(bool enable) = 0;
+
+ virtual Errno SetSndBuf(u32 value) = 0;
+
+ virtual Errno SetRcvBuf(u32 value) = 0;
+
+ virtual Errno SetSndTimeo(u32 value) = 0;
+
+ virtual Errno SetRcvTimeo(u32 value) = 0;
+
+ virtual Errno SetNonBlock(bool enable) = 0;
+
+ virtual bool IsOpened() const = 0;
+
+ virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
+
+ SOCKET fd = INVALID_SOCKET;
+};
+
+class Socket : public SocketBase {
+public:
+ Socket() = default;
+ ~Socket() override;
Socket(const Socket&) = delete;
Socket& operator=(const Socket&) = delete;
@@ -37,57 +105,57 @@ public:
// Avoid closing sockets implicitly
Socket& operator=(Socket&&) noexcept = delete;
- Errno Initialize(Domain domain, Type type, Protocol protocol);
+ Errno Initialize(Domain domain, Type type, Protocol protocol) override;
- Errno Close();
+ Errno Close() override;
- std::pair<AcceptResult, Errno> Accept();
+ std::pair<AcceptResult, Errno> Accept() override;
- Errno Connect(SockAddrIn addr_in);
+ Errno Connect(SockAddrIn addr_in) override;
- std::pair<SockAddrIn, Errno> GetPeerName();
+ std::pair<SockAddrIn, Errno> GetPeerName() override;
- std::pair<SockAddrIn, Errno> GetSockName();
+ std::pair<SockAddrIn, Errno> GetSockName() override;
- Errno Bind(SockAddrIn addr);
+ Errno Bind(SockAddrIn addr) override;
- Errno Listen(s32 backlog);
+ Errno Listen(s32 backlog) override;
- Errno Shutdown(ShutdownHow how);
+ Errno Shutdown(ShutdownHow how) override;
- std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message);
+ std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override;
- std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr);
+ std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override;
- std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags);
+ std::pair<s32, Errno> Send(const std::vector<u8>& message, int flags) override;
- std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message, const SockAddrIn* addr);
+ std::pair<s32, Errno> SendTo(u32 flags, const std::vector<u8>& message,
+ const SockAddrIn* addr) override;
- Errno SetLinger(bool enable, u32 linger);
+ Errno SetLinger(bool enable, u32 linger) override;
- Errno SetReuseAddr(bool enable);
+ Errno SetReuseAddr(bool enable) override;
- Errno SetKeepAlive(bool enable);
+ Errno SetKeepAlive(bool enable) override;
- Errno SetBroadcast(bool enable);
+ Errno SetBroadcast(bool enable) override;
- Errno SetSndBuf(u32 value);
+ Errno SetSndBuf(u32 value) override;
- Errno SetRcvBuf(u32 value);
+ Errno SetRcvBuf(u32 value) override;
- Errno SetSndTimeo(u32 value);
+ Errno SetSndTimeo(u32 value) override;
- Errno SetRcvTimeo(u32 value);
+ Errno SetRcvTimeo(u32 value) override;
- Errno SetNonBlock(bool enable);
+ Errno SetNonBlock(bool enable) override;
- bool IsOpened() const;
+ template <typename T>
+ Errno SetSockOpt(SOCKET fd, int option, T value);
-#if defined(_WIN32)
- SOCKET fd = INVALID_SOCKET;
-#elif YUZU_UNIX
- int fd = -1;
-#endif
+ bool IsOpened() const override;
+
+ void HandleProxyPacket(const ProxyPacket& packet) override;
};
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
deleted file mode 100644
index dfb10c34f..000000000
--- a/src/core/loader/elf.cpp
+++ /dev/null
@@ -1,263 +0,0 @@
-// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
-// SPDX-FileCopyrightText: 2014 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <cstring>
-#include <memory>
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/elf.h"
-#include "common/logging/log.h"
-#include "core/hle/kernel/code_set.h"
-#include "core/hle/kernel/k_page_table.h"
-#include "core/hle/kernel/k_process.h"
-#include "core/loader/elf.h"
-#include "core/memory.h"
-
-using namespace Common::ELF;
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// ElfReader class
-
-typedef int SectionID;
-
-class ElfReader {
-private:
- char* base;
- u32* base32;
-
- Elf32_Ehdr* header;
- Elf32_Phdr* segments;
- Elf32_Shdr* sections;
-
- u32* sectionAddrs;
- bool relocate;
- VAddr entryPoint;
-
-public:
- explicit ElfReader(void* ptr);
-
- u32 Read32(int off) const {
- return base32[off >> 2];
- }
-
- // Quick accessors
- u16 GetType() const {
- return header->e_type;
- }
- u16 GetMachine() const {
- return header->e_machine;
- }
- VAddr GetEntryPoint() const {
- return entryPoint;
- }
- u32 GetFlags() const {
- return (u32)(header->e_flags);
- }
- Kernel::CodeSet LoadInto(VAddr vaddr);
-
- int GetNumSegments() const {
- return (int)(header->e_phnum);
- }
- int GetNumSections() const {
- return (int)(header->e_shnum);
- }
- const u8* GetPtr(int offset) const {
- return (u8*)base + offset;
- }
- const char* GetSectionName(int section) const;
- const u8* GetSectionDataPtr(int section) const {
- if (section < 0 || section >= header->e_shnum)
- return nullptr;
- if (sections[section].sh_type != ElfShtNobits)
- return GetPtr(sections[section].sh_offset);
- else
- return nullptr;
- }
- bool IsCodeSection(int section) const {
- return sections[section].sh_type == ElfShtProgBits;
- }
- const u8* GetSegmentPtr(int segment) {
- return GetPtr(segments[segment].p_offset);
- }
- u32 GetSectionAddr(SectionID section) const {
- return sectionAddrs[section];
- }
- unsigned int GetSectionSize(SectionID section) const {
- return sections[section].sh_size;
- }
- SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found
-
- bool DidRelocate() const {
- return relocate;
- }
-};
-
-ElfReader::ElfReader(void* ptr) {
- base = (char*)ptr;
- base32 = (u32*)ptr;
- header = (Elf32_Ehdr*)ptr;
-
- segments = (Elf32_Phdr*)(base + header->e_phoff);
- sections = (Elf32_Shdr*)(base + header->e_shoff);
-
- entryPoint = header->e_entry;
-}
-
-const char* ElfReader::GetSectionName(int section) const {
- if (sections[section].sh_type == ElfShtNull)
- return nullptr;
-
- int name_offset = sections[section].sh_name;
- const char* ptr = reinterpret_cast<const char*>(GetSectionDataPtr(header->e_shstrndx));
-
- if (ptr)
- return ptr + name_offset;
-
- return nullptr;
-}
-
-Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
- LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx);
-
- // Should we relocate?
- relocate = (header->e_type != ElfTypeExec);
-
- if (relocate) {
- LOG_DEBUG(Loader, "Relocatable module");
- entryPoint += vaddr;
- } else {
- LOG_DEBUG(Loader, "Prerelocated executable");
- }
- LOG_DEBUG(Loader, "{} segments:", header->e_phnum);
-
- // First pass : Get the bits into RAM
- const VAddr base_addr = relocate ? vaddr : 0;
-
- u64 total_image_size = 0;
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- if (p->p_type == ElfPtLoad) {
- total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF;
- }
- }
-
- Kernel::PhysicalMemory program_image(total_image_size);
- std::size_t current_image_position = 0;
-
- Kernel::CodeSet codeset;
-
- for (unsigned int i = 0; i < header->e_phnum; ++i) {
- const Elf32_Phdr* p = &segments[i];
- LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type,
- p->p_vaddr, p->p_filesz, p->p_memsz);
-
- if (p->p_type == ElfPtLoad) {
- Kernel::CodeSet::Segment* codeset_segment;
- u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec);
- if (permission_flags == (ElfPfRead | ElfPfExec)) {
- codeset_segment = &codeset.CodeSegment();
- } else if (permission_flags == (ElfPfRead)) {
- codeset_segment = &codeset.RODataSegment();
- } else if (permission_flags == (ElfPfRead | ElfPfWrite)) {
- codeset_segment = &codeset.DataSegment();
- } else {
- LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i,
- p->p_flags);
- continue;
- }
-
- if (codeset_segment->size != 0) {
- LOG_ERROR(Loader,
- "ELF has more than one segment of the same type. Skipping extra "
- "segment (id {})",
- i);
- continue;
- }
-
- const VAddr segment_addr = base_addr + p->p_vaddr;
- const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF;
-
- codeset_segment->offset = current_image_position;
- codeset_segment->addr = segment_addr;
- codeset_segment->size = aligned_size;
-
- std::memcpy(program_image.data() + current_image_position, GetSegmentPtr(i),
- p->p_filesz);
- current_image_position += aligned_size;
- }
- }
-
- codeset.entrypoint = base_addr + header->e_entry;
- codeset.memory = std::move(program_image);
-
- LOG_DEBUG(Loader, "Done loading.");
-
- return codeset;
-}
-
-SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const {
- for (int i = firstSection; i < header->e_shnum; i++) {
- const char* secname = GetSectionName(i);
-
- if (secname != nullptr && strcmp(name, secname) == 0)
- return i;
- }
- return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Loader namespace
-
-namespace Loader {
-
-AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file_) : AppLoader(std::move(file_)) {}
-
-FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& elf_file) {
- static constexpr u16 ELF_MACHINE_ARM{0x28};
-
- u32 magic = 0;
- if (4 != elf_file->ReadObject(&magic)) {
- return FileType::Error;
- }
-
- u16 machine = 0;
- if (2 != elf_file->ReadObject(&machine, 18)) {
- return FileType::Error;
- }
-
- if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) {
- return FileType::ELF;
- }
-
- return FileType::Error;
-}
-
-AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::KProcess& process,
- [[maybe_unused]] Core::System& system) {
- if (is_loaded) {
- return {ResultStatus::ErrorAlreadyLoaded, {}};
- }
-
- std::vector<u8> buffer = file->ReadAllBytes();
- if (buffer.size() != file->GetSize()) {
- return {ResultStatus::ErrorIncorrectELFFileSize, {}};
- }
-
- const VAddr base_address = process.PageTable().GetCodeRegionStart();
- ElfReader elf_reader(&buffer[0]);
- Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
- const VAddr entry_point = codeset.entrypoint;
-
- // Setup the process code layout
- if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) {
- return {ResultStatus::ErrorNotInitialized, {}};
- }
-
- process.LoadModule(std::move(codeset), entry_point);
-
- is_loaded = true;
- return {ResultStatus::Success, LoadParameters{48, Core::Memory::DEFAULT_STACK_SIZE}};
-}
-
-} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
deleted file mode 100644
index acd33dc3d..000000000
--- a/src/core/loader/elf.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
-// SPDX-FileCopyrightText: 2014 Citra Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include "core/loader/loader.h"
-
-namespace Core {
-class System;
-}
-
-namespace Loader {
-
-/// Loads an ELF/AXF file
-class AppLoader_ELF final : public AppLoader {
-public:
- explicit AppLoader_ELF(FileSys::VirtualFile file);
-
- /**
- * Identifies whether or not the given file is an ELF file.
- *
- * @param elf_file The file to identify.
- *
- * @return FileType::ELF, or FileType::Error if the file is not an ELF file.
- */
- static FileType IdentifyType(const FileSys::VirtualFile& elf_file);
-
- FileType GetFileType() const override {
- return IdentifyType(file);
- }
-
- LoadResult Load(Kernel::KProcess& process, Core::System& system) override;
-};
-
-} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 9af46a0f7..d8a1bf82a 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -14,7 +14,7 @@ namespace Loader {
namespace {
constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
} // Anonymous namespace
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 994ee891f..104d16efa 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -12,7 +12,6 @@
#include "core/core.h"
#include "core/hle/kernel/k_process.h"
#include "core/loader/deconstructed_rom_directory.h"
-#include "core/loader/elf.h"
#include "core/loader/kip.h"
#include "core/loader/nax.h"
#include "core/loader/nca.h"
@@ -39,8 +38,6 @@ std::optional<FileType> IdentifyFileLoader(FileSys::VirtualFile file) {
FileType IdentifyFile(FileSys::VirtualFile file) {
if (const auto romdir_type = IdentifyFileLoader<AppLoader_DeconstructedRomDirectory>(file)) {
return *romdir_type;
- } else if (const auto elf_type = IdentifyFileLoader<AppLoader_ELF>(file)) {
- return *elf_type;
} else if (const auto nso_type = IdentifyFileLoader<AppLoader_NSO>(file)) {
return *nso_type;
} else if (const auto nro_type = IdentifyFileLoader<AppLoader_NRO>(file)) {
@@ -69,8 +66,6 @@ FileType GuessFromFilename(const std::string& name) {
const std::string extension =
Common::ToLower(std::string(Common::FS::GetExtensionFromFilename(name)));
- if (extension == "elf")
- return FileType::ELF;
if (extension == "nro")
return FileType::NRO;
if (extension == "nso")
@@ -89,8 +84,6 @@ FileType GuessFromFilename(const std::string& name) {
std::string GetFileTypeString(FileType type) {
switch (type) {
- case FileType::ELF:
- return "ELF";
case FileType::NRO:
return "NRO";
case FileType::NSO:
@@ -208,10 +201,6 @@ static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::V
FileType type, u64 program_id,
std::size_t program_index) {
switch (type) {
- // Standard ELF file format.
- case FileType::ELF:
- return std::make_unique<AppLoader_ELF>(std::move(file));
-
// NX NSO file format.
case FileType::NSO:
return std::make_unique<AppLoader_NSO>(std::move(file));
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 7bf4faaf1..7b43f70ed 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -34,7 +34,6 @@ namespace Loader {
enum class FileType {
Error,
Unknown,
- ELF,
NSO,
NRO,
NCA,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 1b0bb0876..73d04d7ee 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -125,7 +125,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& nro_file) {
}
static constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) {
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 8dd956fc6..4c3b3c655 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -45,7 +45,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
}
constexpr u32 PageAlignSize(u32 size) {
- return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
+ return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
}
} // Anonymous namespace
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 635449fce..34ad7cadd 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -1,6 +1,5 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2015 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <cstring>
@@ -37,10 +36,11 @@ struct Memory::Impl {
}
void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
- ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
+ ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
+ ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
ASSERT_MSG(target >= DramMemoryMap::Base, "Out of bounds target: {:016X}", target);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, Common::PageType::Memory);
+ MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, target,
+ Common::PageType::Memory);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Map(base, target - DramMemoryMap::Base, size);
@@ -48,9 +48,10 @@ struct Memory::Impl {
}
void UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
- ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:016X}", size);
- ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:016X}", base);
- MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, 0, Common::PageType::Unmapped);
+ ASSERT_MSG((size & YUZU_PAGEMASK) == 0, "non-page aligned size: {:016X}", size);
+ ASSERT_MSG((base & YUZU_PAGEMASK) == 0, "non-page aligned base: {:016X}", base);
+ MapPages(page_table, base / YUZU_PAGESIZE, size / YUZU_PAGESIZE, 0,
+ Common::PageType::Unmapped);
if (Settings::IsFastmemEnabled()) {
system.DeviceMemory().buffer.Unmap(base, size);
@@ -58,7 +59,7 @@ struct Memory::Impl {
}
[[nodiscard]] u8* GetPointerFromRasterizerCachedMemory(VAddr vaddr) const {
- const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
+ const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
if (!paddr) {
return {};
@@ -68,7 +69,7 @@ struct Memory::Impl {
}
[[nodiscard]] u8* GetPointerFromDebugMemory(VAddr vaddr) const {
- const PAddr paddr{current_page_table->backing_addr[vaddr >> PAGE_BITS]};
+ const PAddr paddr{current_page_table->backing_addr[vaddr >> YUZU_PAGEBITS]};
if (paddr == 0) {
return {};
@@ -177,13 +178,14 @@ struct Memory::Impl {
auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) {
const auto& page_table = process.PageTable().PageTableImpl();
std::size_t remaining_size = size;
- std::size_t page_index = addr >> PAGE_BITS;
- std::size_t page_offset = addr & PAGE_MASK;
+ std::size_t page_index = addr >> YUZU_PAGEBITS;
+ std::size_t page_offset = addr & YUZU_PAGEMASK;
while (remaining_size) {
const std::size_t copy_amount =
- std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
- const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
+ std::min(static_cast<std::size_t>(YUZU_PAGESIZE) - page_offset, remaining_size);
+ const auto current_vaddr =
+ static_cast<VAddr>((page_index << YUZU_PAGEBITS) + page_offset);
const auto [pointer, type] = page_table.pointers[page_index].PointerType();
switch (type) {
@@ -193,7 +195,7 @@ struct Memory::Impl {
}
case Common::PageType::Memory: {
DEBUG_ASSERT(pointer);
- u8* mem_ptr = pointer + page_offset + (page_index << PAGE_BITS);
+ u8* mem_ptr = pointer + page_offset + (page_index << YUZU_PAGEBITS);
on_memory(copy_amount, mem_ptr);
break;
}
@@ -340,10 +342,10 @@ struct Memory::Impl {
// Iterate over a contiguous CPU address space, marking/unmarking the region.
// The region is at a granularity of CPU pages.
- const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
- for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
+ const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
+ for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
const Common::PageType page_type{
- current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
if (debug) {
// Switch page type to debug if now debug
switch (page_type) {
@@ -355,7 +357,7 @@ struct Memory::Impl {
// Page is already marked.
break;
case Common::PageType::Memory:
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
nullptr, Common::PageType::DebugMemory);
break;
default:
@@ -372,9 +374,9 @@ struct Memory::Impl {
// Don't mess with already non-debug or rasterizer memory.
break;
case Common::PageType::DebugMemory: {
- u8* const pointer{GetPointerFromDebugMemory(vaddr & ~PAGE_MASK)};
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
- pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
+ u8* const pointer{GetPointerFromDebugMemory(vaddr & ~YUZU_PAGEMASK)};
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
break;
}
default:
@@ -399,10 +401,10 @@ struct Memory::Impl {
// granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
// is different). This assumes the specified GPU address region is contiguous as well.
- const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
- for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
+ const u64 num_pages = ((vaddr + size - 1) >> YUZU_PAGEBITS) - (vaddr >> YUZU_PAGEBITS) + 1;
+ for (u64 i = 0; i < num_pages; ++i, vaddr += YUZU_PAGESIZE) {
const Common::PageType page_type{
- current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Type()};
if (cached) {
// Switch page type to cached if now cached
switch (page_type) {
@@ -412,7 +414,7 @@ struct Memory::Impl {
break;
case Common::PageType::DebugMemory:
case Common::PageType::Memory:
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
nullptr, Common::PageType::RasterizerCachedMemory);
break;
case Common::PageType::RasterizerCachedMemory:
@@ -435,16 +437,16 @@ struct Memory::Impl {
// that this area is already unmarked as cached.
break;
case Common::PageType::RasterizerCachedMemory: {
- u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
+ u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~YUZU_PAGEMASK)};
if (pointer == nullptr) {
// It's possible that this function has been called while updating the
// pagetable after unmapping a VMA. In that case the underlying VMA will no
// longer exist, and we should just leave the pagetable entry blank.
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
nullptr, Common::PageType::Unmapped);
} else {
- current_page_table->pointers[vaddr >> PAGE_BITS].Store(
- pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
+ current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Store(
+ pointer - (vaddr & ~YUZU_PAGEMASK), Common::PageType::Memory);
}
break;
}
@@ -466,8 +468,8 @@ struct Memory::Impl {
*/
void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
Common::PageType type) {
- LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * PAGE_SIZE,
- (base + size) * PAGE_SIZE);
+ LOG_DEBUG(HW_Memory, "Mapping {:016X} onto {:016X}-{:016X}", target, base * YUZU_PAGESIZE,
+ (base + size) * YUZU_PAGESIZE);
// During boot, current_page_table might not be set yet, in which case we need not flush
if (system.IsPoweredOn()) {
@@ -475,7 +477,7 @@ struct Memory::Impl {
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
- gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
+ gpu.FlushAndInvalidateRegion(page << YUZU_PAGEBITS, YUZU_PAGESIZE);
}
}
}
@@ -486,7 +488,7 @@ struct Memory::Impl {
if (!target) {
ASSERT_MSG(type != Common::PageType::Memory,
- "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
+ "Mapping memory page without a pointer @ {:016x}", base * YUZU_PAGESIZE);
while (base != end) {
page_table.pointers[base].Store(nullptr, type);
@@ -497,14 +499,14 @@ struct Memory::Impl {
} else {
while (base != end) {
page_table.pointers[base].Store(
- system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);
- page_table.backing_addr[base] = target - (base << PAGE_BITS);
+ system.DeviceMemory().GetPointer(target) - (base << YUZU_PAGEBITS), type);
+ page_table.backing_addr[base] = target - (base << YUZU_PAGEBITS);
ASSERT_MSG(page_table.pointers[base].Pointer(),
"memory mapping base yield a nullptr within the table");
base += 1;
- target += PAGE_SIZE;
+ target += YUZU_PAGESIZE;
}
}
}
@@ -519,7 +521,7 @@ struct Memory::Impl {
}
// Avoid adding any extra logic to this fast-path block
- const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+ const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> YUZU_PAGEBITS].Raw();
if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
return &pointer[vaddr];
}
@@ -658,7 +660,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, VAddr base, u64 size) {
bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
const Kernel::KProcess& process = *system.CurrentProcess();
const auto& page_table = process.PageTable().PageTableImpl();
- const size_t page = vaddr >> PAGE_BITS;
+ const size_t page = vaddr >> YUZU_PAGEBITS;
if (page >= page_table.pointers.size()) {
return false;
}
@@ -669,9 +671,9 @@ bool Memory::IsValidVirtualAddress(const VAddr vaddr) const {
bool Memory::IsValidVirtualAddressRange(VAddr base, u64 size) const {
VAddr end = base + size;
- VAddr page = Common::AlignDown(base, PAGE_SIZE);
+ VAddr page = Common::AlignDown(base, YUZU_PAGESIZE);
- for (; page < end; page += PAGE_SIZE) {
+ for (; page < end; page += YUZU_PAGESIZE) {
if (!IsValidVirtualAddress(page)) {
return false;
}
diff --git a/src/core/memory.h b/src/core/memory.h
index 780c45385..a11ff8766 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -1,6 +1,5 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2014 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
@@ -28,9 +27,9 @@ namespace Core::Memory {
* Page size used by the ARM architecture. This is the smallest granularity with which memory can
* be mapped.
*/
-constexpr std::size_t PAGE_BITS = 12;
-constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
-constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
+constexpr std::size_t YUZU_PAGEBITS = 12;
+constexpr u64 YUZU_PAGESIZE = 1ULL << YUZU_PAGEBITS;
+constexpr u64 YUZU_PAGEMASK = YUZU_PAGESIZE - 1;
/// Virtual user-space memory regions
enum : VAddr {
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 6ef459b7a..f09c176f8 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <chrono>
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 816202588..dd6becc02 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 654db0b52..abcf6eb11 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 6f3d45bea..887dc98f3 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -1,6 +1,5 @@
-// Copyright 2017 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
+// SPDX-FileCopyrightText: 2017 Citra Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once