diff --git a/CMakeLists.txt b/CMakeLists.txt index 13efa1c..c2a1339 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,10 @@ if(SDL1_2_BUILD) target_link_libraries(SDL::SDL_mixer INTERFACE SDL::SDL) endif() +if(NOT PS1_BUILD) + find_package(ENET 1.3 REQUIRED) +endif() + set(cdroot ${CMAKE_BINARY_DIR}/cdimg) file(MAKE_DIRECTORY ${cdroot}) diff --git a/README.md b/README.md index b808c01..b8a685e 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ cd build SDLDIR= \ SDLMIXERDIR= \ SDLGFXDIR= \ + ENETDIR= \ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/win9x-toolchain.cmake make -j$(nproc --all) ``` @@ -106,6 +107,9 @@ building `SDL_mixer`. - `SDLGFXDIR` is the path to the cross-compiled version for `SDL_gfx`, which would correspond to `./configure --prefix=$SDLGFXDIR` used in building `SDL_gfx`. +- `ENETDIR` is the path to the cross-compiled version for `enet`, +which would correspond to `./configure --prefix=$ENETDIR` used in +building `enet`. A stripped version of the executable, as well as game assets, will be located in `build/cdimg`. diff --git a/cmake/win9x.cmake b/cmake/win9x.cmake index a4d8f79..0d91520 100644 --- a/cmake/win9x.cmake +++ b/cmake/win9x.cmake @@ -6,6 +6,7 @@ add_custom_command(OUTPUT ${cdroot}/${PROJECT_NAME} add_custom_target(stripped-exe ALL DEPENDS ${cdroot}/${PROJECT_NAME}) target_link_libraries(SDL::SDL INTERFACE gdi32 user32 winmm dxguid) +target_link_libraries(ENET INTERFACE ws2_32) add_compile_options(-march=i386) diff --git a/doc/BUILD-win9x.md b/doc/BUILD-win9x.md index b8b053f..cb6368d 100644 --- a/doc/BUILD-win9x.md +++ b/doc/BUILD-win9x.md @@ -132,3 +132,11 @@ WAVE files, support for other audio formats is not required. CFLAGS='-ffunction-sections -fdata-sections' \ CC=i386-mingw32-gcc ``` + +## ENET + +```sh +../enet-1.3.17/configure --prefix=$HOME/enet-1.3.17-win32 \ + --host=i386-mingw32 CFLAGS='-ffunction-sections -fdata-sections' \ + CC=i386-mingw32-gcc +``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da04812..7a35715 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ set(components keyboard menu mouse + net pad peripheral player diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt new file mode 100644 index 0000000..38cd8df --- /dev/null +++ b/src/net/CMakeLists.txt @@ -0,0 +1,32 @@ +set(src "src/common.c") + +if(PS1_BUILD) + set(src ${src} + "src/ps1/net.c" + ) +else() + set(src ${src} + "src/net.c" + "src/enet/ipv4.c" + ) + + if(WIN9X_BUILD OR ${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") + set(src ${src} "src/win9x/serial.c") + else() + # Assume POSIX if the command below executes successfully + execute_process(COMMAND uname -m RESULT_VARIABLE result OUTPUT_QUIET) + + if(result) + message(FATAL_ERROR "Unknown operating system") + else() + set(src ${src} "src/posix/serial.c") + endif() + endif() + + set(priv_deps ${priv_deps} util ENET) + set(privinc ${privinc} "privinc") +endif() + +add_library(net ${src}) +target_include_directories(net PUBLIC "inc" PRIVATE ${privinc}) +target_link_libraries(net PRIVATE ${priv_deps}) diff --git a/src/net/inc/net.h b/src/net/inc/net.h new file mode 100644 index 0000000..cdfeb0e --- /dev/null +++ b/src/net/inc/net.h @@ -0,0 +1,78 @@ +#ifndef NET_H +#define NET_H + +#include +#include + +enum net_domain +{ + NET_DOMAIN_IPV4, + NET_DOMAIN_SERIAL +}; + +union net_connect +{ + struct net_connect_common + { + enum net_domain domain; + void (*on_connected)(void *arg); + void (*on_disconnected)(void *arg); + void *arg; + } common; + + struct net_connect_ipv4 + { + struct net_connect_common common; + const char *addr; + uint16_t port; + } ipv4; + + struct net_connect_serial + { + struct net_connect_common common; + const char *dev; + unsigned long baud; + + enum + { + NET_PARITY_NONE, + NET_PARITY_ODD, + NET_PARITY_EVEN + } parity; + } serial; +}; + +union net_server +{ + struct net_server_common + { + enum net_domain domain; + unsigned max_players; + } common; + + struct net_server_ipv4 + { + struct net_server_common common; + uint16_t port; + } ipv4; + + struct net_server_serial + { + struct net_server_common common; + } serial; +}; + +struct net_socket; + +int net_init(void); +void net_deinit(void); +int net_update(struct net_socket *s); + +struct net_socket *net_server(const union net_server *srv); +struct net_socket *net_connect(const union net_connect *c); +int net_read(struct net_socket *s, void *buf, size_t n); +int net_write(struct net_socket *s, const void *buf, size_t n); +int net_close(struct net_socket *s); +const char *net_domain_str(enum net_domain d); + +#endif /* NET_H */ diff --git a/src/net/privinc/net_private.h b/src/net/privinc/net_private.h new file mode 100644 index 0000000..53d7cd1 --- /dev/null +++ b/src/net/privinc/net_private.h @@ -0,0 +1,30 @@ +#ifndef NET_PRIVATE_H +#define NET_PRIVATE_H + +#include + +struct net_socket +{ + enum net_domain d; + struct net_socket_domain *s; +}; + +int net_init_ipv4(void); +void net_deinit_ipv4(void); +struct net_socket_domain *net_server_ipv4(const union net_server *srv); +struct net_socket_domain *net_connect_ipv4(const union net_connect *c); +int net_read_ipv4(struct net_socket_domain *h, void *buf, size_t n); +int net_write_ipv4(struct net_socket_domain *h, const void *buf, size_t n); +int net_close_ipv4(struct net_socket_domain *h); +int net_update_ipv4(struct net_socket_domain *h); + +int net_init_serial(void); +void net_deinit_serial(void); +struct net_socket_domain *net_server_serial(const union net_server *srv); +struct net_socket_domain *net_connect_serial(const union net_connect *c); +int net_read_serial(struct net_socket_domain *h, void *buf, size_t n); +int net_write_serial(struct net_socket_domain *h, const void *buf, size_t n); +int net_close_serial(struct net_socket_domain *h); +int net_update_serial(struct net_socket_domain *h); + +#endif /* NET_PRIVATE_H */ diff --git a/src/net/src/common.c b/src/net/src/common.c new file mode 100644 index 0000000..8e3c386 --- /dev/null +++ b/src/net/src/common.c @@ -0,0 +1,12 @@ +#include + +const char *net_domain_str(const enum net_domain d) +{ + static const char *const s[] = + { + [NET_DOMAIN_IPV4] = "UDP/IPv4", + [NET_DOMAIN_SERIAL] = "Serial" + }; + + return s[d]; +} diff --git a/src/net/src/enet/ipv4.c b/src/net/src/enet/ipv4.c new file mode 100644 index 0000000..16786cc --- /dev/null +++ b/src/net/src/enet/ipv4.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include + +struct net_socket_domain +{ + ENetHost *host; + ENetPeer *peers; + void (*on_connected)(void *arg); + void (*on_disconnected)(void *arg); + void *arg; +}; + +enum +{ + CHANNEL, + MAX_CHANNELS +}; + +struct net_socket_domain *net_connect_ipv4(const union net_connect *const c) +{ + struct net_socket_domain *const s = calloc(1, sizeof *s); + + if (!s) + goto failure; + + s->on_connected = c->common.on_connected; + s->on_disconnected = c->common.on_disconnected; + s->arg = c->common.arg; + s->host = enet_host_create(NULL, 1, MAX_CHANNELS, 0, 0); + + if (!s->host) + { + fprintf(stderr, "%s: enet_host_create failed\n", __func__); + goto failure; + } + + ENetAddress addr; + + if (enet_address_set_host(&addr, c->ipv4.addr)) + { + fprintf(stderr, "%s: enet_address_set_host failed\n", __func__); + goto failure; + } + + addr.port = c->ipv4.port; + + if (!(s->peers = enet_host_connect(s->host, &addr, MAX_CHANNELS, 0))) + { + fprintf(stderr, "%s: enet_host_connect failed\n", __func__); + goto failure; + } + + return s; + +failure: + + if (s && s->host) + enet_host_destroy(s->host); + + free(s); + return NULL; +} + +int net_read_ipv4(struct net_socket_domain *const s, void *const buf, + const size_t n) +{ + return -1; +} + +int net_write_ipv4(struct net_socket_domain *const s, const void *const buf, + const size_t n) +{ + return -1; +} + +int net_close_ipv4(struct net_socket_domain *const s) +{ + if (s && s->host) + enet_host_destroy(s->host); + + free(s); + return 0; +} + +int net_update_ipv4(struct net_socket_domain *const s) +{ + int res; + ENetEvent ev; + + while ((res = enet_host_service(s->host, &ev, 0)) > 0) + { + switch (ev.type) + { + case ENET_EVENT_TYPE_CONNECT: + if (s->on_connected) + s->on_connected(s->arg); + + break; + + case ENET_EVENT_TYPE_DISCONNECT: + if (s->on_disconnected) + s->on_disconnected(s->arg); + + case ENET_EVENT_TYPE_RECEIVE: + break; + + case ENET_EVENT_TYPE_NONE: + break; + } + } + + if (res < 0) + { + fprintf(stderr, "%s: enet_host_service failed\n", __func__); + return res; + } + + return 0; +} + +struct net_socket_domain *net_server_ipv4(const union net_server *const srv) +{ + struct net_socket_domain *const s = calloc(1, sizeof *s); + + if (!s) + goto failure; + + const ENetAddress addr = + { + .port = srv->ipv4.port + }; + + s->host = enet_host_create(&addr, srv->common.max_players, + MAX_CHANNELS, 0, 0); + + if (!s->host) + { + fprintf(stderr, "%s: enet_host_create failed\n", __func__); + goto failure; + } + + return s; + +failure: + free(s); + return NULL; +} + +int net_init_ipv4(void) +{ + return enet_initialize(); +} + +void net_deinit_ipv4(void) +{ + enet_deinitialize(); +} diff --git a/src/net/src/net.c b/src/net/src/net.c new file mode 100644 index 0000000..32d372a --- /dev/null +++ b/src/net/src/net.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include + +struct net_socket *net_connect(const union net_connect *const c) +{ + struct net_socket *const s = calloc(1, sizeof *s); + + if (!s) + goto failure; + + static struct net_socket_domain *(*const f[])(const union net_connect *) = + { + [NET_DOMAIN_IPV4] = net_connect_ipv4, + [NET_DOMAIN_SERIAL] = net_connect_serial + }; + + if (!(s->s = f[c->common.domain](c))) + goto failure; + + return s; + +failure: + free(s); + return NULL; +} + +int net_read(struct net_socket *const s, void *const buf, const size_t n) +{ + static int (*const f[])(struct net_socket_domain *, void *, size_t) = + { + [NET_DOMAIN_IPV4] = net_read_ipv4, + [NET_DOMAIN_SERIAL] = net_read_serial + }; + + return f[s->d](s->s, buf, n); +} + +int net_write(struct net_socket *const s, const void *const buf, const size_t n) +{ + static int (*const f[])(struct net_socket_domain *, const void *, size_t) = + { + [NET_DOMAIN_IPV4] = net_write_ipv4, + [NET_DOMAIN_SERIAL] = net_write_serial + }; + + return f[s->d](s->s, buf, n); +} + +int net_close(struct net_socket *const s) +{ + if (!s) + return 0; + + static int (*const f[])(struct net_socket_domain *) = + { + [NET_DOMAIN_IPV4] = net_close_ipv4, + [NET_DOMAIN_SERIAL] = net_close_serial + }; + + const int res = f[s->d](s->s); + + free(s); + return res; +} + +int net_update(struct net_socket *const s) +{ + static int (*const f[])(struct net_socket_domain *) = + { + [NET_DOMAIN_IPV4] = net_update_ipv4, + [NET_DOMAIN_SERIAL] = net_update_serial + }; + + return f[s->d](s->s); +} + +struct net_socket *net_server(const union net_server *const srv) +{ + struct net_socket *const s = calloc(1, sizeof *s); + + if (!s) + goto failure; + + s->d = srv->common.domain; + + static struct net_socket_domain *(*const f[])(const union net_server *) = + { + [NET_DOMAIN_IPV4] = net_server_ipv4, + [NET_DOMAIN_SERIAL] = net_server_serial + }; + + if (!(s->s = f[s->d](srv))) + goto failure; + +failure: + net_close(s); + return NULL; +} + +int net_init(void) +{ + return net_init_ipv4() || net_init_serial(); +} + +void net_deinit(void) +{ + net_deinit_ipv4(); + net_deinit_serial(); +} diff --git a/src/net/src/posix/serial.c b/src/net/src/posix/serial.c new file mode 100644 index 0000000..46eb2fa --- /dev/null +++ b/src/net/src/posix/serial.c @@ -0,0 +1,44 @@ +#include +#include +#include + +int net_read_serial(struct net_socket_domain *const h, void *const buf, + const size_t n) +{ + return -1; +} + +int net_write_serial(struct net_socket_domain *const h, const void *const buf, + const size_t n) +{ + return -1; +} + +int net_close_serial(struct net_socket_domain *const h) +{ + return -1; +} + +int net_update_serial(struct net_socket_domain *const h) +{ + return -1; +} + +struct net_socket_domain *net_connect_serial(const union net_connect *const srv) +{ + return NULL; +} + +struct net_socket_domain *net_server_serial(const union net_server *const srv) +{ + return NULL; +} + +int net_init_serial(void) +{ + return 0; +} + +void net_deinit_serial(void) +{ +} diff --git a/src/net/src/ps1/net.c b/src/net/src/ps1/net.c new file mode 100644 index 0000000..9ddf6f0 --- /dev/null +++ b/src/net/src/ps1/net.c @@ -0,0 +1,42 @@ +#include +#include +#include + +int net_read(struct net_socket *const h, void *const buf, const size_t n) +{ + return -1; +} + +int net_write(struct net_socket *const h, const void *const buf, const size_t n) +{ + return -1; +} + +int net_close(struct net_socket *const h) +{ + return -1; +} + +int net_update(struct net_socket *const h) +{ + return -1; +} + +struct net_socket *net_connect(const union net_connect *const c) +{ + return NULL; +} + +struct net_socket *net_server(const union net_server *const c) +{ + return NULL; +} + +int net_init(void) +{ + return -1; +} + +void net_deinit(void) +{ +} diff --git a/src/net/src/win9x/serial.c b/src/net/src/win9x/serial.c new file mode 100644 index 0000000..46eb2fa --- /dev/null +++ b/src/net/src/win9x/serial.c @@ -0,0 +1,44 @@ +#include +#include +#include + +int net_read_serial(struct net_socket_domain *const h, void *const buf, + const size_t n) +{ + return -1; +} + +int net_write_serial(struct net_socket_domain *const h, const void *const buf, + const size_t n) +{ + return -1; +} + +int net_close_serial(struct net_socket_domain *const h) +{ + return -1; +} + +int net_update_serial(struct net_socket_domain *const h) +{ + return -1; +} + +struct net_socket_domain *net_connect_serial(const union net_connect *const srv) +{ + return NULL; +} + +struct net_socket_domain *net_server_serial(const union net_server *const srv) +{ + return NULL; +} + +int net_init_serial(void) +{ + return 0; +} + +void net_deinit_serial(void) +{ +}