code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "core/core.h"
#include "core/hle/kernel/k_shared_memory.h"
#include "hid_core/hid_result.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/shared_memory_format.h"

namespace Service::HID {

AppletResource::AppletResource(Core::System& system_) : system{system_} {}

AppletResource::~AppletResource() = default;

Result AppletResource::CreateAppletResource(u64 aruid) {
    const u64 index = GetIndexFromAruid(aruid);

    if (index >= AruidIndexMax) {
        return ResultAruidNotRegistered;
    }

    if (data[index].flag.is_assigned) {
        return ResultAruidAlreadyRegistered;
    }

    auto& shared_memory = shared_memory_holder[index];
    if (!shared_memory.IsMapped()) {
        const Result result = shared_memory.Initialize(system);
        if (result.IsError()) {
            return result;
        }
        if (shared_memory.GetAddress() == nullptr) {
            shared_memory.Finalize();
            return ResultSharedMemoryNotInitialized;
        }
    }

    auto* shared_memory_format = shared_memory.GetAddress();
    if (shared_memory_format != nullptr) {
        shared_memory_format->Initialize();
    }

    data[index].shared_memory_format = shared_memory_format;
    data[index].flag.is_assigned.Assign(true);
    // TODO: InitializeSixAxisControllerConfig(false);
    active_aruid = aruid;
    return ResultSuccess;
}

Result AppletResource::RegisterAppletResourceUserId(u64 aruid, bool enable_input) {
    const u64 index = GetIndexFromAruid(aruid);

    if (index < AruidIndexMax) {
        return ResultAruidAlreadyRegistered;
    }

    std::size_t data_index = AruidIndexMax;
    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (!data[i].flag.is_initialized) {
            data_index = i;
            break;
        }
    }

    if (data_index == AruidIndexMax) {
        return ResultAruidNoAvailableEntries;
    }

    AruidData& aruid_data = data[data_index];

    aruid_data.aruid = aruid;
    aruid_data.flag.is_initialized.Assign(true);
    if (enable_input) {
        aruid_data.flag.enable_pad_input.Assign(true);
        aruid_data.flag.enable_six_axis_sensor.Assign(true);
        aruid_data.flag.bit_18.Assign(true);
        aruid_data.flag.enable_touchscreen.Assign(true);
    }

    data_index = AruidIndexMax;
    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (registration_list.flag[i] == RegistrationStatus::Initialized) {
            if (registration_list.aruid[i] != aruid) {
                continue;
            }
            data_index = i;
            break;
        }
        // TODO: Don't Handle pending delete here
        if (registration_list.flag[i] == RegistrationStatus::None ||
            registration_list.flag[i] == RegistrationStatus::PendingDelete) {
            data_index = i;
            break;
        }
    }

    if (data_index == AruidIndexMax) {
        return ResultSuccess;
    }

    registration_list.flag[data_index] = RegistrationStatus::Initialized;
    registration_list.aruid[data_index] = aruid;

    return ResultSuccess;
}

void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
    const u64 index = GetIndexFromAruid(aruid);

    if (index >= AruidIndexMax) {
        return;
    }

    FreeAppletResourceId(aruid);
    DestroySevenSixAxisTransferMemory();
    data[index].flag.raw = 0;
    data[index].aruid = 0;

    registration_list.flag[index] = RegistrationStatus::PendingDelete;

    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (registration_list.flag[i] == RegistrationStatus::Initialized) {
            active_aruid = registration_list.aruid[i];
        }
    }
}

void AppletResource::FreeAppletResourceId(u64 aruid) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return;
    }

    auto& aruid_data = data[index];
    if (aruid_data.flag.is_assigned) {
        aruid_data.shared_memory_format = nullptr;
        aruid_data.flag.is_assigned.Assign(false);
        shared_memory_holder[index].Finalize();
    }
}

u64 AppletResource::GetActiveAruid() {
    return active_aruid;
}

Result AppletResource::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return ResultAruidNotRegistered;
    }

    *out_handle = shared_memory_holder[index].GetHandle();
    return ResultSuccess;
}

Result AppletResource::GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format,
                                             u64 aruid) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return ResultAruidNotRegistered;
    }

    *out_shared_memory_format = data[index].shared_memory_format;
    return ResultSuccess;
}

AruidData* AppletResource::GetAruidData(u64 aruid) {
    const u64 aruid_index = GetIndexFromAruid(aruid);
    if (aruid_index == AruidIndexMax) {
        return nullptr;
    }
    return &data[aruid_index];
}

AruidData* AppletResource::GetAruidDataByIndex(std::size_t aruid_index) {
    return &data[aruid_index];
}

bool AppletResource::IsVibrationAruidActive(u64 aruid) const {
    return aruid == 0 || aruid == active_vibration_aruid;
}

u64 AppletResource::GetIndexFromAruid(u64 aruid) {
    for (std::size_t i = 0; i < AruidIndexMax; i++) {
        if (registration_list.flag[i] == RegistrationStatus::Initialized &&
            registration_list.aruid[i] == aruid) {
            return i;
        }
    }
    return AruidIndexMax;
}

Result AppletResource::DestroySevenSixAxisTransferMemory() {
    // TODO
    return ResultSuccess;
}

void AppletResource::EnableInput(u64 aruid, bool is_enabled) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return;
    }

    data[index].flag.enable_pad_input.Assign(is_enabled);
    data[index].flag.enable_touchscreen.Assign(is_enabled);
}

bool AppletResource::SetAruidValidForVibration(u64 aruid, bool is_enabled) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return false;
    }

    if (!is_enabled && aruid == active_vibration_aruid) {
        active_vibration_aruid = SystemAruid;
        return true;
    }

    if (is_enabled && aruid != active_vibration_aruid) {
        active_vibration_aruid = aruid;
        return true;
    }

    return false;
}

void AppletResource::EnableSixAxisSensor(u64 aruid, bool is_enabled) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return;
    }

    data[index].flag.enable_six_axis_sensor.Assign(is_enabled);
}

void AppletResource::EnablePadInput(u64 aruid, bool is_enabled) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return;
    }

    data[index].flag.enable_pad_input.Assign(is_enabled);
}

void AppletResource::EnableTouchScreen(u64 aruid, bool is_enabled) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return;
    }

    data[index].flag.enable_touchscreen.Assign(is_enabled);
}

void AppletResource::SetIsPalmaConnectable(u64 aruid, bool is_connectable) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return;
    }

    data[index].flag.is_palma_connectable.Assign(is_connectable);
}

void AppletResource::EnablePalmaBoostMode(u64 aruid, bool is_enabled) {
    const u64 index = GetIndexFromAruid(aruid);
    if (index >= AruidIndexMax) {
        return;
    }

    data[index].flag.enable_palma_boost_mode.Assign(is_enabled);
}

Result AppletResource::RegisterCoreAppletResource() {
    if (ref_counter == std::numeric_limits<s32>::max() - 1) {
        return ResultAppletResourceOverflow;
    }
    if (ref_counter == 0) {
        const u64 index = GetIndexFromAruid(0);
        if (index < AruidIndexMax) {
            return ResultAruidAlreadyRegistered;
        }

        std::size_t data_index = AruidIndexMax;
        for (std::size_t i = 0; i < AruidIndexMax; i++) {
            if (!data[i].flag.is_initialized) {
                data_index = i;
                break;
            }
        }

        if (data_index == AruidIndexMax) {
            return ResultAruidNoAvailableEntries;
        }

        AruidData& aruid_data = data[data_index];

        aruid_data.aruid = 0;
        aruid_data.flag.is_initialized.Assign(true);
        aruid_data.flag.enable_pad_input.Assign(true);
        aruid_data.flag.enable_six_axis_sensor.Assign(true);
        aruid_data.flag.bit_18.Assign(true);
        aruid_data.flag.enable_touchscreen.Assign(true);

        data_index = AruidIndexMax;
        for (std::size_t i = 0; i < AruidIndexMax; i++) {
            if (registration_list.flag[i] == RegistrationStatus::Initialized) {
                if (registration_list.aruid[i] != 0) {
                    continue;
                }
                data_index = i;
                break;
            }
            if (registration_list.flag[i] == RegistrationStatus::None) {
                data_index = i;
                break;
            }
        }

        Result result = ResultSuccess;

        if (data_index == AruidIndexMax) {
            result = CreateAppletResource(0);
        } else {
            registration_list.flag[data_index] = RegistrationStatus::Initialized;
            registration_list.aruid[data_index] = 0;
        }

        if (result.IsError()) {
            UnregisterAppletResourceUserId(0);
            return result;
        }
    }
    ref_counter++;
    return ResultSuccess;
}

Result AppletResource::UnregisterCoreAppletResource() {
    if (ref_counter == 0) {
        return ResultAppletResourceNotInitialized;
    }

    if (--ref_counter == 0) {
        UnregisterAppletResourceUserId(0);
    }

    return ResultSuccess;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <array>
#include <mutex>

#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/result.h"
#include "hid_core/resources/shared_memory_holder.h"

namespace Core {
class System;
} // namespace Core

namespace Kernel {
class KEvent;
class KSharedMemory;
} // namespace Kernel

namespace Service::HID {
struct SharedMemoryFormat;
class AppletResource;
class NPadResource;

static constexpr std::size_t AruidIndexMax = 0x20;
static constexpr u64 SystemAruid = 0;

enum class RegistrationStatus : u32 {
    None,
    Initialized,
    PendingDelete,
};

struct DataStatusFlag {
    union {
        u32 raw{};

        BitField<0, 1, u32> is_initialized;
        BitField<1, 1, u32> is_assigned;
        BitField<16, 1, u32> enable_pad_input;
        BitField<17, 1, u32> enable_six_axis_sensor;
        BitField<18, 1, u32> bit_18;
        BitField<19, 1, u32> is_palma_connectable;
        BitField<20, 1, u32> enable_palma_boost_mode;
        BitField<21, 1, u32> enable_touchscreen;
    };
};

struct AruidRegisterList {
    std::array<RegistrationStatus, AruidIndexMax> flag{};
    std::array<u64, AruidIndexMax> aruid{};
};
static_assert(sizeof(AruidRegisterList) == 0x180, "AruidRegisterList is an invalid size");

struct AruidData {
    DataStatusFlag flag{};
    u64 aruid{};
    SharedMemoryFormat* shared_memory_format{nullptr};
};

struct HandheldConfig {
    bool is_handheld_hid_enabled;
    bool is_force_handheld;
    bool is_joycon_rail_enabled;
    bool is_force_handheld_style_vibration;
};
static_assert(sizeof(HandheldConfig) == 0x4, "HandheldConfig is an invalid size");

struct AppletResourceHolder {
    std::shared_ptr<AppletResource> applet_resource{nullptr};
    std::recursive_mutex* shared_mutex{nullptr};
    NPadResource* shared_npad_resource{nullptr};
    std::shared_ptr<HandheldConfig> handheld_config{nullptr};
    Kernel::KEvent* input_event{nullptr};
    std::mutex* input_mutex{nullptr};
};

class AppletResource {
public:
    explicit AppletResource(Core::System& system_);
    ~AppletResource();

    Result CreateAppletResource(u64 aruid);

    Result RegisterAppletResourceUserId(u64 aruid, bool enable_input);
    void UnregisterAppletResourceUserId(u64 aruid);

    void FreeAppletResourceId(u64 aruid);

    u64 GetActiveAruid();
    Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
    Result GetSharedMemoryFormat(SharedMemoryFormat** out_shared_memory_format, u64 aruid);
    AruidData* GetAruidData(u64 aruid);
    AruidData* GetAruidDataByIndex(std::size_t aruid_index);

    bool IsVibrationAruidActive(u64 aruid) const;

    u64 GetIndexFromAruid(u64 aruid);

    Result DestroySevenSixAxisTransferMemory();

    void EnableInput(u64 aruid, bool is_enabled);
    bool SetAruidValidForVibration(u64 aruid, bool is_enabled);
    void EnableSixAxisSensor(u64 aruid, bool is_enabled);
    void EnablePadInput(u64 aruid, bool is_enabled);
    void EnableTouchScreen(u64 aruid, bool is_enabled);
    void SetIsPalmaConnectable(u64 aruid, bool is_connectable);
    void EnablePalmaBoostMode(u64 aruid, bool is_enabled);

    Result RegisterCoreAppletResource();
    Result UnregisterCoreAppletResource();

private:
    u64 active_aruid{};
    AruidRegisterList registration_list{};
    std::array<AruidData, AruidIndexMax> data{};
    std::array<SharedMemoryHolder, AruidIndexMax> shared_memory_holder{};
    s32 ref_counter{};
    u64 active_vibration_aruid;

    Core::System& system;
};
} // namespace Service::HID

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "hid_core/resources/controller_base.h"

namespace Service::HID {

ControllerBase::ControllerBase(Core::HID::HIDCore& hid_core_) : hid_core(hid_core_) {}
ControllerBase::~ControllerBase() = default;

Result ControllerBase::Activate() {
    if (is_activated) {
        return ResultSuccess;
    }
    is_activated = true;
    OnInit();
    return ResultSuccess;
}

Result ControllerBase::Activate(u64 aruid) {
    return Activate();
}

void ControllerBase::DeactivateController() {
    if (is_activated) {
        OnRelease();
    }
    is_activated = false;
}

bool ControllerBase::IsControllerActivated() const {
    return is_activated;
}

void ControllerBase::SetAppletResource(std::shared_ptr<AppletResource> resource,
                                       std::recursive_mutex* resource_mutex) {
    applet_resource = resource;
    shared_mutex = resource_mutex;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <memory>

#include "common/common_types.h"
#include "core/hle/result.h"
#include "hid_core/resources/applet_resource.h"

namespace Core::Timing {
class CoreTiming;
}

namespace Core::HID {
class HIDCore;
} // namespace Core::HID

namespace Service::HID {
class ControllerBase {
public:
    explicit ControllerBase(Core::HID::HIDCore& hid_core_);
    virtual ~ControllerBase();

    // Called when the controller is initialized
    virtual void OnInit() = 0;

    // When the controller is released
    virtual void OnRelease() = 0;

    // When the controller is requesting an update for the shared memory
    virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing) = 0;

    // When the controller is requesting a motion update for the shared memory
    virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing) {}

    Result Activate();
    Result Activate(u64 aruid);

    void DeactivateController();

    bool IsControllerActivated() const;

    void SetAppletResource(std::shared_ptr<AppletResource> resource,
                           std::recursive_mutex* resource_mutex);

protected:
    bool is_activated{false};
    std::shared_ptr<AppletResource> applet_resource{nullptr};
    std::recursive_mutex* shared_mutex{nullptr};

    Core::HID::HIDCore& hid_core;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings.h"
#include "core/core_timing.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_types.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/debug_pad/debug_pad.h"
#include "hid_core/resources/shared_memory_format.h"

namespace Service::HID {

DebugPad::DebugPad(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
    controller = hid_core.GetEmulatedController(Core::HID::NpadIdType::Other);
}

DebugPad::~DebugPad() = default;

void DebugPad::OnInit() {}

void DebugPad::OnRelease() {}

void DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    std::scoped_lock shared_lock{*shared_mutex};
    const u64 aruid = applet_resource->GetActiveAruid();
    auto* data = applet_resource->GetAruidData(aruid);

    if (data == nullptr || !data->flag.is_assigned) {
        return;
    }

    DebugPadSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_pad;

    if (!IsControllerActivated()) {
        shared_memory.debug_pad_lifo.buffer_count = 0;
        shared_memory.debug_pad_lifo.buffer_tail = 0;
        return;
    }

    const auto& last_entry = shared_memory.debug_pad_lifo.ReadCurrentEntry().state;
    next_state.sampling_number = last_entry.sampling_number + 1;

    if (Settings::values.debug_pad_enabled) {
        next_state.attribute.connected.Assign(1);

        const auto& button_state = controller->GetDebugPadButtons();
        const auto& stick_state = controller->GetSticks();

        next_state.pad_state = button_state;
        next_state.l_stick = stick_state.left;
        next_state.r_stick = stick_state.right;
    }

    shared_memory.debug_pad_lifo.WriteNextEntry(next_state);
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/debug_pad/debug_pad_types.h"

namespace Core::HID {
class HIDCore;
class EmulatedController;
} // namespace Core::HID

namespace Core::Timing {
class CoreTiming;
}

namespace Service::HID {
class DebugPad final : public ControllerBase {
public:
    explicit DebugPad(Core::HID::HIDCore& hid_core_);
    ~DebugPad() override;

    // Called when the controller is initialized
    void OnInit() override;

    // When the controller is released
    void OnRelease() override;

    // When the controller is requesting an update for the shared memory
    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;

private:
    DebugPadState next_state{};
    Core::HID::EmulatedController* controller = nullptr;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/bit_field.h"
#include "common/common_types.h"
#include "hid_core/hid_types.h"

namespace Service::HID {

// This is nn::hid::DebugPadAttribute
struct DebugPadAttribute {
    union {
        u32 raw{};
        BitField<0, 1, u32> connected;
    };
};
static_assert(sizeof(DebugPadAttribute) == 0x4, "DebugPadAttribute is an invalid size");

// This is nn::hid::DebugPadState
struct DebugPadState {
    s64 sampling_number{};
    DebugPadAttribute attribute{};
    Core::HID::DebugPadButton pad_state{};
    Core::HID::AnalogStickState r_stick{};
    Core::HID::AnalogStickState l_stick{};
};
static_assert(sizeof(DebugPadState) == 0x20, "DebugPadState is an invalid state");

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core_timing.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/digitizer/digitizer.h"
#include "hid_core/resources/shared_memory_format.h"

namespace Service::HID {

Digitizer::Digitizer(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {}

Digitizer::~Digitizer() = default;

void Digitizer::OnInit() {}

void Digitizer::OnRelease() {}

void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    std::scoped_lock shared_lock{*shared_mutex};
    const u64 aruid = applet_resource->GetActiveAruid();
    auto* data = applet_resource->GetAruidData(aruid);

    if (data == nullptr || !data->flag.is_assigned) {
        return;
    }

    auto& header = data->shared_memory_format->digitizer.header;
    header.timestamp = core_timing.GetGlobalTimeNs().count();
    header.total_entry_count = 17;
    header.entry_count = 0;
    header.last_entry_index = 0;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "hid_core/resources/controller_base.h"

namespace Service::HID {

class Digitizer final : public ControllerBase {
public:
    explicit Digitizer(Core::HID::HIDCore& hid_core_);
    ~Digitizer() override;

    // Called when the controller is initialized
    void OnInit() override;

    // When the controller is released
    void OnRelease() override;

    // When the controller is requesting an update for the shared memory
    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "core/hle/service/set/system_settings_server.h"
#include "core/hle/service/sm/sm.h"
#include "hid_core/resources/hid_firmware_settings.h"

namespace Service::HID {

HidFirmwareSettings::HidFirmwareSettings(Core::System& system) {
    m_set_sys =
        system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true);
    LoadSettings(true);
}

void HidFirmwareSettings::Reload() {
    LoadSettings(true);
}

void HidFirmwareSettings::LoadSettings(bool reload_config) {
    if (is_initialized && !reload_config) {
        return;
    }

    m_set_sys->GetSettingsItemValueImpl<bool>(is_debug_pad_enabled, "hid_debug",
                                              "enables_debugpad");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_device_managed, "hid_debug", "manages_devices");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_touch_i2c_managed, "hid_debug",
                                              "manages_touch_ic_i2c");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_future_devices_emulated, "hid_debug",
                                              "emulate_future_device");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_mcu_hardware_error_emulated, "hid_debug",
                                              "emulate_mcu_hardware_error");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_rail_enabled, "hid_debug", "enables_rail");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_firmware_update_failure_emulated, "hid_debug",
                                              "emulate_firmware_update_failure");
    is_firmware_update_failure = {};
    m_set_sys->GetSettingsItemValueImpl<bool>(is_ble_disabled, "hid_debug", "ble_disabled");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_dscale_disabled, "hid_debug", "dscale_disabled");
    m_set_sys->GetSettingsItemValueImpl<bool>(is_handheld_forced, "hid_debug", "force_handheld");
    features_per_id_disabled = {};
    m_set_sys->GetSettingsItemValueImpl<bool>(is_touch_firmware_auto_update_disabled, "hid_debug",
                                              "touch_firmware_auto_update_disabled");

    bool has_rail_interface{};
    bool has_sio_mcu{};
    m_set_sys->GetSettingsItemValueImpl<bool>(has_rail_interface, "hid", "has_rail_interface");
    m_set_sys->GetSettingsItemValueImpl<bool>(has_sio_mcu, "hid", "has_sio_mcu");
    platform_config.has_rail_interface.Assign(has_rail_interface);
    platform_config.has_sio_mcu.Assign(has_sio_mcu);

    is_initialized = true;
}

bool HidFirmwareSettings::IsDebugPadEnabled() {
    LoadSettings(false);
    return is_debug_pad_enabled;
}

bool HidFirmwareSettings::IsDeviceManaged() {
    LoadSettings(false);
    return is_device_managed;
}

bool HidFirmwareSettings::IsEmulateFutureDevice() {
    LoadSettings(false);
    return is_future_devices_emulated;
}

bool HidFirmwareSettings::IsTouchI2cManaged() {
    LoadSettings(false);
    return is_touch_i2c_managed;
}

bool HidFirmwareSettings::IsHandheldForced() {
    LoadSettings(false);
    return is_handheld_forced;
}

bool HidFirmwareSettings::IsRailEnabled() {
    LoadSettings(false);
    return is_rail_enabled;
}

bool HidFirmwareSettings::IsHardwareErrorEmulated() {
    LoadSettings(false);
    return is_mcu_hardware_error_emulated;
}

bool HidFirmwareSettings::IsBleDisabled() {
    LoadSettings(false);
    return is_ble_disabled;
}

bool HidFirmwareSettings::IsDscaleDisabled() {
    LoadSettings(false);
    return is_dscale_disabled;
}

bool HidFirmwareSettings::IsTouchAutoUpdateDisabled() {
    LoadSettings(false);
    return is_touch_firmware_auto_update_disabled;
}

HidFirmwareSettings::FirmwareSetting HidFirmwareSettings::GetFirmwareUpdateFailure() {
    LoadSettings(false);
    return is_firmware_update_failure;
}

HidFirmwareSettings::FeaturesPerId HidFirmwareSettings::FeaturesDisabledPerId() {
    LoadSettings(false);
    return features_per_id_disabled;
}

Set::PlatformConfig HidFirmwareSettings::GetPlatformConfig() {
    LoadSettings(false);
    return platform_config;
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/common_types.h"
#include "core/hle/service/set/settings_types.h"

namespace Core {
class System;
}

namespace Service::Set {
class ISystemSettingsServer;
}

namespace Service::HID {

/// Loads firmware config from nn::settings::fwdbg
class HidFirmwareSettings {
public:
    using FirmwareSetting = std::array<u8, 4>;
    using FeaturesPerId = std::array<bool, 0xA8>;

    HidFirmwareSettings(Core::System& system);

    void Reload();
    void LoadSettings(bool reload_config);

    bool IsDebugPadEnabled();
    bool IsDeviceManaged();
    bool IsEmulateFutureDevice();
    bool IsTouchI2cManaged();
    bool IsHandheldForced();
    bool IsRailEnabled();
    bool IsHardwareErrorEmulated();
    bool IsBleDisabled();
    bool IsDscaleDisabled();
    bool IsTouchAutoUpdateDisabled();

    FirmwareSetting GetFirmwareUpdateFailure();
    FeaturesPerId FeaturesDisabledPerId();
    Set::PlatformConfig GetPlatformConfig();

private:
    bool is_initialized{};

    // Debug settings
    bool is_debug_pad_enabled{};
    bool is_device_managed{};
    bool is_touch_i2c_managed{};
    bool is_future_devices_emulated{};
    bool is_mcu_hardware_error_emulated{};
    bool is_rail_enabled{};
    bool is_firmware_update_failure_emulated{};
    bool is_ble_disabled{};
    bool is_dscale_disabled{};
    bool is_handheld_forced{};
    bool is_touch_firmware_auto_update_disabled{};
    FirmwareSetting is_firmware_update_failure{};
    FeaturesPerId features_per_id_disabled{};
    Set::PlatformConfig platform_config{};

    std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys;
};

} // namespace Service::HID


code:
// 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


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/settings.h"
#include "core/core_timing.h"
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/keyboard/keyboard.h"
#include "hid_core/resources/shared_memory_format.h"

namespace Service::HID {

Keyboard::Keyboard(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
    emulated_devices = hid_core.GetEmulatedDevices();
}

Keyboard::~Keyboard() = default;

void Keyboard::OnInit() {}

void Keyboard::OnRelease() {}

void Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    std::scoped_lock shared_lock{*shared_mutex};
    const u64 aruid = applet_resource->GetActiveAruid();
    auto* data = applet_resource->GetAruidData(aruid);

    if (data == nullptr || !data->flag.is_assigned) {
        return;
    }

    KeyboardSharedMemoryFormat& shared_memory = data->shared_memory_format->keyboard;

    if (!IsControllerActivated()) {
        shared_memory.keyboard_lifo.buffer_count = 0;
        shared_memory.keyboard_lifo.buffer_tail = 0;
        return;
    }

    const auto& last_entry = shared_memory.keyboard_lifo.ReadCurrentEntry().state;
    next_state.sampling_number = last_entry.sampling_number + 1;

    if (Settings::values.keyboard_enabled) {
        const auto& keyboard_state = emulated_devices->GetKeyboard();
        const auto& keyboard_modifier_state = emulated_devices->GetKeyboardModifier();

        next_state.key = keyboard_state;
        next_state.modifier = keyboard_modifier_state;
        next_state.attribute.is_connected.Assign(1);
    }

    shared_memory.keyboard_lifo.WriteNextEntry(next_state);
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "hid_core/resources/controller_base.h"
#include "hid_core/resources/keyboard/keyboard_types.h"

namespace Core::HID {
class HIDCore;
class EmulatedDevices;
} // namespace Core::HID

namespace Service::HID {
class Keyboard final : public ControllerBase {
public:
    explicit Keyboard(Core::HID::HIDCore& hid_core_);
    ~Keyboard() override;

    // Called when the controller is initialized
    void OnInit() override;

    // When the controller is released
    void OnRelease() override;

    // When the controller is requesting an update for the shared memory
    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;

private:
    KeyboardState next_state{};
    Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/common_types.h"
#include "hid_core/hid_types.h"

namespace Service::HID {

// This is nn::hid::detail::KeyboardState
struct KeyboardState {
    s64 sampling_number{};
    Core::HID::KeyboardModifier modifier{};
    Core::HID::KeyboardAttribute attribute{};
    Core::HID::KeyboardKey key{};
};
static_assert(sizeof(KeyboardState) == 0x30, "KeyboardState is an invalid size");

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/mouse/debug_mouse.h"
#include "hid_core/resources/shared_memory_format.h"

namespace Service::HID {

DebugMouse::DebugMouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
    emulated_devices = hid_core.GetEmulatedDevices();
}

DebugMouse::~DebugMouse() = default;

void DebugMouse::OnInit() {}
void DebugMouse::OnRelease() {}

void DebugMouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    std::scoped_lock shared_lock{*shared_mutex};
    const u64 aruid = applet_resource->GetActiveAruid();
    auto* data = applet_resource->GetAruidData(aruid);

    if (data == nullptr || !data->flag.is_assigned) {
        return;
    }

    MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->debug_mouse;

    if (!IsControllerActivated()) {
        shared_memory.mouse_lifo.buffer_count = 0;
        shared_memory.mouse_lifo.buffer_tail = 0;
        return;
    }

    next_state = {};

    const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
    next_state.sampling_number = last_entry.sampling_number + 1;

    if (Settings::values.mouse_enabled) {
        const auto& mouse_button_state = emulated_devices->GetMouseButtons();
        const auto& mouse_position_state = emulated_devices->GetMousePosition();
        const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
        next_state.attribute.is_connected.Assign(1);
        next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
        next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
        next_state.delta_x = next_state.x - last_entry.x;
        next_state.delta_y = next_state.y - last_entry.y;
        next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
        next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;

        last_mouse_wheel_state = mouse_wheel_state;
        next_state.button = mouse_button_state;
    }

    shared_memory.mouse_lifo.WriteNextEntry(next_state);
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "hid_core/hid_types.h"
#include "hid_core/resources/controller_base.h"

namespace Core::HID {
class HIDCore;
class EmulatedDevices;
} // namespace Core::HID

namespace Service::HID {
class DebugMouse final : public ControllerBase {
public:
    explicit DebugMouse(Core::HID::HIDCore& hid_core_);
    ~DebugMouse() override;

    // Called when the controller is initialized
    void OnInit() override;

    // When the controller is released
    void OnRelease() override;

    // When the controller is requesting an update for the shared memory
    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;

private:
    Core::HID::MouseState next_state{};
    Core::HID::AnalogStickState last_mouse_wheel_state{};
    Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "hid_core/frontend/emulated_devices.h"
#include "hid_core/hid_core.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/mouse/mouse.h"
#include "hid_core/resources/shared_memory_format.h"

namespace Service::HID {

Mouse::Mouse(Core::HID::HIDCore& hid_core_) : ControllerBase{hid_core_} {
    emulated_devices = hid_core.GetEmulatedDevices();
}

Mouse::~Mouse() = default;

void Mouse::OnInit() {}
void Mouse::OnRelease() {}

void Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    std::scoped_lock shared_lock{*shared_mutex};
    const u64 aruid = applet_resource->GetActiveAruid();
    auto* data = applet_resource->GetAruidData(aruid);

    if (data == nullptr || !data->flag.is_assigned) {
        return;
    }

    MouseSharedMemoryFormat& shared_memory = data->shared_memory_format->mouse;

    if (!IsControllerActivated()) {
        shared_memory.mouse_lifo.buffer_count = 0;
        shared_memory.mouse_lifo.buffer_tail = 0;
        return;
    }

    next_state = {};

    const auto& last_entry = shared_memory.mouse_lifo.ReadCurrentEntry().state;
    next_state.sampling_number = last_entry.sampling_number + 1;

    if (Settings::values.mouse_enabled) {
        const auto& mouse_button_state = emulated_devices->GetMouseButtons();
        const auto& mouse_position_state = emulated_devices->GetMousePosition();
        const auto& mouse_wheel_state = emulated_devices->GetMouseWheel();
        next_state.attribute.is_connected.Assign(1);
        next_state.x = static_cast<s32>(mouse_position_state.x * Layout::ScreenUndocked::Width);
        next_state.y = static_cast<s32>(mouse_position_state.y * Layout::ScreenUndocked::Height);
        next_state.delta_x = next_state.x - last_entry.x;
        next_state.delta_y = next_state.y - last_entry.y;
        next_state.delta_wheel_x = mouse_wheel_state.x - last_mouse_wheel_state.x;
        next_state.delta_wheel_y = mouse_wheel_state.y - last_mouse_wheel_state.y;

        last_mouse_wheel_state = mouse_wheel_state;
        next_state.button = mouse_button_state;
    }

    shared_memory.mouse_lifo.WriteNextEntry(next_state);
}

} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "hid_core/hid_types.h"
#include "hid_core/resources/controller_base.h"

namespace Core::HID {
class HIDCore;
class EmulatedDevices;
} // namespace Core::HID

namespace Service::HID {
class Mouse final : public ControllerBase {
public:
    explicit Mouse(Core::HID::HIDCore& hid_core_);
    ~Mouse() override;

    // Called when the controller is initialized
    void OnInit() override;

    // When the controller is released
    void OnRelease() override;

    // When the controller is requesting an update for the shared memory
    void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;

private:
    Core::HID::MouseState next_state{};
    Core::HID::AnalogStickState last_mouse_wheel_state{};
    Core::HID::EmulatedDevices* emulated_devices = nullptr;
};
} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include "common/common_types.h"

namespace Service::HID {} // namespace Service::HID


code:
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <array>
#include <cstring>

#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core_timing.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "hid_core/frontend/emulated_controller.h"
#include "hid_core/hid_core.h"
#include "hid_core/hid_result.h"
#include "hid_core/hid_util.h"
#include "hid_core/resources/applet_resource.h"
#include "hid_core/resources/npad/npad.h"
#include "hid_core/resources/npad/npad_vibration.h"
#include "hid_core/resources/shared_memory_format.h"

namespace Service::HID {

NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
    : hid_core{hid_core_}, service_context{service_context_}, npad_resource{service_context} {
    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
        for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
            auto& controller = controller_data[aruid_index][i];
            controller.device = hid_core.GetEmulatedControllerByIndex(i);
            Core::HID::ControllerUpdateCallback engine_callback{
                .on_change =
                    [this, i](Core::HID::ControllerTriggerType type) { ControllerUpdate(type, i); },
                .is_npad_service = true,
            };
            controller.callback_key = controller.device->SetCallback(engine_callback);
        }
    }
    for (std::size_t i = 0; i < abstracted_pads.size(); ++i) {
        abstracted_pads[i] = AbstractPad{};
        abstracted_pads[i].SetNpadId(IndexToNpadIdType(i));
    }
}

NPad::~NPad() {
    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
        for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
            auto& controller = controller_data[aruid_index][i];
            controller.device->DeleteCallback(controller.callback_key);
        }
    }
}

Result NPad::Activate() {
    if (ref_counter == std::numeric_limits<s32>::max() - 1) {
        return ResultNpadResourceOverflow;
    }

    if (ref_counter == 0) {
        std::scoped_lock lock{mutex};

        // TODO: Activate handlers and AbstractedPad
    }

    ref_counter++;
    return ResultSuccess;
}

Result NPad::Activate(u64 aruid) {
    std::scoped_lock lock{mutex};
    std::scoped_lock shared_lock{*applet_resource_holder.shared_mutex};

    auto* data = applet_resource_holder.applet_resource->GetAruidData(aruid);
    const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);

    if (data == nullptr || !data->flag.is_assigned) {
        return ResultSuccess;
    }

    for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
        auto& controller = controller_data[aruid_index][i];
        controller.shared_memory = &data->shared_memory_format->npad.npad_entry[i].internal_state;
    }

    // Prefill controller buffers
    for (auto& controller : controller_data[aruid_index]) {
        auto* npad = controller.shared_memory;
        npad->fullkey_color = {
            .attribute = ColorAttribute::NoController,
            .fullkey = {},
        };
        npad->joycon_color = {
            .attribute = ColorAttribute::NoController,
            .left = {},
            .right = {},
        };
        // HW seems to initialize the first 19 entries
        for (std::size_t i = 0; i < 19; ++i) {
            WriteEmptyEntry(npad);
        }

        controller.is_active = true;
    }

    return ResultSuccess;
}

Result NPad::ActivateNpadResource() {
    return npad_resource.Activate();
}

Result NPad::ActivateNpadResource(u64 aruid) {
    return npad_resource.Activate(aruid);
}

void NPad::FreeAppletResourceId(u64 aruid) {
    return npad_resource.FreeAppletResourceId(aruid);
}

void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t controller_idx) {
    if (type == Core::HID::ControllerTriggerType::All) {
        ControllerUpdate(Core::HID::ControllerTriggerType::Connected, controller_idx);
        ControllerUpdate(Core::HID::ControllerTriggerType::Battery, controller_idx);
        return;
    }

    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        if (controller_idx >= controller_data[aruid_index].size()) {
            return;
        }

        auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);

        if (data == nullptr || !data->flag.is_assigned) {
            continue;
        }

        auto& controller = controller_data[aruid_index][controller_idx];
        const auto is_connected = controller.device->IsConnected();
        const auto npad_type = controller.device->GetNpadStyleIndex();
        const auto npad_id = controller.device->GetNpadIdType();
        switch (type) {
        case Core::HID::ControllerTriggerType::Connected:
        case Core::HID::ControllerTriggerType::Disconnected:
            if (is_connected == controller.is_connected) {
                return;
            }
            UpdateControllerAt(data->aruid, npad_type, npad_id, is_connected);
            break;
        case Core::HID::ControllerTriggerType::Battery: {
            if (!controller.device->IsConnected()) {
                return;
            }
            auto* shared_memory = controller.shared_memory;
            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;
            break;
        }
        default:
            break;
        }
    }
}

void NPad::InitNewlyAddedController(u64 aruid, Core::HID::NpadIdType npad_id) {
    auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
    if (!npad_resource.IsControllerSupported(aruid, controller.device->GetNpadStyleIndex())) {
        return;
    }
    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) {
        npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
        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::Fullkey:
        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_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);
        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.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_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_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_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_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->fullkey_color.attribute = ColorAttribute::Ok;
        shared_memory->fullkey_color.fullkey = body_colors.left;
        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_footer_type = AppletFooterUiType::JoyLeftHorizontal;
        shared_memory->sixaxis_left_properties.is_newly_assigned.Assign(1);
        break;
    case Core::HID::NpadStyleIndex::JoyconRight:
        shared_memory->fullkey_color.attribute = ColorAttribute::Ok;
        shared_memory->fullkey_color.fullkey = body_colors.right;
        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_footer_type = AppletFooterUiType::JoyRightHorizontal;
        shared_memory->sixaxis_right_properties.is_newly_assigned.Assign(1);
        break;
    case Core::HID::NpadStyleIndex::GameCube:
        shared_memory->style_tag.gamecube.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);
        break;
    case Core::HID::NpadStyleIndex::Pokeball:
        shared_memory->style_tag.palma.Assign(1);
        shared_memory->device_type.palma.Assign(1);
        shared_memory->sixaxis_fullkey_properties.is_newly_assigned.Assign(1);
        break;
    case Core::HID::NpadStyleIndex::NES:
        shared_memory->style_tag.lark.Assign(1);
        shared_memory->device_type.fullkey.Assign(1);
        break;
    case Core::HID::NpadStyleIndex::SNES:
        shared_memory->style_tag.lucia.Assign(1);
        shared_memory->device_type.fullkey.Assign(1);
        shared_memory->applet_footer_type = AppletFooterUiType::Lucia;
        break;
    case Core::HID::NpadStyleIndex::N64:
        shared_memory->style_tag.lagoon.Assign(1);
        shared_memory->device_type.fullkey.Assign(1);
        shared_memory->applet_footer_type = AppletFooterUiType::Lagon;
        break;
    case Core::HID::NpadStyleIndex::SegaGenesis:
        shared_memory->style_tag.lager.Assign(1);
        shared_memory->device_type.fullkey.Assign(1);
        break;
    default:
        break;
    }

    controller.is_connected = true;
    controller.device->Connect();
    controller.device->SetLedPattern();
    if (controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
        if (controller.is_dual_left_connected) {
            controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::LeftIndex,
                                              Common::Input::PollingMode::Active);
        }
        if (controller.is_dual_right_connected) {
            controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
                                              Common::Input::PollingMode::Active);
        }
    } else {
        controller.device->SetPollingMode(Core::HID::EmulatedDeviceIndex::AllDevices,
                                          Common::Input::PollingMode::Active);
    }

    npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
    WriteEmptyEntry(controller.shared_memory);
    hid_core.SetLastActiveController(npad_id);
    abstracted_pads[NpadIdTypeToIndex(npad_id)].Update();
}

void NPad::WriteEmptyEntry(NpadInternalState* npad) {
    NPadGenericState dummy_pad_state{};
    NpadGcTriggerState dummy_gc_state{};
    dummy_pad_state.sampling_number = npad->fullkey_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->fullkey_lifo.WriteNextEntry(dummy_pad_state);
    dummy_pad_state.sampling_number = npad->handheld_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->handheld_lifo.WriteNextEntry(dummy_pad_state);
    dummy_pad_state.sampling_number = npad->joy_dual_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->joy_dual_lifo.WriteNextEntry(dummy_pad_state);
    dummy_pad_state.sampling_number = npad->joy_left_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->joy_left_lifo.WriteNextEntry(dummy_pad_state);
    dummy_pad_state.sampling_number = npad->joy_right_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->joy_right_lifo.WriteNextEntry(dummy_pad_state);
    dummy_pad_state.sampling_number = npad->palma_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->palma_lifo.WriteNextEntry(dummy_pad_state);
    dummy_pad_state.sampling_number = npad->system_ext_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->system_ext_lifo.WriteNextEntry(dummy_pad_state);
    dummy_gc_state.sampling_number = npad->gc_trigger_lifo.ReadCurrentEntry().sampling_number + 1;
    npad->gc_trigger_lifo.WriteNextEntry(dummy_gc_state);
}

void NPad::RequestPadStateUpdate(u64 aruid, Core::HID::NpadIdType npad_id) {
    std::scoped_lock lock{*applet_resource_holder.shared_mutex};
    auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
    const auto controller_type = controller.device->GetNpadStyleIndex();

    if (!controller.device->IsConnected() && controller.is_connected) {
        DisconnectNpad(aruid, npad_id);
        return;
    }
    if (!controller.device->IsConnected()) {
        return;
    }
    if (controller.device->IsConnected() && !controller.is_connected) {
        InitNewlyAddedController(aruid, npad_id);
    }

    // This function is unique to yuzu for the turbo buttons and motion to work properly
    controller.device->StatusUpdate();

    auto& pad_entry = controller.npad_pad_state;
    auto& trigger_entry = controller.npad_trigger_state;
    const auto button_state = controller.device->GetNpadButtons();
    const auto stick_state = controller.device->GetSticks();

    using btn = Core::HID::NpadButton;
    pad_entry.npad_buttons.raw = btn::None;
    if (controller_type != Core::HID::NpadStyleIndex::JoyconLeft) {
        constexpr btn right_button_mask = btn::A | btn::B | btn::X | btn::Y | btn::StickR | btn::R |
                                          btn::ZR | btn::Plus | btn::StickRLeft | btn::StickRUp |
                                          btn::StickRRight | btn::StickRDown;
        pad_entry.npad_buttons.raw = button_state.raw & right_button_mask;
        pad_entry.r_stick = stick_state.right;
    }

    if (controller_type != Core::HID::NpadStyleIndex::JoyconRight) {
        constexpr btn left_button_mask =
            btn::Left | btn::Up | btn::Right | btn::Down | btn::StickL | btn::L | btn::ZL |
            btn::Minus | btn::StickLLeft | btn::StickLUp | btn::StickLRight | btn::StickLDown;
        pad_entry.npad_buttons.raw |= button_state.raw & left_button_mask;
        pad_entry.l_stick = stick_state.left;
    }

    if (controller_type == Core::HID::NpadStyleIndex::JoyconLeft ||
        controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
        pad_entry.npad_buttons.left_sl.Assign(button_state.left_sl);
        pad_entry.npad_buttons.left_sr.Assign(button_state.left_sr);
    }

    if (controller_type == Core::HID::NpadStyleIndex::JoyconRight ||
        controller_type == Core::HID::NpadStyleIndex::JoyconDual) {
        pad_entry.npad_buttons.right_sl.Assign(button_state.right_sl);
        pad_entry.npad_buttons.right_sr.Assign(button_state.right_sr);
    }

    if (controller_type == Core::HID::NpadStyleIndex::GameCube) {
        const auto& trigger_state = controller.device->GetTriggers();
        trigger_entry.l_analog = trigger_state.left;
        trigger_entry.r_analog = trigger_state.right;
        pad_entry.npad_buttons.zl.Assign(false);
        pad_entry.npad_buttons.zr.Assign(button_state.r);
        pad_entry.npad_buttons.l.Assign(button_state.zl);
        pad_entry.npad_buttons.r.Assign(button_state.zr);
    }

    if (pad_entry.npad_buttons.raw != Core::HID::NpadButton::None) {
        hid_core.SetLastActiveController(npad_id);
    }
}

void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
    if (ref_counter == 0) {
        return;
    }

    std::scoped_lock lock{*applet_resource_holder.shared_mutex};
    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) {
        const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index);

        if (data == nullptr || !data->flag.is_assigned) {
            continue;
        }

        bool is_set{};
        const auto aruid = data->aruid;
        npad_resource.IsSupportedNpadStyleSet(is_set, aruid);
        // Wait until style is defined
        if (!is_set) {
            continue;
        }

        for (std::size_t i = 0; i < controller_data[aruid_index].size(); ++i) {
            auto& controller = controller_data[aruid_index][i];
            controller.shared_memory =
                &data->shared_memory_format->npad.npad_entry[i].internal_state;
            auto* npad = controller.shared_memory;

            const auto& controller_type = controller.device->GetNpadStyleIndex();

            if (controller_type == Core::HID::NpadStyleIndex::None ||
                !controller.device->IsConnected()) {
                continue;
            }

            if (!data->flag.enable_pad_input) {
                continue;
            }

            if (!controller.is_active) {
                continue;
            }

            RequestPadStateUpdate(aruid, controller.device->GetNpadIdType());
            auto& pad_state = controller.npad_pad_state;
            auto& libnx_state = controller.npad_libnx_state;
            auto& trigger_state = controller.npad_trigger_state;

            // LibNX exclusively uses this section, so we always update it since LibNX doesn't
            // activate any controllers.
            libnx_state.connection_status.raw = 0;
            libnx_state.connection_status.is_connected.Assign(1);
            switch (controller_type) {
            case Core::HID::NpadStyleIndex::None:
                ASSERT(false);
                break;
            case Core::HID::NpadStyleIndex::Fullkey:
            case Core::HID::NpadStyleIndex::NES:
            case Core::HID::NpadStyleIndex::SNES:
            case Core::HID::NpadStyleIndex::N64:
            case Core::HID::NpadStyleIndex::SegaGenesis:
                pad_state.connection_status.raw = 0;
                pad_state.connection_status.is_connected.Assign(1);
                pad_state.connection_status.is_wired.Assign(1);

                libnx_state.connection_status.is_wired.Assign(1);
                pad_state.sampling_number =
                    npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
                npad->fullkey_lifo.WriteNextEntry(pad_state);
                break;
            case Core::HID::NpadStyleIndex::Handheld:
                pad_state.connection_status.raw = 0;
                pad_state.connection_status.is_connected.Assign(1);
                pad_state.connection_status.is_wired.Assign(1);
                pad_state.connection_status.is_left_connected.Assign(1);
                pad_state.connection_status.is_right_connected.Assign(1);
                pad_state.connection_status.is_left_wired.Assign(1);
                pad_state.connection_status.is_right_wired.Assign(1);

                libnx_state.connection_status.is_wired.Assign(1);
                libnx_state.connection_status.is_left_connected.Assign(1);
                libnx_state.connection_status.is_right_connected.Assign(1);
                libnx_state.connection_status.is_left_wired.Assign(1);
                libnx_state.connection_status.is_right_wired.Assign(1);
                pad_state.sampling_number =
                    npad->handheld_lifo.ReadCurrentEntry().state.sampling_number + 1;
                npad->handheld_lifo.WriteNextEntry(pad_state);
                break;
            case Core::HID::NpadStyleIndex::JoyconDual:
                pad_state.connection_status.raw = 0;
                pad_state.connection_status.is_connected.Assign(1);
                if (controller.is_dual_left_connected) {
                    pad_state.connection_status.is_left_connected.Assign(1);
                    libnx_state.connection_status.is_left_connected.Assign(1);
                }
                if (controller.is_dual_right_connected) {
                    pad_state.connection_status.is_right_connected.Assign(1);
                    libnx_state.connection_status.is_right_connected.Assign(1);
                }

                pad_state.sampling_number =
                    npad->joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
                npad->joy_dual_lifo.WriteNextEntry(pad_state);
                break;
            case Core::HID::NpadStyleIndex::JoyconLeft:
                pad_state.connection_status.raw = 0;
                pad_state.connection_status.is_connected.Assign(1);
                pad_state.connection_status.is_left_connected.Assign(1);

                libnx_state.connection_status.is_left_connected.Assign(1);
                pad_state.sampling_number =
                    npad->joy_left_lifo.ReadCurrentEntry().state.sampling_number + 1;
                npad->joy_left_lifo.WriteNextEntry(pad_state);
                break;
            case Core::HID::NpadStyleIndex::JoyconRight:
                pad_state.connection_status.raw = 0;
                pad_state.connection_status.is_connected.Assign(1);
                pad_state.connection_status.is_right_connected.Assign(1);

                libnx_state.connection_status.is_right_connected.Assign(1);
                pad_state.sampling_number =
                    npad->joy_right_lifo.ReadCurrentEntry().state.sampling_number + 1;
                npad->joy_right_lifo.WriteNextEntry(pad_state);
                break;
            case Core::HID::NpadStyleIndex::GameCube:
                pad_state.connection_status.raw = 0;
                pad_state.connection_status.is_connected.Assign(1);
                pad_state.connection_status.is_wired.Assign(1);

                libnx_state.connection_status.is_wired.Assign(1);
                pad_state.sampling_number =
                    npad->fullkey_lifo.ReadCurrentEntry().state.sampling_number + 1;
                trigger_state.sampling_number =
                    npad->gc_trigger_lifo.ReadCurrentEntry().state.sampling_number + 1;
                npad->fullkey_lifo.WriteNextEntry(pad_state);
                npad->gc_trigger_lifo.WriteNextEntry(trigger_state);
                break;
            case Core::HID::NpadStyleIndex::Pokeball:
                pad_state.connection_status.raw = 0;
                pad_state.connection_status.is_connected.Assign(1);
                pad_state.sampling_number =
                    npad->palma_lifo.ReadCurrentEntry().state.sampling_number + 1;
                npad->palma_lifo.WriteNextEntry(pad_state);
                break;
            default:
                break;
            }

            libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw;
            libnx_state.l_stick = pad_state.l_stick;
            libnx_state.r_stick = pad_state.r_stick;
            libnx_state.sampling_number =
                npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1;
            npad->system_ext_lifo.WriteNextEntry(libnx_state);

            press_state |= static_cast<u64>(pad_state.npad_buttons.raw);
        }
    }
}

Result NPad::SetSupportedNpadStyleSet(u64 aruid, Core::HID::NpadStyleSet supported_style_set) {
    std::scoped_lock lock{mutex};
    hid_core.SetSupportedStyleTag({supported_style_set});
    const Result result = npad_resource.SetSupportedNpadStyleSet(aruid, supported_style_set);
    if (result.IsSuccess()) {
        OnUpdate({});
    }
    return result;
}

Result NPad::GetSupportedNpadStyleSet(u64 aruid,
                                      Core::HID::NpadStyleSet& out_supported_style_set) const {
    std::scoped_lock lock{mutex};
    const Result result = npad_resource.GetSupportedNpadStyleSet(out_supported_style_set, aruid);

    if (result == ResultUndefinedStyleset) {
        out_supported_style_set = Core::HID::NpadStyleSet::None;
        return ResultSuccess;
    }

    return result;
}

Result NPad::GetMaskedSupportedNpadStyleSet(
    u64 aruid, Core::HID::NpadStyleSet& out_supported_style_set) const {
    std::scoped_lock lock{mutex};
    const Result result =
        npad_resource.GetMaskedSupportedNpadStyleSet(out_supported_style_set, aruid);

    if (result == ResultUndefinedStyleset) {
        out_supported_style_set = Core::HID::NpadStyleSet::None;
        return ResultSuccess;
    }

    return result;
}

Result NPad::SetSupportedNpadIdType(u64 aruid,
                                    std::span<const Core::HID::NpadIdType> supported_npad_list) {
    std::scoped_lock lock{mutex};
    if (supported_npad_list.size() > MaxSupportedNpadIdTypes) {
        return ResultInvalidArraySize;
    }

    Result result = npad_resource.SetSupportedNpadIdType(aruid, supported_npad_list);

    if (result.IsSuccess()) {
        OnUpdate({});
    }

    return result;
}

Result NPad::SetNpadJoyHoldType(u64 aruid, NpadJoyHoldType hold_type) {
    std::scoped_lock lock{mutex};
    return npad_resource.SetNpadJoyHoldType(aruid, hold_type);
}

Result NPad::GetNpadJoyHoldType(u64 aruid, NpadJoyHoldType& out_hold_type) const {
    std::scoped_lock lock{mutex};
    return npad_resource.GetNpadJoyHoldType(out_hold_type, aruid);
}

Result NPad::SetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode mode) {
    std::scoped_lock lock{mutex};
    Result result = npad_resource.SetNpadHandheldActivationMode(aruid, mode);
    if (result.IsSuccess()) {
        OnUpdate({});
    }
    return result;
}

Result NPad::GetNpadHandheldActivationMode(u64 aruid, NpadHandheldActivationMode& out_mode) const {
    std::scoped_lock lock{mutex};
    return npad_resource.GetNpadHandheldActivationMode(out_mode, aruid);
}

bool NPad::SetNpadMode(u64 aruid, Core::HID::NpadIdType& new_npad_id, Core::HID::NpadIdType npad_id,
                       NpadJoyDeviceType npad_device_type, NpadJoyAssignmentMode assignment_mode) {
    if (!IsNpadIdValid(npad_id)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
        return false;
    }

    auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
    if (controller.shared_memory->assignment_mode != assignment_mode) {
        controller.shared_memory->assignment_mode = assignment_mode;
    }

    if (!controller.device->IsConnected()) {
        return false;
    }

    if (assignment_mode == NpadJoyAssignmentMode::Dual) {
        if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
            DisconnectNpad(aruid, npad_id);
            controller.is_dual_left_connected = true;
            controller.is_dual_right_connected = false;
            UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
            return false;
        }
        if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
            DisconnectNpad(aruid, npad_id);
            controller.is_dual_left_connected = false;
            controller.is_dual_right_connected = true;
            UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
            return false;
        }
        return false;
    }

    // This is for NpadJoyAssignmentMode::Single

    // Only JoyconDual get affected by this function
    if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
        return false;
    }

    if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
        DisconnectNpad(aruid, npad_id);
        UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
        return false;
    }
    if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
        DisconnectNpad(aruid, npad_id);
        UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
        return false;
    }

    // We have two controllers connected to the same npad_id we need to split them
    new_npad_id = hid_core.GetFirstDisconnectedNpadId();
    auto& controller_2 = GetControllerFromNpadIdType(aruid, new_npad_id);
    DisconnectNpad(aruid, npad_id);
    if (npad_device_type == NpadJoyDeviceType::Left) {
        UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
        controller_2.is_dual_left_connected = false;
        controller_2.is_dual_right_connected = true;
        UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
    } else {
        UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
        controller_2.is_dual_left_connected = true;
        controller_2.is_dual_right_connected = false;
        UpdateControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, new_npad_id, true);
    }
    return true;
}

Result NPad::AcquireNpadStyleSetUpdateEventHandle(u64 aruid, Kernel::KReadableEvent** out_event,
                                                  Core::HID::NpadIdType npad_id) {
    std::scoped_lock lock{mutex};
    return npad_resource.AcquireNpadStyleSetUpdateEventHandle(aruid, out_event, npad_id);
}

void NPad::AddNewControllerAt(u64 aruid, Core::HID::NpadStyleIndex controller,
                              Core::HID::NpadIdType npad_id) {
    UpdateControllerAt(aruid, controller, npad_id, true);
}

void NPad::UpdateControllerAt(u64 aruid, Core::HID::NpadStyleIndex type,
                              Core::HID::NpadIdType npad_id, bool connected) {
    auto& controller = GetControllerFromNpadIdType(aruid, npad_id);
    if (!connected) {
        DisconnectNpad(aruid, npad_id);
        return;
    }

    controller.device->SetNpadStyleIndex(type);
    InitNewlyAddedController(aruid, npad_id);
}

Result NPad::DisconnectNpad(u64 aruid, Core::HID::NpadIdType npad_id) {
    if (!IsNpadIdValid(npad_id)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
        return ResultInvalidNpadId;
    }

    LOG_DEBUG(Service_HID, "Npad disconnected {}", npad_id);
    auto& controller = GetControllerFromNpadIdType(aruid, npad_id);

    auto* shared_memory = controller.shared_memory;
    // Don't reset shared_memory->assignment_mode this value is persistent
    shared_memory->style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
    shared_memory->device_type.raw = 0;
    shared_memory->system_properties.raw = 0;
    shared_memory->button_properties.raw = 0;
    shared_memory->sixaxis_fullkey_properties.raw = 0;
    shared_memory->sixaxis_handheld_properties.raw = 0;
    shared_memory->sixaxis_dual_left_properties.raw = 0;
    shared_memory->sixaxis_dual_right_properties.raw = 0;
    shared_memory->sixaxis_left_properties.raw = 0;
    shared_memory->sixaxis_right_properties.raw = 0;
    shared_memory->battery_level_dual = Core::HID::NpadBatteryLevel::Empty;
    shared_memory->battery_level_left = Core::HID::NpadBatteryLevel::Empty;
    shared_memory->battery_level_right = Core::HID::NpadBatteryLevel::Empty;
    shared_memory->fullkey_color = {
        .attribute = ColorAttribute::NoController,
        .fullkey = {},
    };
    shared_memory->joycon_color = {
        .attribute = ColorAttribute::NoController,
        .left = {},
        .right = {},
    };
    shared_memory->applet_footer_type = AppletFooterUiType::None;

    controller.is_dual_left_connected = true;
    controller.is_dual_right_connected = true;
    controller.is_connected = false;
    controller.device->Disconnect();
    npad_resource.SignalStyleSetUpdateEvent(aruid, npad_id);
    WriteEmptyEntry(shared_memory);
    return ResultSuccess;
}

Result NPad::IsFirmwareUpdateAvailableForSixAxisSensor(
    u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle,
    bool& is_firmware_available) const {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    const auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle);
    is_firmware_available = sixaxis_properties.is_firmware_update_available != 0;
    return ResultSuccess;
}

Result NPad::ResetIsSixAxisSensorDeviceNewlyAssigned(
    u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
    const auto is_valid = IsSixaxisHandleValid(sixaxis_handle);
    if (is_valid.IsError()) {
        LOG_ERROR(Service_HID, "Invalid handle, error_code={}", is_valid.raw);
        return is_valid;
    }

    auto& sixaxis_properties = GetSixaxisProperties(aruid, sixaxis_handle);
    sixaxis_properties.is_newly_assigned.Assign(0);

    return ResultSuccess;
}

Result NPad::MergeSingleJoyAsDualJoy(u64 aruid, Core::HID::NpadIdType npad_id_1,
                                     Core::HID::NpadIdType npad_id_2) {
    if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
                  npad_id_2);
        return ResultInvalidNpadId;
    }
    auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1);
    auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2);
    auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
    auto controller_style_2 = controller_2.device->GetNpadStyleIndex();

    // Simplify this code by converting dualjoycon with only a side connected to single joycons
    if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual) {
        if (controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
            controller_style_1 = Core::HID::NpadStyleIndex::JoyconLeft;
        }
        if (!controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
            controller_style_1 = Core::HID::NpadStyleIndex::JoyconRight;
        }
    }
    if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
        if (controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
            controller_style_2 = Core::HID::NpadStyleIndex::JoyconLeft;
        }
        if (!controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
            controller_style_2 = Core::HID::NpadStyleIndex::JoyconRight;
        }
    }

    // Invalid merge errors
    if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual ||
        controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual) {
        return NpadIsDualJoycon;
    }
    if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
        controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft) {
        return NpadIsSameType;
    }
    if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
        controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
        return NpadIsSameType;
    }

    // These exceptions are handled as if they where dual joycon
    if (controller_style_1 != Core::HID::NpadStyleIndex::JoyconLeft &&
        controller_style_1 != Core::HID::NpadStyleIndex::JoyconRight) {
        return NpadIsDualJoycon;
    }
    if (controller_style_2 != Core::HID::NpadStyleIndex::JoyconLeft &&
        controller_style_2 != Core::HID::NpadStyleIndex::JoyconRight) {
        return NpadIsDualJoycon;
    }

    // Disconnect the joycons and connect them as dual joycon at the first index.
    DisconnectNpad(aruid, npad_id_1);
    DisconnectNpad(aruid, npad_id_2);
    controller_1.is_dual_left_connected = true;
    controller_1.is_dual_right_connected = true;
    AddNewControllerAt(aruid, Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
    return ResultSuccess;
}

Result NPad::StartLrAssignmentMode(u64 aruid) {
    std::scoped_lock lock{mutex};
    bool is_enabled{};
    Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid);
    if (result.IsSuccess() && is_enabled == false) {
        result = npad_resource.SetLrAssignmentMode(aruid, true);
    }
    return result;
}

Result NPad::StopLrAssignmentMode(u64 aruid) {
    std::scoped_lock lock{mutex};
    bool is_enabled{};
    Result result = npad_resource.GetLrAssignmentMode(is_enabled, aruid);
    if (result.IsSuccess() && is_enabled == true) {
        result = npad_resource.SetLrAssignmentMode(aruid, false);
    }
    return result;
}

Result NPad::SwapNpadAssignment(u64 aruid, Core::HID::NpadIdType npad_id_1,
                                Core::HID::NpadIdType npad_id_2) {
    if (!IsNpadIdValid(npad_id_1) || !IsNpadIdValid(npad_id_2)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id_1:{}, npad_id_2:{}", npad_id_1,
                  npad_id_2);
        return ResultInvalidNpadId;
    }
    if (npad_id_1 == Core::HID::NpadIdType::Handheld ||
        npad_id_2 == Core::HID::NpadIdType::Handheld || npad_id_1 == Core::HID::NpadIdType::Other ||
        npad_id_2 == Core::HID::NpadIdType::Other) {
        return ResultSuccess;
    }
    const auto& controller_1 = GetControllerFromNpadIdType(aruid, npad_id_1).device;
    const auto& controller_2 = GetControllerFromNpadIdType(aruid, npad_id_2).device;
    const auto type_index_1 = controller_1->GetNpadStyleIndex();
    const auto type_index_2 = controller_2->GetNpadStyleIndex();
    const auto is_connected_1 = controller_1->IsConnected();
    const auto is_connected_2 = controller_2->IsConnected();

    if (!npad_resource.IsControllerSupported(aruid, type_index_1) && is_connected_1) {
        return ResultNpadNotConnected;
    }
    if (!npad_resource.IsControllerSupported(aruid, type_index_2) && is_connected_2) {
        return ResultNpadNotConnected;
    }

    UpdateControllerAt(aruid, type_index_2, npad_id_1, is_connected_2);
    UpdateControllerAt(aruid, type_index_1, npad_id_2, is_connected_1);

    return ResultSuccess;
}

Result NPad::IsUnintendedHomeButtonInputProtectionEnabled(bool& out_is_enabled, u64 aruid,
                                                          Core::HID::NpadIdType npad_id) const {
    std::scoped_lock lock{mutex};
    return npad_resource.GetHomeProtectionEnabled(out_is_enabled, aruid, npad_id);
}

Result NPad::EnableUnintendedHomeButtonInputProtection(u64 aruid, Core::HID::NpadIdType npad_id,
                                                       bool is_enabled) {
    std::scoped_lock lock{mutex};
    return npad_resource.SetHomeProtectionEnabled(aruid, npad_id, is_enabled);
}

void NPad::SetNpadAnalogStickUseCenterClamp(u64 aruid, bool is_enabled) {
    std::scoped_lock lock{mutex};
    npad_resource.SetNpadAnalogStickUseCenterClamp(aruid, is_enabled);
}

void NPad::ClearAllConnectedControllers() {
    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        for (auto& controller : controller_data[aruid_index]) {
            if (controller.device->IsConnected() &&
                controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None) {
                controller.device->Disconnect();
                controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
            }
        }
    }
}

void NPad::DisconnectAllConnectedControllers() {
    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        for (auto& controller : controller_data[aruid_index]) {
            controller.device->Disconnect();
        }
    }
}

void NPad::ConnectAllDisconnectedControllers() {
    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        for (auto& controller : controller_data[aruid_index]) {
            if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::None &&
                !controller.device->IsConnected()) {
                controller.device->Connect();
            }
        }
    }
}

void NPad::ClearAllControllers() {
    for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) {
        for (auto& controller : controller_data[aruid_index]) {
            controller.device->Disconnect();
            controller.device->SetNpadStyleIndex(Core::HID::NpadStyleIndex::None);
        }
    }
}

Core::HID::NpadButton NPad::GetAndResetPressState() {
    return static_cast<Core::HID::NpadButton>(press_state.exchange(0));
}

Result NPad::ApplyNpadSystemCommonPolicy(u64 aruid) {
    std::scoped_lock lock{mutex};
    const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, false);
    if (result.IsSuccess()) {
        OnUpdate({});
    }
    return result;
}

Result NPad::ApplyNpadSystemCommonPolicyFull(u64 aruid) {
    std::scoped_lock lock{mutex};
    const Result result = npad_resource.ApplyNpadSystemCommonPolicy(aruid, true);
    if (result.IsSuccess()) {
        OnUpdate({});
    }
    return result;
}

Result NPad::ClearNpadSystemCommonPolicy(u64 aruid) {
    std::scoped_lock lock{mutex};
    const Result result = npad_resource.ClearNpadSystemCommonPolicy(aruid);
    if (result.IsSuccess()) {
        OnUpdate({});
    }
    return result;
}

void NPad::SetRevision(u64 aruid, NpadRevision revision) {
    npad_resource.SetNpadRevision(aruid, revision);
}

NpadRevision NPad::GetRevision(u64 aruid) {
    return npad_resource.GetNpadRevision(aruid);
}

Result NPad::RegisterAppletResourceUserId(u64 aruid) {
    return npad_resource.RegisterAppletResourceUserId(aruid);
}

void NPad::UnregisterAppletResourceUserId(u64 aruid) {
    // TODO: Remove this once abstract pad is emulated properly
    const auto aruid_index = npad_resource.GetIndexFromAruid(aruid);
    for (auto& controller : controller_data[aruid_index]) {
        controller.is_active = false;
        controller.is_connected = false;
        controller.shared_memory = nullptr;
    }

    npad_resource.UnregisterAppletResourceUserId(aruid);
}

void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource,
                            std::recursive_mutex* shared_mutex,
                            std::shared_ptr<HandheldConfig> handheld_config,
                            Kernel::KEvent* input_event, std::mutex* input_mutex,
                            std::shared_ptr<Service::Set::ISystemSettingsServer> settings) {
    applet_resource_holder.applet_resource = resource;
    applet_resource_holder.shared_mutex = shared_mutex;
    applet_resource_holder.shared_npad_resource = &npad_resource;
    applet_resource_holder.handheld_config = handheld_config;
    applet_resource_holder.input_event = input_event;
    applet_resource_holder.input_mutex = input_mutex;

    vibration_handler.SetSettingsService(settings);

    for (auto& abstract_pad : abstracted_pads) {
        abstract_pad.SetExternals(&applet_resource_holder, nullptr, nullptr, nullptr, nullptr,
                                  &vibration_handler, &hid_core);
    }
}

NPad::NpadControllerData& NPad::GetControllerFromHandle(
    u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) {
    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
    return GetControllerFromNpadIdType(aruid, npad_id);
}

const NPad::NpadControllerData& NPad::GetControllerFromHandle(
    u64 aruid, const Core::HID::SixAxisSensorHandle& device_handle) const {
    const auto npad_id = static_cast<Core::HID::NpadIdType>(device_handle.npad_id);
    return GetControllerFromNpadIdType(aruid, npad_id);
}

NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(u64 aruid,
                                                            Core::HID::NpadIdType npad_id) {
    if (!IsNpadIdValid(npad_id)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
        npad_id = Core::HID::NpadIdType::Player1;
    }
    const auto npad_index = NpadIdTypeToIndex(npad_id);
    const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
    return controller_data[aruid_index][npad_index];
}

const NPad::NpadControllerData& NPad::GetControllerFromNpadIdType(
    u64 aruid, Core::HID::NpadIdType npad_id) const {
    if (!IsNpadIdValid(npad_id)) {
        LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
        npad_id = Core::HID::NpadIdType::Player1;
    }
    const auto npad_index = NpadIdTypeToIndex(npad_id);
    const auto aruid_index = applet_resource_holder.applet_resource->GetIndexFromAruid(aruid);
    return controller_data[aruid_index][npad_index];
}

Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
    u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) {
    auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
    switch (sixaxis_handle.npad_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
    case Core::HID::NpadStyleIndex::Pokeball:
        return controller.shared_memory->sixaxis_fullkey_properties;
    case Core::HID::NpadStyleIndex::Handheld:
        return controller.shared_memory->sixaxis_handheld_properties;
    case Core::HID::NpadStyleIndex::JoyconDual:
        if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
            return controller.shared_memory->sixaxis_dual_left_properties;
        }
        return controller.shared_memory->sixaxis_dual_right_properties;
    case Core::HID::NpadStyleIndex::JoyconLeft:
        return controller.shared_memory->sixaxis_left_properties;
    case Core::HID::NpadStyleIndex::JoyconRight:
        return controller.shared_memory->sixaxis_right_properties;
    default:
        return controller.shared_memory->sixaxis_fullkey_properties;
    }
}

const Core::HID::SixAxisSensorProperties& NPad::GetSixaxisProperties(
    u64 aruid, const Core::HID::SixAxisSensorHandle& sixaxis_handle) const {
    const auto& controller = GetControllerFromHandle(aruid, sixaxis_handle);
    switch (sixaxis_handle.npad_type) {
    case Core::HID::NpadStyleIndex::Fullkey:
    case Core::HID::NpadStyleIndex::Pokeball:
        return controller.shared_memory->sixaxis_fullkey_properties;
    case Core::HID::NpadStyleIndex::Handheld:
        return controller.shared_memory->sixaxis_handheld_properties;
    case Core::HID::NpadStyleIndex::JoyconDual:
        if (sixaxis_handle.device_index == Core::HID::DeviceIndex::Left) {
            return controller.shared_memory->sixaxis_dual_left_properties;
        }
        return controller.shared_memory->sixaxis_dual_right_properties;
    case Core::HID::NpadStyleIndex::JoyconLeft:
        return controller.shared_memory->sixaxis_left_properties;
    case Core::HID::NpadStyleIndex::JoyconRight:
        return controller.shared_memory->sixaxis_right_properties;
    default:
        return controller.shared_memory->sixaxis_fullkey_properties;
    }
}

AppletDetailedUiType NPad::GetAppletDetailedUiType(Core::HID::NpadIdType npad_id) {
    const auto aruid = applet_resource_holder.applet_resource->GetActiveAruid();
    const auto& shared_memory = GetControllerFromNpadIdType(aruid, npad_id).shared_memory;

    return {
        .ui_variant = 0,
        .footer = shared_memory->applet_footer_type,
    };
}

Result NPad::SetNpadCaptureButtonAssignment(u64 aruid, Core::HID::NpadStyleSet npad_style_set,
                                            Core::HID::NpadButton button_assignment) {
    std::scoped_lock lock{mutex};
    return npad_resource.SetNpadCaptureButtonAssignment(aruid, npad_style_set, button_assignment);
}

Result NPad::ClearNpadCaptureButtonAssignment(u64 aruid) {
    std::scoped_lock lock{mutex};
    return npad_resource.ClearNpadCaptureButtonAssignment(aruid);
}

std::size_t NPad::GetNpadCaptureButtonAssignment(std::span<Core::HID::NpadButton> out_list,
                                                 u64 aruid) const {
    std::scoped_lock lock{mutex};
    return npad_resource.GetNpadCaptureButtonAssignment(out_list, aruid);
}

Result NPad::SetNpadSystemExtStateEnabled(u64 aruid, bool is_enabled) {
    std::scoped_lock lock{mutex};
    const auto result = npad_resource.SetNpadSystemExtStateEnabled(aruid, is_enabled);

    if (result.IsSuccess()) {
        std::scoped_lock shared_lock{*applet_resource_holder.shared_mutex};
        // TODO: abstracted_pad->EnableAppletToGetInput(aruid);
    }

    return result;
}

Result NPad::AssigningSingleOnSlSrPress(u64 aruid, bool is_enabled) {
    std::scoped_lock lock{mutex};
    bool is_currently_enabled{};
    Result result = npad_resource.IsAssigningSingleOnSlSrPressEnabled(is_currently_enabled, aruid);
    if (result.IsSuccess() && is_enabled != is_currently_enabled) {
        result = npad_resource.SetAssigningSingleOnSlSrPress(aruid, is_enabled);
    }
    return result;
}

Result NPad::GetLastActiveNpad(Core::HID::NpadIdType& out_npad_id) const {
    std::scoped_lock lock{mutex};
    out_npad_id = hid_core.GetLastActiveController();
    return ResultSuccess;
}

NpadVibration* NPad::GetVibrationHandler() {
    return &vibration_handler;
}

std::vector<NpadVibrationBase*> NPad::GetAllVibrationDevices() {
    std::vector<NpadVibrationBase*> vibration_devices;

    for (auto& abstract_pad : abstracted_pads) {
        auto* left_device = abstract_pad.GetVibrationDevice(Core::HID::DeviceIndex::Left);
        auto* right_device = abstract_pad.GetVibrationDevice(Core::HID::DeviceIndex::Right);
        auto* n64_device = abstract_pad.GetGCVibrationDevice();
        auto* gc_device = abstract_pad.GetGCVibrationDevice();

        if (left_device != nullptr) {
            vibration_devices.emplace_back(left_device);
        }
        if (right_device != nullptr) {
            vibration_devices.emplace_back(right_device);
        }
        if (n64_device != nullptr) {
            vibration_devices.emplace_back(n64_device);
        }
        if (gc_device != nullptr) {
            vibration_devices.emplace_back(gc_device);
        }
    }

    return vibration_devices;
}

NpadVibrationBase* NPad::GetVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
    if (IsVibrationHandleValid(handle).IsError()) {
        return nullptr;
    }

    const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
    const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
    if (style_inde == Core::HID::NpadStyleIndex::GameCube) {
        return abstracted_pads[npad_index].GetGCVibrationDevice();
    }
    if (style_inde == Core::HID::NpadStyleIndex::N64) {
        return abstracted_pads[npad_index].GetN64VibrationDevice();
    }
    return abstracted_pads[npad_index].GetVibrationDevice(handle.device_index);
}

NpadN64VibrationDevice* NPad::GetN64VibrationDevice(
    const Core::HID::VibrationDeviceHandle& handle) {
    if (IsVibrationHandleValid(handle).IsError()) {
        return nullptr;
    }

    const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
    const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
    if (style_inde != Core::HID::NpadStyleIndex::N64) {
        return nullptr;
    }
    return abstracted_pads[npad_index].GetN64VibrationDevice();
}

NpadVibrationDevice* NPad::GetNSVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
    if (IsVibrationHandleValid(handle).IsError()) {
        return nullptr;
    }

    const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
    const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
    if (style_inde == Core::HID::NpadStyleIndex::GameCube ||
        style_inde == Core::HID::NpadStyleIndex::N64) {
        return nullptr;
    }

    return abstracted_pads[npad_index].GetVibrationDevice(handle.device_index);
}

NpadGcVibrationDevice* NPad::GetGcVibrationDevice(const Core::HID::VibrationDeviceHandle& handle) {
    if (IsVibrationHandleValid(handle).IsError()) {
        return nullptr;
    }

    const auto npad_index = NpadIdTypeToIndex(static_cast<Core::HID::NpadIdType>(handle.npad_id));
    const auto style_inde = static_cast<Core::HID::NpadStyleIndex>(handle.npad_type);
    if (style_inde != Core::HID::NpadStyleIndex::GameCube) {
        return nullptr;
    }
    return abstracted_pads[npad_index].GetGCVibrationDevice();
}

void NPad::UpdateHandheldAbstractState() {
    std::scoped_lock lock{mutex};
    abstracted_pads[NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld)].Update();
}

void NPad::EnableAppletToGetInput(u64 aruid) {
    std::scoped_lock lock{mutex};
    std::scoped_lock shared_lock{*applet_resource_holder.shared_mutex};

    for (auto& abstract_pad : abstracted_pads) {
        abstract_pad.EnableAppletToGetInput(aruid);
    }
}

} // namespace Service::HID