diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-09-27 17:03:06 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2022-11-01 16:26:16 +0100 |
| commit | 980858186149651df5543b6fc99a4f7db0cdd089 (patch) | |
| tree | d347200b0a562d84df505097651ad0642f207fdd | |
| parent | 39f50e601d395bbd2d78d0147ac530b756da2fff (diff) | |
WIP
84 files changed, 5151 insertions, 777 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c2a1339..86037b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,12 +16,13 @@ if(CMAKE_TOOLCHAIN_FILE MATCHES "ps1") elseif(CMAKE_TOOLCHAIN_FILE MATCHES "win9x") set(WIN9X_BUILD 1) set(SDL1_2_BUILD 1) + set(exec_flags WIN32) else() set(HOST_BUILD 1) set(SDL1_2_BUILD 1) endif() -add_executable(${PROJECT_NAME} "src/main.c") +add_executable(${PROJECT_NAME} ${exec_flags} "src/main.c") if(SDL1_2_BUILD) find_package(SDL 1.2 REQUIRED) @@ -83,6 +83,10 @@ This will generate a `.bin`/`.cue` file pair in `build` that can be played on an emulator or burnt into a CD-r in order to play the game on real hardware. +A custom version of [PSXSDK](https://git.disroot.org/xavi92/psxsdk) is +required. Once built, the installation prefix must be stored into an +environment variable called `PSXSDK_PATH`. + #### Microsoft® Win9x ```sh diff --git a/cmake/host.cmake b/cmake/host.cmake index 6fa47b9..36d7d70 100644 --- a/cmake/host.cmake +++ b/cmake/host.cmake @@ -1,5 +1,5 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(cflags ${cflags} -Og) + set(cflags ${cflags} -Og) else() set(cflags ${cflags} -O3) endif() diff --git a/cmake/win9x.cmake b/cmake/win9x.cmake index 0d91520..c66ed5e 100644 --- a/cmake/win9x.cmake +++ b/cmake/win9x.cmake @@ -1,12 +1,13 @@ -add_custom_command(OUTPUT ${cdroot}/${PROJECT_NAME} - COMMAND i386-mingw32-strip ${PROJECT_NAME} -o ${cdroot}/${PROJECT_NAME} +add_custom_command(OUTPUT ${cdroot}/${PROJECT_NAME}.exe + COMMAND i386-mingw32-strip ${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX} + -o ${cdroot}/${PROJECT_NAME}.exe WORKING_DIRECTORY ${CMAKE_BINARY_DIR} DEPENDS ${PROJECT_NAME} VERBATIM) -add_custom_target(stripped-exe ALL DEPENDS ${cdroot}/${PROJECT_NAME}) +add_custom_target(stripped-exe ALL DEPENDS ${cdroot}/${PROJECT_NAME}.exe) target_link_libraries(SDL::SDL INTERFACE gdi32 user32 winmm dxguid) -target_link_libraries(ENET INTERFACE ws2_32) +target_link_libraries(ENET INTERFACE wsock32) add_compile_options(-march=i386) @@ -14,7 +15,7 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug") # i386-mingw32-gcc 3.4.5 does not support -Og. set(cflags ${cflags} -O0) else() - set(cflags ${cflags} -O2) + set(cflags ${cflags} -Os) endif() include("${CMAKE_CURRENT_LIST_DIR}/fetch-libfixmath.cmake") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a35715..bb05f29 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ # Avoid C11 since it is not supported by the i386-mingw32 toolchain. -set(cflags -Wall -g3 -ffunction-sections -fdata-sections -pedantic -std=c99) +set(cflags ${cflags} -Wall -g3 -ffunction-sections -fdata-sections -pedantic -std=c99) set(components building @@ -16,13 +16,16 @@ set(components menu mouse net + packet pad peripheral player resource + settings sfx system terrain + transport unit util ) diff --git a/src/camera/src/mouse.c b/src/camera/src/mouse.c index 22fc3ba..30fedb9 100644 --- a/src/camera/src/mouse.c +++ b/src/camera/src/mouse.c @@ -13,6 +13,9 @@ static void cursor_update(struct cursor *const c, const struct mouse *const m) c->x = m->x; c->y = m->y; + c->state = mouse_pressed(m, MOUSE_BUTTON_LEFT) || + mouse_pressed(m, MOUSE_BUTTON_RIGHT) ? + CURSOR_STATE_PRESSED: CURSOR_STATE_IDLE; } static void update_speed(struct camera *const cam, const struct mouse *const m) diff --git a/src/camera/src/pad.c b/src/camera/src/pad.c index 13cb43e..f3af527 100644 --- a/src/camera/src/pad.c +++ b/src/camera/src/pad.c @@ -36,8 +36,8 @@ static void cursor_update(struct camera *const cam, && (c->y != c->y_init || cam->y <= -cam->dim.h)) c->y += STEP; - c->state = input_pad_pressed(in, p, PAD_KEY_A) || - input_pad_pressed(in, p, PAD_KEY_B) ? + c->state = pad_pressed(p, PAD_KEY_A) || + pad_pressed(p, PAD_KEY_B) ? CURSOR_STATE_PRESSED: CURSOR_STATE_IDLE; } diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index 0c21b0c..ce76968 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(game "src/game.c" "src/res.c") target_include_directories(game PUBLIC "inc" PRIVATE "privinc") -target_link_libraries(game PRIVATE +target_link_libraries(game PUBLIC peripheral PRIVATE building container font diff --git a/src/game/inc/game.h b/src/game/inc/game.h index 5298b9f..66e5e79 100644 --- a/src/game/inc/game.h +++ b/src/game/inc/game.h @@ -1,15 +1,35 @@ #ifndef GAME_H #define GAME_H -#include <stddef.h> +#include <peripheral.h> #ifdef __cplusplus extern "C" { #endif +enum +{ + GAME_MAX_PLAYERS = 4, + GAME_PLAYER_NAME_LEN = 16 +}; + struct game_cfg { + struct game_cfg_player + { + enum game_cfg_player_type + { + GAME_CFG_PLAYER_TYPE_HUMAN, + GAME_CFG_PLAYER_TYPE_BOT, + GAME_CFG_PLAYER_TYPE_NET + } type; + + char name[GAME_PLAYER_NAME_LEN]; + } *players; + + size_t n; + union peripheral *p; }; int game_resinit(void); diff --git a/src/game/src/game.c b/src/game/src/game.c index 83be2fd..baf9a86 100644 --- a/src/game/src/game.c +++ b/src/game/src/game.c @@ -11,36 +11,32 @@ int game(const struct game_cfg *const cfg) { int ret = -1; - enum {HUMAN_PLAYERS = 1, MAP_RESOURCES = 3}; - struct human_player humans[HUMAN_PLAYERS]; + enum {MAP_RESOURCES = 3}; + struct human_player human; struct terrain_map map; terrain_init(&map); building_set_alive_cb(terrain_block_update, &map); - for (size_t i = 0; i < sizeof humans / sizeof *humans; i++) + const struct human_player_cfg hcfg = { - const struct human_player_cfg cfg = + .p = cfg->p, + .dim = { - .sel_periph = PERIPHERAL_TYPE_KEYBOARD_MOUSE, - .padn = i, - .dim = - { - .w = MAP_W, - .h = MAP_H, - }, + .w = MAP_W, + .h = MAP_H, + }, - .pl = - { - .team = PLAYER_COLOR_BLUE, - .x = 80, - .y = 40 - } - }; + .pl = + { + .team = PLAYER_COLOR_BLUE, + .x = 80, + .y = 40 + } + }; - if (human_player_init(&cfg, &humans[i])) - goto end; - } + if (human_player_init(&hcfg, &human)) + goto end; struct resource res[MAP_RESOURCES] = {0}; @@ -72,28 +68,23 @@ int game(const struct game_cfg *const cfg) { system_loop(); - for (size_t i = 0; i < sizeof humans / sizeof *humans; i++) + if (human.pl.alive) { - const struct human_player *const h = &humans[i]; - - if (h->pl.alive) + struct player_others o = { - struct player_others o = - { - .res = res, - .n_res = sizeof res / sizeof *res - }; - - human_player_update(&humans[i], &o); - exit |= humans[i].periph.common.exit; - terrain_update(&map); - - if (terrain_render(&map, &h->cam) - || human_player_render(h, &o)) - goto end; - - /* TODO: render AI players. */ - } + .res = res, + .n_res = sizeof res / sizeof *res + }; + + human_player_update(&human, &o); + exit |= human.periph->common.exit; + terrain_update(&map); + + if (terrain_render(&map, &human.cam) + || human_player_render(&human, &o)) + goto end; + + /* TODO: render AI players. */ } instance_cyclic(); diff --git a/src/gfx/inc/gfx.h b/src/gfx/inc/gfx.h index 9c4eccd..a6a16bc 100644 --- a/src/gfx/inc/gfx.h +++ b/src/gfx/inc/gfx.h @@ -13,8 +13,10 @@ extern "C" int gfx_init(void); int gfx_draw(void); int gfx_toggle_fullscreen(void); -bool gfx_toggle_fullscreen_available(void); +int gfx_set_fullscreen(short w, short h); +bool gfx_fullscreen_available(void); bool gfx_fullscreen(void); +int gfx_display_size(short *w, short *h); void sprite_sort(struct sprite *s); int sprite_clone(const struct sprite *src, struct sprite *dst); void rect_init(struct rect *r); diff --git a/src/gfx/ps1/src/env.c b/src/gfx/ps1/src/env.c index 41f490e..a373b9c 100644 --- a/src/gfx/ps1/src/env.c +++ b/src/gfx/ps1/src/env.c @@ -38,7 +38,13 @@ int gfx_toggle_fullscreen(void) return -1; } -bool gfx_toggle_fullscreen_available(void) +int gfx_set_fullscreen(const short w, const short h) +{ + /* Not supported. */ + return -1; +} + +bool gfx_fullscreen_available(void) { return false; } @@ -48,6 +54,13 @@ bool gfx_fullscreen(void) return true; } +int gfx_display_size(short *const w, short *const h) +{ + *w = SCREEN_W; + *h = SCREEN_H; + return 0; +} + bool gfx_inside_drawenv(const short x, const short y, const short w, const short h) { diff --git a/src/gfx/sdl-1.2/src/env.c b/src/gfx/sdl-1.2/src/env.c index 27ed55d..4bd174a 100644 --- a/src/gfx/sdl-1.2/src/env.c +++ b/src/gfx/sdl-1.2/src/env.c @@ -102,6 +102,13 @@ SDL_Surface *gfx_screen(void) return screen; } +int gfx_display_size(short *const w, short *const h) +{ + *w = display.w; + *h = display.h; + return 0; +} + int gfx_init(void) { if (SDL_InitSubSystem(SDL_INIT_VIDEO)) @@ -157,13 +164,15 @@ int gfx_toggle_fullscreen(void) const int w = fullscreen ? display.w : windowed.w; const int h = fullscreen ? display.h : windowed.h; - if ((resize_screen(w, h, fullscreen))) - return -1; + return resize_screen(w, h, fullscreen); +} - return 0; +int gfx_set_fullscreen(const short w, const short h) +{ + return resize_screen(display.w = w, display.h = h, fullscreen = true); } -bool gfx_toggle_fullscreen_available(void) +bool gfx_fullscreen_available(void) { return true; } diff --git a/src/gfx/sdl-1.2/src/sprite.c b/src/gfx/sdl-1.2/src/sprite.c index 4c1ecbc..011b2ec 100644 --- a/src/gfx/sdl-1.2/src/sprite.c +++ b/src/gfx/sdl-1.2/src/sprite.c @@ -37,7 +37,6 @@ int sprite_screen_resize_ev(struct sprite *const s) int ret = -1; SDL_Surface *const old = s->s, *const old_x = s->s_x; - /* Magenta as transparent. */ if (s->transparent && (set_transparent(old) || set_transparent(old_x))) goto end; else if (!(s->s = SDL_DisplayFormat(old))) diff --git a/src/gui/inc/gui/rounded_rect.h b/src/gui/inc/gui/rounded_rect.h index e1203e4..13242f4 100644 --- a/src/gui/inc/gui/rounded_rect.h +++ b/src/gui/inc/gui/rounded_rect.h @@ -15,11 +15,9 @@ struct gui_rounded_rect { struct gui_common common; unsigned short w, h; + bool adjust; }; -UTIL_STATIC_ASSERT(!offsetof(struct gui_rounded_rect, common), - "unexpected offset for struct gui_rounded_rect"); - void gui_rounded_rect_init(struct gui_rounded_rect *r); enum @@ -36,6 +34,9 @@ enum extern struct sprite gui_rounded_rect_sprites[MAX_GUI_ROUNDED_RECT_SPRITES]; +UTIL_STATIC_ASSERT(!offsetof(struct gui_rounded_rect, common), + "unexpected offset for struct gui_rounded_rect"); + #ifdef __cplusplus } #endif diff --git a/src/gui/src/button_type1.c b/src/gui/src/button_type1.c index 16038b5..f07f2f3 100644 --- a/src/gui/src/button_type1.c +++ b/src/gui/src/button_type1.c @@ -2,6 +2,7 @@ #include <gui/button.h> #include <gui_private.h> #include <gui_button_private.h> +#include <stdio.h> struct sprite gui_button_sprites[MAX_GUI_BUTTON_SPRITES]; @@ -63,7 +64,11 @@ static int render_mid(const struct gui_button *const b, } } else + { + fprintf(stderr, "%s: unexpected negative size for button %p\n", + __func__, (void *)b); return -1; + } return 0; } diff --git a/src/gui/src/line_edit.c b/src/gui/src/line_edit.c index ef37708..df52fd0 100644 --- a/src/gui/src/line_edit.c +++ b/src/gui/src/line_edit.c @@ -130,7 +130,10 @@ static void on_char(const char ch, void *const user) allowed = true; if (allowed && l->i + 1 < l->sz) + { l->text[l->i++] = ch; + l->text[l->i] = '\0'; + } } static void on_erase(void *const user) @@ -196,11 +199,11 @@ void gui_line_edit_init(struct gui_line_edit *const l, char *const buf, }, .text = buf, - .sz = sz + .sz = sz, + .i = strlen(buf) }; gui_label_init(&l->label); - memset(l->text, '\0', l->sz); l->label.common.hcentered = true; l->label.common.vcentered = true; l->label.text = l->text; diff --git a/src/gui/src/rounded_rect.c b/src/gui/src/rounded_rect.c index cd2bbff..b60a05a 100644 --- a/src/gui/src/rounded_rect.c +++ b/src/gui/src/rounded_rect.c @@ -222,11 +222,69 @@ static void get_dim(const struct gui_common *const g, short *const w, *h = r->h; } +static void add_child(struct gui_common *const parent, + struct gui_common *const child) +{ + struct gui_rounded_rect *const r = (struct gui_rounded_rect *)parent; + + if (!r->adjust || child->hidden || !child->cb || !child->cb->get_dim) + return; + + short w, h; + const short ref_w = refs[GUI_ROUNDED_RECT_MID_VERT].w, + ref_h = refs[GUI_ROUNDED_RECT_MID].h, + min_w = ref_w * 2 + refs[GUI_ROUNDED_RECT_MID].w, + min_h = ref_h * 2 + refs[GUI_ROUNDED_RECT_MID_VERT].h; + + child->cb->get_dim(child, &w, &h); + + if (w > r->w - (ref_w * 2)) + r->w = w + (ref_w * 2); + + if (h > r->h - (ref_h * 2)) + r->h = h + (ref_h * 2); + + if (r->w < min_w) + r->w = min_w; + + if (r->h < min_h) + r->h = min_h; + + if (!child->hcentered) + child->xoff = ref_w; + + if (!child->vcentered) + child->yoff = ref_h; +} + +static int update(struct gui_common *const g, + const union peripheral *const p, const struct camera *const cam, + struct input *const in) +{ + struct gui_rounded_rect *const r = (struct gui_rounded_rect *)g; + + if (r->adjust) + { + r->w = r->h = 0; + + struct gui_common *const child = r->common.child; + + if (child && child->cb && child->cb->get_dim) + add_child(&r->common, child); + + for (struct gui_common *s = child->sibling; s; s = s->sibling) + add_child(&r->common, s); + } + + return 0; +} + void gui_rounded_rect_init(struct gui_rounded_rect *const r) { static const struct gui_common_cb cb = { .get_dim = get_dim, + .update = update, .render = render }; diff --git a/src/input/inc/input.h b/src/input/inc/input.h index 2dd401b..5780000 100644 --- a/src/input/inc/input.h +++ b/src/input/inc/input.h @@ -22,7 +22,7 @@ struct input void *user; unsigned char t; bool repeat; - struct keyboard_combo prev; + struct keyboard_input prev; }; void input_update(struct input *in, const union peripheral *p); diff --git a/src/input/src/input.c b/src/input/src/input.c index 26116c8..ee6d4b7 100644 --- a/src/input/src/input.c +++ b/src/input/src/input.c @@ -8,8 +8,7 @@ #include <stddef.h> #include <string.h> -static void send_input(struct input *const in, const struct keyboard *const k, - const enum keyboard_key key) +static void send_key(struct input *const in, const enum keyboard_key key) { if (key != KEYBOARD_KEY_NONE) { @@ -24,28 +23,44 @@ static void send_input(struct input *const in, const struct keyboard *const k, break; default: - { - const char ch = keyboard_to_char(k, key); - - if (isprint(ch)) - in->cb(ch, in->user); - } break; } } } +static void send_input(struct input *const in, + const struct keyboard_input *const ki) +{ + { + const struct keyboard_combo *const c = &ki->combo; + + for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++) + send_key(in, c->keys[i]); + } + + { + const struct keyboard_chars *const c = &ki->chars; + + for (size_t i = 0; i < sizeof c->c / sizeof *c->c; i++) + { + const char ch = c->c[i]; + + if (ch) + in->cb(ch, in->user); + } + } +} + static void update_keyboard(struct input *const in, const struct keyboard *const k) { - struct keyboard_combo c; + struct keyboard_input ki; - if (keyboard_any_justpressed(k, &c)) - for (size_t i = 0; i < sizeof c.keys / sizeof *c.keys; i++) - send_input(in, k, c.keys[i]); - else if (keyboard_any_pressed(k, &c)) + if (keyboard_any_justpressed(k, &ki)) + send_input(in, &ki); + else if (keyboard_any_pressed(k, &ki)) { - if (memcmp(&c, &in->prev, sizeof c)) + if (memcmp(&ki, &in->prev, sizeof ki)) { in->repeat = false; in->t = 0; @@ -61,15 +76,12 @@ static void update_keyboard(struct input *const in, } else if (++in->t >= SHORT_INTERVAL) { - for (size_t i = 0; - i < sizeof c.keys / sizeof *c.keys; i++) - send_input(in, k, c.keys[i]); - + send_input(in, &ki); in->t = 0; } } - in->prev = c; + in->prev = ki; } else { diff --git a/src/keyboard/CMakeLists.txt b/src/keyboard/CMakeLists.txt index 5fa8892..2cdf85c 100644 --- a/src/keyboard/CMakeLists.txt +++ b/src/keyboard/CMakeLists.txt @@ -3,9 +3,11 @@ set(inc "inc") if(PS1_BUILD) set(src ${src} "ps1/src/keyboard.c") + set(inc ${inc} "ps1/inc") set(privdeps ${privdeps} PSXSDK::PSXSDK) elseif(SDL1_2_BUILD) set(src ${src} "sdl-1.2/src/keyboard.c") + set(inc ${inc} "sdl-1.2/inc") set(privdeps ${privdeps} SDL::SDL) endif() diff --git a/src/keyboard/inc/keyboard.h b/src/keyboard/inc/keyboard.h index 0fb0179..8bae4d3 100644 --- a/src/keyboard/inc/keyboard.h +++ b/src/keyboard/inc/keyboard.h @@ -1,7 +1,9 @@ #ifndef KEYBOARD_H #define KEYBOARD_H -#include <keyboard_key.h> +#include <keyboard/key.h> +#include <keyboard/combo.h> +#include <keyboard/port.h> #include <stdbool.h> #include <stddef.h> @@ -12,26 +14,33 @@ extern "C" #define KEYBOARD_COMBO(...) (const struct keyboard_combo){.keys = {__VA_ARGS__}} -enum {KEYBOARD_MAX_COMBO_KEYS = 3}; - struct keyboard { - struct keyboard_combo + struct keyboard_input { - enum keyboard_key keys[KEYBOARD_MAX_COMBO_KEYS]; - } combo, oldcombo; + struct keyboard_combo + { + enum keyboard_key keys[KEYBOARD_MAX_COMBO_KEYS]; + } combo; + + struct keyboard_chars + { + char c[KEYBOARD_MAX_COMBO_KEYS]; + } chars; + } input, oldinput; size_t i; + struct keyboard_port port; }; void keyboard_init(struct keyboard *k); void keyboard_update(struct keyboard *k); +bool keyboard_available(void); bool keyboard_justpressed(const struct keyboard *k, const struct keyboard_combo *c); bool keyboard_pressed(const struct keyboard *k, const struct keyboard_combo *c); bool keyboard_justreleased(const struct keyboard *k, const struct keyboard_combo *c); -bool keyboard_any_justpressed(const struct keyboard *k, struct keyboard_combo *c); -bool keyboard_any_pressed(const struct keyboard *k, struct keyboard_combo *c); -char keyboard_to_char(const struct keyboard *k, enum keyboard_key key); +bool keyboard_any_justpressed(const struct keyboard *k, struct keyboard_input *in); +bool keyboard_any_pressed(const struct keyboard *k, struct keyboard_input *in); const char *keyboard_key_str(enum keyboard_key k); #ifdef __cplusplus diff --git a/src/keyboard/inc/keyboard/combo.h b/src/keyboard/inc/keyboard/combo.h new file mode 100644 index 0000000..e0ef759 --- /dev/null +++ b/src/keyboard/inc/keyboard/combo.h @@ -0,0 +1,15 @@ +#ifndef KEYBOARD_COMBO_H +#define KEYBOARD_COMBO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum {KEYBOARD_MAX_COMBO_KEYS = 3}; + +#ifdef __cplusplus +} +#endif + +#endif /* KEYBOARD_COMBO_H */ diff --git a/src/keyboard/inc/keyboard_key.h b/src/keyboard/inc/keyboard/key.h index 49c31d2..0a25a97 100644 --- a/src/keyboard/inc/keyboard_key.h +++ b/src/keyboard/inc/keyboard/key.h @@ -59,6 +59,7 @@ extern "C" X(KEYBOARD_KEY_SPACE) \ X(KEYBOARD_KEY_MINUS) \ X(KEYBOARD_KEY_DOT) \ + X(KEYBOARD_KEY_RETURN) \ X(KEYBOARD_KEY_SLASH) enum keyboard_key diff --git a/src/keyboard/ps1/inc/keyboard/port.h b/src/keyboard/ps1/inc/keyboard/port.h new file mode 100644 index 0000000..d0d5ffd --- /dev/null +++ b/src/keyboard/ps1/inc/keyboard/port.h @@ -0,0 +1,18 @@ +#ifndef KEYBOARD_PS1_H +#define KEYBOARD_PS1_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct keyboard_port +{ + int dummy; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* KEYBOARD_PS1_H */ diff --git a/src/keyboard/ps1/src/keyboard.c b/src/keyboard/ps1/src/keyboard.c index 69e7a90..ea31cd3 100644 --- a/src/keyboard/ps1/src/keyboard.c +++ b/src/keyboard/ps1/src/keyboard.c @@ -3,3 +3,8 @@ void keyboard_update(struct keyboard *const k) { } + +bool keyboard_available(void) +{ + return false; +} diff --git a/src/keyboard/sdl-1.2/inc/keyboard/port.h b/src/keyboard/sdl-1.2/inc/keyboard/port.h new file mode 100644 index 0000000..69628f1 --- /dev/null +++ b/src/keyboard/sdl-1.2/inc/keyboard/port.h @@ -0,0 +1,21 @@ +#ifndef KEYBOARD_SDL_1_2_H +#define KEYBOARD_SDL_1_2_H + +#include <keyboard.h> +#include <SDL_keyboard.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct keyboard_port +{ + Uint8 scancodes[KEYBOARD_MAX_COMBO_KEYS]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* KEYBOARD_SDL_1_2_H */ diff --git a/src/keyboard/sdl-1.2/src/keyboard.c b/src/keyboard/sdl-1.2/src/keyboard.c index 78056e1..87ae808 100644 --- a/src/keyboard/sdl-1.2/src/keyboard.c +++ b/src/keyboard/sdl-1.2/src/keyboard.c @@ -1,38 +1,67 @@ #include <keyboard.h> -#include <keyboard_key.h> +#include <keyboard/port.h> +#include <keyboard/key.h> #include <SDL.h> +#include <ctype.h> +#include <stddef.h> #include <stdio.h> -static void append_key(const enum keyboard_key key, struct keyboard *const k) +static void append_key(const enum keyboard_key key, const char ch, + const Uint8 scancode, struct keyboard *const k) { - struct keyboard_combo *const c = &k->combo; + struct keyboard_combo *const c = &k->input.combo; for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++) { enum keyboard_key *const sel = &c->keys[i]; + char *const pch = &k->input.chars.c[i]; + Uint8 *const sc = &k->port.scancodes[i]; - if (*sel == KEYBOARD_KEY_NONE) + if (*sel == KEYBOARD_KEY_NONE && !*pch) { *sel = key; + *pch = ch; + *sc = scancode; break; } } } -static void remove_key(const enum keyboard_key key, struct keyboard *const k) +static enum keyboard_key char_to_key(const char ch) { - struct keyboard_combo *const c = &k->combo; - - for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++) + if (ch >= 'a' && ch <= 'b') + return KEYBOARD_KEY_A + (ch - 'a'); + else if (ch >= 'A' && ch <= 'Z') + return KEYBOARD_KEY_A + (ch - 'A'); + else if (ch >= '0' && ch <= '9') + return KEYBOARD_KEY_0 + (ch - '0'); + else { - enum keyboard_key *const sel = &c->keys[i]; + static const struct map + { + enum keyboard_key key; + char ch; + } map[] = + { + {.key = KEYBOARD_KEY_DOT, .ch = '.'}, + {.key = KEYBOARD_KEY_SPACE, .ch = ' '}, + {.key = KEYBOARD_KEY_MINUS, .ch = '-'}, + {.key = KEYBOARD_KEY_SLASH, .ch = '/'} + }; - if (*sel == key) - *sel = KEYBOARD_KEY_NONE; + for (size_t i = 0; i < sizeof map / sizeof *map; i++) + { + const struct map *const m = &map[i]; + + if (ch == m->ch) + return m->key; + } } + + return KEYBOARD_KEY_NONE; } -static void key_event(const SDL_KeyboardEvent *const ev, +static void on_pressed(const SDL_KeyboardEvent *const ev, struct keyboard *const k) { static const struct keymap @@ -46,86 +75,79 @@ static void key_event(const SDL_KeyboardEvent *const ev, {.key = KEYBOARD_KEY_RIGHT, .sdl_key = SDLK_RIGHT}, {.key = KEYBOARD_KEY_UP, .sdl_key = SDLK_UP}, {.key = KEYBOARD_KEY_DOWN, .sdl_key = SDLK_DOWN}, + {.key = KEYBOARD_KEY_ESC, .sdl_key = SDLK_ESCAPE}, + {.key = KEYBOARD_KEY_F11, .sdl_key = SDLK_F11}, {.key = KEYBOARD_KEY_LCTRL, .sdl_key = SDLK_LCTRL}, {.key = KEYBOARD_KEY_RCTRL, .sdl_key = SDLK_RCTRL}, {.key = KEYBOARD_KEY_LSHIFT, .sdl_key = SDLK_LSHIFT}, {.key = KEYBOARD_KEY_RSHIFT, .sdl_key = SDLK_RSHIFT}, - {.key = KEYBOARD_KEY_ESC, .sdl_key = SDLK_ESCAPE}, - {.key = KEYBOARD_KEY_F11, .sdl_key = SDLK_F11}, - {.key = KEYBOARD_KEY_SPACE, .sdl_key = SDLK_SPACE}, - {.key = KEYBOARD_KEY_MINUS, .sdl_key = SDLK_MINUS}, - {.key = KEYBOARD_KEY_SLASH, .sdl_key = SDLK_SLASH}, - {.key = KEYBOARD_KEY_SLASH, .sdl_key = SDLK_KP_DIVIDE}, - {.key = KEYBOARD_KEY_A, .sdl_key = SDLK_a}, - {.key = KEYBOARD_KEY_B, .sdl_key = SDLK_b}, - {.key = KEYBOARD_KEY_C, .sdl_key = SDLK_c}, - {.key = KEYBOARD_KEY_D, .sdl_key = SDLK_d}, - {.key = KEYBOARD_KEY_E, .sdl_key = SDLK_e}, - {.key = KEYBOARD_KEY_F, .sdl_key = SDLK_f}, - {.key = KEYBOARD_KEY_G, .sdl_key = SDLK_g}, - {.key = KEYBOARD_KEY_H, .sdl_key = SDLK_h}, - {.key = KEYBOARD_KEY_I, .sdl_key = SDLK_i}, - {.key = KEYBOARD_KEY_J, .sdl_key = SDLK_j}, - {.key = KEYBOARD_KEY_K, .sdl_key = SDLK_k}, - {.key = KEYBOARD_KEY_L, .sdl_key = SDLK_l}, - {.key = KEYBOARD_KEY_M, .sdl_key = SDLK_m}, - {.key = KEYBOARD_KEY_N, .sdl_key = SDLK_n}, - {.key = KEYBOARD_KEY_O, .sdl_key = SDLK_o}, - {.key = KEYBOARD_KEY_P, .sdl_key = SDLK_p}, - {.key = KEYBOARD_KEY_Q, .sdl_key = SDLK_q}, - {.key = KEYBOARD_KEY_R, .sdl_key = SDLK_r}, - {.key = KEYBOARD_KEY_S, .sdl_key = SDLK_s}, - {.key = KEYBOARD_KEY_T, .sdl_key = SDLK_t}, - {.key = KEYBOARD_KEY_U, .sdl_key = SDLK_u}, - {.key = KEYBOARD_KEY_V, .sdl_key = SDLK_v}, - {.key = KEYBOARD_KEY_W, .sdl_key = SDLK_w}, - {.key = KEYBOARD_KEY_X, .sdl_key = SDLK_x}, - {.key = KEYBOARD_KEY_Y, .sdl_key = SDLK_y}, - {.key = KEYBOARD_KEY_Z, .sdl_key = SDLK_z}, - {.key = KEYBOARD_KEY_0, .sdl_key = SDLK_0}, - {.key = KEYBOARD_KEY_1, .sdl_key = SDLK_1}, - {.key = KEYBOARD_KEY_2, .sdl_key = SDLK_2}, - {.key = KEYBOARD_KEY_3, .sdl_key = SDLK_3}, - {.key = KEYBOARD_KEY_4, .sdl_key = SDLK_4}, - {.key = KEYBOARD_KEY_5, .sdl_key = SDLK_5}, - {.key = KEYBOARD_KEY_6, .sdl_key = SDLK_6}, - {.key = KEYBOARD_KEY_7, .sdl_key = SDLK_7}, - {.key = KEYBOARD_KEY_8, .sdl_key = SDLK_8}, - {.key = KEYBOARD_KEY_9, .sdl_key = SDLK_9}, - {.key = KEYBOARD_KEY_0, .sdl_key = SDLK_KP0}, - {.key = KEYBOARD_KEY_1, .sdl_key = SDLK_KP1}, - {.key = KEYBOARD_KEY_2, .sdl_key = SDLK_KP2}, - {.key = KEYBOARD_KEY_3, .sdl_key = SDLK_KP3}, - {.key = KEYBOARD_KEY_4, .sdl_key = SDLK_KP4}, - {.key = KEYBOARD_KEY_5, .sdl_key = SDLK_KP5}, - {.key = KEYBOARD_KEY_6, .sdl_key = SDLK_KP6}, - {.key = KEYBOARD_KEY_7, .sdl_key = SDLK_KP7}, - {.key = KEYBOARD_KEY_8, .sdl_key = SDLK_KP8}, - {.key = KEYBOARD_KEY_9, .sdl_key = SDLK_KP9}, - {.key = KEYBOARD_KEY_DOT, .sdl_key = SDLK_PERIOD}, - {.key = KEYBOARD_KEY_DOT, .sdl_key = SDLK_KP_PERIOD} + {.key = KEYBOARD_KEY_RETURN, .sdl_key = SDLK_KP_ENTER}, + {.key = KEYBOARD_KEY_RETURN, .sdl_key = SDLK_RETURN} }; - for (size_t i = 0; i < sizeof keymap / sizeof *keymap; i++) + const char ch = ev->keysym.unicode; + + /* According to the man page for isprint(3): + * The standards require that the argument c for these functions is + * either EOF or a value that is representable in the type unsigned + * char. If the argument is of type char, it must be cast to + * unsigned char. */ + if (isprint((unsigned char)ch)) + append_key(char_to_key(ch), ch, ev->keysym.scancode, k); + else { - const struct keymap *const km = &keymap[i]; + for (size_t i = 0; i < sizeof keymap / sizeof *keymap; i++) + { + const struct keymap *const km = &keymap[i]; - if (ev->keysym.sym == km->sdl_key) + if (ev->keysym.sym == km->sdl_key) + { + append_key(km->key, '\0', ev->keysym.scancode, k); + break; + } + } + } +} + +static void remove_key(const Uint8 scancode, struct keyboard *const k) +{ + struct keyboard_port *const p = &k->port; + + for (size_t i = 0; i < sizeof p->scancodes / sizeof *p->scancodes; i++) + { + Uint8 *const sc = &p->scancodes[i]; + + if (*sc == scancode) { - if (ev->state == SDL_PRESSED) - append_key(km->key, k); - else - remove_key(km->key, k); + struct keyboard_combo *const c = &k->input.combo; + struct keyboard_chars *const ch = &k->input.chars; + + c->keys[i] = KEYBOARD_KEY_NONE; + ch->c[i] = '\0'; + *sc = 0; + break; } } } +static void key_event(const SDL_KeyboardEvent *const ev, + struct keyboard *const k) +{ + if (ev->state == SDL_PRESSED) + on_pressed(ev, k); + else + /* SDL allows reading Unicode only for pressed events. + * In order to know whether a Unicode was released, its + * corresponding scancode must be inspected. */ + remove_key(ev->keysym.scancode, k); +} + void keyboard_update(struct keyboard *const k) { SDL_Event ev; int n; - k->oldcombo = k->combo; + k->oldinput = k->input; while ((n = SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_KEYEVENTMASK | SDL_QUITMASK)) > 0) @@ -139,7 +161,7 @@ void keyboard_update(struct keyboard *const k) break; case SDL_QUIT: - append_key(KEYBOARD_KEY_EXIT, k); + append_key(KEYBOARD_KEY_EXIT, '\0', 0, k); break; default: @@ -150,9 +172,11 @@ void keyboard_update(struct keyboard *const k) } if (n < 0) - { - fprintf(stderr, "%s: SDL_PeepEvents: %s\n", - __func__, SDL_GetError()); - return; - } + fprintf(stderr, "%s: SDL_PeepEvents: %s\n", __func__, SDL_GetError()); +} + +bool keyboard_available(void) +{ + /* Keyboard availability is always assumed in SDL. */ + return true; } diff --git a/src/keyboard/src/keyboard.c b/src/keyboard/src/keyboard.c index 3d6f496..21d6898 100644 --- a/src/keyboard/src/keyboard.c +++ b/src/keyboard/src/keyboard.c @@ -1,24 +1,49 @@ #include <keyboard.h> -#include <keyboard_key.h> +#include <keyboard/key.h> #include <stdbool.h> #include <stddef.h> #include <string.h> -bool keyboard_any_justpressed(const struct keyboard *const k, +static bool chars_justpressed(const struct keyboard *const k, + struct keyboard_chars *const c) +{ + bool ret = false; + const struct keyboard_chars *const ref = &k->input.chars; + + for (size_t i = 0; i < sizeof c->c / sizeof *c->c; i++) + c->c[i] = '\0'; + + for (size_t i = 0, j = 0; i < sizeof ref->c / sizeof *ref->c; i++) + { + const char ch = ref->c[i]; + + if (ch && k->oldinput.chars.c[i] != ch) + { + c->c[j++] = ch; + ret = true; + } + } + + return ret; +} + +static bool keys_justpressed(const struct keyboard *const k, struct keyboard_combo *const c) { bool ret = false; - for (size_t i = 0; i < sizeof c->keys / sizeof c->keys; i++) + for (size_t i = 0; i < sizeof c->keys / sizeof *c->keys; i++) c->keys[i] = KEYBOARD_KEY_NONE; + const struct keyboard_combo *const ref = &k->input.combo; + for (size_t i = 0, j = 0; - i < sizeof k->combo.keys / sizeof *k->combo.keys; i++) + i < sizeof ref->keys / sizeof *ref->keys; i++) { - const enum keyboard_key key = k->combo.keys[i]; + const enum keyboard_key key = ref->keys[i]; if (key != KEYBOARD_KEY_NONE - && k->oldcombo.keys[i] == KEYBOARD_KEY_NONE) + && k->oldinput.combo.keys[i] != key) { c->keys[j++] = key; ret = true; @@ -28,16 +53,24 @@ bool keyboard_any_justpressed(const struct keyboard *const k, return ret; } -bool keyboard_any_pressed(const struct keyboard *const k, +bool keyboard_any_justpressed(const struct keyboard *const k, + struct keyboard_input *const in) +{ + /* Use bitwise OR since both functions must be always executed. */ + return keys_justpressed(k, &in->combo) + | chars_justpressed(k, &in->chars); +} + +static bool keys_pressed(const struct keyboard *const k, struct keyboard_combo *const c) { - for (size_t i = 0; i < sizeof k->combo.keys / sizeof *k->combo.keys; i++) - { - const enum keyboard_key key = k->combo.keys[i]; + const struct keyboard_combo *const ref = &k->input.combo; - if (key != KEYBOARD_KEY_NONE) + for (size_t i = 0; i < sizeof ref->keys / sizeof *ref->keys; i++) + { + if (ref->keys[i] != KEYBOARD_KEY_NONE) { - *c = k->combo; + *c = *ref; return true; } } @@ -45,43 +78,28 @@ bool keyboard_any_pressed(const struct keyboard *const k, return false; } -char keyboard_to_char(const struct keyboard *const k, - const enum keyboard_key key) +static bool chars_pressed(const struct keyboard *const k, + struct keyboard_chars *const ch) { - if (key >= KEYBOARD_KEY_0 && key <= KEYBOARD_KEY_9) - return '0' + key - KEYBOARD_KEY_0; - else if (key >= KEYBOARD_KEY_A && key <= KEYBOARD_KEY_Z) - { - if (keyboard_pressed(k, &KEYBOARD_COMBO(KEYBOARD_KEY_LSHIFT)) - || keyboard_pressed(k, &KEYBOARD_COMBO(KEYBOARD_KEY_RSHIFT))) - return 'A' + key - KEYBOARD_KEY_A; - else - return 'a' + key - KEYBOARD_KEY_A; - } - else - { - static const struct map - { - enum keyboard_key key; - char ch; - } map[] = - { - {.key = KEYBOARD_KEY_DOT, .ch = '.'}, - {.key = KEYBOARD_KEY_SPACE, .ch = ' '}, - {.key = KEYBOARD_KEY_MINUS, .ch = '-'}, - {.key = KEYBOARD_KEY_SLASH, .ch = '/'} - }; + const struct keyboard_chars *const ref = &k->input.chars; - for (size_t i = 0; i < sizeof map / sizeof *map; i++) + for (size_t i = 0; i < sizeof ref->c / sizeof *ref->c; i++) + { + if (ref->c[i]) { - const struct map *const m = &map[i]; - - if (key == m->key) - return m->ch; + *ch = *ref; + return true; } } - return -1; + return false; +} + +bool keyboard_any_pressed(const struct keyboard *const k, + struct keyboard_input *const in) +{ + /* Use bitwise OR since both functions must be always executed. */ + return keys_pressed(k, &in->combo) | chars_pressed(k, &in->chars); } static bool combo_pressed(const struct keyboard_combo *const ref, @@ -117,19 +135,21 @@ static bool combo_pressed(const struct keyboard_combo *const ref, bool keyboard_pressed(const struct keyboard *const k, const struct keyboard_combo *const c) { - return combo_pressed(&k->combo, c); + return combo_pressed(&k->input.combo, c); } bool keyboard_justreleased(const struct keyboard *const k, const struct keyboard_combo *const c) { - return !combo_pressed(&k->combo, c) && combo_pressed(&k->oldcombo, c); + return !combo_pressed(&k->input.combo, c) + && combo_pressed(&k->oldinput.combo, c); } bool keyboard_justpressed(const struct keyboard *const k, const struct keyboard_combo *const c) { - return combo_pressed(&k->combo, c) && !combo_pressed(&k->oldcombo, c); + return combo_pressed(&k->input.combo, c) + && !combo_pressed(&k->oldinput.combo, c); } void keyboard_init(struct keyboard *const k) diff --git a/src/menu/CMakeLists.txt b/src/menu/CMakeLists.txt index 465250f..9780148 100644 --- a/src/menu/CMakeLists.txt +++ b/src/menu/CMakeLists.txt @@ -1,9 +1,23 @@ add_library(menu "src/gamecfg_menu.c" "src/hostjoin_menu.c" + "src/host_menu.c" "src/join_menu.c" "src/menu.c" + "src/menu_net.c" "src/main_menu.c" + "src/settings_menu.c" ) target_include_directories(menu PUBLIC "inc" PRIVATE "privinc") -target_link_libraries(menu PRIVATE camera game gfx gui input system) +target_link_libraries(menu PRIVATE + camera + game + gfx + gui + input + net + packet + settings + system + util +) diff --git a/src/menu/privinc/menu_net.h b/src/menu/privinc/menu_net.h new file mode 100644 index 0000000..1980917 --- /dev/null +++ b/src/menu/privinc/menu_net.h @@ -0,0 +1,62 @@ +#ifndef MENU_NETCFG_H +#define MENU_NETCFG_H + +#include <gui.h> +#include <gui/button.h> +#include <gui/checkbox.h> +#include <gui/container.h> +#include <gui/label.h> +#include <gui/line_edit.h> +#include <net.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct menu_net_gui +{ + char device[32], address[32], port[sizeof "65535"]; + bool update_domain; + struct gui_container cnt; + struct gui_label type; + struct gui_button type_btn; + enum net_domain domain; + + union + { + struct gui_container cnt; + + struct menu_ipv4_gui + { + struct gui_container cnt, relay_cnt, announce_cnt; + struct gui_button next_page_btn; + struct gui_label address, port, relay, announce; + struct gui_line_edit address_le, port_le, announce_le; + struct gui_checkbox relay_ch, announce_ch; + } ipv4; + + struct menu_serial_gui + { + struct gui_container cnt, hw_ctrl_cnt; + struct gui_label device, speed, hw_ctrl; + struct gui_button speed_btn; + struct gui_line_edit device_le; + struct gui_checkbox hw_ctrl_ch; + size_t speed_i; + } serial; + } gui; +}; + +int menu_net_gui_init(struct menu_net_gui *m); +void menu_net_gui_update(struct menu_net_gui *m); +int menu_net_server(const struct menu_net_gui *m, union net_server *s); +int menu_net_connect(const struct menu_net_gui *m, union net_connect *c); + +#ifdef __cplusplus +} +#endif + +#endif /* MENU_NETCFG_H */ diff --git a/src/menu/privinc/menu_private.h b/src/menu/privinc/menu_private.h index 181825c..d8aa089 100644 --- a/src/menu/privinc/menu_private.h +++ b/src/menu/privinc/menu_private.h @@ -3,8 +3,10 @@ #include <camera.h> #include <input.h> +#include <net.h> #include <peripheral.h> -#include <stdbool.h> +#include <settings.h> +#include <stddef.h> #ifdef __cplusplus extern "C" @@ -16,6 +18,7 @@ struct menu_common struct camera cam; union peripheral p; struct input in; + struct settings s; }; int menu_update(struct menu_common *c, @@ -23,9 +26,11 @@ int menu_update(struct menu_common *c, int (*render)(const struct menu_common *, void *), void *arg); int menu_main(struct menu_common *c); -int menu_hostjoin(struct menu_common *c, bool *back); +int menu_hostjoin(struct menu_common *c); +int menu_settings(struct menu_common *c); int menu_join(struct menu_common *c); -int menu_gamecfg(struct menu_common *c); +int menu_host(struct menu_common *c); +int menu_gamecfg(struct menu_common *c, struct net_host *h, net_peer p); void menu_on_pressed(void *arg); #ifdef __cplusplus diff --git a/src/menu/src/common.c b/src/menu/src/common.c new file mode 100644 index 0000000..494c047 --- /dev/null +++ b/src/menu/src/common.c @@ -0,0 +1,11 @@ +#include <menu_private.h> +#include <net.h> +#include <stddef.h> + +const enum net_domain menu_domains[] = +{ + NET_DOMAIN_IPV4, + NET_DOMAIN_SERIAL +}; + +const size_t menu_domains_len = sizeof menu_domains / sizeof *menu_domains; diff --git a/src/menu/src/gamecfg_menu.c b/src/menu/src/gamecfg_menu.c index 12184f5..ac01718 100644 --- a/src/menu/src/gamecfg_menu.c +++ b/src/menu/src/gamecfg_menu.c @@ -3,27 +3,476 @@ #include <game.h> #include <gui.h> #include <gui/button.h> +#include <gui/checkbox.h> #include <gui/container.h> +#include <gui/label.h> +#include <gui/line_edit.h> #include <gui/rounded_rect.h> +#include <net.h> +#include <packet.h> +#include <util.h> #include <stdbool.h> #include <stddef.h> +#include <string.h> + +enum +{ + CHAT_ENTRIES = 6, + TOTAL_MESSAGE_LEN = GAME_PLAYER_NAME_LEN + + sizeof ": " - 1 + + PACKET_MAX_MESSAGE_LEN +}; struct gamecfg_menu { - struct gui_container cnt, bcnt; - struct gui_rounded_rect r; + char chat_in[PACKET_MAX_MESSAGE_LEN], + chat[CHAT_ENTRIES][TOTAL_MESSAGE_LEN]; + char chat_label[sizeof "Chat (6)"]; + size_t chat_i, unread_messages; + struct gui_container bcnt; struct gui_button start, back; + struct net_host *host; + struct packet_ctx ctx; + bool update_page; + + enum + { + PAGE_1, + PAGE_2, + + N_PAGES + } page_i; + + struct player_info + { + struct player_info_net + { + net_peer peer; + struct packet_input in; + } *net; + + char name[GAME_PLAYER_NAME_LEN]; + + enum + { + OPEN, + CLOSED, + CONNECTING, + NOT_READY, + READY + } state; + } pl[GAME_MAX_PLAYERS]; + + union + { + struct gui_container root; + + struct gamecfg_page1 + { + struct gui_container root, cnt, ready_cnt; + struct gui_rounded_rect r; + struct gui_label ready; + struct gui_checkbox ready_ch; + struct gui_button chat_page; + + struct player_info_gui + { + struct gui_container cnt; + struct gui_label name, state_label; + } pl[GAME_MAX_PLAYERS]; + } page1; + + struct gamecfg_page2 + { + struct gui_container root, chat_cnt, bcnt; + struct gui_rounded_rect r; + struct gui_line_edit chat_le; + struct gui_button send, player_page; + struct gui_label entries[CHAT_ENTRIES]; + } page2; + } u; }; +static void update_players(struct gamecfg_menu *const m) +{ + if (m->page_i != PAGE_1) + return; + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + struct player_info_gui *const g = &m->u.page1.pl[i]; + + static const char *const states[] = + { + [OPEN] = "Open", + [CLOSED] = "Closed", + [NOT_READY] = "Not ready", + [CONNECTING] = "Connecting", + [READY] = "Ready" + }; + + g->state_label.text = states[pi->state]; + } +} + +static void on_next_page_pressed(void *const arg) +{ + struct gamecfg_menu *const m = arg; + + m->update_page = true; +} + +static int send_ready(const net_peer p, const struct packet_ctx *const c, + const struct player_info *const pi) +{ + const struct packet_ready r = + { + .common = + { + .type = PACKET_TYPE_READY + }, + + .ready = pi->state == READY + }; + + return packet_send(c, p, (const union packet *)&r); +} + +static void on_ready_pressed(const bool active, void *const arg) +{ + struct gamecfg_menu *const m = arg; + struct player_info *const self = m->pl; + + self->state = active ? READY : NOT_READY; + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info_net *const net = m->pl[i].net; + + if (net && net->peer) + send_ready(net->peer, &m->ctx, self); + } +} + +static void init_page1(struct gamecfg_menu *const m) +{ + struct gamecfg_page1 *const p = &m->u.page1; + + { + struct gui_rounded_rect *const r = &p->r; + + gui_rounded_rect_init(r); + r->adjust = true; + r->common.hcentered = true; + gui_add_child(&p->root.common, &r->common); + } + + { + struct gui_container *const c = &p->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 4; + gui_add_child(&p->r.common, &c->common); + } + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + struct player_info_gui *const g = &p->pl[i]; + + { + struct gui_container *const c = &g->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 8; + gui_add_child(&p->cnt.common, &c->common); + } + + { + struct gui_label *const l = &g->name; + + gui_label_init(l); + l->text = pi->name; + l->common.vcentered = true; + gui_add_child(&g->cnt.common, &l->common); + } + + { + struct gui_label *const l = &g->state_label; + + gui_label_init(l); + l->common.vcentered = true; + gui_add_child(&g->cnt.common, &l->common); + } + } + + { + struct gui_container *const c = &p->ready_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 4; + c->common.hcentered = true; + gui_add_child(&p->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &p->ready_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->on_pressed = on_ready_pressed; + c->arg = m; + gui_add_child(&p->ready_cnt.common, &c->common); + } + + { + struct gui_label *const l = &p->ready; + + gui_label_init(l); + l->font = FONT; + l->text = "Ready"; + l->common.vcentered = true; + gui_add_child(&p->ready_cnt.common, &l->common); + } + + { + struct gui_button *const b = &p->chat_page; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.w = 140; + b->common.hcentered = true; + b->on_pressed = on_next_page_pressed; + b->arg = m; + b->u.type1.label.text = "Chat"; + gui_add_child(&m->u.root.common, &b->common); + } +} + +static int broadcast(struct gamecfg_menu *const m, const union packet *const p) +{ + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info_net *const net = m->pl[i].net; + + if (net) + { + const net_peer peer = net->peer; + + if (peer && packet_send(&m->ctx, peer, p)) + { + fprintf(stderr, "%s: net_write failed\n", __func__); + return -1; + } + } + } + + return 0; +} + +static void append_msg(struct gamecfg_menu *const m, + const char *const player, const char *const msg) +{ + static const size_t sz = sizeof m->chat / sizeof *m->chat; + char *dst; + + if (m->chat_i < sz) + dst = m->chat[m->chat_i++]; + else + { + for (size_t i = 0; i < sz - 1; i++) + strcpy(m->chat[i], m->chat[i + 1]); + + dst = m->chat[sz - 1]; + *dst = '\0'; + } + + strcat(dst, player); + strcat(dst, ": "); + strcat(dst, msg); +} + +static void on_send_pressed(void *const arg) +{ + struct gamecfg_menu *const m = arg; + struct gui_line_edit *const le = &m->u.page2.chat_le; + char *const src = le->text; + + if (!*src) + return; + + const struct packet_chat c = + { + .common = + { + .type = PACKET_TYPE_CHAT + }, + + .msg = src + }; + + if (broadcast(m, (const union packet *)&c)) + return; + + append_msg(m, m->pl->name, src); + *src = '\0'; + le->i = 0; +} + +static void init_page2(struct gamecfg_menu *const m) +{ + struct gamecfg_page2 *const p = &m->u.page2; + + { + struct gui_rounded_rect *const r = &p->r; + + gui_rounded_rect_init(r); + r->adjust = true; + r->common.hcentered = true; + gui_add_child(&m->u.root.common, &r->common); + } + + { + struct gui_container *const c = &p->chat_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 2; + gui_add_child(&p->r.common, &c->common); + } + + for (size_t i = 0; i < sizeof p->entries / sizeof *p->entries; i++) + { + struct gui_label *const l = &p->entries[i]; + + gui_label_init(l); + l->text = m->chat[i]; + l->font = FONT; + gui_add_child(&p->chat_cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &p->chat_le; + + gui_line_edit_init(le, m->chat_in, sizeof m->chat_in); + le->common.hcentered = true; + le->w = 240; + gui_add_child(&m->u.root.common, &le->common); + } + + { + struct gui_container *const c = &p->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 2; + gui_add_child(&m->u.root.common, &c->common); + } + + { + struct gui_button *const b = &p->send; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.vcentered = true; + b->u.type1.w = 100; + b->u.type1.label.text = "Send"; + b->on_pressed = on_send_pressed; + b->arg = m; + gui_add_child(&p->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &p->player_page; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.vcentered = true; + b->u.type1.w = 150; + b->u.type1.label.text = "Player info"; + b->on_pressed = on_next_page_pressed; + b->arg = m; + gui_add_child(&p->bcnt.common, &b->common); + } + + m->unread_messages = 0; +} + +static void init_page_gui(struct gamecfg_menu *const m) +{ + { + struct gui_container *const c = &m->u.root; + + gui_container_init(c); + c->common.hcentered = true; + c->common.vcentered = true; + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 4; + } + + static void (*const init[])(struct gamecfg_menu *) = + { + [PAGE_1] = init_page1, + [PAGE_2] = init_page2 + }; + + init[m->page_i](m); +} + +static void update_send(struct menu_common *const c, + struct gamecfg_menu *const m) +{ + if (m->page_i != PAGE_2) + return; + + switch (c->p.common.type) + { + case PERIPHERAL_TYPE_KEYBOARD_MOUSE: + /* Fall through. */ + case PERIPHERAL_TYPE_TOUCH: + { + struct keyboard *const k = &c->p.kbm.keyboard; + + if (m->u.page2.chat_le.focus + && keyboard_justreleased(k, + &KEYBOARD_COMBO(KEYBOARD_KEY_RETURN))) + on_send_pressed(m); + } + break; + + case PERIPHERAL_TYPE_PAD: + break; + } +} + +static void update_page(struct gamecfg_menu *const m) +{ + if (m->update_page) + { + if (++m->page_i >= N_PAGES) + m->page_i = 0; + + init_page_gui(m); + m->update_page = false; + } +} + static int update(struct menu_common *const c, void *const arg) { struct gamecfg_menu *const m = arg; m->bcnt.common.y = screen_h - 40; - m->r.w = screen_w / 2; - m->r.h = screen_h / 2; - if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) + if (m->host) + net_update(m->host); + + update_players(m); + update_send(c, m); + update_page(m); + + if (gui_update(&m->u.root.common, &c->p, &c->cam, &c->in) || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) return -1; @@ -34,54 +483,264 @@ static int render(const struct menu_common *const c, void *const arg) { const struct gamecfg_menu *const m = arg; - if (gui_render(&m->cnt.common) + if (gui_render(&m->u.root.common) || gui_render(&m->bcnt.common)) return -1; return 0; } -int menu_gamecfg(struct menu_common *const c) +static int add_peer(struct gamecfg_menu *const m, const net_peer p) +{ + bool found = false; + + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + struct player_info_net *const net = pi->net; + + if (net && !net->peer) + { + net->peer = p; + pi->state = CONNECTING; + strcpy(pi->name, "<new player>"); + found = true; + break; + } + } + + return !found; +} + +static int send_name(const net_peer p, const struct packet_ctx *const c, + const struct player_info *const pi) +{ + const struct packet_name n = + { + .common = + { + .type = PACKET_TYPE_NAME + }, + + .name = pi->name + }; + + return packet_send(c, p, (const union packet *)&n); +} + +static int broadcast_player_info(const net_peer p, + const struct gamecfg_menu *const m) +{ + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + const struct player_info *const pi = &m->pl[i]; + + if (*pi->name) + { + const struct packet_ctx *const c = &m->ctx; + + if (send_name(p, c, pi) || send_ready(p, c, pi)) + return -1; + } + } + + return 0; +} + +static void on_connected(const net_peer p, void *const arg) +{ + struct gamecfg_menu *const m = arg; + + if (net_role(m->host) == NET_ROLE_SERVER) + broadcast_player_info(p, m); + + add_peer(m, p); +} + +static void on_disconnected(const net_peer p, void *const arg) { - struct gamecfg_menu m; +} + +static struct player_info *get_pl_from_peer(struct gamecfg_menu *const m, + const net_peer peer) +{ + for (size_t i = 0; i < sizeof m->pl / sizeof *m->pl; i++) + { + struct player_info *const pi = &m->pl[i]; + + if (pi->net && pi->net->peer == peer) + return pi; + } + + return NULL; +} + +static void on_received(const net_peer p, const void *const buf, + const size_t n, void *const arg) +{ + struct gamecfg_menu *const m = arg; + struct player_info *const pi = get_pl_from_peer(m, p); + + if (pi && pi->net && packet_feed(&m->ctx, p, &pi->net->in, buf, n)) + fprintf(stderr, "%s: packet_feed failed\n", __func__); +} + +static void init_footer_gui(struct gamecfg_menu *const m, + bool *const start, bool *const back) +{ + { + struct gui_container *const c = &m->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 8; + } + + if (net_role(m->host) == NET_ROLE_SERVER) + { + struct gui_button *const b = &m->start; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Start"; + b->u.type1.w = 140; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = start; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->u.type1.w = 100; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = back; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } +} + +static void init_own(const struct menu_common *const c, + struct player_info *const pi) +{ + strcpy(pi->name, c->s.name); + pi->state = NOT_READY; +} + +static void on_packet_received(const net_peer peer, + const union packet *const pkt, void *const arg) +{ + struct gamecfg_menu *const m = arg; + + switch (pkt->common.type) + { + case PACKET_TYPE_CHAT: + { + struct player_info *const pi = get_pl_from_peer(m, peer); + + if (pi) + { + append_msg(m, pi->name, pkt->chat.msg); + + if (m->page_i == PAGE_1) + { + if (m->unread_messages < CHAT_ENTRIES) + m->unread_messages++; + + snprintf(m->chat_label, sizeof m->chat_label, + "Chat (%zu)", m->unread_messages); + m->u.page1.chat_page.u.type1.label.text = m->chat_label; + } + } + } + break; + + case PACKET_TYPE_NAME: + { + struct player_info *const pi = get_pl_from_peer(m, peer); + + if (pi) + strcpy(pi->name, pkt->name.name); + } + break; + + case PACKET_TYPE_READY: + { + struct player_info *const pi = get_pl_from_peer(m, peer); + + if (pi) + pi->state = pkt->ready.ready ? READY : NOT_READY; + } + break; + + default: + break; + } +} + +int menu_gamecfg(struct menu_common *const c, struct net_host *const h, + const net_peer p) +{ + struct gamecfg_menu m = + { + .host = h, + .ctx = + { + .arg = &m, + .cb = on_packet_received, + .host = h + } + }; + + const struct net_event ev = + { + .connected = on_connected, + .disconnected = on_disconnected, + .received = on_received, + .arg = &m + }; + + struct player_info_net n[sizeof m.pl / sizeof *m.pl - 1] = {0}; bool start = false, back = false; - gui_container_init(&m.cnt); - m.cnt.common.hcentered = true; - m.cnt.common.vcentered = true; - - gui_rounded_rect_init(&m.r); - gui_add_child(&m.cnt.common, &m.r.common); - - gui_container_init(&m.bcnt); - m.bcnt.common.hcentered = true; - m.bcnt.mode = GUI_CONTAINER_MODE_H; - m.bcnt.spacing = 8; - - gui_button_init(&m.start, GUI_BUTTON_TYPE_1); - m.start.u.type1.label.text = "Start"; - m.start.u.type1.w = 100; - m.start.common.vcentered = true; - m.start.on_pressed = menu_on_pressed; - m.start.arg = &start; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.start.common); - - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.u.type1.w = 100; - m.back.common.vcentered = true; - m.back.on_pressed = menu_on_pressed; - m.back.arg = &back; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.back.common); + for (size_t i = 0; i < sizeof n / sizeof *n; i++) + m.pl[i + 1].net = &n[i]; + + if (net_set_event(m.host, &ev)) + return -1; + + init_own(c, m.pl); + init_page_gui(&m); + init_footer_gui(&m, &start, &back); + + if (p) + { + const struct packet_ctx *const c = &m.ctx; + + if (add_peer(&m, p) + || send_name(p, c, m.pl) + || send_ready(p, c, m.pl)) + return -1; + } while (!back && !c->p.common.exit && !start) if (menu_update(c, update, render, &m)) return -1; if (start) - return game(NULL); + { + struct game_cfg cfg = + { + .p = &c->p + }; + + return game(&cfg); + } return 0; } diff --git a/src/menu/src/host_menu.c b/src/menu/src/host_menu.c new file mode 100644 index 0000000..102275e --- /dev/null +++ b/src/menu/src/host_menu.c @@ -0,0 +1,159 @@ +#include <menu.h> +#include <menu_net.h> +#include <menu_private.h> +#include <game.h> +#include <gui.h> +#include <gui/button.h> +#include <gui/checkbox.h> +#include <gui/container.h> +#include <gui/label.h> +#include <gui/line_edit.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +struct host_menu +{ + struct gui_container cnt, bcnt; + struct gui_label error; + struct menu_net_gui net; + struct gui_button host, back; +}; + +static void hide_elements(struct menu_net_gui *const m) +{ + if (m->domain == NET_DOMAIN_IPV4) + { + struct menu_ipv4_gui *const g = &m->gui.ipv4; + + g->address.common.hidden = true; + g->address_le.common.hidden = true; + } +} + +static int update(struct menu_common *const c, void *const arg) +{ + struct host_menu *const m = arg; + + m->bcnt.common.y = screen_h - 40; + menu_net_gui_update(&m->net); + m->net.cnt.common.parent = &m->cnt.common; + hide_elements(&m->net); + + if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) + || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) + return -1; + + return 0; +} + +static int render(const struct menu_common *const c, void *const arg) +{ + const struct host_menu *const m = arg; + + if (gui_render(&m->cnt.common) + || gui_render(&m->bcnt.common)) + return -1; + + return 0; +} + +static void init_footer_gui(struct host_menu *const m, + bool *const back, bool *const host) +{ + { + struct gui_label *const l = &m->error; + + gui_label_init(l); + l->font = FONT; + l->common.hcentered = true; + l->common.hidden = true; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_container *const c = &m->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + } + + { + struct gui_button *const b = &m->host; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Host"; + b->u.type1.w = 140; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = host; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->u.type1.w = 100; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = back; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } +} + +static struct net_host *init_server(const struct host_menu *const m) +{ + union net_server s; + + if (menu_net_server(&m->net, &s)) + return NULL; + + return net_server(&s); +} + +int menu_host(struct menu_common *const c) +{ + int ret = -1; + bool back = false; + + do + { + struct host_menu m = {0}; + struct net_host *h = NULL; + bool host = false; + + gui_container_init(&m.cnt); + m.cnt.common.hcentered = true; + m.cnt.common.vcentered = true; + m.cnt.mode = GUI_CONTAINER_MODE_V; + m.cnt.spacing = 2; + + menu_net_gui_init(&m.net); + m.net.cnt.common.hcentered = true; + gui_add_child(&m.cnt.common, &m.net.cnt.common); + + init_footer_gui(&m, &back, &host); + + while (!back && !c->p.common.exit && !host) + if (menu_update(c, update, render, &m)) + goto end; + + if (host && (h = init_server(&m)) && menu_gamecfg(c, h, NULL)) + goto end; + + net_close(h); + gui_deinit(&m.cnt.common, &c->in); + } while (!back && !c->p.common.exit); + + ret = 0; + +end: + return ret; +} diff --git a/src/menu/src/hostjoin_menu.c b/src/menu/src/hostjoin_menu.c index 9ce93b1..121efd6 100644 --- a/src/menu/src/hostjoin_menu.c +++ b/src/menu/src/hostjoin_menu.c @@ -1,15 +1,16 @@ #include <menu.h> #include <menu_private.h> -#include <game.h> #include <gui.h> #include <gui/button.h> #include <gui/container.h> +#include <gui/checkbox.h> +#include <gui/label.h> #include <stdbool.h> struct menu_hostjoin { struct gui_container cnt; - struct gui_button host, join, back; + struct gui_button local, host, join, back; }; static int update(struct menu_common *const c, void *const arg) @@ -32,61 +33,91 @@ static int render(const struct menu_common *const c, void *const arg) return 0; } -int menu_hostjoin(struct menu_common *const c, bool *const back) +static void init_header_gui(struct menu_hostjoin *const m, bool *const local, + bool *const host, bool *const join) { + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->common.hcentered = true; + c->common.vcentered = true; + c->spacing = 4; + c->mode = GUI_CONTAINER_MODE_V; + } + + { + struct gui_button *const b = &m->local; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Local game"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = local; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_button *const b = &m->host; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Host game"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = host; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_button *const b = &m->join; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Join game"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = join; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); + } +} + +static void init_footer_gui(struct menu_hostjoin *const m, bool *const back) +{ + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->common.hcentered = true; + b->u.type1.w = 140; + b->arg = back; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->cnt.common, &b->common); +} + +int menu_hostjoin(struct menu_common *const c) +{ + bool back = false; + do { struct menu_hostjoin m; - bool host = false, join = false; - - gui_container_init(&m.cnt); - m.cnt.common.hcentered = true; - m.cnt.common.vcentered = true; - m.cnt.spacing = 4; - m.cnt.mode = GUI_CONTAINER_MODE_V; - - gui_button_init(&m.host, GUI_BUTTON_TYPE_1); - m.host.u.type1.label.text = "Host game"; - m.host.common.hcentered = true; - m.host.u.type1.w = 140; - m.host.arg = &host; - m.host.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.host.common); - - gui_button_init(&m.join, GUI_BUTTON_TYPE_1); - m.join.u.type1.label.text = "Join game"; - m.join.common.hcentered = true; - m.join.u.type1.w = 140; - m.join.arg = &join; - m.join.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.join.common); - - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.common.hcentered = true; - m.back.u.type1.w = 140; - m.back.arg = back; - m.back.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.back.common); - - while (!*back && !c->p.common.exit && !host && !join) - { + bool local = false, host = false, join = false; + + init_header_gui(&m, &local, &host, &join); + init_footer_gui(&m, &back); + + while (!back && !c->p.common.exit && !local && !host && !join) if (menu_update(c, update, render, &m)) return -1; - } - if (host) - { - if (menu_gamecfg(c)) - return -1; - } - else if (join) - { - if (menu_join(c)) - return -1; - } + if (host && menu_host(c)) + return -1; + else if (join && menu_join(c)) + return -1; - } while (!*back && !c->p.common.exit); + } while (!back && !c->p.common.exit); return 0; } diff --git a/src/menu/src/join_menu.c b/src/menu/src/join_menu.c index 4755b5c..66a3fdc 100644 --- a/src/menu/src/join_menu.c +++ b/src/menu/src/join_menu.c @@ -1,5 +1,6 @@ #include <menu.h> #include <menu_private.h> +#include <menu_net.h> #include <gui.h> #include <gui/button.h> #include <gui/container.h> @@ -12,14 +13,13 @@ #include <errno.h> #include <stdbool.h> #include <stddef.h> -#include <stdlib.h> struct join_menu { struct gui_container cnt, bcnt; - struct gui_label type_label, address_device, port, connecting, speed; - struct gui_button type_btn, back, connect, device_btn, speed_btn; - struct gui_line_edit address_le, port_le; + struct menu_net_gui net; + struct gui_label connecting; + struct gui_button back, connect; enum { @@ -30,120 +30,77 @@ struct join_menu CONNECT_FAILED } state; - struct net_socket *socket; - size_t domain_i, speed_i, device_i, n_devices; - const char *const *devices; + struct net_host *host; + net_peer peer; }; -static const enum net_domain domains[] = -{ - NET_DOMAIN_IPV4, - NET_DOMAIN_SERIAL -}; - -static const char *const speeds[] = -{ - "19200", "38400", "57600", "115200" -}; - -static void on_connected(void *const arg) +static void on_connected(const net_peer p, void *const arg) { struct join_menu *const m = arg; m->state = CONNECTED; + m->peer = p; } -static void on_disconnected(void *const arg) +static void on_disconnected(const net_peer p, void *const arg) { struct join_menu *const m = arg; m->state = CONNECT_FAILED; - m->socket = NULL; + m->host = NULL; } static int on_connect(struct join_menu *const m) { - const enum net_domain d = domains[m->domain_i]; + union net_connect c; - union net_connect c = + if (menu_net_connect(&m->net, &c)) { - .common = - { - .domain = d, - .ev = - { - .connected = on_connected, - .disconnected = on_disconnected, - .arg = m - } - } - }; + fprintf(stderr, "%s: menu_net_connect failed\n", __func__); + return -1; + } - switch (d) + c.common.ev = (const struct net_event) { - case NET_DOMAIN_IPV4: - { - errno = 0; - - const unsigned long port = strtoul(m->port_le.text, NULL, 10); - - if (errno) - { - m->connecting.text = "Invalid port"; - return -1; - } - - c.ipv4.addr = m->address_le.text; - c.ipv4.port = port; - } - break; - - case NET_DOMAIN_SERIAL: - break; - } + .connected = on_connected, + .disconnected = on_disconnected, + .arg = m + }; - if (!(m->socket = net_connect(&c))) + if (!(m->host = net_connect(&c))) { fprintf(stderr, "%s: net_connect failed\n", __func__); - on_disconnected(m); + on_disconnected(NULL, m); return -1; } return 0; } -static int update(struct menu_common *const c, void *const arg) +static int update_socket(struct join_menu *const m) { - struct join_menu *const m = arg; - - m->bcnt.common.y = screen_h - 40; - - if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) - || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) - return -1; - - if (m->socket && net_update(m->socket)) + if (m->host && net_update(m->host)) return -1; switch (m->state) { case CONNECT: if (on_connect(m)) - { m->state = CONNECT_FAILED; + else + { + m->state = CONNECTING; + m->connecting.text = "Connecting..."; + m->connecting.common.hidden = false; break; } - m->state = CONNECTING; - m->connecting.text = "Connecting..."; - m->connecting.common.hidden = false; - break; - + /* Fall through. */ case CONNECT_FAILED: - net_close(m->socket); + net_close(m->host); m->connecting.text = "Failed to connect"; m->connecting.common.hidden = false; - m->socket = NULL; + m->host = NULL; m->state = IDLE; break; @@ -154,98 +111,101 @@ static int update(struct menu_common *const c, void *const arg) return 0; } -static int render(const struct menu_common *const c, void *const arg) +static void hide_elements(struct menu_net_gui *const m) { - const struct join_menu *const m = arg; - - if (gui_render(&m->cnt.common) - || gui_render(&m->bcnt.common)) - return -1; + if (m->domain == NET_DOMAIN_IPV4) + { + struct menu_ipv4_gui *const g = &m->gui.ipv4; - return 0; + g->announce_cnt.common.hidden = true; + } } -static int update_domain(struct join_menu *const m) +static int update(struct menu_common *const c, void *const arg) { - switch (domains[m->domain_i]) - { - case NET_DOMAIN_IPV4: - m->port.common.hidden = false; - m->port_le.common.hidden = false; - m->address_le.common.hidden = false; - m->device_btn.common.hidden = true; - m->speed.common.hidden = true; - m->speed_btn.common.hidden = true; - m->address_device.text = "Address:"; - break; + struct join_menu *const m = arg; - case NET_DOMAIN_SERIAL: - m->port.common.hidden = true; - m->port_le.common.hidden = true; - m->address_le.common.hidden = true; - m->device_btn.common.hidden = false; - m->speed.common.hidden = false; - m->speed_btn.common.hidden = false; - m->address_device.text = "Device:"; - m->speed_btn.u.type1.label.text = speeds[m->speed_i = 0]; - m->devices = net_serial_devices(&m->n_devices); - - if (!m->devices) - return -1; - - m->device_btn.u.type1.label.text = m->devices[m->device_i = 0]; - break; - } + m->bcnt.common.y = screen_h - 40; + menu_net_gui_update(&m->net); + update_socket(m); + hide_elements(&m->net); + + if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in) + || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) + return -1; return 0; } -static void on_type_pressed(void *const arg) +static int render(const struct menu_common *const c, void *const arg) { - struct join_menu *const m = arg; + const struct join_menu *const m = arg; - do - if (++m->domain_i >= sizeof domains / sizeof *domains) - m->domain_i = 0; - while (!net_available(domains[m->domain_i])); + if (gui_render(&m->cnt.common) + || gui_render(&m->bcnt.common)) + return -1; - m->type_btn.u.type1.label.text = net_domain_str(domains[m->domain_i]); - update_domain(m); + return 0; } -static void on_device_pressed(void *const arg) +static void on_connect_pressed(void *const arg) { struct join_menu *const m = arg; - if (++m->device_i >= m->n_devices) - m->device_i = 0; - - m->device_btn.u.type1.label.text = m->devices[m->device_i]; + if (m->state == IDLE) + m->state = CONNECT; } -static void on_speed_pressed(void *const arg) +static void init_footer_gui(struct join_menu *const m, bool *const back) { - struct join_menu *const m = arg; + { + struct gui_label *const l = &m->connecting; - if (++m->speed_i >= sizeof speeds / sizeof *speeds) - m->speed_i = 0; + gui_label_init(l); + l->common.hcentered = true; + l->common.hidden = true; + gui_add_child(&m->cnt.common, &l->common); + } - m->speed_btn.u.type1.label.text = speeds[m->speed_i]; -} + { + struct gui_container *const c = &m->bcnt; -static void on_connect_pressed(void *const arg) -{ - struct join_menu *const m = arg; + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + } - if (m->state == IDLE) - m->state = CONNECT; + { + struct gui_button *const b = &m->connect; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Connect"; + b->u.type1.w = 140; + b->common.vcentered = true; + b->on_pressed = on_connect_pressed; + b->arg = m; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->u.type1.w = 100; + b->common.vcentered = true; + b->on_pressed = menu_on_pressed; + b->arg = back; + b->common.vcentered = true; + gui_add_child(&m->bcnt.common, &b->common); + } } int menu_join(struct menu_common *const c) { int ret = -1; struct join_menu m = {0}; - bool connect = false; bool back = false; gui_container_init(&m.cnt); @@ -254,107 +214,23 @@ int menu_join(struct menu_common *const c) m.cnt.mode = GUI_CONTAINER_MODE_V; m.cnt.spacing = 2; - gui_label_init(&m.type_label); - m.type_label.common.hcentered = true; - m.type_label.text = "Type:"; - gui_add_child(&m.cnt.common, &m.type_label.common); - - while (!net_available(domains[m.domain_i])) - if (++m.domain_i >= sizeof domains / sizeof *domains) - return -1; - - gui_button_init(&m.type_btn, GUI_BUTTON_TYPE_1); - m.type_btn.arg = &m; - m.type_btn.u.type1.w = 140; - m.type_btn.common.hcentered = true; - m.type_btn.on_pressed = on_type_pressed; - m.type_btn.u.type1.label.text = net_domain_str(domains[m.domain_i]); - gui_add_child(&m.cnt.common, &m.type_btn.common); - - gui_label_init(&m.address_device); - m.address_device.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.address_device.common); - - gui_button_init(&m.device_btn, GUI_BUTTON_TYPE_1); - m.device_btn.common.hcentered = true; - m.device_btn.on_pressed = on_device_pressed; - m.device_btn.arg = &m; - m.device_btn.u.type1.w = 140; - gui_add_child(&m.cnt.common, &m.device_btn.common); - - char address[sizeof "255.255.255.255"]; - gui_line_edit_init(&m.address_le, address, sizeof address); - m.address_le.w = 140; - m.address_le.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.address_le.common); - - gui_label_init(&m.port); - m.port.common.hcentered = true; - m.port.text = "Port:"; - gui_add_child(&m.cnt.common, &m.port.common); - - char port[sizeof "65535"]; - gui_line_edit_init(&m.port_le, port, sizeof port); - m.port_le.w = 80; - m.port_le.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.port_le.common); - - gui_label_init(&m.speed); - m.speed.font = FONT; - m.speed.text = "Baud rate:"; - m.speed.common.hcentered = true; - gui_add_child(&m.cnt.common, &m.speed.common); - - gui_button_init(&m.speed_btn, GUI_BUTTON_TYPE_1); - m.speed_btn.arg = &m; - m.speed_btn.on_pressed = on_speed_pressed; - m.speed_btn.u.type1.w = 140; - gui_add_child(&m.cnt.common, &m.speed_btn.common); - - gui_label_init(&m.connecting); - m.connecting.common.hcentered = true; - m.connecting.common.hidden = true; - gui_add_child(&m.cnt.common, &m.connecting.common); - - gui_container_init(&m.bcnt); - m.bcnt.common.hcentered = true; - m.bcnt.mode = GUI_CONTAINER_MODE_H; - - gui_button_init(&m.connect, GUI_BUTTON_TYPE_1); - m.connect.u.type1.label.text = "Connect"; - m.connect.u.type1.w = 140; - m.connect.common.vcentered = true; - m.connect.on_pressed = on_connect_pressed; - m.connect.arg = &m; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.connect.common); - - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.u.type1.w = 100; - m.back.common.vcentered = true; - m.back.on_pressed = menu_on_pressed; - m.back.arg = &back; - m.back.common.vcentered = true; - gui_add_child(&m.bcnt.common, &m.back.common); - - if (update_domain(&m)) + if (menu_net_gui_init(&m.net)) return -1; - while (!back && !c->p.common.exit && !connect) - { + gui_add_child(&m.cnt.common, &m.net.cnt.common); + init_footer_gui(&m, &back); + + while (!back && !c->p.common.exit && m.state != CONNECTED) if (menu_update(c, update, render, &m)) goto end; - } - if (connect) - { - - } + if (m.state == CONNECTED && menu_gamecfg(c, m.host, m.peer)) + goto end; ret = 0; end: - gui_deinit(&m.cnt.common, &c->in); + net_close(m.host); + gui_deinit(&m.net.cnt.common, &c->in); return ret; } diff --git a/src/menu/src/main_menu.c b/src/menu/src/main_menu.c index 0c9ddb0..49e0fd8 100644 --- a/src/menu/src/main_menu.c +++ b/src/menu/src/main_menu.c @@ -9,8 +9,8 @@ struct main_menu { - bool start, exit; - struct gui_button play, exit_btn; + bool start, settings, exit; + struct gui_button play, settings_btn, exit_btn; struct gui_container cnt; }; @@ -36,14 +36,10 @@ static int render(const struct menu_common *const c, void *const arg) int menu_main(struct menu_common *const c) { - bool back; - do { struct main_menu m = {0}; - back = false; - gui_container_init(&m.cnt); m.cnt.mode = GUI_CONTAINER_MODE_V; m.cnt.common.hcentered = true; @@ -58,6 +54,14 @@ int menu_main(struct menu_common *const c) m.play.u.type1.label.text = "Play"; gui_add_child(&m.cnt.common, &m.play.common); + gui_button_init(&m.settings_btn, GUI_BUTTON_TYPE_1); + m.settings_btn.on_pressed = menu_on_pressed; + m.settings_btn.arg = &m.settings; + m.settings_btn.u.type1.w = 140; + m.settings_btn.common.hcentered = true; + m.settings_btn.u.type1.label.text = "Settings"; + gui_add_child(&m.cnt.common, &m.settings_btn.common); + if (system_can_exit()) { gui_button_init(&m.exit_btn, GUI_BUTTON_TYPE_1); @@ -69,7 +73,7 @@ int menu_main(struct menu_common *const c) gui_add_child(&m.cnt.common, &m.exit_btn.common); } - while (!m.start) + while (!m.start && !m.settings && !c->p.common.exit) { if (menu_update(c, update, render, &m)) return -1; @@ -78,10 +82,12 @@ int menu_main(struct menu_common *const c) return 0; } - if (menu_hostjoin(c, &back)) + if (m.start && menu_hostjoin(c)) + return -1; + else if (m.settings && menu_settings(c)) return -1; - } while (back && !c->p.common.exit); + } while (!c->p.common.exit); return 0; } diff --git a/src/menu/src/menu.c b/src/menu/src/menu.c index ba90708..eb99619 100644 --- a/src/menu/src/menu.c +++ b/src/menu/src/menu.c @@ -5,6 +5,7 @@ #include <gfx.h> #include <input.h> #include <peripheral.h> +#include <settings.h> #include <system.h> #include <stdbool.h> @@ -62,18 +63,17 @@ int menu_update(struct menu_common *const c, int menu(void) { - const struct peripheral_cfg cfg = - { - .type = PERIPHERAL_TYPE_KEYBOARD_MOUSE - }; - + struct peripheral_cfg cfg; struct menu_common c = {0}; + peripheral_get_default(&cfg); + if (game_resinit()) return -1; cursor_init(&c.cam.cursor); peripheral_init(&cfg, &c.p); + settings_load("settings.ini", &c.s); return menu_main(&c); } diff --git a/src/menu/src/menu_net.c b/src/menu/src/menu_net.c new file mode 100644 index 0000000..5b8eedc --- /dev/null +++ b/src/menu/src/menu_net.c @@ -0,0 +1,447 @@ +#include <menu.h> +#include <menu_net.h> +#include <game.h> +#include <gui.h> +#include <gui/container.h> +#include <gui/label.h> +#include <gui/button.h> +#include <gui/line_edit.h> +#include <gui/checkbox.h> +#include <net.h> +#include <net/serial.h> +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static const enum net_domain domains[] = +{ + NET_DOMAIN_IPV4, + NET_DOMAIN_SERIAL +}; + +static void on_relay_pressed(const bool active, void *const arg) +{ + struct menu_ipv4_gui *const m = arg; + + m->next_page_btn.common.hidden = !active; +} + +static void init_ipv4_gui(struct menu_ipv4_gui *const m, + char *const addr, const size_t addrlen, + char *const port, const size_t portlen) +{ + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 2; + } + + { + struct gui_label *const l = &m->address; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Address:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->address_le; + + gui_line_edit_init(le, addr, addrlen); + le->w = 280; + le->common.hcentered = true; + gui_add_child(&m->cnt.common, &le->common); + } + + { + struct gui_label *const l = &m->port; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Port:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->port_le; + + gui_line_edit_init(le, port, portlen); + le->w = 80; + le->common.hcentered = true; + le->filters = GUI_LINE_EDIT_FILTER_NUM; + gui_add_child(&m->cnt.common, &le->common); + } + + { + struct gui_container *const c = &m->relay_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 4; + c->common.hcentered = true; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &m->relay_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->on_pressed = on_relay_pressed; + c->arg = m; + gui_add_child(&m->relay_cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->relay; + + gui_label_init(l); + l->common.vcentered = true; + l->font = FONT; + l->text = "Relay to serial"; + gui_add_child(&m->relay_cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->next_page_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.vcentered = true; + b->u.type1.label.text = "Next page"; + b->u.type1.w = 140; + b->common.hidden = true; + gui_add_child(&m->relay_cnt.common, &b->common); + } + + { + struct gui_container *const c = &m->announce_cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_H; + c->spacing = 4; + c->common.hcentered = true; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &m->announce_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->arg = m; + gui_add_child(&m->announce_cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->announce; + + gui_label_init(l); + l->common.vcentered = true; + l->font = FONT; + l->text = "Announce server"; + gui_add_child(&m->announce_cnt.common, &l->common); + } +} + +static const char *const speeds[] = +{ + "19200", "38400", "57600", "115200" +}; + +static void on_speed_pressed(void *const arg) +{ + struct menu_serial_gui *const m = arg; + + if (++m->speed_i >= sizeof speeds / sizeof *speeds) + m->speed_i = 0; + + m->speed_btn.u.type1.label.text = speeds[m->speed_i]; +} + +static void init_serial_gui(struct menu_serial_gui *const m, + char *const buf, const size_t n) +{ + m->speed_i = sizeof speeds / sizeof *speeds - 1; + + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 2; + } + + { + struct gui_label *const l = &m->device; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Device:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->device_le; + + gui_line_edit_init(le, buf, n); + le->w = 280; + le->common.hcentered = true; + gui_add_child(&m->cnt.common, &le->common); + } + + { + struct gui_label *const l = &m->speed; + + gui_label_init(l); + l->font = FONT; + l->text = "Baud rate:"; + l->common.hcentered = true; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->speed_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->arg = m; + b->on_pressed = on_speed_pressed; + b->u.type1.w = 140; + b->u.type1.label.text = speeds[m->speed_i]; + b->common.hcentered = true; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_container *const c = &m->hw_ctrl_cnt; + + gui_container_init(c); + c->spacing = 8; + c->mode = GUI_CONTAINER_MODE_H; + c->common.hcentered = true; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->hw_ctrl; + + gui_label_init(l); + l->text = "HW control"; + l->font = FONT; + l->common.vcentered = true; + gui_add_child(&m->hw_ctrl_cnt.common, &l->common); + } + + { + struct gui_checkbox *const c = &m->hw_ctrl_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + gui_add_child(&m->hw_ctrl_cnt.common, &c->common); + } +} + +static void on_type_pressed(void *const arg) +{ + struct menu_net_gui *const m = arg; + + do + if (++m->domain >= sizeof domains / sizeof *domains) + m->domain = 0; + while (!net_available(domains[m->domain])); + + m->update_domain = true; +} + +static void update_domain(struct menu_net_gui *const m) +{ + const enum net_domain d = domains[m->domain]; + + switch (d) + { + case NET_DOMAIN_IPV4: + init_ipv4_gui(&m->gui.ipv4, + m->address, sizeof m->address, + m->port, sizeof m->port); + break; + + case NET_DOMAIN_SERIAL: + { + struct menu_serial_gui *const s = &m->gui.serial; + + init_serial_gui(s, m->device, sizeof m->device); + + if (net_serial_unique()) + { + s->device.common.hidden = true; + s->device_le.common.hidden = true; + } + else + s->device.common.hidden = false; + + break; + } + } + + m->type_btn.u.type1.label.text = net_domain_str(d); + m->cnt.common.hcentered = true; + /* Restore parent removed by init function. */ + m->gui.cnt.common.parent = &m->cnt.common; +} + +static int set_common(const struct menu_net_gui *const m, + const char **const addr, short *const port, + const char **const dev, unsigned long *const baud) +{ + switch (m->domain) + { + case NET_DOMAIN_IPV4: + { + const struct menu_ipv4_gui *const g = &m->gui.ipv4; + + if (addr) + *addr = g->address_le.text; + + if (port) + { + const char *const t = g->port_le.text; + + if (!*t) + return -1; + + errno = 0; + + const unsigned long port_ul = strtoul(t, NULL, 10); + + if (errno) + { + fprintf(stderr, "%s: strtoul(3) failed: %s\n", + __func__, strerror(errno)); + return -1; + } + + *port = port_ul; + } + } + break; + + case NET_DOMAIN_SERIAL: + { + const struct menu_serial_gui *const g = &m->gui.serial; + + if (dev) + *dev = g->device_le.label.text; + + if (baud) + { + const char *const t = g->speed_btn.u.type1.label.text; + + if (!*t) + return -1; + + errno = 0; + *baud = strtoul(t, NULL, 10); + + if (errno) + { + fprintf(stderr, "%s: strtoul(3) failed: %s\n", + __func__, strerror(errno)); + return -1; + } + } + } + break; + } + + return 0; +} + +int menu_net_server(const struct menu_net_gui *const m, + union net_server *const s) +{ + *s = (const union net_server) + { + .common = + { + .domain = m->domain, + .max_players = GAME_MAX_PLAYERS - 1 + } + }; + + struct net_serial_cfg *const scfg = &s->serial.cfg; + + return set_common(m, NULL, &s->ipv4.port, &scfg->dev, &scfg->baud); +} + +int menu_net_connect(const struct menu_net_gui *const m, + union net_connect *const c) +{ + *c = (const union net_connect) + { + .common = + { + .domain = m->domain + } + }; + + struct net_serial_cfg *const scfg = &c->serial.cfg; + + return set_common(m, &c->ipv4.addr, &c->ipv4.port, &scfg->dev, &scfg->baud); +} + +void menu_net_gui_update(struct menu_net_gui *const m) +{ + if (m->update_domain) + { + m->update_domain = false; + update_domain(m); + } +} + +int menu_net_gui_init(struct menu_net_gui *const m) +{ + { + struct gui_container *const c = &m->cnt; + + gui_container_init(c); + c->mode = GUI_CONTAINER_MODE_V; + c->spacing = 1; + } + + { + struct gui_label *const l = &m->type; + + gui_label_init(l); + l->common.hcentered = true; + l->text = "Type:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->type_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.hcentered = true; + b->u.type1.w = 140; + b->u.type1.label.text = net_domain_str(domains[m->domain]); + b->on_pressed = on_type_pressed; + b->arg = m; + gui_add_child(&m->cnt.common, &b->common); + } + + /* Set default port. */ + strcpy(m->port, "7822"); + + while (!net_available(domains[m->domain])) + if (++m->domain >= sizeof domains / sizeof *domains) + return -1; + + update_domain(m); + gui_add_child(&m->cnt.common, &m->gui.cnt.common); + return 0; +} diff --git a/src/menu/src/settings_menu.c b/src/menu/src/settings_menu.c index 0e782bd..72154fb 100644 --- a/src/menu/src/settings_menu.c +++ b/src/menu/src/settings_menu.c @@ -1,22 +1,77 @@ #include <menu_private.h> +#include <game.h> +#include <gfx.h> #include <gui.h> #include <gui/container.h> +#include <gui/checkbox.h> #include <gui/label.h> #include <gui/line_edit.h> +#include <gui/rounded_rect.h> #include <gui/button.h> +#include <errno.h> #include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> struct menu_settings { - struct gui_container cnt; - struct gui_button back; + struct gui_container cnt, bcnt, fs_cnt, res_cnt; + struct gui_rounded_rect r; + struct gui_checkbox fs_ch; + struct gui_label fs, xres, yres, periph, pad, name; + struct gui_button apply, back, periph_btn, pad_btn; + struct gui_line_edit name_le, xres_le, yres_le; + int pad_i; + size_t n_pads; + enum peripheral_type type; +}; + +static bool keyboard_mouse_available(void) +{ + return keyboard_available() && mouse_available(); +} + +static bool pad_available(void) +{ + return pad_count(); +} + +static const struct type +{ + const char *str; + bool (*available)(void); +} types[] = +{ + [PERIPHERAL_TYPE_KEYBOARD_MOUSE] = + { + .str = "Keyboard + mouse", + .available = keyboard_mouse_available + }, + + [PERIPHERAL_TYPE_TOUCH] = + { + .str = "Touch", + .available = keyboard_mouse_available + }, + + [PERIPHERAL_TYPE_PAD] = + { + .str = "Gamepad", + .available = pad_available + } }; static int update(struct menu_common *const c, void *const arg) { struct menu_settings *const m = arg; - if (gui_update(&m->cnt.common, &c->p, &c->cam, &c->in)) + m->bcnt.common.y = screen_h - 40; + m->res_cnt.common.hidden = !m->fs_ch.active; + + if (gui_update(&m->r.common, &c->p, &c->cam, &c->in) + || gui_update(&m->bcnt.common, &c->p, &c->cam, &c->in)) return -1; return 0; @@ -26,37 +81,332 @@ static int render(const struct menu_common *const c, void *const arg) { const struct menu_settings *const m = arg; - if (gui_render(&m->cnt.common)) + if (gui_render(&m->r.common) + || gui_render(&m->bcnt.common)) return -1; return 0; } -int menu_settings(struct menu_common *const c, bool *const back) +static void update_device(struct menu_settings *const m) +{ + const struct type *const t = &types[m->type]; + + m->periph_btn.u.type1.label.text = t->str; + + if (m->type == PERIPHERAL_TYPE_PAD) + { + m->pad_btn.common.hidden = false; + m->pad.common.hidden = false; + m->pad_btn.u.type1.label.text = pad_name(m->pad_i = 0); + } + else + { + m->pad_btn.common.hidden = true; + m->pad.common.hidden = true; + } +} + +static void on_input_pressed(void *const arg) { + struct menu_settings *const m = arg; + enum peripheral_type i = m->type; + do { - struct menu_settings m; + if (++i >= sizeof types / sizeof *types) + i = 0; + + const struct type *const t = &types[i]; + + if (t->available()) + { + m->type = i; + update_device(m); + break; + } + + } while (i != m->type); +} + +static void init_settings_gui(struct menu_settings *const m, + char *const name, const size_t name_len) +{ + { + struct gui_label *const l = &m->name; - gui_container_init(&m.cnt); - m.cnt.common.hcentered = true; - m.cnt.common.vcentered = true; - m.cnt.spacing = 4; - m.cnt.mode = GUI_CONTAINER_MODE_V; + gui_label_init(l); + l->font = FONT; + l->common.hcentered = true; + l->text = "Name:"; + gui_add_child(&m->cnt.common, &l->common); + } - gui_button_init(&m.back, GUI_BUTTON_TYPE_1); - m.back.u.type1.label.text = "Back"; - m.back.common.hcentered = true; - m.back.u.type1.w = 140; - m.back.arg = back; - m.back.on_pressed = menu_on_pressed; - gui_add_child(&m.cnt.common, &m.back.common); + { + struct gui_line_edit *const le = &m->name_le; - while (!*back && !c->p.common.exit) - if (menu_update(c, update, render, &m)) - return -1; + gui_line_edit_init(le, name, name_len); + le->common.hcentered = true; + le->w = 140; + gui_add_child(&m->cnt.common, &le->common); + } - } while (!*back && !c->p.common.exit); + { + struct gui_label *const l = &m->periph; - return 0; + gui_label_init(l); + l->common.hcentered = true; + l->font = FONT; + l->text = "Input method:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->periph_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.hcentered = true; + b->u.type1.w = 200; + b->on_pressed = on_input_pressed; + b->arg = m; + gui_add_child(&m->cnt.common, &b->common); + } + + { + struct gui_label *const l = &m->pad; + + gui_label_init(l); + l->common.hcentered = true; + l->font = FONT; + l->text = "Pad:"; + gui_add_child(&m->cnt.common, &l->common); + } + + { + struct gui_button *const b = &m->pad_btn; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->common.hcentered = true; + b->u.type1.w = 200; + gui_add_child(&m->cnt.common, &b->common); + } + + update_device(m); +} + +static void init_display_gui(struct menu_settings *const m, + char *const xres, const size_t xres_len, char *const yres, + const size_t yres_len) +{ + enum {WIDTH = 60}; + + { + struct gui_container *const c = &m->res_cnt; + + gui_container_init(c); + c->common.hcentered = true; + c->spacing = 2; + c->mode = GUI_CONTAINER_MODE_H; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->xres; + + gui_label_init(l); + l->font = FONT; + l->text = "x:"; + l->common.vcentered = true; + gui_add_child(&m->res_cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->xres_le; + + gui_line_edit_init(le, xres, xres_len); + le->common.vcentered = true; + le->w = WIDTH; + le->filters = GUI_LINE_EDIT_FILTER_NUM; + gui_add_child(&m->res_cnt.common, &le->common); + } + + { + struct gui_label *const l = &m->yres; + + gui_label_init(l); + l->font = FONT; + l->text = "y:"; + l->common.vcentered = true; + gui_add_child(&m->res_cnt.common, &l->common); + } + + { + struct gui_line_edit *const le = &m->yres_le; + + gui_line_edit_init(le, yres, yres_len); + le->common.vcentered = true; + le->w = WIDTH; + le->filters = GUI_LINE_EDIT_FILTER_NUM; + gui_add_child(&m->res_cnt.common, &le->common); + } +} + +static void init_fullscreen_gui(struct menu_settings *const m) +{ + { + struct gui_container *const c = &m->fs_cnt; + + gui_container_init(c); + c->common.hcentered = true; + c->spacing = 8; + c->mode = GUI_CONTAINER_MODE_H; + gui_add_child(&m->cnt.common, &c->common); + } + + { + struct gui_checkbox *const c = &m->fs_ch; + + gui_checkbox_init(c); + c->common.vcentered = true; + c->active = gfx_fullscreen(); + gui_add_child(&m->fs_cnt.common, &c->common); + } + + { + struct gui_label *const l = &m->fs; + + gui_label_init(l); + l->font = FONT; + l->text = "Fullscreen"; + l->common.vcentered = true; + gui_add_child(&m->fs_cnt.common, &l->common); + } +} + +static void init_footer_gui(struct menu_settings *const m, bool *const back, + bool *const apply) +{ + { + struct gui_container *const c = &m->bcnt; + + gui_container_init(c); + c->common.hcentered = true; + c->mode = GUI_CONTAINER_MODE_H; + } + + { + struct gui_button *const b = &m->apply; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Apply"; + b->common.vcentered = true; + b->u.type1.w = 140; + b->on_pressed = menu_on_pressed; + b->arg = apply; + gui_add_child(&m->bcnt.common, &b->common); + } + + { + struct gui_button *const b = &m->back; + + gui_button_init(b, GUI_BUTTON_TYPE_1); + b->u.type1.label.text = "Back"; + b->common.vcentered = true; + b->u.type1.w = 100; + b->arg = back; + b->on_pressed = menu_on_pressed; + gui_add_child(&m->bcnt.common, &b->common); + } +} + +static int apply_settings(struct menu_common *const c, + struct menu_settings *const m, + struct settings *const s) +{ + if (gfx_fullscreen_available() + && (s->fullscreen = m->fs_ch.active)) + { + const unsigned long w = strtoul(m->xres_le.text, NULL, 10), + h = strtoul(m->yres_le.text, NULL, 10); + + if (!w) + { + fprintf(stderr, "%s: unexpected X resolution %lu\n", + __func__, w); + return -1; + } + else if (!h) + { + fprintf(stderr, "%s: unexpected Y resolution %lu\n", + __func__, h); + return -1; + } + + s->screen_w = w; + s->screen_h = h; + } + + s->periph_type = m->type; + s->pad_i = m->pad_i; + strcpy(s->name, m->name_le.text); + return settings_apply(s, &(const struct settings_rt){.p = &c->p}); +} + +int menu_settings(struct menu_common *const c) +{ + int ret = -1; + struct menu_settings m = {0}; + + { + struct gui_rounded_rect *const r = &m.r; + + gui_rounded_rect_init(r); + r->adjust = true; + r->common.hcentered = true; + r->common.vcentered = true; + } + + { + struct gui_container *const c = &m.cnt; + + gui_container_init(c); + c->spacing = 4; + c->common.hcentered = true; + c->common.vcentered = true; + c->mode = GUI_CONTAINER_MODE_V; + gui_add_child(&m.r.common, &c->common); + } + + struct settings *const s = &c->s; + char name[sizeof s->name], xres[sizeof "65535"], yres[sizeof xres]; + bool back = false; + + strcpy(name, s->name); + m.type = c->p.common.type; + init_settings_gui(&m, name, sizeof name); + + if (gfx_fullscreen_available()) + { + init_fullscreen_gui(&m); + snprintf(xres, sizeof xres, "%hd", s->screen_w); + snprintf(yres, sizeof yres, "%hd", s->screen_h); + init_display_gui(&m, xres, sizeof xres, yres, sizeof yres); + } + + bool apply = false; + + init_footer_gui(&m, &back, &apply); + + while (!back && !c->p.common.exit && !apply) + if (menu_update(c, update, render, &m)) + goto end; + + if (apply && apply_settings(c, &m, s)) + goto end; + + ret = 0; + +end: + gui_deinit(&m.cnt.common, &c->in); + return ret; } diff --git a/src/mouse/inc/mouse.h b/src/mouse/inc/mouse.h index 60b63d3..55d0227 100644 --- a/src/mouse/inc/mouse.h +++ b/src/mouse/inc/mouse.h @@ -22,6 +22,7 @@ struct mouse void mouse_init(struct mouse *m); void mouse_update(struct mouse *m); +bool mouse_available(void); bool mouse_pressed(const struct mouse *m, enum mouse_button b); bool mouse_justpressed(const struct mouse *m, enum mouse_button b); bool mouse_justreleased(const struct mouse *m, enum mouse_button b); diff --git a/src/mouse/ps1/src/mouse.c b/src/mouse/ps1/src/mouse.c index 78ea425..865d4c1 100644 --- a/src/mouse/ps1/src/mouse.c +++ b/src/mouse/ps1/src/mouse.c @@ -1,9 +1,11 @@ #include <mouse.h> +#include <stdbool.h> void mouse_update(struct mouse *const m) { } -void mouse_init(struct mouse *const m) +bool mouse_available(void) { + return false; } diff --git a/src/mouse/sdl-1.2/src/mouse.c b/src/mouse/sdl-1.2/src/mouse.c index 56b22f3..35fc030 100644 --- a/src/mouse/sdl-1.2/src/mouse.c +++ b/src/mouse/sdl-1.2/src/mouse.c @@ -85,7 +85,8 @@ end: } } -void mouse_init(struct mouse *const m) +bool mouse_available(void) { - *m = (const struct mouse){0}; + /* Mouse availability is always assumed in SDL. */ + return true; } diff --git a/src/mouse/src/mouse.c b/src/mouse/src/mouse.c index bec978d..b0fab77 100644 --- a/src/mouse/src/mouse.c +++ b/src/mouse/src/mouse.c @@ -17,3 +17,8 @@ bool mouse_justreleased(const struct mouse *const m, { return !(m->mask & (1 << b)) && m->oldmask & (1 << b); } + +void mouse_init(struct mouse *const m) +{ + *m = (const struct mouse){0}; +} diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index 79bd653..03329c9 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -1,4 +1,6 @@ -set(src "src/common.c") +set(src "src/common.c" "src/serial.c") +set(priv_deps transport) +set(privinc "privinc") if(PS1_BUILD) set(src ${src} @@ -24,7 +26,6 @@ else() endif() set(priv_deps ${priv_deps} util ENET) - set(privinc ${privinc} "privinc") endif() add_library(net ${src}) diff --git a/src/net/inc/net.h b/src/net/inc/net.h index bbbd4ce..47bd0f8 100644 --- a/src/net/inc/net.h +++ b/src/net/inc/net.h @@ -3,7 +3,6 @@ #include <stdbool.h> #include <stddef.h> -#include <stdint.h> #ifdef __cplusplus extern "C" @@ -16,39 +15,54 @@ enum net_domain NET_DOMAIN_SERIAL }; +enum net_role +{ + NET_ROLE_SERVER, + NET_ROLE_CLIENT +}; + +typedef void *net_peer; + +struct net_event +{ + void (*connected)(net_peer p, void *arg); + void (*disconnected)(net_peer p, void *arg); + void (*received)(net_peer p, const void *buf, size_t n, void *arg); + void *arg; +}; + union net_connect { struct net_connect_common { enum net_domain domain; - - struct net_connect_ev - { - void (*connected)(void *arg); - void (*disconnected)(void *arg); - void *arg; - } ev; + struct net_event ev; } common; struct net_connect_ipv4 { struct net_connect_common common; const char *addr; - uint16_t port; + short port; } ipv4; struct net_connect_serial { struct net_connect_common common; - const char *dev; - unsigned long baud; - enum + struct net_serial_cfg { - NET_PARITY_NONE, - NET_PARITY_ODD, - NET_PARITY_EVEN - } parity; + const char *dev; + unsigned long baud; + bool hw_ctrl; + + enum + { + NET_PARITY_NONE, + NET_PARITY_ODD, + NET_PARITY_EVEN + } parity; + } cfg; } serial; }; @@ -58,31 +72,34 @@ union net_server { enum net_domain domain; unsigned max_players; + struct net_event ev; } common; struct net_server_ipv4 { struct net_server_common common; - uint16_t port; + short port; } ipv4; struct net_server_serial { struct net_server_common common; + struct net_serial_cfg cfg; } serial; }; -struct net_socket; +struct net_host; int net_init(void); void net_deinit(void); -int net_update(struct net_socket *s); +int net_update(struct net_host *s); bool net_available(enum net_domain d); -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); +struct net_host *net_server(const union net_server *srv); +struct net_host *net_connect(const union net_connect *c); +int net_write(struct net_host *h, net_peer p, const void *buf, size_t n); +int net_set_event(struct net_host *h, const struct net_event *ev); +int net_close(struct net_host *s); +enum net_role net_role(const struct net_host *s); const char *net_domain_str(enum net_domain d); #ifdef __cplusplus diff --git a/src/net/inc/net/serial.h b/src/net/inc/net/serial.h index ee81e9f..06cbc95 100644 --- a/src/net/inc/net/serial.h +++ b/src/net/inc/net/serial.h @@ -1,14 +1,14 @@ #ifndef NET_SERIAL_H #define NET_SERIAL_H -#include <stddef.h> +#include <stdbool.h> #ifdef __cplusplus extern "C" { #endif -const char *const *net_serial_devices(size_t *n); +bool net_serial_unique(void); #ifdef __cplusplus } diff --git a/src/net/posix/src/serial.c b/src/net/posix/src/serial.c index b0357b9..852fa0b 100644 --- a/src/net/posix/src/serial.c +++ b/src/net/posix/src/serial.c @@ -1,40 +1,290 @@ #include <net.h> #include <net/serial.h> #include <net_private.h> +#include <transport.h> +#include <fcntl.h> +#include <termios.h> +#include <unistd.h> +#include <errno.h> #include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> -int net_read_serial(struct net_socket_domain *const h, void *const buf, - const size_t n) +/* Based on https://en.wikibooks.org/wiki/Serial_Programming/termios */ + +struct net_host_domain { - return -1; + int fd; + enum net_role role; + struct transport_handle h; + struct net_event ev; +}; + +int net_write_serial(struct net_host_domain *const h, const net_peer p, + const void *const buf, const size_t n) +{ + return transport_send(&h->h, buf, n); } -int net_write_serial(struct net_socket_domain *const h, const void *const buf, - const size_t n) +int net_close_serial(struct net_host_domain *const h) { - return -1; + if (h && h->fd >= 0) + close(h->fd); + + free(h); + return 0; } -int net_close_serial(struct net_socket_domain *const h) +int net_update_serial(struct net_host_domain *const h) { - return -1; + return transport_update(&h->h); +} + +static void on_received(const struct transport_event *const ev, + void *const arg) +{ + struct net_host_domain *const h = arg; + + net_serial_on_received(ev, &h->ev); +} + +static int on_read(void *const buf, const size_t n, void *const arg) +{ + struct net_host_domain *const h = arg; + const ssize_t res = read(h->fd, buf, n); + + if (res < 0) + switch (errno) + { + case EAGAIN: + /* Fall through. */ + case EINTR: + return 0; + + default: + return -1; + } + + return res; +} + +static int on_write(const void *const buf, const size_t n, void *const arg) +{ + struct net_host_domain *const h = arg; + const ssize_t res = write(h->fd, buf, n); + + if (res < 0) + switch (errno) + { + case EAGAIN: + /* Fall through. */ + case EINTR: + return 0; + + default: + return -1; + } + + return res; } -int net_update_serial(struct net_socket_domain *const h) +static struct transport_cfg get_transport_cfg(void *const arg) { + return (const struct transport_cfg) + { + .arg = arg, + .received = on_received, + .read = on_read, + .write = on_write + }; +} + +static int open_dev(const char *const dev) +{ + return open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); +} + +static int get_speed(const unsigned long baud, speed_t *const out_s) +{ +#define BAUD(x) {.baud = x, .s = B##x} + + static const struct speed + { + unsigned long baud; + speed_t s; + } speeds[] = + { + BAUD(19200), + BAUD(38400), + BAUD(57600), + BAUD(115200) + }; + +#undef BAUD + + for (size_t i = 0; i < sizeof speeds / sizeof *speeds; i++) + { + const struct speed *const s = &speeds[i]; + + if (baud == s->baud) + { + *out_s = s->s; + return 0; + } + } + return -1; } -struct net_socket_domain *net_connect_serial(const union net_connect *const srv) +static int config_fd(const int fd, const unsigned long baud) +{ + struct termios t; + int ret = -1; + + if (tcgetattr(fd, &t)) + { + fprintf(stderr, "%s: tcgetattr(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + + /* cfmakeraw(3) is non-portable, but its man page states the + * following attributes are set. */ + t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + t.c_oflag &= ~OPOST; + t.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + t.c_cflag &= ~(CSIZE | PARENB); + t.c_cflag |= CS8; + + /* Make read(2) immediately regardless how many data is available. */ + t.c_cc[VMIN] = 0; + t.c_cc[VTIME] = 0; + + speed_t s; + + if (get_speed(baud, &s)) + { + fprintf(stderr, "%s: get_speed failed\n", __func__); + goto end; + } + else if (cfsetispeed(&t, s)) + { + fprintf(stderr, "%s: cfsetispeed(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + else if (cfsetospeed(&t, s)) + { + fprintf(stderr, "%s: cfsetospeed(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + else if (tcsetattr(fd, TCSANOW, &t)) + { + fprintf(stderr, "%s: tcsetattr(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + + ret = 0; + +end: + return ret; +} + +struct net_host_domain *net_connect_serial(const union net_connect *const c) { + struct net_host_domain *const h = malloc(sizeof *h); + const struct net_serial_cfg *const cfg = &c->serial.cfg; + + if (!h) + goto failure; + + *h = (const struct net_host_domain) + { + .role = NET_ROLE_CLIENT, + .fd = open_dev(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->fd < 0) + { + fprintf(stderr, "%s: open(2) failed: %s\n", __func__, + strerror(errno)); + goto failure; + } + else if (config_fd(h->fd, cfg->baud)) + { + fprintf(stderr, "%s: config_gf failed\n", __func__); + goto failure; + } + else if (transport_connect(&h->h)) + { + fprintf(stderr, "%s: transport_connect failed\n", __func__); + goto failure; + } + + return h; + +failure: + net_close_serial(h); return NULL; } -struct net_socket_domain *net_server_serial(const union net_server *const srv) +int net_set_event_serial(struct net_host_domain *const h, + const struct net_event *const ev) +{ + h->ev = *ev; + return 0; +} + +struct net_host_domain *net_server_serial(const union net_server *const s) { + struct net_host_domain *const h = malloc(sizeof *h); + const struct net_serial_cfg *const cfg = &s->serial.cfg; + + if (!h) + goto failure; + + *h = (const struct net_host_domain) + { + .role = NET_ROLE_SERVER, + .fd = open_dev(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->fd < 0) + { + fprintf(stderr, "%s: open(2) failed: %s\n", __func__, + strerror(errno)); + goto failure; + } + else if (config_fd(h->fd, cfg->baud)) + { + fprintf(stderr, "%s: config_gf failed\n", __func__); + goto failure; + } + + return h; + +failure: + net_close_serial(h); return NULL; } +enum net_role net_role_serial(const struct net_host_domain *const h) +{ + return h->role; +} + int net_init_serial(void) { return 0; @@ -44,7 +294,7 @@ void net_deinit_serial(void) { } -const char *const *net_serial_devices(size_t *const n) +bool net_serial_unique(void) { - return NULL; + return false; } diff --git a/src/net/privinc/net_private.h b/src/net/privinc/net_private.h index 53d7cd1..bc405d1 100644 --- a/src/net/privinc/net_private.h +++ b/src/net/privinc/net_private.h @@ -2,29 +2,35 @@ #define NET_PRIVATE_H #include <net.h> +#include <transport.h> +#include <stddef.h> -struct net_socket +struct net_host { enum net_domain d; - struct net_socket_domain *s; + struct net_host_domain *h; }; 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); +struct net_host_domain *net_server_ipv4(const union net_server *srv); +struct net_host_domain *net_connect_ipv4(const union net_connect *c); +int net_write_ipv4(struct net_host_domain *h, net_peer p, const void *buf, size_t n); +int net_close_ipv4(struct net_host_domain *h); +int net_update_ipv4(struct net_host_domain *h); +enum net_role net_role_ipv4(const struct net_host_domain *h); +int net_set_event_ipv4(struct net_host_domain *d, const struct net_event *ev); 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); +struct net_host_domain *net_server_serial(const union net_server *srv); +struct net_host_domain *net_connect_serial(const union net_connect *c); +int net_write_serial(struct net_host_domain *h, net_peer p, const void *buf, size_t n); +int net_close_serial(struct net_host_domain *h); +int net_update_serial(struct net_host_domain *h); +enum net_role net_role_serial(const struct net_host_domain *h); +int net_set_event_serial(struct net_host_domain *d, const struct net_event *ev); +void net_serial_on_received(const struct transport_event *t_ev, + const struct net_event *n_ev); #endif /* NET_PRIVATE_H */ diff --git a/src/net/privinc/net_serial_private.h b/src/net/privinc/net_serial_private.h new file mode 100644 index 0000000..74eee55 --- /dev/null +++ b/src/net/privinc/net_serial_private.h @@ -0,0 +1,19 @@ +#ifndef NET_SERIAL_PRIVATE_H +#define NET_SERIAL_PRIVATE_H + +#include <net.h> +#include <transport.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +void net_serial_on_received(const struct transport_event *t_ev, + const struct net_event *n_ev); + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SERIAL_PRIVATE_H */ diff --git a/src/net/ps1/src/net.c b/src/net/ps1/src/net.c index 8c037f3..af964cf 100644 --- a/src/net/ps1/src/net.c +++ b/src/net/ps1/src/net.c @@ -1,39 +1,233 @@ #include <net.h> #include <net/serial.h> -#include <errno.h> +#include <net_serial_private.h> +#include <transport.h> +#include <psx.h> +#include <psxsio.h> #include <stdbool.h> #include <stddef.h> +#include <stdio.h> -int net_read(struct net_socket *const h, void *const buf, const size_t n) +static struct net_host { - return -1; + enum net_role role; + bool used, sending; + struct transport_handle h; + struct net_event ev; + + struct net_host_fifo + { + char buf[32]; + size_t pending, read; + } in, out; +} host; + +enum {TX_INT_ENABLE = 1 << 10}; + +int net_write(struct net_host *const h, const net_peer peer, + const void *const buf, size_t n) +{ + return transport_send(&h->h, buf, n); } -int net_write(struct net_socket *const h, const void *const buf, const size_t n) +int net_close(struct net_host *const h) { - return -1; + SIOStop(); + RemoveSIOHandler(); + h->used = false; + return 0; +} + +static void send_byte(struct net_host *const h) +{ + struct net_host_fifo *const f = &h->out; + + if (f->pending != f->read) + { + if (SIOCheckOutBuffer()) + { + size_t new = f->read + 1; + + if (new >= sizeof f->buf) + new = 0; + + h->sending = true; + SIOSendByte(f->buf[f->read = new]); + } + } + else + { + h->sending = false; + SIO_CTRL &= ~TX_INT_ENABLE; + } } -int net_close(struct net_socket *const h) +int net_update(struct net_host *const h) { - return -1; + return transport_update(&h->h); } -int net_update(struct net_socket *const h) +static void on_received(const struct transport_event *const ev, + void *const arg) { - return -1; + struct net_host *const h = arg; + + net_serial_on_received(ev, &h->ev); } -struct net_socket *net_connect(const union net_connect *const c) +static int on_write(const void *const buf, size_t n, void *const arg) { - return NULL; + const size_t orig = n; + struct net_host *const h = arg; + + EnterCriticalSection(); + + for (const char *b = buf; n; n--, b++) + { + struct net_host_fifo *const f = &h->out; + size_t new = f->pending + 1; + + if (new >= sizeof f->buf) + new = 0; + + if (new == f->read) + { + fprintf(stderr, "%s: out FIFO full\n", __func__); + goto end; + } + + f->buf[f->pending = new] = *b; + } + + if (n != orig) + SIO_CTRL |= TX_INT_ENABLE; + +end: + ExitCriticalSection(); + return orig - n; +} + +static int on_read(void *const buf, const size_t n, void *const arg) +{ + size_t rem = n; + struct net_host *const h = arg; + struct net_host_fifo *const f = &h->in; + + EnterCriticalSection(); + + for (char *b = buf; rem; rem--, b++) + { + if (f->read == f->pending) + goto end; + + size_t new = f->read + 1; + + if (new >= sizeof f->buf) + new = 0; + + *b = f->buf[f->read = new]; + } + +end: + ExitCriticalSection(); + return n - rem; +} + +static struct transport_cfg get_transport_cfg(void) +{ + return (const struct transport_cfg) + { + .arg = &host, + .received = on_received, + .read = on_read, + .write = on_write + }; } -struct net_socket *net_server(const union net_server *const c) +static void on_byte_received(struct net_host *const h, const char ch) { + struct net_host_fifo *const f = &h->in; + size_t new = f->pending + 1; + + if (new >= sizeof f->buf) + new = 0; + + f->buf[f->pending = new] = ch; +} + +static void sio(void) +{ + struct net_host *const h = &host; + + if (SIOCheckInBuffer()) + on_byte_received(h, SIOReadByte()); + + send_byte(h); +} + +static void start_sio(const unsigned long baud) +{ + enum + { + RX_INT_MODE_1_BYTE = 1 << 8, + RX_INT_ENABLE = 1 << 11 + }; + + SIOStartEx(baud, SIO_DATA_LEN_8, SIO_PARITY_NONE, SIO_STOP_BIT_1); + SetSIOHandler(sio); + SIO_CTRL |= RX_INT_ENABLE | RX_INT_MODE_1_BYTE; +} + +struct net_host *net_connect(const union net_connect *const c) +{ + struct net_host *const h = &host; + + if (h->used) + goto failure; + + start_sio(c->serial.cfg.baud); + + *h = (const struct net_host) + { + .role = NET_ROLE_CLIENT, + .used = true, + .h = + { + .cfg = get_transport_cfg() + } + }; + + if (transport_connect(&h->h)) + goto failure; + + return h; + +failure: + net_close(h); return NULL; } +struct net_host *net_server(const union net_server *const c) +{ + struct net_host *const h = &host; + + if (h->used) + return NULL; + + *h = (const struct net_host) + { + .role = NET_ROLE_SERVER, + .used = true, + .h = + { + .cfg = get_transport_cfg() + } + }; + + start_sio(c->serial.cfg.baud); + return h; +} + int net_init(void) { return 0; @@ -43,18 +237,24 @@ void net_deinit(void) { } +int net_set_event(struct net_host *const h, + const struct net_event *const ev) +{ + h->ev = *ev; + return 0; +} + bool net_available(const enum net_domain d) { return d == NET_DOMAIN_SERIAL; } -const char *const *net_serial_devices(size_t *const n) +bool net_serial_unique(void) { - static const char *const dev[] = - { - "PS1 port" - }; + return true; +} - *n = sizeof dev / sizeof *dev; - return dev; +enum net_role net_role(const struct net_host *s) +{ + return s->role; } diff --git a/src/net/src/enet/ipv4.c b/src/net/src/enet/ipv4.c index 86d1697..53b6dc0 100644 --- a/src/net/src/enet/ipv4.c +++ b/src/net/src/enet/ipv4.c @@ -5,11 +5,11 @@ #include <stdio.h> #include <stdlib.h> -struct net_socket_domain +struct net_host_domain { + enum net_role role; ENetHost *host; - ENetPeer *peers; - struct net_connect_ev ev; + struct net_event ev; }; enum @@ -18,17 +18,20 @@ enum MAX_CHANNELS }; -struct net_socket_domain *net_connect_ipv4(const union net_connect *const c) +struct net_host_domain *net_connect_ipv4(const union net_connect *const c) { - struct net_socket_domain *const s = calloc(1, sizeof *s); + struct net_host_domain *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; - s->ev = c->common.ev; - s->host = enet_host_create(NULL, 1, MAX_CHANNELS, 0, 0); + *h = (const struct net_host_domain) + { + .ev = c->common.ev, + .host = enet_host_create(NULL, 1, MAX_CHANNELS, 0, 0) + }; - if (!s->host) + if (!h->host) { fprintf(stderr, "%s: enet_host_create failed\n", __func__); goto failure; @@ -44,66 +47,89 @@ struct net_socket_domain *net_connect_ipv4(const union net_connect *const c) addr.port = c->ipv4.port; - if (!(s->peers = enet_host_connect(s->host, &addr, MAX_CHANNELS, 0))) + if (!enet_host_connect(h->host, &addr, MAX_CHANNELS, 0)) { fprintf(stderr, "%s: enet_host_connect failed\n", __func__); goto failure; } - return s; + h->role = NET_ROLE_CLIENT; + return h; failure: - if (s && s->host) - enet_host_destroy(s->host); + if (h && h->host) + enet_host_destroy(h->host); - free(s); + free(h); return NULL; } -int net_read_ipv4(struct net_socket_domain *const s, void *const buf, - const size_t n) +int net_write_ipv4(struct net_host_domain *const h, const net_peer p, + const void *const buf, const size_t n) { - return -1; + ENetPacket *const pk = enet_packet_create(buf, n, ENET_PACKET_FLAG_RELIABLE); + + if (!p) + { + fprintf(stderr, "%s: enet_packet_create failed\n", __func__); + return -1; + } + else if (enet_peer_send(p, CHANNEL, pk)) + { + fprintf(stderr, "%s: enet_peer_send failed\n", __func__); + return -1; + } + + return 0; } -int net_write_ipv4(struct net_socket_domain *const s, const void *const buf, - const size_t n) +int net_set_event_ipv4(struct net_host_domain *const d, + const struct net_event *const ev) { - return -1; + d->ev = *ev; + return 0; } -int net_close_ipv4(struct net_socket_domain *const s) +int net_close_ipv4(struct net_host_domain *const h) { - if (s && s->host) - enet_host_destroy(s->host); + if (h && h->host) + enet_host_destroy(h->host); - free(s); + free(h); return 0; } -int net_update_ipv4(struct net_socket_domain *const s) +int net_update_ipv4(struct net_host_domain *const h) { int res; ENetEvent ev; - while ((res = enet_host_service(s->host, &ev, 0)) > 0) + while ((res = enet_host_service(h->host, &ev, 0)) > 0) { switch (ev.type) { case ENET_EVENT_TYPE_CONNECT: - if (s->ev.connected) - s->ev.connected(s->ev.arg); + if (h->ev.connected) + h->ev.connected(ev.peer, h->ev.arg); break; case ENET_EVENT_TYPE_DISCONNECT: - if (s->ev.disconnected) - s->ev.disconnected(s->ev.arg); + if (h->ev.disconnected) + h->ev.disconnected(ev.peer, h->ev.arg); break; case ENET_EVENT_TYPE_RECEIVE: + if (h->ev.received) + { + const enet_uint8 *const buf = ev.packet->data; + const size_t n = ev.packet->dataLength; + + if (buf && n) + h->ev.received(ev.peer, buf, n, h->ev.arg); + } break; case ENET_EVENT_TYPE_NONE: @@ -120,11 +146,11 @@ int net_update_ipv4(struct net_socket_domain *const s) return 0; } -struct net_socket_domain *net_server_ipv4(const union net_server *const srv) +struct net_host_domain *net_server_ipv4(const union net_server *const srv) { - struct net_socket_domain *const s = calloc(1, sizeof *s); + struct net_host_domain *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; const ENetAddress addr = @@ -132,22 +158,31 @@ struct net_socket_domain *net_server_ipv4(const union net_server *const srv) .port = srv->ipv4.port }; - s->host = enet_host_create(&addr, srv->common.max_players, - MAX_CHANNELS, 0, 0); + *h = (const struct net_host_domain) + { + .host = enet_host_create(&addr, srv->common.max_players, + MAX_CHANNELS, 0, 0) + }; - if (!s->host) + if (!h->host) { fprintf(stderr, "%s: enet_host_create failed\n", __func__); goto failure; } - return s; + h->role = NET_ROLE_SERVER; + return h; failure: - free(s); + free(h); return NULL; } +enum net_role net_role_ipv4(const struct net_host_domain *const h) +{ + return h->role; +} + int net_init_ipv4(void) { return enet_initialize(); diff --git a/src/net/src/net.c b/src/net/src/net.c index f5dc938..fb0d155 100644 --- a/src/net/src/net.c +++ b/src/net/src/net.c @@ -1,104 +1,129 @@ #include <net.h> #include <net_private.h> +#include <net_serial_private.h> #include <stddef.h> #include <stdlib.h> -struct net_socket *net_connect(const union net_connect *const c) +struct net_host *net_connect(const union net_connect *const c) { - struct net_socket *const s = calloc(1, sizeof *s); + struct net_host *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; - static struct net_socket_domain *(*const f[])(const union net_connect *) = + static struct net_host_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))) + *h = (const struct net_host) + { + .d = c->common.domain + }; + + if (!(h->h = f[h->d](c))) goto failure; - return s; + return h; failure: - free(s); + free(h); return NULL; } -int net_read(struct net_socket *const s, void *const buf, const size_t n) +int net_write(struct net_host *const h, const net_peer p, + const 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) = + static int (*const f[])(struct net_host_domain *, net_peer, + const void *, size_t) = { [NET_DOMAIN_IPV4] = net_write_ipv4, [NET_DOMAIN_SERIAL] = net_write_serial }; - return f[s->d](s->s, buf, n); + return f[h->d](h->h, p, buf, n); } -int net_close(struct net_socket *const s) +int net_close(struct net_host *const h) { - if (!s) + if (!h) return 0; - static int (*const f[])(struct net_socket_domain *) = + static int (*const f[])(struct net_host_domain *) = { [NET_DOMAIN_IPV4] = net_close_ipv4, [NET_DOMAIN_SERIAL] = net_close_serial }; - const int res = f[s->d](s->s); + const int res = f[h->d](h->h); - free(s); + free(h); return res; } -int net_update(struct net_socket *const s) +int net_update(struct net_host *const h) { - static int (*const f[])(struct net_socket_domain *) = + static int (*const f[])(struct net_host_domain *) = { [NET_DOMAIN_IPV4] = net_update_ipv4, [NET_DOMAIN_SERIAL] = net_update_serial }; - return f[s->d](s->s); + return f[h->d](h->h); } -struct net_socket *net_server(const union net_server *const srv) +struct net_host *net_server(const union net_server *const srv) { - struct net_socket *const s = calloc(1, sizeof *s); + struct net_host *const h = malloc(sizeof *h); - if (!s) + if (!h) goto failure; - s->d = srv->common.domain; - - static struct net_socket_domain *(*const f[])(const union net_server *) = + static struct net_host_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))) + *h = (const struct net_host) + { + .d = srv->common.domain + }; + + if (!(h->h = f[h->d](srv))) goto failure; + return h; + failure: - net_close(s); + net_close(h); return NULL; } +enum net_role net_role(const struct net_host *const h) +{ + static enum net_role (*const f[])(const struct net_host_domain *) = + { + [NET_DOMAIN_IPV4] = net_role_ipv4, + [NET_DOMAIN_SERIAL] = net_role_serial + }; + + return f[h->d](h->h); +} + +int net_set_event(struct net_host *const h, + const struct net_event *const ev) +{ + static int (*const f[])(struct net_host_domain *, const struct net_event *) = + { + [NET_DOMAIN_IPV4] = net_set_event_ipv4, + [NET_DOMAIN_SERIAL] = net_set_event_serial + }; + + return f[h->d](h->h, ev); +} + int net_init(void) { return net_init_ipv4() || net_init_serial(); diff --git a/src/net/src/serial.c b/src/net/src/serial.c new file mode 100644 index 0000000..a9f3f73 --- /dev/null +++ b/src/net/src/serial.c @@ -0,0 +1,37 @@ +#include <net.h> +#include <net_serial_private.h> +#include <transport.h> + +void net_serial_on_received(const struct transport_event *const t_ev, + const struct net_event *const n_ev) +{ + static struct peer + { + int dummy; + } peer; + + switch (t_ev->common.type) + { + case TRANSPORT_EVENT_TYPE_CONNECT: + if (n_ev->connected) + n_ev->connected(&peer, n_ev->arg); + + break; + + case TRANSPORT_EVENT_TYPE_DISCONNECT: + if (n_ev->disconnected) + n_ev->disconnected(&peer, n_ev->arg); + + break; + + case TRANSPORT_EVENT_TYPE_DATA: + if (n_ev->received) + { + const struct transport_event_data *const d = &t_ev->u.data; + + n_ev->received(&peer, d->buf, d->n, n_ev->arg); + } + + break; + } +} diff --git a/src/net/win9x/src/serial.c b/src/net/win9x/src/serial.c index c12369a..c1cffb6 100644 --- a/src/net/win9x/src/serial.c +++ b/src/net/win9x/src/serial.c @@ -1,36 +1,406 @@ #include <net.h> #include <net_private.h> +#include <windows.h> +#include <errno.h> #include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> -int net_read_serial(struct net_socket_domain *const h, void *const buf, - const size_t n) +struct net_host_domain { - return -1; + HANDLE com; + enum net_role role; + struct transport_handle h; + struct net_event ev; + + struct req + { + const void *buf; + size_t n; + OVERLAPPED ov; + + enum req_type + { + REQ_TYPE_READ, + REQ_TYPE_WRITE + } type; + } *reqs; + + size_t n_reqs; +}; + +int net_write_serial(struct net_host_domain *const h, const net_peer p, + const void *const buf, const size_t n) +{ + return transport_send(&h->h, buf, n); +} + +int net_close_serial(struct net_host_domain *const h) +{ + if (h) + { + if (h->com != INVALID_HANDLE_VALUE) + CloseHandle(h->com); + + if (h->reqs) + for (size_t i = 0; i < h->n_reqs; i++) + { + struct req *const r = &h->reqs[i]; + + if (r->ov.hEvent) + CloseHandle(r->ov.hEvent); + } + + free(h->reqs); + } + + free(h); + return 0; +} + +int net_update_serial(struct net_host_domain *const h) +{ + return transport_update(&h->h); +} + +static void on_received(const struct transport_event *const ev, + void *const arg) +{ + struct net_host_domain *const h = arg; + + net_serial_on_received(ev, &h->ev); +} + +static struct req *find_req(struct net_host_domain *const h, + const void *const buf, const size_t n, const enum req_type t) +{ + for (size_t i = 0; i < h->n_reqs; i++) + { + struct req *const r = &h->reqs[i]; + + if (r->buf == buf && r->n == n && r->type == t) + return r; + } + + return NULL; } -int net_write_serial(struct net_socket_domain *const h, const void *const buf, - const size_t n) +static int free_req(struct net_host_domain *const h, struct req *const r) { + for (size_t i = 0; i < h->n_reqs; i++) + if (&h->reqs[i] == r) + { + const HANDLE ev = r->ov.hEvent; + + if (ev) + CloseHandle(ev); + + for (size_t j = i + 1; j < h->n_reqs; j++) + h->reqs[j - 1] = h->reqs[j]; + + const size_t n = h->n_reqs - 1; + + if (n) + { + if (!(h->reqs = realloc(h->reqs, n * sizeof *h->reqs))) + { + fprintf(stderr, "%s: realloc(3) failed: %s\n", + __func__, strerror(errno)); + return -1; + } + } + else + { + free(h->reqs); + h->reqs = NULL; + } + + h->n_reqs = n; + return 0; + } + return -1; } -int net_close_serial(struct net_socket_domain *const h) +static struct req *alloc_req(struct net_host_domain *const h, + const void *const buf, const size_t n, const enum req_type t) +{ + if (!(h->reqs = realloc(h->reqs, (h->n_reqs + 1) * sizeof *h->reqs))) + { + fprintf(stderr, "%s: realloc(3) failed: %s\n", + __func__, strerror(errno)); + return NULL; + } + + struct req *const r = &h->reqs[h->n_reqs++]; + + *r = (const struct req) + { + .buf = buf, + .n = n, + .type = t, + .ov = + { + .hEvent = CreateEvent(NULL, TRUE, FALSE, NULL) + } + }; + + if (!r->ov.hEvent) + { + fprintf(stderr, "%s: CreateEvent failed: %#lx\n", __func__, + GetLastError()); + free_req(h, r); + return NULL; + } + + return r; +} + +static int get_result(struct net_host_domain *const h, struct req *const r) +{ + DWORD res; + + if (GetOverlappedResult(h->com, &r->ov, &res, FALSE) == FALSE) + { + const DWORD error = GetLastError(); + + if (error != ERROR_IO_INCOMPLETE) + { + fprintf(stderr, "%s: GetOverlappedResult failed: %#lx\n", + __func__, error); + return -1; + } + else + return 0; + } + else if (ResetEvent(r->ov.hEvent) == FALSE) + { + fprintf(stderr, "%s: ResetEvent failed: %#lx\n", __func__, + GetLastError()); + return -1; + } + else if (free_req(h, r)) + { + fprintf(stderr, "%s: free_req failed\n", __func__); + return -1; + } + + return res; +} + +static int on_read(void *const buf, const size_t n, void *const arg) { + const enum req_type t = REQ_TYPE_READ; + struct net_host_domain *const h = arg; + struct req *r = find_req(h, buf, n, t); + + if (!r) + { + DWORD res; + + if (!(r = alloc_req(h, buf, n, t))) + { + fprintf(stderr, "%s: alloc_req failed\n", __func__); + goto failure; + } + else if (ReadFile(h->com, buf, n, &res, &r->ov) == FALSE + && GetLastError() != ERROR_IO_PENDING) + { + fprintf(stderr, "%s: ReadFile failed: %#lx\n", + __func__, GetLastError()); + goto failure; + } + } + + return get_result(h, r); + +failure: + if (r) + free_req(h, r); + return -1; } -int net_update_serial(struct net_socket_domain *const h) +static int on_write(const void *const buf, const size_t n, void *const arg) { + const enum req_type t = REQ_TYPE_WRITE; + struct net_host_domain *const h = arg; + struct req *r = find_req(h, buf, n, t); + + if (!r) + { + DWORD res; + + if (!(r = alloc_req(h, buf, n, t))) + { + fprintf(stderr, "%s: alloc_req failed\n", __func__); + goto failure; + } + else if (WriteFile(h->com, buf, n, &res, &r->ov) == FALSE + && GetLastError() != ERROR_IO_PENDING) + { + fprintf(stderr, "%s: WriteFile failed: %#lx\n", + __func__, GetLastError()); + goto failure; + } + } + + return get_result(h, r); + +failure: + if (r) + free_req(h, r); + return -1; } -struct net_socket_domain *net_connect_serial(const union net_connect *const srv) +static struct transport_cfg get_transport_cfg(void *const arg) +{ + return (const struct transport_cfg) + { + .arg = arg, + .received = on_received, + .read = on_read, + .write = on_write + }; +} + +static HANDLE init_com(const char *const dev) +{ + static const char *const prefix = "\\\\.\\"; + + /* snprintf(NULL, 0, ...) could have been used to determine the + * total length of the string. However, this is a C99 feature that + * must be supported by the underlying libc, which is not the case + * for Win9x, where only ANSI C is supported. */ + char *const fulldev = calloc(1, strlen(prefix) + strlen(dev) + 1); + HANDLE ret = INVALID_HANDLE_VALUE; + + if (!fulldev) + { + fprintf(stderr, "%s: calloc(3) failed: %s\n", __func__, + strerror(errno)); + goto end; + } + + strcat(fulldev, prefix); + strcat(fulldev, dev); + + ret = CreateFile( + fulldev, + GENERIC_READ | GENERIC_WRITE, + 0, /* No sharing. */ + NULL, /* No security. */ + OPEN_EXISTING, /* Open existing port only. */ + FILE_FLAG_OVERLAPPED, /* Asynchronous I/O. */ + NULL /* NULL for COM devices. */); + + if (ret == INVALID_HANDLE_VALUE) + fprintf(stderr, "%s: CreateFile failed: %#lx\n", __func__, + GetLastError()); + +end: + free(fulldev); + return ret; +} + +static int config_baud(const HANDLE com, const unsigned long baud) +{ + DCB dcb = + { + .DCBlength = sizeof dcb, + .BaudRate = baud, + .ByteSize = 8, + .StopBits = ONESTOPBIT, + .Parity = NOPARITY, + .fBinary = TRUE + }; + + if (SetCommState(com, &dcb) == FALSE) + { + fprintf(stderr, "%s: SetCommState failed: %#lx\n", + __func__, GetLastError()); + return -1; + } + + return 0; +} + +struct net_host_domain *net_connect_serial(const union net_connect *const c) { + struct net_host_domain *const h = malloc(sizeof *h); + const struct net_serial_cfg *const cfg = &c->serial.cfg; + + if (!h) + goto failure; + + *h = (const struct net_host_domain) + { + .role = NET_ROLE_CLIENT, + .com = init_com(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->com == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "%s: init_com failed\n", __func__); + goto failure; + } + else if (config_baud(h->com, cfg->baud)) + { + fprintf(stderr, "%s: config_com_baud failed\n", __func__); + goto failure; + } + else if (transport_connect(&h->h)) + { + fprintf(stderr, "%s: transport_connect failed\n", __func__); + goto failure; + } + + return h; + +failure: + net_close_serial(h); return NULL; } -struct net_socket_domain *net_server_serial(const union net_server *const srv) +struct net_host_domain *net_server_serial(const union net_server *const s) { + struct net_host_domain *const h = malloc(sizeof *h); + const struct net_serial_cfg *const cfg = &s->serial.cfg; + + if (!h) + goto failure; + + *h = (const struct net_host_domain) + { + .role = NET_ROLE_SERVER, + .com = init_com(cfg->dev), + .h = + { + .cfg = get_transport_cfg(h) + } + }; + + if (h->com == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "%s: init_com failed\n", __func__); + goto failure; + } + else if (config_baud(h->com, cfg->baud)) + { + fprintf(stderr, "%s: config_com_baud failed\n", __func__); + goto failure; + } + + return h; + +failure: + net_close_serial(h); return NULL; } @@ -43,7 +413,19 @@ void net_deinit_serial(void) { } -const char *const *net_serial_devices(size_t *const n) +int net_set_event_serial(struct net_host_domain *const d, + const struct net_event *const ev) { - return NULL; + d->ev = *ev; + return 0; +} + +enum net_role net_role_serial(const struct net_host_domain *const h) +{ + return h->role; +} + +bool net_serial_unique(void) +{ + return false; } diff --git a/src/packet/CMakeLists.txt b/src/packet/CMakeLists.txt new file mode 100644 index 0000000..597dff5 --- /dev/null +++ b/src/packet/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(packet "src/packet.c") +target_include_directories(packet PUBLIC "inc") +target_link_libraries(packet PUBLIC game net PRIVATE util) diff --git a/src/packet/inc/packet.h b/src/packet/inc/packet.h new file mode 100644 index 0000000..6f555a6 --- /dev/null +++ b/src/packet/inc/packet.h @@ -0,0 +1,91 @@ +#ifndef PACKET_H +#define PACKET_H + +#include <game.h> +#include <net.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +union packet +{ + struct packet_common + { + enum packet_type + { + PACKET_TYPE_NAME, + PACKET_TYPE_CHAT, + PACKET_TYPE_READY, + + MAX_PACKET_TYPES + } type; + } common; + + struct packet_name + { + struct packet_common common; + const char *name; + } name; + + struct packet_chat + { + struct packet_common common; + const char *msg; + } chat; + + struct packet_ready + { + struct packet_common common; + bool ready; + } ready; +}; + +enum {PACKET_MAX_MESSAGE_LEN = 64}; + +struct packet_ctx +{ + struct net_host *host; + void (*cb)(net_peer, const union packet *, void *); + void *arg; +}; + +struct packet_input +{ + union packet p; + size_t body_i; + + enum + { + PACKET_STATE_HEADER, + PACKET_STATE_BODY + } state; + + union packet_data + { + struct packet_ctx_name + { + size_t len; + char name[GAME_PLAYER_NAME_LEN]; + } name; + + struct packet_ctx_chat + { + size_t len; + char msg[PACKET_MAX_MESSAGE_LEN]; + } chat; + } data; +}; + +int packet_send(const struct packet_ctx *h, net_peer peer, const union packet *pkt); +int packet_feed(const struct packet_ctx *h, net_peer p, + struct packet_input *in, const void *buf, size_t n); + +#ifdef __cplusplus +} +#endif + +#endif /* PACKET_H */ diff --git a/src/packet/src/packet.c b/src/packet/src/packet.c new file mode 100644 index 0000000..3ace4af --- /dev/null +++ b/src/packet/src/packet.c @@ -0,0 +1,251 @@ +#include <packet.h> +#include <net.h> +#include <util.h> +#include <ctype.h> +#include <inttypes.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +typedef uint8_t packet_len, packet_ready; + +int packet_send(const struct packet_ctx *const h, const net_peer peer, + const union packet *const pkt) +{ + const uint8_t header = pkt->common.type; + + if (net_write(h->host, peer, &header, sizeof header)) + { + fprintf(stderr, "%s: failed to write header\n", __func__); + return -1; + } + + const void *buf = NULL; + size_t n = 0; + bool send_len = false; + packet_ready r; + + switch (pkt->common.type) + { + case PACKET_TYPE_NAME: + buf = pkt->name.name; + n = strlen(buf); + send_len = true; + break; + + case PACKET_TYPE_CHAT: + buf = pkt->chat.msg; + n = strlen(buf); + send_len = true; + break; + + case PACKET_TYPE_READY: + r = pkt->ready.ready; + buf = &r; + n = sizeof r; + break; + + default: + fprintf(stderr, "%s: unknown packet type %d\n", __func__, + pkt->common.type); + return -1; + } + + if (send_len) + { + const packet_len len = n; + + if (net_write(h->host, peer, &len, sizeof len)) + { + fprintf(stderr, "%s: failed to write body length\n", __func__); + return -1; + } + } + + if (net_write(h->host, peer, buf, n)) + { + fprintf(stderr, "%s: failed to write body\n", __func__); + return -1; + } + + return 0; +} + +static int read_header(struct packet_input *const in, + const void **const buf, size_t *const n) +{ + const uint8_t header = *(const uint8_t *)*buf; + + if (header >= MAX_PACKET_TYPES) + { + fprintf(stderr, "%s: invalid packet type %" PRIu8 "\n", + __func__, header); + return -1; + } + + in->p.common.type = header; + *buf = (const uint8_t *)*buf + sizeof header; + *n -= sizeof header; + return 0; +} + +static int read_string_len(size_t *const len, const size_t maxlen, + const void **const buf, size_t *const n) +{ + *len = *(const packet_len *)*buf; + + if (*len >= maxlen) + { + fprintf(stderr, "%s: maximum length exceeded (%zu/%zu)\n", + __func__, *len, maxlen); + return -1; + } + else if (!*len) + { + fprintf(stderr, "%s: invalid zero length\n", __func__); + return -1; + } + + *buf = (const uint8_t *)*buf + sizeof (packet_len); + *n -= sizeof (packet_len); + return 0; +} + +static int read_string_body(struct packet_input *const in, char *const dst, + const size_t len, const void **const buf, size_t *const n) +{ + const size_t rem = len > *n ? *n : len; + + for (const char *ch = *buf; + ch - (const char *)*buf < rem; + ch++) + { + if (!isprint((unsigned char)*ch)) + { + fprintf(stderr, "%s: unexpected character %#hhx\n", __func__, *ch); + return -1; + } + + dst[in->body_i++] = *ch; + } + + *buf = (const uint8_t *)*buf + rem; + *n -= rem; + + if (in->body_i == len) + { + dst[in->body_i] = '\0'; + return 0; + } + + return 1; +} + +static int read_string(struct packet_input *const in, char *const dst, + size_t *const len, const size_t maxlen, const void **const buf, + size_t *const n) +{ + if (!*len && read_string_len(len, maxlen, buf, n)) + return -1; + else if (!*n) + return 1; + + return read_string_body(in, dst, *len, buf, n); +} + +static int read_chat(struct packet_input *const in, const void **const buf, + size_t *const n) +{ + struct packet_ctx_chat *const c = &in->data.chat; + const int ret = read_string(in, c->msg, &c->len, sizeof c->msg - 1, buf, n); + + if (!ret) + in->p.chat.msg = c->msg; + + return ret; +} + +static int read_name(struct packet_input *const in, const void **const buf, + size_t *const n) +{ + struct packet_ctx_name *const c = &in->data.name; + const int ret = read_string(in, c->name, &c->len, + sizeof c->name - 1, buf, n); + + if (!ret) + in->p.name.name = c->name; + + return ret; +} + +static int read_ready(struct packet_input *const in, const void **const buf, + size_t *const n) +{ + const packet_ready pr = *(const packet_ready *)*buf; + + if (pr > 1) + { + fprintf(stderr, "%s: invalid ready flag %#hhx\n", __func__, pr); + return -1; + } + + in->p.ready.ready = pr; + *buf = (const uint8_t *)*buf + sizeof pr; + *n -= sizeof pr; + return 0; +} + +static int read_body(struct packet_input *const in, const void **const buf, + size_t *const n) +{ + static int (*const f[])(struct packet_input *, const void **, size_t *) = + { + [PACKET_TYPE_CHAT] = read_chat, + [PACKET_TYPE_NAME] = read_name, + [PACKET_TYPE_READY] = read_ready + }; + + return f[in->p.common.type](in, buf, n); +} + +static void reset(struct packet_input *const in) +{ + *in = (const struct packet_input){0}; +} + +int packet_feed(const struct packet_ctx *const h, const net_peer p, + struct packet_input *const in, const void *buf, size_t n) +{ + int res; + + switch (in->state) + { + case PACKET_STATE_HEADER: + if ((res = read_header(in, &buf, &n)) < 0) + goto failure; + else if (!res) + in->state = PACKET_STATE_BODY; + + if (!n) + break; + + /* Fall through. */ + case PACKET_STATE_BODY: + if ((res = read_body(in, &buf, &n)) < 0) + goto failure; + else if (!res) + { + h->cb(p, &in->p, h->arg); + reset(in); + } + + break; + } + + return 0; + +failure: + reset(in); + return -1; +} diff --git a/src/pad/inc/pad.h b/src/pad/inc/pad.h index c8f6b31..d68f734 100644 --- a/src/pad/inc/pad.h +++ b/src/pad/inc/pad.h @@ -3,6 +3,7 @@ #include <pad/port.h> #include <stdbool.h> +#include <stddef.h> #ifdef __cplusplus extern "C" @@ -34,17 +35,20 @@ enum pad_key struct pad { - int player; + int id; int mask, oldmask; struct pad_port port; }; -void pad_init(int player, struct pad *p); +void pad_init(int id, struct pad *p); +void pad_deinit(int id, struct pad *p); void pad_update(struct pad *p); bool pad_pressed(const struct pad *p, enum pad_key k); bool pad_justpressed(const struct pad *p, enum pad_key k); bool pad_released(const struct pad *p, enum pad_key k); const char *pad_str(enum pad_key k); +size_t pad_count(void); +const char *pad_name(int id); #ifdef __cplusplus } diff --git a/src/pad/ps1/src/pad.c b/src/pad/ps1/src/pad.c index eea56ed..660cf89 100644 --- a/src/pad/ps1/src/pad.c +++ b/src/pad/ps1/src/pad.c @@ -4,10 +4,18 @@ #include <stddef.h> #include <string.h> -void pad_init(const int player, struct pad *const p) +static const char *const pads[] = { - memset(p, 0, sizeof *p); - p->player = player; + "Pad 1", + "Pad 2" +}; + +void pad_init(const int id, struct pad *const p) +{ + *p = (const struct pad) + { + .id = id + }; } void pad_port_update(struct pad *const p) @@ -27,7 +35,7 @@ void pad_port_update(struct pad *const p) }; p->mask = 0; - PSX_PollPad(p->player, &p->port.pps); + PSX_PollPad(p->id, &p->port.pps); for (size_t i = 0; i < sizeof psx_keys / sizeof *psx_keys; i++) { @@ -39,3 +47,13 @@ void pad_port_update(struct pad *const p) p->mask &= ~mask; } } + +size_t pad_count(void) +{ + return sizeof pads / sizeof *pads; +} + +const char *pad_name(const int id) +{ + return pads[id]; +} diff --git a/src/pad/sdl-1.2/inc/pad/port.h b/src/pad/sdl-1.2/inc/pad/port.h index 8334b6a..d28bb11 100644 --- a/src/pad/sdl-1.2/inc/pad/port.h +++ b/src/pad/sdl-1.2/inc/pad/port.h @@ -1,6 +1,8 @@ #ifndef PAD_SDL_1_2_H #define PAD_SDL_1_2_H +#include <SDL_joystick.h> + #ifdef __cplusplus extern "C" { @@ -8,7 +10,7 @@ extern "C" struct pad_port { - int dummy; + SDL_Joystick *joystick; }; #ifdef __cplusplus diff --git a/src/pad/sdl-1.2/src/pad.c b/src/pad/sdl-1.2/src/pad.c index 57fdb25..97aa639 100644 --- a/src/pad/sdl-1.2/src/pad.c +++ b/src/pad/sdl-1.2/src/pad.c @@ -1,11 +1,57 @@ #include <pad.h> #include <SDL.h> +#include <SDL_joystick.h> #include <stdio.h> +static void global_init(void) +{ + SDL_JoystickEventState(SDL_ENABLE); +} + void pad_port_update(struct pad *const p) { + enum {FLAGS = SDL_JOYBUTTONDOWNMASK + | SDL_JOYBUTTONUPMASK + | SDL_JOYAXISMOTIONMASK + | SDL_QUITMASK}; + + SDL_Event ev; + int n; + + while ((n = SDL_PeepEvents(&ev, 1, SDL_GETEVENT, FLAGS)) > 0) + { + } + + if (n < 0) + fprintf(stderr, "%s: SDL_PeepEvents: %s\n", __func__, SDL_GetError()); +} + +void pad_init(const int id, struct pad *const p) +{ + static bool init; + + if (!init) + { + global_init(); + init = true; + } + + if (!(p->port.joystick = SDL_JoystickOpen(id))) + fprintf(stderr, "%s: SDL_JoystickOpen failed: %s\n", + __func__, SDL_GetError()); + else + printf("%s: num axis: %d\n", __func__, + SDL_JoystickNumAxes(p->port.joystick)); +} + +size_t pad_count(void) +{ + const int res = SDL_NumJoysticks(); + + return res > 0 ? res : 0; } -void pad_init(const int player, struct pad *const p) +const char *pad_name(const int id) { + return SDL_JoystickName(id); } diff --git a/src/peripheral/inc/peripheral.h b/src/peripheral/inc/peripheral.h index 8f8f73b..2895f54 100644 --- a/src/peripheral/inc/peripheral.h +++ b/src/peripheral/inc/peripheral.h @@ -55,6 +55,7 @@ UTIL_STATIC_ASSERT(!offsetof(struct peripheral_pad, common), UTIL_STATIC_ASSERT(!offsetof(struct peripheral_kbm, common), "unexpected offsetof for struct peripheral_kbm"); +int peripheral_get_default(struct peripheral_cfg *cfg); void peripheral_init(const struct peripheral_cfg *cfg, union peripheral *p); void peripheral_input_set(union peripheral *p, void (*cb)(char, void *)); void peripheral_update(union peripheral *p); diff --git a/src/peripheral/src/peripheral.c b/src/peripheral/src/peripheral.c index eeec4fa..71f81f8 100644 --- a/src/peripheral/src/peripheral.c +++ b/src/peripheral/src/peripheral.c @@ -62,5 +62,14 @@ void peripheral_init(const struct peripheral_cfg *const cfg, int peripheral_get_default(struct peripheral_cfg *const cfg) { - return -1; + *cfg = (const struct peripheral_cfg){0}; + + if (pad_count()) + cfg->type = PERIPHERAL_TYPE_PAD; + else if (keyboard_available() && mouse_available()) + cfg->type = PERIPHERAL_TYPE_KEYBOARD_MOUSE; + else + return -1; + + return 0; } diff --git a/src/player/inc/human_player.h b/src/player/inc/human_player.h index 173ff80..abc25e9 100644 --- a/src/player/inc/human_player.h +++ b/src/player/inc/human_player.h @@ -22,9 +22,8 @@ enum {MAX_SELECTED_INSTANCES = 4}; struct human_player_cfg { - enum peripheral_type sel_periph; struct player_cfg pl; - int padn; + union peripheral *p; struct camera_dim dim; }; @@ -32,7 +31,7 @@ struct human_player { struct player pl; struct camera cam; - union peripheral periph; + union peripheral *periph; struct input in; struct sel_instance diff --git a/src/player/src/human_player.c b/src/player/src/human_player.c index 314581b..cded3b0 100644 --- a/src/player/src/human_player.c +++ b/src/player/src/human_player.c @@ -543,7 +543,7 @@ static void update_target(struct human_player *const h) static void update_from_pad(struct human_player *const h, struct player_others *const o) { - const struct pad *const p = &h->periph.pad.pad; + const struct pad *const p = &h->periph->pad.pad; const struct input *const in = &h->in; if (input_pad_justpressed(in, p, PAD_KEY_A)) @@ -559,9 +559,9 @@ static void update_from_pad(struct human_player *const h, static void update_from_touch(struct human_player *const h, struct player_others *const o) { - const struct mouse *const m = &h->periph.kbm.mouse; + const struct mouse *const m = &h->periph->kbm.mouse; const struct input *const in = &h->in; - struct peripheral_kbm *const kbm = &h->periph.kbm; + struct peripheral_kbm *const kbm = &h->periph->kbm; bool *const pan = &h->cam.pan; if (input_mouse_pressed(in, m, MOUSE_BUTTON_LEFT) && !*pan) @@ -590,8 +590,8 @@ static void update_from_touch(struct human_player *const h, static void update_from_keyboard_mouse(struct human_player *const h, struct player_others *const o) { - const struct mouse *const m = &h->periph.kbm.mouse; - const struct keyboard *const k = &h->periph.kbm.keyboard; + const struct mouse *const m = &h->periph->kbm.mouse; + const struct keyboard *const k = &h->periph->kbm.keyboard; const struct input *const in = &h->in; if (input_mouse_justreleased(in, m, MOUSE_BUTTON_LEFT)) @@ -619,10 +619,10 @@ void human_player_update(struct human_player *const h, human_player_gui_update(h); update_selected(h); update_target(h); - peripheral_update(&h->periph); - input_update(&h->in, &h->periph); + peripheral_update(h->periph); + input_update(&h->in, h->periph); - switch (h->periph.common.type) + switch (h->periph->common.type) { case PERIPHERAL_TYPE_PAD: update_from_pad(h, o); @@ -637,7 +637,7 @@ void human_player_update(struct human_player *const h, break; } - camera_update(&h->cam, &h->periph, &h->in); + camera_update(&h->cam, h->periph, &h->in); player_update(p); } } @@ -710,10 +710,10 @@ int human_player_render(const struct human_player *const h, || render_own_buildings(h) || render_resources(h, o->res, o->n_res) || human_player_gui_render(h) - || input_render(&h->in, &h->periph)) + || input_render(&h->in, h->periph)) return -1; - switch (h->periph.common.type) + switch (h->periph->common.type) { case PERIPHERAL_TYPE_PAD: /* Fall through. */ @@ -739,14 +739,8 @@ int human_player_init(const struct human_player_cfg *const cfg, if (player_init(&cfg->pl, &h->pl)) return -1; - const struct peripheral_cfg p_cfg = - { - .type = cfg->sel_periph, - .padn = cfg->padn - }; - - peripheral_init(&p_cfg, &h->periph); cursor_init(&h->cam.cursor); + h->periph = cfg->p; h->cam.dim = cfg->dim; h->top_gui = true; UTIL_STATIC_ASSERT(sizeof h->gui_res == sizeof h->pl.resources, diff --git a/src/player/src/human_player_gui.c b/src/player/src/human_player_gui.c index fce749e..79245dc 100644 --- a/src/player/src/human_player_gui.c +++ b/src/player/src/human_player_gui.c @@ -248,14 +248,13 @@ static int render_sel_multiple(const struct human_player *const h, static int render_sel(const struct human_player *const h) { - enum {OFFSET = 60}; struct gui_rounded_rect r; gui_rounded_rect_init(&r); r.common.x = 16; - r.common.y = screen_h - OFFSET; r.w = screen_w - (r.common.x * 2); - r.h = OFFSET; + r.h = screen_h / 4; + r.common.y = screen_h - r.h; if (h->n_sel == 1) return render_sel_single(h, &r.common); diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt new file mode 100644 index 0000000..f7442e4 --- /dev/null +++ b/src/settings/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(settings + "src/settings.c" +) +set(inc "inc") + +if(PS1_BUILD) + set(inc ${inc} "ps1/inc") +elseif(SDL1_2_BUILD) + set(inc ${inc} "sdl-1.2/inc") +endif() + +target_include_directories(settings PUBLIC ${inc}) +target_link_libraries(settings PUBLIC game peripheral PRIVATE gfx) diff --git a/src/settings/inc/settings.h b/src/settings/inc/settings.h new file mode 100644 index 0000000..25e73b9 --- /dev/null +++ b/src/settings/inc/settings.h @@ -0,0 +1,36 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include <settings/port.h> +#include <game.h> +#include <peripheral.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +struct settings +{ + short screen_w, screen_h; + bool fullscreen; + enum peripheral_type periph_type; + int pad_i; + char name[GAME_PLAYER_NAME_LEN]; +}; + +struct settings_rt +{ + union peripheral *p; +}; + +int settings_load_ex(const char *path, struct settings *s); +int settings_apply(const struct settings *s, const struct settings_rt *r); +int settings_save_ex(const char *path, const struct settings *s); + +#ifdef __cplusplus +} +#endif + +#endif /* SETTINGS_H */ diff --git a/src/settings/ps1/inc/settings/port.h b/src/settings/ps1/inc/settings/port.h new file mode 100644 index 0000000..2792717 --- /dev/null +++ b/src/settings/ps1/inc/settings/port.h @@ -0,0 +1,16 @@ +#ifndef SETTINGS_PORT_H +#define SETTINGS_PORT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define settings_load(path, s) settings_load_ex("bu00:\\" path, s) +#define settings_save(path, s) settings_save_ex("bu00:\\" path, s) + +#ifdef __cplusplus +} +#endif + +#endif /* SETTINGS_PORT_H */ diff --git a/src/settings/sdl-1.2/inc/settings/port.h b/src/settings/sdl-1.2/inc/settings/port.h new file mode 100644 index 0000000..6332842 --- /dev/null +++ b/src/settings/sdl-1.2/inc/settings/port.h @@ -0,0 +1,15 @@ +#ifndef SETTINGS_PORT_H +#define SETTINGS_PORT_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define settings_load(...) settings_load_ex(__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#endif /* SETTINGS_PORT_H */ diff --git a/src/settings/src/settings.c b/src/settings/src/settings.c new file mode 100644 index 0000000..0427ef8 --- /dev/null +++ b/src/settings/src/settings.c @@ -0,0 +1,48 @@ +#include <settings.h> +#include <gfx.h> +#include <peripheral.h> +#include <stdio.h> + +int settings_apply(const struct settings *const s, + const struct settings_rt *const r) +{ + if (gfx_fullscreen_available() + && gfx_set_fullscreen(s->screen_w, s->screen_h)) + return -1; + + const struct peripheral_cfg cfg = + { + .padn = s->pad_i, + .type = s->periph_type + }; + + peripheral_init(&cfg, r->p); + return 0; +} + +int settings_load_ex(const char *const path, struct settings *const s) +{ + int ret = -1; + FILE *const f = fopen(path, "rb"); + + if (!f) + goto end; + + ret = 0; + +end: + if (ret) + { + *s = (const struct settings) + { + .name = "Player", + }; + + gfx_display_size(&s->screen_w, &s->screen_h); + } + + if (f) + fclose(f); + + return ret; +} diff --git a/src/system/CMakeLists.txt b/src/system/CMakeLists.txt index 4c315b9..e541096 100644 --- a/src/system/CMakeLists.txt +++ b/src/system/CMakeLists.txt @@ -8,9 +8,16 @@ if(PS1_BUILD) elseif(SDL1_2_BUILD) set(src "sdl-1.2/src/system.c") set(inc ${inc} "sdl-1.2/inc") + set(privinc ${privinc} "sdl-1.2/privinc") set(privdeps ${privdeps} SDL::SDL) + + if(WIN9X_BUILD) + set(src ${src} "win9x/src/system.c") + else() + set(src ${src} "sdl-1.2/src/stubs.c") + endif() endif() add_library(system ${src}) -target_include_directories(system PUBLIC ${inc}) +target_include_directories(system PUBLIC ${inc} PRIVATE ${privinc}) target_link_libraries(system PRIVATE ${privdeps}) diff --git a/src/system/sdl-1.2/privinc/system_private.h b/src/system/sdl-1.2/privinc/system_private.h new file mode 100644 index 0000000..70ed8c8 --- /dev/null +++ b/src/system/sdl-1.2/privinc/system_private.h @@ -0,0 +1,15 @@ +#ifndef SYSTEM_PRIVATE_H +#define SYSTEM_PRIVATE_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +int system_init_os(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SYSTEM_PRIVATE_H */ diff --git a/src/system/sdl-1.2/src/stubs.c b/src/system/sdl-1.2/src/stubs.c new file mode 100644 index 0000000..c07a790 --- /dev/null +++ b/src/system/sdl-1.2/src/stubs.c @@ -0,0 +1,6 @@ +#include <system_private.h> + +int system_init_os(void) +{ + return 0; +} diff --git a/src/system/sdl-1.2/src/system.c b/src/system/sdl-1.2/src/system.c index bff5e4f..71d5d7e 100644 --- a/src/system/sdl-1.2/src/system.c +++ b/src/system/sdl-1.2/src/system.c @@ -1,7 +1,8 @@ +#include <system.h> +#include <system_private.h> #include <gfx.h> -#include <sfx.h> #include <net.h> -#include <system.h> +#include <sfx.h> #include <SDL.h> #include <stdio.h> #include <stdlib.h> @@ -26,7 +27,9 @@ void system_deinit(void) int system_init(void) { - if (SDL_Init(0)) + if (system_init_os()) + goto failure; + else if (SDL_Init(0)) { fprintf(stderr, "SDL_Init: %s\n", SDL_GetError()); goto failure; @@ -36,6 +39,8 @@ int system_init(void) SDL_WM_SetCaption("rts", NULL); SDL_ShowCursor(0); + /* SDL_WM_GrabInput(SDL_GRAB_ON); */ + SDL_EnableUNICODE(1); return 0; failure: diff --git a/src/system/win9x/src/system.c b/src/system/win9x/src/system.c new file mode 100644 index 0000000..dd2aa10 --- /dev/null +++ b/src/system/win9x/src/system.c @@ -0,0 +1,36 @@ +#include <system_private.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +int system_init_os(void) +{ + /* Windows GUI applications do not print stderr or stdout anywhere. + * Therefore, it makes sense to redirect them to plaintext files. */ + if (!(freopen("stderr.txt", "wb", stderr))) + { + fprintf(stderr, "%s: freopen(3) stderr failed: %s\n", + __func__, strerror(errno)); + return -1; + } + else if (setvbuf(stderr, NULL, _IONBF, 0)) + { + fprintf(stderr, "%s: setvbuf(3) stderr failed: %s\n", + __func__, strerror(errno)); + return -1; + } + else if (!(freopen("stdout.txt", "wb", stdout))) + { + fprintf(stderr, "%s: freopen(3) stdout failed: %s\n", + __func__, strerror(errno)); + return -1; + } + else if (setvbuf(stdout, NULL, _IONBF, 0)) + { + fprintf(stderr, "%s: setvbuf(3) stdout failed: %s\n", + __func__, strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/transport/CMakeLists.txt b/src/transport/CMakeLists.txt new file mode 100644 index 0000000..8f05c31 --- /dev/null +++ b/src/transport/CMakeLists.txt @@ -0,0 +1,10 @@ +set(src "src/transport.c") + +if(PS1_BUILD) + set(src ${src} "src/static.c") +elseif(SDL1_2_BUILD) + set(src ${src} "src/heap.c") +endif() + +add_library(transport ${src}) +target_include_directories(transport PUBLIC "inc" PRIVATE "privinc") diff --git a/src/transport/inc/transport.h b/src/transport/inc/transport.h new file mode 100644 index 0000000..503812b --- /dev/null +++ b/src/transport/inc/transport.h @@ -0,0 +1,66 @@ +#ifndef TRANSPORT_H +#define TRANSPORT_H + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum {TRANSPORT_MAX_DATA_LEN = 32}; + +struct transport_event +{ + struct transport_event_common + { + enum transport_event_type + { + TRANSPORT_EVENT_TYPE_CONNECT, + TRANSPORT_EVENT_TYPE_DISCONNECT, + TRANSPORT_EVENT_TYPE_DATA + } type; + } common; + + union + { + struct transport_event_data + { + const void *buf; + size_t n; + } data; + } u; +}; + +union transport_packet; + +struct transport_handle +{ + struct transport_cfg + { + void *arg; + int (*write)(const void *, size_t, void *); + int (*read)(void *, size_t, void *); + void (*received)(const struct transport_event *, void *); + } cfg; + + union transport_packet **packets; + size_t n_packets; + + struct transport_input + { + union transport_packet *p; + struct transport_event ev; + } input; +}; + +int transport_connect(struct transport_handle *h); +int transport_disconnect(struct transport_handle *h); +int transport_send(struct transport_handle *h, const void *buf, size_t n); +int transport_update(struct transport_handle *h); + +#ifdef __cplusplus +} +#endif + +#endif /* TRANSPORT_H */ diff --git a/src/transport/privinc/transport_private.h b/src/transport/privinc/transport_private.h new file mode 100644 index 0000000..dc7481e --- /dev/null +++ b/src/transport/privinc/transport_private.h @@ -0,0 +1,66 @@ +#ifndef TRANSPORT_PRIVATE_H +#define TRANSPORT_PRIVATE_H + +#include <transport.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef uint8_t transport_checksum; + +union transport_packet +{ + struct transport_common + { + enum transport_packet_type + { + TRANSPORT_PACKET_TYPE_CONNECT, + TRANSPORT_PACKET_TYPE_DISCONNECT, + TRANSPORT_PACKET_TYPE_ACK, + TRANSPORT_PACKET_TYPE_NACK, + TRANSPORT_PACKET_TYPE_DATA, + + MAX_TRANSPORT_PACKET_TYPES + } type; + + enum transport_state + { + TRANSPORT_STATE_HEADER, + TRANSPORT_STATE_LEN, + TRANSPORT_STATE_BODY, + TRANSPORT_STATE_CHECKSUM + } state; + + bool done; + unsigned ttl; + } common; + + struct transport_packet_data + { + struct transport_common common; + uint8_t buf[TRANSPORT_MAX_DATA_LEN]; + size_t n, written; + } data; + + struct transport_packet_ack + { + struct transport_common common; + transport_checksum checksum; + } ack; +}; + +union transport_packet *transport_packet_alloc(enum transport_packet_type t); +void transport_packet_free(union transport_packet *p); +int transport_append(struct transport_handle *h, union transport_packet *p); +int transport_pop(struct transport_handle *h); + +#ifdef __cplusplus +} +#endif + +#endif /* TRANSPORT_PRIVATE_H */ diff --git a/src/transport/src/heap.c b/src/transport/src/heap.c new file mode 100644 index 0000000..cf43426 --- /dev/null +++ b/src/transport/src/heap.c @@ -0,0 +1,93 @@ +#include <transport.h> +#include <transport_private.h> +#include <stdlib.h> + +static void *alloc_common(void) +{ + struct transport_common *const ret = malloc(sizeof *ret); + + if (ret) + *ret = (const struct transport_common){0}; + + return ret; +} + +static void *alloc_ack(void) +{ + struct transport_packet_ack *const ret = malloc(sizeof *ret); + + if (ret) + *ret = (const struct transport_packet_ack){0}; + + return ret; +} + +static void *alloc_data(void) +{ + struct transport_packet_data *const ret = malloc(sizeof *ret); + + if (ret) + *ret = (const struct transport_packet_data){0}; + + return ret; +} + +union transport_packet *transport_packet_alloc(const enum transport_packet_type t) +{ + static void *(*const alloc[])(void) = + { + [TRANSPORT_PACKET_TYPE_CONNECT] = alloc_common, + [TRANSPORT_PACKET_TYPE_DISCONNECT] = alloc_common, + [TRANSPORT_PACKET_TYPE_NACK] = alloc_common, + [TRANSPORT_PACKET_TYPE_ACK] = alloc_ack, + [TRANSPORT_PACKET_TYPE_DATA] = alloc_data + }; + + union transport_packet *const ret = alloc[t](); + + if (!ret) + return NULL; + + ret->common.type = t; + return ret; +} + +int transport_append(struct transport_handle *const h, + union transport_packet *const p) +{ + h->packets = realloc(h->packets, (h->n_packets + 1) * sizeof *h->packets); + + if (!h->packets) + return -1; + + h->packets[h->n_packets++] = p; + return 0; +} + +void transport_packet_free(union transport_packet *const p) +{ + free(p); +} + +int transport_pop(struct transport_handle *const h) +{ + if (!h->n_packets) + return -1; + + union transport_packet *const p = h->packets[h->n_packets - 1]; + + if (--h->n_packets) + { + if (!(h->packets = realloc(h->packets, + (h->n_packets - 1) * sizeof *h->packets))) + return -1; + } + else + { + free(h->packets); + h->packets = NULL; + } + + transport_packet_free(p); + return 0; +} diff --git a/src/transport/src/static.c b/src/transport/src/static.c new file mode 100644 index 0000000..e5dda54 --- /dev/null +++ b/src/transport/src/static.c @@ -0,0 +1,71 @@ +#include <transport.h> +#include <transport_private.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +static struct list +{ + union transport_packet p; + bool used; +} list[16]; + +static union transport_packet *packets[sizeof list / sizeof *list]; + +union transport_packet *transport_packet_alloc(const enum transport_packet_type t) +{ + for (size_t i = 0; i < sizeof list / sizeof *list; i++) + { + struct list *const l = &list[i]; + + if (!l->used) + { + union transport_packet *const p = &l->p; + + l->used = true; + *p = (const union transport_packet) + { + .common = + { + .type = t + } + }; + + return p; + } + } + + return NULL; +} + +int transport_append(struct transport_handle *const h, + union transport_packet *const p) +{ + if (h->n_packets >= sizeof packets / sizeof *packets) + return -1; + + packets[h->n_packets++] = p; + h->packets = packets; + return 0; +} + +void transport_packet_free(union transport_packet *const p) +{ + for (size_t i = 0; i < sizeof list / sizeof *list; i++) + { + struct list *const l = &list[i]; + + if (p == &l->p) + { + l->used = false; + return; + } + } +} + +int transport_pop(struct transport_handle *const h) +{ + transport_packet_free(h->packets[h->n_packets - 1]); + return 0; +} diff --git a/src/transport/src/transport.c b/src/transport/src/transport.c new file mode 100644 index 0000000..fe2d54f --- /dev/null +++ b/src/transport/src/transport.c @@ -0,0 +1,613 @@ +#include <transport.h> +#include <transport_private.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +typedef uint8_t packet_header, packet_len; + +int transport_connect(struct transport_handle *const h) +{ + union transport_packet *const p = transport_packet_alloc( + TRANSPORT_PACKET_TYPE_CONNECT); + + if (!p) + return -1; + + return transport_append(h, p); +} + +int transport_disconnect(struct transport_handle *const h) +{ + union transport_packet *const p = transport_packet_alloc( + TRANSPORT_PACKET_TYPE_DISCONNECT); + + if (!p) + return -1; + + return transport_append(h, p); +} + +int transport_send(struct transport_handle *const h, + const void *const buf, size_t n) +{ + const char *b = buf; + + while (n) + { + union transport_packet *const p = transport_packet_alloc( + TRANSPORT_PACKET_TYPE_DATA); + + if (!p) + return -1; + + struct transport_packet_data *const d = &p->data; + const size_t rem = n > sizeof d->buf ? sizeof d->buf : n; + + memcpy(d->buf, b, rem); + d->n = rem; + + if (transport_append(h, p)) + return -1; + + n -= rem; + b += rem; + } + + return 0; +} + +static enum transport_state header_next_state(const enum transport_packet_type t) +{ + switch (t) + { + case TRANSPORT_PACKET_TYPE_CONNECT: + /* Fall through. */ + case TRANSPORT_PACKET_TYPE_DISCONNECT: + /* Fall through. */ + case TRANSPORT_PACKET_TYPE_NACK: + /* Fall through. */ + return TRANSPORT_STATE_CHECKSUM; + + case TRANSPORT_PACKET_TYPE_ACK: + return TRANSPORT_STATE_BODY; + + case TRANSPORT_PACKET_TYPE_DATA: + return TRANSPORT_STATE_LEN; + + default: + break; + } + + return 0; +} + +static int read_header(const struct transport_cfg *const cfg, + struct transport_input *const in, bool *const done) +{ + packet_header header; + const int res = cfg->read(&header, sizeof header, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof header) + return 1; + + if (header >= MAX_TRANSPORT_PACKET_TYPES) + { + fprintf(stderr, "%s: invalid header %" PRIu8 "\n", + __func__, header); + return -1; + } + + if (!(in->p = transport_packet_alloc(header))) + { + fprintf(stderr, "%s: transport_packet_alloc failed\n", __func__); + return -1; + } + + struct transport_common *const c = &in->p->common; + + c->state = header_next_state(c->type); + return 0; +} + +static int read_len(const struct transport_cfg *const cfg, + struct transport_input *const in, bool *const done) +{ + packet_len len; + const int res = cfg->read(&len, sizeof len, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof len) + return 1; + + struct transport_packet_data *const d = &in->p->data; + + if (len >= sizeof d->buf) + { + fprintf(stderr, "%s: invalid length %" PRIu8 "\n", __func__, len); + return -1; + } + + d->n = len; + d->common.state = TRANSPORT_STATE_BODY; + return 0; +} + +static int read_data_body(const struct transport_cfg *const cfg, + struct transport_input *const in) +{ + struct transport_packet_data *const d = &in->p->data; + const size_t rem = d->n - d->written; + const int res = cfg->read(&d->buf[d->written], rem, cfg->arg); + + if (res < 0) + return res; + else if (res != rem) + { + d->written += res; + return 1; + } + + in->p->common.state = TRANSPORT_STATE_CHECKSUM; + return 0; +} + +static int read_ack_body(const struct transport_cfg *const cfg, + struct transport_input *const in) +{ + struct transport_packet_ack *const a = &in->p->ack; + const int res = cfg->read(&a->checksum, sizeof a->checksum, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof a->checksum) + return 1; + + in->p->common.state = TRANSPORT_STATE_CHECKSUM; + return 0; +} + +static int read_body(const struct transport_cfg *const cfg, + struct transport_input *const in, bool *const done) +{ + const struct transport_common *const c = &in->p->common; + + switch (c->type) + { + case TRANSPORT_PACKET_TYPE_ACK: + return read_ack_body(cfg, in); + + case TRANSPORT_PACKET_TYPE_DATA: + return read_data_body(cfg, in); + + default: + break; + } + + return -1; +} + +static transport_checksum calc_checksum(const union transport_packet *const p) +{ + const struct transport_common *const c = &p->common; + transport_checksum ret = c->type; + + switch (c->type) + { + case TRANSPORT_PACKET_TYPE_DATA: + { + const struct transport_packet_data *const d = &p->data; + + ret += d->n; + + for (size_t i = 0; i < d->n; i++) + ret += d->buf[i]; + } + break; + + case TRANSPORT_PACKET_TYPE_ACK: + ret += p->ack.checksum; + break; + + default: + break; + } + + return ~ret; +} + +static int read_checksum(const struct transport_cfg *const cfg, + struct transport_input *const in, bool *const done) +{ + transport_checksum checksum; + const int res = cfg->read(&checksum, sizeof checksum, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof checksum) + return 1; + + const transport_checksum expected = calc_checksum(in->p); + + *done = true; + + if (checksum != expected) + { + fprintf(stderr, "%s: invalid checksum %#" PRIx8 ", expected %#" PRIx8, + __func__, checksum, expected); + return -1; + } + + return 0; +} + +static int send_nack(struct transport_handle *const h) +{ + union transport_packet *const p = transport_packet_alloc( + TRANSPORT_PACKET_TYPE_NACK); + + if (!p) + return -1; + + return transport_append(h, p); +} + +static bool requires_ack(const enum transport_packet_type t) +{ + static const bool r[] = + { + [TRANSPORT_PACKET_TYPE_CONNECT] = true, + [TRANSPORT_PACKET_TYPE_DISCONNECT] = true, + [TRANSPORT_PACKET_TYPE_ACK] = false, + [TRANSPORT_PACKET_TYPE_NACK] = false, + [TRANSPORT_PACKET_TYPE_DATA] = true + }; + + return r[t]; +} + +static int send_ack(struct transport_handle *const h) +{ + union transport_packet *const p = transport_packet_alloc( + TRANSPORT_PACKET_TYPE_ACK); + + if (!p) + return -1; + + p->ack.checksum = calc_checksum(h->input.p); + return transport_append(h, p); +} + +static void delete_input(struct transport_input *const in) +{ + transport_packet_free(in->p); + in->p = NULL; +} + +static void send_event(const struct transport_cfg *const cfg, + const union transport_packet *const p, + struct transport_event *const ev) +{ + switch (p->common.type) + { + case TRANSPORT_PACKET_TYPE_CONNECT: + ev->common.type = TRANSPORT_EVENT_TYPE_CONNECT; + break; + + case TRANSPORT_PACKET_TYPE_DISCONNECT: + ev->common.type = TRANSPORT_EVENT_TYPE_DISCONNECT; + break; + + case TRANSPORT_PACKET_TYPE_DATA: + ev->common.type = TRANSPORT_EVENT_TYPE_DATA; + ev->u.data.buf = p->data.buf; + ev->u.data.n = p->data.n; + break; + + default: + return; + } + + if (cfg->received) + cfg->received(ev, cfg->arg); +} + +static int remove_packet(struct transport_handle *const h, + union transport_packet *const p) +{ + for (size_t i = 0; i < h->n_packets; i++) + { + union transport_packet **pp = &h->packets[i]; + + if (*pp == p) + { + if (i + 1 < h->n_packets) + for (union transport_packet **pr = &h->packets[i + 1]; + pr - h->packets < h->n_packets; + pp++, pr++) + *pp = *pr; + + return transport_pop(h); + } + } + + return -1; +} + +static void get_event(const struct transport_cfg *const cfg, + const union transport_packet *const p, + struct transport_event *const ev) +{ + switch (p->common.type) + { + case TRANSPORT_PACKET_TYPE_CONNECT: + ev->common.type = TRANSPORT_EVENT_TYPE_CONNECT; + break; + + case TRANSPORT_PACKET_TYPE_DISCONNECT: + ev->common.type = TRANSPORT_EVENT_TYPE_DISCONNECT; + break; + + default: + return; + } + + if (cfg->received) + cfg->received(ev, cfg->arg); +} + +static int process_ack(struct transport_handle *const h, + const struct transport_packet_ack *const a) +{ + for (size_t i = 0; i < h->n_packets; i++) + { + union transport_packet *const p = h->packets[i]; + + if (p->common.ttl && a->checksum == calc_checksum(p)) + { + get_event(&h->cfg, p, &h->input.ev); + + if (remove_packet(h, p)) + return -1; + + break; + } + } + + return 0; +} + +static int process_nack(struct transport_handle *const h) +{ + if (!h->n_packets) + { + fprintf(stderr, "%s: received nack without previous sent packet\n", + __func__); + return -1; + } + + h->packets[0]->common.ttl = 0; + return 0; +} + +static int process_input(struct transport_handle *const h) +{ + const union transport_packet *const p = h->input.p; + + switch (p->common.type) + { + case TRANSPORT_PACKET_TYPE_ACK: + return process_ack(h, &p->ack); + + case TRANSPORT_PACKET_TYPE_NACK: + return process_nack(h); + + default: + break; + } + + return 0; +} + +static int update_input(struct transport_handle *const h) +{ + int ret = -1; + bool done = false; + struct transport_input *const in = &h->input; + + static int (*const f[])(const struct transport_cfg *, + struct transport_input *, bool *) = + { + [TRANSPORT_STATE_HEADER] = read_header, + [TRANSPORT_STATE_LEN] = read_len, + [TRANSPORT_STATE_BODY] = read_body, + [TRANSPORT_STATE_CHECKSUM] = read_checksum + }; + + const struct transport_cfg *const cfg = &h->cfg; + + while (!done) + { + const enum transport_state s = in->p ? in->p->common.state : 0; + const int res = f[s](cfg, in, &done); + + if (res < 0) + { + ret = send_nack(h); + goto end; + } + else if (res) + return 0; + } + + const union transport_packet *const p = in->p; + const enum transport_packet_type t = p->common.type; + + if (process_input(h)) + goto end; + else if (requires_ack(t)) + { + if (send_ack(h)) + { + fprintf(stderr, "%s: send_ack failed\n", __func__); + goto end; + } + + send_event(&h->cfg, p, &h->input.ev); + } + + ret = 0; + +end: + delete_input(in); + return ret; +} + +static int write_header(const struct transport_cfg *const cfg, + union transport_packet *const p) +{ + const packet_header header = p->common.type; + struct transport_common *const c = &p->common; + const int res = cfg->write(&header, sizeof header, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof header) + return 1; + + c->state = header_next_state(c->type); + return 0; +} + +static int write_len(const struct transport_cfg *const cfg, + union transport_packet *const p) +{ + const packet_len len = p->data.n; + const int res = cfg->write(&len, sizeof len, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof len) + return 1; + + p->common.state = TRANSPORT_STATE_BODY; + return 0; +} + +static int write_ack_body(const struct transport_cfg *const cfg, + struct transport_packet_ack *const a) +{ + const int res = cfg->write(&a->checksum, sizeof a->checksum, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof a->checksum) + return 1; + + a->common.state = TRANSPORT_STATE_CHECKSUM; + return 0; +} + +static int write_data_body(const struct transport_cfg *const cfg, + struct transport_packet_data *const d) +{ + const size_t rem = d->n - d->written; + const int res = cfg->write(&d->buf[d->written], rem, cfg->arg); + + if (res < 0) + return res; + else if (res != rem) + { + d->written += res; + return 1; + } + + d->common.state = TRANSPORT_STATE_CHECKSUM; + return 0; +} + +static int write_body(const struct transport_cfg *const cfg, + union transport_packet *const p) +{ + struct transport_common *const c = &p->common; + + switch (c->type) + { + case TRANSPORT_PACKET_TYPE_ACK: + return write_ack_body(cfg, &p->ack); + + case TRANSPORT_PACKET_TYPE_DATA: + return write_data_body(cfg, &p->data); + + default: + break; + } + + return -1; +} + +static int write_checksum(const struct transport_cfg *const cfg, + union transport_packet *const p) +{ + const transport_checksum checksum = calc_checksum(p); + const int res = cfg->write(&checksum, sizeof checksum, cfg->arg); + + if (res < 0) + return res; + else if (res != sizeof checksum) + return 1; + + p->common.done = true; + return 0; +} + +static int update_output(struct transport_handle *const h) +{ + for (size_t i = 0; i < h->n_packets; i++) + { + union transport_packet *const p = h->packets[i]; + struct transport_common *const c = &p->common; + + if (!c->ttl) + { + static int (*const f[])(const struct transport_cfg *, + union transport_packet *) = + { + [TRANSPORT_STATE_HEADER] = write_header, + [TRANSPORT_STATE_LEN] = write_len, + [TRANSPORT_STATE_BODY] = write_body, + [TRANSPORT_STATE_CHECKSUM] = write_checksum + }; + + while (!c->done) + { + const int res = f[c->state](&h->cfg, p); + + if (res < 0) + return res; + else if (res) + return 0; + } + + /* Arbitrary value, also depends on frame rate, yet good enough. */ + enum {TTL = 200}; + + c->ttl = TTL; + } + else + c->ttl--; + } + + return 0; +} + +int transport_update(struct transport_handle *const h) +{ + return update_input(h) || update_output(h); +} |
