BBA: Added BuiltIn device that allow BBA emulation without the need of a TapDevice Configuration include a dns server setting

This commit is contained in:
schthack 2022-05-22 12:21:28 -04:00
parent d625c612c4
commit 01ada3850f
16 changed files with 1063 additions and 45 deletions

View File

@ -5,6 +5,7 @@
#include <algorithm>
#include <string_view>
#include <vector>
#ifndef _WIN32
#include <netinet/in.h>
@ -141,6 +142,18 @@ TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, co
checksum = htons(static_cast<u16>(tcp_checksum));
}
TCPHeader::TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, u32 ack, u16 flags)
{
source_port = from.sin_port;
destination_port = to.sin_port;
sequence_number = htonl(seq);
acknowledgement_number = htonl(ack);
properties = 0x50 | flags;
window_size = 0x7c;
checksum = 0;
}
u16 TCPHeader::Size() const
{
return static_cast<u16>(SIZE);
@ -170,6 +183,59 @@ u8 UDPHeader::IPProto() const
return static_cast<u8>(IPPROTO_UDP);
}
ARPHeader::ARPHeader() = default;
ARPHeader::ARPHeader(u32 from_ip, MACAddress from_mac, u32 to_ip, MACAddress to_mac)
{
hardware_type = htons(BBA_HARDWARE_TYPE);
protocol_type = IPV4_HEADER_TYPE;
hardware_size = MAC_ADDRESS_SIZE;
protocol_size = IPV4_ADDR_LEN;
opcode = 0x200;
sender_ip = from_ip;
target_ip = to_ip;
targer_address = to_mac;
sender_address = from_mac;
}
u16 ARPHeader::Size() const
{
return static_cast<u16>(SIZE);
}
DHCPBody::DHCPBody() = default;
DHCPBody::DHCPBody(u32 transaction, MACAddress client_address, u32 new_ip, u32 serv_ip)
{
transaction_id = transaction;
message_type = DHCPConst::MESSAGE_REPLY;
hardware_type = BBA_HARDWARE_TYPE;
hardware_addr = MAC_ADDRESS_SIZE;
client_mac = client_address;
your_ip = new_ip;
server_ip = serv_ip;
}
// Add an option to the DHCP Body
bool DHCPBody::AddDHCPOption(u8 size, u8 fnc, const std::vector<u8>& params)
{
int i = 0;
while (options[i] != 0)
{
i += options[i + 1] + 2;
if (i >= std::size(options))
{
return false;
}
}
options[i++] = fnc;
options[i++] = size;
for (auto val : params)
options[i++] = val;
return true;
}
// Compute the network checksum with a 32-bit accumulator using the
// "Normal" order, see RFC 1071 for more details.
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
@ -187,6 +253,20 @@ u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
return ~static_cast<u16>(checksum);
}
// Compute the TCP network checksum with a fake header
u16 ComputeTCPNetworkChecksum(const sockaddr_in& from, const sockaddr_in& to, const void* data,
u16 length, u8 protocol)
{
// Compute the TCP checksum with its pseudo header
const u32 source_addr = ntohl(from.sin_addr.s_addr);
const u32 destination_addr = ntohl(to.sin_addr.s_addr);
const u32 initial_value = (source_addr >> 16) + (source_addr & 0xFFFF) +
(destination_addr >> 16) + (destination_addr & 0xFFFF) + protocol +
length;
const u32 tcp_checksum = ComputeNetworkChecksum(data, length, initial_value);
return htons(static_cast<u16>(tcp_checksum));
}
NetworkErrorState SaveNetworkErrorState()
{
return {

View File

@ -7,6 +7,7 @@
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "Common/CommonTypes.h"
@ -22,7 +23,15 @@ enum class MACConsumer
enum
{
MAC_ADDRESS_SIZE = 6
BBA_HARDWARE_TYPE = 1,
MAC_ADDRESS_SIZE = 6,
IPV4_HEADER_TYPE = 8
};
enum DHCPConst
{
MESSAGE_QUERY = 1,
MESSAGE_REPLY = 2
};
using MACAddress = std::array<u8, MAC_ADDRESS_SIZE>;
@ -67,6 +76,7 @@ struct TCPHeader
{
TCPHeader();
TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, const u8* data, u16 length);
TCPHeader(const sockaddr_in& from, const sockaddr_in& to, u32 seq, u32 ack, u16 flags);
u16 Size() const;
u8 IPProto() const;
@ -99,6 +109,54 @@ struct UDPHeader
};
static_assert(sizeof(UDPHeader) == UDPHeader::SIZE);
#pragma pack(push, 1)
struct ARPHeader
{
ARPHeader();
ARPHeader(u32 from_ip, MACAddress from_mac, u32 to_ip, MACAddress to_mac);
u16 Size() const;
static constexpr std::size_t SIZE = 28;
u16 hardware_type = 0;
u16 protocol_type = 0;
u8 hardware_size = 0;
u8 protocol_size = 0;
u16 opcode = 0;
MACAddress sender_address{};
u32 sender_ip = 0;
MACAddress targer_address{};
u32 target_ip = 0;
};
static_assert(sizeof(ARPHeader) == ARPHeader::SIZE);
#pragma pack(pop)
struct DHCPBody
{
DHCPBody();
DHCPBody(u32 transaction, MACAddress client_address, u32 new_ip, u32 serv_ip);
bool AddDHCPOption(u8 size, u8 fnc, const std::vector<u8>& params);
static constexpr std::size_t SIZE = 540;
u8 message_type = 0;
u8 hardware_type = 0;
u8 hardware_addr = 0;
u8 hops = 0;
u32 transaction_id = 0;
u16 secondes = 0;
u16 boot_flag = 0;
u32 client_ip = 0;
u32 your_ip = 0;
u32 server_ip = 0;
u32 relay_ip = 0;
MACAddress client_mac{};
unsigned char padding[10]{};
unsigned char hostname[0x40]{};
unsigned char boot_file[0x80]{};
u32 magic_cookie = 0x63538263;
u8 options[300]{};
};
static_assert(sizeof(DHCPBody) == DHCPBody::SIZE);
struct NetworkErrorState
{
int error;
@ -111,6 +169,8 @@ MACAddress GenerateMacAddress(MACConsumer type);
std::string MacAddressToString(const MACAddress& mac);
std::optional<MACAddress> StringToMacAddress(std::string_view mac_string);
u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value = 0);
u16 ComputeTCPNetworkChecksum(const sockaddr_in& from, const sockaddr_in& to, const void* data,
u16 length, u8 protocol);
NetworkErrorState SaveNetworkErrorState();
void RestoreNetworkErrorState(const NetworkErrorState& state);
} // namespace Common

View File

@ -647,6 +647,8 @@ if(WIN32)
HW/EXI/BBA/TAP_Win32.cpp
HW/EXI/BBA/TAP_Win32.h
HW/EXI/BBA/XLINK_KAI_BBA.cpp
HW/EXI/BBA/BuiltIn.cpp
HW/EXI/BBA/BuiltIn.h
HW/WiimoteReal/IOWin.cpp
HW/WiimoteReal/IOWin.h
)
@ -662,12 +664,16 @@ elseif(APPLE)
HW/EXI/BBA/TAP_Apple.cpp
HW/EXI/BBA/TAPServer_Apple.cpp
HW/EXI/BBA/XLINK_KAI_BBA.cpp
HW/EXI/BBA/BuiltIn.cpp
HW/EXI/BBA/BuiltIn.h
)
target_link_libraries(core PUBLIC ${IOB_LIBRARY})
elseif(UNIX)
target_sources(core PRIVATE
HW/EXI/BBA/TAP_Unix.cpp
HW/EXI/BBA/XLINK_KAI_BBA.cpp
HW/EXI/BBA/BuiltIn.cpp
HW/EXI/BBA/BuiltIn.h
)
if(ANDROID)
target_sources(core PRIVATE
@ -717,4 +723,4 @@ endif()
if(MSVC)
# Add precompiled header
target_link_libraries(core PRIVATE use_pch)
endif()
endif()

View File

@ -116,6 +116,11 @@ const Info<std::string> MAIN_BBA_MAC{{System::Main, "Core", "BBA_MAC"}, ""};
const Info<std::string> MAIN_BBA_XLINK_IP{{System::Main, "Core", "BBA_XLINK_IP"}, "127.0.0.1"};
const Info<bool> MAIN_BBA_XLINK_CHAT_OSD{{System::Main, "Core", "BBA_XLINK_CHAT_OSD"}, true};
// Schthack PSO Server - https://schtserv.com/
const Info<std::string> MAIN_BBA_BUILTIN_DNS{{System::Main, "Core", "BBA_BUILTIN_DNS"},
"149.56.167.128"};
const Info<std::string> MAIN_BBA_BUILTIN_IP{{System::Main, "Core", "BBA_BUILTIN_IP"}, ""};
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel)
{
static const std::array<const Info<SerialInterface::SIDevices>, 4> infos{

View File

@ -86,6 +86,8 @@ const Info<ExpansionInterface::EXIDeviceType>& GetInfoForEXIDevice(ExpansionInte
extern const Info<std::string> MAIN_BBA_MAC;
extern const Info<std::string> MAIN_BBA_XLINK_IP;
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
extern const Info<std::string> MAIN_BBA_BUILTIN_DNS;
extern const Info<std::string> MAIN_BBA_BUILTIN_IP;
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
const Info<bool>& GetInfoForAdapterRumble(int channel);
const Info<bool>& GetInfoForSimulateKonga(int channel);

View File

@ -81,6 +81,8 @@ bool IsSettingSaveable(const Config::Location& config_location)
&Config::MAIN_AGP_CART_B_PATH.GetLocation(),
&Config::MAIN_BBA_MAC.GetLocation(),
&Config::MAIN_BBA_XLINK_IP.GetLocation(),
&Config::MAIN_BBA_BUILTIN_DNS.GetLocation(),
&Config::MAIN_BBA_BUILTIN_IP.GetLocation(),
&Config::MAIN_BBA_XLINK_CHAT_OSD.GetLocation(),
&Config::MAIN_OVERRIDE_REGION_SETTINGS.GetLocation(),
&Config::MAIN_CUSTOM_RTC_ENABLE.GetLocation(),

View File

@ -0,0 +1,700 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SFML/Network.hpp>
#include "Common/Logging/Log.h"
#include "Core/HW/EXI/BBA/BuiltIn.h"
#include "Core/HW/EXI/EXI_Device.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
// #define BBA_TRACK_PAGE_PTRS
namespace ExpansionInterface
{
u64 GetTickCountStd()
{
using namespace std::chrono;
return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}
void SetHardwareInfo(u8* data, Common::MACAddress dest, Common::MACAddress src)
{
Common::EthernetHeader* hwpart = (Common::EthernetHeader*)data;
*hwpart = Common::EthernetHeader(IP_PROTOCOL);
hwpart->destination = dest;
hwpart->source = src;
}
std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::TCPHeader*>
getTcpHeaders(u8* data, Common::MACAddress dest, Common::MACAddress src)
{
SetHardwareInfo(data, dest, src);
return std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::TCPHeader*>(
(Common::EthernetHeader*)data, (Common::IPv4Header*)&data[14],
(Common::TCPHeader*)&data[0x22]);
}
std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::UDPHeader*>
getUdpHeaders(u8* data, Common::MACAddress dest, Common::MACAddress src)
{
SetHardwareInfo(data, dest, src);
return std::tuple<Common::EthernetHeader*, Common::IPv4Header*, Common::UDPHeader*>(
(Common::EthernetHeader*)data, (Common::IPv4Header*)&data[14],
(Common::UDPHeader*)&data[0x22]);
}
bool CEXIETHERNET::BuiltInBBAInterface::Activate()
{
if (IsActivated())
return true;
active = true;
m_in_frame = std::make_unique<u8[]>(9004);
m_out_frame = std::make_unique<u8[]>(9004);
for (auto& buf : queue_data)
buf.reserve(2048);
fake_mac = Common::GenerateMacAddress(Common::MACConsumer::BBA);
const u32 ip = m_local_ip.empty() ? sf::IpAddress::getLocalAddress().toInteger() :
sf::IpAddress(m_local_ip).toInteger();
m_current_ip = htonl(ip);
m_router_ip = (m_current_ip & 0xFFFFFF) | 0x01000000;
// clear all ref
for (int i = 0; i < std::size(network_ref); i++)
{
network_ref[i].ip = 0;
}
return RecvInit();
}
void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
{
// Is the BBA Active? If not skip shutdown
if (!IsActivated())
return;
// Signal read thread to exit.
m_read_enabled.Clear();
m_read_thread_shutdown.Set();
active = false;
// kill all active socket
for (int i = 0; i < std::size(network_ref); i++)
{
if (network_ref[i].ip != 0)
{
network_ref[i].type == IPPROTO_TCP ? network_ref[i].tcp_socket.disconnect() :
network_ref[i].udp_socket.unbind();
}
network_ref[i].ip = 0;
}
// Wait for read thread to exit.
if (m_read_thread.joinable())
m_read_thread.join();
}
bool CEXIETHERNET::BuiltInBBAInterface::IsActivated()
{
return active;
}
void CEXIETHERNET::BuiltInBBAInterface::WriteToQueue(const u8* data, int length)
{
queue_data[queue_write].resize(length);
std::memcpy(&queue_data[queue_write][0], data, length);
if (((queue_write + 1) & 15) == queue_read)
{
return;
}
queue_write = (queue_write + 1) & 15;
}
void CEXIETHERNET::BuiltInBBAInterface::HandleARP(Common::EthernetHeader* hwdata,
Common::ARPHeader* arpdata)
{
std::memset(m_in_frame.get(), 0, 0x30);
Common::EthernetHeader* hwpart = (Common::EthernetHeader*)m_in_frame.get();
*hwpart = Common::EthernetHeader(ARP_PROTOCOL);
hwpart->destination = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
hwpart->source = fake_mac;
Common::ARPHeader* arppart = (Common::ARPHeader*)&m_in_frame[14];
Common::MACAddress bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
if (arpdata->target_ip == m_current_ip)
{
// game asked for himself, reply with his mac address
*arppart = Common::ARPHeader(arpdata->target_ip, bba_mac, m_current_ip, bba_mac);
}
else
{
*arppart = Common::ARPHeader(arpdata->target_ip, fake_mac, m_current_ip, bba_mac);
}
WriteToQueue(&m_in_frame[0], 0x2a);
}
void CEXIETHERNET::BuiltInBBAInterface::HandleDHCP(Common::EthernetHeader* hwdata,
Common::UDPHeader* udpdata,
Common::DHCPBody* request)
{
Common::DHCPBody* reply = (Common::DHCPBody*)&m_in_frame[0x2a];
sockaddr_in from;
sockaddr_in to;
std::memset(m_in_frame.get(), 0, 0x156);
// build layer
auto [hwpart, ippart, udppart] = getUdpHeaders(
m_in_frame.get(), *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0], fake_mac);
from.sin_addr.s_addr = m_router_ip;
from.sin_family = IPPROTO_UDP;
from.sin_port = htons(67);
to.sin_addr.s_addr = m_current_ip;
to.sin_family = IPPROTO_UDP;
to.sin_port = udpdata->source_port;
const std::vector<u8> ip_part = {((u8*)&m_router_ip)[0], ((u8*)&m_router_ip)[1],
((u8*)&m_router_ip)[2], ((u8*)&m_router_ip)[3]};
*ippart = Common::IPv4Header(308, IPPROTO_UDP, from, to);
*udppart = Common::UDPHeader(from, to, 300);
*reply = Common::DHCPBody(request->transaction_id,
*(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0], m_current_ip,
m_router_ip);
// options
request->options[2] == 1 ? reply->AddDHCPOption(1, 53, std::vector<u8>{2}) :
reply->AddDHCPOption(1, 53, std::vector<u8>{5});
reply->AddDHCPOption(4, 54, ip_part); // dhcp server ip
reply->AddDHCPOption(4, 51, std::vector<u8>{0, 1, 0x51, 0x80}); // lease time 24h
reply->AddDHCPOption(4, 58, std::vector<u8>{0, 1, 0x51, 0x80}); // renewal
reply->AddDHCPOption(4, 59, std::vector<u8>{0, 1, 0x51, 0x80}); // rebind
reply->AddDHCPOption(4, 1, std::vector<u8>{255, 255, 255, 0}); // submask
reply->AddDHCPOption(4, 28,
std::vector<u8>{ip_part[0], ip_part[1], ip_part[2], 255}); // broadcast ip
reply->AddDHCPOption(4, 6, ip_part); // dns server
reply->AddDHCPOption(3, 15, std::vector<u8>{0x6c, 0x61, 0x6e}); // domaine name "lan"
reply->AddDHCPOption(4, 3, ip_part); // router ip
reply->AddDHCPOption(0, 255, {}); // end
udppart->checksum = Common::ComputeTCPNetworkChecksum(from, to, udppart, 308, IPPROTO_UDP);
WriteToQueue(m_in_frame.get(), 0x156);
}
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetAvaibleSlot(u16 port)
{
if (port > 0) // existing connection?
{
for (int i = 0; i < std::size(network_ref); i++)
{
if (network_ref[i].ip != 0 && network_ref[i].local == port)
return &network_ref[i];
}
}
for (int i = 0; i < std::size(network_ref); i++)
{
if (network_ref[i].ip == 0)
return &network_ref[i];
}
return nullptr;
}
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetTCPSlot(u16 src_port, u16 dst_port, u32 ip)
{
for (int i = 0; i < std::size(network_ref); i++)
{
if (network_ref[i].ip == ip && network_ref[i].remote == dst_port &&
network_ref[i].local == src_port)
{
return &network_ref[i];
}
}
return nullptr;
}
int BuildFINFrame(StackRef* ref, u8* buf)
{
std::memset(buf, 0, 0x36);
auto [hwpart, ippart, tcppart] = getTcpHeaders(buf, ref->bba_mac, ref->my_mac);
*ippart = Common::IPv4Header(20, IPPROTO_TCP, ref->from, ref->to);
*tcppart = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num,
TCP_FLAG_FIN | TCP_FLAG_ACK | TCP_FLAG_RST);
tcppart->checksum =
Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart, 20, IPPROTO_TCP);
for (auto& tcp_buf : ref->tcp_buffers)
tcp_buf.used = false;
return 0x36;
}
int BuildAckFrame(StackRef* ref, u8* buf)
{
std::memset(buf, 0, 0x36);
auto [hwpart, ippart, tcppart] = getTcpHeaders(buf, ref->bba_mac, ref->my_mac);
*ippart = Common::IPv4Header(20, IPPROTO_TCP, ref->from, ref->to);
*tcppart = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num, TCP_FLAG_ACK);
tcppart->checksum =
Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart, 20, IPPROTO_TCP);
return 0x36;
}
void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(Common::EthernetHeader* hwdata,
Common::IPv4Header* ipdata,
Common::TCPHeader* tcpdata, u8* data)
{
sf::IpAddress target;
StackRef* ref =
GetTCPSlot(tcpdata->source_port, tcpdata->destination_port, *(u32*)&ipdata->destination_addr);
if (tcpdata->properties & (TCP_FLAG_FIN | TCP_FLAG_RST))
{
if (ref == nullptr)
return; // not found
ref->ack_num++;
const int size = BuildFINFrame(ref, m_in_frame.get());
WriteToQueue(m_in_frame.get(), size);
ref->ip = 0;
ref->tcp_socket.disconnect();
}
else if (tcpdata->properties & TCP_FLAG_SIN)
{
// new connection
if (ref != nullptr)
return;
ref = GetAvaibleSlot(0);
ref->delay = GetTickCountStd();
ref->local = tcpdata->source_port;
ref->remote = tcpdata->destination_port;
ref->ack_num = htonl(tcpdata->sequence_number) + 1;
ref->ack_base = ref->ack_num;
ref->seq_num = 0x1000000;
ref->window_size = htons(tcpdata->window_size);
ref->type = IPPROTO_TCP;
for (auto& tcp_buf : ref->tcp_buffers)
tcp_buf.used = false;
ref->from.sin_addr.s_addr = *(u32*)&ipdata->destination_addr;
ref->from.sin_port = tcpdata->destination_port;
ref->to.sin_addr.s_addr = *(u32*)&ipdata->source_addr;
ref->to.sin_port = tcpdata->source_port;
ref->bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
ref->my_mac = fake_mac;
ref->tcp_socket.setBlocking(false);
// reply with a sin_ack
std::memset(m_in_frame.get(), 0, 0x100);
auto [hwpart, ippart, tcppart] = getTcpHeaders(m_in_frame.get(), ref->bba_mac, ref->my_mac);
*ippart = Common::IPv4Header(28, IPPROTO_TCP, ref->from, ref->to);
*tcppart = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num,
0x70 | TCP_FLAG_SIN | TCP_FLAG_ACK);
const u8 options[] = {0x02, 0x04, 0x05, 0xb4, 0x01, 0x01, 0x01, 0x01};
std::memcpy(&m_in_frame[0x36], options, std::size(options));
// do checksum
tcppart->checksum =
Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart, 28, IPPROTO_TCP);
ref->seq_num++;
target = sf::IpAddress(htonl(*(u32*)&ipdata->destination_addr));
ref->tcp_socket.connect(target, ntohs(tcpdata->destination_port));
ref->ready = false;
ref->ip = *(u32*)ipdata->destination_addr;
std::memcpy(&ref->tcp_buffers[0].data, m_in_frame.get(), 0x3e);
ref->tcp_buffers[0].data_size = 0x3e;
ref->tcp_buffers[0].seq_id = ref->seq_num - 1;
ref->tcp_buffers[0].tick = GetTickCountStd() - 900; // delay
ref->tcp_buffers[0].used = true;
}
else
{
// data packet
if (ref == nullptr)
return; // not found
const int c = (tcpdata->properties & 0xf0) >> 2; // header size
const int size = ntohs(ipdata->total_len) - 20 - c;
const u32 this_seq = ntohl(tcpdata->sequence_number);
if (size > 0)
{
// only if data
if ((int)(this_seq - ref->ack_num) >= 0)
{
ref->tcp_socket.send(data, size);
ref->ack_num += size;
}
// send ack
BuildAckFrame(ref, m_in_frame.get());
WriteToQueue(m_in_frame.get(), 0x36);
}
// update windows size
ref->window_size = ntohs(tcpdata->window_size);
// clear any ack data
if (tcpdata->properties & TCP_FLAG_ACK)
{
const u32 ack_num = ntohl(tcpdata->acknowledgement_number);
for (auto& tcp_buf : ref->tcp_buffers)
{
if (!tcp_buf.used || tcp_buf.seq_id >= ack_num)
continue;
Common::TCPHeader* tcppart = (Common::TCPHeader*)&tcp_buf.data[0x22];
const u32 seq_end =
tcp_buf.seq_id + tcp_buf.data_size - ((tcppart->properties & 0xf0) >> 2) - 34;
if (seq_end <= ack_num)
{
tcp_buf.used = false; // confirmed data received
if (!ref->ready && !ref->tcp_buffers[0].used)
ref->ready = true;
continue;
}
// partial data, adjust the packet for next ack
const u16 ack_size = ack_num - tcp_buf.seq_id;
const u16 new_data_size = tcp_buf.data_size - 0x36 - ack_size;
std::memmove(&tcp_buf.data[0x36], &tcp_buf.data[0x36 + ack_size], new_data_size);
tcp_buf.data_size -= ack_size;
tcp_buf.seq_id += ack_size;
tcppart->sequence_number = htonl(tcp_buf.seq_id);
Common::IPv4Header* ippart = (Common::IPv4Header*)&tcp_buf.data[14];
ippart->total_len = htons(tcp_buf.data_size - 14);
tcppart->checksum = 0;
tcppart->checksum = Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcppart,
new_data_size + 20, IPPROTO_TCP);
}
}
}
}
/// <summary>
/// This is a litle hack, Mario Kart open some UDP port
/// and listen to it. We open it on our side manualy.
/// </summary>
void CEXIETHERNET::BuiltInBBAInterface::InitUDPPort(u16 port)
{
StackRef* ref = GetAvaibleSlot(htons(port));
if (ref == nullptr || ref->ip != 0)
return;
ref->ip = 0x08080808; // change for ip
ref->local = htons(port);
ref->remote = htons(port);
ref->type = 17;
ref->bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
ref->my_mac = fake_mac;
ref->from.sin_addr.s_addr = 0;
ref->from.sin_port = htons(port);
ref->to.sin_addr.s_addr = m_current_ip;
ref->to.sin_port = htons(port);
ref->udp_socket.setBlocking(false);
if (ref->udp_socket.bind(port) != sf::Socket::Done)
{
ERROR_LOG_FMT(SP1, "Couldn't open UDP socket");
return;
}
}
void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(Common::EthernetHeader* hwdata,
Common::IPv4Header* ipdata,
Common::UDPHeader* udpdata, u8* data)
{
sf::IpAddress target;
if (*(u32*)ipdata->destination_addr == 0)
*(u32*)ipdata->destination_addr = m_router_ip;
// dns request
StackRef* ref = GetAvaibleSlot(udpdata->source_port);
if (ref->ip == 0)
{
ref->ip = *(u32*)ipdata->destination_addr; // change for ip
ref->local = udpdata->source_port;
ref->remote = udpdata->destination_port;
ref->type = 17;
ref->bba_mac = *(Common::MACAddress*)&m_eth_ref->mBbaMem[BBA_NAFR_PAR0];
ref->my_mac = fake_mac;
ref->from.sin_addr.s_addr = *(u32*)&ipdata->destination_addr;
ref->from.sin_port = udpdata->destination_port;
ref->to.sin_addr.s_addr = *(u32*)&ipdata->source_addr;
ref->to.sin_port = udpdata->source_port;
ref->udp_socket.setBlocking(false);
if (ref->udp_socket.bind(htons(udpdata->source_port)) != sf::Socket::Done &&
ref->udp_socket.bind(sf::Socket::AnyPort) != sf::Socket::Done)
{
ERROR_LOG_FMT(SP1, "Couldn't open UDP socket");
return;
}
}
if (ntohs(udpdata->destination_port) == 1900)
{
InitUDPPort(26512); // MK DD and 1080
InitUDPPort(26502); // Air Ride
if (*(u32*)ipdata->destination_addr == 0xFAFFFFEF) // force real broadcast
*(u32*)ipdata->destination_addr = 0xFFFFFFFF; // Multi cast cannot be read
if (udpdata->length > 150)
{
// Quick hack to unlock the connection, throw it back at him
Common::EthernetHeader* hwpart = (Common::EthernetHeader*)m_in_frame.get();
Common::IPv4Header* ippart = (Common::IPv4Header*)&m_in_frame[14];
std::memcpy(m_in_frame.get(), hwdata, ntohs(ipdata->total_len) + 14);
hwpart->destination = hwdata->source;
hwpart->source = hwdata->destination;
*(u32*)ippart->destination_addr = *(u32*)ipdata->source_addr;
*(u32*)ippart->source_addr = *(u32*)ipdata->destination_addr;
WriteToQueue(m_in_frame.get(), ntohs(ipdata->total_len) + 14);
}
}
if (ntohs(udpdata->destination_port) == 53)
{
target = sf::IpAddress(m_dns_ip.c_str()); // dns server ip
}
else
{
target = sf::IpAddress(ntohl(*(u32*)ipdata->destination_addr));
}
ref->udp_socket.send(data, ntohs(udpdata->length) - 8, target, ntohs(udpdata->destination_port));
}
bool CEXIETHERNET::BuiltInBBAInterface::SendFrame(const u8* frame, u32 size)
{
int offset = 0;
std::memcpy(m_out_frame.get(), frame, size);
std::lock_guard<std::mutex> lock(mtx);
// handle the packet data
Common::EthernetHeader* hwdata = (Common::EthernetHeader*)m_out_frame.get();
if (hwdata->ethertype == 0x08) // IPV4
{
// IP sub
Common::IPv4Header* ipdata = (Common::IPv4Header*)&m_out_frame[14];
offset = Common::EthernetHeader::SIZE + (ipdata->version_ihl & 0xf) * 4;
switch (ipdata->protocol)
{
case IPPROTO_UDP:
{
Common::UDPHeader* udpdata = (Common::UDPHeader*)&m_out_frame[offset];
offset += Common::UDPHeader::SIZE;
if (ntohs(udpdata->destination_port) == 67)
{
Common::DHCPBody* request = (Common::DHCPBody*)&m_out_frame[offset];
HandleDHCP(hwdata, udpdata, request);
}
else
{
HandleUDPFrame(hwdata, ipdata, udpdata, &m_out_frame[offset]);
}
break;
}
case IPPROTO_TCP:
{
Common::TCPHeader* tcpdata = (Common::TCPHeader*)&m_out_frame[offset];
offset += (tcpdata->properties & 0xf0) >> 2;
HandleTCPFrame(hwdata, ipdata, tcpdata, &m_out_frame[offset]);
break;
}
}
}
if (hwdata->ethertype == 0x608) // arp
{
Common::ARPHeader* arpdata = (Common::ARPHeader*)&m_out_frame[14];
HandleARP(hwdata, arpdata);
}
m_eth_ref->SendComplete();
return true;
}
size_t TryGetDataFromSocket(StackRef* ref, u8* buffer)
{
size_t datasize = 0; // this will be filled by the socket read later
unsigned short remote_port;
switch (ref->type)
{
case IPPROTO_UDP:
ref->udp_socket.receive(&buffer[0x2a], 1500, datasize, ref->target, remote_port);
if (datasize > 0)
{
std::memset(buffer, 0, 0x2a);
auto [hwpart, ipdata, udpdata] = getUdpHeaders(buffer, ref->bba_mac, ref->my_mac);
ref->from.sin_port = htons(remote_port);
ref->from.sin_addr.s_addr = htonl(ref->target.toInteger());
*ipdata = Common::IPv4Header((u16)(datasize + 8), IPPROTO_UDP, ref->from, ref->to);
*udpdata = Common::UDPHeader(ref->from, ref->to, (u16)datasize);
udpdata->checksum = Common::ComputeTCPNetworkChecksum(ref->from, ref->to, udpdata,
(u16)(datasize + 8), IPPROTO_UDP);
datasize += 0x2a;
}
break;
case IPPROTO_TCP:
sf::Socket::Status st = sf::Socket::Status::Done;
TcpBuffer* tcp_buffer = nullptr;
for (auto& tcp_buf : ref->tcp_buffers)
{
if (tcp_buf.used)
continue;
tcp_buffer = &tcp_buf;
break;
}
// set default size to 0 to avoid issue
datasize = 0;
const bool can_go = (GetTickCountStd() - ref->poke_time > 100 || ref->window_size > 2000);
if (tcp_buffer != nullptr && ref->ready && can_go)
st = ref->tcp_socket.receive(&buffer[0x36], 440, datasize);
if (datasize > 0)
{
std::memset(buffer, 0, 0x36);
auto [hwpart, ipdata, tcpdata] = getTcpHeaders(buffer, ref->bba_mac, ref->my_mac);
*ipdata = Common::IPv4Header((u16)(datasize + 20), IPPROTO_TCP, ref->from, ref->to);
*tcpdata = Common::TCPHeader(ref->from, ref->to, ref->seq_num, ref->ack_num, TCP_FLAG_ACK);
tcpdata->checksum = Common::ComputeTCPNetworkChecksum(ref->from, ref->to, tcpdata,
(u16)(datasize + 20), IPPROTO_TCP);
// build buffer
tcp_buffer->seq_id = ref->seq_num;
tcp_buffer->data_size = (u16)datasize + 0x36;
tcp_buffer->tick = GetTickCountStd();
std::memcpy(&tcp_buffer->data[0], buffer, datasize + 0x36);
tcp_buffer->seq_id = ref->seq_num;
tcp_buffer->used = true;
ref->seq_num += (u32)datasize;
ref->poke_time = GetTickCountStd();
datasize += 0x36;
}
if (GetTickCountStd() - ref->delay > 3000)
{
if (st == sf::Socket::Disconnected || st == sf::Socket::Error)
{
ref->ip = 0;
ref->tcp_socket.disconnect();
datasize = BuildFINFrame(ref, buffer);
}
}
break;
}
return datasize;
}
void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInBBAInterface* self)
{
while (!self->m_read_thread_shutdown.IsSet())
{
// make thread less cpu hungry
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (!self->m_read_enabled.IsSet())
continue;
size_t datasize = 0;
u8 wp = self->m_eth_ref->page_ptr(BBA_RWP);
const u8 rp = self->m_eth_ref->page_ptr(BBA_RRP);
if (rp > wp)
wp += 16;
if ((wp - rp) >= 8)
continue;
std::lock_guard<std::mutex> lock(self->mtx);
// process queue file first
if (self->queue_read != self->queue_write)
{
datasize = self->queue_data[self->queue_read].size();
std::memcpy(self->m_eth_ref->mRecvBuffer.get(), &self->queue_data[self->queue_read][0],
datasize);
self->queue_read++;
self->queue_read &= 15;
}
else
{
// test connections data
for (auto& net_ref : self->network_ref)
{
if (net_ref.ip == 0)
continue;
datasize = TryGetDataFromSocket(&net_ref, self->m_eth_ref->mRecvBuffer.get());
if (datasize > 0)
break;
}
}
// test and add any sleeping tcp data
for (auto& net_ref : self->network_ref)
{
if (net_ref.ip == 0 || net_ref.type != IPPROTO_TCP)
continue;
for (auto& tcp_buf : net_ref.tcp_buffers)
{
if (!tcp_buf.used || (GetTickCountStd() - tcp_buf.tick) <= 1000)
continue;
tcp_buf.tick = GetTickCountStd();
// late data, resend
if (((self->queue_write + 1) & 15) != self->queue_read)
{
self->WriteToQueue(&tcp_buf.data[0], tcp_buf.data_size);
}
}
}
if (datasize > 0)
{
u8* b = &self->m_eth_ref->mRecvBuffer[0];
Common::EthernetHeader* hwdata = (Common::EthernetHeader*)b;
if (hwdata->ethertype == 0x8) // IP_PROTOCOL
{
Common::IPv4Header* ipdata = (Common::IPv4Header*)&b[14];
ipdata->identification = ntohs(++self->ip_frame_id);
ipdata->header_checksum = 0;
ipdata->header_checksum = htons(Common::ComputeNetworkChecksum(ipdata, 20));
}
self->m_eth_ref->mRecvBufferLength = datasize > 64 ? (u32)datasize : 64;
self->m_eth_ref->RecvHandlePacket();
}
}
}
bool CEXIETHERNET::BuiltInBBAInterface::RecvInit()
{
m_read_thread = std::thread(ReadThreadHandler, this);
return true;
}
void CEXIETHERNET::BuiltInBBAInterface::RecvStart()
{
m_read_enabled.Set();
}
void CEXIETHERNET::BuiltInBBAInterface::RecvStop()
{
m_read_enabled.Clear();
for (auto& net_ref : network_ref)
{
if (net_ref.ip != 0)
{
net_ref.type == IPPROTO_TCP ? net_ref.tcp_socket.disconnect() : net_ref.udp_socket.unbind();
}
net_ref.ip = 0;
}
queue_read = 0;
queue_write = 0;
}
} // namespace ExpansionInterface

View File

@ -0,0 +1,56 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _WIN32
#include <WinSock2.h>
#else
#include <netinet/in.h>
#endif
#include <SFML/Network.hpp>
#include "Common/CommonTypes.h"
#include "Common/Network.h"
constexpr u16 TCP_FLAG_SIN = 0x200;
constexpr u16 TCP_FLAG_ACK = 0x1000;
constexpr u16 TCP_FLAG_PSH = 0x800;
constexpr u16 TCP_FLAG_FIN = 0x100;
constexpr u16 TCP_FLAG_RST = 0x400;
constexpr u16 IP_PROTOCOL = 0x800;
constexpr u16 ARP_PROTOCOL = 0x806;
constexpr u8 MAX_TCP_BUFFER = 4;
struct TcpBuffer
{
bool used;
u64 tick;
u32 seq_id;
u16 data_size;
std::array<u8, 2048> data;
};
struct StackRef
{
u32 ip;
u16 local;
u16 remote;
u16 type;
sf::IpAddress target;
u32 seq_num;
u32 ack_num;
u32 ack_base;
u16 window_size;
u64 delay;
std::array<TcpBuffer, MAX_TCP_BUFFER> tcp_buffers;
bool ready;
sockaddr_in from;
sockaddr_in to;
Common::MACAddress bba_mac{};
Common::MACAddress my_mac{};
sf::UdpSocket udp_socket;
sf::TcpSocket tcp_socket;
u64 poke_time;
};

View File

@ -143,6 +143,10 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(const EXIDeviceType device_type, co
result = std::make_unique<CEXIETHERNET>(BBADeviceType::XLINK);
break;
case EXIDeviceType::EthernetBuiltIn:
result = std::make_unique<CEXIETHERNET>(BBADeviceType::BuiltIn);
break;
case EXIDeviceType::Gecko:
result = std::make_unique<CEXIGecko>();
break;

View File

@ -37,6 +37,7 @@ enum class EXIDeviceType : int
EthernetXLink,
// Only used on Apple devices.
EthernetTapServer,
EthernetBuiltIn,
None = 0xFF
};
@ -79,7 +80,7 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(EXIDeviceType device_type, int chan
template <>
struct fmt::formatter<ExpansionInterface::EXIDeviceType>
: EnumFormatter<ExpansionInterface::EXIDeviceType::EthernetTapServer>
: EnumFormatter<ExpansionInterface::EXIDeviceType::EthernetBuiltIn>
{
static constexpr array_type names = {
_trans("Dummy"),
@ -95,6 +96,7 @@ struct fmt::formatter<ExpansionInterface::EXIDeviceType>
_trans("Advance Game Port"),
_trans("Broadband Adapter (XLink Kai)"),
_trans("Broadband Adapter (tapserver)"),
_trans("Broadband Adapter (Built In)"),
};
constexpr formatter() : EnumFormatter(names) {}

View File

@ -18,6 +18,8 @@
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/Memmap.h"
//#define BBA_TRACK_PAGE_PTRS
namespace ExpansionInterface
{
// XXX: The BBA stores multi-byte elements as little endian.
@ -53,6 +55,11 @@ CEXIETHERNET::CEXIETHERNET(BBADeviceType type)
INFO_LOG_FMT(SP1, "Created tapserver physical network interface.");
break;
#endif
case BBADeviceType::BuiltIn:
m_network_interface = std::make_unique<BuiltInBBAInterface>(
this, Config::Get(Config::MAIN_BBA_BUILTIN_DNS), Config::Get(Config::MAIN_BBA_BUILTIN_IP));
INFO_LOG_FMT(SP1, "Created Built in network interface.");
break;
case BBADeviceType::XLINK:
// TODO start BBA with network link down, bring it up after "connected" response from XLink
@ -155,6 +162,17 @@ void CEXIETHERNET::ImmWrite(u32 data, u32 size)
{
case INTERRUPT:
exi_status.interrupt &= data ^ 0xff;
// raise back if there is still data
if (page_ptr(BBA_RRP) != page_ptr(BBA_RWP))
{
if (mBbaMem[BBA_IMR] & INT_R)
{
mBbaMem[BBA_IR] |= INT_R;
exi_status.interrupt |= exi_status.TRANSFER;
}
}
break;
case INTERRUPT_MASK:
exi_status.interrupt_mask = data;
@ -228,9 +246,7 @@ void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
void CEXIETHERNET::DMARead(u32 addr, u32 size)
{
DEBUG_LOG_FMT(SP1, "DMA read: {:08x} {:x}", addr, size);
Memory::CopyToEmu(addr, &mBbaMem[transfer.address], size);
transfer.address += size;
}
@ -348,7 +364,6 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
// MXSoftReset();
m_network_interface->Activate();
}
if (((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR)) != 0)
{
DEBUG_LOG_FMT(SP1, "{} rx", (data & NCRA_SR) ? "start" : "stop");
@ -415,7 +430,6 @@ void CEXIETHERNET::DirectFIFOWrite(const u8* data, u32 size)
{
// In direct mode, the hardware handles creating the state required by the
// GMAC instead of finagling with packet descriptors and such
u16* tx_fifo_count = (u16*)&mBbaMem[BBA_TXFIFOCNT];
memcpy(tx_fifo.get() + *tx_fifo_count, data, size);
@ -510,76 +524,80 @@ inline void CEXIETHERNET::inc_rwp()
{
u16* rwp = (u16*)&mBbaMem[BBA_RWP];
if (*rwp + 1 == page_ptr(BBA_RHBP))
if (*rwp == page_ptr(BBA_RHBP))
*rwp = page_ptr(BBA_BP);
else
(*rwp)++;
}
inline void CEXIETHERNET::set_rwp(u16 value)
{
u16* rwp = (u16*)&mBbaMem[BBA_RWP];
*rwp = value;
}
// This function is on the critical path for receiving data.
// Be very careful about calling into the logger and other slow things
bool CEXIETHERNET::RecvHandlePacket()
{
u8* write_ptr;
u8* end_ptr;
u8* read_ptr;
Descriptor* descriptor;
u32 status = 0;
u16 rwp_initial = page_ptr(BBA_RWP);
u16 current_rwp = 0;
u32 off = 4;
if (!RecvMACFilter())
goto wait_for_next;
#ifdef BBA_TRACK_PAGE_PTRS
INFO_LOG_FMT(SP1, "RecvHandlePacket {:x}\n{}", mRecvBufferLength,
ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100));
ArrayToString(mRecvBuffer.get(), mRecvBufferLength, 16));
INFO_LOG_FMT(SP1, "{:x} {:x} {:x} {:x}", page_ptr(BBA_BP), page_ptr(BBA_RRP), page_ptr(BBA_RWP),
page_ptr(BBA_RHBP));
#endif
write_ptr = ptr_from_page_ptr(BBA_RWP);
end_ptr = ptr_from_page_ptr(BBA_RHBP);
read_ptr = ptr_from_page_ptr(BBA_RRP);
write_ptr = &mBbaMem[page_ptr(BBA_RWP) << 8];
descriptor = (Descriptor*)write_ptr;
write_ptr += 4;
for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off)
current_rwp = page_ptr(BBA_RWP);
DEBUG_LOG_FMT(SP1, "Frame recv: {:x}", mRecvBufferLength);
for (u32 i = 0; i < mRecvBufferLength; i++)
{
*write_ptr++ = mRecvBuffer[i];
if (off == 0xff)
write_ptr[off] = mRecvBuffer[i];
off++;
if (off == 0x100)
{
off = 0;
inc_rwp();
}
// avoid increasing the BBA register while copying
// sometime the OS can try to process when it's not completed
current_rwp = current_rwp == page_ptr(BBA_RHBP) ? page_ptr(BBA_BP) : ++current_rwp;
if (write_ptr == end_ptr)
write_ptr = ptr_from_page_ptr(BBA_BP);
write_ptr = &mBbaMem[current_rwp << 8];
if (write_ptr == read_ptr)
{
/*
halt copy
if (cur_packet_size >= PAGE_SIZE)
desc.status |= FO | BF
if (RBFIM)
raise RBFI
if (AUTORCVR)
discard bad packet
else
inc MPC instead of receiving packets
*/
status |= DESC_FO | DESC_BF;
mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
break;
if (page_ptr(BBA_RRP) == current_rwp)
{
/*
halt copy
if (cur_packet_size >= PAGE_SIZE)
desc.status |= FO | BF
if (RBFIM)
raise RBFI
if (AUTORCVR)
discard bad packet
else
inc MPC instead of receiving packets
*/
status |= DESC_FO | DESC_BF;
mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
break;
}
}
}
// Align up to next page
if ((mRecvBufferLength + 4) % 256)
inc_rwp();
current_rwp = current_rwp == page_ptr(BBA_RHBP) ? page_ptr(BBA_BP) : ++current_rwp;
#ifdef BBA_TRACK_PAGE_PTRS
INFO_LOG_FMT(SP1, "{:x} {:x} {:x} {:x}", page_ptr(BBA_BP), page_ptr(BBA_RRP), page_ptr(BBA_RWP),
@ -602,7 +620,9 @@ bool CEXIETHERNET::RecvHandlePacket()
}
}
descriptor->set(*(u16*)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, status);
descriptor->set(current_rwp, 4 + mRecvBufferLength, status);
set_rwp(current_rwp);
mBbaMem[BBA_LRPS] = status;

View File

@ -13,7 +13,10 @@
#include <SFML/Network.hpp>
#include <mutex>
#include "Common/Flag.h"
#include "Common/Network.h"
#include "Core/HW/EXI/BBA/BuiltIn.h"
#include "Core/HW/EXI/EXI_Device.h"
class PointerWrap;
@ -204,6 +207,7 @@ enum class BBADeviceType
#if defined(__APPLE__)
TAPSERVER,
#endif
BuiltIn,
};
class CEXIETHERNET : public IEXIDevice
@ -289,7 +293,6 @@ private:
return ((u16)mBbaMem[index + 1] << 8) | mBbaMem[index];
}
inline u8* ptr_from_page_ptr(int const index) const { return &mBbaMem[page_ptr(index) << 8]; }
bool IsMXCommand(u32 const data);
bool IsWriteCommand(u32 const data);
const char* GetRegisterName() const;
@ -299,9 +302,11 @@ private:
void SendFromDirectFIFO();
void SendFromPacketBuffer();
void SendComplete();
void SendCompleteBack();
u8 HashIndex(const u8* dest_eth_addr);
bool RecvMACFilter();
void inc_rwp();
void set_rwp(u16 value);
bool RecvHandlePacket();
std::unique_ptr<u8[]> mBbaMem;
@ -413,6 +418,60 @@ private:
#endif
};
class BuiltInBBAInterface : public NetworkInterface
{
public:
BuiltInBBAInterface(CEXIETHERNET* eth_ref, std::string dns_ip, std::string local_ip)
: NetworkInterface(eth_ref), m_dns_ip(std::move(dns_ip)), m_local_ip(std::move(local_ip))
{
}
public:
bool Activate() override;
void Deactivate() override;
bool IsActivated() override;
bool SendFrame(const u8* frame, u32 size) override;
bool RecvInit() override;
void RecvStart() override;
void RecvStop() override;
private:
std::string m_mac_id;
std::string m_dns_ip;
bool active = false;
u16 ip_frame_id = 0;
u8 queue_read = 0;
u8 queue_write = 0;
std::array<std::vector<u8>, 16> queue_data;
std::mutex mtx;
std::string m_local_ip;
u32 m_current_ip = 0;
u32 m_router_ip = 0;
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
std::array<StackRef, 10> network_ref{}; // max 10 at same time, i think most gc game had a
// limit of 8 in the gc framework
std::unique_ptr<u8[]> m_in_frame;
std::unique_ptr<u8[]> m_out_frame;
std::thread m_read_thread;
Common::Flag m_read_enabled;
Common::Flag m_read_thread_shutdown;
static void ReadThreadHandler(BuiltInBBAInterface* self);
Common::MACAddress fake_mac{};
#endif
void WriteToQueue(const u8* data, int length);
void HandleARP(Common::EthernetHeader* hwdata, Common::ARPHeader* arpdata);
void HandleDHCP(Common::EthernetHeader* hwdata, Common::UDPHeader* udpdata,
Common::DHCPBody* request);
StackRef* GetAvaibleSlot(u16 port);
StackRef* GetTCPSlot(u16 src_port, u16 dst_port, u32 ip);
void HandleTCPFrame(Common::EthernetHeader* hwdata, Common::IPv4Header* ipdata,
Common::TCPHeader* tcpdata, u8* data);
void InitUDPPort(u16 port);
void HandleUDPFrame(Common::EthernetHeader* hwdata, Common::IPv4Header* ipdata,
Common::UDPHeader* udpdata, u8* data);
};
std::unique_ptr<NetworkInterface> m_network_interface;
std::unique_ptr<u8[]> mRecvBuffer;

View File

@ -254,6 +254,7 @@
<ClInclude Include="Core\HW\DVD\DVDMath.h" />
<ClInclude Include="Core\HW\DVD\DVDThread.h" />
<ClInclude Include="Core\HW\DVD\FileMonitor.h" />
<ClInclude Include="Core\HW\EXI\BBA\BuiltIn.h" />
<ClInclude Include="Core\HW\EXI\BBA\TAP_Win32.h" />
<ClInclude Include="Core\HW\EXI\EXI_Channel.h" />
<ClInclude Include="Core\HW\EXI\EXI_Device.h" />
@ -868,6 +869,7 @@
<ClCompile Include="Core\HW\DVD\DVDMath.cpp" />
<ClCompile Include="Core\HW\DVD\DVDThread.cpp" />
<ClCompile Include="Core\HW\DVD\FileMonitor.cpp" />
<ClCompile Include="Core\HW\EXI\BBA\BuiltIn.cpp" />
<ClCompile Include="Core\HW\EXI\BBA\TAP_Win32.cpp" />
<ClCompile Include="Core\HW\EXI\BBA\XLINK_KAI_BBA.cpp" />
<ClCompile Include="Core\HW\EXI\EXI_Channel.cpp" />

View File

@ -48,6 +48,15 @@ void BroadbandAdapterSettingsDialog::InitControls()
window_title = tr("Broadband Adapter MAC Address");
break;
case Type::BuiltIn:
address_label = new QLabel(tr("Enter the DNS server to use:"));
address_placeholder = QString::fromStdString("8.8.8.8");
current_address = QString::fromStdString(Config::Get(Config::MAIN_BBA_BUILTIN_DNS));
description = new QLabel(tr("Use 8.8.8.8 for normal DNS, else enter your custom one"));
window_title = tr("Broadband Adapter DNS setting");
break;
case Type::XLinkKai:
address_label = new QLabel(tr("Enter IP address of device running the XLink Kai Client:"));
address_placeholder = QString::fromStdString("127.0.0.1");
@ -103,6 +112,9 @@ void BroadbandAdapterSettingsDialog::SaveAddress()
Config::SetBaseOrCurrent(Config::MAIN_BBA_MAC, bba_new_address);
break;
case Type::BuiltIn:
Config::SetBaseOrCurrent(Config::MAIN_BBA_BUILTIN_DNS, bba_new_address);
break;
case Type::XLinkKai:
Config::SetBaseOrCurrent(Config::MAIN_BBA_XLINK_IP, bba_new_address);
break;

View File

@ -15,6 +15,7 @@ public:
{
Ethernet,
XLinkKai,
BuiltIn
};
explicit BroadbandAdapterSettingsDialog(QWidget* target, Type bba_type);

View File

@ -120,6 +120,7 @@ void GameCubePane::CreateWidgets()
#ifdef __APPLE__
EXIDeviceType::EthernetTapServer,
#endif
EXIDeviceType::EthernetBuiltIn,
})
{
m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()),
@ -259,7 +260,8 @@ void GameCubePane::UpdateButton(ExpansionInterface::Slot slot)
break;
case ExpansionInterface::Slot::SP1:
has_config = (device == ExpansionInterface::EXIDeviceType::Ethernet ||
device == ExpansionInterface::EXIDeviceType::EthernetXLink);
device == ExpansionInterface::EXIDeviceType::EthernetXLink ||
device == ExpansionInterface::EXIDeviceType::EthernetBuiltIn);
break;
}
@ -293,6 +295,11 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot)
BroadbandAdapterSettingsDialog(this, BroadbandAdapterSettingsDialog::Type::XLinkKai).exec();
return;
}
case ExpansionInterface::EXIDeviceType::EthernetBuiltIn:
{
BroadbandAdapterSettingsDialog(this, BroadbandAdapterSettingsDialog::Type::BuiltIn).exec();
return;
}
default:
PanicAlertFmt("Unknown settings pressed for {}", device);
return;