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;