diff --git a/.gitmodules b/.gitmodules index deef328f15..b7de90145e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -114,6 +114,10 @@ path = Externals/pugixml/pugixml url = https://github.com/zeux/pugixml.git shallow = true +[submodule "Externals/cpp-ipc/cpp-ipc"] + path = Externals/cpp-ipc/cpp-ipc + url = https://github.com/mutouyun/cpp-ipc.git + shallow = true [submodule "Externals/cpp-optparse/cpp-optparse"] path = Externals/cpp-optparse/cpp-optparse url = https://github.com/weisslj/cpp-optparse.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c708e9987..2f4572f5f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -793,6 +793,10 @@ endif() add_subdirectory(Externals/watcher) +if(WIN32 OR LINUX) + add_subdirectory(Externals/cpp-ipc) +endif() + ######################################## # Pre-build events: Define configuration variables and write SCM info header # diff --git a/Externals/cpp-ipc/CMakeLists.txt b/Externals/cpp-ipc/CMakeLists.txt new file mode 100644 index 0000000000..ab776d4f89 --- /dev/null +++ b/Externals/cpp-ipc/CMakeLists.txt @@ -0,0 +1,9 @@ +add_subdirectory(cpp-ipc) + +dolphin_disable_warnings(ipc) + +if (NOT MSVC) + target_compile_options(ipc PRIVATE "-fexceptions") +endif () + +add_library(cpp-ipc::ipc ALIAS ipc) diff --git a/Externals/cpp-ipc/cpp-ipc b/Externals/cpp-ipc/cpp-ipc new file mode 160000 index 0000000000..a0c7725a14 --- /dev/null +++ b/Externals/cpp-ipc/cpp-ipc @@ -0,0 +1 @@ +Subproject commit a0c7725a1441d18bc768d748a93e512a0fa7ab52 diff --git a/Externals/cpp-ipc/cpp-ipc.vcxproj b/Externals/cpp-ipc/cpp-ipc.vcxproj new file mode 100644 index 0000000000..61dc5eda6e --- /dev/null +++ b/Externals/cpp-ipc/cpp-ipc.vcxproj @@ -0,0 +1,78 @@ + + + + + + {7299DDD3-BBEC-4027-AF30-8DACC5415F96} + + + + + + + + + + + + + + cpp-ipc\include;cpp-ipc\src;%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Externals/cpp-ipc/exports.props b/Externals/cpp-ipc/exports.props new file mode 100644 index 0000000000..bbcdc43b66 --- /dev/null +++ b/Externals/cpp-ipc/exports.props @@ -0,0 +1,13 @@ + + + + + $(ExternalsDir)cpp-ipc\cpp-ipc\include;%(AdditionalIncludeDirectories) + + + + + {7299DDD3-BBEC-4027-AF30-8DACC5415F96} + + + diff --git a/Externals/licenses.md b/Externals/licenses.md index b3d1913fd3..727a41e2a1 100644 --- a/Externals/licenses.md +++ b/Externals/licenses.md @@ -8,6 +8,8 @@ Dolphin includes or links code of the following third-party software projects: [LGPLv2.1+](http://bochs.sourceforge.net/cgi-bin/lxr/source/COPYING) - [bzip2](https://www.sourceware.org/bzip2/): [bzip2 license](https://www.sourceware.org/git/?p=bzip2.git;a=blob;f=LICENSE;hb=HEAD) (similar to 3-clause BSD) +- [cpp-ipc](https://github.com/mutouyun/cpp-ipc): + [MIT](https://github.com/mutouyun/cpp-ipc/blob/master/LICENSE) - [cubeb](https://github.com/kinetiknz/cubeb): [ISC](https://github.com/kinetiknz/cubeb/blob/master/LICENSE) - [Discord-RPC](https://github.com/discordapp/discord-rpc): diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 6e7ff1a0bc..cb2a3ac58a 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -797,6 +797,11 @@ if(UNIX) ) endif() +if(WIN32 OR LINUX) + target_sources(core PRIVATE HW/EXI/BBA/IPC.cpp) + target_link_libraries(core PRIVATE cpp-ipc::ipc) +endif() + if(MSVC) # Add precompiled header target_link_libraries(core PRIVATE use_pch) diff --git a/Source/Core/Core/HW/EXI/BBA/IPC.cpp b/Source/Core/Core/HW/EXI/BBA/IPC.cpp new file mode 100644 index 0000000000..e23795e7eb --- /dev/null +++ b/Source/Core/Core/HW/EXI/BBA/IPC.cpp @@ -0,0 +1,101 @@ +// Copyright 2025 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include + +#include "Common/Logging/Log.h" +#include "Core/HW/EXI/EXI_DeviceEthernet.h" + +namespace ExpansionInterface +{ + +bool CEXIETHERNET::IPCBBAInterface::Activate() +{ + if (m_active) + return false; + + m_channel.connect("dolphin-emu-bba-ipc", ipc::sender | ipc::receiver); + if (!m_channel.valid()) + return false; + + m_read_enabled.Clear(); + m_read_thread_shutdown.Clear(); + + m_active = true; + + return RecvInit(); +} + +void CEXIETHERNET::IPCBBAInterface::Deactivate() +{ + m_read_enabled.Clear(); + m_read_thread_shutdown.Set(); + + if (m_read_thread.joinable()) + { + m_read_thread.join(); + } + + m_channel.disconnect(); + + m_active = false; +} + +bool CEXIETHERNET::IPCBBAInterface::IsActivated() +{ + return m_active; +} + +bool CEXIETHERNET::IPCBBAInterface::SendFrame(const u8* const frame, const u32 size) +{ + if (!m_active) + return false; + + static constexpr u64 TIMEOUT_IN_MS{3000}; + if (!m_channel.send(frame, size, TIMEOUT_IN_MS)) + { + ERROR_LOG_FMT(SP1, "Failed to send frame"); + return false; + } + + m_eth_ref->SendComplete(); + + return true; +} + +bool CEXIETHERNET::IPCBBAInterface::RecvInit() +{ + m_read_thread = std::thread(&CEXIETHERNET::IPCBBAInterface::ReadThreadHandler, this); + return true; +} + +void CEXIETHERNET::IPCBBAInterface::RecvStart() +{ + m_read_enabled.Set(); +} + +void CEXIETHERNET::IPCBBAInterface::RecvStop() +{ + m_read_enabled.Clear(); +} + +void CEXIETHERNET::IPCBBAInterface::ReadThreadHandler() +{ + while (!m_read_thread_shutdown.IsSet()) + { + const ipc::buff_t buffer{m_channel.recv(50)}; + if (buffer.empty() || !m_read_enabled.IsSet()) + continue; + + const u8* const frame{reinterpret_cast(buffer.data())}; + const u64 size{buffer.size()}; + + std::memcpy(m_eth_ref->mRecvBuffer.get(), frame, size); + m_eth_ref->mRecvBufferLength = static_cast(size); + m_eth_ref->RecvHandlePacket(); + } +} + +} // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp index ac92c7b723..c1300a363f 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp @@ -160,6 +160,10 @@ std::unique_ptr EXIDevice_Create(Core::System& system, const EXIDevi result = std::make_unique(system, BBADeviceType::BuiltIn); break; + case EXIDeviceType::EthernetIPC: + result = std::make_unique(system, BBADeviceType::IPC); + break; + case EXIDeviceType::ModemTapServer: result = std::make_unique(system, ModemDeviceType::TAPSERVER); break; diff --git a/Source/Core/Core/HW/EXI/EXI_Device.h b/Source/Core/Core/HW/EXI/EXI_Device.h index 25263af425..129f7a2308 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.h +++ b/Source/Core/Core/HW/EXI/EXI_Device.h @@ -40,6 +40,7 @@ enum class EXIDeviceType : int EthernetTapServer, EthernetBuiltIn, ModemTapServer, + EthernetIPC, None = 0xFF }; @@ -86,7 +87,7 @@ std::unique_ptr EXIDevice_Create(Core::System& system, EXIDeviceType template <> struct fmt::formatter - : EnumFormatter + : EnumFormatter { static constexpr array_type names = { _trans("Dummy"), @@ -104,6 +105,7 @@ struct fmt::formatter _trans("Broadband Adapter (tapserver)"), _trans("Broadband Adapter (HLE)"), _trans("Modem Adapter (tapserver)"), + _trans("Broadband Adapter (IPC)"), }; constexpr formatter() : EnumFormatter(names) {} diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp index 202dc557f8..311f1e2226 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.cpp @@ -60,6 +60,11 @@ CEXIETHERNET::CEXIETHERNET(Core::System& system, BBADeviceType type) : IEXIDevic this, Config::Get(Config::MAIN_BBA_BUILTIN_DNS), Config::Get(Config::MAIN_BBA_BUILTIN_IP)); INFO_LOG_FMT(SP1, "Created Built in network interface."); break; + case BBADeviceType::IPC: + mac_addr = Common::GenerateMacAddress(Common::MACConsumer::BBA); // Always randomize + m_network_interface = std::make_unique(this); + INFO_LOG_FMT(SP1, "Created IPC-based network interface."); + break; case BBADeviceType::XLINK: // TODO start BBA with network link down, bring it up after "connected" response from XLink diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h index 21852f341c..67a0317831 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceEthernet.h @@ -15,6 +15,9 @@ #endif #include +#if defined(WIN32) || (defined(__linux__) && !defined(__ANDROID__)) +#include +#endif #include "Common/Flag.h" #include "Common/Network.h" @@ -210,6 +213,7 @@ enum class BBADeviceType XLINK, TAPSERVER, BuiltIn, + IPC, }; class CEXIETHERNET : public IEXIDevice @@ -474,6 +478,43 @@ private: const Common::MACAddress& ResolveAddress(u32 inet_ip); }; + class IPCBBAInterface : public NetworkInterface + { + public: + explicit IPCBBAInterface(CEXIETHERNET* const eth_ref) : NetworkInterface(eth_ref) {} + +#if defined(WIN32) || (defined(__linux__) && !defined(__ANDROID__)) + + bool Activate() override; + void Deactivate() override; + bool IsActivated() override; + bool SendFrame(const u8* frame, u32 size) override; + bool RecvInit() override; + void RecvStart() override; + void RecvStop() override; + + private: + void ReadThreadHandler(); + + bool m_active{}; + ipc::channel m_channel; + std::thread m_read_thread; + Common::Flag m_read_enabled; + Common::Flag m_read_thread_shutdown; + +#else + + bool Activate() override { return false; } + void Deactivate() override {} + bool IsActivated() override { return false; } + bool SendFrame(const u8* const frame, const u32 size) override { return false; } + bool RecvInit() override { return false; } + void RecvStart() override {} + void RecvStop() override {} + +#endif + }; + std::unique_ptr m_network_interface; std::unique_ptr mRecvBuffer; diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 062d7f979a..35d170a0d5 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -962,6 +962,7 @@ + diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index 0d4925c47e..104b2d86ed 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -30,6 +30,7 @@ + diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index c84475600e..3df220243b 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -143,6 +143,9 @@ void GameCubePane::CreateWidgets() EXIDeviceType::EthernetXLink, EXIDeviceType::EthernetTapServer, EXIDeviceType::EthernetBuiltIn, +#if defined(WIN32) || (defined(__linux__) && !defined(__ANDROID__)) + EXIDeviceType::EthernetIPC, +#endif EXIDeviceType::ModemTapServer, }) { diff --git a/Source/dolphin-emu.sln b/Source/dolphin-emu.sln index f1183c287f..64650e330f 100644 --- a/Source/dolphin-emu.sln +++ b/Source/dolphin-emu.sln @@ -49,6 +49,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "curl", "..\Externals\curl\c EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glslang", "..\Externals\glslang\glslang.vcxproj", "{D178061B-84D3-44F9-BEED-EFD18D9033F0}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpp-ipc", "..\Externals\cpp-ipc\cpp-ipc.vcxproj", "{7299DDD3-BBEC-4027-AF30-8DACC5415F96}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpp-optparse", "..\Externals\cpp-optparse\cpp-optparse.vcxproj", "{C636D9D1-82FE-42B5-9987-63B7D4836341}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeb", "..\Externals\cubeb\msvc\cubeb.vcxproj", "{8EA11166-6512-44FC-B7A5-A4D1ECC81170}" @@ -271,6 +273,14 @@ Global {D178061B-84D3-44F9-BEED-EFD18D9033F0}.Release|ARM64.Build.0 = Release|ARM64 {D178061B-84D3-44F9-BEED-EFD18D9033F0}.Release|x64.ActiveCfg = Release|x64 {D178061B-84D3-44F9-BEED-EFD18D9033F0}.Release|x64.Build.0 = Release|x64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Debug|ARM64.Build.0 = Debug|ARM64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Debug|x64.ActiveCfg = Debug|x64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Debug|x64.Build.0 = Debug|x64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Release|ARM64.ActiveCfg = Release|ARM64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Release|ARM64.Build.0 = Release|ARM64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Release|x64.ActiveCfg = Release|x64 + {7299DDD3-BBEC-4027-AF30-8DACC5415F96}.Release|x64.Build.0 = Release|x64 {C636D9D1-82FE-42B5-9987-63B7D4836341}.Debug|ARM64.ActiveCfg = Debug|ARM64 {C636D9D1-82FE-42B5-9987-63B7D4836341}.Debug|ARM64.Build.0 = Debug|ARM64 {C636D9D1-82FE-42B5-9987-63B7D4836341}.Debug|x64.ActiveCfg = Debug|x64 @@ -456,6 +466,7 @@ Global {CBC76802-C128-4B17-BF6C-23B08C313E5E} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {BB00605C-125F-4A21-B33B-7BF418322DCB} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {D178061B-84D3-44F9-BEED-EFD18D9033F0} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} + {7299DDD3-BBEC-4027-AF30-8DACC5415F96} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {C636D9D1-82FE-42B5-9987-63B7D4836341} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {8EA11166-6512-44FC-B7A5-A4D1ECC81170} = {87ADDFF9-5768-4DA2-A33B-2477593D6677} {38FEE76F-F347-484B-949C-B4649381CFFB} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}