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

paths.c (3939B)


      1 /* paths.c - rutas XDG de la app y reloj. En Windows todo va a %APPDATA%\APP_NAME
      2  * (salvo que XDG_* esten definidas). En la web (emscripten) todo va a /data/APP_NAME,
      3  * un IDBFS que persiste en IndexedDB (lo monta web/pre.js antes de main). */
      4 #include <dirent.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <sys/stat.h>
      9 #include <time.h>
     10 #include "plat.h"
     11 #include "paths.h"
     12 #include "app_config.h"
     13 
     14 #ifdef __EMSCRIPTEN__
     15 #include <emscripten.h>
     16 #endif
     17 
     18 #ifdef _WIN32
     19 long long now_ms(void) { return (long long)GetTickCount64(); }
     20 
     21 /* en Windows datos y config van juntos en %APPDATA% */
     22 static const char *home_dir(const char *posix_sub, char *buf, size_t n)
     23 {
     24     (void)posix_sub;
     25     const char *a = getenv("APPDATA");
     26     snprintf(buf, n, "%s", a && *a ? a : ".");
     27     return buf;
     28 }
     29 #else
     30 #ifndef __EMSCRIPTEN__
     31 static const char *home_dir(const char *posix_sub, char *buf, size_t n)
     32 {
     33     const char *h = getenv("HOME");
     34     snprintf(buf, n, "%s/%s", h ? h : ".", posix_sub);
     35     return buf;
     36 }
     37 #endif
     38 
     39 long long now_ms(void)
     40 {
     41     struct timespec ts;
     42     clock_gettime(CLOCK_MONOTONIC, &ts);
     43     return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
     44 }
     45 #endif
     46 
     47 void mkdirs(const char *path)
     48 {
     49     char tmp[512];
     50     snprintf(tmp, sizeof tmp, "%s", path);
     51     for (char *p = tmp + 1; *p; p++)
     52         if (*p == '/' || *p == '\\') { char c = *p; *p = 0; mkdir(tmp, 0755); *p = c; }
     53     mkdir(tmp, 0755);
     54 }
     55 
     56 const char *data_dir(void)
     57 {
     58     static char dir[512];
     59     if (!dir[0]) {
     60 #ifdef __EMSCRIPTEN__
     61         snprintf(dir, sizeof dir, "/data/" APP_NAME);
     62 #else
     63         const char *x = getenv("XDG_DATA_HOME");
     64         char base[400];
     65         snprintf(dir, sizeof dir, "%s/" APP_NAME, x && *x ? x : home_dir(".local/share", base, sizeof base));
     66 #endif
     67         char saves[600];
     68         snprintf(saves, sizeof saves, "%s/saves", dir);
     69         mkdirs(saves);
     70     }
     71     return dir;
     72 }
     73 
     74 const char *config_path(void)
     75 {
     76     static char path[512];
     77     if (!path[0]) {
     78 #ifdef __EMSCRIPTEN__
     79         snprintf(path, sizeof path, "%s/settings", data_dir());
     80 #else
     81         const char *x = getenv("XDG_CONFIG_HOME");
     82         char dir[480], base[400];
     83         snprintf(dir, sizeof dir, "%s/" APP_NAME, x && *x ? x : home_dir(".config", base, sizeof base));
     84         mkdirs(dir);
     85         snprintf(path, sizeof path, "%s/settings", dir);
     86 #endif
     87     }
     88     return path;
     89 }
     90 
     91 void paths_flush(void)
     92 {
     93 #ifdef __EMSCRIPTEN__
     94     /* FS.syncfs es asincronico: se espera (cediendo al navegador) hasta que termine,
     95      * con un tope por si IndexedDB no responde */
     96     if (EM_ASM_INT({ return Module.noPersist ? 1 : 0; })) return;      /* la carga fallo: no pisar IndexedDB */
     97     int id = EM_ASM_INT({
     98         var id = Module.fsSeq = (Module.fsSeq || 0) + 1;
     99         Module.fsDone = Module.fsDone || {};
    100         FS.syncfs(false, function(err) { Module.fsDone[id] = true; if (err) console.warn('syncfs', err); });
    101         return id;
    102     });
    103     /* se espera solo a este sync (si uno viejo nunca volvio, no bloquea a los demas) */
    104     for (int i = 0; i < 200 && !EM_ASM_INT({ return Module.fsDone[$0] ? 1 : 0; }, id); i++) emscripten_sleep(10);
    105     EM_ASM({ delete Module.fsDone[$0]; }, id);
    106 #endif
    107 }
    108 
    109 bool latest_file(const char *ext, char *out, int n)
    110 {
    111     char dir[600];
    112     snprintf(dir, sizeof dir, "%s/saves", data_dir());
    113     DIR *d = opendir(dir);
    114     if (!d) return false;
    115     struct dirent *e;
    116     time_t best = 0;
    117     bool found = false;
    118     size_t el = strlen(ext);
    119     while ((e = readdir(d))) {
    120         size_t l = strlen(e->d_name);
    121         if (l < el + 1 || strcmp(e->d_name + l - el, ext)) continue;
    122         char p[900];
    123         snprintf(p, sizeof p, "%s/%s", dir, e->d_name);
    124         struct stat st;
    125         if (stat(p, &st) == 0 && st.st_mtime >= best) {
    126             best = st.st_mtime;
    127             snprintf(out, (size_t)n, "%s", p);
    128             found = true;
    129         }
    130     }
    131     closedir(d);
    132     return found;
    133 }