/* * st2sdl12.c: SDL 1.2 calls of Super Transball 2 (and the three SGE functions it uses) on top of SDL2, * for the fredygames.com browser build (Emscripten). See st2sdl12.h and sge.h. * * Video: the game draws into a 32-bit "screen" surface; SDL_Flip copies it to a streaming texture and * presents it on the page's canvas (the page scales the canvas to fit, keeping the proportion). * SDL_Delay becomes emscripten_sleep, so the game's main loop hands control back to the browser * (ASYNCIFY) and the page keeps responding. * * Copyright (C) 2026 FredyGames. GNU GPL version 2 or later, like Super Transball 2. */ #include #include #include #include #include "st2sdl12.h" #include "sge.h" static SDL_Window *window; static SDL_Renderer *renderer; static SDL_Texture *texture; static SDL_Surface *screen; SDL_Surface *SDL_SetVideoMode (int width, int height, int bpp, Uint32 flags) { (void) bpp; (void) flags; if (screen) SDL_FreeSurface (screen); if (texture) SDL_DestroyTexture (texture); if (!window) { window = SDL_CreateWindow ("Super Transball 2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0); if (!window) return NULL; renderer = SDL_CreateRenderer (window, -1, 0); if (!renderer) return NULL; } else SDL_SetWindowSize (window, width, height); SDL_RenderSetLogicalSize (renderer, width, height); texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, width, height); screen = SDL_CreateRGBSurfaceWithFormat (0, width, height, 32, SDL_PIXELFORMAT_RGB888); return screen; } int SDL_Flip (SDL_Surface *s) { if (!texture || s != screen) return -1; SDL_UpdateTexture (texture, NULL, s->pixels, s->pitch); SDL_RenderClear (renderer); SDL_RenderCopy (renderer, texture, NULL, NULL); SDL_RenderPresent (renderer); return 0; } void SDL_WM_SetCaption (const char *title, const char *icon) { (void) title; (void) icon; } int SDL_EnableUNICODE (int enable) { (void) enable; return 0; } void st2_Delay (Uint32 ms) { emscripten_sleep (ms); } int SDL_SetAlpha (SDL_Surface *surface, Uint32 flags, Uint8 alpha) { if (flags & SDL_SRCALPHA) { SDL_SetSurfaceAlphaMod (surface, alpha); return SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_BLEND); } SDL_SetSurfaceAlphaMod (surface, 255); return SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_NONE); } SDL_Surface *SDL_DisplayFormat (SDL_Surface *surface) { return SDL_ConvertSurfaceFormat (surface, SDL_PIXELFORMAT_RGB888, 0); } SDL_Surface *SDL_DisplayFormatAlpha (SDL_Surface *surface) { return SDL_ConvertSurfaceFormat (surface, SDL_PIXELFORMAT_ARGB8888, 0); } /* ---- keys ---- */ /* SDL2 scancode -> SDL 1.2 key symbol, for the keys whose SDL2 keycode is not their SDL 1.2 symbol */ static int scancode_to_sdl12 (SDL_Scancode sc) { if (sc >= SDL_SCANCODE_F1 && sc <= SDL_SCANCODE_F12) return 282 + (sc - SDL_SCANCODE_F1); if (sc >= SDL_SCANCODE_KP_1 && sc <= SDL_SCANCODE_KP_9) return 257 + (sc - SDL_SCANCODE_KP_1); switch (sc) { case SDL_SCANCODE_KP_0: return 256; case SDL_SCANCODE_KP_PERIOD: return 266; case SDL_SCANCODE_KP_DIVIDE: return 267; case SDL_SCANCODE_KP_MULTIPLY: return 268; case SDL_SCANCODE_KP_MINUS: return 269; case SDL_SCANCODE_KP_PLUS: return 270; case SDL_SCANCODE_KP_ENTER: return 271; case SDL_SCANCODE_KP_EQUALS: return 272; case SDL_SCANCODE_UP: return 273; case SDL_SCANCODE_DOWN: return 274; case SDL_SCANCODE_RIGHT: return 275; case SDL_SCANCODE_LEFT: return 276; case SDL_SCANCODE_INSERT: return 277; case SDL_SCANCODE_HOME: return 278; case SDL_SCANCODE_END: return 279; case SDL_SCANCODE_PAGEUP: return 280; case SDL_SCANCODE_PAGEDOWN: return 281; case SDL_SCANCODE_NUMLOCKCLEAR: return 300; case SDL_SCANCODE_CAPSLOCK: return 301; case SDL_SCANCODE_SCROLLLOCK: return 302; case SDL_SCANCODE_RSHIFT: return 303; case SDL_SCANCODE_LSHIFT: return 304; case SDL_SCANCODE_RCTRL: return 305; case SDL_SCANCODE_LCTRL: return 306; case SDL_SCANCODE_RALT: return 307; case SDL_SCANCODE_LALT: return 308; case SDL_SCANCODE_RGUI: return 309; case SDL_SCANCODE_LGUI: return 310; default: return 0; } } static int to_sdl12 (SDL_Scancode sc) { SDL_Keycode k = SDL_GetKeyFromScancode (sc); if (k > 0 && k < 256) return k; return scancode_to_sdl12 (sc); } Uint8 *SDL_GetKeyState (int *numkeys) { static Uint8 state[SDLK_LAST]; int n, i; const Uint8 *keys = SDL_GetKeyboardState (&n); memset (state, 0, sizeof state); for (i = 0; i < n; i++) if (keys[i]) { int k = to_sdl12 ((SDL_Scancode) i); if (k > 0 && k < SDLK_LAST) state[k] = 1; } if (numkeys) *numkeys = SDLK_LAST; return state; } const char *st2_GetKeyName (SDLKey key) { static char name[32]; const char *s = NULL; int i; if (key > 0 && key < 256) s = SDL_GetKeyName ((SDL_Keycode) key); else for (i = 0; i < SDL_NUM_SCANCODES && !s; i++) if (scancode_to_sdl12 ((SDL_Scancode) i) == key) s = SDL_GetKeyName (SDL_GetKeyFromScancode ((SDL_Scancode) i)); if (!s || !*s) s = "unknown key"; /* SDL 1.2 names were lower case ("space", "left"); the game prints them as they come */ for (i = 0; s[i] && i < (int) sizeof name - 1; i++) name[i] = (char) SDL_tolower ((unsigned char) s[i]); name[i] = 0; return name; } #undef SDL_PollEvent int st2_PollEvent (SDL_Event *event) { int r = SDL_PollEvent (event); if (r && (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP)) event->key.keysym.sym = to_sdl12 (event->key.keysym.scancode); return r; } /* ---- SGE ---- */ static Uint32 get_pixel (SDL_Surface *s, int x, int y) { Uint8 *p = (Uint8 *) s->pixels + y * s->pitch + x * s->format->BytesPerPixel; switch (s->format->BytesPerPixel) { case 1: return *p; case 2: return *(Uint16 *) p; case 3: return p[0] | p[1] << 8 | p[2] << 16; default: return *(Uint32 *) p; } } static void put_pixel (SDL_Surface *s, int x, int y, Uint32 v) { Uint8 *p = (Uint8 *) s->pixels + y * s->pitch + x * s->format->BytesPerPixel; switch (s->format->BytesPerPixel) { case 1: *p = (Uint8) v; break; case 2: *(Uint16 *) p = (Uint16) v; break; case 3: p[0] = v & 255; p[1] = (v >> 8) & 255; p[2] = (v >> 16) & 255; break; default: *(Uint32 *) p = v; break; } } /* Every pixel of dst (within the clip rectangle) whose inverse image falls inside src gets that src pixel: dst point d maps to src point p + R(-angle) * (d - q) / scale, with the angle in degrees. */ SDL_Rect sge_transform (SDL_Surface *src, SDL_Surface *dst, float angle, float xscale, float yscale, Uint16 px, Uint16 py, Uint16 qx, Uint16 qy, Uint8 flags) { SDL_Rect r = { 0, 0, 0, 0 }, sc, dc; double t = angle * M_PI / 180.0, s = sin (t), c = cos (t); int x, y, same; (void) flags; if (xscale == 0 || yscale == 0) return r; SDL_GetClipRect (src, &sc); SDL_GetClipRect (dst, &dc); same = src->format->format == dst->format->format; SDL_LockSurface (src); SDL_LockSurface (dst); for (y = dc.y; y < dc.y + dc.h; y++) for (x = dc.x; x < dc.x + dc.w; x++) { double dx = x - qx, dy = y - qy; int rx = (int) floor ((c * dx + s * dy) / xscale + px); int ry = (int) floor ((c * dy - s * dx) / yscale + py); if (rx >= sc.x && rx < sc.x + sc.w && ry >= sc.y && ry < sc.y + sc.h) { Uint32 v = get_pixel (src, rx, ry); if (!same) { Uint8 cr, cg, cb, ca; SDL_GetRGBA (v, src->format, &cr, &cg, &cb, &ca); v = SDL_MapRGBA (dst->format, cr, cg, cb, ca); } put_pixel (dst, x, y, v); } } SDL_UnlockSurface (dst); SDL_UnlockSurface (src); r.x = dc.x; r.y = dc.y; r.w = dc.w; r.h = dc.h; return r; } sge_cdata *sge_make_cmap (SDL_Surface *img) { sge_cdata *cd = (sge_cdata *) malloc (sizeof *cd); Uint32 key = 0; int x, y; if (!cd) return NULL; cd->w = img->w; cd->h = img->h; cd->map = (Uint8 *) calloc ((size_t) img->w * img->h + 1, 1); if (SDL_GetColorKey (img, &key) != 0) key = 0; SDL_LockSurface (img); for (y = 0; y < img->h; y++) for (x = 0; x < img->w; x++) cd->map[y * img->w + x] = get_pixel (img, x, y) != key; SDL_UnlockSurface (img); return cd; } int sge_cmcheck (sge_cdata *cd1, Sint16 x1, Sint16 y1, sge_cdata *cd2, Sint16 x2, Sint16 y2) { int x0 = x1 > x2 ? x1 : x2, y0 = y1 > y2 ? y1 : y2; int xe = x1 + cd1->w < x2 + cd2->w ? x1 + cd1->w : x2 + cd2->w; int ye = y1 + cd1->h < y2 + cd2->h ? y1 + cd1->h : y2 + cd2->h; int x, y; if (!cd1 || !cd2 || !cd1->map || !cd2->map) return 0; for (y = y0; y < ye; y++) for (x = x0; x < xe; x++) if (cd1->map[(y - y1) * cd1->w + (x - x1)] && cd2->map[(y - y2) * cd2->w + (x - x2)]) return 1; return 0; } void sge_destroy_cmap (sge_cdata *cd) { if (!cd) return; free (cd->map); free (cd); }