naomi: network code for model 3 comm board
This commit is contained in:
parent
6215623640
commit
a41a81f5dd
|
@ -12,7 +12,7 @@ RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ \
|
|||
hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \
|
||||
hw/extdev/ hw/arm/ hw/naomi/ imgread/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ \
|
||||
deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/xbrz/ \
|
||||
deps/libzip/ deps/imgui/ archive/ input/ log/ wsi/
|
||||
deps/libzip/ deps/imgui/ archive/ input/ log/ wsi/ network/
|
||||
|
||||
ifndef NOT_ARM
|
||||
RZDCY_MODULES += rec-ARM/
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
# Reicast Modem Configuration
|
||||
|
||||
You need to assign an IP address from your local LAN to the Dreamcast. Do not use the address of the computer on which reicast is running.
|
||||
Add it to your `emu.cfg` file in the `[network]` section.
|
||||
|
||||
For example:
|
||||
```
|
||||
[network]
|
||||
IP = 192.168.1.99
|
||||
```
|
||||
Make sure this address is not being used on your network. Ping it just in case...
|
||||
|
||||
## Windows (7)
|
||||
1. Install TAP-Windows
|
||||
|
||||
Navigate to [OpenVPN Community Downloads](https://openvpn.net/index.php/download/community-downloads.html) and scroll all the way to the bottom. Download and install the NDIS 6 version.
|
||||
|
||||
2. In your Network Connections you should see a new network with a TAP-Windows Adapter V9 device.
|
||||
*Make sure it is enabled.*
|
||||
|
||||
3. Run `reicast.exe` as an administrator (right-click on the program and select "Run as administrator")
|
||||
|
||||
4. That's it! Now you need to configure internet access in the Dreamcast itself.
|
||||
|
||||
## Linux
|
||||
All these commands must be run as root.
|
||||
|
||||
1. Create the IP tunnel. If your username is not joe, put your username there.
|
||||
```
|
||||
# ip tuntap add mode tun user joe
|
||||
```
|
||||
|
||||
2. Bring the interface up
|
||||
```
|
||||
# ip link set tun0 up
|
||||
```
|
||||
|
||||
3. Add a route to this IP.
|
||||
```
|
||||
# ip route add 192.168.1.99/32 dev tun0
|
||||
```
|
||||
Replace 192.168.1.99 by the IP set in your emu.cfg file.
|
||||
|
||||
4. Enable proxy ARP for all interfaces.
|
||||
```
|
||||
# echo '1' >/proc/sys/net/ipv4/conf/all/proxy_arp
|
||||
```
|
||||
5. Follow up to the next section
|
||||
|
||||
|
||||
## Dreamcast
|
||||
You need to configure the ISP settings in the Dreamcast. Some games allow to do it within the game itself and will save the configuration. Other games can then use it.
|
||||
|
||||
You can put any name, password and phone number in the ISP settings as they are ignored. Do not change or set any other option.
|
||||
|
||||
Some games require a DMZ or port forwarding to be configured on your Internet router. Refer to the [Dreamcast Live web site](https://www.dreamcastlive.net/connection-guide.html) for details about each game.
|
|
@ -25,7 +25,7 @@
|
|||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
|
||||
#include "net_platform.h"
|
||||
#include "network/net_platform.h"
|
||||
|
||||
extern "C" {
|
||||
#include <pico_stack.h>
|
||||
|
@ -44,6 +44,7 @@ static int qname_len;
|
|||
|
||||
void get_host_by_name(const char *host, struct pico_ip4 dnsaddr)
|
||||
{
|
||||
DEBUG_LOG(MODEM, "get_host_by_name: %s", host);
|
||||
if (!VALID(sock_fd))
|
||||
{
|
||||
sock_fd = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP);
|
||||
|
@ -149,4 +150,4 @@ char *read_name(char *reader, char *buffer, int *count)
|
|||
return name;
|
||||
}
|
||||
|
||||
#endif // !COMPILER_VC_OR_CLANG_WIN32
|
||||
#endif // !COMPILER_VC_OR_CLANG_WIN32
|
||||
|
|
|
@ -36,7 +36,7 @@ extern "C" {
|
|||
#include <pico_tcp.h>
|
||||
}
|
||||
|
||||
#include "net_platform.h"
|
||||
#include "network/net_platform.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "cfg/cfg.h"
|
||||
|
@ -165,31 +165,6 @@ int read_pico()
|
|||
}
|
||||
}
|
||||
|
||||
void set_non_blocking(sock_t fd)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
fcntl(fd, F_SETFL, O_NONBLOCK);
|
||||
#else
|
||||
u_long optl = 1;
|
||||
ioctlsocket(fd, FIONBIO, &optl);
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_tcp_nodelay(sock_t fd)
|
||||
{
|
||||
int optval = 1;
|
||||
socklen_t optlen = sizeof(optval);
|
||||
#if defined(_WIN32)
|
||||
struct protoent *tcp_proto = getprotobyname("TCP");
|
||||
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, (const char *)&optval, optlen);
|
||||
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
|
||||
setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
|
||||
#else
|
||||
struct protoent *tcp_proto = getprotobyname("TCP");
|
||||
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void read_from_dc_socket(pico_socket *pico_sock, sock_t nat_sock)
|
||||
{
|
||||
char buf[1510];
|
||||
|
@ -716,8 +691,7 @@ static void *pico_thread_func(void *)
|
|||
pico_string_to_ipv4("192.168.167.1", &ipaddr.addr);
|
||||
pico_ppp_set_ip(ppp, ipaddr);
|
||||
|
||||
std::string dns_ip = cfgLoadStr("network", "DNS", "46.101.91.123"); // Dreamcast Live DNS
|
||||
pico_string_to_ipv4(dns_ip.c_str(), &dnsaddr.addr);
|
||||
pico_string_to_ipv4(settings.network.dns.c_str(), &dnsaddr.addr);
|
||||
pico_ppp_set_dns1(ppp, dnsaddr);
|
||||
|
||||
pico_udp_socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &udp_callback);
|
||||
|
|
|
@ -13,9 +13,12 @@
|
|||
#include "naomi.h"
|
||||
#include "naomi_cart.h"
|
||||
#include "naomi_regs.h"
|
||||
#include "naomi_m3comm.h"
|
||||
|
||||
//#define NAOMI_COMM
|
||||
|
||||
static NaomiM3Comm m3comm;
|
||||
|
||||
static const u32 BoardID=0x980055AA;
|
||||
u32 GSerialBuffer=0,BSerialBuffer=0;
|
||||
int GBufPos=0,BBufPos=0;
|
||||
|
@ -395,25 +398,31 @@ void naomi_process(u32 command, u32 offsetl, u32 parameterl, u32 parameterh)
|
|||
}
|
||||
}
|
||||
|
||||
u32 ReadMem_naomi(u32 Addr, u32 sz)
|
||||
u32 ReadMem_naomi(u32 address, u32 size)
|
||||
{
|
||||
verify(sz!=1);
|
||||
verify(size != 1);
|
||||
if (unlikely(CurrentCartridge == NULL))
|
||||
{
|
||||
INFO_LOG(NAOMI, "called without cartridge");
|
||||
return 0xFFFF;
|
||||
}
|
||||
return CurrentCartridge->ReadMem(Addr, sz);
|
||||
if (address >= NAOMI_COMM2_CTRL_addr && address <= NAOMI_COMM2_STATUS1_addr)
|
||||
return m3comm.ReadMem(address, size);
|
||||
else
|
||||
return CurrentCartridge->ReadMem(address, size);
|
||||
}
|
||||
|
||||
void WriteMem_naomi(u32 Addr, u32 data, u32 sz)
|
||||
void WriteMem_naomi(u32 address, u32 data, u32 size)
|
||||
{
|
||||
if (unlikely(CurrentCartridge == NULL))
|
||||
{
|
||||
INFO_LOG(NAOMI, "called without cartridge");
|
||||
return;
|
||||
}
|
||||
CurrentCartridge->WriteMem(Addr, data, sz);
|
||||
if (address >= NAOMI_COMM2_CTRL_addr && address <= NAOMI_COMM2_STATUS1_addr)
|
||||
m3comm.WriteMem(address, data, size);
|
||||
else
|
||||
CurrentCartridge->WriteMem(address, data, size);
|
||||
}
|
||||
|
||||
//Dma Start
|
||||
|
@ -429,16 +438,11 @@ void Naomi_DmaStart(u32 addr, u32 data)
|
|||
|
||||
if (SB_GDST==1)
|
||||
{
|
||||
verify(1 == SB_GDDIR );
|
||||
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %d", SB_GDSTAR, SB_GDLEN);
|
||||
|
||||
SB_GDSTARD = SB_GDSTAR + SB_GDLEN;
|
||||
|
||||
SB_GDLEND = SB_GDLEN;
|
||||
SB_GDST = 0;
|
||||
if (CurrentCartridge != NULL)
|
||||
if (!m3comm.DmaStart(addr, data) && CurrentCartridge != NULL)
|
||||
{
|
||||
u32 len = SB_GDLEN;
|
||||
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %d", SB_GDSTAR, SB_GDLEN);
|
||||
verify(1 == SB_GDDIR);
|
||||
u32 len = (SB_GDLEN + 30) & ~30;
|
||||
u32 offset = 0;
|
||||
while (len > 0)
|
||||
{
|
||||
|
@ -455,7 +459,9 @@ void Naomi_DmaStart(u32 addr, u32 data)
|
|||
offset += block_len;
|
||||
}
|
||||
}
|
||||
|
||||
SB_GDSTARD = SB_GDSTAR + SB_GDLEN;
|
||||
SB_GDLEND = SB_GDLEN;
|
||||
SB_GDST = 0;
|
||||
asic_RaiseInterrupt(holly_GDROM_DMA);
|
||||
}
|
||||
}
|
||||
|
@ -522,7 +528,7 @@ void naomi_reg_Init()
|
|||
|
||||
void naomi_reg_Term()
|
||||
{
|
||||
#ifdef NAOMI_COMM
|
||||
#ifdef NAOMI_COMM
|
||||
if (CommSharedMem)
|
||||
{
|
||||
UnmapViewOfFile(CommSharedMem);
|
||||
|
@ -531,8 +537,10 @@ void naomi_reg_Term()
|
|||
{
|
||||
CloseHandle(CommMapFile);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
m3comm.closeNetwork();
|
||||
}
|
||||
|
||||
void naomi_reg_Reset(bool hard)
|
||||
{
|
||||
sb_rio_register(SB_GDST_addr, RIO_WF, 0, &Naomi_DmaStart);
|
||||
|
@ -562,6 +570,7 @@ void naomi_reg_Reset(bool hard)
|
|||
reg_dimm_parameterl = 0;
|
||||
reg_dimm_parameterh = 0;
|
||||
reg_dimm_status = 0x11;
|
||||
m3comm.closeNetwork();
|
||||
}
|
||||
|
||||
static u8 aw_maple_devs;
|
||||
|
|
|
@ -219,6 +219,8 @@ static void naomi_cart_LoadZip(const char *filename)
|
|||
u32 region_flag = settings.dreamcast.region;
|
||||
if (region_flag > game->region_flag)
|
||||
region_flag = game->region_flag;
|
||||
if (game->region_flag == REGION_EXPORT_ONLY)
|
||||
region_flag = REGION_EXPORT;
|
||||
if (!naomi_LoadBios(bios, archive.get(), parent_archive.get(), region_flag))
|
||||
{
|
||||
WARN_LOG(NAOMI, "Warning: Region %d bios not found in %s", region_flag, bios);
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
Created on: Mar 15, 2020
|
||||
|
||||
Copyright 2020 flyinghead
|
||||
|
||||
This file is part of flycast.
|
||||
|
||||
flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "naomi_m3comm.h"
|
||||
#include "naomi_regs.h"
|
||||
#include "hw/holly/sb.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include <chrono>
|
||||
|
||||
static inline u16 swap16(u16 w)
|
||||
{
|
||||
return (w >> 8) | (w << 8);
|
||||
}
|
||||
|
||||
void NaomiM3Comm::closeNetwork()
|
||||
{
|
||||
network_stopping = true;
|
||||
network.shutdown();
|
||||
if (thread && thread->joinable())
|
||||
thread->join();
|
||||
}
|
||||
|
||||
void NaomiM3Comm::connectNetwork()
|
||||
{
|
||||
if (network.startNetwork())
|
||||
{
|
||||
slot_count = network.slotCount();
|
||||
slot_id = network.slotId();
|
||||
connectedState(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
connectedState(false);
|
||||
network_stopping = true;
|
||||
network.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void NaomiM3Comm::receiveNetwork()
|
||||
{
|
||||
const u32 slot_size = swap16(*(u16*)&m68k_ram[0x204]);
|
||||
const u32 packet_size = slot_size * slot_count;
|
||||
|
||||
u8 buf[packet_size];
|
||||
|
||||
if (network.receive(buf, packet_size))
|
||||
{
|
||||
*(u16*)&comm_ram[6] = swap16(network.packetNumber());
|
||||
std::unique_lock<std::mutex> lock(mem_mutex);
|
||||
memcpy(&comm_ram[0x100 + slot_size], buf, packet_size);
|
||||
}
|
||||
}
|
||||
|
||||
void NaomiM3Comm::sendNetwork()
|
||||
{
|
||||
if (network.hasToken())
|
||||
{
|
||||
const u32 packet_size = swap16(*(u16*)&m68k_ram[0x204]) * slot_count;
|
||||
std::unique_lock<std::mutex> lock(mem_mutex);
|
||||
network.send(&comm_ram[0x100], packet_size);
|
||||
*(u16*)&comm_ram[6] = swap16(network.packetNumber());
|
||||
}
|
||||
}
|
||||
|
||||
NaomiM3Comm::~NaomiM3Comm()
|
||||
{
|
||||
closeNetwork();
|
||||
network.terminate();
|
||||
}
|
||||
|
||||
u32 NaomiM3Comm::ReadMem(u32 address, u32 size)
|
||||
{
|
||||
switch (address & 255)
|
||||
{
|
||||
case NAOMI_COMM2_CTRL_addr & 255:
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_CTRL read");
|
||||
return comm_ctrl;
|
||||
|
||||
case NAOMI_COMM2_OFFSET_addr & 255:
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_OFFSET read");
|
||||
return comm_offset;
|
||||
|
||||
case NAOMI_COMM2_DATA_addr & 255:
|
||||
{
|
||||
u16 value;
|
||||
if (comm_ctrl & 1)
|
||||
value = *(u16*)&m68k_ram[comm_offset];
|
||||
else
|
||||
// TODO u16 *commram = (u16*)membank("comm_ram")->base();
|
||||
value = *(u16*)&comm_ram[comm_offset];
|
||||
value = swap16(value);
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_DATA %s read @ %04x: %x", (comm_ctrl & 1) ? "m68k ram" : "comm ram", comm_offset, value);
|
||||
comm_offset += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
case NAOMI_COMM2_STATUS0_addr & 255:
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS0 read %x", comm_status0);
|
||||
return comm_status0;
|
||||
|
||||
case NAOMI_COMM2_STATUS1_addr & 255:
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS1 read %x", comm_status1);
|
||||
return comm_status1;
|
||||
|
||||
default:
|
||||
DEBUG_LOG(NAOMI, "NaomiM3Comm::ReadMem unmapped: %08x sz %d", address, size);
|
||||
return 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
void NaomiM3Comm::connectedState(bool success)
|
||||
{
|
||||
if (!success)
|
||||
return;
|
||||
|
||||
memset(&comm_ram[0xf000], 0, 16);
|
||||
comm_ram[0xf000] = 1;
|
||||
comm_ram[0xf001] = 1;
|
||||
comm_ram[0xf002] = m68k_ram[0x204];
|
||||
comm_ram[0xf003] = m68k_ram[0x205];
|
||||
|
||||
u32 slot_size = swap16(*(u16*)&m68k_ram[0x204]);
|
||||
|
||||
memset(&comm_ram[0], 0, 32);
|
||||
// 80000
|
||||
comm_ram[0] = 0;
|
||||
comm_ram[1] = slot_id == 0 ? 0 : 1;
|
||||
// 80002
|
||||
comm_ram[2] = 0x01;
|
||||
comm_ram[3] = 0x01;
|
||||
// 80004
|
||||
if (slot_id == 0)
|
||||
{
|
||||
comm_ram[4] = 0;
|
||||
comm_ram[5] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
comm_ram[4] = 1;
|
||||
comm_ram[5] = 1;
|
||||
}
|
||||
// 80006: packet number
|
||||
comm_ram[6] = 0;
|
||||
comm_ram[7] = 0;
|
||||
// 80008
|
||||
comm_ram[8] = slot_id == 0 ? 0x78 : 0x73;
|
||||
comm_ram[9] = slot_id == 0 ? 0x30 : 0xa2;
|
||||
// 8000A
|
||||
*(u16 *)(comm_ram + 10) = 0x100 + slot_size; // offset of recvd data
|
||||
// 8000C
|
||||
*(u16 *)(comm_ram + 12) = slot_size * slot_count; // recvd data size
|
||||
// 8000E
|
||||
*(u16 *)(comm_ram + 14) = 0x100; // offset of sent data
|
||||
// 80010
|
||||
*(u16 *)(comm_ram + 16) = 0x80 + slot_size * slot_count; // sent data size
|
||||
// FIXME wrungp uses 100, others 80
|
||||
|
||||
comm_status0 = 0xff01; // But 1 at connect time before f000 is read
|
||||
comm_status1 = (slot_count << 8) | slot_id;
|
||||
}
|
||||
|
||||
void NaomiM3Comm::WriteMem(u32 address, u32 data, u32 size)
|
||||
{
|
||||
switch (address & 255)
|
||||
{
|
||||
case NAOMI_COMM2_CTRL_addr & 255:
|
||||
// bit 0: access RAM is 0 - communication RAM / 1 - M68K RAM
|
||||
// bit 1: comm RAM bank (seems R/O for SH4)
|
||||
// bit 5: M68K Reset
|
||||
// bit 6: ???
|
||||
// bit 7: might be M68K IRQ 5 or 2
|
||||
// bit 14: G1 DMA bus master 0 - active / 1 - disabled
|
||||
// bit 15: 0 - enable / 1 - disable this device ???
|
||||
if (data & (1 << 5))
|
||||
{
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_CTRL m68k reset");
|
||||
closeNetwork();
|
||||
memset(&comm_ram[0], 0, 32);
|
||||
comm_status0 = 0; // varies...
|
||||
comm_status1 = 0;
|
||||
startThread();
|
||||
}
|
||||
comm_ctrl = (u16)(data & ~(1 << 5));
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_CTRL set to %x", comm_ctrl);
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_OFFSET_addr & 255:
|
||||
comm_offset = (u16)data;
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_OFFSET set to %x", comm_offset);
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_DATA_addr & 255:
|
||||
DEBUG_LOG(NAOMI, "NAOMI_COMM2_DATA written @ %04x %04x", comm_offset, (u16)data);
|
||||
data = swap16(data);
|
||||
if (comm_ctrl & 1)
|
||||
*(u16*)&m68k_ram[comm_offset] = (u16)data;
|
||||
else
|
||||
*(u16*)&comm_ram[comm_offset] = (u16)data;
|
||||
comm_offset += 2;
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_STATUS0_addr & 255:
|
||||
comm_status0 = (u16)data;
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS0 set to %x", comm_status0);
|
||||
return;
|
||||
|
||||
case NAOMI_COMM2_STATUS1_addr & 255:
|
||||
comm_status1 = (u16)data;
|
||||
//DEBUG_LOG(NAOMI, "NAOMI_COMM2_STATUS1 set to %x", comm_status1);
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
DEBUG_LOG(NAOMI, "NaomiM3Comm::WriteMem: %x <= %x sz %d", address, data, size);
|
||||
}
|
||||
|
||||
bool NaomiM3Comm::DmaStart(u32 addr, u32 data)
|
||||
{
|
||||
if (comm_ctrl & 0x4000)
|
||||
return false;
|
||||
|
||||
DEBUG_LOG(NAOMI, "NaomiM3Comm: DMA addr %08X <-> %04x len %d %s", SB_GDSTAR, comm_offset, SB_GDLEN, SB_GDDIR == 0 ? "OUT" : "IN");
|
||||
std::unique_lock<std::mutex> lock(mem_mutex);
|
||||
if (SB_GDDIR == 0)
|
||||
{
|
||||
// Network write
|
||||
for (u32 i = 0; i < SB_GDLEN; i++)
|
||||
comm_ram[comm_offset++] = ReadMem8_nommu(SB_GDSTAR + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Network read
|
||||
if (SB_GDLEN == 32 && (comm_ctrl & 1) == 0)
|
||||
{
|
||||
char buf[32 * 5 + 1];
|
||||
buf[0] = 0;
|
||||
for (u32 i = 0; i < SB_GDLEN; i++)
|
||||
{
|
||||
u8 value = comm_ram[comm_offset + i];
|
||||
sprintf(buf + strlen(buf), "%02x ", value);
|
||||
}
|
||||
DEBUG_LOG(NAOMI, "Comm RAM read @%x: %s", comm_offset, buf);
|
||||
}
|
||||
for (u32 i = 0; i < SB_GDLEN; i++)
|
||||
WriteMem8_nommu(SB_GDSTAR + i, comm_ram[comm_offset++]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NaomiM3Comm::startThread()
|
||||
{
|
||||
network_stopping = false;
|
||||
thread = std::unique_ptr<std::thread>(new std::thread([this]() {
|
||||
using the_clock = std::chrono::high_resolution_clock;
|
||||
|
||||
connectNetwork();
|
||||
|
||||
the_clock::time_point token_time = the_clock::now();
|
||||
|
||||
while (!network_stopping)
|
||||
{
|
||||
network.pipeSlaves();
|
||||
receiveNetwork();
|
||||
|
||||
if (slot_id == 0 && network.hasToken())
|
||||
{
|
||||
const auto target_duration = std::chrono::milliseconds(10);
|
||||
auto duration = the_clock::now() - token_time;
|
||||
if (duration < target_duration)
|
||||
{
|
||||
DEBUG_LOG(NAOMI, "Sleeping for %ld ms", std::chrono::duration_cast<std::chrono::milliseconds>(target_duration - duration).count());
|
||||
std::this_thread::sleep_for(target_duration - duration);
|
||||
}
|
||||
token_time = the_clock::now();
|
||||
}
|
||||
|
||||
sendNetwork();
|
||||
|
||||
}
|
||||
DEBUG_LOG(NAOMI, "Network thread exiting");
|
||||
}));
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Created on: Mar 15, 2020
|
||||
|
||||
Copyright 2020 flyinghead
|
||||
|
||||
This file is part of flycast.
|
||||
|
||||
flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include "network/naomi_network.h"
|
||||
|
||||
class NaomiM3Comm
|
||||
{
|
||||
public:
|
||||
~NaomiM3Comm();
|
||||
u32 ReadMem(u32 address, u32 size);
|
||||
void WriteMem(u32 address, u32 data, u32 size);
|
||||
bool DmaStart(u32 addr, u32 data);
|
||||
|
||||
void closeNetwork();
|
||||
|
||||
private:
|
||||
void initNetwork();
|
||||
void connectNetwork();
|
||||
void receiveNetwork();
|
||||
void sendNetwork();
|
||||
void connectedState(bool success);
|
||||
void startThread();
|
||||
|
||||
u16 comm_ctrl = 0xC000;
|
||||
u16 comm_offset = 0;
|
||||
u16 comm_status0 = 0;
|
||||
u16 comm_status1 = 0;
|
||||
u8 m68k_ram[128 * 1024];
|
||||
u8 comm_ram[128 * 1024];
|
||||
|
||||
int slot_count = 0;
|
||||
int slot_id = 0;
|
||||
std::atomic<bool> network_stopping{ false };
|
||||
std::unique_ptr<std::thread> thread;
|
||||
std::mutex mem_mutex;
|
||||
NaomiNetwork network;
|
||||
};
|
|
@ -0,0 +1,465 @@
|
|||
/*
|
||||
Created on: Apr 12, 2020
|
||||
|
||||
Copyright 2020 flyinghead
|
||||
|
||||
This file is part of flycast.
|
||||
|
||||
flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "naomi_network.h"
|
||||
|
||||
#include "types.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "rend/gui.h"
|
||||
|
||||
sock_t NaomiNetwork::createAndBind(int protocol)
|
||||
{
|
||||
sock_t sock = socket(AF_INET, protocol == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM, protocol);
|
||||
if (sock == INVALID_SOCKET)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "Cannot create server socket");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
int option = 1;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
|
||||
|
||||
struct sockaddr_in serveraddr;
|
||||
memset(&serveraddr, 0, sizeof(serveraddr));
|
||||
serveraddr.sin_family = AF_INET;
|
||||
serveraddr.sin_port = htons(SERVER_PORT);
|
||||
|
||||
if (::bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "NaomiServer: bind() failed. errno=%d", get_last_error());
|
||||
closesocket(sock);
|
||||
sock = INVALID_SOCKET;
|
||||
}
|
||||
else
|
||||
set_non_blocking(sock);
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
bool NaomiNetwork::init()
|
||||
{
|
||||
if (settings.network.ActAsServer)
|
||||
return createBeaconSocket() && createServerSocket();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NaomiNetwork::createServerSocket()
|
||||
{
|
||||
if (server_sock != INVALID_SOCKET)
|
||||
return true;
|
||||
|
||||
server_sock = createAndBind(IPPROTO_TCP);
|
||||
if (server_sock == INVALID_SOCKET)
|
||||
return false;
|
||||
|
||||
if (listen(server_sock, 5) < 0)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "NaomiServer: listen() failed. errno=%d", get_last_error());
|
||||
closesocket(server_sock);
|
||||
server_sock = INVALID_SOCKET;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NaomiNetwork::createBeaconSocket()
|
||||
{
|
||||
if (beacon_sock == INVALID_SOCKET)
|
||||
beacon_sock = createAndBind(IPPROTO_UDP);
|
||||
|
||||
return beacon_sock != INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void NaomiNetwork::processBeacon()
|
||||
{
|
||||
// Receive broadcast queries on beacon socket and reply
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
char buf[6];
|
||||
ssize_t n;
|
||||
do {
|
||||
memset(buf, '\0', sizeof(buf));
|
||||
if ((n = recvfrom(beacon_sock, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &addrlen)) == -1)
|
||||
{
|
||||
if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
|
||||
WARN_LOG(NETWORK, "NaomiServer: Error receiving datagram. errno=%d", get_last_error());
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(NETWORK, "NaomiServer: beacon received %ld bytes", n);
|
||||
if (n == sizeof(buf) && !strncmp(buf, "flycast", n))
|
||||
sendto(beacon_sock, buf, n, 0, (const struct sockaddr *)&addr, addrlen);
|
||||
}
|
||||
} while (n != -1);
|
||||
}
|
||||
|
||||
bool NaomiNetwork::findServer()
|
||||
{
|
||||
// Automatically find the adhoc server on the local network using broadcast
|
||||
sock_t sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd == INVALID_SOCKET)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "Datagram socket creation error. errno=%d", get_last_error());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow broadcast packets to be sent
|
||||
int broadcast = 1;
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) == -1)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "setsockopt(SO_BROADCAST) failed. errno=%d", get_last_error());
|
||||
closesocket(sockfd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set a 1 sec timeout on recv call
|
||||
if (!set_recv_timeout(sockfd, 1000))
|
||||
{
|
||||
ERROR_LOG(NETWORK, "setsockopt(SO_RCVTIMEO) failed. errno=%d", get_last_error());
|
||||
closesocket(sockfd);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET; // host byte order
|
||||
addr.sin_port = htons(SERVER_PORT); // short, network byte order
|
||||
addr.sin_addr.s_addr = INADDR_BROADCAST;
|
||||
memset(addr.sin_zero, '\0', sizeof(addr.sin_zero));
|
||||
|
||||
struct sockaddr server_addr;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
if (sendto(sockfd, "flycast", 6, 0, (struct sockaddr *)&addr, sizeof addr) == -1)
|
||||
{
|
||||
WARN_LOG(NETWORK, "Send datagram failed. errno=%d", get_last_error());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
|
||||
char buf[6];
|
||||
memset(&server_addr, '\0', sizeof(server_addr));
|
||||
socklen_t addrlen = sizeof(server_addr);
|
||||
if (recvfrom(sockfd, buf, sizeof(buf), 0, &server_addr, &addrlen) == -1)
|
||||
{
|
||||
if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
|
||||
WARN_LOG(NETWORK, "Recv datagram failed. errno=%d", get_last_error());
|
||||
else
|
||||
INFO_LOG(NETWORK, "Recv datagram timeout. i=%d", i);
|
||||
continue;
|
||||
}
|
||||
server_ip = ((struct sockaddr_in *)&server_addr)->sin_addr;
|
||||
char addressBuffer[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &server_ip, addressBuffer, INET_ADDRSTRLEN);
|
||||
server_name = addressBuffer;
|
||||
break;
|
||||
}
|
||||
closesocket(sockfd);
|
||||
if (server_ip.s_addr == INADDR_NONE)
|
||||
{
|
||||
WARN_LOG(NETWORK, "Network Error: Can't find ad-hoc server on local network");
|
||||
gui_display_notification("No server found", 8000);
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(NETWORK, "Found ad-hoc server at %s", server_name.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NaomiNetwork::startNetwork()
|
||||
{
|
||||
network_stopping = false;
|
||||
if (!init())
|
||||
return false;
|
||||
|
||||
slot_id = 0;
|
||||
slot_count = 0;
|
||||
packet_number = 0;
|
||||
slaves.clear();
|
||||
got_token = false;
|
||||
|
||||
using namespace std::chrono;
|
||||
const auto timeout = seconds(10);
|
||||
|
||||
if (settings.network.ActAsServer)
|
||||
{
|
||||
NOTICE_LOG(NETWORK, "Waiting for slave connections");
|
||||
steady_clock::time_point start_time = steady_clock::now();
|
||||
|
||||
while (steady_clock::now() - start_time < timeout)
|
||||
{
|
||||
if (network_stopping)
|
||||
{
|
||||
for (auto clientSock : slaves)
|
||||
if (clientSock != INVALID_SOCKET)
|
||||
closesocket(clientSock);
|
||||
return false;
|
||||
}
|
||||
std::string notif = slaves.empty() ? "Waiting for players..."
|
||||
: std::to_string(slaves.size()) + " player(s) connected. Waiting...";
|
||||
gui_display_notification(notif.c_str(), timeout.count() * 2000);
|
||||
|
||||
processBeacon();
|
||||
|
||||
struct sockaddr_in src_addr;
|
||||
socklen_t addr_len = sizeof(src_addr);
|
||||
memset(&src_addr, 0, addr_len);
|
||||
sock_t clientSock = accept(server_sock, (struct sockaddr *)&src_addr, &addr_len);
|
||||
if (clientSock == INVALID_SOCKET)
|
||||
{
|
||||
if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
|
||||
perror("accept");
|
||||
}
|
||||
else
|
||||
{
|
||||
NOTICE_LOG(NETWORK, "Slave connection accepted");
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
slaves.push_back(clientSock);
|
||||
if (slaves.size() == 3)
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(milliseconds(100));
|
||||
}
|
||||
slot_id = 0;
|
||||
slot_count = slaves.size() + 1;
|
||||
u8 buf[2] = { (u8)slot_count, 0 };
|
||||
int slot_num = 1;
|
||||
{
|
||||
for (int socket : slaves)
|
||||
{
|
||||
buf[1] = { (u8)slot_num };
|
||||
slot_num++;
|
||||
write(socket, buf, 2);
|
||||
set_non_blocking(socket);
|
||||
set_tcp_nodelay(socket);
|
||||
}
|
||||
}
|
||||
NOTICE_LOG(NETWORK, "Master starting: %zd slaves", slaves.size());
|
||||
if (slot_count > 1)
|
||||
gui_display_notification("Starting game", 2000);
|
||||
else
|
||||
gui_display_notification("No player connected", 8000);
|
||||
|
||||
return !slaves.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!settings.network.server.empty())
|
||||
{
|
||||
struct addrinfo *resultAddr;
|
||||
if (getaddrinfo(settings.network.server.c_str(), 0, nullptr, &resultAddr))
|
||||
WARN_LOG(NETWORK, "Server %s is unknown", settings.network.server.c_str());
|
||||
else
|
||||
for (struct addrinfo *ptr = resultAddr; ptr != nullptr; ptr = ptr->ai_next)
|
||||
if (ptr->ai_family == AF_INET)
|
||||
{
|
||||
server_ip = ((sockaddr_in *)ptr->ai_addr)->sin_addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NOTICE_LOG(NETWORK, "Connecting to server");
|
||||
gui_display_notification("Connecting to server", 10000);
|
||||
steady_clock::time_point start_time = steady_clock::now();
|
||||
|
||||
while (client_sock == INVALID_SOCKET && !network_stopping
|
||||
&& steady_clock::now() - start_time < timeout)
|
||||
{
|
||||
if (server_ip.s_addr == INADDR_NONE && !findServer())
|
||||
continue;
|
||||
|
||||
client_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
struct sockaddr_in src_addr;
|
||||
src_addr.sin_family = AF_INET;
|
||||
src_addr.sin_addr = server_ip;
|
||||
src_addr.sin_port = htons(SERVER_PORT);
|
||||
if (::connect(client_sock, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "Socket connect failed");
|
||||
closesocket(client_sock);
|
||||
client_sock = INVALID_SOCKET;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
else
|
||||
{
|
||||
gui_display_notification("Waiting for server to start", 10000);
|
||||
set_recv_timeout(client_sock, (int)std::chrono::milliseconds(timeout * 2).count());
|
||||
u8 buf[2];
|
||||
if (read(client_sock, buf, 2) < 2)
|
||||
{
|
||||
ERROR_LOG(NETWORK, "Connection failed: errno=%d", get_last_error());
|
||||
closesocket(client_sock);
|
||||
client_sock = -1;
|
||||
gui_display_notification("Connection failed", 10000);
|
||||
|
||||
return false;
|
||||
}
|
||||
slot_count = buf[0];
|
||||
slot_id = buf[1];
|
||||
got_token = slot_id == 1;
|
||||
set_tcp_nodelay(client_sock);
|
||||
set_non_blocking(client_sock);
|
||||
std::string notif = "Connected as slot " + std::to_string(slot_id);
|
||||
gui_display_notification(notif.c_str(), 2000);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void NaomiNetwork::pipeSlaves()
|
||||
{
|
||||
if (isMaster() || slot_count < 3)
|
||||
return;
|
||||
u8 buf[16384];
|
||||
for (auto it = slaves.begin(); it != slaves.end() - 1; it++)
|
||||
{
|
||||
ssize_t l = read(*it, buf, sizeof(buf));
|
||||
if (l > 0)
|
||||
write(*(it + 1), buf, l);
|
||||
// TODO handle errors
|
||||
}
|
||||
}
|
||||
|
||||
bool NaomiNetwork::receive(u8 *data, u32 size)
|
||||
{
|
||||
sock_t sockfd = INVALID_SOCKET;
|
||||
if (isMaster())
|
||||
sockfd = slaves.empty() ? INVALID_SOCKET : slaves.back();
|
||||
else
|
||||
sockfd = client_sock;
|
||||
if (sockfd == INVALID_SOCKET)
|
||||
return false;
|
||||
|
||||
u16 pktnum;
|
||||
ssize_t l = read(sockfd, &pktnum, sizeof(pktnum));
|
||||
if (l <= 0)
|
||||
{
|
||||
if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
|
||||
{
|
||||
WARN_LOG(NETWORK, "receiveNetwork: read failed. errno=%d", get_last_error());
|
||||
if (isMaster())
|
||||
{
|
||||
slaves.back() = -1;
|
||||
closesocket(sockfd);
|
||||
got_token = false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
packet_number = pktnum;
|
||||
|
||||
ssize_t received = 0;
|
||||
while (received != size && !network_stopping)
|
||||
{
|
||||
l = read(sockfd, data + received, size - received);
|
||||
if (l <= 0)
|
||||
{
|
||||
if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK)
|
||||
{
|
||||
WARN_LOG(NETWORK, "receiveNetwork: read failed. errno=%d", get_last_error());
|
||||
if (isMaster())
|
||||
{
|
||||
slaves.back() = -1;
|
||||
closesocket(sockfd);
|
||||
got_token = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
received += l;
|
||||
}
|
||||
DEBUG_LOG(NETWORK, "[%d] Received %d bytes", slot_id, size);
|
||||
got_token = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void NaomiNetwork::send(u8 *data, u32 size)
|
||||
{
|
||||
if (!got_token)
|
||||
return;
|
||||
|
||||
sock_t sockfd;
|
||||
if (isMaster())
|
||||
sockfd = slaves.empty() ? INVALID_SOCKET : slaves.front();
|
||||
else
|
||||
sockfd = client_sock;
|
||||
if (sockfd == INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
u16 pktnum = packet_number + 1;
|
||||
struct iovec iov[] = { { &pktnum, sizeof(pktnum) }, { data, size } };
|
||||
struct msghdr msg{};
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = ARRAY_SIZE(iov);
|
||||
if (sendmsg(sockfd, &msg, 0) < 0)
|
||||
{
|
||||
if (errno != L_EAGAIN && errno != L_EWOULDBLOCK)
|
||||
{
|
||||
WARN_LOG(NETWORK, "sendmsg failed. errno=%d", get_last_error());
|
||||
if (isMaster())
|
||||
{
|
||||
slaves.front() = -1;
|
||||
closesocket(sockfd);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_LOG(NETWORK, "[%d] Sent %d bytes", slot_id, size);
|
||||
got_token = false;
|
||||
packet_number = pktnum;
|
||||
}
|
||||
}
|
||||
|
||||
void NaomiNetwork::shutdown()
|
||||
{
|
||||
network_stopping = true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
for (auto& clientSock : slaves)
|
||||
{
|
||||
closesocket(clientSock);
|
||||
clientSock = -1;
|
||||
}
|
||||
}
|
||||
if (client_sock != INVALID_SOCKET)
|
||||
closesocket(client_sock);
|
||||
}
|
||||
|
||||
void NaomiNetwork::terminate()
|
||||
{
|
||||
shutdown();
|
||||
if (beacon_sock != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(beacon_sock);
|
||||
beacon_sock = INVALID_SOCKET;
|
||||
}
|
||||
if (server_sock != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(server_sock);
|
||||
server_sock = INVALID_SOCKET;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Created on: Apr 12, 2020
|
||||
|
||||
Copyright 2020 flyinghead
|
||||
|
||||
This file is part of flycast.
|
||||
|
||||
flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "net_platform.h"
|
||||
|
||||
class NaomiNetwork
|
||||
{
|
||||
public:
|
||||
~NaomiNetwork() { terminate(); }
|
||||
bool init();
|
||||
bool startNetwork();
|
||||
void pipeSlaves();
|
||||
bool receive(u8 *data, u32 size);
|
||||
void send(u8 *data, u32 size);
|
||||
void shutdown(); // thread-safe
|
||||
void terminate(); // thread-safe
|
||||
int slotCount() const { return slot_count; }
|
||||
int slotId() const { return slot_id; }
|
||||
u16 packetNumber() const { return packet_number; }
|
||||
bool hasToken() const { return got_token; }
|
||||
|
||||
private:
|
||||
bool createServerSocket();
|
||||
bool createBeaconSocket();
|
||||
void processBeacon();
|
||||
bool findServer();
|
||||
int createAndBind(int protocol);
|
||||
bool isMaster() const { return slot_id == 0; }
|
||||
|
||||
struct in_addr server_ip{ INADDR_NONE };
|
||||
std::string server_name;
|
||||
// server stuff
|
||||
sock_t server_sock = INVALID_SOCKET;
|
||||
sock_t beacon_sock = INVALID_SOCKET;
|
||||
std::vector<sock_t> slaves;
|
||||
// client stuff
|
||||
sock_t client_sock = INVALID_SOCKET;
|
||||
// common stuff
|
||||
int slot_count = 0;
|
||||
int slot_id = 0;
|
||||
bool got_token = false;
|
||||
u16 packet_number = 0;
|
||||
std::atomic<bool> network_stopping{ false };
|
||||
std::mutex mutex;
|
||||
|
||||
static const uint16_t SERVER_PORT = 37391;
|
||||
};
|
|
@ -790,6 +790,9 @@ void InitSettings()
|
|||
settings.input.maple_expansion_devices[i][0] = i == 0 ? MDT_SegaVMU : MDT_None;
|
||||
settings.input.maple_expansion_devices[i][1] = i == 0 ? MDT_SegaVMU : MDT_None;
|
||||
}
|
||||
settings.network.ActAsServer = false;
|
||||
settings.network.dns = "46.101.91.123"; // Dreamcast Live DNS
|
||||
settings.network.server = "";
|
||||
|
||||
#if SUPPORT_DISPMANX
|
||||
settings.dispmanx.Width = 640;
|
||||
|
@ -893,6 +896,9 @@ void LoadSettings(bool game_specific)
|
|||
sprintf(device_name, "device%d.2", i + 1);
|
||||
settings.input.maple_expansion_devices[i][1] = (MapleDeviceType)cfgLoadInt(input_section, device_name, settings.input.maple_expansion_devices[i][1]);
|
||||
}
|
||||
settings.network.ActAsServer = cfgLoadBool("network", "ActAsServer", settings.network.ActAsServer);
|
||||
settings.network.dns = cfgLoadStr("network", "DNS", settings.network.dns.c_str());
|
||||
settings.network.server = cfgLoadStr("network", "server", settings.network.server.c_str());
|
||||
|
||||
#if SUPPORT_DISPMANX
|
||||
settings.dispmanx.Width = cfgLoadInt(game_specific ? cfgGetGameId() : "dispmanx", "width", settings.dispmanx.Width);
|
||||
|
@ -1049,6 +1055,9 @@ void SaveSettings()
|
|||
}
|
||||
cfgSaveStr("config", "Dreamcast.ContentPath", paths.c_str());
|
||||
cfgSaveBool("config", "Dreamcast.HideLegacyNaomiRoms", settings.dreamcast.HideLegacyNaomiRoms);
|
||||
cfgSaveBool("network", "ActAsServer", settings.network.ActAsServer);
|
||||
cfgSaveStr("network", "DNS", settings.network.dns.c_str());
|
||||
cfgSaveStr("network", "server", settings.network.server.c_str());
|
||||
|
||||
GamepadDevice::SaveMaplePorts();
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
extern void dc_loadstate();
|
||||
extern void dc_savestate();
|
||||
extern void dc_stop();
|
||||
extern void dc_reset(bool hard);
|
||||
extern void dc_resume();
|
||||
extern void dc_start_game(const char *path);
|
||||
extern void UpdateInputState(u32 port);
|
||||
|
@ -363,6 +364,7 @@ static void gui_display_commands()
|
|||
gui_state = Main;
|
||||
game_started = false;
|
||||
settings.imgread.ImagePath[0] = '\0';
|
||||
dc_reset(true);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
@ -1282,6 +1284,18 @@ static void gui_display_settings()
|
|||
ImGui::SameLine();
|
||||
ShowHelpMarker("Skip wait loops. Recommended");
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Network", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Checkbox("Act as Server", &settings.network.ActAsServer);
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("Create a local server for Naomi network games");
|
||||
char server_name[256];
|
||||
strcpy(server_name, settings.network.server.c_str());
|
||||
ImGui::InputText("Server", server_name, sizeof(server_name), ImGuiInputTextFlags_CharsNoBlank, nullptr, nullptr);
|
||||
ImGui::SameLine();
|
||||
ShowHelpMarker("The server to connect to. Leave blank to find a server automatically");
|
||||
settings.network.server = server_name;
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Other", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Checkbox("HLE BIOS", &settings.bios.UseReios);
|
||||
|
|
|
@ -533,6 +533,12 @@ struct settings_t
|
|||
int maple_expansion_devices[4][2];
|
||||
int VirtualGamepadVibration;
|
||||
} input;
|
||||
|
||||
struct {
|
||||
bool ActAsServer;
|
||||
std::string dns;
|
||||
std::string server;
|
||||
} network;
|
||||
};
|
||||
|
||||
extern settings_t settings;
|
||||
|
|
|
@ -238,6 +238,8 @@
|
|||
AE7BCB5E243DDCD1007285F8 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AE7BCB5D243DDCD1007285F8 /* AudioToolbox.framework */; };
|
||||
AE7BCB60243DDD49007285F8 /* libiconv.2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = AE7BCB5F243DDD3A007285F8 /* libiconv.2.tbd */; };
|
||||
AE7BCB62243DDD92007285F8 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AE7BCB61243DDD92007285F8 /* Carbon.framework */; };
|
||||
AE7BCB6B244608B6007285F8 /* naomi_network.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE7BCB69244608B5007285F8 /* naomi_network.cpp */; };
|
||||
AE7BCB6E24460910007285F8 /* naomi_m3comm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE7BCB6C24460910007285F8 /* naomi_m3comm.cpp */; };
|
||||
AE80EDB72157D4D500F7800F /* serialize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE80EDB62157D4D500F7800F /* serialize.cpp */; };
|
||||
AE80EDBE2157D4E600F7800F /* naomi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE80EDB92157D4E600F7800F /* naomi.cpp */; };
|
||||
AE80EDBF2157D4E600F7800F /* naomi_cart.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE80EDBB2157D4E600F7800F /* naomi_cart.cpp */; };
|
||||
|
@ -792,6 +794,11 @@
|
|||
AE7BCB5D243DDCD1007285F8 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
AE7BCB5F243DDD3A007285F8 /* libiconv.2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.2.tbd; path = usr/lib/libiconv.2.tbd; sourceTree = SDKROOT; };
|
||||
AE7BCB61243DDD92007285F8 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
|
||||
AE7BCB68244608B5007285F8 /* net_platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = net_platform.h; sourceTree = "<group>"; };
|
||||
AE7BCB69244608B5007285F8 /* naomi_network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = naomi_network.cpp; sourceTree = "<group>"; };
|
||||
AE7BCB6A244608B6007285F8 /* naomi_network.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = naomi_network.h; sourceTree = "<group>"; };
|
||||
AE7BCB6C24460910007285F8 /* naomi_m3comm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = naomi_m3comm.cpp; sourceTree = "<group>"; };
|
||||
AE7BCB6D24460910007285F8 /* naomi_m3comm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = naomi_m3comm.h; sourceTree = "<group>"; };
|
||||
AE80EDB62157D4D500F7800F /* serialize.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = serialize.cpp; path = ../../../core/serialize.cpp; sourceTree = "<group>"; };
|
||||
AE80EDB92157D4E600F7800F /* naomi.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = naomi.cpp; sourceTree = "<group>"; };
|
||||
AE80EDBA2157D4E600F7800F /* naomi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = naomi.h; sourceTree = "<group>"; };
|
||||
|
@ -1151,6 +1158,7 @@
|
|||
84B7BE4D1B72720100F9733F /* khronos */,
|
||||
84B7BE641B72720100F9733F /* linux */,
|
||||
AE43536822C9420C005E19CE /* log */,
|
||||
AE7BCB6724460875007285F8 /* network */,
|
||||
84B7BE6F1B72720200F9733F /* oslib */,
|
||||
84B7BE7C1B72720200F9733F /* profiler */,
|
||||
AE1E29392095FB1600FC6BA2 /* rec-cpp */,
|
||||
|
@ -2037,6 +2045,17 @@
|
|||
path = ../../../core/wsi;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AE7BCB6724460875007285F8 /* network */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AE7BCB69244608B5007285F8 /* naomi_network.cpp */,
|
||||
AE7BCB6A244608B6007285F8 /* naomi_network.h */,
|
||||
AE7BCB68244608B5007285F8 /* net_platform.h */,
|
||||
);
|
||||
name = network;
|
||||
path = ../../../core/network;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
AE80EDB82157D4E600F7800F /* naomi */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -2051,6 +2070,8 @@
|
|||
AE2A2D5021D6846F004B308D /* m1cartridge.h */,
|
||||
AE2A2D5621D68470004B308D /* m4cartridge.cpp */,
|
||||
AE2A2D5421D68470004B308D /* m4cartridge.h */,
|
||||
AE7BCB6C24460910007285F8 /* naomi_m3comm.cpp */,
|
||||
AE7BCB6D24460910007285F8 /* naomi_m3comm.h */,
|
||||
AE2A2D5821D68470004B308D /* naomi_roms_input.h */,
|
||||
AE2A2D5521D68470004B308D /* naomi_roms.h */,
|
||||
AE80EDB92157D4E600F7800F /* naomi.cpp */,
|
||||
|
@ -2747,6 +2768,7 @@
|
|||
84B7BF391B72720200F9733F /* drkPvr.cpp in Sources */,
|
||||
84B7BF621B72720200F9733F /* context.cpp in Sources */,
|
||||
AEFF7F72214D9D590068CE11 /* pico_protocol.c in Sources */,
|
||||
AE7BCB6E24460910007285F8 /* naomi_m3comm.cpp in Sources */,
|
||||
84B7BF6A1B72720200F9733F /* audiobackend_pulseaudio.cpp in Sources */,
|
||||
AED73E8A2348E45000ECDB64 /* doc.cpp in Sources */,
|
||||
84B7BF3F1B72720200F9733F /* ta.cpp in Sources */,
|
||||
|
@ -2754,6 +2776,7 @@
|
|||
AED73DC42303E19200ECDB64 /* sdl.cpp in Sources */,
|
||||
84B7BF751B72720200F9733F /* gdrom_hle.cpp in Sources */,
|
||||
84B7BEFF1B72720200F9733F /* zip_name_locate.c in Sources */,
|
||||
AE7BCB6B244608B6007285F8 /* naomi_network.cpp in Sources */,
|
||||
AED73E662348E45000ECDB64 /* iomapper.cpp in Sources */,
|
||||
AE649BFD218C552500EF4A81 /* lpc_intrin_avx2.c in Sources */,
|
||||
AE649BF8218C552500EF4A81 /* fixed_intrin_sse2.c in Sources */,
|
||||
|
|
Loading…
Reference in New Issue