juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

hal_sim.c (7635B)


      1 /* hal_sim.c - simulador de la PicoCalc en la PC (SDL3). El cable se simula con TCP.
      2  *
      3  *   <juego>-picosim [--scale N] [--link-listen PUERTO | --link-connect PUERTO] [--store ARCHIVO]
      4  *
      5  * Para probar el cable: una ventana con --link-listen 9999 y otra con --link-connect 9999. */
      6 #include <SDL3/SDL.h>
      7 #include <arpa/inet.h>
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <netinet/in.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <sys/socket.h>
     15 #include <time.h>
     16 #include <unistd.h>
     17 #include "hal.h"
     18 #include "app_config.h"
     19 
     20 static SDL_Window *win;
     21 static SDL_Renderer *ren;
     22 static SDL_Texture *tex;
     23 static uint16_t fb[320 * 320];
     24 static int scale = 2, link_port = 0, lfd = -1, cfd = -1;
     25 static bool link_listen;
     26 static char store_path[512];
     27 static const char *keys;
     28 bool hal_keys_ext;
     29 
     30 static void span(Surface *s, int x, int y, int n, Color c)
     31 {
     32     (void)s;
     33     uint16_t *p = fb + y * 320 + x;
     34     while (n--) *p++ = c;
     35 }
     36 
     37 void hal_init(void) {}
     38 
     39 uint32_t hal_ms(void)
     40 {
     41     struct timespec ts;
     42     clock_gettime(CLOCK_MONOTONIC, &ts);
     43     return (uint32_t)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
     44 }
     45 
     46 void hal_sleep(int ms) { SDL_Delay((Uint32)ms); }
     47 
     48 uint32_t hal_random(void)
     49 {
     50     struct timespec ts;
     51     clock_gettime(CLOCK_REALTIME, &ts);
     52     return (uint32_t)(ts.tv_nsec ^ ts.tv_sec ^ (getpid() << 16));
     53 }
     54 
     55 int hal_battery(void) { return -1; }
     56 void hal_log(const char *line) { fprintf(stderr, "%s\n", line); }
     57 void hal_log_clear(void) {}
     58 
     59 void hal_draw(void (*paint)(Surface *, void *), void *ctx)
     60 {
     61     Surface s;
     62     surf_init(&s, 320, 320, span, 0);
     63     /* igual que en la PicoCalc: por franjas, para probar el recorte */
     64     for (int y = 0; y < 320; y += 40) {
     65         gfx_band(&s, y, y + 40);
     66         paint(&s, ctx);
     67     }
     68     const char *shot = getenv("APP_SHOT");
     69     if (shot) {
     70         FILE *f = fopen(shot, "wb");
     71         if (f) {
     72             fprintf(f, "P6 320 320 255\n");
     73             for (int i = 0; i < 320 * 320; i++) {
     74                 fputc((fb[i] >> 11) << 3, f); fputc(((fb[i] >> 5) & 63) << 2, f); fputc((fb[i] & 31) << 3, f);
     75             }
     76             fclose(f);
     77         }
     78     }
     79     SDL_UpdateTexture(tex, NULL, fb, 640);
     80     SDL_RenderClear(ren);
     81     SDL_RenderTexture(ren, tex, NULL, NULL);
     82     SDL_RenderPresent(ren);
     83 }
     84 
     85 static int map_key(SDL_Keycode k)
     86 {
     87     switch (k) {
     88     case SDLK_UP: return K_UP;
     89     case SDLK_DOWN: return K_DOWN;
     90     case SDLK_LEFT: return K_LEFT;
     91     case SDLK_RIGHT: return K_RIGHT;
     92     case SDLK_RETURN: case SDLK_KP_ENTER: return K_OK;
     93     case SDLK_ESCAPE: return K_BACK;
     94     case SDLK_TAB: return K_TAB;
     95     case SDLK_BACKSPACE: return K_BKSP;
     96     case SDLK_DELETE: return hal_keys_ext ? K_DEL : K_BKSP;   /* como la PicoCalc */
     97     case SDLK_HOME: return hal_keys_ext ? K_HOME : 0;
     98     case SDLK_END: return hal_keys_ext ? K_END : 0;
     99     case SDLK_PAGEUP: return hal_keys_ext ? K_PGUP : 0;
    100     case SDLK_PAGEDOWN: return hal_keys_ext ? K_PGDN : 0;
    101     }
    102     if (hal_keys_ext && k >= SDLK_F1 && k <= SDLK_F10) return K_F1 + (int)(k - SDLK_F1);
    103     return 0;
    104 }
    105 
    106 int hal_key(void)
    107 {
    108     SDL_Event e;
    109     while (SDL_PollEvent(&e)) {
    110         if (e.type == SDL_EVENT_QUIT) exit(0);
    111         if (e.type == SDL_EVENT_KEY_DOWN) { int k = map_key(e.key.key); if (k) return k; }
    112         if (e.type == SDL_EVENT_TEXT_INPUT) { const char *t = e.text.text; int c = utf8_next(&t); if (c) return c; }
    113         if (e.type == SDL_EVENT_WINDOW_EXPOSED) return 0;   /* 0: solo fuerza redibujo */
    114     }
    115     if (keys && *keys) {           /* APP_KEYS: ver key_script en input.h */
    116         int k = key_script(&keys);
    117         if (k == KS_PAUSE) { SDL_Delay(300); return -1; }
    118         if (k == KS_QUIT) exit(0);
    119         return k;
    120     }
    121     return -1;
    122 }
    123 
    124 /* ------------------------------------------------------------ cable por TCP */
    125 
    126 static void link_setup(void)
    127 {
    128     if (!link_port) return;
    129     if (link_listen) {
    130         lfd = socket(AF_INET, SOCK_STREAM, 0);
    131         int one = 1;
    132         setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
    133         struct sockaddr_in a = { .sin_family = AF_INET, .sin_port = htons((uint16_t)link_port) };
    134         a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    135         if (bind(lfd, (struct sockaddr *)&a, sizeof a) < 0 || listen(lfd, 1) < 0) { perror("link"); exit(1); }
    136         fcntl(lfd, F_SETFL, O_NONBLOCK);
    137     }
    138 }
    139 
    140 static void link_connect(void)
    141 {
    142     if (cfd >= 0 || !link_port) return;
    143     if (link_listen) {
    144         cfd = accept(lfd, 0, 0);
    145     } else {
    146         static uint32_t last;
    147         if (hal_ms() - last < 1000) return;
    148         last = hal_ms();
    149         cfd = socket(AF_INET, SOCK_STREAM, 0);
    150         struct sockaddr_in a = { .sin_family = AF_INET, .sin_port = htons((uint16_t)link_port) };
    151         a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    152         if (connect(cfd, (struct sockaddr *)&a, sizeof a) < 0) { close(cfd); cfd = -1; return; }
    153     }
    154     if (cfd >= 0) fcntl(cfd, F_SETFL, O_NONBLOCK);
    155 }
    156 
    157 void hal_link_write(const char *buf, int n)
    158 {
    159     link_connect();
    160     if (cfd < 0) return;    /* como un cable desenchufado: se pierde */
    161     while (n > 0) {
    162         ssize_t w = send(cfd, buf, (size_t)n, MSG_NOSIGNAL);
    163         if (w < 0 && errno == EAGAIN) { SDL_Delay(1); continue; }
    164         if (w <= 0) { close(cfd); cfd = -1; return; }
    165         buf += w;
    166         n -= (int)w;
    167     }
    168 }
    169 
    170 int hal_link_read(char *buf, int max)
    171 {
    172     link_connect();
    173     if (cfd < 0 || max <= 0) return 0;
    174     ssize_t r = recv(cfd, buf, (size_t)max, 0);
    175     if (r == 0 || (r < 0 && errno != EAGAIN)) { close(cfd); cfd = -1; return 0; }
    176     return r < 0 ? 0 : (int)r;
    177 }
    178 
    179 /* ------------------------------------------------------------ guardado */
    180 
    181 bool hal_store_save(const char *data, int n)
    182 {
    183     FILE *f = fopen(store_path, "wb");
    184     if (!f) return false;
    185     fwrite(data, 1, (size_t)n, f);
    186     fclose(f);
    187     return true;
    188 }
    189 
    190 int hal_store_load(char *buf, int max)
    191 {
    192     FILE *f = fopen(store_path, "rb");
    193     if (!f) return 0;
    194     int n = (int)fread(buf, 1, (size_t)max, f);
    195     fclose(f);
    196     return n;
    197 }
    198 
    199 int main(int argc, char **argv)
    200 {
    201     const char *home = getenv("HOME");
    202     snprintf(store_path, sizeof store_path, "%s/.local/share/" APP_NAME "/picosim.sav", home ? home : ".");
    203     for (int i = 1; i < argc; i++) {
    204         if (!strcmp(argv[i], "--scale") && i + 1 < argc) scale = atoi(argv[++i]);
    205         else if (!strcmp(argv[i], "--link-listen") && i + 1 < argc) { link_port = atoi(argv[++i]); link_listen = true; }
    206         else if (!strcmp(argv[i], "--link-connect") && i + 1 < argc) link_port = atoi(argv[++i]);
    207         else if (!strcmp(argv[i], "--store") && i + 1 < argc) snprintf(store_path, sizeof store_path, "%s", argv[++i]);
    208         else if (!strcmp(argv[i], "--lang") && i + 1 < argc) g_lang = !strcmp(argv[++i], "en");
    209         else { fprintf(stderr, "uso: %s [--scale N] [--link-listen P | --link-connect P] [--store F] [--lang es|en]\n", argv[0]); return 1; }
    210     }
    211     if (scale < 1) scale = 1;
    212     keys = getenv("APP_KEYS");
    213     if (!SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "SDL: %s\n", SDL_GetError()); return 1; }
    214     char title[64];
    215     snprintf(title, sizeof title, "PicoCalc " APP_TITLE "%s", link_port ? (link_listen ? " (A)" : " (B)") : "");
    216     if (!SDL_CreateWindowAndRenderer(title, 320 * scale, 320 * scale, SDL_WINDOW_RESIZABLE, &win, &ren)) return 1;
    217     SDL_SetRenderLogicalPresentation(ren, 320, 320, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE);
    218     tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, 320, 320);
    219     SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
    220     SDL_StartTextInput(win);
    221     link_setup();
    222     app_main();
    223     return 0;
    224 }