This commit is contained in:
Clinv committed 2026-10-24 12:01:52 -03:00
1 parent 7f7fcd545d
commit 4d9a339421
114 files changed
+11383 -1837

No files matched your search

+195
View File
@@ -0,0 +1,195 @@
/* Adaptación a Windows (Win32).
*
* ESTADO: escrito, NO compilado ni probado (el entorno de desarrollo era Linux y no había
* compilador para Windows). Hay que compilarlo y pasar las pruebas en Windows antes de
* distribuirlo. Ver docs/STATUS.md.
*
* Todas las rutas de la API pública son UTF-8; aquí se convierten a UTF-16 para las funciones W. */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "../../core/internal.h"
#include "../platform.h"
static wchar_t *to_wide(const char *s) {
int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
if (n <= 0) return NULL;
wchar_t *w = malloc((size_t)n * sizeof *w);
if (w && MultiByteToWideChar(CP_UTF8, 0, s, -1, w, n) <= 0) { free(w); return NULL; }
return w;
}
static bool from_wide(const wchar_t *w, char *out, size_t cap) {
return WideCharToMultiByte(CP_UTF8, 0, w, -1, out, (int)cap, NULL, NULL) > 0;
}
static void slashes_to_backslash(wchar_t *w) { for (; *w; w++) if (*w == L'/') *w = L'\\'; }
tgb_status tgb_plat_default_dirs(char *config, char *data, char *cache, char *state, size_t cap) {
const wchar_t *roaming = _wgetenv(L"APPDATA"), *local = _wgetenv(L"LOCALAPPDATA");
if (!roaming || !local) return tgb_fail(TGB_ERR_NOT_FOUND, "No se encontraron APPDATA/LOCALAPPDATA");
char r[400], l[400];
if (!from_wide(roaming, r, sizeof r) || !from_wide(local, l, sizeof l)) return tgb_fail(TGB_ERR_LIMIT, "Ruta de datos de usuario demasiado larga");
snprintf(config, cap, "%s\\Tahgibu", r);
snprintf(data, cap, "%s\\Tahgibu\\data", l);
snprintf(cache, cap, "%s\\Tahgibu\\cache", l);
snprintf(state, cap, "%s\\Tahgibu\\state", l);
return TGB_OK;
}
tgb_status tgb_plat_mkdirs(const char *path) {
wchar_t *w = to_wide(path);
if (!w) return tgb_fail(TGB_ERR_INVALID_ARG, "Ruta no válida");
slashes_to_backslash(w);
for (wchar_t *p = w + 1; ; p++) {
if (*p == L'\\' || *p == L'\0') {
wchar_t c = *p; *p = L'\0';
if (!CreateDirectoryW(w, NULL) && GetLastError() != ERROR_ALREADY_EXISTS && GetLastError() != ERROR_ACCESS_DENIED) {
free(w); return tgb_fail(TGB_ERR_IO, "No se pudo crear la carpeta '%s' (error %lu)", path, GetLastError());
}
*p = c;
if (c == L'\0') break;
}
}
DWORD a = GetFileAttributesW(w);
free(w);
return (a != INVALID_FILE_ATTRIBUTES && (a & FILE_ATTRIBUTE_DIRECTORY)) ? TGB_OK : tgb_fail(TGB_ERR_IO, "'%s' no es una carpeta", path);
}
bool tgb_plat_exists(const char *path) { wchar_t *w = to_wide(path); if (!w) return false; DWORD a = GetFileAttributesW(w); free(w); return a != INVALID_FILE_ATTRIBUTES; }
bool tgb_plat_is_dir(const char *path) { wchar_t *w = to_wide(path); if (!w) return false; DWORD a = GetFileAttributesW(w); free(w); return a != INVALID_FILE_ATTRIBUTES && (a & FILE_ATTRIBUTE_DIRECTORY); }
tgb_status tgb_plat_read_file(const char *path, uint8_t **out, size_t *len, size_t max_bytes) {
wchar_t *w = to_wide(path);
if (!w) return tgb_fail(TGB_ERR_INVALID_ARG, "Ruta no válida");
HANDLE h = CreateFileW(w, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
free(w);
if (h == INVALID_HANDLE_VALUE) return tgb_fail(GetLastError() == ERROR_FILE_NOT_FOUND ? TGB_ERR_NOT_FOUND : TGB_ERR_IO, "No se pudo abrir '%s' (error %lu)", path, GetLastError());
LARGE_INTEGER sz;
if (!GetFileSizeEx(h, &sz) || sz.QuadPart < 0) { CloseHandle(h); return tgb_fail(TGB_ERR_IO, "No se pudo leer '%s'", path); }
if ((unsigned long long)sz.QuadPart > (unsigned long long)max_bytes) { CloseHandle(h); return tgb_fail(TGB_ERR_LIMIT, "'%s' es demasiado grande", path); }
size_t size = (size_t)sz.QuadPart;
uint8_t *buf = malloc(size ? size : 1);
if (!buf) { CloseHandle(h); return tgb_fail(TGB_ERR_NOMEM, "Sin memoria"); }
size_t done = 0;
while (done < size) {
DWORD got = 0, want = (DWORD)((size - done) > 0x40000000u ? 0x40000000u : (size - done));
if (!ReadFile(h, buf + done, want, &got, NULL) || got == 0) { free(buf); CloseHandle(h); return tgb_fail(TGB_ERR_IO, "Error leyendo '%s'", path); }
done += got;
}
CloseHandle(h);
*out = buf; *len = size;
return TGB_OK;
}
tgb_status tgb_plat_write_atomic(const char *path, const void *data, size_t len) {
char tmp[1100];
snprintf(tmp, sizeof tmp, "%s.tmp-%lu", path, GetCurrentProcessId());
wchar_t *wt = to_wide(tmp), *wp = to_wide(path);
if (!wt || !wp) { free(wt); free(wp); return tgb_fail(TGB_ERR_INVALID_ARG, "Ruta no válida"); }
HANDLE h = CreateFileW(wt, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) { free(wt); free(wp); return tgb_fail(TGB_ERR_IO, "No se pudo crear el temporal '%s' (error %lu)", tmp, GetLastError()); }
const uint8_t *p = data; size_t left = len; bool ok = true;
while (left > 0 && ok) {
DWORD w = 0, want = (DWORD)(left > 0x40000000u ? 0x40000000u : left);
ok = WriteFile(h, p, want, &w, NULL) && w == want;
p += w; left -= w;
}
if (ok) ok = FlushFileBuffers(h) != 0;
DWORD err = ok ? 0 : GetLastError();
CloseHandle(h);
if (ok && !MoveFileExW(wt, wp, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { ok = false; err = GetLastError(); }
if (!ok) { DeleteFileW(wt); free(wt); free(wp); return tgb_fail(TGB_ERR_IO, "Error escribiendo '%s' (error %lu); el archivo anterior no se modificó", path, err); }
free(wt); free(wp);
return TGB_OK;
}
tgb_status tgb_plat_remove(const char *path) {
wchar_t *w = to_wide(path);
if (!w) return tgb_fail(TGB_ERR_INVALID_ARG, "Ruta no válida");
BOOL ok = DeleteFileW(w);
DWORD e = GetLastError();
free(w);
if (!ok && e != ERROR_FILE_NOT_FOUND && e != ERROR_PATH_NOT_FOUND) return tgb_fail(TGB_ERR_IO, "No se pudo borrar '%s' (error %lu)", path, e);
return TGB_OK;
}
tgb_status tgb_plat_copy_file(const char *src, const char *dst) {
uint8_t *buf; size_t len;
tgb_status s = tgb_plat_read_file(src, &buf, &len, (size_t)1 << 30);
if (TGB_FAILED(s)) return s;
s = tgb_plat_write_atomic(dst, buf, len);
free(buf);
return s;
}
tgb_status tgb_plat_list_dir(const char *dir, char ***names, size_t *count) {
char pat[1100]; snprintf(pat, sizeof pat, "%s\\*", dir);
wchar_t *w = to_wide(pat);
if (!w) return tgb_fail(TGB_ERR_INVALID_ARG, "Ruta no válida");
WIN32_FIND_DATAW fd;
HANDLE h = FindFirstFileW(w, &fd);
free(w);
if (h == INVALID_HANDLE_VALUE) return tgb_fail(GetLastError() == ERROR_PATH_NOT_FOUND ? TGB_ERR_NOT_FOUND : TGB_ERR_IO, "No se pudo abrir la carpeta '%s'", dir);
char **list = NULL; size_t n = 0, cap = 0;
do {
if (!wcscmp(fd.cFileName, L".") || !wcscmp(fd.cFileName, L"..")) continue;
char u[600];
if (!from_wide(fd.cFileName, u, sizeof u)) continue;
if (n == cap) { size_t nc = cap ? cap * 2 : 16; char **nl = realloc(list, nc * sizeof *nl); if (!nl) { FindClose(h); tgb_plat_free_list(list, n); return tgb_fail(TGB_ERR_NOMEM, "Sin memoria"); } list = nl; cap = nc; }
list[n] = tgb_strdup(u);
if (!list[n]) { FindClose(h); tgb_plat_free_list(list, n); return tgb_fail(TGB_ERR_NOMEM, "Sin memoria"); }
n++;
} while (FindNextFileW(h, &fd));
FindClose(h);
*names = list; *count = n;
return TGB_OK;
}
void tgb_plat_free_list(char **names, size_t count) { for (size_t i = 0; i < count; i++) free(names[i]); free(names); }
size_t tgb_plat_available_memory(void) { MEMORYSTATUSEX m; m.dwLength = sizeof m; return GlobalMemoryStatusEx(&m) ? (size_t)m.ullAvailPhys : 0; }
size_t tgb_plat_free_disk(const char *dir) {
wchar_t *w = to_wide(dir); ULARGE_INTEGER freeb;
BOOL ok = w && GetDiskFreeSpaceExW(w, &freeb, NULL, NULL);
free(w);
return ok ? (size_t)freeb.QuadPart : 0;
}
long tgb_plat_pid(void) { return (long)GetCurrentProcessId(); }
int64_t tgb_plat_process_start_token(long pid) {
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)pid);
if (!h) return 0;
FILETIME c, e, k, u; int64_t v = 0;
if (GetProcessTimes(h, &c, &e, &k, &u)) v = (int64_t)(((uint64_t)c.dwHighDateTime << 32 | c.dwLowDateTime) / 10000ull);
CloseHandle(h);
return v;
}
bool tgb_plat_process_alive(long pid, int64_t start_token) {
HANDLE h = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)pid);
if (!h) return GetLastError() == ERROR_ACCESS_DENIED;
DWORD code = 0; BOOL ok = GetExitCodeProcess(h, &code);
CloseHandle(h);
if (!ok || code != STILL_ACTIVE) return false;
if (start_token != 0) { int64_t cur = tgb_plat_process_start_token(pid); if (cur != 0 && cur != start_token) return false; }
return true;
}
int64_t tgb_plat_now_unix(void) { return (int64_t)time(NULL); }
int64_t tgb_plat_file_mtime(const char *path) {
wchar_t *w = to_wide(path); WIN32_FILE_ATTRIBUTE_DATA d; int64_t r = 0;
if (w && GetFileAttributesExW(w, GetFileExInfoStandard, &d)) r = (int64_t)((((uint64_t)d.ftLastWriteTime.dwHighDateTime << 32 | d.ftLastWriteTime.dwLowDateTime) / 10000000ull) - 11644473600ull);
free(w);
return r;
}
double tgb_plat_monotonic_seconds(void) {
LARGE_INTEGER f, c; QueryPerformanceFrequency(&f); QueryPerformanceCounter(&c);
return (double)c.QuadPart / (double)f.QuadPart;
}
void *tgb_plat_dlopen(const char *path) { wchar_t *w = to_wide(path); HMODULE m = w ? LoadLibraryW(w) : NULL; free(w); return (void *)m; }
void *tgb_plat_dlsym(void *lib, const char *name) { return (void *)GetProcAddress((HMODULE)lib, name); }
void tgb_plat_dlclose(void *lib) { if (lib) FreeLibrary((HMODULE)lib); }
const char *tgb_plat_dlerror(void) { static char msg[64]; snprintf(msg, sizeof msg, "error de Windows %lu", GetLastError()); return msg; }
const char *tgb_plat_dll_extension(void) { return ".dll"; }