/* * hcsdl12.c: the part of the SDL 1.2 API that Holotz's Castle and its JLib use, on top of SDL2, for * the fredygames.com browser build (Emscripten). See hcsdl12.h. * * The game draws into a 16-bit (RGB565) SDL_Surface, the "screen" of SDL_SetVideoMode and the game's * default depth. Each SDL_Flip or SDL_UpdateRect uploads it to a streaming texture and presents it, * then gives the browser a turn (the build uses Asyncify). 16 bits also keep the color key exact: a * 32-bit surface without alpha may get 0xFF in its unused byte when SDL2 converts it, and then black * (key 0) is no longer transparent. * * Copyright (C) 2026 FredyGames. GNU GPL version 2 or later, like Holotz's Castle. */ #include "hcsdl12.h" #include #ifdef __EMSCRIPTEN__ #include #endif #undef SDL_PollEvent #undef SDL_CreateThread #undef SDL_CreateRGBSurface #undef unicode static SDL_Window *window = NULL; static SDL_Renderer *renderer = NULL; static SDL_Texture *texture = NULL; static SDL_Surface *screen = NULL; static Uint8 keystate[1024]; /* folds an SDL2 key symbol to the index the game uses (see hcsdl12.h) */ static int fold_key (SDL_Keycode sym, SDL_Scancode scancode) { if (sym >= 0 && sym < 256) return sym; return 256 + (int) scancode; } const SDL_VideoInfo * SDL_GetVideoInfo (void) { static SDL_VideoInfo info; static SDL_PixelFormat *format = NULL; if (format == NULL) format = SDL_AllocFormat (SDL_PIXELFORMAT_RGB565); info.vfmt = format; info.current_w = screen != NULL ? screen->w : 0; info.current_h = screen != NULL ? screen->h : 0; return &info; } SDL_Rect ** SDL_ListModes (SDL_PixelFormat * format, Uint32 flags) { (void) format; (void) flags; return (SDL_Rect **) - 1; } int SDL_VideoModeOK (int width, int height, int bpp, Uint32 flags) { (void) width; (void) height; (void) bpp; (void) flags; return 16; } SDL_Surface * SDL_SetVideoMode (int width, int height, int bpp, Uint32 flags) { (void) bpp; (void) flags; if (window == NULL) { window = SDL_CreateWindow ("Holotz's Castle", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 0); if (window == NULL) return NULL; renderer = SDL_CreateRenderer (window, -1, 0); if (renderer == NULL) return NULL; } else SDL_SetWindowSize (window, width, height); if (texture != NULL) SDL_DestroyTexture (texture); if (screen != NULL) { screen->refcount = 1; SDL_FreeSurface (screen); } texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, width, height); screen = SDL_CreateRGBSurfaceWithFormat (0, width, height, 16, SDL_PIXELFORMAT_RGB565); /* in SDL 1.2, SDL_FreeSurface on the screen does nothing, and the game relies on that */ if (screen != NULL) screen->refcount = 0x40000000; return screen; } SDL_Surface * SDL_GetVideoSurface (void) { return screen; } int SDL_Flip (SDL_Surface * surface) { if (surface != screen || texture == NULL) return -1; SDL_UpdateTexture (texture, NULL, screen->pixels, screen->pitch); SDL_RenderClear (renderer); SDL_RenderCopy (renderer, texture, NULL, NULL); SDL_RenderPresent (renderer); #ifdef __EMSCRIPTEN__ emscripten_sleep (0); #endif return 0; } void SDL_UpdateRect (SDL_Surface * surface, Sint32 x, Sint32 y, Uint32 w, Uint32 h) { (void) x; (void) y; (void) w; (void) h; SDL_Flip (surface); } void SDL_UpdateRects (SDL_Surface * surface, int numrects, SDL_Rect * rects) { (void) numrects; (void) rects; SDL_Flip (surface); } void SDL_WM_SetCaption (const char *title, const char *icon) { (void) icon; if (window != NULL && title != NULL) SDL_SetWindowTitle (window, title); } void SDL_WM_SetIcon (SDL_Surface * icon, Uint8 * mask) { /* the page has its own icon */ (void) icon; (void) mask; } int SDL_EnableKeyRepeat (int delay, int interval) { /* the browser repeats keys by itself */ (void) delay; (void) interval; return 0; } int SDL_EnableUNICODE (int enable) { (void) enable; return 1; } Uint8 * SDL_GetKeyState (int *numkeys) { if (numkeys != NULL) *numkeys = (int) sizeof (keystate); return keystate; } int SDL_SetAlpha (SDL_Surface * surface, Uint32 flags, Uint8 alpha) { if (surface == NULL) return -1; if (flags & SDL_SRCALPHA) { SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_BLEND); SDL_SetSurfaceAlphaMod (surface, alpha); } else { SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_NONE); SDL_SetSurfaceAlphaMod (surface, 255); } return 0; } SDL_Surface * SDL_DisplayFormat (SDL_Surface * surface) { /* like SDL 1.2: the screen's format, without per-pixel alpha */ if (surface == NULL || screen == NULL) return NULL; return SDL_ConvertSurface (surface, screen->format, 0); } SDL_Surface * SDL_DisplayFormatAlpha (SDL_Surface * surface) { SDL_Surface *converted; if (surface == NULL) return NULL; converted = SDL_ConvertSurfaceFormat (surface, SDL_PIXELFORMAT_ARGB8888, 0); if (converted != NULL) SDL_SetSurfaceBlendMode (converted, SDL_BLENDMODE_BLEND); return converted; } void SDL_KillThread (SDL_Thread * thread) { (void) thread; } SDL_Thread * hc_CreateThread (int (*fn) (void *), void *data) { return SDL_CreateThread (fn, "JThread", data); } SDL_Surface * hc_CreateRGBSurface (Uint32 flags, int width, int height, int depth, Uint32 rmask, Uint32 gmask, Uint32 bmask, Uint32 amask) { SDL_Surface *surface; (void) flags; surface = SDL_CreateRGBSurface (0, width, height, depth, rmask, gmask, bmask, amask); /* the depth that fits the masks (32-bit masks with depth 16, or the other way round) */ if (surface == NULL && depth != 32) surface = SDL_CreateRGBSurface (0, width, height, 32, rmask, gmask, bmask, amask); if (surface == NULL && depth != 16) surface = SDL_CreateRGBSurface (0, width, height, 16, rmask, gmask, bmask, amask); return surface; } Uint32 hc_ColorKey (SDL_Surface * surface) { Uint32 key = 0; if (surface != NULL) SDL_GetColorKey (surface, &key); return key; } Uint8 hc_SurfaceAlpha (SDL_Surface * surface) { Uint8 alpha = 255; if (surface != NULL) SDL_GetSurfaceAlphaMod (surface, &alpha); return alpha; } /* the character a key types, as SDL 1.2 put it in keysym.unicode (letters, digits and signs; shift * gives capitals) */ static Uint32 key_character (const SDL_Keysym * keysym) { SDL_Keycode sym = keysym->sym; int shift = (keysym->mod & KMOD_SHIFT) != 0; if (sym >= SDLK_a && sym <= SDLK_z) return shift ? (Uint32) (sym - 'a' + 'A') : (Uint32) sym; if (sym == SDLK_BACKSPACE || sym == SDLK_RETURN || sym == SDLK_TAB || (sym >= 32 && sym < 127)) return (Uint32) sym; return 0; } int hc_PollEvent (SDL_Event * event) { int got = SDL_PollEvent (event); if (got && event != NULL && (event->type == SDL_KEYDOWN || event->type == SDL_KEYUP)) { int index = fold_key (event->key.keysym.sym, event->key.keysym.scancode); if (index >= 0 && index < (int) sizeof (keystate)) keystate[index] = event->type == SDL_KEYDOWN ? SDL_PRESSED : SDL_RELEASED; event->key.keysym.unused = key_character (&event->key.keysym); event->key.keysym.sym = index; } return got; }