Files
nethack/src/utf8map.c
2022-07-03 21:23:13 -04:00

1124 lines
42 KiB
C

/* NetHack 3.7 utf8map.c */
/* Copyright (c) Michael Allison, 2021. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include "integer.h"
#include <ctype.h>
extern const struct symparse loadsyms[];
extern struct enum_dump monsdump[];
extern struct enum_dump objdump[];
extern glyph_map glyphmap[MAX_GLYPH];
extern const char *known_handling[]; /* symbols.c */
#ifdef ENHANCED_SYMBOLS
#define Fprintf (void) fprintf
enum reserved_activities {res_nothing, res_dump_glyphids, res_fill_cache};
enum things_to_find { find_nothing, find_pm, find_oc, find_cmap, find_glyph };
struct find_struct {
enum things_to_find findtype;
int val;
int loadsyms_offset;
int loadsyms_count;
int *extraval;
long color;
const char *unicode_val; /* U+NNNN format */
void (*callback)(int glyph, struct find_struct *);
enum reserved_activities restype;
genericptr_t reserved;
};
const struct find_struct zero_find = { 0 };
struct glyphid_cache_t {
int glyphnum;
char *id;
};
struct glyphid_cache_t *glyphid_cache;
struct find_struct glyphcache_find, to_custom_symbol_find;
static void to_custom_symset_entry_callback(int glyph, struct find_struct *findwhat);
static int unicode_val(const char *cp);
static int parse_id(const char *id, struct find_struct *findwhat);
static int glyph_find_core(const char *id, struct find_struct *findwhat);
static char *fix_glyphname(char *str);
static int32_t rgbstr_to_int32(const char *rgbstr);
boolean closest_color(uint32_t lcolor, uint32_t *closecolor, int *clridx);
static void
to_custom_symset_entry_callback(int glyph, struct find_struct *findwhat)
{
#ifdef NO_PARSING_SYMSET
glyph_map *gm = &glyphmap[glyph];
#endif
uint8 utf8str[6] = { 0, 0, 0, 0, 0, 0 };
int uval;
if (!findwhat->unicode_val)
return;
if (findwhat->extraval)
*findwhat->extraval = glyph;
uval = unicode_val(findwhat->unicode_val);
if (unicodeval_to_utf8str(uval, utf8str, sizeof utf8str)) {
#ifdef NO_PARSING_SYMSET
set_map_u(gm, utf8str,
(findwhat->color != 0L) ? findwhat->color : 0L);
#endif
add_custom_urep_entry(known_handling[H_UTF8], glyph,
utf8str, findwhat->color, UNICODESET);
}
}
/*
* Return value:
* 1 = success
* 0 = failure
*/
int
glyphrep_to_custom_map_entries(const char *op, int *glyphptr)
{
to_custom_symbol_find = zero_find;
char buf[BUFSZ], *c_glyphid, *c_unicode, *c_rgb, *cp;
int milestone, reslt = 0;
long rgb;
boolean slash = FALSE;
if (!glyphid_cache)
reslt = 1; /* for debugger use only; no cache available */
milestone = 0;
Snprintf(buf, sizeof buf, "%s", op);
c_unicode = c_rgb = (char *) 0;
c_glyphid = cp = buf;
while (*cp) {
if ((*cp == '/') || (*cp == ':')) {
*cp = '\0';
milestone++;
slash = TRUE;
}
cp++;
if (slash) {
if (milestone < 2)
c_unicode = cp;
else
c_rgb = cp;
slash = FALSE;
}
}
/* some sanity checks */
if (c_glyphid && *c_glyphid == ' ')
c_glyphid++;
if (c_unicode && *c_unicode == ' ')
c_unicode++;
if (c_rgb && *c_rgb == ' ')
c_rgb++;
if (c_unicode && (*c_unicode == 'U' || *c_unicode == 'u')
&& (c_unicode[1] == '+')) {
/* unicode = unicode_val(c_unicode); */
if ((rgb = rgbstr_to_int32(c_rgb)) != -1L || !c_rgb) {
to_custom_symbol_find.unicode_val = c_unicode;
/* if the color 0 is an actual color, as opposed to just "not set"
we set a marker bit outside the 24-bit range to indicate a
valid color value 0. That allows valid color 0, but allows a
simple checking for 0 to detect "not set". The window port that
implements the color switch, needs to either check that bit
or appropriately mask colors with 0xFFFFFF. */
to_custom_symbol_find.color = (rgb == -1 || !c_rgb) ? 0L
: (rgb == 0L) ? (0 & 0x1000000)
: rgb;
to_custom_symbol_find.extraval = glyphptr;
to_custom_symbol_find.callback = to_custom_symset_entry_callback;
reslt = glyph_find_core(c_glyphid, &to_custom_symbol_find);
return reslt;
}
}
return 0;
}
static int32_t
rgbstr_to_int32(const char *rgbstr)
{
int r, gn, b, milestone = 0;
char *cp, *c_r,*c_g,*c_b;
int32_t rgb = 0;
char buf[BUFSZ];
boolean dash = FALSE;
r = gn = b = 0;
c_g = c_b = (char *) 0;
Snprintf(buf, sizeof buf, "%s", rgbstr);
c_r = cp = buf;
while (*cp) {
if (digit(*cp) || *cp == '-') {
if (*cp == '-') {
*cp = '\0';
milestone++;
dash = TRUE;
}
cp++;
if (dash) {
if (milestone < 2)
c_g = cp;
else
c_b = cp;
dash = FALSE;
}
} else {
return -1L;
}
}
/* sanity checks */
if (c_r && c_g && c_b
&& (strlen(c_r) > 0 && strlen(c_r) < 4)
&& (strlen(c_g) > 0 && strlen(c_g) < 4)
&& (strlen(c_b) > 0 && strlen(c_b) < 4)) {
r = atoi(c_r);
gn = atoi(c_g);
b = atoi(c_b);
rgb = (r << 16) | (gn << 8) | (b << 0);
return rgb;
}
return -1L;
}
static char *
fix_glyphname(char *str)
{
char *c;
for (c = str; *c; c++) {
if (*c >= 'A' && *c <= 'Z')
*c += (char) ('a' - 'A');
else if (*c >= '0' && *c <= '9')
;
else if (*c < 'a' || *c > 'z')
*c = '_';
}
return str;
}
static int
unicode_val(const char *cp)
{
const char *dp;
int cval = 0, dcount, unicode = 0;
static const char hex[] = "00112233445566778899aAbBcCdDeEfF";
if (cp && *cp) {
cval = dcount = 0;
if ((unicode = ((*cp == 'U' || *cp == 'u') && cp[1] == '+')) && cp[2]
&& (dp = index(hex, cp[2])) != 0) {
cp += 2; /* move past the 'U' and '+' */
do {
cval = (cval * 16) + ((int) (dp - hex) / 2);
} while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 7);
}
}
return cval;
}
int
set_map_u(glyph_map *gm, const uint8 *utf8str, long ucolor)
{
static uint32_t closecolor = 0;
static int clridx = 0;
if (gm) {
if (gm->u == 0) {
gm->u = (struct unicode_representation *) alloc(sizeof *gm->u);
gm->u->utf8str = 0;
}
if (gm->u->utf8str != 0) {
free(gm->u->utf8str);
gm->u->utf8str = 0;
}
gm->u->utf8str = (uint8 *) dupstr((const char *) utf8str);
gm->u->ucolor = ucolor;
if (closest_color(ucolor, &closecolor, &clridx))
gm->u->u256coloridx = clridx;
else
gm->u->u256coloridx = 0;
return 1;
}
return 0;
}
static int
glyph_find_core(const char *id, struct find_struct *findwhat)
{
int glyph;
boolean do_callback, end_find = FALSE;
if (parse_id(id, findwhat)) {
if (findwhat->findtype == find_glyph) {
(findwhat->callback)(findwhat->val, findwhat);
} else {
for (glyph = 0; glyph < MAX_GLYPH; ++glyph) {
do_callback = FALSE;
switch (findwhat->findtype) {
case find_cmap:
if (glyph_to_cmap(glyph) == findwhat->val)
do_callback = TRUE;
break;
case find_pm:
if (glyph_is_monster(glyph)
&& mons[glyph_to_mon(glyph)].mlet == findwhat->val)
do_callback = TRUE;
break;
case find_oc:
if (glyph_is_object(glyph)
&& glyph_to_obj(glyph) == findwhat->val)
do_callback = TRUE;
break;
case find_glyph:
if (glyph == findwhat->val) {
do_callback = TRUE;
end_find = TRUE;
}
break;
case find_nothing:
default:
end_find = TRUE;
break;
}
if (do_callback)
(findwhat->callback)(glyph, findwhat);
if (end_find)
break;
}
}
return 1;
}
return 0;
}
/*
When we start to process a config file or a symbol file,
that might have G_ entries, generating all 9000+ glyphid
for comparison repeatedly each time we encounter a G_
entry to decipher, then comparing against them, is obviously
extremely performance-poor.
Setting aside the "comparison" part for now (that has to be
done in some manner), we can likely do something about the
repeated "generation" of the names for parsing prior to the
actual comparison part by generating them once, ahead of the
bulk of the potential parsings. We can later free up
all the memory those names consumed once the bulk parsing is
overwith.
*/
void fill_glyphid_cache(void)
{
int glyph, reslt = 0;
if (!glyphid_cache) {
glyphid_cache = (struct glyphid_cache_t *) alloc(
MAX_GLYPH * sizeof(struct glyphid_cache_t));
for (glyph = 0; glyph < MAX_GLYPH; ++glyph) {
glyphid_cache[glyph].glyphnum = 0;
glyphid_cache[glyph].id = (char *) 0;
}
}
if (glyphid_cache) {
glyphcache_find = zero_find;
glyphcache_find.findtype = find_nothing;
glyphcache_find.reserved = (genericptr_t) glyphid_cache;
glyphcache_find.restype = res_fill_cache;
reslt = parse_id((char *) 0, &glyphcache_find);
if (!reslt) {
free_glyphid_cache();
glyphid_cache = (struct glyphid_cache_t *) 0;
}
}
}
void free_glyphid_cache(void)
{
int glyph;
if (!glyphid_cache)
return;
for (glyph = 0; glyph < MAX_GLYPH; ++glyph) {
if (glyphid_cache[glyph].id) {
free(glyphid_cache[glyph].id);
glyphid_cache[glyph].id = (char *) 0;
}
}
free(glyphid_cache);
glyphid_cache = (struct glyphid_cache_t *) 0;
}
boolean
glyphid_cache_status(void)
{
return (glyphid_cache != 0);
}
void
free_all_glyphmap_u(void)
{
int glyph;
for (glyph = 0; glyph < MAX_GLYPH; ++glyph) {
if (glyphmap[glyph].u) {
if (glyphmap[glyph].u->utf8str) {
free(glyphmap[glyph].u->utf8str);
glyphmap[glyph].u->utf8str = 0;
}
free(glyphmap[glyph].u);
glyphmap[glyph].u = 0;
}
}
}
/* helper routine if a window port wants to embed any UTF-8 sequences
for the glyph representation in the string in place of the \GNNNNNNNN
reference */
char *
mixed_to_utf8(char *buf, size_t bufsz, const char *str, int *retflags)
{
char *put = buf;
glyph_info glyphinfo = nul_glyphinfo;
if (!str)
return strcpy(buf, "");
while (*str && put < (buf + bufsz) - 1) {
if (*str == '\\') {
int dcount, so, gv;
const char *save_str;
save_str = str++;
switch (*str) {
case 'G': /* glyph value \GXXXXNNNN*/
if ((dcount = decode_glyph(str + 1, &gv))) {
str += (dcount + 1);
map_glyphinfo(0, 0, gv, 0, &glyphinfo);
if (glyphinfo.gm.u && glyphinfo.gm.u->utf8str) {
uint8 *ucp = glyphinfo.gm.u->utf8str;
while (*ucp && put < (buf + bufsz) - 1)
*put++ = *ucp++;
if (retflags)
*retflags = 1;
} else {
so = glyphinfo.gm.sym.symidx;
*put++ = g.showsyms[so];
if (retflags)
*retflags = 0;
}
/* 'str' is ready for the next loop iteration and
'*str' should not be copied at the end of this
iteration */
continue;
} else {
/* possible forgery - leave it the way it is */
str = save_str;
}
break;
case '\\':
break;
case '\0':
/* String ended with '\\'. This can happen when someone
names an object with a name ending with '\\', drops the
named object on the floor nearby and does a look at all
nearby objects. */
/* brh - should we perhaps not allow things to have names
that contain '\\' */
str = save_str;
break;
}
}
if (put < (buf + bufsz) - 1)
*put++ = *str++;
}
*put = '\0';
return buf;
}
void
dump_all_glyphids(FILE *fp)
{
struct find_struct dump_glyphid_find = zero_find;
dump_glyphid_find.findtype = find_nothing;
dump_glyphid_find.reserved = (genericptr_t) fp;
dump_glyphid_find.restype = res_dump_glyphids;
(void) parse_id((char *) 0, &dump_glyphid_find);
}
int
match_glyph(char *buf)
{
char workbuf[BUFSZ];
/* buf contains a G_ glyph reference, not an S_ symbol.
There could be an R-G-B color attached too.
Let's get a copy to work with. */
Snprintf(workbuf, sizeof workbuf, "%s", buf); /* get a copy */
return glyphrep(workbuf);
}
int
glyphrep(const char *op)
{
int reslt = 0, glyph = NO_GLYPH;
if (!glyphid_cache)
reslt = 1; /* for debugger use only; no cache available */
reslt = glyphrep_to_custom_map_entries(op, &glyph);
if (reslt)
return 1;
return 0;
}
int
add_custom_urep_entry(const char *customization_name, int glyphidx,
const uint8 *utf8str, long ucolor,
enum graphics_sets which_set)
{
static uint32_t closecolor = 0;
static int clridx = 0;
int retval = 0;
struct symset_customization *gdc = &g.sym_customizations[which_set];
struct customization_detail *details, *prev = 0, *newdetails = 0,
*lastdetail = 0;
if (!gdc->details) {
gdc->customization_name = dupstr(customization_name);
gdc->custtype = custom_ureps;
gdc->details = 0;
}
details = find_matching_symset_customization(customization_name, custom_symbols,
which_set);
if (details) {
while (details) {
if (details->content.urep.glyphidx == glyphidx) {
if (details->content.urep.u.utf8str)
free(details->content.urep.u.utf8str);
details->content.urep.u.utf8str =
(uint8 *) dupstr((const char *) utf8str);
details->content.urep.u.ucolor = ucolor;
if (closest_color(ucolor, &closecolor, &clridx))
details->content.urep.u.u256coloridx = clridx;
else
details->content.urep.u.u256coloridx = 0;
return 1;
}
prev = details;
details = details->next;
}
}
/* create new details entry */
newdetails = (struct customization_detail *) alloc(
sizeof(struct customization_detail));
newdetails->content.urep.glyphidx = glyphidx;
newdetails->content.urep.u.utf8str =
(uint8 *) dupstr((const char *) utf8str);
newdetails->content.urep.u.ucolor = ucolor;
if (closest_color(ucolor, &closecolor, &clridx))
newdetails->content.urep.u.u256coloridx = clridx;
else
newdetails->content.urep.u.u256coloridx = 0;
newdetails->next = (struct customization_detail *) 0;
if (!details && prev) {
prev->next = newdetails;
retval = 1;
} else if (!gdc->details) {
gdc->details = newdetails;
retval = 1;
} else {
lastdetail = gdc->details;
while (lastdetail) {
prev = lastdetail;
lastdetail = lastdetail->next;
}
prev->next = newdetails;
retval = 1;
}
gdc->count++;
return retval;
}
static int
parse_id(const char *id, struct find_struct *findwhat)
{
FILE *fp = (FILE *) 0;
int i = 0, j, mnum, glyph, pm_offset = 0, oc_offset = 0, cmap_offset = 0,
pm_count = 0, oc_count = 0, cmap_count = 0;
boolean skip_base = FALSE, skip_this_one, dump_ids = FALSE,
filling_cache = FALSE, is_S = FALSE, is_G = FALSE;
char buf[5][QBUFSZ];
if (findwhat->findtype == find_nothing && findwhat->restype) {
if (findwhat->restype == res_dump_glyphids) {
if (findwhat->reserved) {
fp = (FILE *) findwhat->reserved;
dump_ids = TRUE;
} else {
return 0;
}
}
if (findwhat->restype == res_fill_cache) {
if (findwhat->reserved
&& findwhat->reserved == (genericptr_t) glyphid_cache) {
filling_cache = TRUE;
} else {
return 0;
}
}
}
is_G = (id && id[0] == 'G' && id[1] == '_');
is_S = (id && id[0] == 'S' && id[1] == '_');
if ((is_G && !glyphid_cache) || filling_cache || dump_ids || is_S) {
while (loadsyms[i].range) {
if (!pm_offset && loadsyms[i].range == SYM_MON)
pm_offset = i;
if (!pm_count && pm_offset && loadsyms[i].range != SYM_MON)
pm_count = i - pm_offset;
if (!oc_offset && loadsyms[i].range == SYM_OC)
oc_offset = i;
if (!oc_count && oc_offset && loadsyms[i].range != SYM_OC)
oc_count = i - oc_offset;
if (!cmap_offset && loadsyms[i].range == SYM_PCHAR)
cmap_offset = i;
if (!cmap_count && cmap_offset && loadsyms[i].range != SYM_PCHAR)
cmap_count = i - cmap_offset;
i++;
}
}
if (is_G || filling_cache || dump_ids) {
/* individual matching glyph entries */
for (glyph = 0; glyph < MAX_GLYPH; ++glyph) {
if (!filling_cache && id && glyphid_cache) {
if (!glyphid_cache[glyph].id) /* skipped during cache fill */
continue;
if (!strcmpi(id, glyphid_cache[glyph].id)) {
findwhat->findtype = find_glyph;
findwhat->val = glyph;
findwhat->loadsyms_offset = 0;
return 1;
}
} else {
skip_base = FALSE;
skip_this_one = FALSE;
buf[0][0] = buf[1][0] = buf[2][0] = buf[3][0] = buf[4][0] =
'\0';
if (glyph_is_monster(glyph)) {
/* buf[2] will hold the distinguishing prefix */
/* buf[3] will hold the base name */
buf[2][0] = '\0';
Snprintf(buf[3], sizeof buf[3], "%s",
monsdump[glyph_to_mon(glyph)].nm);
if (glyph_is_normal_male_monster(glyph)) {
Snprintf(buf[2], sizeof buf[2], "male_");
} else if (glyph_is_normal_female_monster(glyph)) {
Snprintf(buf[2], sizeof buf[2], "female_");
} else if (glyph_is_ridden_male_monster(glyph)) {
Snprintf(buf[2], sizeof buf[2], "ridden_male_");
} else if (glyph_is_ridden_female_monster(glyph)) {
Snprintf(buf[2], sizeof buf[2], "ridden_female_");
} else if (glyph_is_detected_male_monster(glyph)) {
Snprintf(buf[2], sizeof buf[2], "detected_male_");
} else if (glyph_is_detected_female_monster(glyph)) {
Snprintf(buf[2], sizeof buf[2], "detected_female_");
} else if (glyph_is_male_pet(glyph)) {
Snprintf(buf[2], sizeof buf[2], "pet_male_");
} else if (glyph_is_female_pet(glyph)) {
Snprintf(buf[2], sizeof buf[2], "pet_female_");
}
Snprintf(buf[1], sizeof buf[1], "%s%s", buf[2], buf[3]);
} else if (glyph_is_body(glyph)) {
/* buf[2] will hold the distinguishing prefix */
/* buf[3] will hold the base name */
buf[2][0] = '\0';
Snprintf(buf[3], sizeof buf[3], "%s",
monsdump[glyph_to_body_corpsenm(glyph)].nm);
if (glyph_is_body_piletop(glyph)) {
Snprintf(buf[2], sizeof buf[2], "piletop_body_");
} else {
Snprintf(buf[2], sizeof buf[2], "body_");
}
Snprintf(buf[1], sizeof buf[1], "%s%s", buf[2], buf[3]);
} else if (glyph_is_statue(glyph)) {
/* buf[2] will hold the distinguishing prefix */
/* buf[3] will hold the base name */
buf[2][0] = '\0';
Snprintf(buf[3], sizeof buf[3], "%s",
monsdump[glyph_to_statue_corpsenm(glyph)].nm);
if (glyph_is_fem_statue_piletop(glyph)) {
Snprintf(buf[2], sizeof buf[2],
"piletop_statue_of_female_");
} else if (glyph_is_fem_statue(glyph)) {
Snprintf(buf[2], sizeof buf[2], "statue_of_female_");
} else if (glyph_is_male_statue_piletop(glyph)) {
Snprintf(buf[2], sizeof buf[2],
"piletop_statue_of_male_");
} else if (glyph_is_male_statue(glyph)) {
Snprintf(buf[2], sizeof buf[2], "statue_of_male_");
}
Snprintf(buf[1], sizeof buf[1], "%s%s", buf[2], buf[3]);
} else if (glyph_is_object(glyph)) {
i = glyph_to_obj(glyph);
/* buf[2] will hold the distinguishing prefix */
/* buf[3] will hold the base name */
buf[2][0] = '\0';
if (((i > SCR_STINKING_CLOUD) && (i < SCR_MAIL))
|| ((i > WAN_LIGHTNING) && (i < GOLD_PIECE)))
skip_this_one = TRUE;
if (!skip_this_one) {
if ((i >= WAN_LIGHT) && (i <= WAN_LIGHTNING))
Snprintf(buf[2], sizeof buf[2], "wand of ");
else if ((i >= SPE_DIG) && (i < SPE_BLANK_PAPER))
Snprintf(buf[2], sizeof buf[2], "spellbook of ");
else if ((i >= SCR_ENCHANT_ARMOR)
&& (i <= SCR_STINKING_CLOUD))
Snprintf(buf[2], sizeof buf[2], "scroll of ");
else if ((i >= POT_GAIN_ABILITY) && (i <= POT_WATER))
Snprintf(buf[2], sizeof buf[2], "%s",
(i == POT_WATER) ? "flask of n"
: "potion of ");
else if ((i >= RIN_ADORNMENT)
&& (i <= RIN_PROTECTION_FROM_SHAPE_CHAN))
Snprintf(buf[2], sizeof buf[2], "ring of ");
else if (i == LAND_MINE)
Snprintf(buf[2], sizeof buf[2], "unset ");
Snprintf(buf[3], sizeof buf[3], "%s",
(i == SCR_BLANK_PAPER) ? "blank scroll"
: (i == SPE_BLANK_PAPER)
? "blank spellbook"
: obj_descr[i].oc_name);
Snprintf(buf[1], sizeof buf[1], "%s%s%s",
glyph_is_normal_piletop_obj(glyph)
? "piletop_"
: "",
buf[2], buf[3]);
}
} else if (glyph_is_cmap(glyph) || glyph_is_cmap_zap(glyph)
|| glyph_is_swallow(glyph)
|| glyph_is_explosion(glyph)) {
int cmap = -1;
buf[2][0] =
'\0'; /* buf[2] will hold the distinguishing prefix */
buf[3][0] = '\0'; /* buf[3] will hold the base name */
buf[4][0] =
'\0'; /* buf[4] will hold the distinguishing suffix */
if (glyph == GLYPH_CMAP_OFF) {
cmap = S_stone;
Strcpy(buf[3], "stone substrate");
skip_base = TRUE;
} else if (glyph_is_cmap_gehennom(glyph)) {
cmap = (glyph - GLYPH_CMAP_GEH_OFF) + S_vwall;
Snprintf(buf[4], sizeof buf[4], "%s", "_gehennom");
} else if (glyph_is_cmap_knox(glyph)) {
cmap = (glyph - GLYPH_CMAP_KNOX_OFF) + S_vwall;
Snprintf(buf[4], sizeof buf[4], "%s", "_knox");
} else if (glyph_is_cmap_main(glyph)) {
cmap = (glyph - GLYPH_CMAP_MAIN_OFF) + S_vwall;
Snprintf(buf[4], sizeof buf[4], "%s", "_main");
} else if (glyph_is_cmap_mines(glyph)) {
cmap = (glyph - GLYPH_CMAP_MINES_OFF) + S_vwall;
Snprintf(buf[4], sizeof buf[4], "%s", "_mines");
} else if (glyph_is_cmap_sokoban(glyph)) {
cmap = (glyph - GLYPH_CMAP_SOKO_OFF) + S_vwall;
Snprintf(buf[4], sizeof buf[4], "%s", "_sokoban");
} else if (glyph_is_cmap_a(glyph)) {
cmap = (glyph - GLYPH_CMAP_A_OFF) + S_ndoor;
} else if (glyph_is_cmap_altar(glyph)) {
const char *altar_text[] = {
"unaligned", "chaotic", "neutral",
"lawful", "other",
};
j = (glyph - GLYPH_ALTAR_OFF);
cmap = S_altar;
if (j != altar_other) {
Snprintf(buf[2], sizeof buf[2], "%s_",
altar_text[j]);
} else {
Strcpy(buf[3], "altar other");
skip_base = TRUE;
}
} else if (glyph_is_cmap_b(glyph)) {
cmap = (glyph - GLYPH_CMAP_B_OFF) + S_grave;
} else if (glyph_is_cmap_zap(glyph)) {
static const char *zap_texts[] = {
"missile", "fire", "frost", "sleep",
"death", "lightning", "poison gas", "acid"
};
j = (glyph - GLYPH_ZAP_OFF);
cmap = (j % 4) + S_vbeam;
Snprintf(buf[2], sizeof buf[2], "%s",
loadsyms[cmap + cmap_offset].name + 2);
Snprintf(buf[3], sizeof buf[3], "%s zap %s",
zap_texts[j / 4], fix_glyphname(buf[2]));
buf[2][0] = '\0';
skip_base = TRUE;
} else if (glyph_is_cmap_c(glyph)) {
cmap = (glyph - GLYPH_CMAP_C_OFF) + S_digbeam;
} else if (glyph_is_swallow(glyph)) {
static const char *swallow_texts[] = {
"top left", "top center", "top right",
"middle left", "middle right", "bottom left",
"bottom center", "bottom right",
};
j = glyph - GLYPH_SWALLOW_OFF;
cmap = glyph_to_swallow(glyph);
mnum = j / ((S_sw_br - S_sw_tl) + 1);
i = cmap - S_sw_tl;
Snprintf(buf[3], sizeof buf[3], "%s %s %s", "swallow",
monsdump[mnum].nm, swallow_texts[cmap]);
skip_base = TRUE;
} else if (glyph_is_explosion(glyph)) {
int expl;
static const char *expl_type_texts[] = {
"dark", "noxious", "muddy", "wet",
"magical", "fiery", "frosty",
};
static const char *expl_texts[] = {
"tl", "tc", "tr", "ml", "mc",
"mr", "bl", "bc", "br",
};
j = glyph - GLYPH_EXPLODE_OFF;
expl = j / ((S_expl_br - S_expl_tl) + 1);
cmap =
(j % ((S_expl_br - S_expl_tl) + 1)) + S_expl_tl;
i = cmap - S_expl_tl;
Snprintf(buf[2], sizeof buf[2], "%s ",
expl_type_texts[expl]);
Snprintf(buf[3], sizeof buf[3], "%s%s", "expl_",
expl_texts[i]);
skip_base = TRUE;
}
if (!skip_base) {
if (cmap >= 0 && cmap < MAXPCHARS) {
Snprintf(buf[3], sizeof buf[3], "%s",
loadsyms[cmap + cmap_offset].name + 2);
}
}
Snprintf(buf[1], sizeof buf[1], "%s%s%s", buf[2], buf[3],
buf[4]);
} else if (glyph_is_invisible(glyph)) {
Snprintf(buf[1], sizeof buf[1], "%s", "invisible");
} else if (glyph_is_nothing(glyph)) {
Snprintf(buf[1], sizeof buf[1], "%s", "nothing");
} else if (glyph_is_unexplored(glyph)) {
Snprintf(buf[1], sizeof buf[1], "%s", "unexplored");
} else if (glyph_is_warning(glyph)) {
j = glyph - GLYPH_WARNING_OFF;
Snprintf(buf[1], sizeof buf[1], "%s%d", "warning", j);
}
if (!skip_this_one) {
Snprintf(buf[0], sizeof buf[0], "G_%s",
fix_glyphname(buf[1]));
if (dump_ids) {
Fprintf(fp, "(%04d) %s\n", glyph, buf[0]);
} else if (filling_cache) {
glyphid_cache[glyph].glyphnum = glyph;
glyphid_cache[glyph].id = dupstr(buf[0]);
} else if (id) {
if (!strcmpi(id, buf[0])) {
findwhat->findtype = find_glyph;
findwhat->val = glyph;
findwhat->loadsyms_offset = 0;
return 1;
}
}
}
} /* not glyphid_cache */
}
} else if (is_S) {
/* cmap entries */
for (i = 0; i < cmap_count; ++i) {
if (!strcmpi(loadsyms[i + cmap_offset].name + 2, id + 2)) {
findwhat->findtype = find_cmap;
findwhat->val = i;
findwhat->loadsyms_offset = i + cmap_offset;
return 1;
}
}
/* objclass entries */
for (i = 0; i < oc_count; ++i) {
if (!strcmpi(loadsyms[i + oc_offset].name + 2, id + 2)) {
findwhat->findtype = find_oc;
findwhat->val = i;
findwhat->loadsyms_offset = i + oc_offset;
return 1;
}
}
/* permonst entries */
for (i = 0; i <= pm_count; ++i) {
if (!strcmpi(loadsyms[i + pm_offset].name + 2, id + 2)) {
findwhat->findtype = find_pm;
findwhat->val = i + 1; /* starts at 1 */
findwhat->loadsyms_offset = i + pm_offset;
return 1;
}
}
}
if (dump_ids || filling_cache)
return 1;
findwhat->findtype = find_nothing;
findwhat->val = 0;
findwhat->loadsyms_offset = 0;
return 0;
}
static struct {
int index;
uint32_t value;
} color_definitions_256[] = {
/* color values are from unnethack */
{ 16, 0x000000 }, { 17, 0x00005f }, { 18, 0x000087 },
{ 19, 0x0000af }, { 20, 0x0000d7 }, { 21, 0x0000ff },
{ 22, 0x005f00 }, { 23, 0x005f5f }, { 24, 0x005f87 },
{ 25, 0x005faf }, { 26, 0x005fd7 }, { 27, 0x005fff },
{ 28, 0x008700 }, { 29, 0x00875f }, { 30, 0x008787 },
{ 31, 0x0087af }, { 32, 0x0087d7 }, { 33, 0x0087ff },
{ 34, 0x00af00 }, { 35, 0x00af5f }, { 36, 0x00af87 },
{ 37, 0x00afaf }, { 38, 0x00afd7 }, { 39, 0x00afff },
{ 40, 0x00d700 }, { 41, 0x00d75f }, { 42, 0x00d787 },
{ 43, 0x00d7af }, { 44, 0x00d7d7 }, { 45, 0x00d7ff },
{ 46, 0x00ff00 }, { 47, 0x00ff5f }, { 48, 0x00ff87 },
{ 49, 0x00ffaf }, { 50, 0x00ffd7 }, { 51, 0x00ffff },
{ 52, 0x5f0000 }, { 53, 0x5f005f }, { 54, 0x5f0087 },
{ 55, 0x5f00af }, { 56, 0x5f00d7 }, { 57, 0x5f00ff },
{ 58, 0x5f5f00 }, { 59, 0x5f5f5f }, { 60, 0x5f5f87 },
{ 61, 0x5f5faf }, { 62, 0x5f5fd7 }, { 63, 0x5f5fff },
{ 64, 0x5f8700 }, { 65, 0x5f875f }, { 66, 0x5f8787 },
{ 67, 0x5f87af }, { 68, 0x5f87d7 }, { 69, 0x5f87ff },
{ 70, 0x5faf00 }, { 71, 0x5faf5f }, { 72, 0x5faf87 },
{ 73, 0x5fafaf }, { 74, 0x5fafd7 }, { 75, 0x5fafff },
{ 76, 0x5fd700 }, { 77, 0x5fd75f }, { 78, 0x5fd787 },
{ 79, 0x5fd7af }, { 80, 0x5fd7d7 }, { 81, 0x5fd7ff },
{ 82, 0x5fff00 }, { 83, 0x5fff5f }, { 84, 0x5fff87 },
{ 85, 0x5fffaf }, { 86, 0x5fffd7 }, { 87, 0x5fffff },
{ 88, 0x870000 }, { 89, 0x87005f }, { 90, 0x870087 },
{ 91, 0x8700af }, { 92, 0x8700d7 }, { 93, 0x8700ff },
{ 94, 0x875f00 }, { 95, 0x875f5f }, { 96, 0x875f87 },
{ 97, 0x875faf }, { 98, 0x875fd7 }, { 99, 0x875fff },
{ 100, 0x878700 }, { 101, 0x87875f }, { 102, 0x878787 },
{ 103, 0x8787af }, { 104, 0x8787d7 }, { 105, 0x8787ff },
{ 106, 0x87af00 }, { 107, 0x87af5f }, { 108, 0x87af87 },
{ 109, 0x87afaf }, { 110, 0x87afd7 }, { 111, 0x87afff },
{ 112, 0x87d700 }, { 113, 0x87d75f }, { 114, 0x87d787 },
{ 115, 0x87d7af }, { 116, 0x87d7d7 }, { 117, 0x87d7ff },
{ 118, 0x87ff00 }, { 119, 0x87ff5f }, { 120, 0x87ff87 },
{ 121, 0x87ffaf }, { 122, 0x87ffd7 }, { 123, 0x87ffff },
{ 124, 0xaf0000 }, { 125, 0xaf005f }, { 126, 0xaf0087 },
{ 127, 0xaf00af }, { 128, 0xaf00d7 }, { 129, 0xaf00ff },
{ 130, 0xaf5f00 }, { 131, 0xaf5f5f }, { 132, 0xaf5f87 },
{ 133, 0xaf5faf }, { 134, 0xaf5fd7 }, { 135, 0xaf5fff },
{ 136, 0xaf8700 }, { 137, 0xaf875f }, { 138, 0xaf8787 },
{ 139, 0xaf87af }, { 140, 0xaf87d7 }, { 141, 0xaf87ff },
{ 142, 0xafaf00 }, { 143, 0xafaf5f }, { 144, 0xafaf87 },
{ 145, 0xafafaf }, { 146, 0xafafd7 }, { 147, 0xafafff },
{ 148, 0xafd700 }, { 149, 0xafd75f }, { 150, 0xafd787 },
{ 151, 0xafd7af }, { 152, 0xafd7d7 }, { 153, 0xafd7ff },
{ 154, 0xafff00 }, { 155, 0xafff5f }, { 156, 0xafff87 },
{ 157, 0xafffaf }, { 158, 0xafffd7 }, { 159, 0xafffff },
{ 160, 0xd70000 }, { 161, 0xd7005f }, { 162, 0xd70087 },
{ 163, 0xd700af }, { 164, 0xd700d7 }, { 165, 0xd700ff },
{ 166, 0xd75f00 }, { 167, 0xd75f5f }, { 168, 0xd75f87 },
{ 169, 0xd75faf }, { 170, 0xd75fd7 }, { 171, 0xd75fff },
{ 172, 0xd78700 }, { 173, 0xd7875f }, { 174, 0xd78787 },
{ 175, 0xd787af }, { 176, 0xd787d7 }, { 177, 0xd787ff },
{ 178, 0xd7af00 }, { 179, 0xd7af5f }, { 180, 0xd7af87 },
{ 181, 0xd7afaf }, { 182, 0xd7afd7 }, { 183, 0xd7afff },
{ 184, 0xd7d700 }, { 185, 0xd7d75f }, { 186, 0xd7d787 },
{ 187, 0xd7d7af }, { 188, 0xd7d7d7 }, { 189, 0xd7d7ff },
{ 190, 0xd7ff00 }, { 191, 0xd7ff5f }, { 192, 0xd7ff87 },
{ 193, 0xd7ffaf }, { 194, 0xd7ffd7 }, { 195, 0xd7ffff },
{ 196, 0xff0000 }, { 197, 0xff005f }, { 198, 0xff0087 },
{ 199, 0xff00af }, { 200, 0xff00d7 }, { 201, 0xff00ff },
{ 202, 0xff5f00 }, { 203, 0xff5f5f }, { 204, 0xff5f87 },
{ 205, 0xff5faf }, { 206, 0xff5fd7 }, { 207, 0xff5fff },
{ 208, 0xff8700 }, { 209, 0xff875f }, { 210, 0xff8787 },
{ 211, 0xff87af }, { 212, 0xff87d7 }, { 213, 0xff87ff },
{ 214, 0xffaf00 }, { 215, 0xffaf5f }, { 216, 0xffaf87 },
{ 217, 0xffafaf }, { 218, 0xffafd7 }, { 219, 0xffafff },
{ 220, 0xffd700 }, { 221, 0xffd75f }, { 222, 0xffd787 },
{ 223, 0xffd7af }, { 224, 0xffd7d7 }, { 225, 0xffd7ff },
{ 226, 0xffff00 }, { 227, 0xffff5f }, { 228, 0xffff87 },
{ 229, 0xffffaf }, { 230, 0xffffd7 }, { 231, 0xffffff },
{ 232, 0x080808 }, { 233, 0x121212 }, { 234, 0x1c1c1c },
{ 235, 0x262626 }, { 236, 0x303030 }, { 237, 0x3a3a3a },
{ 238, 0x444444 }, { 239, 0x4e4e4e }, { 240, 0x585858 },
{ 241, 0x626262 }, { 242, 0x6c6c6c }, { 243, 0x767676 },
{ 244, 0x808080 }, { 245, 0x8a8a8a }, { 246, 0x949494 },
{ 247, 0x9e9e9e }, { 248, 0xa8a8a8 }, { 249, 0xb2b2b2 },
{ 250, 0xbcbcbc }, { 251, 0xc6c6c6 }, { 252, 0xd0d0d0 },
{ 253, 0xdadada }, { 254, 0xe4e4e4 }, { 255, 0xeeeeee },
};
/** Calculate the color distance between two colors.
*
* Algorithm taken from UnNetHack which took it from
* https://www.compuphase.com/cmetric.htm
**/
static int
color_distance(uint32_t rgb1, uint32_t rgb2)
{
int r1 = (rgb1 >> 16) & 0xFF;
int g1 = (rgb1 >> 8) & 0xFF;
int b1 = (rgb1) &0xFF;
int r2 = (rgb2 >> 16) & 0xFF;
int g2 = (rgb2 >> 8) & 0xFF;
int b2 = (rgb2) &0xFF;
int rmean = (r1 + r2) / 2;
int r = r1 - r2;
int gr = g1 - g2;
int b = b1 - b2;
return ((((512 + rmean) * r * r) >> 8) + 4 * gr * gr
+ (((767 - rmean) * b * b) >> 8));
}
boolean
closest_color(uint32_t lcolor, uint32_t *closecolor, int *clridx)
{
int i, color_index = -1, similar = INT_MAX, current;
boolean retbool = FALSE;
for (i = 0; i < SIZE(color_definitions_256); i++) {
/* look for an exact match */
if (lcolor == color_definitions_256[i].value) {
color_index = i;
break;
}
/* find a close color match */
current = color_distance(lcolor, color_definitions_256[i].value);
if (current < similar) {
color_index = i;
similar = current;
}
}
if (closecolor && clridx && color_index >= 0) {
*closecolor = color_definitions_256[color_index].value;
*clridx = color_definitions_256[color_index].index;
retbool = TRUE;
}
return retbool;
}
#endif /* ENHANCED_SYMBOLS */
#ifdef TEST_GLYPHNAMES
static struct {
int idx;
const char *nm1;
const char *nm2;
} cmapname[MAXPCHARS] = {
#define PCHAR_TILES
#include "defsym.h"
#undef PCHAR_TILES
};
static int glyphs_to_unicode(const char *id, const char *unicode_val,
long clr);
static int find_glyphs(const char *id);
static void just_find_callback(int glyph, struct find_struct *findwhat);
static void to_unicode_callback(int glyph, struct find_struct *findwhat);
static struct customization_detail *find_display_urep_customization(
const char *customization_name, int glyphidx,
enum graphics_sets which_set);
void
test_glyphnames(void)
{
int reslt;
reslt = find_glyphs("G_potion_of_monster_detection");
reslt = find_glyphs("G_piletop_body_chickatrice");
reslt = find_glyphs("G_detected_male_homunculus");
reslt = find_glyphs("S_pool");
reslt = find_glyphs("S_dog");
reslt = glyphs_to_unicode("S_dog", "U+130E6", 0L);
}
static void
just_find_callback(int glyph UNUSED, struct find_struct *findwhat UNUSED)
{
/* nothing */
}
static int
find_glyphs(const char *id)
{
struct find_struct find_only = zero_find;
find_only.unicode_val = 0;
find_only.callback = just_find_callback;
return glyph_find_core(id, &find_only);
}
static void
to_unicode_callback(int glyph UNUSED, struct find_struct *findwhat)
{
int uval;
#ifdef NO_PARSING_SYMSET
glyph_map *gm = &glyphmap[glyph];
#endif
uint8 utf8str[6];
if (!findwhat->unicode_val)
return;
uval = unicode_val(findwhat->unicode_val);
if (unicodeval_to_utf8str(uval, utf8str, sizeof utf8str)) {
#ifdef NO_PARSING_SYMSET
set_map_u(gm, utf8str,
(findwhat->color != 0L) ? findwhat->color : 0L);
#else
#endif
}
}
int
glyphs_to_unicode(const char *id, const char *unicode_val, long clr)
{
struct find_struct to_unicode = zero_find;
to_unicode.unicode_val = unicode_val;
to_unicode.callback = to_unicode_callback;
/* if the color 0 is an actual color, as opposed to just "not set"
we set a marker bit outside the 24-bit range to indicate a
valid color value 0. That allows valid color 0, but allows a
simple checking for 0 to detect "not set". The window port that
implements the color switch, needs to either check that bit
or appropriately mask colors with 0xFFFFFF. */
to_unicode.color = (clr == -1) ? 0L : (clr == 0L) ? (0 & 0x1000000) : clr;
return glyph_find_core(id, &to_unicode);
}
#if 0
struct customization_detail *
find_display_urep_customization(const char *customization_name, int glyphidx,
enum graphics_sets which_set)
{
struct symset_customization *gdc = &g.sym_customizations[which_set];
struct customization_detail *urepdetails;
if ((gdc->custtype == custom_ureps)
|| (strcmp(customization_name, gdc->customization_name) == 0)) {
urepdetails = gdc->details;
while (urepdetails) {
if (urepdetails->content.urep.glyphidx == glyphidx)
return urepdetails;
urepdetails = urepdetails->next;
}
}
return (struct customization_detail *) 0;
}
#endif
#endif /* SOME TEST STUFF */
/* utf8map.c */