diff --git a/config.h b/config.h new file mode 100644 index 0000000..f9d7000 --- /dev/null +++ b/config.h @@ -0,0 +1,27 @@ +#define HAVE_ATEXIT 1 +#define HAVE_ERRNO_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LIBSDL 1 +#define HAVE_MALLOC 1 +#define HAVE_MEMORY_H 1 +#define HAVE_MEMSET 1 +#define HAVE_RAND 1 +#define HAVE_STDINT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_STRING_H 1 +#define HAVE_STRRCHR 1 +#define HAVE_SYS_IOCTL_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_SYS_TIME_H 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_UNISTD_H 1 +#define PACKAGE "xsoldier" +#define PACKAGE_NAME "xsoldier" +#define PACKAGE_STRING "xsoldier 1.8" +#define PACKAGE_TARNAME "xsoldier" +#define PACKAGE_VERSION "1.8" +#define STDC_HEADERS 1 +#define VERSION "1.8" +#define X_DISPLAY_MISSING 1 diff --git a/ending.c b/ending.c index 615d035..b730e44 100644 --- a/ending.c +++ b/ending.c @@ -72,9 +72,13 @@ void Ending(void) while (1) { +#ifdef __EMSCRIPTEN__ + fredy_frame_wait(); +#else if (waittime && (signal_delivered==0)) pause(); signal_delivered = 0; +#endif if (count < time) { diff --git a/extern.h b/extern.h index 3102a88..8fc871a 100644 --- a/extern.h +++ b/extern.h @@ -27,6 +27,12 @@ EXTERN int signal_delivered; EXTERN int waittime; +#ifdef __EMSCRIPTEN__ +/* fredy.c */ +extern void fredy_frame_wait(void); +extern SDL_Surface *fredy_load_xpm(const char *filename); +#endif + #ifdef HAVE_LIBSDL EXTERN SDL_Surface *dpy; EXTERN SDL_Event event; diff --git a/fredy-x11/X11/Xlib.h b/fredy-x11/X11/Xlib.h new file mode 100644 index 0000000..61155cb --- /dev/null +++ b/fredy-x11/X11/Xlib.h @@ -0,0 +1 @@ +/* Empty stand-in: the SDL build does not use X11 (see fredy.c). */ diff --git a/fredy-x11/X11/Xutil.h b/fredy-x11/X11/Xutil.h new file mode 100644 index 0000000..61155cb --- /dev/null +++ b/fredy-x11/X11/Xutil.h @@ -0,0 +1 @@ +/* Empty stand-in: the SDL build does not use X11 (see fredy.c). */ diff --git a/fredy-x11/X11/keysym.h b/fredy-x11/X11/keysym.h new file mode 100644 index 0000000..61155cb --- /dev/null +++ b/fredy-x11/X11/keysym.h @@ -0,0 +1 @@ +/* Empty stand-in: the SDL build does not use X11 (see fredy.c). */ diff --git a/fredy-x11/X11/xpm.h b/fredy-x11/X11/xpm.h new file mode 100644 index 0000000..61155cb --- /dev/null +++ b/fredy-x11/X11/xpm.h @@ -0,0 +1 @@ +/* Empty stand-in: the SDL build does not use X11 (see fredy.c). */ diff --git a/fredy.c b/fredy.c new file mode 100644 index 0000000..64ff922 --- /dev/null +++ b/fredy.c @@ -0,0 +1,163 @@ +/* fredy.c: what xsoldier needs to run in a browser (Emscripten), for fredygames.com. + * + * - fredy_load_xpm(): reads the game's XPM sprites into SDL surfaces. The game normally uses SDL_image for + * this; Emscripten's SDL_image cannot read XPM, so this reads the format the sprites use (one character + * per color, "#RRRRGGGGBBBB" or "None") itself. Transparent ("None") pixels and black pixels, which the + * game treats as transparent through a color key that Emscripten's SDL leaves out, get alpha 0. + * - fredy_frame_wait(): the game waits for the next frame with a timer signal and pause(), which a browser + * does not have. Here the wait is a sleep until 35 ms after the last frame (the game's own WAIT), done with + * Asyncify so the browser keeps drawing and reading the keyboard. + * + * Copyright (C) 2026 FredyGames. Distributed under the GNU General Public License, version 2 or (at your + * option) any later version, like xsoldier. + */ +#include + +#include +#include +#include + +#include +#include + +#include "xsoldier.h" + +extern int waittime; /* extern.h */ + +static double next_frame = 0; + +void fredy_frame_wait(void) +{ + double now = emscripten_get_now(); + double step = (waittime > 0 ? waittime : WAIT) / 1000.0; + + if (next_frame == 0 || now - next_frame > 250) + next_frame = now; + next_frame += step; + if (next_frame > now) + emscripten_sleep((unsigned int)(next_frame - now + 0.5)); + else + emscripten_sleep(0); +} + +static int hex(int c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} + +typedef struct { char key; Uint32 argb; } XpmColor; + +SDL_Surface *fredy_load_xpm(const char *filename) +{ + FILE *fp = fopen(filename, "rb"); + long size; + char *buf; + char *p, *end; + int w, h, ncolors, cpp, i, x, y; + XpmColor *colors; + SDL_Surface *surf; + Uint32 *pix; + + if (fp == NULL) + return NULL; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + buf = (char *)malloc(size + 1); + if (buf == NULL || fread(buf, 1, size, fp) != (size_t)size) + { + fclose(fp); + free(buf); + return NULL; + } + fclose(fp); + buf[size] = '\0'; + end = buf + size; + + /* the strings of the C array: the first is the header, then the colors, then one string per row */ +#define NEXT_STRING(dst) \ + do { \ + while (p < end && *p != '"') p++; \ + if (p >= end) goto fail; \ + (dst) = ++p; \ + while (p < end && *p != '"') p++; \ + if (p >= end) goto fail; \ + *p++ = '\0'; \ + } while (0) + + p = strstr(buf, "static"); + if (p == NULL) + goto fail; + { + char *s; + NEXT_STRING(s); + if (sscanf(s, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4 || cpp != 1 || w <= 0 || h <= 0 || ncolors <= 0) + goto fail; + } + colors = (XpmColor *)calloc(ncolors, sizeof(XpmColor)); + for (i = 0; i < ncolors; i++) + { + char *s, *c; + NEXT_STRING(s); + colors[i].key = s[0]; + c = strstr(s + 1, " c "); + if (c == NULL) + c = strstr(s + 1, "\tc "); + if (c == NULL) + goto fail2; + c += 3; + if (*c == '#') + { + int n = (int)strlen(c + 1), r, g, b; + if (n == 12) + { + r = hex(c[1]) * 16 + hex(c[2]); + g = hex(c[5]) * 16 + hex(c[6]); + b = hex(c[9]) * 16 + hex(c[10]); + } + else if (n == 6) + { + r = hex(c[1]) * 16 + hex(c[2]); + g = hex(c[3]) * 16 + hex(c[4]); + b = hex(c[5]) * 16 + hex(c[6]); + } + else + goto fail2; + /* black is the game's color key */ + colors[i].argb = (r == 0 && g == 0 && b == 0) ? 0 : (0xff000000u | (r << 16) | (g << 8) | b); + } + else + colors[i].argb = 0; /* None */ + } + + surf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + if (surf == NULL) + goto fail2; + SDL_LockSurface(surf); + pix = (Uint32 *)surf->pixels; + for (y = 0; y < h; y++) + { + char *row; + NEXT_STRING(row); + for (x = 0; x < w; x++) + { + Uint32 v = 0; + for (i = 0; i < ncolors; i++) + if (colors[i].key == row[x]) { v = colors[i].argb; break; } + pix[y * (surf->pitch / 4) + x] = v; + } + } + SDL_UnlockSurface(surf); + free(colors); + free(buf); + return surf; + +fail2: + free(colors); +fail: + free(buf); + return NULL; +} diff --git a/game.c b/game.c index 3481bcf..bf34554 100644 --- a/game.c +++ b/game.c @@ -99,9 +99,13 @@ int mainLoop(void) #endif /* not HAVE_LIBSDL */ while (1) { +#ifdef __EMSCRIPTEN__ + fredy_frame_wait(); +#else if (waittime && (signal_delivered==0)) pause(); signal_delivered = 0; +#endif if (event_handle() == 0) return 0; diff --git a/image.c b/image.c index a783573..795c173 100644 --- a/image.c +++ b/image.c @@ -45,7 +45,11 @@ void ReadFileToImage(const char *filename, Image **img) { #ifdef HAVE_LIBSDL SDL_Surface *temp; +#ifdef __EMSCRIPTEN__ + temp = fredy_load_xpm(filename); +#else temp = IMG_Load(filename); +#endif if (temp == NULL) { fprintf(stderr, "ReadFileToImage: IMG_Load(%s) failed (%s)\n", diff --git a/input.c b/input.c index b5744f1..4394163 100644 --- a/input.c +++ b/input.c @@ -42,7 +42,18 @@ int input_init(void) initJS(); #endif /* JSTK */ -#ifdef HAVE_LIBSDL +#ifdef __EMSCRIPTEN__ + /* the SDL 1.2 of the browser has no key names */ + upKey = "Up"; + downKey = "Down"; + leftKey = "Left"; + rightKey = "Right"; + shotKey = "Left Shift"; + spdupKey = "a"; + spdwnKey = "s"; + pauseKey = "p"; + quitKey = "q"; +#elif defined(HAVE_LIBSDL) upKey = SDL_GetKeyName(SDLK_UP); downKey = SDL_GetKeyName(SDLK_DOWN); leftKey = SDL_GetKeyName(SDLK_LEFT); diff --git a/main.c b/main.c index e5cdc1e..3a14f16 100644 --- a/main.c +++ b/main.c @@ -392,6 +392,7 @@ static void init(void) else waittime = w_time; +#ifndef __EMSCRIPTEN__ memset(&sig_act, 0, sizeof(sig_act)); sig_act.sa_handler = sig_handle; sigaction(SIGALRM, &sig_act, 0); @@ -400,6 +401,7 @@ static void init(void) value.it_value.tv_sec = 0; value.it_value.tv_usec = waittime; setitimer(ITIMER_REAL, &value, &ovalue); +#endif /* not __EMSCRIPTEN__ */ FieldW = 500; FieldH = 650; diff --git a/manage.c b/manage.c index 4e4008f..e3c07e4 100644 --- a/manage.c +++ b/manage.c @@ -372,6 +372,11 @@ PlayerData *NewPlayerData(void) /* we don't free() the memory allocated by getpwuid() because * it is allocated statically */ +#ifdef __EMSCRIPTEN__ + /* the browser has no user names */ + pw = NULL; + snprintf(name, sizeof(name) - 1, "player"); +#else if ((pw=getpwuid(getuid())) == NULL) snprintf(name, sizeof(name) - 1, "%d",getuid()); else @@ -391,6 +396,7 @@ PlayerData *NewPlayerData(void) } +#endif /* not __EMSCRIPTEN__ */ New = (PlayerData *)malloc(sizeof(PlayerData)); diff --git a/opening.c b/opening.c index f881983..0163174 100644 --- a/opening.c +++ b/opening.c @@ -44,9 +44,13 @@ int Opening(void) while(1) { +#ifdef __EMSCRIPTEN__ + fredy_frame_wait(); +#else if (waittime && (signal_delivered==0)) pause(); signal_delivered = 0; +#endif if (count < 500) count++;