Couche de compatibilité : shim GLib et en-têtes réduits
engine/compat/ remplace ce que le cœur de gnubg attend du reste de gnubg et de GLib : config.h écrit à la main (sans threads, C générique, NEON prévu), shim GLib minimal (dont un analyseur XML pour les tables d'équité et mmap pour les bases de fin de partie), en-têtes réduits et fonctions de support. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
792f6395bf
commit
42bfb6506d
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* backgammon.h -- en-tête réduit remplaçant celui de GNU Backgammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Le backgammon.h d'origine déclare l'état complet de l'application de
|
||||
* bureau (match en cours, commandes, réglages d'interface). Le cœur repris
|
||||
* dans engine/gnubg/ n'en utilise qu'une petite partie, déclarée ici avec
|
||||
* les mêmes noms et les mêmes types que dans l'original (commit 14b6798).
|
||||
* Les définitions correspondantes sont dans engine/compat/gnubg-support.c.
|
||||
*/
|
||||
|
||||
#ifndef BACKGAMMON_H
|
||||
#define BACKGAMMON_H
|
||||
|
||||
#include "common.h"
|
||||
#include "output.h"
|
||||
#include "eval.h"
|
||||
|
||||
#include <glib/gi18n.h>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* config.h -- configuration de compilation du moteur embarqué d'Ustagammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Remplace le config.h que produit le configure de GNU Backgammon. Seules les
|
||||
* macros lues par les fichiers repris dans engine/gnubg/ sont définies ; les
|
||||
* valeurs suivent ce que configure.ac choisit sur une Debian récente avec gcc.
|
||||
*/
|
||||
|
||||
#ifndef USTAGAMMON_CONFIG_H
|
||||
#define USTAGAMMON_CONFIG_H
|
||||
|
||||
#define VERSION "1.08.003"
|
||||
#define PACKAGE "gnubg"
|
||||
#define GETTEXT_PACKAGE "gnubg"
|
||||
|
||||
/* Compilateur : gcc ou clang, qui comprennent tous deux ces extensions. */
|
||||
#define HAVE___ATTRIBUTE__ 1
|
||||
#define HAVE_FUNC_ATTRIBUTE_CONST 1
|
||||
#define HAVE_FUNC_ATTRIBUTE_FALLTHROUGH 1
|
||||
#define HAVE_FUNC_ATTRIBUTE_FORMAT 1
|
||||
#define HAVE_FUNC_ATTRIBUTE_PURE 1
|
||||
#define HAVE_FUNC_ATTRIBUTE_UNUSED 1
|
||||
#define HAVE___BUILTIN_EXPECT 1
|
||||
#define HAVE___BUILTIN_CLZ 1
|
||||
|
||||
/* Bibliothèque C POSIX (glibc, bionic d'Android, musl d'Emscripten). */
|
||||
#define HAVE_UNISTD_H 1
|
||||
#define HAVE_STDINT_H 1
|
||||
#define HAVE_POSIX_MEMALIGN 1
|
||||
|
||||
/*
|
||||
* Pas de threads : une seule instance du moteur, appels sérialisés
|
||||
* (ADR 0005). USE_MULTITHREAD reste donc non défini.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Instructions SIMD. Par défaut, C générique partout (x86 compris), ce qui
|
||||
* est aussi le seul chemin possible en WebAssembly.
|
||||
*
|
||||
* Chemin NEON prévu pour arm64 (Android), activé en compilant avec
|
||||
* -DUSTAGAMMON_NEON ; il reprend les définitions que configure produit avec
|
||||
* --enable-simd=neon. Le test d'instructions à l'exécution est inutile :
|
||||
* NEON est obligatoire sur arm64.
|
||||
*/
|
||||
#if defined(USTAGAMMON_NEON)
|
||||
#if !defined(__aarch64__)
|
||||
#error "USTAGAMMON_NEON n'est prévu que pour arm64"
|
||||
#endif
|
||||
#define USE_SIMD_INSTRUCTIONS 1
|
||||
#define USE_NEON 1
|
||||
#define HAVE_NEON 1
|
||||
#define DISABLE_SIMD_TEST 1
|
||||
#endif
|
||||
|
||||
#endif /* USTAGAMMON_CONFIG_H */
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* external.h -- en-tête réduit remplaçant celui de GNU Backgammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Les deux structures ci-dessous sont reprises telles quelles de external.h
|
||||
* de GNU Backgammon (commit 14b6798, Copyright (C) 2001-2025 the AUTHORS).
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* drawboard.c (repris pour FormatMove, la notation des coups) inclut
|
||||
* external.h pour ces deux types, utilisés par ProcessFIBSBoardInfo. Le
|
||||
* reste de l'interface externe de gnubg (sockets) n'a pas lieu d'être ici.
|
||||
*/
|
||||
|
||||
#ifndef EXTERNAL_H
|
||||
#define EXTERNAL_H
|
||||
|
||||
#include <glib.h>
|
||||
#include "backgammon.h"
|
||||
|
||||
#define MAX_NAME_LEN 32
|
||||
|
||||
typedef struct {
|
||||
/*
|
||||
* These must be in the same order than the FIBS board string definition,
|
||||
* starting at Match Length.
|
||||
* See description at http://www.fibs.com/fibs_interface.html#board_state
|
||||
*/
|
||||
int nMatchTo;
|
||||
int nScore;
|
||||
int nScoreOpp;
|
||||
int anFIBSBoard[26];
|
||||
int nTurn;
|
||||
int anDice[2];
|
||||
int anOppDice[2];
|
||||
int nCube;
|
||||
int fCanDouble;
|
||||
int fOppCanDouble;
|
||||
int fDoubled;
|
||||
int nColor;
|
||||
int nDirection;
|
||||
int nHome; /* unused, set to 0 */
|
||||
int nBar; /* unused, set to 25 */
|
||||
int nOnHome; /* unused */
|
||||
int nOnHomeOpp; /* unused */
|
||||
int nOnBar; /* unused */
|
||||
int nOnBarOpp; /* unused */
|
||||
int nCanMove; /* unused */
|
||||
int fForcedMOve; /* unused */
|
||||
int fPostCrawford;
|
||||
/*
|
||||
* Redoubles from FIBS board is not usable by GNUbg.
|
||||
* A flag for match play is put at the same place
|
||||
*/
|
||||
int fNonCrawford;
|
||||
int nVersion;
|
||||
int padding[51];
|
||||
|
||||
/* These must be last */
|
||||
GString *gsName;
|
||||
GString *gsOpp;
|
||||
} FIBSBoardInfo;
|
||||
|
||||
typedef struct {
|
||||
char szPlayer[MAX_NAME_LEN];
|
||||
char szOpp[MAX_NAME_LEN];
|
||||
int nMatchTo;
|
||||
int nScore;
|
||||
int nScoreOpp;
|
||||
int anDice[2];
|
||||
int nCube;
|
||||
int fCubeOwner;
|
||||
int fDoubled;
|
||||
int fCrawford;
|
||||
int fJacoby;
|
||||
int nResignation;
|
||||
TanBoard anBoard;
|
||||
} ProcessedFIBSBoard;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* format.h -- en-tête réduit remplaçant celui de GNU Backgammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* eval.c inclut format.h sans utiliser ses fonctions d'affichage dans les
|
||||
* parties reprises : l'en-tête est volontairement vide.
|
||||
*/
|
||||
|
||||
#ifndef FORMAT_H
|
||||
#define FORMAT_H
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* glib-shim.c -- implémentation du shim minimal de GLib (voir glib-shim/glib.h).
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Écrit pour Ustagammon d'après la documentation publique de GLib ; aucun code
|
||||
* de GLib n'est repris.
|
||||
*/
|
||||
|
||||
#define _DEFAULT_SOURCE
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <locale.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "glib.h"
|
||||
|
||||
/* ---------------------------------------------------------------- Erreurs */
|
||||
|
||||
void
|
||||
ustagammon_g_assert_failed(const char *expr, const char *file, int line)
|
||||
{
|
||||
fprintf(stderr, "moteur : assertion « %s » fausse (%s:%d)\n", expr, file, line);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Crée une GError dont le message est « message : detail ». */
|
||||
static GError *
|
||||
new_error(gint code, const char *message, const char *detail)
|
||||
{
|
||||
GError *e = g_new0(GError, 1);
|
||||
size_t size = strlen(message) + strlen(detail) + 4;
|
||||
|
||||
e->code = code;
|
||||
e->message = g_malloc(size);
|
||||
snprintf(e->message, size, "%s : %s", message, detail);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void
|
||||
set_error(GError **error, gint code, const char *message, const char *detail)
|
||||
{
|
||||
if (error)
|
||||
*error = new_error(code, message, detail);
|
||||
}
|
||||
|
||||
void
|
||||
g_error_free(GError *error)
|
||||
{
|
||||
if (!error)
|
||||
return;
|
||||
g_free(error->message);
|
||||
g_free(error);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- Mémoire */
|
||||
|
||||
static gpointer
|
||||
check_allocation(gpointer p, gsize n)
|
||||
{
|
||||
if (!p && n) {
|
||||
fprintf(stderr, "moteur : échec d'allocation de %zu octets\n", n);
|
||||
abort();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_malloc(gsize n)
|
||||
{
|
||||
return n ? check_allocation(malloc(n), n) : NULL;
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_malloc0(gsize n)
|
||||
{
|
||||
return n ? check_allocation(calloc(1, n), n) : NULL;
|
||||
}
|
||||
|
||||
void
|
||||
g_free(gpointer p)
|
||||
{
|
||||
free(p);
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_memdup2(gconstpointer mem, gsize n)
|
||||
{
|
||||
gpointer p;
|
||||
|
||||
if (!mem || !n)
|
||||
return NULL;
|
||||
p = g_malloc(n);
|
||||
memcpy(p, mem, n);
|
||||
return p;
|
||||
}
|
||||
|
||||
gchar *
|
||||
g_strdup(const gchar *s)
|
||||
{
|
||||
return s ? (gchar *) g_memdup2(s, strlen(s) + 1) : NULL;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- Messages */
|
||||
|
||||
/* Tout part sur la sortie d'erreur : la sortie standard appartient à
|
||||
* l'application (JSON d'engine-cli). */
|
||||
|
||||
void
|
||||
g_print(const gchar *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
g_printerr(const gchar *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
g_warning(const gchar *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
fputs("moteur (avertissement) : ", stderr);
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
void
|
||||
g_error(const gchar *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
fputs("moteur (erreur fatale) : ", stderr);
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
fputc('\n', stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- Chaînes */
|
||||
|
||||
/*
|
||||
* strtod indépendant de la locale : le point est toujours le séparateur
|
||||
* décimal. Si la locale courante utilise un autre caractère (la virgule en
|
||||
* français), on le substitue au point dans une copie avant d'appeler strtod.
|
||||
*/
|
||||
gdouble
|
||||
g_ascii_strtod(const gchar *nptr, gchar **endptr)
|
||||
{
|
||||
const char *point = localeconv()->decimal_point;
|
||||
char *copy, *end;
|
||||
double value;
|
||||
size_t i;
|
||||
|
||||
if (!point || strlen(point) != 1 || point[0] == '.')
|
||||
return strtod(nptr, endptr);
|
||||
|
||||
copy = g_strdup(nptr);
|
||||
for (i = 0; copy[i]; i++)
|
||||
if (copy[i] == '.')
|
||||
copy[i] = point[0];
|
||||
value = strtod(copy, &end);
|
||||
if (endptr)
|
||||
*endptr = (gchar *) nptr + (end - copy);
|
||||
g_free(copy);
|
||||
return value;
|
||||
}
|
||||
|
||||
guint64
|
||||
g_ascii_strtoull(const gchar *nptr, gchar **endptr, guint base)
|
||||
{
|
||||
return (guint64) strtoull(nptr, endptr, (int) base);
|
||||
}
|
||||
|
||||
static int
|
||||
ascii_tolower(int c)
|
||||
{
|
||||
return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
|
||||
}
|
||||
|
||||
gint
|
||||
g_ascii_strncasecmp(const gchar *s1, const gchar *s2, gsize n)
|
||||
{
|
||||
for (; n > 0; n--, s1++, s2++) {
|
||||
int c1 = ascii_tolower((unsigned char) *s1);
|
||||
int c2 = ascii_tolower((unsigned char) *s2);
|
||||
if (c1 != c2 || !c1)
|
||||
return c1 - c2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint
|
||||
g_ascii_strcasecmp(const gchar *s1, const gchar *s2)
|
||||
{
|
||||
return g_ascii_strncasecmp(s1, s2, (gsize) -1);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
g_strerror(gint errnum)
|
||||
{
|
||||
return strerror(errnum);
|
||||
}
|
||||
|
||||
/* Copie tronquée et toujours terminée ; renvoie la longueur de src. */
|
||||
gsize
|
||||
g_strlcpy(gchar *dest, const gchar *src, gsize dest_size)
|
||||
{
|
||||
gsize len = strlen(src);
|
||||
|
||||
if (dest_size > 0) {
|
||||
gsize n = len < dest_size - 1 ? len : dest_size - 1;
|
||||
memcpy(dest, src, n);
|
||||
dest[n] = '\0';
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Assemble des morceaux de chemin avec « / », sans doubler le séparateur. */
|
||||
gchar *
|
||||
g_build_filename(const gchar *first, ...)
|
||||
{
|
||||
va_list args;
|
||||
const gchar *part;
|
||||
size_t total = 1;
|
||||
gchar *result;
|
||||
|
||||
va_start(args, first);
|
||||
for (part = first; part; part = va_arg(args, const gchar *))
|
||||
total += strlen(part) + 1;
|
||||
va_end(args);
|
||||
|
||||
result = g_malloc(total);
|
||||
result[0] = '\0';
|
||||
va_start(args, first);
|
||||
for (part = first; part; part = va_arg(args, const gchar *)) {
|
||||
size_t len = strlen(result);
|
||||
if (!*part)
|
||||
continue;
|
||||
if (len > 0 && result[len - 1] != '/')
|
||||
strcat(result, "/");
|
||||
if (len > 0 && *part == '/')
|
||||
part++;
|
||||
strcat(result, part);
|
||||
}
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- Fichiers */
|
||||
|
||||
gboolean
|
||||
g_file_test(const gchar *filename, GFileTest test)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(filename, &st) != 0)
|
||||
return FALSE;
|
||||
if (test & G_FILE_TEST_EXISTS)
|
||||
return TRUE;
|
||||
if ((test & G_FILE_TEST_IS_REGULAR) && S_ISREG(st.st_mode))
|
||||
return TRUE;
|
||||
if ((test & G_FILE_TEST_IS_DIR) && S_ISDIR(st.st_mode))
|
||||
return TRUE;
|
||||
if ((test & G_FILE_TEST_IS_EXECUTABLE) && access(filename, X_OK) == 0)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Lit un fichier entier en mémoire, terminé par un octet nul. */
|
||||
gboolean
|
||||
g_file_get_contents(const gchar *filename, gchar **contents, gsize *length, GError **error)
|
||||
{
|
||||
FILE *f;
|
||||
long size;
|
||||
gchar *buffer;
|
||||
|
||||
*contents = NULL;
|
||||
f = fopen(filename, "rb");
|
||||
if (!f) {
|
||||
set_error(error, errno, "lecture impossible", filename);
|
||||
return FALSE;
|
||||
}
|
||||
if (fseek(f, 0, SEEK_END) != 0 || (size = ftell(f)) < 0 || fseek(f, 0, SEEK_SET) != 0) {
|
||||
fclose(f);
|
||||
set_error(error, EIO, "taille illisible", filename);
|
||||
return FALSE;
|
||||
}
|
||||
buffer = g_malloc((size_t) size + 1);
|
||||
if (fread(buffer, 1, (size_t) size, f) != (size_t) size) {
|
||||
fclose(f);
|
||||
g_free(buffer);
|
||||
set_error(error, EIO, "lecture incomplète", filename);
|
||||
return FALSE;
|
||||
}
|
||||
fclose(f);
|
||||
buffer[size] = '\0';
|
||||
*contents = buffer;
|
||||
if (length)
|
||||
*length = (gsize) size;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Fichier projeté en mémoire en lecture seule (mmap). */
|
||||
struct UstagammonMappedFile {
|
||||
gchar *contents;
|
||||
gsize length;
|
||||
};
|
||||
|
||||
GMappedFile *
|
||||
g_mapped_file_new(const gchar *filename, gboolean writable, GError **error)
|
||||
{
|
||||
GMappedFile *m;
|
||||
struct stat st;
|
||||
void *p = NULL;
|
||||
int fd;
|
||||
|
||||
if (writable) {
|
||||
set_error(error, EINVAL, "projection en écriture non prise en charge", filename);
|
||||
return NULL;
|
||||
}
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
set_error(error, errno, "ouverture impossible", filename);
|
||||
return NULL;
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
set_error(error, errno, "taille illisible", filename);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
if (st.st_size > 0) {
|
||||
p = mmap(NULL, (size_t) st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (p == MAP_FAILED) {
|
||||
set_error(error, errno, "projection impossible", filename);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
m = g_new0(GMappedFile, 1);
|
||||
m->contents = p;
|
||||
m->length = (gsize) st.st_size;
|
||||
return m;
|
||||
}
|
||||
|
||||
gchar *
|
||||
g_mapped_file_get_contents(GMappedFile *file)
|
||||
{
|
||||
return file->contents;
|
||||
}
|
||||
|
||||
gsize
|
||||
g_mapped_file_get_length(GMappedFile *file)
|
||||
{
|
||||
return file->length;
|
||||
}
|
||||
|
||||
void
|
||||
g_mapped_file_unref(GMappedFile *file)
|
||||
{
|
||||
if (!file)
|
||||
return;
|
||||
if (file->contents)
|
||||
munmap(file->contents, file->length);
|
||||
g_free(file);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- XML simple */
|
||||
|
||||
/*
|
||||
* Analyseur XML réduit à ce qu'utilisent les tables d'équité de gnubg
|
||||
* (fichiers XML de engine/data/) : éléments, attributs entre guillemets, texte,
|
||||
* commentaires, prologue <?xml?> et <!DOCTYPE> sans sous-ensemble interne,
|
||||
* entités < > & " '. Tout le reste est une erreur.
|
||||
*/
|
||||
|
||||
struct UstagammonMarkupParseContext {
|
||||
const GMarkupParser *parser;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_data_dnotify;
|
||||
};
|
||||
|
||||
GMarkupParseContext *
|
||||
g_markup_parse_context_new(const GMarkupParser *parser, GMarkupParseFlags flags,
|
||||
gpointer user_data, GDestroyNotify user_data_dnotify)
|
||||
{
|
||||
GMarkupParseContext *c = g_new0(GMarkupParseContext, 1);
|
||||
|
||||
(void) flags;
|
||||
c->parser = parser;
|
||||
c->user_data = user_data;
|
||||
c->user_data_dnotify = user_data_dnotify;
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
g_markup_parse_context_free(GMarkupParseContext *context)
|
||||
{
|
||||
if (!context)
|
||||
return;
|
||||
if (context->user_data_dnotify)
|
||||
context->user_data_dnotify(context->user_data);
|
||||
g_free(context);
|
||||
}
|
||||
|
||||
#define MAX_DEPTH 32
|
||||
#define MAX_ATTRIBUTES 8
|
||||
|
||||
/* Copie [start, end) en décodant les entités ; NULL si une entité est inconnue. */
|
||||
static gchar *
|
||||
decode_text(const char *start, const char *end)
|
||||
{
|
||||
static const struct {
|
||||
const char *name;
|
||||
char c;
|
||||
} entities[] = {
|
||||
{ "<", '<' }, { ">", '>' }, { "&", '&' }, { """, '"' }, { "'", '\'' }
|
||||
};
|
||||
gchar *out = g_malloc((size_t) (end - start) + 1);
|
||||
gchar *o = out;
|
||||
const char *p = start;
|
||||
|
||||
while (p < end) {
|
||||
size_t i, n = sizeof entities / sizeof entities[0];
|
||||
if (*p != '&') {
|
||||
*o++ = *p++;
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
size_t len = strlen(entities[i].name);
|
||||
if ((size_t) (end - p) >= len && strncmp(p, entities[i].name, len) == 0) {
|
||||
*o++ = entities[i].c;
|
||||
p += len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == n) {
|
||||
g_free(out);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
*o = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
static int
|
||||
is_name_char(char c)
|
||||
{
|
||||
return isalnum((unsigned char) c) || c == '-' || c == '_' || c == ':' || c == '.';
|
||||
}
|
||||
|
||||
static const char *
|
||||
skip_spaces(const char *p, const char *end)
|
||||
{
|
||||
while (p < end && isspace((unsigned char) *p))
|
||||
p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Copie le nom qui commence en p ; avance *after juste derrière. */
|
||||
static gchar *
|
||||
read_name(const char *p, const char *end, const char **after)
|
||||
{
|
||||
const char *q = p;
|
||||
gchar *name;
|
||||
|
||||
while (q < end && is_name_char(*q))
|
||||
q++;
|
||||
*after = q;
|
||||
if (q == p)
|
||||
return NULL;
|
||||
name = g_malloc((size_t) (q - p) + 1);
|
||||
memcpy(name, p, (size_t) (q - p));
|
||||
name[q - p] = '\0';
|
||||
return name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lit les attributs d'une balise ouvrante jusqu'à « > » ou « /> ».
|
||||
* Renvoie la position après la balise, ou NULL en cas d'erreur.
|
||||
*/
|
||||
static const char *
|
||||
read_attributes(const char *p, const char *end, gchar **names, gchar **values, int *empty)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
names[0] = values[0] = NULL;
|
||||
for (;;) {
|
||||
const char *value_start;
|
||||
char quote;
|
||||
|
||||
p = skip_spaces(p, end);
|
||||
if (p >= end)
|
||||
return NULL;
|
||||
if (*p == '>')
|
||||
return p + 1;
|
||||
if (*p == '/' && p + 1 < end && p[1] == '>') {
|
||||
*empty = 1;
|
||||
return p + 2;
|
||||
}
|
||||
if (n == MAX_ATTRIBUTES)
|
||||
return NULL;
|
||||
names[n] = read_name(p, end, &p);
|
||||
names[n + 1] = values[n] = values[n + 1] = NULL;
|
||||
if (!names[n])
|
||||
return NULL;
|
||||
p = skip_spaces(p, end);
|
||||
if (p >= end || *p != '=')
|
||||
return NULL;
|
||||
p = skip_spaces(p + 1, end);
|
||||
if (p >= end || (*p != '"' && *p != '\''))
|
||||
return NULL;
|
||||
quote = *p++;
|
||||
value_start = p;
|
||||
while (p < end && *p != quote)
|
||||
p++;
|
||||
if (p >= end)
|
||||
return NULL;
|
||||
values[n] = decode_text(value_start, p);
|
||||
if (!values[n])
|
||||
return NULL;
|
||||
n++;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_strings(gchar **strings)
|
||||
{
|
||||
for (; *strings; strings++)
|
||||
g_free(*strings);
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_markup_parse_context_parse(GMarkupParseContext *context, const gchar *text,
|
||||
gssize text_len, GError **error)
|
||||
{
|
||||
const GMarkupParser *mp = context->parser;
|
||||
const char *p = text;
|
||||
const char *end = text + (text_len < 0 ? (gssize) strlen(text) : text_len);
|
||||
gchar *stack[MAX_DEPTH];
|
||||
int depth = 0;
|
||||
const char *problem = NULL;
|
||||
GError *err = NULL;
|
||||
|
||||
while (p < end && !problem && !err) {
|
||||
if (*p != '<') {
|
||||
/* Texte jusqu'à la prochaine balise. */
|
||||
const char *start = p;
|
||||
gchar *t;
|
||||
while (p < end && *p != '<')
|
||||
p++;
|
||||
if (depth == 0) {
|
||||
if (skip_spaces(start, p) != p)
|
||||
problem = "texte hors de l'élément racine";
|
||||
continue;
|
||||
}
|
||||
t = decode_text(start, p);
|
||||
if (!t)
|
||||
problem = "entité inconnue";
|
||||
else if (mp->text)
|
||||
mp->text(context, t, strlen(t), context->user_data, &err);
|
||||
g_free(t);
|
||||
} else if (end - p >= 4 && strncmp(p, "<!--", 4) == 0) {
|
||||
const char *q = p + 4;
|
||||
while (q + 3 <= end && strncmp(q, "-->", 3) != 0)
|
||||
q++;
|
||||
if (q + 3 > end)
|
||||
problem = "commentaire non terminé";
|
||||
p = q + 3;
|
||||
} else if (end - p >= 2 && (p[1] == '?' || p[1] == '!')) {
|
||||
const char *q = memchr(p, '>', (size_t) (end - p));
|
||||
if (!q || memchr(p, '[', (size_t) (q - p)))
|
||||
problem = "déclaration non prise en charge";
|
||||
else
|
||||
p = q + 1;
|
||||
} else if (end - p >= 2 && p[1] == '/') {
|
||||
/* Balise fermante. */
|
||||
gchar *name = read_name(p + 2, end, &p);
|
||||
p = skip_spaces(p, end);
|
||||
if (!name || p >= end || *p != '>')
|
||||
problem = "balise fermante mal formée";
|
||||
else if (depth == 0 || strcmp(stack[depth - 1], name) != 0)
|
||||
problem = "balise fermante inattendue";
|
||||
else {
|
||||
depth--;
|
||||
if (mp->end_element)
|
||||
mp->end_element(context, name, context->user_data, &err);
|
||||
g_free(stack[depth]);
|
||||
p++;
|
||||
}
|
||||
g_free(name);
|
||||
} else {
|
||||
/* Balise ouvrante, éventuellement vide (<a/>). */
|
||||
gchar *names[MAX_ATTRIBUTES + 1], *values[MAX_ATTRIBUTES + 1];
|
||||
int empty = 0;
|
||||
gchar *name = read_name(p + 1, end, &p);
|
||||
names[0] = values[0] = NULL;
|
||||
if (name)
|
||||
p = read_attributes(p, end, names, values, &empty);
|
||||
if (!name || !p)
|
||||
problem = "balise ouvrante mal formée";
|
||||
else if (depth == MAX_DEPTH)
|
||||
problem = "imbrication trop profonde";
|
||||
else {
|
||||
if (mp->start_element)
|
||||
mp->start_element(context, name, (const gchar **) names,
|
||||
(const gchar **) values, context->user_data, &err);
|
||||
if (!err && empty && mp->end_element)
|
||||
mp->end_element(context, name, context->user_data, &err);
|
||||
}
|
||||
free_strings(names);
|
||||
free_strings(values);
|
||||
if (!problem && !err && !empty)
|
||||
stack[depth++] = name;
|
||||
else
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!problem && !err && depth > 0)
|
||||
problem = "document incomplet";
|
||||
while (depth > 0)
|
||||
g_free(stack[--depth]);
|
||||
if (problem)
|
||||
err = new_error(0, "XML invalide", problem);
|
||||
if (err) {
|
||||
if (mp->error)
|
||||
mp->error(context, err, context->user_data);
|
||||
if (error)
|
||||
*error = err;
|
||||
else
|
||||
g_error_free(err);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* glib.h -- shim minimal de GLib pour le moteur embarqué d'Ustagammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Le cœur de GNU Backgammon repris dans engine/gnubg/ utilise une petite
|
||||
* partie de GLib. Plutôt que d'embarquer GLib entière (taille, portage Android
|
||||
* et WebAssembly), ce fichier en fournit le sous-ensemble utilisé, avec le
|
||||
* même comportement observable pour le moteur. Implémentation :
|
||||
* engine/compat/glib-shim.c.
|
||||
*
|
||||
* Écarts assumés avec GLib :
|
||||
* - g_print écrit sur la sortie d'erreur (et non la sortie standard), pour ne
|
||||
* jamais mêler de messages de diagnostic à la sortie JSON d'engine-cli ;
|
||||
* - GMarkup ne reconnaît que le XML simple des tables d'équité de match
|
||||
* (éléments, attributs, texte, commentaires, prologue, DOCTYPE, entités
|
||||
* prédéfinies et numériques) ; pas de CDATA ni d'instructions dans le texte ;
|
||||
* - g_error et l'échec d'allocation interrompent le programme (abort), comme
|
||||
* GLib : ces chemins signalent des erreurs internes, pas des erreurs
|
||||
* d'utilisation ;
|
||||
* - ni threads, ni atomiques, ni listes GLib : le moteur est compilé sans
|
||||
* USE_MULTITHREAD.
|
||||
*
|
||||
* Ce fichier ne sert qu'au moteur embarqué. Les outils de génération de
|
||||
* données (makeweights, makebearoff) sont compilés avec la vraie GLib.
|
||||
*/
|
||||
|
||||
#ifndef USTAGAMMON_GLIB_SHIM_H
|
||||
#define USTAGAMMON_GLIB_SHIM_H
|
||||
|
||||
#include <float.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <alloca.h>
|
||||
|
||||
/* Types de base. */
|
||||
typedef char gchar;
|
||||
typedef unsigned char guchar;
|
||||
typedef int gint;
|
||||
typedef unsigned int guint;
|
||||
typedef int gboolean;
|
||||
typedef short gshort;
|
||||
typedef unsigned short gushort;
|
||||
typedef long glong;
|
||||
typedef unsigned long gulong;
|
||||
typedef float gfloat;
|
||||
typedef double gdouble;
|
||||
typedef void *gpointer;
|
||||
typedef const void *gconstpointer;
|
||||
typedef size_t gsize;
|
||||
typedef ptrdiff_t gssize;
|
||||
typedef int8_t gint8;
|
||||
typedef uint8_t guint8;
|
||||
typedef int16_t gint16;
|
||||
typedef uint16_t guint16;
|
||||
typedef int32_t gint32;
|
||||
typedef uint32_t guint32;
|
||||
typedef int64_t gint64;
|
||||
typedef uint64_t guint64;
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE (0)
|
||||
#endif
|
||||
#ifndef TRUE
|
||||
#define TRUE (!FALSE)
|
||||
#endif
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#define G_PI 3.1415926535897932384626433832795028841971693993751
|
||||
#define G_PI_2 1.5707963267948966192313216916397514420985846996876
|
||||
#define G_GSIZE_FORMAT "zu"
|
||||
#define G_STRINGIFY_ARG(x) #x
|
||||
#define G_STRINGIFY(x) G_STRINGIFY_ARG(x)
|
||||
#define G_STRLOC __FILE__ ":" G_STRINGIFY(__LINE__)
|
||||
|
||||
/*
|
||||
* Version de GLib imitée : 2.84 (celle de Debian trixie), pour que le code
|
||||
* de gnubg choisisse les mêmes branches qu'avec la vraie bibliothèque
|
||||
* (g_memdup2 plutôt que g_memdup, par exemple).
|
||||
*/
|
||||
#define GLIB_MAJOR_VERSION 2
|
||||
#define GLIB_MINOR_VERSION 84
|
||||
#define GLIB_MICRO_VERSION 0
|
||||
#define GLIB_CHECK_VERSION(major, minor, micro) \
|
||||
(GLIB_MAJOR_VERSION > (major) || \
|
||||
(GLIB_MAJOR_VERSION == (major) && GLIB_MINOR_VERSION > (minor)) || \
|
||||
(GLIB_MAJOR_VERSION == (major) && GLIB_MINOR_VERSION == (minor) && \
|
||||
GLIB_MICRO_VERSION >= (micro)))
|
||||
|
||||
/* Assertions : désactivées par G_DISABLE_ASSERT, comme dans le build officiel. */
|
||||
void ustagammon_g_assert_failed(const char *expr, const char *file, int line);
|
||||
|
||||
#if defined(G_DISABLE_ASSERT)
|
||||
#define g_assert(expr) ((void) 0)
|
||||
#define g_assert_not_reached() ((void) 0)
|
||||
#else
|
||||
#define g_assert(expr) \
|
||||
((expr) ? (void) 0 : ustagammon_g_assert_failed(#expr, __FILE__, __LINE__))
|
||||
#define g_assert_not_reached() \
|
||||
ustagammon_g_assert_failed("code inatteignable", __FILE__, __LINE__)
|
||||
#endif
|
||||
|
||||
#define g_return_val_if_fail(expr, val) \
|
||||
do { if (!(expr)) { \
|
||||
g_warning("%s : condition « %s » fausse", G_STRLOC, #expr); \
|
||||
return (val); } } while (0)
|
||||
#define g_return_if_fail(expr) \
|
||||
do { if (!(expr)) { \
|
||||
g_warning("%s : condition « %s » fausse", G_STRLOC, #expr); \
|
||||
return; } } while (0)
|
||||
|
||||
/* Mémoire. */
|
||||
gpointer g_malloc(gsize n);
|
||||
gpointer g_malloc0(gsize n);
|
||||
void g_free(gpointer p);
|
||||
gpointer g_memdup2(gconstpointer mem, gsize n);
|
||||
gchar *g_strdup(const gchar *s);
|
||||
#define g_new(type, n) ((type *) g_malloc(sizeof(type) * (gsize) (n)))
|
||||
#define g_new0(type, n) ((type *) g_malloc0(sizeof(type) * (gsize) (n)))
|
||||
#define g_alloca(size) alloca(size)
|
||||
|
||||
/* Messages. */
|
||||
void g_print(const gchar *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
void g_printerr(const gchar *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
void g_warning(const gchar *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
void g_error(const gchar *format, ...) __attribute__((format(printf, 1, 2), noreturn));
|
||||
|
||||
/* Chaînes. */
|
||||
gdouble g_ascii_strtod(const gchar *nptr, gchar **endptr);
|
||||
guint64 g_ascii_strtoull(const gchar *nptr, gchar **endptr, guint base);
|
||||
gint g_ascii_strcasecmp(const gchar *s1, const gchar *s2);
|
||||
gint g_ascii_strncasecmp(const gchar *s1, const gchar *s2, gsize n);
|
||||
const gchar *g_strerror(gint errnum);
|
||||
gchar *g_build_filename(const gchar *first, ...);
|
||||
gsize g_strlcpy(gchar *dest, const gchar *src, gsize dest_size);
|
||||
|
||||
/* GString : seul le type est utilisé (structures FIBS de drawboard.c). */
|
||||
typedef struct {
|
||||
gchar *str;
|
||||
gsize len;
|
||||
gsize allocated_len;
|
||||
} GString;
|
||||
|
||||
/* Erreurs. */
|
||||
typedef guint32 GQuark;
|
||||
typedef struct {
|
||||
GQuark domain;
|
||||
gint code;
|
||||
gchar *message;
|
||||
} GError;
|
||||
|
||||
void g_error_free(GError *error);
|
||||
|
||||
/* Fichiers. */
|
||||
typedef enum {
|
||||
G_FILE_TEST_IS_REGULAR = 1 << 0,
|
||||
G_FILE_TEST_IS_SYMLINK = 1 << 1,
|
||||
G_FILE_TEST_IS_DIR = 1 << 2,
|
||||
G_FILE_TEST_IS_EXECUTABLE = 1 << 3,
|
||||
G_FILE_TEST_EXISTS = 1 << 4
|
||||
} GFileTest;
|
||||
|
||||
gboolean g_file_test(const gchar *filename, GFileTest test);
|
||||
gboolean g_file_get_contents(const gchar *filename, gchar **contents, gsize *length, GError **error);
|
||||
|
||||
/* Fichier projeté en mémoire (bases de fin de partie .bd). */
|
||||
typedef struct UstagammonMappedFile GMappedFile;
|
||||
|
||||
GMappedFile *g_mapped_file_new(const gchar *filename, gboolean writable, GError **error);
|
||||
gchar *g_mapped_file_get_contents(GMappedFile *file);
|
||||
gsize g_mapped_file_get_length(GMappedFile *file);
|
||||
void g_mapped_file_unref(GMappedFile *file);
|
||||
|
||||
/* Analyseur XML simple (tables d'équité de match). */
|
||||
typedef struct UstagammonMarkupParseContext GMarkupParseContext;
|
||||
typedef void (*GDestroyNotify)(gpointer data);
|
||||
|
||||
typedef enum {
|
||||
G_MARKUP_DO_NOT_USE_THIS_UNSUPPORTED_FLAG = 1 << 0,
|
||||
G_MARKUP_TREAT_CDATA_AS_TEXT = 1 << 1,
|
||||
G_MARKUP_PREFIX_ERROR_POSITION = 1 << 2,
|
||||
G_MARKUP_IGNORE_QUALIFIED = 1 << 3
|
||||
} GMarkupParseFlags;
|
||||
|
||||
typedef struct {
|
||||
void (*start_element)(GMarkupParseContext *context, const gchar *element_name,
|
||||
const gchar **attribute_names, const gchar **attribute_values,
|
||||
gpointer user_data, GError **error);
|
||||
void (*end_element)(GMarkupParseContext *context, const gchar *element_name,
|
||||
gpointer user_data, GError **error);
|
||||
void (*text)(GMarkupParseContext *context, const gchar *text, gsize text_len,
|
||||
gpointer user_data, GError **error);
|
||||
void (*passthrough)(GMarkupParseContext *context, const gchar *passthrough_text,
|
||||
gsize text_len, gpointer user_data, GError **error);
|
||||
void (*error)(GMarkupParseContext *context, GError *error, gpointer user_data);
|
||||
} GMarkupParser;
|
||||
|
||||
GMarkupParseContext *g_markup_parse_context_new(const GMarkupParser *parser, GMarkupParseFlags flags,
|
||||
gpointer user_data, GDestroyNotify user_data_dnotify);
|
||||
gboolean g_markup_parse_context_parse(GMarkupParseContext *context, const gchar *text,
|
||||
gssize text_len, GError **error);
|
||||
void g_markup_parse_context_free(GMarkupParseContext *context);
|
||||
|
||||
#endif /* USTAGAMMON_GLIB_SHIM_H */
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* glib/gi18n.h -- shim minimal de GLib (voir ../glib.h).
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Pas de traduction dans le moteur : les messages de gnubg restent en
|
||||
* anglais et ne sont destinés qu'aux journaux de diagnostic.
|
||||
*/
|
||||
|
||||
#ifndef USTAGAMMON_GLIB_GI18N_H
|
||||
#define USTAGAMMON_GLIB_GI18N_H
|
||||
|
||||
#include "../glib.h"
|
||||
|
||||
#define _(s) (s)
|
||||
#define N_(s) (s)
|
||||
#define gettext(s) (s)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* glib/gprintf.h -- shim minimal de GLib (voir ../glib.h).
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*/
|
||||
|
||||
#ifndef USTAGAMMON_GLIB_GPRINTF_H
|
||||
#define USTAGAMMON_GLIB_GPRINTF_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../glib.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* glib/gstdio.h -- shim minimal de GLib (voir ../glib.h).
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*/
|
||||
|
||||
#ifndef USTAGAMMON_GLIB_GSTDIO_H
|
||||
#define USTAGAMMON_GLIB_GSTDIO_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "../glib.h"
|
||||
|
||||
#define g_fopen fopen
|
||||
#define g_unlink unlink
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* gnubg-support.c -- définitions attendues par le cœur de gnubg.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Le cœur repris dans engine/gnubg/ appelle quelques fonctions et lit
|
||||
* quelques variables définies ailleurs dans GNU Backgammon (gnubg.c, set.c,
|
||||
* output.c, util.c, multithread.c). Ce fichier en fournit des versions
|
||||
* minimales pour le moteur embarqué, compilé sans threads ni interface.
|
||||
* Liste établie à l'édition des liens ; le fichier jni-bridge/src/stubs.c de
|
||||
* CBG (GPLv3) a servi de point de repère, sans reprise de code.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "backgammon.h"
|
||||
#include "multithread.h"
|
||||
#include "util.h"
|
||||
#include "gnubg-support.h"
|
||||
|
||||
/* Code de retour des tâches (multithread.c, branche sans threads). */
|
||||
int asyncRet;
|
||||
|
||||
/* Dossier des données (gnubg.wd, bases .bd) : gnubg le tient en global. */
|
||||
static char *pkg_data_dir;
|
||||
|
||||
void
|
||||
gnubg_support_set_data_dir(const char *dir)
|
||||
{
|
||||
g_free(pkg_data_dir);
|
||||
pkg_data_dir = g_strdup(dir);
|
||||
}
|
||||
|
||||
char *
|
||||
getPkgDataDir(void)
|
||||
{
|
||||
return pkg_data_dir ? pkg_data_dir : ".";
|
||||
}
|
||||
|
||||
/* Sorties de diagnostic : toujours sur la sortie d'erreur. */
|
||||
void
|
||||
outputerrf(const char *sz, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, sz);
|
||||
vfprintf(stderr, sz, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
print_utf8_to_locale(const gchar *sz)
|
||||
{
|
||||
fputs(sz, stderr);
|
||||
}
|
||||
|
||||
void
|
||||
PrintError(const char *message)
|
||||
{
|
||||
perror(message);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* gnubg-support.h -- réglages du cœur de gnubg propres à Ustagammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*/
|
||||
|
||||
#ifndef USTAGAMMON_GNUBG_SUPPORT_H
|
||||
#define USTAGAMMON_GNUBG_SUPPORT_H
|
||||
|
||||
/* Fixe le dossier où gnubg cherche gnubg.wd et les bases .bd. */
|
||||
void gnubg_support_set_data_dir(const char *dir);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* host-tools-support.c -- complément pour l'outil makebearoff de gnubg.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* makebearoff (compilé avec la vraie GLib, pour générer les bases .bd au
|
||||
* build) appelle GetTemporaryFile, définie dans util.c de gnubg. Version
|
||||
* minimale, même contrat : ouvre un fichier temporaire en lecture-écriture
|
||||
* et rend son nom, à supprimer puis libérer par l'appelant.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
FILE *
|
||||
GetTemporaryFile(const char *nameTemplate, char **retName)
|
||||
{
|
||||
FILE *pf;
|
||||
int fd;
|
||||
|
||||
if (!retName)
|
||||
return NULL;
|
||||
fd = g_file_open_tmp(nameTemplate, retName, NULL);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
pf = fdopen(fd, "wb+");
|
||||
if (!pf) {
|
||||
close(fd);
|
||||
unlink(*retName);
|
||||
g_free(*retName);
|
||||
*retName = NULL;
|
||||
}
|
||||
return pf;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* multithread.h -- en-tête réduit remplaçant celui de GNU Backgammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Le moteur embarqué est compilé sans USE_MULTITHREAD (ADR 0005). Ne restent
|
||||
* que les définitions de la branche monothread de l'original : les macros
|
||||
* MT_* et les données locales au fil unique (td.tld).
|
||||
*/
|
||||
|
||||
#ifndef MULTITHREAD_H
|
||||
#define MULTITHREAD_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if defined(USE_MULTITHREAD)
|
||||
#error "Le moteur embarqué d'Ustagammon est compilé sans threads"
|
||||
#endif
|
||||
|
||||
#include "backgammon.h"
|
||||
|
||||
#define multi_debug(x)
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
move *aMoves;
|
||||
NNState *pnnState;
|
||||
} ThreadLocalData;
|
||||
|
||||
typedef struct {
|
||||
ThreadLocalData *tld;
|
||||
} ThreadData;
|
||||
|
||||
extern ThreadData td;
|
||||
|
||||
/* Définies dans mtsupport.c (branche sans threads). */
|
||||
extern void MT_InitThreads(void);
|
||||
extern void MT_Close(void);
|
||||
extern ThreadLocalData *MT_CreateThreadLocalData(int id);
|
||||
|
||||
#if !defined(MAX_NUMTHREADS)
|
||||
#define MAX_NUMTHREADS 1
|
||||
#endif
|
||||
extern int asyncRet;
|
||||
#define MT_Exclusive() {}
|
||||
#define MT_Release() {}
|
||||
#define MT_GetNumThreads() 1
|
||||
#define MT_SetResultFailed() asyncRet = -1
|
||||
#define MT_SafeInc(x) (++(*x))
|
||||
#define MT_SafeIncValue(x) (++(*x))
|
||||
#define MT_SafeIncCheck(x) ((*x)++)
|
||||
#define MT_SafeAdd(x, y) ((*x) += y)
|
||||
#define MT_SafeDec(x) (--(*x))
|
||||
#define MT_SafeDecCheck(x) ((--(*x)) == 0)
|
||||
#define MT_SafeGet(x) (*x)
|
||||
#define MT_SafeSet(x, y) ((*x) = y)
|
||||
#define MT_SafeCompare(x, y) ((*x) == y)
|
||||
#define MT_GetThreadID() 0
|
||||
#define MT_Get_nnState() td.tld->pnnState
|
||||
#define MT_Get_aMoves() td.tld->aMoves
|
||||
#define MT_GetTLD() td.tld
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* output.h -- en-tête réduit remplaçant celui de GNU Backgammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* Seules les fonctions de sortie utilisées par le cœur sont déclarées, avec
|
||||
* les signatures de l'original. Dans le moteur embarqué, elles écrivent sur
|
||||
* la sortie d'erreur (voir engine/compat/gnubg-support.c).
|
||||
*/
|
||||
|
||||
#ifndef OUTPUT_H
|
||||
#define OUTPUT_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <glib.h>
|
||||
|
||||
extern void outputerrf(const char *sz, ...) __attribute__((format(printf, 1, 2)));
|
||||
extern void print_utf8_to_locale(const gchar *sz);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* util.h -- en-tête réduit remplaçant celui de GNU Backgammon.
|
||||
*
|
||||
* Copyright (C) 2026 les auteurs d'Ustagammon.
|
||||
* Licence : GPLv3 ou ultérieure (voir LICENSE).
|
||||
*
|
||||
* getPkgDataDir() renvoie le dossier des données du moteur, fixé à
|
||||
* l'initialisation (engine_init). Définitions : gnubg-support.c.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern char *getPkgDataDir(void);
|
||||
|
||||
#define BuildFilename(file) g_build_filename(getPkgDataDir(), file, NULL)
|
||||
#define BuildFilename2(file1, file2) g_build_filename(getPkgDataDir(), file1, file2, NULL)
|
||||
|
||||
extern void PrintError(const char *message);
|
||||
extern FILE *GetTemporaryFile(const char *nameTemplate, char **retName);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user