V2.0.0
This commit is contained in:
1 parent
7f7fcd545d
commit
4d9a339421
114 files changed
+11383
-1837
No files matched your search
@@ -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"; }
|
||||
Reference in new issue
Block a user