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

#include <cmath>
#include <memory>

#include "common/logging/log.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/lbl/lbl.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"

namespace Service::LBL {

class LBL final : public ServiceFramework<LBL> {
public:
    explicit LBL(Core::System& system_) : ServiceFramework{system_, "lbl"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &LBL::SaveCurrentSetting, "SaveCurrentSetting"},
            {1, &LBL::LoadCurrentSetting, "LoadCurrentSetting"},
            {2, &LBL::SetCurrentBrightnessSetting, "SetCurrentBrightnessSetting"},
            {3, &LBL::GetCurrentBrightnessSetting, "GetCurrentBrightnessSetting"},
            {4, nullptr, "ApplyCurrentBrightnessSettingToBacklight"},
            {5, nullptr, "GetBrightnessSettingAppliedToBacklight"},
            {6, &LBL::SwitchBacklightOn, "SwitchBacklightOn"},
            {7, &LBL::SwitchBacklightOff, "SwitchBacklightOff"},
            {8, &LBL::GetBacklightSwitchStatus, "GetBacklightSwitchStatus"},
            {9, &LBL::EnableDimming, "EnableDimming"},
            {10, &LBL::DisableDimming, "DisableDimming"},
            {11, &LBL::IsDimmingEnabled, "IsDimmingEnabled"},
            {12, &LBL::EnableAutoBrightnessControl, "EnableAutoBrightnessControl"},
            {13, &LBL::DisableAutoBrightnessControl, "DisableAutoBrightnessControl"},
            {14, &LBL::IsAutoBrightnessControlEnabled, "IsAutoBrightnessControlEnabled"},
            {15, &LBL::SetAmbientLightSensorValue, "SetAmbientLightSensorValue"},
            {16, &LBL::GetAmbientLightSensorValue, "GetAmbientLightSensorValue"},
            {17, &LBL::SetBrightnessReflectionDelayLevel, "SetBrightnessReflectionDelayLevel"},
            {18, &LBL::GetBrightnessReflectionDelayLevel, "GetBrightnessReflectionDelayLevel"},
            {19, &LBL::SetCurrentBrightnessMapping, "SetCurrentBrightnessMapping"},
            {20, &LBL::GetCurrentBrightnessMapping, "GetCurrentBrightnessMapping"},
            {21, &LBL::SetCurrentAmbientLightSensorMapping, "SetCurrentAmbientLightSensorMapping"},
            {22, &LBL::GetCurrentAmbientLightSensorMapping, "GetCurrentAmbientLightSensorMapping"},
            {23, &LBL::IsAmbientLightSensorAvailable, "IsAmbientLightSensorAvailable"},
            {24, &LBL::SetCurrentBrightnessSettingForVrMode, "SetCurrentBrightnessSettingForVrMode"},
            {25, &LBL::GetCurrentBrightnessSettingForVrMode, "GetCurrentBrightnessSettingForVrMode"},
            {26, &LBL::EnableVrMode, "EnableVrMode"},
            {27, &LBL::DisableVrMode, "DisableVrMode"},
            {28, &LBL::IsVrModeEnabled, "IsVrModeEnabled"},
            {29, &LBL::IsAutoBrightnessControlSupported, "IsAutoBrightnessControlSupported"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    enum class BacklightSwitchStatus : u32 {
        Off = 0,
        On = 1,
    };

    void SaveCurrentSetting(HLERequestContext& ctx) {
        LOG_WARNING(Service_LBL, "(STUBBED) called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void LoadCurrentSetting(HLERequestContext& ctx) {
        LOG_WARNING(Service_LBL, "(STUBBED) called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void SetCurrentBrightnessSetting(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        auto brightness = rp.Pop<float>();

        if (!std::isfinite(brightness)) {
            LOG_ERROR(Service_LBL, "Brightness is infinite!");
            brightness = 0.0f;
        }

        LOG_DEBUG(Service_LBL, "called brightness={}", brightness);

        current_brightness = brightness;
        update_instantly = true;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetCurrentBrightnessSetting(HLERequestContext& ctx) {
        auto brightness = current_brightness;
        if (!std::isfinite(brightness)) {
            LOG_ERROR(Service_LBL, "Brightness is infinite!");
            brightness = 0.0f;
        }

        LOG_DEBUG(Service_LBL, "called brightness={}", brightness);

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(brightness);
    }

    void SwitchBacklightOn(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto fade_time = rp.Pop<u64_le>();
        LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);

        backlight_enabled = true;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void SwitchBacklightOff(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto fade_time = rp.Pop<u64_le>();
        LOG_WARNING(Service_LBL, "(STUBBED) called, fade_time={}", fade_time);

        backlight_enabled = false;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetBacklightSwitchStatus(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.PushEnum<BacklightSwitchStatus>(backlight_enabled ? BacklightSwitchStatus::On
                                                             : BacklightSwitchStatus::Off);
    }

    void EnableDimming(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        dimming = true;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void DisableDimming(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        dimming = false;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void IsDimmingEnabled(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(dimming);
    }

    void EnableAutoBrightnessControl(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");
        auto_brightness = true;
        update_instantly = true;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void DisableAutoBrightnessControl(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");
        auto_brightness = false;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void IsAutoBrightnessControlEnabled(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(auto_brightness);
    }

    void SetAmbientLightSensorValue(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto light_value = rp.Pop<float>();

        LOG_DEBUG(Service_LBL, "called light_value={}", light_value);

        ambient_light_value = light_value;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetAmbientLightSensorValue(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(ambient_light_value);
    }

    void SetBrightnessReflectionDelayLevel(HLERequestContext& ctx) {
        // This is Intentional, this function does absolutely nothing
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetBrightnessReflectionDelayLevel(HLERequestContext& ctx) {
        // This is intentional, the function is hard coded to return 0.0f on hardware
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(0.0f);
    }

    void SetCurrentBrightnessMapping(HLERequestContext& ctx) {
        // This is Intentional, this function does absolutely nothing
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetCurrentBrightnessMapping(HLERequestContext& ctx) {
        // This is Intentional, this function does absolutely nothing
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
        // This function is suppose to return something but it seems like it doesn't
    }

    void SetCurrentAmbientLightSensorMapping(HLERequestContext& ctx) {
        // This is Intentional, this function does absolutely nothing
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetCurrentAmbientLightSensorMapping(HLERequestContext& ctx) {
        // This is Intentional, this function does absolutely nothing
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
        // This function is suppose to return something but it seems like it doesn't
    }

    void IsAmbientLightSensorAvailable(HLERequestContext& ctx) {
        LOG_WARNING(Service_LBL, "(STUBBED) called");
        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        // TODO(ogniK): Only return true if there's no device error
        rb.Push(true);
    }

    void SetCurrentBrightnessSettingForVrMode(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        auto brightness = rp.Pop<float>();

        if (!std::isfinite(brightness)) {
            LOG_ERROR(Service_LBL, "Brightness is infinite!");
            brightness = 0.0f;
        }

        LOG_DEBUG(Service_LBL, "called brightness={}", brightness);

        current_vr_brightness = brightness;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    void GetCurrentBrightnessSettingForVrMode(HLERequestContext& ctx) {
        auto brightness = current_vr_brightness;
        if (!std::isfinite(brightness)) {
            LOG_ERROR(Service_LBL, "Brightness is infinite!");
            brightness = 0.0f;
        }

        LOG_DEBUG(Service_LBL, "called brightness={}", brightness);

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(brightness);
    }

    void EnableVrMode(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);

        vr_mode_enabled = true;
    }

    void DisableVrMode(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);

        vr_mode_enabled = false;
    }

    void IsVrModeEnabled(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push(vr_mode_enabled);
    }

    void IsAutoBrightnessControlSupported(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LBL, "called");

        IPC::ResponseBuilder rb{ctx, 3};
        rb.Push(ResultSuccess);
        rb.Push<u8>(auto_brightness_supported);
    }

    bool vr_mode_enabled = false;
    float current_brightness = 1.0f;
    float ambient_light_value = 0.0f;
    float current_vr_brightness = 1.0f;
    bool dimming = true;
    bool backlight_enabled = true;
    bool update_instantly = false;
    bool auto_brightness = false;
    bool auto_brightness_supported = true; // TODO(ogniK): Move to system settings
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("lbl", std::make_shared<LBL>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::LBL


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

#pragma once

namespace Core {
class System;
}

namespace Service::LBL {

void LoopProcess(Core::System& system);

} // namespace Service::LBL

repo_name: litucks/torzu

license: mit


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

#include "core/hle/service/ldn/lan_discovery.h"
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"

namespace Service::LDN {

LanStation::LanStation(s8 node_id_, LANDiscovery* discovery_)
    : node_info(nullptr), status(NodeStatus::Disconnected), node_id(node_id_),
      discovery(discovery_) {}

LanStation::~LanStation() = default;

NodeStatus LanStation::GetStatus() const {
    return status;
}

void LanStation::OnClose() {
    LOG_INFO(Service_LDN, "OnClose {}", node_id);
    Reset();
    discovery->UpdateNodes();
}

void LanStation::Reset() {
    status = NodeStatus::Disconnected;
};

void LanStation::OverrideInfo() {
    bool connected = GetStatus() == NodeStatus::Connected;
    node_info->node_id = node_id;
    node_info->is_connected = connected ? 1 : 0;
}

LANDiscovery::LANDiscovery(Network::RoomNetwork& room_network_)
    : stations({{{1, this}, {2, this}, {3, this}, {4, this}, {5, this}, {6, this}, {7, this}}}),
      room_network{room_network_} {}

LANDiscovery::~LANDiscovery() {
    if (inited) {
        Result rc = Finalize();
        LOG_INFO(Service_LDN, "Finalize: {}", rc.raw);
    }
}

void LANDiscovery::InitNetworkInfo() {
    network_info.common.bssid = GetFakeMac();
    network_info.common.channel = WifiChannel::Wifi24_6;
    network_info.common.link_level = LinkLevel::Good;
    network_info.common.network_type = PackedNetworkType::Ldn;
    network_info.common.ssid = fake_ssid;

    auto& nodes = network_info.ldn.nodes;
    for (std::size_t i = 0; i < NodeCountMax; i++) {
        nodes[i].node_id = static_cast<s8>(i);
        nodes[i].is_connected = 0;
    }
}

void LANDiscovery::InitNodeStateChange() {
    for (auto& node_update : node_changes) {
        node_update.state_change = NodeStateChange::None;
    }
    for (auto& node_state : node_last_states) {
        node_state = 0;
    }
}

State LANDiscovery::GetState() const {
    return state;
}

void LANDiscovery::SetState(State new_state) {
    state = new_state;
}

Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const {
    if (state == State::AccessPointCreated || state == State::StationConnected) {
        std::memcpy(&out_network, &network_info, sizeof(network_info));
        return ResultSuccess;
    }

    return ResultBadState;
}

Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
                                    std::span<NodeLatestUpdate> out_updates) {
    if (out_updates.size() > NodeCountMax) {
        return ResultInvalidBufferCount;
    }

    if (state == State::AccessPointCreated || state == State::StationConnected) {
        std::memcpy(&out_network, &network_info, sizeof(network_info));
        for (std::size_t i = 0; i < out_updates.size(); i++) {
            out_updates[i].state_change = node_changes[i].state_change;
            node_changes[i].state_change = NodeStateChange::None;
        }
        return ResultSuccess;
    }

    return ResultBadState;
}

DisconnectReason LANDiscovery::GetDisconnectReason() const {
    return disconnect_reason;
}

Result LANDiscovery::Scan(std::span<NetworkInfo> out_networks, s16& out_count,
                          const ScanFilter& filter) {
    {
        std::scoped_lock lock{packet_mutex};
        scan_results.clear();

        SendBroadcast(Network::LDNPacketType::Scan);
    }

    LOG_INFO(Service_LDN, "Waiting for scan replies");
    std::this_thread::sleep_for(std::chrono::seconds(1));

    std::scoped_lock lock{packet_mutex};
    for (const auto& [key, info] : scan_results) {
        if (out_count >= static_cast<s16>(out_networks.size())) {
            break;
        }

        if (IsFlagSet(filter.flag, ScanFilterFlag::LocalCommunicationId)) {
            if (filter.network_id.intent_id.local_communication_id !=
                info.network_id.intent_id.local_communication_id) {
                continue;
            }
        }
        if (IsFlagSet(filter.flag, ScanFilterFlag::SessionId)) {
            if (filter.network_id.session_id != info.network_id.session_id) {
                continue;
            }
        }
        if (IsFlagSet(filter.flag, ScanFilterFlag::NetworkType)) {
            if (filter.network_type != static_cast<NetworkType>(info.common.network_type)) {
                continue;
            }
        }
        if (IsFlagSet(filter.flag, ScanFilterFlag::Ssid)) {
            if (filter.ssid != info.common.ssid) {
                continue;
            }
        }
        if (IsFlagSet(filter.flag, ScanFilterFlag::SceneId)) {
            if (filter.network_id.intent_id.scene_id != info.network_id.intent_id.scene_id) {
                continue;
            }
        }

        out_networks[out_count++] = info;
    }

    return ResultSuccess;
}

Result LANDiscovery::SetAdvertiseData(std::span<const u8> data) {
    std::scoped_lock lock{packet_mutex};
    const std::size_t size = data.size();
    if (size > AdvertiseDataSizeMax) {
        return ResultAdvertiseDataTooLarge;
    }

    std::memcpy(network_info.ldn.advertise_data.data(), data.data(), size);
    network_info.ldn.advertise_data_size = static_cast<u16>(size);

    UpdateNodes();

    return ResultSuccess;
}

Result LANDiscovery::OpenAccessPoint() {
    std::scoped_lock lock{packet_mutex};
    disconnect_reason = DisconnectReason::None;
    if (state == State::None) {
        return ResultBadState;
    }

    ResetStations();
    SetState(State::AccessPointOpened);

    return ResultSuccess;
}

Result LANDiscovery::CloseAccessPoint() {
    std::scoped_lock lock{packet_mutex};
    if (state == State::None) {
        return ResultBadState;
    }

    if (state == State::AccessPointCreated) {
        DestroyNetwork();
    }

    ResetStations();
    SetState(State::Initialized);

    return ResultSuccess;
}

Result LANDiscovery::OpenStation() {
    std::scoped_lock lock{packet_mutex};
    disconnect_reason = DisconnectReason::None;
    if (state == State::None) {
        return ResultBadState;
    }

    ResetStations();
    SetState(State::StationOpened);

    return ResultSuccess;
}

Result LANDiscovery::CloseStation() {
    std::scoped_lock lock{packet_mutex};
    if (state == State::None) {
        return ResultBadState;
    }

    if (state == State::StationConnected) {
        Disconnect();
    }

    ResetStations();
    SetState(State::Initialized);

    return ResultSuccess;
}

Result LANDiscovery::CreateNetwork(const SecurityConfig& security_config,
                                   const UserConfig& user_config,
                                   const NetworkConfig& network_config) {
    std::scoped_lock lock{packet_mutex};

    if (state != State::AccessPointOpened) {
        return ResultBadState;
    }

    InitNetworkInfo();
    network_info.ldn.node_count_max = network_config.node_count_max;
    network_info.ldn.security_mode = security_config.security_mode;

    if (network_config.channel == WifiChannel::Default) {
        network_info.common.channel = WifiChannel::Wifi24_6;
    } else {
        network_info.common.channel = network_config.channel;
    }

    std::independent_bits_engine<std::mt19937, 64, u64> bits_engine;
    network_info.network_id.session_id.high = bits_engine();
    network_info.network_id.session_id.low = bits_engine();
    network_info.network_id.intent_id = network_config.intent_id;

    NodeInfo& node0 = network_info.ldn.nodes[0];
    const Result rc2 = GetNodeInfo(node0, user_config, network_config.local_communication_version);
    if (rc2.IsError()) {
        return ResultAccessPointConnectionFailed;
    }

    SetState(State::AccessPointCreated);

    InitNodeStateChange();
    node0.is_connected = 1;
    UpdateNodes();

    return rc2;
}

Result LANDiscovery::DestroyNetwork() {
    for (auto local_ip : connected_clients) {
        SendPacket(Network::LDNPacketType::DestroyNetwork, local_ip);
    }

    ResetStations();

    SetState(State::AccessPointOpened);
    lan_event();

    return ResultSuccess;
}

Result LANDiscovery::Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
                             u16 local_communication_version) {
    std::scoped_lock lock{packet_mutex};
    if (network_info_.ldn.node_count == 0) {
        return ResultInvalidNodeCount;
    }

    Result rc = GetNodeInfo(node_info, user_config, local_communication_version);
    if (rc.IsError()) {
        return ResultConnectionFailed;
    }

    Ipv4Address node_host = network_info_.ldn.nodes[0].ipv4_address;
    std::reverse(std::begin(node_host), std::end(node_host)); // htonl
    host_ip = node_host;
    SendPacket(Network::LDNPacketType::Connect, node_info, *host_ip);

    InitNodeStateChange();

    std::this_thread::sleep_for(std::chrono::seconds(1));

    return ResultSuccess;
}

Result LANDiscovery::Disconnect() {
    if (host_ip) {
        SendPacket(Network::LDNPacketType::Disconnect, node_info, *host_ip);
    }

    SetState(State::StationOpened);
    lan_event();

    return ResultSuccess;
}

Result LANDiscovery::Initialize(LanEventFunc lan_event_, bool listening) {
    std::scoped_lock lock{packet_mutex};
    if (inited) {
        return ResultSuccess;
    }

    for (auto& station : stations) {
        station.discovery = this;
        station.node_info = &network_info.ldn.nodes[station.node_id];
        station.Reset();
    }

    connected_clients.clear();
    lan_event = lan_event_;

    SetState(State::Initialized);

    inited = true;
    return ResultSuccess;
}

Result LANDiscovery::Finalize() {
    std::scoped_lock lock{packet_mutex};
    Result rc = ResultSuccess;

    if (inited) {
        if (state == State::AccessPointCreated) {
            DestroyNetwork();
        }
        if (state == State::StationConnected) {
            Disconnect();
        }

        ResetStations();
        inited = false;
    }

    SetState(State::None);

    return rc;
}

void LANDiscovery::ResetStations() {
    for (auto& station : stations) {
        station.Reset();
    }
    connected_clients.clear();
}

void LANDiscovery::UpdateNodes() {
    u8 count = 0;
    for (auto& station : stations) {
        bool connected = station.GetStatus() == NodeStatus::Connected;
        if (connected) {
            count++;
        }
        station.OverrideInfo();
    }
    network_info.ldn.node_count = count + 1;

    for (auto local_ip : connected_clients) {
        SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip);
    }

    OnNetworkInfoChanged();
}

void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) {
    network_info = info;
    if (state == State::StationOpened) {
        SetState(State::StationConnected);
    }
    OnNetworkInfoChanged();
}

void LANDiscovery::OnDisconnectFromHost() {
    LOG_INFO(Service_LDN, "OnDisconnectFromHost state: {}", static_cast<int>(state));
    host_ip = std::nullopt;
    if (state == State::StationConnected) {
        SetState(State::StationOpened);
        lan_event();
    }
}

void LANDiscovery::OnNetworkInfoChanged() {
    if (IsNodeStateChanged()) {
        lan_event();
    }
    return;
}

Network::IPv4Address LANDiscovery::GetLocalIp() const {
    Network::IPv4Address local_ip{0xFF, 0xFF, 0xFF, 0xFF};
    if (auto room_member = room_network.GetRoomMember().lock()) {
        if (room_member->IsConnected()) {
            local_ip = room_member->GetFakeIpAddress();
        }
    }
    return local_ip;
}

template <typename Data>
void LANDiscovery::SendPacket(Network::LDNPacketType type, const Data& data,
                              Ipv4Address remote_ip) {
    Network::LDNPacket packet;
    packet.type = type;

    packet.broadcast = false;
    packet.local_ip = GetLocalIp();
    packet.remote_ip = remote_ip;

    packet.data.resize(sizeof(data));
    std::memcpy(packet.data.data(), &data, sizeof(data));
    SendPacket(packet);
}

void LANDiscovery::SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip) {
    Network::LDNPacket packet;
    packet.type = type;

    packet.broadcast = false;
    packet.local_ip = GetLocalIp();
    packet.remote_ip = remote_ip;

    SendPacket(packet);
}

template <typename Data>
void LANDiscovery::SendBroadcast(Network::LDNPacketType type, const Data& data) {
    Network::LDNPacket packet;
    packet.type = type;

    packet.broadcast = true;
    packet.local_ip = GetLocalIp();

    packet.data.resize(sizeof(data));
    std::memcpy(packet.data.data(), &data, sizeof(data));
    SendPacket(packet);
}

void LANDiscovery::SendBroadcast(Network::LDNPacketType type) {
    Network::LDNPacket packet;
    packet.type = type;

    packet.broadcast = true;
    packet.local_ip = GetLocalIp();

    SendPacket(packet);
}

void LANDiscovery::SendPacket(const Network::LDNPacket& packet) {
    if (auto room_member = room_network.GetRoomMember().lock()) {
        if (room_member->IsConnected()) {
            room_member->SendLdnPacket(packet);
        }
    }
}

void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) {
    std::scoped_lock lock{packet_mutex};
    switch (packet.type) {
    case Network::LDNPacketType::Scan: {
        LOG_INFO(Frontend, "Scan packet received!");
        if (state == State::AccessPointCreated) {
            // Reply to the sender
            SendPacket(Network::LDNPacketType::ScanResp, network_info, packet.local_ip);
        }
        break;
    }
    case Network::LDNPacketType::ScanResp: {
        LOG_INFO(Frontend, "ScanResp packet received!");

        NetworkInfo info{};
        std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
        scan_results.insert({info.common.bssid, info});

        break;
    }
    case Network::LDNPacketType::Connect: {
        LOG_INFO(Frontend, "Connect packet received!");

        NodeInfo info{};
        std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));

        connected_clients.push_back(packet.local_ip);

        for (LanStation& station : stations) {
            if (station.status != NodeStatus::Connected) {
                *station.node_info = info;
                station.status = NodeStatus::Connected;
                break;
            }
        }

        UpdateNodes();

        break;
    }
    case Network::LDNPacketType::Disconnect: {
        LOG_INFO(Frontend, "Disconnect packet received!");

        connected_clients.erase(
            std::remove(connected_clients.begin(), connected_clients.end(), packet.local_ip),
            connected_clients.end());

        NodeInfo info{};
        std::memcpy(&info, packet.data.data(), sizeof(NodeInfo));

        for (LanStation& station : stations) {
            if (station.status == NodeStatus::Connected &&
                station.node_info->mac_address == info.mac_address) {
                station.OnClose();
                break;
            }
        }

        break;
    }
    case Network::LDNPacketType::DestroyNetwork: {
        ResetStations();
        OnDisconnectFromHost();
        break;
    }
    case Network::LDNPacketType::SyncNetwork: {
        if (state == State::StationOpened || state == State::StationConnected) {
            LOG_INFO(Frontend, "SyncNetwork packet received!");
            NetworkInfo info{};
            std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));

            OnSyncNetwork(info);
        } else {
            LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!");
        }

        break;
    }
    default: {
        LOG_INFO(Frontend, "ReceivePacket unhandled type {}", static_cast<int>(packet.type));
        break;
    }
    }
}

bool LANDiscovery::IsNodeStateChanged() {
    bool changed = false;
    const auto& nodes = network_info.ldn.nodes;
    for (int i = 0; i < NodeCountMax; i++) {
        if (nodes[i].is_connected != node_last_states[i]) {
            if (nodes[i].is_connected) {
                node_changes[i].state_change |= NodeStateChange::Connect;
            } else {
                node_changes[i].state_change |= NodeStateChange::Disconnect;
            }
            node_last_states[i] = nodes[i].is_connected;
            changed = true;
        }
    }
    return changed;
}

bool LANDiscovery::IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const {
    const auto flag_value = static_cast<u32>(flag);
    const auto search_flag_value = static_cast<u32>(search_flag);
    return (flag_value & search_flag_value) == search_flag_value;
}

int LANDiscovery::GetStationCount() const {
    return static_cast<int>(
        std::count_if(stations.begin(), stations.end(), [](const auto& station) {
            return station.GetStatus() != NodeStatus::Disconnected;
        }));
}

MacAddress LANDiscovery::GetFakeMac() const {
    MacAddress mac{};
    mac.raw[0] = 0x02;
    mac.raw[1] = 0x00;

    const auto ip = GetLocalIp();
    memcpy(mac.raw.data() + 2, &ip, sizeof(ip));

    return mac;
}

Result LANDiscovery::GetNodeInfo(NodeInfo& node, const UserConfig& userConfig,
                                 u16 localCommunicationVersion) {
    const auto network_interface = Network::GetSelectedNetworkInterface();

    if (!network_interface) {
        LOG_ERROR(Service_LDN, "No network interface available");
        return ResultNoIpAddress;
    }

    node.mac_address = GetFakeMac();
    node.is_connected = 1;
    std::memcpy(node.user_name.data(), userConfig.user_name.data(), UserNameBytesMax + 1);
    node.local_communication_version = localCommunicationVersion;

    Ipv4Address current_address = GetLocalIp();
    std::reverse(std::begin(current_address), std::end(current_address)); // ntohl
    node.ipv4_address = current_address;

    return ResultSuccess;
}

} // namespace Service::LDN


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

#pragma once

#include <array>
#include <cstring>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <random>
#include <span>
#include <thread>
#include <unordered_map>

#include "common/logging/log.h"
#include "common/socket_types.h"
#include "core/hle/result.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h"
#include "network/network.h"

namespace Service::LDN {

class LANDiscovery;

class LanStation {
public:
    LanStation(s8 node_id_, LANDiscovery* discovery_);
    ~LanStation();

    void OnClose();
    NodeStatus GetStatus() const;
    void Reset();
    void OverrideInfo();

protected:
    friend class LANDiscovery;
    NodeInfo* node_info;
    NodeStatus status;
    s8 node_id;
    LANDiscovery* discovery;
};

class LANDiscovery {
public:
    using LanEventFunc = std::function<void()>;

    LANDiscovery(Network::RoomNetwork& room_network_);
    ~LANDiscovery();

    State GetState() const;
    void SetState(State new_state);

    Result GetNetworkInfo(NetworkInfo& out_network) const;
    Result GetNetworkInfo(NetworkInfo& out_network, std::span<NodeLatestUpdate> out_updates);

    DisconnectReason GetDisconnectReason() const;
    Result Scan(std::span<NetworkInfo> out_networks, s16& out_count, const ScanFilter& filter);
    Result SetAdvertiseData(std::span<const u8> data);

    Result OpenAccessPoint();
    Result CloseAccessPoint();

    Result OpenStation();
    Result CloseStation();

    Result CreateNetwork(const SecurityConfig& security_config, const UserConfig& user_config,
                         const NetworkConfig& network_config);
    Result DestroyNetwork();

    Result Connect(const NetworkInfo& network_info_, const UserConfig& user_config,
                   u16 local_communication_version);
    Result Disconnect();

    Result Initialize(LanEventFunc lan_event_ = empty_func, bool listening = true);
    Result Finalize();

    void ReceivePacket(const Network::LDNPacket& packet);

protected:
    friend class LanStation;

    void InitNetworkInfo();
    void InitNodeStateChange();

    void ResetStations();
    void UpdateNodes();

    void OnSyncNetwork(const NetworkInfo& info);
    void OnDisconnectFromHost();
    void OnNetworkInfoChanged();

    bool IsNodeStateChanged();
    bool IsFlagSet(ScanFilterFlag flag, ScanFilterFlag search_flag) const;
    int GetStationCount() const;
    MacAddress GetFakeMac() const;
    Result GetNodeInfo(NodeInfo& node, const UserConfig& user_config,
                       u16 local_communication_version);

    Network::IPv4Address GetLocalIp() const;
    template <typename Data>
    void SendPacket(Network::LDNPacketType type, const Data& data, Ipv4Address remote_ip);
    void SendPacket(Network::LDNPacketType type, Ipv4Address remote_ip);
    template <typename Data>
    void SendBroadcast(Network::LDNPacketType type, const Data& data);
    void SendBroadcast(Network::LDNPacketType type);
    void SendPacket(const Network::LDNPacket& packet);

    static const LanEventFunc empty_func;
    static constexpr Ssid fake_ssid{"YuzuFakeSsidForLdn"};

    bool inited{};
    std::mutex packet_mutex;
    std::array<LanStation, StationCountMax> stations;
    std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
    std::array<u8, NodeCountMax> node_last_states{};
    std::unordered_map<MacAddress, NetworkInfo, MACAddressHash> scan_results{};
    NodeInfo node_info{};
    NetworkInfo network_info{};
    State state{State::None};
    DisconnectReason disconnect_reason{DisconnectReason::None};

    // TODO (flTobi): Should this be an std::set?
    std::vector<Ipv4Address> connected_clients;
    std::optional<Ipv4Address> host_ip;

    LanEventFunc lan_event;

    Network::RoomNetwork& room_network;
};
} // namespace Service::LDN


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

#include "core/core.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn.h"
#include "core/hle/service/ldn/monitor_service.h"
#include "core/hle/service/ldn/sf_monitor_service.h"
#include "core/hle/service/ldn/sf_service.h"
#include "core/hle/service/ldn/sf_service_monitor.h"
#include "core/hle/service/ldn/system_local_communication_service.h"
#include "core/hle/service/ldn/user_local_communication_service.h"

namespace Service::LDN {

class IMonitorServiceCreator final : public ServiceFramework<IMonitorServiceCreator> {
public:
    explicit IMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:m"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&IMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"}
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    Result CreateMonitorService(OutInterface<IMonitorService> out_interface) {
        LOG_DEBUG(Service_LDN, "called");

        *out_interface = std::make_shared<IMonitorService>(system);
        R_SUCCEED();
    }
};

class ISystemServiceCreator final : public ServiceFramework<ISystemServiceCreator> {
public:
    explicit ISystemServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:s"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&ISystemServiceCreator::CreateSystemLocalCommunicationService>, "CreateSystemLocalCommunicationService"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    Result CreateSystemLocalCommunicationService(
        OutInterface<ISystemLocalCommunicationService> out_interface) {
        LOG_DEBUG(Service_LDN, "called");

        *out_interface = std::make_shared<ISystemLocalCommunicationService>(system);
        R_SUCCEED();
    }
};

class IUserServiceCreator final : public ServiceFramework<IUserServiceCreator> {
public:
    explicit IUserServiceCreator(Core::System& system_) : ServiceFramework{system_, "ldn:u"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&IUserServiceCreator::CreateUserLocalCommunicationService>, "CreateUserLocalCommunicationService"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    Result CreateUserLocalCommunicationService(
        OutInterface<IUserLocalCommunicationService> out_interface) {
        LOG_DEBUG(Service_LDN, "called");

        *out_interface = std::make_shared<IUserLocalCommunicationService>(system);
        R_SUCCEED();
    }
};

class ISfServiceCreator final : public ServiceFramework<ISfServiceCreator> {
public:
    explicit ISfServiceCreator(Core::System& system_, bool is_system_, const char* name_)
        : ServiceFramework{system_, name_}, is_system{is_system_} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&ISfServiceCreator::CreateNetworkService>, "CreateNetworkService"},
            {8, C<&ISfServiceCreator::CreateNetworkServiceMonitor>, "CreateNetworkServiceMonitor"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    Result CreateNetworkService(OutInterface<ISfService> out_interface, u32 input,
                                u64 reserved_input) {
        LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={} input={}", reserved_input,
                    input);

        *out_interface = std::make_shared<ISfService>(system);
        R_SUCCEED();
    }

    Result CreateNetworkServiceMonitor(OutInterface<ISfServiceMonitor> out_interface,
                                       u64 reserved_input) {
        LOG_WARNING(Service_LDN, "(STUBBED) called reserved_input={}", reserved_input);

        *out_interface = std::make_shared<ISfServiceMonitor>(system);
        R_SUCCEED();
    }

    bool is_system{};
};

class ISfMonitorServiceCreator final : public ServiceFramework<ISfMonitorServiceCreator> {
public:
    explicit ISfMonitorServiceCreator(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&ISfMonitorServiceCreator::CreateMonitorService>, "CreateMonitorService"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    Result CreateMonitorService(OutInterface<ISfMonitorService> out_interface, u64 reserved_input) {
        LOG_INFO(Service_LDN, "called, reserved_input={}", reserved_input);

        *out_interface = std::make_shared<ISfMonitorService>(system);
        R_SUCCEED();
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("ldn:m", std::make_shared<IMonitorServiceCreator>(system));
    server_manager->RegisterNamedService("ldn:s", std::make_shared<ISystemServiceCreator>(system));
    server_manager->RegisterNamedService("ldn:u", std::make_shared<IUserServiceCreator>(system));

    server_manager->RegisterNamedService(
        "lp2p:app", std::make_shared<ISfServiceCreator>(system, false, "lp2p:app"));
    server_manager->RegisterNamedService(
        "lp2p:sys", std::make_shared<ISfServiceCreator>(system, true, "lp2p:sys"));
    server_manager->RegisterNamedService("lp2p:m",
                                         std::make_shared<ISfMonitorServiceCreator>(system));

    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::LDN


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

#pragma once

namespace Core {
class System;
}

namespace Service::LDN {

void LoopProcess(Core::System& system);

} // namespace Service::LDN


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

#pragma once

#include "core/hle/result.h"

namespace Service::LDN {

constexpr Result ResultAdvertiseDataTooLarge{ErrorModule::LDN, 10};
constexpr Result ResultAuthenticationFailed{ErrorModule::LDN, 20};
constexpr Result ResultDisabled{ErrorModule::LDN, 22};
constexpr Result ResultAirplaneModeEnabled{ErrorModule::LDN, 23};
constexpr Result ResultInvalidNodeCount{ErrorModule::LDN, 30};
constexpr Result ResultConnectionFailed{ErrorModule::LDN, 31};
constexpr Result ResultBadState{ErrorModule::LDN, 32};
constexpr Result ResultNoIpAddress{ErrorModule::LDN, 33};
constexpr Result ResultInvalidBufferCount{ErrorModule::LDN, 50};
constexpr Result ResultAccessPointConnectionFailed{ErrorModule::LDN, 65};
constexpr Result ResultAuthenticationTimeout{ErrorModule::LDN, 66};
constexpr Result ResultMaximumNodeCount{ErrorModule::LDN, 67};
constexpr Result ResultBadInput{ErrorModule::LDN, 96};
constexpr Result ResultLocalCommunicationIdNotFound{ErrorModule::LDN, 97};
constexpr Result ResultLocalCommunicationVersionTooLow{ErrorModule::LDN, 113};
constexpr Result ResultLocalCommunicationVersionTooHigh{ErrorModule::LDN, 114};

} // namespace Service::LDN


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

#pragma once

#include <fmt/format.h>

#include "common/common_funcs.h"
#include "common/common_types.h"
#include "network/network.h"

namespace Service::LDN {

constexpr size_t SsidLengthMax = 32;
constexpr size_t AdvertiseDataSizeMax = 384;
constexpr size_t UserNameBytesMax = 32;
constexpr int NodeCountMax = 8;
constexpr int StationCountMax = NodeCountMax - 1;
constexpr size_t PassphraseLengthMax = 64;

enum class SecurityMode : u16 {
    All,
    Retail,
    Debug,
};

enum class NodeStateChange : u8 {
    None,
    Connect,
    Disconnect,
    DisconnectAndConnect,
};

DECLARE_ENUM_FLAG_OPERATORS(NodeStateChange)

enum class ScanFilterFlag : u32 {
    None = 0,
    LocalCommunicationId = 1 << 0,
    SessionId = 1 << 1,
    NetworkType = 1 << 2,
    Ssid = 1 << 4,
    SceneId = 1 << 5,
    IntentId = LocalCommunicationId | SceneId,
    NetworkId = IntentId | SessionId,
};

enum class NetworkType : u32 {
    None,
    General,
    Ldn,
    All,
};

enum class PackedNetworkType : u8 {
    None,
    General,
    Ldn,
    All,
};

enum class State : u32 {
    None,
    Initialized,
    AccessPointOpened,
    AccessPointCreated,
    StationOpened,
    StationConnected,
    Error,
};

enum class DisconnectReason : s16 {
    Unknown = -1,
    None,
    DisconnectedByUser,
    DisconnectedBySystem,
    DestroyedByUser,
    DestroyedBySystem,
    Rejected,
    SignalLost,
};

enum class NetworkError {
    Unknown = -1,
    None = 0,
    PortUnreachable,
    TooManyPlayers,
    VersionTooLow,
    VersionTooHigh,
    ConnectFailure,
    ConnectNotFound,
    ConnectTimeout,
    ConnectRejected,
    RejectFailed,
};

enum class AcceptPolicy : u8 {
    AcceptAll,
    RejectAll,
    BlackList,
    WhiteList,
};

enum class WifiChannel : s16 {
    Default = 0,
    Wifi24_1 = 1,
    Wifi24_6 = 6,
    Wifi24_11 = 11,
    Wifi50_36 = 36,
    Wifi50_40 = 40,
    Wifi50_44 = 44,
    Wifi50_48 = 48,
};

enum class LinkLevel : s8 {
    Bad,
    Low,
    Good,
    Excellent,
};

enum class NodeStatus : u8 {
    Disconnected,
    Connected,
};

enum class WirelessControllerRestriction : u32 {
    None,
    Default,
};

struct ConnectOption {
    union {
        u32 raw;
    };
};
static_assert(sizeof(ConnectOption) == 0x4, "ConnectOption is an invalid size");

struct NodeLatestUpdate {
    NodeStateChange state_change;
    INSERT_PADDING_BYTES(0x7); // Unknown
};
static_assert(sizeof(NodeLatestUpdate) == 0x8, "NodeLatestUpdate is an invalid size");

struct SessionId {
    u64 high;
    u64 low;

    bool operator==(const SessionId&) const = default;
};
static_assert(sizeof(SessionId) == 0x10, "SessionId is an invalid size");

struct IntentId {
    u64 local_communication_id;
    INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved
    u16 scene_id;
    INSERT_PADDING_BYTES_NOINIT(0x4); // Reserved
};
static_assert(sizeof(IntentId) == 0x10, "IntentId is an invalid size");

struct NetworkId {
    IntentId intent_id;
    SessionId session_id;
};
static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size");

struct Ssid {
    u8 length;
    std::array<char, SsidLengthMax + 1> raw;

    Ssid() = default;

    constexpr explicit Ssid(std::string_view data) {
        length = static_cast<u8>(std::min(data.size(), SsidLengthMax));
        raw = {};
        data.copy(raw.data(), length);
        raw[length] = 0;
    }

    std::string GetStringValue() const {
        return std::string(raw.data());
    }

    bool operator==(const Ssid& b) const {
        return (length == b.length) && (std::memcmp(raw.data(), b.raw.data(), length) == 0);
    }

    bool operator!=(const Ssid& b) const {
        return !operator==(b);
    }
};
static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size");

using Ipv4Address = std::array<u8, 4>;
static_assert(sizeof(Ipv4Address) == 0x4, "Ipv4Address is an invalid size");

struct MacAddress {
    std::array<u8, 6> raw;

    friend bool operator==(const MacAddress& lhs, const MacAddress& rhs) = default;
};
static_assert(sizeof(MacAddress) == 0x6, "MacAddress is an invalid size");

struct MACAddressHash {
    size_t operator()(const MacAddress& address) const {
        u64 value{};
        std::memcpy(&value, address.raw.data(), sizeof(address.raw));
        return value;
    }
};

struct ScanFilter {
    NetworkId network_id;
    NetworkType network_type;
    MacAddress mac_address;
    Ssid ssid;
    INSERT_PADDING_BYTES(0x10);
    ScanFilterFlag flag;
};
static_assert(sizeof(ScanFilter) == 0x60, "ScanFilter is an invalid size");

struct CommonNetworkInfo {
    MacAddress bssid;
    Ssid ssid;
    WifiChannel channel;
    LinkLevel link_level;
    PackedNetworkType network_type;
    INSERT_PADDING_BYTES_NOINIT(0x4);
};
static_assert(sizeof(CommonNetworkInfo) == 0x30, "CommonNetworkInfo is an invalid size");

struct NodeInfo {
    Ipv4Address ipv4_address;
    MacAddress mac_address;
    s8 node_id;
    u8 is_connected;
    std::array<u8, UserNameBytesMax + 1> user_name;
    INSERT_PADDING_BYTES_NOINIT(0x1); // Reserved
    s16 local_communication_version;
    INSERT_PADDING_BYTES_NOINIT(0x10); // Reserved
};
static_assert(sizeof(NodeInfo) == 0x40, "NodeInfo is an invalid size");

struct LdnNetworkInfo {
    std::array<u8, 0x10> security_parameter;
    SecurityMode security_mode;
    AcceptPolicy station_accept_policy;
    u8 has_action_frame;
    INSERT_PADDING_BYTES_NOINIT(0x2); // Padding
    u8 node_count_max;
    u8 node_count;
    std::array<NodeInfo, NodeCountMax> nodes;
    INSERT_PADDING_BYTES_NOINIT(0x2); // Reserved
    u16 advertise_data_size;
    std::array<u8, AdvertiseDataSizeMax> advertise_data;
    INSERT_PADDING_BYTES_NOINIT(0x8C); // Reserved
    u64 random_authentication_id;
};
static_assert(sizeof(LdnNetworkInfo) == 0x430, "LdnNetworkInfo is an invalid size");

struct NetworkInfo {
    NetworkId network_id;
    CommonNetworkInfo common;
    LdnNetworkInfo ldn;
};
static_assert(sizeof(NetworkInfo) == 0x480, "NetworkInfo is an invalid size");
static_assert(std::is_trivial_v<NetworkInfo>, "NetworkInfo type must be trivially copyable.");

struct SecurityConfig {
    SecurityMode security_mode;
    u16 passphrase_size;
    std::array<u8, PassphraseLengthMax> passphrase;
};
static_assert(sizeof(SecurityConfig) == 0x44, "SecurityConfig is an invalid size");

struct UserConfig {
    std::array<u8, UserNameBytesMax + 1> user_name;
    INSERT_PADDING_BYTES(0xF); // Reserved
};
static_assert(sizeof(UserConfig) == 0x30, "UserConfig is an invalid size");

#pragma pack(push, 4)
struct ConnectRequest {
    SecurityConfig security_config;
    UserConfig user_config;
    u32 local_communication_version;
    u32 option_unknown;
    NetworkInfo network_info;
};
static_assert(sizeof(ConnectRequest) == 0x4FC, "ConnectRequest is an invalid size");
#pragma pack(pop)

struct SecurityParameter {
    std::array<u8, 0x10> data; // Data, used with the same key derivation as SecurityConfig
    SessionId session_id;
};
static_assert(sizeof(SecurityParameter) == 0x20, "SecurityParameter is an invalid size");

struct NetworkConfig {
    IntentId intent_id;
    WifiChannel channel;
    u8 node_count_max;
    INSERT_PADDING_BYTES(0x1); // Reserved
    u16 local_communication_version;
    INSERT_PADDING_BYTES(0xA); // Reserved
};
static_assert(sizeof(NetworkConfig) == 0x20, "NetworkConfig is an invalid size");

struct AddressEntry {
    Ipv4Address ipv4_address;
    MacAddress mac_address;
    INSERT_PADDING_BYTES(0x2); // Reserved
};
static_assert(sizeof(AddressEntry) == 0xC, "AddressEntry is an invalid size");

struct AddressList {
    std::array<AddressEntry, 0x8> addresses;
};
static_assert(sizeof(AddressList) == 0x60, "AddressList is an invalid size");

struct GroupInfo {
    std::array<u8, 0x200> info;
};

struct CreateNetworkConfig {
    SecurityConfig security_config;
    UserConfig user_config;
    INSERT_PADDING_BYTES(0x4);
    NetworkConfig network_config;
};
static_assert(sizeof(CreateNetworkConfig) == 0x98, "CreateNetworkConfig is an invalid size");

#pragma pack(push, 4)
struct CreateNetworkConfigPrivate {
    SecurityConfig security_config;
    SecurityParameter security_parameter;
    UserConfig user_config;
    INSERT_PADDING_BYTES(0x4);
    NetworkConfig network_config;
};
#pragma pack(pop)
static_assert(sizeof(CreateNetworkConfigPrivate) == 0xB8,
              "CreateNetworkConfigPrivate is an invalid size");

struct ConnectNetworkData {
    SecurityConfig security_config;
    UserConfig user_config;
    s32 local_communication_version;
    ConnectOption option;
};
static_assert(sizeof(ConnectNetworkData) == 0x7c, "ConnectNetworkData is an invalid size");

} // namespace Service::LDN


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

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/monitor_service.h"

namespace Service::LDN {

IMonitorService::IMonitorService(Core::System& system_)
    : ServiceFramework{system_, "IMonitorService"} {
    // clang-format off
    static const FunctionInfo functions[] = {
        {0, C<&IMonitorService::GetStateForMonitor>, "GetStateForMonitor"},
        {1, nullptr, "GetNetworkInfoForMonitor"},
        {2, nullptr, "GetIpv4AddressForMonitor"},
        {3, nullptr, "GetDisconnectReasonForMonitor"},
        {4, nullptr, "GetSecurityParameterForMonitor"},
        {5, nullptr, "GetNetworkConfigForMonitor"},
        {100, C<&IMonitorService::InitializeMonitor>, "InitializeMonitor"},
        {101, C<&IMonitorService::FinalizeMonitor>, "FinalizeMonitor"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

IMonitorService::~IMonitorService() = default;

Result IMonitorService::GetStateForMonitor(Out<State> out_state) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");
    *out_state = State::None;
    R_SUCCEED();
}

Result IMonitorService::InitializeMonitor() {
    LOG_INFO(Service_LDN, "called");
    R_SUCCEED();
}

Result IMonitorService::FinalizeMonitor() {
    LOG_INFO(Service_LDN, "called");
    R_SUCCEED();
}

} // namespace Service::LDN


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

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ldn/ldn_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::LDN {

class IMonitorService final : public ServiceFramework<IMonitorService> {
public:
    explicit IMonitorService(Core::System& system_);
    ~IMonitorService() override;

private:
    Result GetStateForMonitor(Out<State> out_state);
    Result InitializeMonitor();
    Result FinalizeMonitor();

    State state{State::None};
};

} // namespace Service::LDN


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

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn_types.h"
#include "core/hle/service/ldn/sf_monitor_service.h"

namespace Service::LDN {

ISfMonitorService::ISfMonitorService(Core::System& system_)
    : ServiceFramework{system_, "ISfMonitorService"} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&ISfMonitorService::Initialize>, "Initialize"},
            {288, C<&ISfMonitorService::GetGroupInfo>, "GetGroupInfo"},
            {320, nullptr, "GetLinkLevel"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

ISfMonitorService::~ISfMonitorService() = default;

Result ISfMonitorService::Initialize(Out<u32> out_value) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");

    *out_value = 0;
    R_SUCCEED();
}

Result ISfMonitorService::GetGroupInfo(
    OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");

    *out_group_info = GroupInfo{};
    R_SUCCEED();
}

} // namespace Service::LDN


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

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::LDN {
struct GroupInfo;

class ISfMonitorService final : public ServiceFramework<ISfMonitorService> {
public:
    explicit ISfMonitorService(Core::System& system_);
    ~ISfMonitorService() override;

private:
    Result Initialize(Out<u32> out_value);
    Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
};

} // namespace Service::LDN


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

#include "core/hle/service/ldn/sf_service.h"

namespace Service::LDN {

ISfService::ISfService(Core::System& system_) : ServiceFramework{system_, "ISfService"} {
    // clang-format off
    static const FunctionInfo functions[] = {
            {0, nullptr, "Initialize"},
            {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
            {264, nullptr, "GetNetworkInterfaceLastError"},
            {272, nullptr, "GetRole"},
            {280, nullptr, "GetAdvertiseData"},
            {288, nullptr, "GetGroupInfo"},
            {296, nullptr, "GetGroupInfo2"},
            {304, nullptr, "GetGroupOwner"},
            {312, nullptr, "GetIpConfig"},
            {320, nullptr, "GetLinkLevel"},
            {512, nullptr, "Scan"},
            {768, nullptr, "CreateGroup"},
            {776, nullptr, "DestroyGroup"},
            {784, nullptr, "SetAdvertiseData"},
            {1536, nullptr, "SendToOtherGroup"},
            {1544, nullptr, "RecvFromOtherGroup"},
            {1552, nullptr, "AddAcceptableGroupId"},
            {1560, nullptr, "ClearAcceptableGroupId"},
    };
    // clang-format on

    RegisterHandlers(functions);
}

ISfService::~ISfService() = default;

} // namespace Service::LDN


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

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::LDN {

class ISfService final : public ServiceFramework<ISfService> {
public:
    explicit ISfService(Core::System& system_);
    ~ISfService() override;
};

} // namespace Service::LDN


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

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn_types.h"
#include "core/hle/service/ldn/sf_service_monitor.h"

namespace Service::LDN {

ISfServiceMonitor::ISfServiceMonitor(Core::System& system_)
    : ServiceFramework{system_, "ISfServiceMonitor"} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&ISfServiceMonitor::Initialize>, "Initialize"},
            {256, nullptr, "AttachNetworkInterfaceStateChangeEvent"},
            {264, nullptr, "GetNetworkInterfaceLastError"},
            {272, nullptr, "GetRole"},
            {280, nullptr, "GetAdvertiseData"},
            {281, nullptr, "GetAdvertiseData2"},
            {288, C<&ISfServiceMonitor::GetGroupInfo>, "GetGroupInfo"},
            {296, nullptr, "GetGroupInfo2"},
            {304, nullptr, "GetGroupOwner"},
            {312, nullptr, "GetIpConfig"},
            {320, nullptr, "GetLinkLevel"},
            {328, nullptr, "AttachJoinEvent"},
            {336, nullptr, "GetMembers"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

ISfServiceMonitor::~ISfServiceMonitor() = default;

Result ISfServiceMonitor::Initialize(Out<u32> out_value) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");

    *out_value = 0;
    R_SUCCEED();
}

Result ISfServiceMonitor::GetGroupInfo(
    OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");

    *out_group_info = GroupInfo{};
    R_SUCCEED();
}

} // namespace Service::LDN


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

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::LDN {
struct GroupInfo;

class ISfServiceMonitor final : public ServiceFramework<ISfServiceMonitor> {
public:
    explicit ISfServiceMonitor(Core::System& system_);
    ~ISfServiceMonitor() override;

private:
    Result Initialize(Out<u32> out_value);
    Result GetGroupInfo(OutLargeData<GroupInfo, BufferAttr_HipcAutoSelect> out_group_info);
};

} // namespace Service::LDN


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

#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/system_local_communication_service.h"

namespace Service::LDN {

ISystemLocalCommunicationService::ISystemLocalCommunicationService(Core::System& system_)
    : ServiceFramework{system_, "ISystemLocalCommunicationService"} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "GetState"},
            {1, nullptr, "GetNetworkInfo"},
            {2, nullptr, "GetIpv4Address"},
            {3, nullptr, "GetDisconnectReason"},
            {4, nullptr, "GetSecurityParameter"},
            {5, nullptr, "GetNetworkConfig"},
            {100, nullptr, "AttachStateChangeEvent"},
            {101, nullptr, "GetNetworkInfoLatestUpdate"},
            {102, nullptr, "Scan"},
            {103, nullptr, "ScanPrivate"},
            {104, nullptr, "SetWirelessControllerRestriction"},
            {200, nullptr, "OpenAccessPoint"},
            {201, nullptr, "CloseAccessPoint"},
            {202, nullptr, "CreateNetwork"},
            {203, nullptr, "CreateNetworkPrivate"},
            {204, nullptr, "DestroyNetwork"},
            {205, nullptr, "Reject"},
            {206, nullptr, "SetAdvertiseData"},
            {207, nullptr, "SetStationAcceptPolicy"},
            {208, nullptr, "AddAcceptFilterEntry"},
            {209, nullptr, "ClearAcceptFilter"},
            {300, nullptr, "OpenStation"},
            {301, nullptr, "CloseStation"},
            {302, nullptr, "Connect"},
            {303, nullptr, "ConnectPrivate"},
            {304, nullptr, "Disconnect"},
            {400, nullptr, "InitializeSystem"},
            {401, nullptr, "FinalizeSystem"},
            {402, nullptr, "SetOperationMode"},
            {403, C<&ISystemLocalCommunicationService::InitializeSystem2>, "InitializeSystem2"},
        };
    // clang-format on

    RegisterHandlers(functions);
}

ISystemLocalCommunicationService::~ISystemLocalCommunicationService() = default;

Result ISystemLocalCommunicationService::InitializeSystem2() {
    LOG_WARNING(Service_LDN, "(STUBBED) called");
    R_SUCCEED();
}

} // namespace Service::LDN


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

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Service::LDN {

class ISystemLocalCommunicationService final
    : public ServiceFramework<ISystemLocalCommunicationService> {
public:
    explicit ISystemLocalCommunicationService(Core::System& system_);
    ~ISystemLocalCommunicationService() override;

private:
    Result InitializeSystem2();
};

} // namespace Service::LDN


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

#include <memory>

#include "core/core.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/ldn/ldn_results.h"
#include "core/hle/service/ldn/ldn_types.h"
#include "core/hle/service/ldn/user_local_communication_service.h"
#include "core/hle/service/server_manager.h"
#include "core/internal_network/network.h"
#include "core/internal_network/network_interface.h"
#include "network/network.h"

// This is defined by synchapi.h and conflicts with ServiceContext::CreateEvent
#undef CreateEvent

namespace Service::LDN {

IUserLocalCommunicationService::IUserLocalCommunicationService(Core::System& system_)
    : ServiceFramework{system_, "IUserLocalCommunicationService"},
      service_context{system, "IUserLocalCommunicationService"},
      room_network{system_.GetRoomNetwork()}, lan_discovery{room_network} {
    // clang-format off
        static const FunctionInfo functions[] = {
            {0, C<&IUserLocalCommunicationService::GetState>, "GetState"},
            {1, C<&IUserLocalCommunicationService::GetNetworkInfo>, "GetNetworkInfo"},
            {2, C<&IUserLocalCommunicationService::GetIpv4Address>, "GetIpv4Address"},
            {3, C<&IUserLocalCommunicationService::GetDisconnectReason>, "GetDisconnectReason"},
            {4, C<&IUserLocalCommunicationService::GetSecurityParameter>, "GetSecurityParameter"},
            {5, C<&IUserLocalCommunicationService::GetNetworkConfig>, "GetNetworkConfig"},
            {100, C<&IUserLocalCommunicationService::AttachStateChangeEvent>, "AttachStateChangeEvent"},
            {101, C<&IUserLocalCommunicationService::GetNetworkInfoLatestUpdate>, "GetNetworkInfoLatestUpdate"},
            {102, C<&IUserLocalCommunicationService::Scan>, "Scan"},
            {103, C<&IUserLocalCommunicationService::ScanPrivate>, "ScanPrivate"},
            {104, C<&IUserLocalCommunicationService::SetWirelessControllerRestriction>, "SetWirelessControllerRestriction"},
            {200, C<&IUserLocalCommunicationService::OpenAccessPoint>, "OpenAccessPoint"},
            {201, C<&IUserLocalCommunicationService::CloseAccessPoint>, "CloseAccessPoint"},
            {202, C<&IUserLocalCommunicationService::CreateNetwork>, "CreateNetwork"},
            {203, C<&IUserLocalCommunicationService::CreateNetworkPrivate>, "CreateNetworkPrivate"},
            {204, C<&IUserLocalCommunicationService::DestroyNetwork>, "DestroyNetwork"},
            {205, nullptr, "Reject"},
            {206, C<&IUserLocalCommunicationService::SetAdvertiseData>, "SetAdvertiseData"},
            {207, C<&IUserLocalCommunicationService::SetStationAcceptPolicy>, "SetStationAcceptPolicy"},
            {208, C<&IUserLocalCommunicationService::AddAcceptFilterEntry>, "AddAcceptFilterEntry"},
            {209, nullptr, "ClearAcceptFilter"},
            {300, C<&IUserLocalCommunicationService::OpenStation>, "OpenStation"},
            {301, C<&IUserLocalCommunicationService::CloseStation>, "CloseStation"},
            {302, C<&IUserLocalCommunicationService::Connect>, "Connect"},
            {303, nullptr, "ConnectPrivate"},
            {304, C<&IUserLocalCommunicationService::Disconnect>, "Disconnect"},
            {400, C<&IUserLocalCommunicationService::Initialize>, "Initialize"},
            {401, C<&IUserLocalCommunicationService::Finalize>, "Finalize"},
            {402, C<&IUserLocalCommunicationService::Initialize2>, "Initialize2"},
        };
    // clang-format on

    RegisterHandlers(functions);

    state_change_event =
        service_context.CreateEvent("IUserLocalCommunicationService:StateChangeEvent");
}

IUserLocalCommunicationService::~IUserLocalCommunicationService() {
    if (is_initialized) {
        if (auto room_member = room_network.GetRoomMember().lock()) {
            room_member->Unbind(ldn_packet_received);
        }
    }

    service_context.CloseEvent(state_change_event);
}

Result IUserLocalCommunicationService::GetState(Out<State> out_state) {
    *out_state = State::Error;

    if (is_initialized) {
        *out_state = lan_discovery.GetState();
    }

    LOG_INFO(Service_LDN, "called, state={}", *out_state);

    R_SUCCEED();
}

Result IUserLocalCommunicationService::GetNetworkInfo(
    OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info) {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info));
}

Result IUserLocalCommunicationService::GetIpv4Address(Out<Ipv4Address> out_current_address,
                                                      Out<Ipv4Address> out_subnet_mask) {
    LOG_INFO(Service_LDN, "called");
    const auto network_interface = Network::GetSelectedNetworkInterface();

    R_UNLESS(network_interface.has_value(), ResultNoIpAddress);

    *out_current_address = {Network::TranslateIPv4(network_interface->ip_address)};
    *out_subnet_mask = {Network::TranslateIPv4(network_interface->subnet_mask)};

    // When we're connected to a room, spoof the hosts IP address
    if (auto room_member = room_network.GetRoomMember().lock()) {
        if (room_member->IsConnected()) {
            *out_current_address = room_member->GetFakeIpAddress();
        }
    }

    std::reverse(std::begin(*out_current_address), std::end(*out_current_address)); // ntohl
    std::reverse(std::begin(*out_subnet_mask), std::end(*out_subnet_mask));         // ntohl
    R_SUCCEED();
}

Result IUserLocalCommunicationService::GetDisconnectReason(
    Out<DisconnectReason> out_disconnect_reason) {
    LOG_INFO(Service_LDN, "called");

    *out_disconnect_reason = lan_discovery.GetDisconnectReason();
    R_SUCCEED();
}

Result IUserLocalCommunicationService::GetSecurityParameter(
    Out<SecurityParameter> out_security_parameter) {
    LOG_INFO(Service_LDN, "called");

    NetworkInfo info{};
    R_TRY(lan_discovery.GetNetworkInfo(info));

    out_security_parameter->session_id = info.network_id.session_id;
    std::memcpy(out_security_parameter->data.data(), info.ldn.security_parameter.data(),
                sizeof(SecurityParameter::data));
    R_SUCCEED();
}

Result IUserLocalCommunicationService::GetNetworkConfig(Out<NetworkConfig> out_network_config) {
    LOG_INFO(Service_LDN, "called");

    NetworkInfo info{};
    R_TRY(lan_discovery.GetNetworkInfo(info));

    out_network_config->intent_id = info.network_id.intent_id;
    out_network_config->channel = info.common.channel;
    out_network_config->node_count_max = info.ldn.node_count_max;
    out_network_config->local_communication_version = info.ldn.nodes[0].local_communication_version;
    R_SUCCEED();
}

Result IUserLocalCommunicationService::AttachStateChangeEvent(
    OutCopyHandle<Kernel::KReadableEvent> out_event) {
    LOG_INFO(Service_LDN, "called");

    *out_event = &state_change_event->GetReadableEvent();
    R_SUCCEED();
}

Result IUserLocalCommunicationService::GetNetworkInfoLatestUpdate(
    OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info,
    OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update) {
    LOG_INFO(Service_LDN, "called");

    R_UNLESS(!out_node_latest_update.empty(), ResultBadInput);

    R_RETURN(lan_discovery.GetNetworkInfo(*out_network_info, out_node_latest_update));
}

Result IUserLocalCommunicationService::Scan(
    Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
    OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) {
    LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}",
             channel, scan_filter.flag, scan_filter.network_type);

    R_UNLESS(!out_network_info.empty(), ResultBadInput);
    R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter));
}

Result IUserLocalCommunicationService::ScanPrivate(
    Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
    OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info) {
    LOG_INFO(Service_LDN, "called, channel={}, filter_scan_flag={}, filter_network_type={}",
             channel, scan_filter.flag, scan_filter.network_type);

    R_UNLESS(out_network_info.empty(), ResultBadInput);
    R_RETURN(lan_discovery.Scan(out_network_info, *network_count, scan_filter));
}

Result IUserLocalCommunicationService::SetWirelessControllerRestriction(
    WirelessControllerRestriction wireless_restriction) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");
    R_SUCCEED();
}

Result IUserLocalCommunicationService::OpenAccessPoint() {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.OpenAccessPoint());
}

Result IUserLocalCommunicationService::CloseAccessPoint() {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.CloseAccessPoint());
}

Result IUserLocalCommunicationService::CreateNetwork(const CreateNetworkConfig& create_config) {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config,
                                         create_config.network_config));
}

Result IUserLocalCommunicationService::CreateNetworkPrivate(
    const CreateNetworkConfigPrivate& create_config,
    InArray<AddressEntry, BufferAttr_HipcPointer> address_list) {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.CreateNetwork(create_config.security_config, create_config.user_config,
                                         create_config.network_config));
}

Result IUserLocalCommunicationService::DestroyNetwork() {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.DestroyNetwork());
}

Result IUserLocalCommunicationService::SetAdvertiseData(
    InBuffer<BufferAttr_HipcAutoSelect> buffer_data) {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.SetAdvertiseData(buffer_data));
}

Result IUserLocalCommunicationService::SetStationAcceptPolicy(AcceptPolicy accept_policy) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");
    R_SUCCEED();
}

Result IUserLocalCommunicationService::AddAcceptFilterEntry(MacAddress mac_address) {
    LOG_WARNING(Service_LDN, "(STUBBED) called");
    R_SUCCEED();
}

Result IUserLocalCommunicationService::OpenStation() {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.OpenStation());
}

Result IUserLocalCommunicationService::CloseStation() {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.CloseStation());
}

Result IUserLocalCommunicationService::Connect(
    const ConnectNetworkData& connect_data,
    InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info) {
    LOG_INFO(Service_LDN,
             "called, passphrase_size={}, security_mode={}, "
             "local_communication_version={}",
             connect_data.security_config.passphrase_size,
             connect_data.security_config.security_mode, connect_data.local_communication_version);

    R_RETURN(lan_discovery.Connect(*network_info, connect_data.user_config,
                                   static_cast<u16>(connect_data.local_communication_version)));
}

Result IUserLocalCommunicationService::Disconnect() {
    LOG_INFO(Service_LDN, "called");

    R_RETURN(lan_discovery.Disconnect());
}

Result IUserLocalCommunicationService::Initialize(ClientProcessId aruid) {
    LOG_INFO(Service_LDN, "called, process_id={}", aruid.pid);

    const auto network_interface = Network::GetSelectedNetworkInterface();
    R_UNLESS(network_interface, ResultAirplaneModeEnabled);

    if (auto room_member = room_network.GetRoomMember().lock()) {
        ldn_packet_received = room_member->BindOnLdnPacketReceived(
            [this](const Network::LDNPacket& packet) { OnLDNPacketReceived(packet); });
    } else {
        LOG_ERROR(Service_LDN, "Couldn't bind callback!");
        R_RETURN(ResultAirplaneModeEnabled);
    }

    lan_discovery.Initialize([&]() { OnEventFired(); });
    is_initialized = true;
    R_SUCCEED();
}

Result IUserLocalCommunicationService::Finalize() {
    LOG_INFO(Service_LDN, "called");
    if (auto room_member = room_network.GetRoomMember().lock()) {
        room_member->Unbind(ldn_packet_received);
    }

    is_initialized = false;

    R_RETURN(lan_discovery.Finalize());
}

Result IUserLocalCommunicationService::Initialize2(u32 version, ClientProcessId process_id) {
    LOG_INFO(Service_LDN, "called, version={}, process_id={}", version, process_id.pid);
    R_RETURN(Initialize(process_id));
}

void IUserLocalCommunicationService::OnLDNPacketReceived(const Network::LDNPacket& packet) {
    lan_discovery.ReceivePacket(packet);
}

void IUserLocalCommunicationService::OnEventFired() {
    state_change_event->Signal();
}

} // namespace Service::LDN


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

#pragma once

#include "core/hle/service/cmif_types.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/ldn/lan_discovery.h"
#include "core/hle/service/ldn/ldn_types.h"
#include "core/hle/service/service.h"

namespace Core {
class System;
}

namespace Network {
class RoomNetwork;
}

namespace Service::LDN {

class IUserLocalCommunicationService final
    : public ServiceFramework<IUserLocalCommunicationService> {
public:
    explicit IUserLocalCommunicationService(Core::System& system_);
    ~IUserLocalCommunicationService() override;

private:
    Result GetState(Out<State> out_state);

    Result GetNetworkInfo(OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info);

    Result GetIpv4Address(Out<Ipv4Address> out_current_address, Out<Ipv4Address> out_subnet_mask);

    Result GetDisconnectReason(Out<DisconnectReason> out_disconnect_reason);

    Result GetSecurityParameter(Out<SecurityParameter> out_security_parameter);

    Result GetNetworkConfig(Out<NetworkConfig> out_network_config);

    Result AttachStateChangeEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);

    Result GetNetworkInfoLatestUpdate(
        OutLargeData<NetworkInfo, BufferAttr_HipcPointer> out_network_info,
        OutArray<NodeLatestUpdate, BufferAttr_HipcPointer> out_node_latest_update);

    Result Scan(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
                OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info);

    Result ScanPrivate(Out<s16> network_count, WifiChannel channel, const ScanFilter& scan_filter,
                       OutArray<NetworkInfo, BufferAttr_HipcAutoSelect> out_network_info);

    Result SetWirelessControllerRestriction(WirelessControllerRestriction wireless_restriction);

    Result OpenAccessPoint();

    Result CloseAccessPoint();

    Result CreateNetwork(const CreateNetworkConfig& create_network_Config);

    Result CreateNetworkPrivate(const CreateNetworkConfigPrivate& create_network_Config,
                                InArray<AddressEntry, BufferAttr_HipcPointer> address_list);

    Result DestroyNetwork();

    Result SetAdvertiseData(InBuffer<BufferAttr_HipcAutoSelect> buffer_data);

    Result SetStationAcceptPolicy(AcceptPolicy accept_policy);

    Result AddAcceptFilterEntry(MacAddress mac_address);

    Result OpenStation();

    Result CloseStation();

    Result Connect(const ConnectNetworkData& connect_data,
                   InLargeData<NetworkInfo, BufferAttr_HipcPointer> network_info);

    Result Disconnect();

    Result Initialize(ClientProcessId aruid);

    Result Finalize();

    Result Initialize2(u32 version, ClientProcessId aruid);

private:
    /// Callback to parse and handle a received LDN packet.
    void OnLDNPacketReceived(const Network::LDNPacket& packet);
    void OnEventFired();

    KernelHelpers::ServiceContext service_context;
    Kernel::KEvent* state_change_event;
    Network::RoomNetwork& room_network;
    LANDiscovery lan_discovery;

    // Callback identifier for the OnLDNPacketReceived event.
    Network::RoomMember::CallbackHandle<Network::LDNPacket> ldn_packet_received;

    bool is_initialized{};
};

} // namespace Service::LDN


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

#include "core/hle/service/ldr/ldr.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::LDR {

class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
    explicit DebugMonitor(Core::System& system_) : ServiceFramework{system_, "ldr:dmnt"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "SetProgramArgument"},
            {1, nullptr, "FlushArguments"},
            {2, nullptr, "GetProcessModuleInfo"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class ProcessManager final : public ServiceFramework<ProcessManager> {
public:
    explicit ProcessManager(Core::System& system_) : ServiceFramework{system_, "ldr:pm"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "CreateProcess"},
            {1, nullptr, "GetProgramInfo"},
            {2, nullptr, "PinProgram"},
            {3, nullptr, "UnpinProgram"},
            {4, nullptr, "SetEnabledProgramVerification"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

class Shell final : public ServiceFramework<Shell> {
public:
    explicit Shell(Core::System& system_) : ServiceFramework{system_, "ldr:shel"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, nullptr, "SetProgramArgument"},
            {1, nullptr, "FlushArguments"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("ldr:dmnt", std::make_shared<DebugMonitor>(system));
    server_manager->RegisterNamedService("ldr:pm", std::make_shared<ProcessManager>(system));
    server_manager->RegisterNamedService("ldr:shel", std::make_shared<Shell>(system));

    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::LDR


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

#pragma once

namespace Core {
class System;
}

namespace Service::LDR {

void LoopProcess(Core::System& system);

} // namespace Service::LDR


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

#include <string>

#include <optional>
#include <unordered_map>
#include <boost/container_hash/hash.hpp>
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::LM {
enum class LogSeverity : u8 {
    Trace = 0,
    Info = 1,
    Warning = 2,
    Error = 3,
    Fatal = 4,
};

// To keep flags out of hashing as well as the payload size
struct LogPacketHeaderEntry {
    u64_le pid{};
    u64_le tid{};
    LogSeverity severity{};
    u8 verbosity{};

    auto operator<=>(const LogPacketHeaderEntry&) const = default;
};
} // namespace Service::LM

namespace std {
template <>
struct hash<Service::LM::LogPacketHeaderEntry> {
    std::size_t operator()(const Service::LM::LogPacketHeaderEntry& k) const noexcept {
        std::size_t seed{};
        boost::hash_combine(seed, k.pid);
        boost::hash_combine(seed, k.tid);
        boost::hash_combine(seed, k.severity);
        boost::hash_combine(seed, k.verbosity);
        return seed;
    }
};
} // namespace std

namespace Service::LM {
namespace {
std::string_view NameOf(LogSeverity severity) {
    switch (severity) {
    case LogSeverity::Trace:
        return "TRACE";
    case LogSeverity::Info:
        return "INFO";
    case LogSeverity::Warning:
        return "WARNING";
    case LogSeverity::Error:
        return "ERROR";
    case LogSeverity::Fatal:
        return "FATAL";
    default:
        return "UNKNOWN";
    }
}
} // Anonymous namespace

enum class LogDestination : u32 {
    TargetManager = 1 << 0,
    Uart = 1 << 1,
    UartSleep = 1 << 2,
    All = 0xffff,
};
DECLARE_ENUM_FLAG_OPERATORS(LogDestination);

enum class LogPacketFlags : u8 {
    Head = 1 << 0,
    Tail = 1 << 1,
    LittleEndian = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(LogPacketFlags);

class ILogger final : public ServiceFramework<ILogger> {
public:
    explicit ILogger(Core::System& system_) : ServiceFramework{system_, "ILogger"} {
        static const FunctionInfo functions[] = {
            {0, &ILogger::Log, "Log"},
            {1, &ILogger::SetDestination, "SetDestination"},
        };
        RegisterHandlers(functions);
    }

private:
    void Log(HLERequestContext& ctx) {
        std::size_t offset{};
        const auto data = ctx.ReadBuffer();

        // This function only succeeds - Get that out of the way
        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);

        if (data.size() < sizeof(LogPacketHeader)) {
            LOG_ERROR(Service_LM, "Data size is too small for header! size={}", data.size());
            return;
        }

        LogPacketHeader header{};
        std::memcpy(&header, data.data(), sizeof(LogPacketHeader));
        offset += sizeof(LogPacketHeader);

        const LogPacketHeaderEntry entry{
            .pid = header.pid,
            .tid = header.tid,
            .severity = header.severity,
            .verbosity = header.verbosity,
        };

        if (True(header.flags & LogPacketFlags::Head)) {
            std::vector<u8> tmp(data.size() - sizeof(LogPacketHeader));
            std::memcpy(tmp.data(), data.data() + offset, tmp.size());
            entries.insert_or_assign(entry, std::move(tmp));
        } else {
            const auto entry_iter = entries.find(entry);

            // Append to existing entry
            if (entry_iter == entries.cend()) {
                LOG_ERROR(Service_LM, "Log entry does not exist!");
                return;
            }

            auto& existing_entry = entry_iter->second;
            const auto base = existing_entry.size();
            existing_entry.resize(base + (data.size() - sizeof(LogPacketHeader)));
            std::memcpy(existing_entry.data() + base, data.data() + offset,
                        (data.size() - sizeof(LogPacketHeader)));
        }

        if (True(header.flags & LogPacketFlags::Tail)) {
            auto it = entries.find(entry);
            if (it == entries.end()) {
                LOG_ERROR(Service_LM, "Log entry does not exist!");
                return;
            }
            ParseLog(it->first, it->second);
            entries.erase(it);
        }
    }

    void SetDestination(HLERequestContext& ctx) {
        IPC::RequestParser rp{ctx};
        const auto log_destination = rp.PopEnum<LogDestination>();

        LOG_DEBUG(Service_LM, "called, destination={}", DestinationToString(log_destination));
        destination = log_destination;

        IPC::ResponseBuilder rb{ctx, 2};
        rb.Push(ResultSuccess);
    }

    u64 ReadLeb128(const std::vector<u8>& data, std::size_t& offset) {
        u64 result{};
        u32 shift{};

        for (std::size_t i = 0; i < sizeof(u64); i++) {
            const auto v = data[offset];
            result |= (static_cast<u64>(v & 0x7f) << shift);
            shift += 7;
            offset++;
            if (offset >= data.size() || ((v & 0x80) == 0)) {
                break;
            }
        }
        return result;
    }

    std::optional<std::string> ReadString(const std::vector<u8>& data, std::size_t& offset,
                                          std::size_t length) {
        if (length == 0) {
            return std::nullopt;
        }
        const auto length_to_read = std::min(length, data.size() - offset);

        std::string output(length_to_read, '\0');
        std::memcpy(output.data(), data.data() + offset, length_to_read);
        offset += length_to_read;
        return output;
    }

    u32_le ReadAsU32(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
        ASSERT(length == sizeof(u32));
        u32_le output{};
        std::memcpy(&output, data.data() + offset, sizeof(u32));
        offset += length;
        return output;
    }

    u64_le ReadAsU64(const std::vector<u8>& data, std::size_t& offset, std::size_t length) {
        ASSERT(length == sizeof(u64));
        u64_le output{};
        std::memcpy(&output, data.data() + offset, sizeof(u64));
        offset += length;
        return output;
    }

    void ParseLog(const LogPacketHeaderEntry entry, const std::vector<u8>& log_data) {
        // Possible entries
        std::optional<std::string> text_log;
        std::optional<u32> line_number;
        std::optional<std::string> file_name;
        std::optional<std::string> function_name;
        std::optional<std::string> module_name;
        std::optional<std::string> thread_name;
        std::optional<u64> log_pack_drop_count;
        std::optional<s64> user_system_clock;
        std::optional<std::string> process_name;

        std::size_t offset{};
        while (offset < log_data.size()) {
            const auto key = static_cast<LogDataChunkKey>(ReadLeb128(log_data, offset));
            const auto chunk_size = ReadLeb128(log_data, offset);

            switch (key) {
            case LogDataChunkKey::LogSessionBegin:
            case LogDataChunkKey::LogSessionEnd:
                break;
            case LogDataChunkKey::TextLog:
                text_log = ReadString(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::LineNumber:
                line_number = ReadAsU32(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::FileName:
                file_name = ReadString(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::FunctionName:
                function_name = ReadString(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::ModuleName:
                module_name = ReadString(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::ThreadName:
                thread_name = ReadString(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::LogPacketDropCount:
                log_pack_drop_count = ReadAsU64(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::UserSystemClock:
                user_system_clock = ReadAsU64(log_data, offset, chunk_size);
                break;
            case LogDataChunkKey::ProcessName:
                process_name = ReadString(log_data, offset, chunk_size);
                break;
            }
        }

        std::string output_log{};
        if (process_name) {
            output_log += fmt::format("Process: {}\n", *process_name);
        }
        if (module_name) {
            output_log += fmt::format("Module: {}\n", *module_name);
        }
        if (file_name) {
            output_log += fmt::format("File: {}\n", *file_name);
        }
        if (function_name) {
            output_log += fmt::format("Function: {}\n", *function_name);
        }
        if (line_number && *line_number != 0) {
            output_log += fmt::format("Line: {}\n", *line_number);
        }
        output_log += fmt::format("ProcessID: {:X}\n", entry.pid);
        output_log += fmt::format("ThreadID: {:X}\n", entry.tid);

        if (text_log) {
            output_log += fmt::format("Log Text: {}\n", *text_log);
        }
        LOG_DEBUG(Service_LM, "LogManager {} ({}):\n{}", NameOf(entry.severity),
                  DestinationToString(destination), output_log);
    }

    static std::string DestinationToString(LogDestination destination) {
        if (True(destination & LogDestination::All)) {
            return "TargetManager | Uart | UartSleep";
        }
        std::string output{};
        if (True(destination & LogDestination::TargetManager)) {
            output += "| TargetManager";
        }
        if (True(destination & LogDestination::Uart)) {
            output += "| Uart";
        }
        if (True(destination & LogDestination::UartSleep)) {
            output += "| UartSleep";
        }
        if (output.length() > 0) {
            return output.substr(2);
        }
        return "No Destination";
    }

    enum class LogDataChunkKey : u32 {
        LogSessionBegin = 0,
        LogSessionEnd = 1,
        TextLog = 2,
        LineNumber = 3,
        FileName = 4,
        FunctionName = 5,
        ModuleName = 6,
        ThreadName = 7,
        LogPacketDropCount = 8,
        UserSystemClock = 9,
        ProcessName = 10,
    };

    struct LogPacketHeader {
        u64_le pid{};
        u64_le tid{};
        LogPacketFlags flags{};
        INSERT_PADDING_BYTES(1);
        LogSeverity severity{};
        u8 verbosity{};
        u32_le payload_size{};
    };
    static_assert(sizeof(LogPacketHeader) == 0x18, "LogPacketHeader is an invalid size");

    std::unordered_map<LogPacketHeaderEntry, std::vector<u8>> entries{};
    LogDestination destination{LogDestination::All};
};

class LM final : public ServiceFramework<LM> {
public:
    explicit LM(Core::System& system_) : ServiceFramework{system_, "lm"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {0, &LM::OpenLogger, "OpenLogger"},
        };
        // clang-format on

        RegisterHandlers(functions);
    }

private:
    void OpenLogger(HLERequestContext& ctx) {
        LOG_DEBUG(Service_LM, "called");

        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
        rb.Push(ResultSuccess);
        rb.PushIpcInterface<ILogger>(system);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("lm", std::make_shared<LM>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::LM


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

#pragma once

namespace Core {
class System;
}

namespace Service::LM {

void LoopProcess(Core::System& system);

} // namespace Service::LM


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

#include <memory>

#include "core/hle/service/mig/mig.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/service.h"

namespace Service::Migration {

class MIG_USR final : public ServiceFramework<MIG_USR> {
public:
    explicit MIG_USR(Core::System& system_) : ServiceFramework{system_, "mig:usr"} {
        // clang-format off
        static const FunctionInfo functions[] = {
            {10, nullptr, "TryGetLastMigrationInfo"},
            {100, nullptr, "CreateServer"},
            {101, nullptr, "ResumeServer"},
            {200, nullptr, "CreateClient"},
            {201, nullptr, "ResumeClient"},
            {1001, nullptr, "Unknown1001"},
            {1010, nullptr, "Unknown1010"},
            {1100, nullptr, "Unknown1100"},
            {1101, nullptr, "Unknown1101"},
            {1200, nullptr, "Unknown1200"},
            {1201, nullptr, "Unknown1201"}
        };
        // clang-format on

        RegisterHandlers(functions);
    }
};

void LoopProcess(Core::System& system) {
    auto server_manager = std::make_unique<ServerManager>(system);

    server_manager->RegisterNamedService("mig:user", std::make_shared<MIG_USR>(system));
    ServerManager::RunServer(std::move(server_manager));
}

} // namespace Service::Migration


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

#pragma once

namespace Core {
class System;
}

namespace Service::Migration {

void LoopProcess(Core::System& system);

} // namespace Service::Migration