diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 433aeb9d15..540087c9ea 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -100,7 +100,8 @@ add_library(core
HW/EXI/EXI_DeviceAD16.cpp
HW/EXI/EXI_DeviceAGP.cpp
HW/EXI/EXI_DeviceDummy.cpp
- HW/EXI/EXI_DeviceEthernet.cpp
+ HW/EXI/EXI_DeviceEthernetBase.cpp
+ HW/EXI/EXI_DeviceEthernetTCP.cpp
HW/EXI/EXI_DeviceGecko.cpp
HW/EXI/EXI_DeviceIPL.cpp
HW/EXI/EXI_DeviceMemoryCard.cpp
@@ -330,7 +331,7 @@ endif()
if(WIN32)
target_sources(core PRIVATE
- HW/EXI/BBA-TAP/TAP_Win32.cpp
+ HW/EXI/EXI_DeviceEthernetTAP_Win32.cpp
HW/WiimoteReal/IOWin.cpp
)
target_link_libraries(core PUBLIC
@@ -340,12 +341,12 @@ if(WIN32)
)
elseif(APPLE)
target_sources(core PRIVATE
- HW/EXI/BBA-TAP/TAP_Apple.cpp
+ HW/EXI/EXI_DeviceEthernetTAP_Apple.cpp
HW/WiimoteReal/IOdarwin.mm
)
target_link_libraries(core PUBLIC ${IOB_LIBRARY})
elseif(UNIX)
- target_sources(core PRIVATE HW/EXI/BBA-TAP/TAP_Unix.cpp)
+ target_sources(core PRIVATE HW/EXI/EXI_DeviceEthernetTAP_Unix.cpp)
if(ANDROID)
target_sources(core PRIVATE HW/WiimoteReal/IOAndroid.cpp)
endif()
diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp
index 95b1222c17..b85bf3c94d 100644
--- a/Source/Core/Core/ConfigManager.cpp
+++ b/Source/Core/Core/ConfigManager.cpp
@@ -235,6 +235,8 @@ void SConfig::SaveCoreSettings(IniFile& ini)
core->Set("SlotB", m_EXIDevice[1]);
core->Set("SerialPort1", m_EXIDevice[2]);
core->Set("BBA_MAC", m_bba_mac);
+ core->Set("BBA_SERVER", m_bba_server);
+ core->Set("BBA_PORT", m_bba_port);
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
{
core->Set(StringFromFormat("SIDevice%i", i), m_SIDevice[i]);
@@ -527,6 +529,8 @@ void SConfig::LoadCoreSettings(IniFile& ini)
core->Get("SlotB", (int*)&m_EXIDevice[1], ExpansionInterface::EXIDEVICE_NONE);
core->Get("SerialPort1", (int*)&m_EXIDevice[2], ExpansionInterface::EXIDEVICE_NONE);
core->Get("BBA_MAC", &m_bba_mac);
+ core->Get("BBA_SERVER", &m_bba_server);
+ core->Get("BBA_PORT", &m_bba_port);
for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
{
core->Get(StringFromFormat("SIDevice%i", i), (u32*)&m_SIDevice[i],
diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h
index 8b95fde543..c04e7f1b68 100644
--- a/Source/Core/Core/ConfigManager.h
+++ b/Source/Core/Core/ConfigManager.h
@@ -227,6 +227,8 @@ struct SConfig
ExpansionInterface::TEXIDevices m_EXIDevice[3];
SerialInterface::SIDevices m_SIDevice[4];
std::string m_bba_mac;
+ std::string m_bba_server;
+ u16 m_bba_port;
// interface language
std::string m_InterfaceLanguage;
diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj
index bff81652df..41ed4eac1e 100644
--- a/Source/Core/Core/Core.vcxproj
+++ b/Source/Core/Core/Core.vcxproj
@@ -132,14 +132,15 @@
-
-
+
+
+
@@ -396,14 +397,16 @@
-
-
+
+
+
+
diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters
index 7955ded024..1b665c8f9d 100644
--- a/Source/Core/Core/Core.vcxproj.filters
+++ b/Source/Core/Core/Core.vcxproj.filters
@@ -441,7 +441,16 @@
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
-
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
+
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
@@ -456,9 +465,6 @@
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
-
- HW %28Flipper/Hollywood%29\EXI - Expansion Interface
-
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
@@ -1143,7 +1149,16 @@
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
-
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
+
+ HW %28Flipper/Hollywood%29\EXI - Expansion Interface
+
+
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
@@ -1158,9 +1173,6 @@
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
-
- HW %28Flipper/Hollywood%29\EXI - Expansion Interface
-
HW %28Flipper/Hollywood%29\EXI - Expansion Interface
@@ -1638,4 +1650,4 @@
-
\ No newline at end of file
+
diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp
index be4a7029c8..62a9ac5613 100644
--- a/Source/Core/Core/HW/EXI/EXI_Device.cpp
+++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp
@@ -10,11 +10,12 @@
#include "Core/HW/EXI/EXI_DeviceAD16.h"
#include "Core/HW/EXI/EXI_DeviceAGP.h"
#include "Core/HW/EXI/EXI_DeviceDummy.h"
-#include "Core/HW/EXI/EXI_DeviceEthernet.h"
+#include "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
#include "Core/HW/EXI/EXI_DeviceGecko.h"
#include "Core/HW/EXI/EXI_DeviceIPL.h"
#include "Core/HW/EXI/EXI_DeviceMemoryCard.h"
#include "Core/HW/EXI/EXI_DeviceMic.h"
+#include "Core/HW/EXI/EXI_DeviceEthernetTCP.h"
#include "Core/HW/Memmap.h"
namespace ExpansionInterface
@@ -131,8 +132,8 @@ std::unique_ptr EXIDevice_Create(const TEXIDevices device_type, cons
result = std::make_unique(channel_num);
break;
- case EXIDEVICE_ETH:
- result = std::make_unique();
+ case EXIDEVICE_ETH_TAP:
+ result = std::make_unique();
break;
case EXIDEVICE_GECKO:
@@ -143,6 +144,10 @@ std::unique_ptr EXIDevice_Create(const TEXIDevices device_type, cons
result = std::make_unique(channel_num);
break;
+ case EXIDEVICE_ETH_TCP:
+ result = std::make_unique();
+ break;
+
case EXIDEVICE_AM_BASEBOARD:
case EXIDEVICE_NONE:
default:
diff --git a/Source/Core/Core/HW/EXI/EXI_Device.h b/Source/Core/Core/HW/EXI/EXI_Device.h
index 0eb30904ca..58ef1cfd42 100644
--- a/Source/Core/Core/HW/EXI/EXI_Device.h
+++ b/Source/Core/Core/HW/EXI/EXI_Device.h
@@ -18,7 +18,7 @@ enum TEXIDevices : int
EXIDEVICE_MASKROM,
EXIDEVICE_AD16,
EXIDEVICE_MIC,
- EXIDEVICE_ETH,
+ EXIDEVICE_ETH_TAP,
// Was used for Triforce in the past, but the implementation is no longer in Dolphin.
// It's kept here so that values below will stay constant.
EXIDEVICE_AM_BASEBOARD,
@@ -27,6 +27,7 @@ enum TEXIDevices : int
// Converted to EXIDEVICE_MEMORYCARD internally.
EXIDEVICE_MEMORYCARDFOLDER,
EXIDEVICE_AGP,
+ EXIDEVICE_ETH_TCP,
EXIDEVICE_NONE = 0xFF
};
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetBase.cpp
similarity index 71%
rename from Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp
rename to Source/Core/Core/HW/EXI/EXI_DeviceEthernetBase.cpp
index 1972e3e3ea..a8349720e4 100644
--- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetBase.cpp
@@ -2,11 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/HW/EXI/EXI_DeviceEthernet.h"
-
-#include
-#include
-#include
+#include "Core/HW/EXI/EXI_DeviceEthernetBase.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
@@ -22,40 +18,43 @@ namespace ExpansionInterface
// XXX: The BBA stores multi-byte elements as little endian.
// Multiple parts of this implementation depend on Dolphin
// being compiled for a little endian host.
-
-CEXIETHERNET::CEXIETHERNET()
+CEXIEthernetBase::CEXIEthernetBase()
{
- tx_fifo = std::make_unique(BBA_TXFIFO_SIZE);
- mBbaMem = std::make_unique(BBA_MEM_SIZE);
- mRecvBuffer = std::make_unique(BBA_RECV_SIZE);
+ m_tx_fifo = std::make_unique(BBA_TXFIFO_SIZE);
+ m_bba_mem = std::make_unique(BBA_MEM_SIZE);
+ m_recv_buffer = std::make_unique(BBA_RECV_SIZE);
MXHardReset();
// Parse MAC address from config, and generate a new one if it doesn't
// exist or can't be parsed.
- std::string& mac_addr_setting = SConfig::GetInstance().m_bba_mac;
- std::optional mac_addr = Common::StringToMacAddress(mac_addr_setting);
+ const std::string& mac_addr_setting = SConfig::GetInstance().m_bba_mac;
+ Common::MACAddress mac_addr;
- if (!mac_addr)
- {
+ if (mac_addr_setting.empty())
mac_addr = Common::GenerateMacAddress(Common::MACConsumer::BBA);
- mac_addr_setting = Common::MacAddressToString(mac_addr.value());
- SConfig::GetInstance().SaveSettings();
+ else
+ {
+ std::optional parsed = Common::StringToMacAddress(mac_addr_setting);
+
+ if (!parsed)
+ {
+ mac_addr = Common::GenerateMacAddress(Common::MACConsumer::BBA);
+ ERROR_LOG(SP1, "Invalid mac address (%s), generated a temporary one (%s)",
+ mac_addr_setting.c_str(), Common::MacAddressToString(mac_addr).c_str());
+ }
+ else
+ mac_addr = parsed.value();
}
- const auto& mac = mac_addr.value();
- memcpy(&mBbaMem[BBA_NAFR_PAR0], mac.data(), mac.size());
+
+ memcpy(&m_bba_mem[BBA_NAFR_PAR0], mac_addr.data(), mac_addr.size());
// HACK: .. fully established 100BASE-T link
- mBbaMem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT;
+ m_bba_mem[BBA_NWAYS] = NWAYS_LS100 | NWAYS_LPNWAY | NWAYS_100TXF | NWAYS_ANCLPT;
}
-CEXIETHERNET::~CEXIETHERNET()
-{
- Deactivate();
-}
-
-void CEXIETHERNET::SetCS(int cs)
+void CEXIEthernetBase::SetCS(int cs)
{
if (cs)
{
@@ -64,17 +63,17 @@ void CEXIETHERNET::SetCS(int cs)
}
}
-bool CEXIETHERNET::IsPresent() const
+bool CEXIEthernetBase::IsPresent() const
{
return true;
}
-bool CEXIETHERNET::IsInterruptSet()
+bool CEXIEthernetBase::IsInterruptSet()
{
return !!(exi_status.interrupt & exi_status.interrupt_mask);
}
-void CEXIETHERNET::ImmWrite(u32 data, u32 size)
+void CEXIEthernetBase::ImmWrite(u32 data, u32 size)
{
data >>= (4 - size) * 8;
@@ -125,7 +124,7 @@ void CEXIETHERNET::ImmWrite(u32 data, u32 size)
}
}
-u32 CEXIETHERNET::ImmRead(u32 size)
+u32 CEXIEthernetBase::ImmRead(u32 size)
{
u32 ret = 0;
@@ -155,7 +154,7 @@ u32 CEXIETHERNET::ImmRead(u32 size)
else
{
for (int i = size - 1; i >= 0; i--)
- ret |= mBbaMem[transfer.address++] << (i * 8);
+ ret |= m_bba_mem[transfer.address++] << (i * 8);
}
DEBUG_LOG(SP1, "imm r%i: %0*x", size, size * 2, ret);
@@ -165,7 +164,7 @@ u32 CEXIETHERNET::ImmRead(u32 size)
return ret;
}
-void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
+void CEXIEthernetBase::DMAWrite(u32 addr, u32 size)
{
DEBUG_LOG(SP1, "DMA write: %08x %x", addr, size);
@@ -182,32 +181,32 @@ void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
}
}
-void CEXIETHERNET::DMARead(u32 addr, u32 size)
+void CEXIEthernetBase::DMARead(u32 addr, u32 size)
{
DEBUG_LOG(SP1, "DMA read: %08x %x", addr, size);
- Memory::CopyToEmu(addr, &mBbaMem[transfer.address], size);
+ Memory::CopyToEmu(addr, &m_bba_mem[transfer.address], size);
transfer.address += size;
}
-void CEXIETHERNET::DoState(PointerWrap& p)
+void CEXIEthernetBase::DoState(PointerWrap& p)
{
- p.DoArray(tx_fifo.get(), BBA_TXFIFO_SIZE);
- p.DoArray(mBbaMem.get(), BBA_MEM_SIZE);
+ p.DoArray(m_tx_fifo.get(), BBA_TXFIFO_SIZE);
+ p.DoArray(m_bba_mem.get(), BBA_MEM_SIZE);
}
-bool CEXIETHERNET::IsMXCommand(u32 const data)
+bool CEXIEthernetBase::IsMXCommand(u32 const data)
{
return !!(data & (1 << 31));
}
-bool CEXIETHERNET::IsWriteCommand(u32 const data)
+bool CEXIEthernetBase::IsWriteCommand(u32 const data)
{
return IsMXCommand(data) ? !!(data & (1 << 30)) : !!(data & (1 << 14));
}
-const char* CEXIETHERNET::GetRegisterName() const
+const char* CEXIEthernetBase::GetRegisterName() const
{
#define STR_RETURN(x) \
case x: \
@@ -285,16 +284,16 @@ const char* CEXIETHERNET::GetRegisterName() const
#undef STR_RETURN
}
-void CEXIETHERNET::MXHardReset()
+void CEXIEthernetBase::MXHardReset()
{
- memset(mBbaMem.get(), 0, BBA_MEM_SIZE);
+ memset(m_bba_mem.get(), 0, BBA_MEM_SIZE);
- mBbaMem[BBA_NCRB] = NCRB_PR;
- mBbaMem[BBA_NWAYC] = NWAYC_LTE | NWAYC_ANE;
- mBbaMem[BBA_MISC] = MISC1_TPF | MISC1_TPH | MISC1_TXF | MISC1_TXH;
+ m_bba_mem[BBA_NCRB] = NCRB_PR;
+ m_bba_mem[BBA_NWAYC] = NWAYC_LTE | NWAYC_ANE;
+ m_bba_mem[BBA_MISC] = MISC1_TPF | MISC1_TPH | MISC1_TXF | MISC1_TXH;
}
-void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
+void CEXIEthernetBase::MXCommandHandler(u32 data, u32 size)
{
switch (transfer.address)
{
@@ -306,7 +305,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
Activate();
}
- if ((mBbaMem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR))
+ if ((m_bba_mem[BBA_NCRA] & NCRA_SR) ^ (data & NCRA_SR))
{
DEBUG_LOG(SP1, "%s rx", (data & NCRA_SR) ? "start" : "stop");
@@ -317,7 +316,7 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
}
// Only start transfer if there isn't one currently running
- if (!(mBbaMem[BBA_NCRA] & (NCRA_ST0 | NCRA_ST1)))
+ if (!(m_bba_mem[BBA_NCRA] & (NCRA_ST0 | NCRA_ST1)))
{
// Technically transfer DMA status is kept in TXDMA - not implemented
@@ -362,20 +361,20 @@ void CEXIETHERNET::MXCommandHandler(u32 data, u32 size)
default:
for (int i = size - 1; i >= 0; i--)
{
- mBbaMem[transfer.address++] = (data >> (i * 8)) & 0xff;
+ m_bba_mem[transfer.address++] = (data >> (i * 8)) & 0xff;
}
return;
}
}
-void CEXIETHERNET::DirectFIFOWrite(const u8* data, u32 size)
+void CEXIEthernetBase::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];
+ u16* tx_fifo_count = (u16*)&m_bba_mem[BBA_TXFIFOCNT];
- memcpy(tx_fifo.get() + *tx_fifo_count, data, size);
+ memcpy(m_tx_fifo.get() + *tx_fifo_count, data, size);
*tx_fifo_count += size;
// TODO: not sure this mask is correct.
@@ -384,33 +383,33 @@ void CEXIETHERNET::DirectFIFOWrite(const u8* data, u32 size)
*tx_fifo_count &= (1 << 12) - 1;
}
-void CEXIETHERNET::SendFromDirectFIFO()
+void CEXIEthernetBase::SendFromDirectFIFO()
{
- SendFrame(tx_fifo.get(), *(u16*)&mBbaMem[BBA_TXFIFOCNT]);
+ SendFrame(m_tx_fifo.get(), *(u16*)&m_bba_mem[BBA_TXFIFOCNT]);
}
-void CEXIETHERNET::SendFromPacketBuffer()
+void CEXIEthernetBase::SendFromPacketBuffer()
{
ERROR_LOG(SP1, "tx packet buffer not implemented.");
}
-void CEXIETHERNET::SendComplete()
+void CEXIEthernetBase::SendComplete()
{
- mBbaMem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
- *(u16*)&mBbaMem[BBA_TXFIFOCNT] = 0;
+ m_bba_mem[BBA_NCRA] &= ~(NCRA_ST0 | NCRA_ST1);
+ *(u16*)&m_bba_mem[BBA_TXFIFOCNT] = 0;
- if (mBbaMem[BBA_IMR] & INT_T)
+ if (m_bba_mem[BBA_IMR] & INT_T)
{
- mBbaMem[BBA_IR] |= INT_T;
+ m_bba_mem[BBA_IR] |= INT_T;
exi_status.interrupt |= exi_status.TRANSFER;
ExpansionInterface::ScheduleUpdateInterrupts(CoreTiming::FromThread::CPU, 0);
}
- mBbaMem[BBA_LTPS] = 0;
+ m_bba_mem[BBA_LTPS] = 0;
}
-inline u8 CEXIETHERNET::HashIndex(const u8* dest_eth_addr)
+inline u8 CEXIEthernetBase::HashIndex(const u8* dest_eth_addr)
{
// Calculate CRC
u32 crc = 0xffffffff;
@@ -432,25 +431,25 @@ inline u8 CEXIETHERNET::HashIndex(const u8* dest_eth_addr)
return crc >> 26;
}
-inline bool CEXIETHERNET::RecvMACFilter()
+inline bool CEXIEthernetBase::RecvMACFilter()
{
static u8 const broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
// Accept all destination addrs?
- if (mBbaMem[BBA_NCRB] & NCRB_PR)
+ if (m_bba_mem[BBA_NCRB] & NCRB_PR)
return true;
// Unicast?
- if ((mRecvBuffer[0] & 0x01) == 0)
+ if ((m_recv_buffer[0] & 0x01) == 0)
{
- return memcmp(mRecvBuffer.get(), &mBbaMem[BBA_NAFR_PAR0], 6) == 0;
+ return memcmp(m_recv_buffer.get(), &m_bba_mem[BBA_NAFR_PAR0], 6) == 0;
}
- else if (memcmp(mRecvBuffer.get(), broadcast, 6) == 0)
+ else if (memcmp(m_recv_buffer.get(), broadcast, 6) == 0)
{
// Accept broadcast?
- return !!(mBbaMem[BBA_NCRB] & NCRB_AB);
+ return !!(m_bba_mem[BBA_NCRB] & NCRB_AB);
}
- else if (mBbaMem[BBA_NCRB] & NCRB_PM)
+ else if (m_bba_mem[BBA_NCRB] & NCRB_PM)
{
// Accept all multicast
return true;
@@ -458,14 +457,14 @@ inline bool CEXIETHERNET::RecvMACFilter()
else
{
// Lookup the dest eth address in the hashmap
- u16 index = HashIndex(mRecvBuffer.get());
- return !!(mBbaMem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8)));
+ u16 index = HashIndex(m_recv_buffer.get());
+ return !!(m_bba_mem[BBA_NAFR_MAR0 + index / 8] & (1 << (index % 8)));
}
}
-inline void CEXIETHERNET::inc_rwp()
+inline void CEXIEthernetBase::inc_rwp()
{
- u16* rwp = (u16*)&mBbaMem[BBA_RWP];
+ u16* rwp = (u16*)&m_bba_mem[BBA_RWP];
if (*rwp + 1 == page_ptr(BBA_RHBP))
*rwp = page_ptr(BBA_BP);
@@ -475,7 +474,7 @@ inline void CEXIETHERNET::inc_rwp()
// 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()
+bool CEXIEthernetBase::RecvHandlePacket()
{
u8* write_ptr;
u8* end_ptr;
@@ -488,23 +487,23 @@ bool CEXIETHERNET::RecvHandlePacket()
goto wait_for_next;
#ifdef BBA_TRACK_PAGE_PTRS
- INFO_LOG(SP1, "RecvHandlePacket %x\n%s", mRecvBufferLength,
- ArrayToString(mRecvBuffer, mRecvBufferLength, 0x100).c_str());
+ INFO_LOG(SP1, "RecvHandlePacket %x\n%s", m_recv_buffer_length,
+ ArrayToString(m_recv_buffer, m_recv_buffer_length, 0x100).c_str());
INFO_LOG(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 = PtrFromPagePtr(BBA_RWP);
+ end_ptr = PtrFromPagePtr(BBA_RHBP);
+ read_ptr = PtrFromPagePtr(BBA_RRP);
descriptor = (Descriptor*)write_ptr;
write_ptr += 4;
- for (u32 i = 0, off = 4; i < mRecvBufferLength; ++i, ++off)
+ for (u32 i = 0, off = 4; i < m_recv_buffer_length; ++i, ++off)
{
- *write_ptr++ = mRecvBuffer[i];
+ *write_ptr++ = m_recv_buffer[i];
if (off == 0xff)
{
@@ -513,7 +512,7 @@ bool CEXIETHERNET::RecvHandlePacket()
}
if (write_ptr == end_ptr)
- write_ptr = ptr_from_page_ptr(BBA_BP);
+ write_ptr = PtrFromPagePtr(BBA_BP);
if (write_ptr == read_ptr)
{
@@ -529,13 +528,13 @@ bool CEXIETHERNET::RecvHandlePacket()
inc MPC instead of receiving packets
*/
status |= DESC_FO | DESC_BF;
- mBbaMem[BBA_IR] |= mBbaMem[BBA_IMR] & INT_RBF;
+ m_bba_mem[BBA_IR] |= m_bba_mem[BBA_IMR] & INT_RBF;
break;
}
}
// Align up to next page
- if ((mRecvBufferLength + 4) % 256)
+ if ((m_recv_buffer_length + 4) % 256)
inc_rwp();
#ifdef BBA_TRACK_PAGE_PTRS
@@ -544,14 +543,14 @@ bool CEXIETHERNET::RecvHandlePacket()
#endif
// Is the current frame multicast?
- if (mRecvBuffer[0] & 0x01)
+ if (m_recv_buffer[0] & 0x01)
status |= DESC_MF;
if (status & DESC_BF)
{
- if (mBbaMem[BBA_MISC2] & MISC2_AUTORCVR)
+ if (m_bba_mem[BBA_MISC2] & MISC2_AUTORCVR)
{
- *(u16*)&mBbaMem[BBA_RWP] = rwp_initial;
+ *(u16*)&m_bba_mem[BBA_RWP] = rwp_initial;
}
else
{
@@ -559,14 +558,14 @@ bool CEXIETHERNET::RecvHandlePacket()
}
}
- descriptor->set(*(u16*)&mBbaMem[BBA_RWP], 4 + mRecvBufferLength, status);
+ descriptor->set(*(u16*)&m_bba_mem[BBA_RWP], 4 + m_recv_buffer_length, status);
- mBbaMem[BBA_LRPS] = status;
+ m_bba_mem[BBA_LRPS] = status;
// Raise interrupt
- if (mBbaMem[BBA_IMR] & INT_R)
+ if (m_bba_mem[BBA_IMR] & INT_R)
{
- mBbaMem[BBA_IR] |= INT_R;
+ m_bba_mem[BBA_IR] |= INT_R;
exi_status.interrupt |= exi_status.TRANSFER;
ExpansionInterface::ScheduleUpdateInterrupts(CoreTiming::FromThread::NON_CPU, 0);
@@ -578,9 +577,9 @@ bool CEXIETHERNET::RecvHandlePacket()
}
wait_for_next:
- if (mBbaMem[BBA_NCRA] & NCRA_SR)
+ if (m_bba_mem[BBA_NCRA] & NCRA_SR)
RecvStart();
return true;
}
-} // namespace ExpansionInterface
+}
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetBase.h
similarity index 79%
rename from Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h
rename to Source/Core/Core/HW/EXI/EXI_DeviceEthernetBase.h
index 38a27bd7e0..9560330b5d 100644
--- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetBase.h
@@ -4,15 +4,6 @@
#pragma once
-#include
-#include
-#include
-
-#ifdef _WIN32
-#include
-#endif
-
-#include "Common/Flag.h"
#include "Core/HW/EXI/EXI_Device.h"
class PointerWrap;
@@ -194,13 +185,11 @@ enum RecvStatus
DESC_RERR = 0x80
};
-#define BBA_RECV_SIZE 0x800
-
-class CEXIETHERNET : public IEXIDevice
+class CEXIEthernetBase : public IEXIDevice
{
public:
- CEXIETHERNET();
- virtual ~CEXIETHERNET();
+ CEXIEthernetBase();
+
void SetCS(int cs) override;
bool IsPresent() const override;
bool IsInterruptSet() override;
@@ -210,7 +199,42 @@ public:
void DMARead(u32 addr, u32 size) override;
void DoState(PointerWrap& p) override;
-private:
+protected:
+ virtual bool Activate() = 0;
+ virtual bool IsActivated() const = 0;
+ virtual bool SendFrame(const u8* frame, u32 size) = 0;
+ virtual bool RecvInit() = 0;
+ virtual void RecvStart() = 0;
+ virtual void RecvStop() = 0;
+
+ inline u16 page_ptr(int const index) const
+ {
+ return ((u16)m_bba_mem[index + 1] << 8) | m_bba_mem[index];
+ }
+
+ inline u8* PtrFromPagePtr(int const index) const { return &m_bba_mem[page_ptr(index) << 8]; }
+ bool IsMXCommand(u32 const data);
+ bool IsWriteCommand(u32 const data);
+ const char* GetRegisterName() const;
+ void MXHardReset();
+ void MXCommandHandler(u32 data, u32 size);
+ void DirectFIFOWrite(const u8* data, u32 size);
+ void SendFromDirectFIFO();
+ void SendFromPacketBuffer();
+ void SendComplete();
+ u8 HashIndex(const u8* dest_eth_addr);
+ bool RecvMACFilter();
+ void inc_rwp();
+ bool RecvHandlePacket();
+
+ std::unique_ptr m_bba_mem;
+ std::unique_ptr m_tx_fifo;
+
+ std::unique_ptr m_recv_buffer;
+ u32 m_recv_buffer_length = 0;
+
+ static constexpr std::size_t BBA_RECV_SIZE = 0x800;
+
struct
{
enum
@@ -273,58 +297,5 @@ private:
word |= next_page & 0xfff;
}
};
-
- inline u16 page_ptr(int const index) const
- {
- 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;
- void MXHardReset();
- void MXCommandHandler(u32 data, u32 size);
- void DirectFIFOWrite(const u8* data, u32 size);
- void SendFromDirectFIFO();
- void SendFromPacketBuffer();
- void SendComplete();
- u8 HashIndex(const u8* dest_eth_addr);
- bool RecvMACFilter();
- void inc_rwp();
- bool RecvHandlePacket();
-
- std::unique_ptr mBbaMem;
- std::unique_ptr tx_fifo;
-
- // TAP interface
- static void ReadThreadHandler(CEXIETHERNET* self);
- bool Activate();
- void Deactivate();
- bool IsActivated();
- bool SendFrame(const u8* frame, u32 size);
- bool RecvInit();
- void RecvStart();
- void RecvStop();
-
- std::unique_ptr mRecvBuffer;
- u32 mRecvBufferLength = 0;
-
-#if defined(_WIN32)
- HANDLE mHAdapter = INVALID_HANDLE_VALUE;
- OVERLAPPED mReadOverlapped = {};
- OVERLAPPED mWriteOverlapped = {};
- std::vector mWriteBuffer;
- bool mWritePending = false;
-#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
- int fd = -1;
-#endif
-
-#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
- defined(__OpenBSD__)
- std::thread readThread;
- Common::Flag readEnabled;
- Common::Flag readThreadShutdown;
-#endif
};
} // namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP.h
new file mode 100644
index 0000000000..47244244d8
--- /dev/null
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP.h
@@ -0,0 +1,52 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+#ifdef _WIN32
+#include
+#include
+#endif
+
+#include "Common/Flag.h"
+#include "EXI_DeviceEthernetBase.h"
+
+namespace ExpansionInterface
+{
+class CEXIEthernetTAP : public CEXIEthernetBase
+{
+public:
+ ~CEXIEthernetTAP() override;
+
+protected:
+ bool Activate() override;
+ bool IsActivated() const override;
+ bool SendFrame(const u8* frame, u32 size) override;
+ bool RecvInit() override;
+ void RecvStart() override;
+ void RecvStop() override;
+
+private:
+ static void ReadThreadHandler(CEXIEthernetTAP* self);
+
+#ifdef _WIN32
+ HANDLE mHAdapter = INVALID_HANDLE_VALUE;
+ OVERLAPPED mReadOverlapped = {};
+ OVERLAPPED mWriteOverlapped = {};
+ std::vector mWriteBuffer;
+ bool mWritePending = false;
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
+ int fd = -1;
+#endif
+
+#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
+ defined(__OpenBSD__)
+ std::thread m_read_thread;
+ Common::Flag m_read_enabled;
+ Common::Flag m_read_thread_shutdown;
+#endif
+};
+} // namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Apple.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Apple.cpp
similarity index 62%
rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Apple.cpp
rename to Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Apple.cpp
index 6dbed04f20..89b1575915 100644
--- a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Apple.cpp
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Apple.cpp
@@ -7,12 +7,22 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
-#include "Core/HW/EXI/EXI_Device.h"
-#include "Core/HW/EXI/EXI_DeviceEthernet.h"
+#include "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
namespace ExpansionInterface
{
-bool CEXIETHERNET::Activate()
+CEXIEthernetTAP::~CEXIEthernetTAP()
+{
+ close(fd);
+ fd = -1;
+
+ m_read_enabled.Clear();
+ m_read_thread_shutdown.Set();
+ if (m_read_thread.joinable())
+ m_read_thread.join();
+}
+
+bool CEXIEthernetTAP::Activate()
{
if (IsActivated())
return true;
@@ -30,23 +40,12 @@ bool CEXIETHERNET::Activate()
return RecvInit();
}
-void CEXIETHERNET::Deactivate()
-{
- close(fd);
- fd = -1;
-
- readEnabled.Clear();
- readThreadShutdown.Set();
- if (readThread.joinable())
- readThread.join();
-}
-
-bool CEXIETHERNET::IsActivated()
+bool CEXIEthernetTAP::IsActivated() const
{
return fd != -1;
}
-bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
+bool CEXIEthernetTAP::SendFrame(const u8* frame, u32 size)
{
INFO_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
@@ -63,9 +62,9 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
}
}
-void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
+void CEXIEthernetTAP::ReadThreadHandler(CEXIEthernetTAP* self)
{
- while (!self->readThreadShutdown.IsSet())
+ while (!self->m_read_thread_shutdown.IsSet())
{
fd_set rfds;
FD_ZERO(&rfds);
@@ -77,34 +76,34 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
continue;
- int readBytes = read(self->fd, self->mRecvBuffer.get(), BBA_RECV_SIZE);
+ int readBytes = read(self->fd, self->m_recv_buffer.get(), BBA_RECV_SIZE);
if (readBytes < 0)
{
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
}
- else if (self->readEnabled.IsSet())
+ else if (self->m_read_enabled.IsSet())
{
INFO_LOG(SP1, "Read data: %s",
- ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str());
- self->mRecvBufferLength = readBytes;
+ ArrayToString(self->m_recv_buffer.get(), readBytes, 0x10).c_str());
+ self->m_recv_buffer_length = readBytes;
self->RecvHandlePacket();
}
}
}
-bool CEXIETHERNET::RecvInit()
+bool CEXIEthernetTAP::RecvInit()
{
- readThread = std::thread(ReadThreadHandler, this);
+ m_read_thread = std::thread(ReadThreadHandler, this);
return true;
}
-void CEXIETHERNET::RecvStart()
+void CEXIEthernetTAP::RecvStart()
{
- readEnabled.Set();
+ m_read_enabled.Set();
}
-void CEXIETHERNET::RecvStop()
+void CEXIEthernetTAP::RecvStop()
{
- readEnabled.Clear();
+ m_read_enabled.Clear();
}
} // namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Unix.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Unix.cpp
similarity index 77%
rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Unix.cpp
rename to Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Unix.cpp
index 05505af9db..cef9171bd4 100644
--- a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Unix.cpp
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Unix.cpp
@@ -11,7 +11,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Core/HW/EXI/EXI_Device.h"
-#include "Core/HW/EXI/EXI_DeviceEthernet.h"
+#include "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
#ifdef __linux__
#include
@@ -28,7 +28,22 @@ namespace ExpansionInterface
#define NOTIMPLEMENTED(Name) \
NOTICE_LOG(SP1, "CEXIETHERNET::%s not implemented for your UNIX", Name);
-bool CEXIETHERNET::Activate()
+CEXIEthernetTAP::~CEXIEthernetTAP()
+{
+#ifdef __linux__
+ close(fd);
+ fd = -1;
+
+ m_read_enabled.Clear();
+ m_read_thread_shutdown.Set();
+ if (m_read_thread.joinable())
+ m_read_thread.join();
+#else
+ NOTIMPLEMENTED("Deactivate");
+#endif
+}
+
+bool CEXIEthernetTAP::Activate()
{
#ifdef __linux__
if (IsActivated())
@@ -78,22 +93,7 @@ bool CEXIETHERNET::Activate()
#endif
}
-void CEXIETHERNET::Deactivate()
-{
-#ifdef __linux__
- close(fd);
- fd = -1;
-
- readEnabled.Clear();
- readThreadShutdown.Set();
- if (readThread.joinable())
- readThread.join();
-#else
- NOTIMPLEMENTED("Deactivate");
-#endif
-}
-
-bool CEXIETHERNET::IsActivated()
+bool CEXIEthernetTAP::IsActivated() const
{
#ifdef __linux__
return fd != -1 ? true : false;
@@ -102,7 +102,7 @@ bool CEXIETHERNET::IsActivated()
#endif
}
-bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
+bool CEXIEthernetTAP::SendFrame(const u8* frame, u32 size)
{
#ifdef __linux__
DEBUG_LOG(SP1, "SendFrame %x\n%s", size, ArrayToString(frame, size, 0x10).c_str());
@@ -124,10 +124,39 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
#endif
}
-#ifdef __linux__
-void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
+bool CEXIEthernetTAP::RecvInit()
{
- while (!self->readThreadShutdown.IsSet())
+#ifdef __linux__
+ m_read_thread = std::thread(ReadThreadHandler, this);
+ return true;
+#else
+ NOTIMPLEMENTED("RecvInit");
+ return false;
+#endif
+}
+
+void CEXIEthernetTAP::RecvStart()
+{
+#ifdef __linux__
+ m_read_enabled.Set();
+#else
+ NOTIMPLEMENTED("RecvStart");
+#endif
+}
+
+void CEXIEthernetTAP::RecvStop()
+{
+#ifdef __linux__
+ m_read_enabled.Clear();
+#else
+ NOTIMPLEMENTED("RecvStop");
+#endif
+}
+
+#ifdef __linux__
+void CEXIEthernetTAP::ReadThreadHandler(CEXIEthernetTAP* self)
+{
+ while (!self->m_read_thread_shutdown.IsSet())
{
fd_set rfds;
FD_ZERO(&rfds);
@@ -139,48 +168,19 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
if (select(self->fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
continue;
- int readBytes = read(self->fd, self->mRecvBuffer.get(), BBA_RECV_SIZE);
+ int readBytes = read(self->fd, self->m_recv_buffer.get(), BBA_RECV_SIZE);
if (readBytes < 0)
{
ERROR_LOG(SP1, "Failed to read from BBA, err=%d", readBytes);
}
- else if (self->readEnabled.IsSet())
+ else if (self->m_read_enabled.IsSet())
{
DEBUG_LOG(SP1, "Read data: %s",
- ArrayToString(self->mRecvBuffer.get(), readBytes, 0x10).c_str());
- self->mRecvBufferLength = readBytes;
+ ArrayToString(self->m_recv_buffer.get(), readBytes, 0x10).c_str());
+ self->m_recv_buffer_length = readBytes;
self->RecvHandlePacket();
}
}
}
#endif
-
-bool CEXIETHERNET::RecvInit()
-{
-#ifdef __linux__
- readThread = std::thread(ReadThreadHandler, this);
- return true;
-#else
- NOTIMPLEMENTED("RecvInit");
- return false;
-#endif
-}
-
-void CEXIETHERNET::RecvStart()
-{
-#ifdef __linux__
- readEnabled.Set();
-#else
- NOTIMPLEMENTED("RecvStart");
-#endif
-}
-
-void CEXIETHERNET::RecvStop()
-{
-#ifdef __linux__
- readEnabled.Clear();
-#else
- NOTIMPLEMENTED("RecvStop");
-#endif
-}
} // namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Win32.cpp
similarity index 90%
rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.cpp
rename to Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Win32.cpp
index 3e8b4d5442..eff81bc104 100644
--- a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.cpp
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTAP_Win32.cpp
@@ -2,13 +2,13 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
-#include "Core/HW/EXI/BBA-TAP/TAP_Win32.h"
+#include "Core/HW/EXI/EXI_DeviceEthernetTAP.h"
+#include "Core/HW/EXI/EXI_DeviceEthernetTAP_Win32.h"
+
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
-#include "Core/HW/EXI/EXI_Device.h"
-#include "Core/HW/EXI/EXI_DeviceEthernet.h"
namespace Win32TAPHelper
{
@@ -167,7 +167,32 @@ bool OpenTAP(HANDLE& adapter, const std::basic_string& device_guid)
namespace ExpansionInterface
{
-bool CEXIETHERNET::Activate()
+CEXIEthernetTAP::~CEXIEthernetTAP()
+{
+ if (!IsActivated())
+ return;
+
+ // Signal read thread to exit.
+ m_read_enabled.Clear();
+ m_read_thread_shutdown.Set();
+
+ // Cancel any outstanding requests from both this thread (writes), and the read thread.
+ CancelIoEx(mHAdapter, nullptr);
+
+ // Wait for read thread to exit.
+ if (m_read_thread.joinable())
+ m_read_thread.join();
+
+ // Clean-up handles
+ CloseHandle(mReadOverlapped.hEvent);
+ CloseHandle(mWriteOverlapped.hEvent);
+ CloseHandle(mHAdapter);
+ mHAdapter = INVALID_HANDLE_VALUE;
+ memset(&mReadOverlapped, 0, sizeof(mReadOverlapped));
+ memset(&mWriteOverlapped, 0, sizeof(mWriteOverlapped));
+}
+
+bool CEXIEthernetTAP::Activate()
{
if (IsActivated())
return true;
@@ -233,44 +258,19 @@ bool CEXIETHERNET::Activate()
return RecvInit();
}
-void CEXIETHERNET::Deactivate()
-{
- if (!IsActivated())
- return;
-
- // Signal read thread to exit.
- readEnabled.Clear();
- readThreadShutdown.Set();
-
- // Cancel any outstanding requests from both this thread (writes), and the read thread.
- CancelIoEx(mHAdapter, nullptr);
-
- // Wait for read thread to exit.
- if (readThread.joinable())
- readThread.join();
-
- // Clean-up handles
- CloseHandle(mReadOverlapped.hEvent);
- CloseHandle(mWriteOverlapped.hEvent);
- CloseHandle(mHAdapter);
- mHAdapter = INVALID_HANDLE_VALUE;
- memset(&mReadOverlapped, 0, sizeof(mReadOverlapped));
- memset(&mWriteOverlapped, 0, sizeof(mWriteOverlapped));
-}
-
-bool CEXIETHERNET::IsActivated()
+bool CEXIEthernetTAP::IsActivated() const
{
return mHAdapter != INVALID_HANDLE_VALUE;
}
-void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
+void CEXIEthernetTAP::ReadThreadHandler(CEXIEthernetTAP* self)
{
- while (!self->readThreadShutdown.IsSet())
+ while (!self->m_read_thread_shutdown.IsSet())
{
DWORD transferred;
// Read from TAP into internal buffer.
- if (ReadFile(self->mHAdapter, self->mRecvBuffer.get(), BBA_RECV_SIZE, &transferred,
+ if (ReadFile(self->mHAdapter, self->m_recv_buffer.get(), BBA_RECV_SIZE, &transferred,
&self->mReadOverlapped))
{
// Returning immediately is not likely to happen, but if so, reset the event state manually.
@@ -300,16 +300,16 @@ void CEXIETHERNET::ReadThreadHandler(CEXIETHERNET* self)
// Copy to BBA buffer, and fire interrupt if enabled.
DEBUG_LOG(SP1, "Received %u bytes:\n %s", transferred,
- ArrayToString(self->mRecvBuffer.get(), transferred, 0x10).c_str());
- if (self->readEnabled.IsSet())
+ ArrayToString(self->m_recv_buffer.get(), transferred, 0x10).c_str());
+ if (self->m_read_enabled.IsSet())
{
- self->mRecvBufferLength = transferred;
+ self->m_recv_buffer_length = transferred;
self->RecvHandlePacket();
}
}
}
-bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
+bool CEXIEthernetTAP::SendFrame(const u8* frame, u32 size)
{
DEBUG_LOG(SP1, "SendFrame %u bytes:\n%s", size, ArrayToString(frame, size, 0x10).c_str());
@@ -349,19 +349,19 @@ bool CEXIETHERNET::SendFrame(const u8* frame, u32 size)
return true;
}
-bool CEXIETHERNET::RecvInit()
+bool CEXIEthernetTAP::RecvInit()
{
- readThread = std::thread(ReadThreadHandler, this);
+ m_read_thread = std::thread(ReadThreadHandler, this);
return true;
}
-void CEXIETHERNET::RecvStart()
+void CEXIEthernetTAP::RecvStart()
{
- readEnabled.Set();
+ m_read_enabled.Set();
}
-void CEXIETHERNET::RecvStop()
+void CEXIEthernetTAP::RecvStop()
{
- readEnabled.Clear();
+ m_read_enabled.Clear();
}
} // namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTCP.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTCP.cpp
new file mode 100644
index 0000000000..c36b15069d
--- /dev/null
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTCP.cpp
@@ -0,0 +1,188 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "Core/HW/EXI/EXI_DeviceEthernetTCP.h"
+
+#include
+#include
+#include
+
+#include
+
+#include "Common/ChunkFile.h"
+#include "Common/CommonTypes.h"
+#include "Common/Logging/Log.h"
+#include "Common/Network.h"
+#include "Common/StringUtil.h"
+#include "Core/ConfigManager.h"
+#include "Core/CoreTiming.h"
+#include "Core/HW/EXI/EXI.h"
+#include "Core/HW/Memmap.h"
+
+namespace ExpansionInterface
+{
+CEXIEthernetTCP::~CEXIEthernetTCP()
+{
+ DEBUG_LOG(SP1, "Ending receive thread...");
+ m_read_enabled.Clear();
+ m_read_thread_shutdown.Set();
+ if (m_read_thread.joinable())
+ m_read_thread.join();
+}
+
+bool CEXIEthernetTCP::Activate()
+{
+ if (IsActivated())
+ return true;
+
+ DEBUG_LOG(SP1, "Connecting...");
+
+ m_socket = std::make_unique();
+ const sf::Socket::Status status = m_socket->connect(SConfig::GetInstance().m_bba_server, SConfig::GetInstance().m_bba_port);
+ if (status != sf::Socket::Done)
+ {
+ ERROR_LOG(SP1, "Could not connect: %i", status);
+ return false;
+ }
+
+ INFO_LOG(SP1, "Connected!");
+
+ return RecvInit();
+}
+
+bool CEXIEthernetTCP::IsActivated() const
+{
+ return m_socket != nullptr;
+}
+
+bool CEXIEthernetTCP::SendFrame(const u8* frame, u32 size)
+{
+ INFO_LOG(SP1, "SendFrame %i", size);
+
+ std::unique_ptr packet = std::make_unique(sizeof(int) + size);
+
+ // prepend size
+ for (std::size_t i = 0; i < sizeof(int); i++)
+ packet[i] = (size >> (i * 8)) & 0xFF;
+
+ memcpy(packet.get() + sizeof(int), frame, size);
+
+ const sf::Socket::Status status = m_socket->send(packet.get(), sizeof(int) + size);
+
+ if (status != sf::Socket::Done)
+ {
+ WARN_LOG(SP1, "Sending failed %i", status);
+ return false;
+ }
+
+ SendComplete();
+ return true;
+}
+
+bool CEXIEthernetTCP::RecvInit()
+{
+ m_read_thread = std::thread(ReadThreadHandler, this);
+ return true;
+}
+
+void CEXIEthernetTCP::RecvStart()
+{
+ m_read_enabled.Set();
+}
+
+void CEXIEthernetTCP::RecvStop()
+{
+ m_read_enabled.Clear();
+}
+
+void CEXIEthernetTCP::ReadThreadHandler(CEXIEthernetTCP* self)
+{
+ sf::SocketSelector selector;
+ selector.add(*self->m_socket);
+
+ // are we currently waiting for size (4 bytes) or payload?
+ enum { StateSize, StatePayload } state = StateSize;
+
+ // buffer to store size temporarily
+ u8 sizeBuffer[sizeof(int)];
+
+ // how much of size or payload have we already received?
+ std::size_t offset = 0;
+
+ // payload size
+ int size;
+
+ while (!self->m_read_thread_shutdown.IsSet())
+ {
+ // blocks until socket has data to read or 100ms passed
+ selector.wait(sf::milliseconds(100));
+
+ std::size_t received;
+ sf::Socket::Status status;
+
+ switch (state)
+ {
+ case StateSize:
+ // try to read remaining bytes for size
+ status = self->m_socket->receive(&sizeBuffer + offset, sizeof(int) - offset, received);
+ if (status != sf::Socket::Done)
+ {
+ ERROR_LOG(SP1, "Receiving failed %i", status);
+ return;
+ }
+
+ DEBUG_LOG(SP1, "Received %lu bytes for size", received);
+
+ // Have we got all bytes for size?
+ offset += received;
+ if(offset < sizeof(int))
+ continue;
+ offset = 0;
+
+ // convert char array to size int
+ size = 0;
+ for(int i = 0; i < sizeof(int); i++)
+ size |= sizeBuffer[i] << (i * 8);
+
+ DEBUG_LOG(SP1, "Finished size %i", size);
+
+ if (size > BBA_RECV_SIZE)
+ {
+ ERROR_LOG(SP1, "Received frame bigger than internal buffer!");
+ return;
+ }
+
+ state = StatePayload;
+ case StatePayload:
+ // try to read remaining bytes for payload
+ status = self->m_socket->receive(self->m_recv_buffer.get() + offset, size - offset, received);
+ if (status != sf::Socket::Done)
+ {
+ ERROR_LOG(SP1, "Receiving failed %i", status);
+ return;
+ }
+
+ DEBUG_LOG(SP1, "Receiving %lu bytes for payload", received);
+
+ // Have we got all bytes for payload?
+ offset += received;
+ if(offset < size)
+ continue;
+ offset = 0;
+
+ INFO_LOG(SP1, "Received payload %i", size);
+
+ if (self->m_read_enabled.IsSet())
+ {
+ self->m_recv_buffer_length = size;
+ self->RecvHandlePacket();
+ }
+
+ state = StateSize;
+ }
+ }
+
+ DEBUG_LOG(SP1, "Receive thread ended!");
+}
+} // namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTCP.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTCP.h
new file mode 100644
index 0000000000..9d284022dc
--- /dev/null
+++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTCP.h
@@ -0,0 +1,38 @@
+// Copyright 2008 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+#include "Common/Flag.h"
+#include "EXI_DeviceEthernetBase.h"
+
+namespace sf { class TcpSocket; }
+
+namespace ExpansionInterface
+{
+class CEXIEthernetTCP : public CEXIEthernetBase
+{
+public:
+ ~CEXIEthernetTCP() override;
+
+protected:
+ bool Activate() override;
+ bool IsActivated() const override;
+ bool SendFrame(const u8* frame, u32 size) override;
+ bool RecvInit() override;
+ void RecvStart() override;
+ void RecvStop() override;
+
+private:
+ static void ReadThreadHandler(CEXIEthernetTCP* self);
+
+ std::unique_ptr m_socket;
+
+ std::thread m_read_thread;
+ Common::Flag m_read_enabled;
+ Common::Flag m_read_thread_shutdown;
+};
+} // namespace ExpansionInterface
diff --git a/Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernetTap_Win32.h
similarity index 100%
rename from Source/Core/Core/HW/EXI/BBA-TAP/TAP_Win32.h
rename to Source/Core/Core/HW/EXI/EXI_DeviceEthernetTap_Win32.h
diff --git a/Source/Core/DolphinQt/BBAClient.cpp b/Source/Core/DolphinQt/BBAClient.cpp
new file mode 100644
index 0000000000..08551d86e8
--- /dev/null
+++ b/Source/Core/DolphinQt/BBAClient.cpp
@@ -0,0 +1,73 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt/BBAClient.h"
+
+#include
+#include
+#include
+
+#include "DolphinQt/BBAServer.h"
+#include "DolphinQt/BBADebug.h"
+
+BBAClient::BBAClient(QTcpSocket &socket, BBAServer &server) :
+ QObject(&server),
+ m_socket(socket),
+ m_server(server),
+ m_state(SocketState::Size)
+{
+ m_socket.setParent(this);
+ connect(&m_socket, &QIODevice::readyRead, this, &BBAClient::ReadyRead);
+ connect(&m_socket, &QAbstractSocket::disconnected, this, [this](){
+ LogInfo() << m_socket.peerAddress() << m_socket.peerPort() << "disconnected";
+ deleteLater();
+ });
+
+ LogInfo() << m_socket.peerAddress() << m_socket.peerPort() << "connected";
+}
+
+BBADebug BBAClient::LogInfo()
+{
+ return m_server.LogInfo();
+}
+
+void BBAClient::SendMessage(const QByteArray &buffer)
+{
+ {
+ QDataStream dataStream(&m_socket);
+ dataStream.setByteOrder(QDataStream::LittleEndian);
+ dataStream << buffer.size();
+ }
+ m_socket.write(buffer);
+}
+
+void BBAClient::ReadyRead()
+{
+ m_buffer.append(m_socket.readAll());
+
+ while (m_buffer.size())
+ {
+ switch(m_state)
+ {
+ case SocketState::Size:
+ if (m_buffer.size() < sizeof(int))
+ return;
+ {
+ QDataStream dataStream(&m_buffer, QIODevice::ReadOnly);
+ dataStream.setByteOrder(QDataStream::LittleEndian);
+ dataStream >> m_size;
+ }
+ m_buffer.remove(0, sizeof(int));
+ m_state = SocketState::Payload;
+ Q_FALLTHROUGH();
+ case SocketState::Payload:
+ if (m_buffer.size() < m_size)
+ return;
+ const auto payload = m_buffer.left(m_size);
+ Q_EMIT ReceivedMessage(payload);
+ m_buffer.remove(0, m_size);
+ m_state = SocketState::Size;
+ }
+ }
+}
diff --git a/Source/Core/DolphinQt/BBAClient.h b/Source/Core/DolphinQt/BBAClient.h
new file mode 100644
index 0000000000..7a515d1483
--- /dev/null
+++ b/Source/Core/DolphinQt/BBAClient.h
@@ -0,0 +1,44 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+class QTcpSocket;
+
+class BBAServer;
+class BBADebug;
+
+class BBAClient : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit BBAClient(QTcpSocket &socket, BBAServer &server);
+
+ BBADebug LogInfo();
+
+signals:
+ void ReceivedMessage(const QByteArray &buffer);
+
+public slots:
+ void SendMessage(const QByteArray &buffer);
+
+private slots:
+ void ReadyRead();
+
+private:
+ enum class SocketState {
+ Size,
+ Payload
+ };
+
+ QTcpSocket &m_socket;
+ BBAServer &m_server;
+
+ QByteArray m_buffer;
+ SocketState m_state;
+ int m_size;
+};
diff --git a/Source/Core/DolphinQt/BBADebug.cpp b/Source/Core/DolphinQt/BBADebug.cpp
new file mode 100644
index 0000000000..80ad39b40a
--- /dev/null
+++ b/Source/Core/DolphinQt/BBADebug.cpp
@@ -0,0 +1,20 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt/BBADebug.h"
+
+#include
+
+#include "DolphinQt/BBAServer.h"
+
+BBADebug::BBADebug(BBAServer &server) :
+ QDebug(&m_log_line),
+ m_server(server)
+{
+}
+
+BBADebug::~BBADebug()
+{
+ m_server.Information(QDateTime::currentDateTime(), m_log_line);
+}
diff --git a/Source/Core/DolphinQt/BBADebug.h b/Source/Core/DolphinQt/BBADebug.h
new file mode 100644
index 0000000000..7e7f30d5fb
--- /dev/null
+++ b/Source/Core/DolphinQt/BBADebug.h
@@ -0,0 +1,20 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+class BBAServer;
+
+class BBADebug : public QDebug
+{
+public:
+ explicit BBADebug(BBAServer &server);
+ ~BBADebug();
+
+private:
+ BBAServer &m_server;
+ QString m_log_line;
+};
diff --git a/Source/Core/DolphinQt/BBAServer.cpp b/Source/Core/DolphinQt/BBAServer.cpp
new file mode 100644
index 0000000000..d9b747c629
--- /dev/null
+++ b/Source/Core/DolphinQt/BBAServer.cpp
@@ -0,0 +1,143 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt/BBAServer.h"
+
+#include
+#include
+#include
+#include
+
+#include "DolphinQt/BBAClient.h"
+#include "DolphinQt/BBADebug.h"
+
+BBAServer::BBAServer(QObject *parent) :
+ QObject(parent),
+ m_server(*new QTcpServer(this))
+{
+ connect(&m_server, &QTcpServer::newConnection, this, &BBAServer::NewConnection);
+}
+
+bool BBAServer::Listen(const QHostAddress &address, quint16 port)
+{
+ const auto result = m_server.listen(address, port);
+ if (result)
+ LogInfo() << "Started listening on" << address << port;
+ else
+ LogInfo() << "Could not start listening on" << address << port << "because" << m_server.errorString();
+ return result;
+}
+
+bool BBAServer::Listen(const QString &address, quint16 port)
+{
+ return Listen(address.isEmpty() ? QHostAddress::Any : QHostAddress(address), port);
+}
+
+void BBAServer::Close()
+{
+ LogInfo() << "Stopped listening";
+ return m_server.close();
+}
+
+bool BBAServer::IsListening() const
+{
+ return m_server.isListening();
+}
+
+void BBAServer::SetMaxPendingConnections(int num_connections)
+{
+ m_server.setMaxPendingConnections(num_connections);
+}
+
+int BBAServer::MaxPendingConnections() const
+{
+ return m_server.maxPendingConnections();
+}
+
+quint16 BBAServer::ServerPort() const
+{
+ return m_server.serverPort();
+}
+
+QHostAddress BBAServer::ServerAddress() const
+{
+ return m_server.serverAddress();
+}
+
+//QAbstractSocket::SocketError BbaServer::ServerError() const
+//{
+// return m_server.serverError();
+//}
+
+QString BBAServer::ErrorString() const
+{
+ return m_server.errorString();
+}
+
+void BBAServer::PauseAccepting()
+{
+ m_server.pauseAccepting();
+}
+
+void BBAServer::ResumeAccepting()
+{
+ m_server.resumeAccepting();
+}
+
+#ifndef QT_NO_NETWORKPROXY
+void BBAServer::SetProxy(const QNetworkProxy &networkProxy)
+{
+ m_server.setProxy(networkProxy);
+}
+
+QNetworkProxy BBAServer::Proxy() const
+{
+ return m_server.proxy();
+}
+#endif
+
+BBADebug BBAServer::LogInfo()
+{
+ return BBADebug(*this);
+}
+
+void BBAServer::timerEvent(QTimerEvent *ev)
+{
+ if (ev->timerId() == m_timer_id)
+ {
+ if (m_counter <= 0)
+ return;
+
+ LogInfo() << m_counter << "packets transmitted";
+ m_counter = 0;
+ }
+ else
+ {
+ QObject::timerEvent(ev);
+ }
+}
+
+void BBAServer::NewConnection()
+{
+ auto* const socket = m_server.nextPendingConnection();
+ if (!socket)
+ return;
+
+ auto client = new BBAClient(*socket, *this);
+ for(auto otherClient : m_clients)
+ {
+ connect(client, &BBAClient::ReceivedMessage, otherClient, &BBAClient::SendMessage);
+ connect(otherClient, &BBAClient::ReceivedMessage, client, &BBAClient::SendMessage);
+ }
+ if (m_timer_id == -1)
+ m_timer_id = startTimer(1000);
+ connect(client, &BBAClient::ReceivedMessage, this, [&counter=m_counter](){ counter++; });
+ connect(client, &QObject::destroyed, this, [client,this](){
+ m_clients.remove(client);
+ if (m_clients.empty() && m_timer_id != -1)
+ killTimer(m_timer_id);
+ m_timer_id = -1;
+ });
+ m_clients.insert(client);
+}
diff --git a/Source/Core/DolphinQt/BBAServer.h b/Source/Core/DolphinQt/BBAServer.h
new file mode 100644
index 0000000000..b6b1df3034
--- /dev/null
+++ b/Source/Core/DolphinQt/BBAServer.h
@@ -0,0 +1,64 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+
+class QHostAddress;
+class QNetworkProxy;
+class QTcpServer;
+
+class BBAClient;
+class BBADebug;
+
+class BBAServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit BBAServer(QObject *parent = nullptr);
+
+ bool Listen(const QHostAddress &address, quint16 port = 0);
+ bool Listen(const QString &address, quint16 port = 0);
+ void Close();
+
+ bool IsListening() const;
+
+ void SetMaxPendingConnections(int num_connections);
+ int MaxPendingConnections() const;
+
+ quint16 ServerPort() const;
+ QHostAddress ServerAddress() const;
+
+ //QAbstractSocket::SocketError ServerError() const; // can't be forward declared
+ QString ErrorString() const;
+
+ void PauseAccepting();
+ void ResumeAccepting();
+
+#ifndef QT_NO_NETWORKPROXY
+ void SetProxy(const QNetworkProxy &networkProxy);
+ QNetworkProxy Proxy() const;
+#endif
+
+ BBADebug LogInfo();
+
+signals:
+ void Information(const QDateTime ×tamp, const QString &message);
+
+protected:
+ void timerEvent(QTimerEvent *ev) override;
+
+private slots:
+ void NewConnection();
+
+private:
+ QSet m_clients;
+ QTcpServer &m_server;
+
+ int m_counter = 0;
+ int m_timer_id = -1;
+};
diff --git a/Source/Core/DolphinQt/BBAServerWindow.cpp b/Source/Core/DolphinQt/BBAServerWindow.cpp
new file mode 100644
index 0000000000..ddf5d26169
--- /dev/null
+++ b/Source/Core/DolphinQt/BBAServerWindow.cpp
@@ -0,0 +1,102 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt/BBAServerWindow.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+BBAServerWindow::BBAServerWindow(QWidget *parent) :
+ QDialog(parent),
+ m_server(this)
+{
+ auto vbox_layout = new QVBoxLayout(this);
+
+ {
+ auto hbox_layout = new QHBoxLayout;
+
+ auto host_addr_label = new QLabel(tr("Host address:"), this);
+ hbox_layout->addWidget(host_addr_label);
+
+ m_host_addr = new QLineEdit(this);
+ host_addr_label->setBuddy(m_host_addr);
+ m_host_addr->setPlaceholderText(tr("Leave empty for Any"));
+ hbox_layout->addWidget(m_host_addr);
+
+ auto port_label = new QLabel(tr("Port:"), this);
+ hbox_layout->addWidget(port_label);
+
+ m_port = new QSpinBox(this);
+ port_label->setBuddy(m_port);
+ m_port->setRange(std::numeric_limits::min(), std::numeric_limits::max());
+ m_port->setValue(50000);
+ hbox_layout->addWidget(m_port);
+
+ m_toggle = new QPushButton(this);
+ connect(m_toggle, &QAbstractButton::pressed, this, &BBAServerWindow::Toggle);
+ hbox_layout->addWidget(m_toggle);
+
+ vbox_layout->addLayout(hbox_layout);
+ }
+
+ m_log_output = new QPlainTextEdit(this);
+ m_log_output->setLineWrapMode(QPlainTextEdit::NoWrap);
+ m_log_output->setReadOnly(true);
+ vbox_layout->addWidget(m_log_output, 1);
+
+ {
+ auto button_box = new QDialogButtonBox(QDialogButtonBox::Close, this);
+ connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ vbox_layout->addWidget(button_box, 1);
+ }
+
+ setLayout(vbox_layout);
+
+ connect(&m_server, &BBAServer::Information, this, &BBAServerWindow::LogOutput);
+
+ Update();
+}
+
+void BBAServerWindow::Toggle()
+{
+ if (m_server.IsListening())
+ {
+ m_server.Close();
+ }
+ else
+ {
+ if (!m_server.Listen(m_host_addr->text(), m_port->value()))
+ QMessageBox::warning(this, tr("Could not start listening!"), tr("Could not start listening:\n\n%0").arg(m_server.ErrorString()));
+ }
+
+ Update();
+}
+
+void BBAServerWindow::LogOutput(const QDateTime ×tamp, const QString &log_line)
+{
+ m_log_output->appendPlainText(QStringLiteral("%0: %1").arg(timestamp.toString(QStringLiteral("HH:mm:ss.zzz")), log_line));
+ auto scrollBar = m_log_output->verticalScrollBar();
+ scrollBar->setValue(scrollBar->maximum());
+}
+
+void BBAServerWindow::Update()
+{
+ const auto is_listening = m_server.IsListening();
+
+ m_host_addr->setEnabled(!is_listening);
+ m_port->setEnabled(!is_listening);
+
+ m_toggle->setText(is_listening ? tr("Stop") : tr("Start"));
+}
diff --git a/Source/Core/DolphinQt/BBAServerWindow.h b/Source/Core/DolphinQt/BBAServerWindow.h
new file mode 100644
index 0000000000..d13484a1e0
--- /dev/null
+++ b/Source/Core/DolphinQt/BBAServerWindow.h
@@ -0,0 +1,35 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+#include "DolphinQt/BBAServer.h"
+
+class QLineEdit;
+class QSpinBox;
+class QPlainTextEdit;
+
+class BBAServerWindow : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit BBAServerWindow(QWidget *parent = nullptr);
+
+private slots:
+ void Toggle();
+ void LogOutput(const QDateTime ×tamp, const QString &log_line);
+
+private:
+ void Update();
+
+ BBAServer m_server;
+
+ QLineEdit *m_host_addr;
+ QSpinBox *m_port;
+ QPushButton *m_toggle;
+
+ QPlainTextEdit *m_log_output;
+};
diff --git a/Source/Core/DolphinQt/BBAServer_main.cpp b/Source/Core/DolphinQt/BBAServer_main.cpp
new file mode 100644
index 0000000000..096828e392
--- /dev/null
+++ b/Source/Core/DolphinQt/BBAServer_main.cpp
@@ -0,0 +1,21 @@
+#include
+#include
+
+#include "DolphinQt/BBAServer.h"
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ BBAServer server(&app);
+
+ QObject::connect(&server, &BBAServer::Information, [](const QDateTime ×tamp, const QString &logLine){
+ Q_UNUSED(timestamp)
+ qInfo().noquote() << logLine;
+ });
+
+ if(!server.Listen(QString(), 50000))
+ return -1;
+
+ return app.exec();
+}
diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index 04100f7465..b847975950 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -1,4 +1,4 @@
-find_package(Qt5 5.9 REQUIRED COMPONENTS Gui Widgets)
+find_package(Qt5 5.9 REQUIRED COMPONENTS Core Network Gui Widgets)
set_property(TARGET Qt5::Core PROPERTY INTERFACE_COMPILE_FEATURES "")
message(STATUS "Found Qt version ${Qt5Core_VERSION}")
@@ -7,6 +7,7 @@ set(CMAKE_AUTOMOC ON)
add_executable(dolphin-emu
AboutDialog.cpp
+ BBAServerWindow.cpp
CheatsManager.cpp
DiscordHandler.cpp
DiscordJoinRequestDialog.cpp
@@ -27,6 +28,7 @@ add_executable(dolphin-emu
WiiUpdate.h
Config/CheatCodeEditor.cpp
Config/ARCodeWidget.cpp
+ Config/BBAConfigWidget.cpp
Config/CheatWarningWidget.cpp
Config/ControllersWindow.cpp
Config/FilesystemWidget.cpp
@@ -146,6 +148,7 @@ PRIVATE
Qt5::Widgets
uicommon
imgui
+ bbaserverlib
)
if(WIN32)
@@ -238,3 +241,11 @@ endif()
if(USE_DISCORD_PRESENCE)
target_compile_definitions(dolphin-emu PRIVATE -DUSE_DISCORD_PRESENCE)
endif()
+
+add_executable(bbaserver BBAServer_main.cpp)
+
+target_link_libraries(bbaserver Qt5::Core Qt5::Network bbaserverlib)
+
+add_library(bbaserverlib BBAClient.cpp BBADebug.cpp BBAServer.cpp)
+
+target_link_libraries(bbaserverlib PUBLIC Qt5::Core PRIVATE Qt5::Network)
diff --git a/Source/Core/DolphinQt/Config/BBAConfigWidget.cpp b/Source/Core/DolphinQt/Config/BBAConfigWidget.cpp
new file mode 100644
index 0000000000..90d9d7cfb3
--- /dev/null
+++ b/Source/Core/DolphinQt/Config/BBAConfigWidget.cpp
@@ -0,0 +1,134 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt/Config/BBAConfigWidget.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "Common/Network.h"
+
+BBAConfigWidget::BBAConfigWidget(bool showServer, QWidget* parent) :
+ QDialog(parent),
+ m_server(nullptr),
+ m_port(nullptr)
+{
+ setWindowTitle(tr("Broadband Adapter Configuration"));
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+ auto vbox_layout = new QVBoxLayout(this);
+
+ {
+ auto form_layout = new QFormLayout;
+
+ {
+ auto hbox_layout = new QHBoxLayout;
+
+ m_mac_addr = new QLineEdit(this);
+ m_mac_addr->setPlaceholderText(tr("Leave empty for random"));
+ connect(m_mac_addr, &QLineEdit::textChanged, this, &BBAConfigWidget::TextChanged);
+ hbox_layout->addWidget(m_mac_addr);
+
+ {
+ auto button = new QToolButton(this);
+ button->setText(tr("Randomize"));
+ connect(button, &QAbstractButton::pressed, this, &BBAConfigWidget::GenerateMac);
+ hbox_layout->addWidget(button);
+ }
+
+ form_layout->addRow(tr("MAC address:"), hbox_layout);
+ }
+
+ if (showServer)
+ {
+ auto hboxLayout = new QHBoxLayout;
+
+ m_server = new QLineEdit(this);
+ hboxLayout->addWidget(m_server);
+
+ auto portLabel = new QLabel(tr("Port:"), this);
+ hboxLayout->addWidget(portLabel);
+
+ m_port = new QSpinBox(this);
+ portLabel->setBuddy(m_port);
+ m_port->setRange(std::numeric_limits::min(), std::numeric_limits::max());
+ hboxLayout->addWidget(m_port);
+
+ form_layout->addRow(tr("Server:"), hboxLayout);
+ }
+
+ vbox_layout->addLayout(form_layout, 1);
+ }
+
+ {
+ auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Close, this);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &BBAConfigWidget::Submit);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ vbox_layout->addWidget(buttonBox, 1);
+ }
+
+ setLayout(vbox_layout);
+}
+
+QString BBAConfigWidget::MacAddr() const
+{
+ return m_mac_addr->text();
+}
+
+void BBAConfigWidget::SetMacAddr(const QString& mac_addr)
+{
+ m_mac_addr->setText(mac_addr);
+}
+
+QString BBAConfigWidget::Server() const
+{
+ return m_server ? m_server->text() : QString();
+}
+
+void BBAConfigWidget::SetServer(const QString& server)
+{
+ m_server->setText(server);
+}
+
+quint16 BBAConfigWidget::Port() const
+{
+ return m_port ? m_port->value() : 0;
+}
+
+void BBAConfigWidget::SetPort(quint16 port)
+{
+ m_port->setValue(port);
+}
+
+void BBAConfigWidget::Submit()
+{
+ if(!MacAddr().isEmpty() && !Common::StringToMacAddress(MacAddr().toStdString()))
+ {
+ QMessageBox::warning(this, tr("Invalid MAC address!"), tr("Invalid MAC address!"));
+ return;
+ }
+
+ accept();
+}
+
+void BBAConfigWidget::GenerateMac()
+{
+ m_mac_addr->setText(QString::fromStdString(Common::MacAddressToString(Common::GenerateMacAddress(Common::MACConsumer::BBA))));
+}
+
+void BBAConfigWidget::TextChanged(const QString& text)
+{
+ QString inputMask;
+ if(!text.isEmpty() && text != QStringLiteral(":::::"))
+ inputMask = QStringLiteral("HH:HH:HH:HH:HH:HH;_");
+ if (m_mac_addr->inputMask() != inputMask)
+ m_mac_addr->setInputMask(inputMask);
+}
diff --git a/Source/Core/DolphinQt/Config/BBAConfigWidget.h b/Source/Core/DolphinQt/Config/BBAConfigWidget.h
new file mode 100644
index 0000000000..36d70aa93b
--- /dev/null
+++ b/Source/Core/DolphinQt/Config/BBAConfigWidget.h
@@ -0,0 +1,36 @@
+// Copyright 2019 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+
+class QLineEdit;
+class QSpinBox;
+
+class BBAConfigWidget : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit BBAConfigWidget(bool showServer, QWidget* parent = nullptr);
+
+ QString MacAddr() const;
+ void SetMacAddr(const QString& mac_addr);
+
+ QString Server() const;
+ void SetServer(const QString& server);
+
+ quint16 Port() const;
+ void SetPort(quint16 port);
+
+private slots:
+ void Submit();
+ void GenerateMac();
+ void TextChanged(const QString& text);
+
+private:
+ QLineEdit *m_mac_addr;
+ QLineEdit *m_server;
+ QSpinBox *m_port;
+};
diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj
index 0dde566e5a..497f8a0656 100644
--- a/Source/Core/DolphinQt/DolphinQt.vcxproj
+++ b/Source/Core/DolphinQt/DolphinQt.vcxproj
@@ -57,9 +57,11 @@
+
+
@@ -172,6 +174,8 @@
+
+
@@ -273,9 +277,11 @@
+
+
@@ -491,4 +497,4 @@
-
\ No newline at end of file
+
diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp
index b615e6a693..be17908dd4 100644
--- a/Source/Core/DolphinQt/MainWindow.cpp
+++ b/Source/Core/DolphinQt/MainWindow.cpp
@@ -59,6 +59,7 @@
#include "DiscIO/NANDImporter.h"
#include "DolphinQt/AboutDialog.h"
+#include "DolphinQt/BBAServerWindow.h"
#include "DolphinQt/CheatsManager.h"
#include "DolphinQt/Config/ControllersWindow.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h"
@@ -438,6 +439,9 @@ void MainWindow::ConnectMenuBar()
connect(m_menu_bar, &MenuBar::BootWiiSystemMenu, this, &MainWindow::BootWiiSystemMenu);
connect(m_menu_bar, &MenuBar::StartNetPlay, this, &MainWindow::ShowNetPlaySetupDialog);
connect(m_menu_bar, &MenuBar::ShowFIFOPlayer, this, &MainWindow::ShowFIFOPlayer);
+#ifndef _WIN32
+ connect(m_menu_bar, &MenuBar::StartBbaServer, this, &MainWindow::StartBbaServer);
+#endif
connect(m_menu_bar, &MenuBar::ConnectWiiRemote, this, &MainWindow::OnConnectWiiRemote);
// Movie
@@ -1127,6 +1131,18 @@ void MainWindow::ShowFIFOPlayer()
m_fifo_window->activateWindow();
}
+#ifndef _WIN32
+void MainWindow::StartBbaServer()
+{
+ if (!m_bba_server_window)
+ m_bba_server_window = new BBAServerWindow(this);
+
+ m_bba_server_window->show();
+ m_bba_server_window->raise();
+ m_bba_server_window->activateWindow();
+}
+#endif
+
void MainWindow::StateLoad()
{
QString path = QFileDialog::getOpenFileName(this, tr("Select a File"), QDir::currentPath(),
diff --git a/Source/Core/DolphinQt/MainWindow.h b/Source/Core/DolphinQt/MainWindow.h
index 4c5224fb6a..d6c34c368b 100644
--- a/Source/Core/DolphinQt/MainWindow.h
+++ b/Source/Core/DolphinQt/MainWindow.h
@@ -23,6 +23,7 @@ class ControllersWindow;
class DiscordHandler;
class DragEnterEvent;
class FIFOPlayerWindow;
+class BBAServerWindow;
class GameList;
class GCTASInputWindow;
class GraphicsWindow;
@@ -148,6 +149,9 @@ private:
void ShowHotkeyDialog();
void ShowNetPlaySetupDialog();
void ShowFIFOPlayer();
+#ifndef _WIN32
+ void StartBbaServer();
+#endif
void ShowMemcardManager();
void ShowResourcePackManager();
void ShowCheatsManager();
@@ -204,6 +208,9 @@ private:
SettingsWindow* m_settings_window = nullptr;
GraphicsWindow* m_graphics_window = nullptr;
FIFOPlayerWindow* m_fifo_window = nullptr;
+#ifndef _WIN32
+ BBAServerWindow* m_bba_server_window = nullptr;
+#endif
MappingWindow* m_hotkey_window = nullptr;
HotkeyScheduler* m_hotkey_scheduler;
diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp
index e533ab73ae..7c5c12f172 100644
--- a/Source/Core/DolphinQt/MenuBar.cpp
+++ b/Source/Core/DolphinQt/MenuBar.cpp
@@ -240,7 +240,9 @@ void MenuBar::AddToolsMenu()
tools_menu->addAction(tr("Start &NetPlay..."), this, &MenuBar::StartNetPlay);
tools_menu->addAction(tr("FIFO Player"), this, &MenuBar::ShowFIFOPlayer);
-
+#ifndef _WIN32
+ tools_menu->addAction(tr("&Broadband Adapter Server"), this, &MenuBar::StartBbaServer);
+#endif
tools_menu->addSeparator();
// Label will be set by a NANDRefresh later
diff --git a/Source/Core/DolphinQt/MenuBar.h b/Source/Core/DolphinQt/MenuBar.h
index 1d731d240a..978d0960be 100644
--- a/Source/Core/DolphinQt/MenuBar.h
+++ b/Source/Core/DolphinQt/MenuBar.h
@@ -62,6 +62,7 @@ signals:
void FrameAdvance();
void Screenshot();
void StartNetPlay();
+ void StartBbaServer();
void StateLoad();
void StateSave();
void StateLoadSlot();
diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp
index 69beebf9db..36ddb7f504 100644
--- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp
+++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp
@@ -28,6 +28,7 @@
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GCMemcard/GCMemcard.h"
+#include "DolphinQt/Config/BBAConfigWidget.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h"
enum
@@ -104,7 +105,8 @@ void GameCubePane::CreateWidgets()
for (const auto& entry :
{std::make_pair(tr(""), ExpansionInterface::EXIDEVICE_NONE),
std::make_pair(tr("Dummy"), ExpansionInterface::EXIDEVICE_DUMMY),
- std::make_pair(tr("Broadband Adapter"), ExpansionInterface::EXIDEVICE_ETH)})
+ std::make_pair(tr("Broadband Adapter (TAP)"), ExpansionInterface::EXIDEVICE_ETH_TAP),
+ std::make_pair(tr("Broadband Adapter (TCP)"), ExpansionInterface::EXIDEVICE_ETH_TCP)})
{
m_slot_combos[2]->addItem(entry.first, entry.second);
}
@@ -160,7 +162,8 @@ void GameCubePane::UpdateButton(int slot)
value == ExpansionInterface::EXIDEVICE_AGP || value == ExpansionInterface::EXIDEVICE_MIC);
break;
case SLOT_SP1_INDEX:
- has_config = (value == ExpansionInterface::EXIDEVICE_ETH);
+ has_config = (value == ExpansionInterface::EXIDEVICE_ETH_TAP ||
+ value == ExpansionInterface::EXIDEVICE_ETH_TCP);
break;
}
@@ -172,7 +175,9 @@ void GameCubePane::OnConfigPressed(int slot)
QString filter;
bool memcard = false;
- switch (m_slot_combos[slot]->currentData().toInt())
+ const auto currentData = m_slot_combos[slot]->currentData().toInt();
+
+ switch (currentData)
{
case ExpansionInterface::EXIDEVICE_MEMORYCARD:
filter = tr("GameCube Memory Cards (*.raw *.gcp)");
@@ -184,14 +189,26 @@ void GameCubePane::OnConfigPressed(int slot)
case ExpansionInterface::EXIDEVICE_MIC:
MappingWindow(this, MappingWindow::Type::MAPPING_GC_MICROPHONE, slot).exec();
return;
- case ExpansionInterface::EXIDEVICE_ETH:
+ case ExpansionInterface::EXIDEVICE_ETH_TAP:
+ case ExpansionInterface::EXIDEVICE_ETH_TCP:
{
- bool ok;
- const auto new_mac = QInputDialog::getText(
- this, tr("Broadband Adapter MAC address"), tr("Enter new Broadband Adapter MAC address:"),
- QLineEdit::Normal, QString::fromStdString(SConfig::GetInstance().m_bba_mac), &ok);
- if (ok)
- SConfig::GetInstance().m_bba_mac = new_mac.toStdString();
+ const auto isTcp = currentData == ExpansionInterface::EXIDEVICE_ETH_TCP;
+ BBAConfigWidget dialog(isTcp, this);
+ dialog.SetMacAddr(QString::fromStdString(SConfig::GetInstance().m_bba_mac));
+ if (isTcp)
+ {
+ dialog.SetServer(QString::fromStdString(SConfig::GetInstance().m_bba_server));
+ dialog.SetPort(SConfig::GetInstance().m_bba_port);
+ }
+ if(dialog.exec() == QDialog::Accepted)
+ {
+ SConfig::GetInstance().m_bba_mac = dialog.MacAddr().toStdString();
+ if (isTcp)
+ {
+ SConfig::GetInstance().m_bba_server = dialog.Server().toStdString();
+ SConfig::GetInstance().m_bba_port = dialog.Port();
+ }
+ }
return;
}
default:
@@ -200,7 +217,7 @@ void GameCubePane::OnConfigPressed(int slot)
QString filename = QFileDialog::getSaveFileName(
this, tr("Choose a file to open"), QString::fromStdString(File::GetUserPath(D_GCUSER_IDX)),
- filter, 0, QFileDialog::DontConfirmOverwrite);
+ filter, nullptr, QFileDialog::DontConfirmOverwrite);
if (filename.isEmpty())
return;