From be9e430ebb936a9d315516eb551abfe5a704c48a Mon Sep 17 00:00:00 2001 From: Ingo Paschke Date: Mon, 25 May 2026 19:23:21 +0200 Subject: [PATCH] glyphs: replace open-addressed glyphname hashtable with sorted index The open-addressed glyphname_hashtable stored each canonical "G_xxx" name as a dupstr'd string in its bucket: ~9577 small heap allocations and ~290 KB of resident name strings, on top of ~256 KB of 32768-bucket scaffolding kept at <50% load for probe performance. On classic Mac OS the populate cost (quadratic small-allocation in a fragmented Memory Manager heap) dominated startup time -- many seconds on an SE/30 -- and ~800 KB resident is meaningful on the small machines the port targets. Switch to a sorted (hash, glyph) index sized exactly to the number of named glyphs: struct glyphname_hashtable_entry_t { uint32 hash; int glyphnum; }; populate_glyphname_hashtable() allocates one block of MAX_GLYPH entries (no per-name strings), fills it via compose_glyph_name() + glyph_hash(), and qsort()s ascending by hash. Lookup binary-searches the hash column, then walks any equal-hash neighbours verifying each candidate by reconstructing its canonical name and strcmpi'ing it back -- collisions are rare with 9577 uniformly-distributed 32-bit hashes. Other changes that fall out: * Extract compose_glyph_name() from parse_id's bulk-iteration switch so it is the single source of truth for "glyph number -> canonical name". Called by find_glyph_in_hashtable for collision verification, by populate_glyphname_hashtable, by the --dumpglyphnames path, and by wizcustom_glyphnames. * empty_glyphname_hashtable() reduces to free(ptr); no per-entry strings to release. * Drop find_glyphname_in_hashtable_by_glyphnum (no longer used -- wizcustom_glyphnames iterates compose_glyph_name directly). * Drop the res_fill_hashtable parse_id mode; populate iterates directly. Memory drops from ~800 KB to ~75 KB. One allocation instead of ~9578. Lookup goes from O(1) to O(log N) but N ~ 9577 means ~14 comparisons per probe -- well under what the upstream cache ever cost in practice. Function names (populate_glyphname_hashtable, etc.) are kept for extern.h compatibility; the data structure is now a sorted index, "hashtable" in the names is historical. --dumpglyphnames output is byte-identical. --- src/glyphs.c | 734 +++++++++++++++++++++++---------------------------- 1 file changed, 333 insertions(+), 401 deletions(-) diff --git a/src/glyphs.c b/src/glyphs.c index e38e1baa9..6132844e8 100644 --- a/src/glyphs.c +++ b/src/glyphs.c @@ -11,7 +11,7 @@ extern struct enum_dump objdump[]; #define Fprintf (void) fprintf -enum reserved_activities { res_nothing, res_dump_glyphnames, res_fill_hashtable }; +enum reserved_activities { res_nothing, res_dump_glyphnames }; enum things_to_find { find_nothing, find_pm, find_oc, find_cmap, find_glyph }; struct find_struct { enum things_to_find findtype; @@ -27,20 +27,18 @@ struct find_struct { }; static const struct find_struct zero_find = { 0 }; struct glyphname_hashtable_entry_t { - int glyphnum; - char *id; + uint32 hash; + int glyphnum; /* NO_GLYPH (==MAX_GLYPH) marks an empty bucket */ }; static struct glyphname_hashtable_entry_t *glyphname_hashtable_ptr; -static unsigned glyphname_hashtable_lsize; -static size_t glyphname_hashtable_size; -static struct find_struct glyphname_hashtable_find, to_custom_symbol_find; +static size_t glyphname_hashtable_count; +static struct find_struct to_custom_symbol_find; static const long nonzero_black = CLR_BLACK | NH_BASIC_COLOR; -staticfn void init_glyphname_hashtable(void); -staticfn void add_glyph_to_glyphname_hashtable(int glyphnum, const char *id); -staticfn int find_glyph_in_cache(const char *id); -staticfn char *find_glyphname_in_hashtable_by_glyphnum(int glyphnum); +staticfn int find_glyph_in_hashtable(const char *id); +staticfn int cmp_glyphname_entry(const void *a, const void *b); staticfn uint32 glyph_hash(const char *id); +staticfn int compose_glyph_name(int glyph, char *buf, size_t bufsz); staticfn void to_custom_symset_entry_callback(int glyph, struct find_struct *findwhat); staticfn int parse_id(const char *id, struct find_struct *findwhat); @@ -196,6 +194,239 @@ fix_glyphname(char *str) return str; } +/* Build the canonical "G_xxx" identifier for the given glyph into buf. + * Returns 1 if a name was produced, 0 if this glyph has no canonical name + * (a few unused object indices); buf[0] is set to '\0' in that case. + * Used by parse_id's bulk-iteration paths, by find_glyph_in_hashtable to + * verify hash matches, by populate_glyphname_hashtable to fill the table, + * and by wizcustom_glyphnames. */ +staticfn int +compose_glyph_name(int glyph, char *buf, size_t bufsz) +{ + int i, j, mnum, cmap_offset = 0; + boolean skip_base = FALSE; + const char *buf2, *buf3, *buf4; + char tmpbuf[4][QBUFSZ]; + + if (bufsz < 2) + return 0; + buf[0] = '\0'; + tmpbuf[0][0] = tmpbuf[1][0] = tmpbuf[2][0] = tmpbuf[3][0] = '\0'; + + /* compute cmap_offset (the start of SYM_PCHAR entries in loadsyms[]) */ + i = 0; + while (loadsyms[i].range) { + if (!cmap_offset && loadsyms[i].range == SYM_PCHAR) + cmap_offset = i; + i++; + } + + if (glyph_is_monster(glyph)) { + buf2 = ""; + buf3 = monsdump[glyph_to_mon(glyph)].nm; + if (glyph_is_normal_male_monster(glyph)) { + buf2 = "male_"; + } else if (glyph_is_normal_female_monster(glyph)) { + buf2 = "female_"; + } else if (glyph_is_ridden_male_monster(glyph)) { + buf2 = "ridden_male_"; + } else if (glyph_is_ridden_female_monster(glyph)) { + buf2 = "ridden_female_"; + } else if (glyph_is_detected_male_monster(glyph)) { + buf2 = "detected_male_"; + } else if (glyph_is_detected_female_monster(glyph)) { + buf2 = "detected_female_"; + } else if (glyph_is_male_pet(glyph)) { + buf2 = "pet_male_"; + } else if (glyph_is_female_pet(glyph)) { + buf2 = "pet_female_"; + } + Strcpy(buf, "G_"); + Strcat(buf, buf2); + Strcat(buf, buf3); + } else if (glyph_is_body(glyph)) { + buf2 = glyph_is_body_piletop(glyph) ? "piletop_body_" : "body_"; + buf3 = monsdump[glyph_to_body_corpsenm(glyph)].nm; + Strcpy(buf, "G_"); + Strcat(buf, buf2); + Strcat(buf, buf3); + } else if (glyph_is_statue(glyph)) { + buf2 = glyph_is_fem_statue_piletop(glyph) + ? "piletop_statue_of_female_" + : glyph_is_fem_statue(glyph) + ? "statue_of_female_" + : glyph_is_male_statue_piletop(glyph) + ? "piletop_statue_of_male_" + : glyph_is_male_statue(glyph) + ? "statue_of_male_" + : ""; + buf3 = monsdump[glyph_to_statue_corpsenm(glyph)].nm; + Strcpy(buf, "G_"); + Strcat(buf, buf2); + Strcat(buf, buf3); + } else if (glyph_is_object(glyph)) { + i = glyph_to_obj(glyph); + if (((i > SCR_STINKING_CLOUD) && (i < SCR_MAIL)) + || ((i > WAN_LIGHTNING) && (i < GOLD_PIECE))) { + return 0; + } + if ((i >= WAN_LIGHT) && (i <= WAN_LIGHTNING)) + buf2 = "wand of "; + else if ((i >= SPE_DIG) && (i < SPE_BLANK_PAPER)) + buf2 = "spellbook of "; + else if ((i >= SCR_ENCHANT_ARMOR) && (i <= SCR_STINKING_CLOUD)) + buf2 = "scroll of "; + else if ((i >= POT_GAIN_ABILITY) && (i <= POT_WATER)) + buf2 = (i == POT_WATER) ? "flask of n" : "potion of "; + else if ((i >= RIN_ADORNMENT) && (i <= RIN_PROTECTION_FROM_SHAPE_CHAN)) + buf2 = "ring of "; + else if (i == LAND_MINE) + buf2 = "unset "; + else + buf2 = ""; + buf3 = (i == SCR_BLANK_PAPER) ? "blank scroll" + : (i == SPE_BLANK_PAPER) ? "blank spellbook" + : (i == SLIME_MOLD) ? "slime mold" + : obj_descr[i].oc_name + ? obj_descr[i].oc_name + : obj_descr[i].oc_descr; + Strcpy(buf, "G_"); + if (glyph_is_normal_piletop_obj(glyph)) + Strcat(buf, "piletop_"); + Strcat(buf, buf2); + Strcat(buf, buf3); + } else if (glyph_is_cmap(glyph) || glyph_is_cmap_zap(glyph) + || glyph_is_swallow(glyph) || glyph_is_explosion(glyph)) { + int cmap = -1; + + buf2 = ""; + buf3 = ""; + buf4 = ""; + if (glyph == GLYPH_CMAP_OFF) { + cmap = S_stone; + buf3 = "stone substrate"; + skip_base = TRUE; + } else if (glyph_is_cmap_gehennom(glyph)) { + cmap = (glyph - GLYPH_CMAP_GEH_OFF) + S_vwall; + buf4 = "_gehennom"; + } else if (glyph_is_cmap_knox(glyph)) { + cmap = (glyph - GLYPH_CMAP_KNOX_OFF) + S_vwall; + buf4 = "_knox"; + } else if (glyph_is_cmap_main(glyph)) { + cmap = (glyph - GLYPH_CMAP_MAIN_OFF) + S_vwall; + buf4 = "_main"; + } else if (glyph_is_cmap_mines(glyph)) { + cmap = (glyph - GLYPH_CMAP_MINES_OFF) + S_vwall; + buf4 = "_mines"; + } else if (glyph_is_cmap_sokoban(glyph)) { + cmap = (glyph - GLYPH_CMAP_SOKO_OFF) + S_vwall; + buf4 = "_sokoban"; + } else if (glyph_is_cmap_a(glyph)) { + cmap = (glyph - GLYPH_CMAP_A_OFF) + S_ndoor; + } else if (glyph_is_cmap_altar(glyph)) { + static const char *const altar_text[] = { + "unaligned", "chaotic", "neutral", + "lawful", "other", + }; + + j = (glyph - GLYPH_ALTAR_OFF); + cmap = S_altar; + if (j != altar_other) { + Snprintf(tmpbuf[2], sizeof tmpbuf[2], "%s_", altar_text[j]); + buf2 = tmpbuf[2]; + } else { + buf3 = "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 *const zap_texts[] = { + "missile", "fire", "frost", "sleep", + "death", "lightning", "poison gas", "acid" + }; + + j = (glyph - GLYPH_ZAP_OFF); + cmap = (j % 4) + S_vbeam; + Snprintf(tmpbuf[2], sizeof tmpbuf[2], "%s", + loadsyms[cmap + cmap_offset].name + 2); + Snprintf(tmpbuf[3], sizeof tmpbuf[3], "%s zap %s", + zap_texts[j / 4], fix_glyphname(tmpbuf[2])); + buf3 = tmpbuf[3]; + buf2 = ""; + 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 *const 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); + Strcpy(tmpbuf[3], "swallow "); + Strcat(tmpbuf[3], monsdump[mnum].nm); + Strcat(tmpbuf[3], " "); + Strcat(tmpbuf[3], swallow_texts[cmap]); + buf3 = tmpbuf[3]; + skip_base = TRUE; + } else if (glyph_is_explosion(glyph)) { + static const char *const expl_type_texts[] = { + "dark", "noxious", "muddy", "wet", + "magical", "fiery", "frosty", + }; + static const char *const expl_texts[] = { + "tl", "tc", "tr", "ml", "mc", + "mr", "bl", "bc", "br", + }; + int expl; + + j = glyph - GLYPH_EXPLODE_OFF; + expl = j / ((S_expl_br - S_expl_tl) + 1); + cmap = glyph_to_explosion(glyph) + S_expl_tl; + i = cmap - S_expl_tl; + Snprintf(tmpbuf[2], sizeof tmpbuf[2], "%s ", + expl_type_texts[expl]); + buf2 = tmpbuf[2]; + Snprintf(tmpbuf[3], sizeof tmpbuf[3], "%s%s", "expl_", + expl_texts[i]); + buf3 = tmpbuf[3]; + skip_base = TRUE; + } + if (!skip_base) { + if (cmap >= 0 && cmap < MAXPCHARS) + buf3 = loadsyms[cmap + cmap_offset].name + 2; + } + Strcpy(buf, "G_"); + Strcat(buf, buf2); + Strcat(buf, buf3); + Strcat(buf, buf4); + } else if (glyph_is_invisible(glyph)) { + Strcpy(buf, "G_invisible"); + } else if (glyph_is_nothing(glyph)) { + Strcpy(buf, "G_nothing"); + } else if (glyph_is_unexplored(glyph)) { + Strcpy(buf, "G_unexplored"); + } else if (glyph_is_warning(glyph)) { + j = glyph - GLYPH_WARNING_OFF; + Snprintf(buf, bufsz, "G_%s%d", "warning", j); + } + + if (buf[0] == '\0') + return 0; + if (memchr(buf, '\0', bufsz) == NULL) { + /* defensive: caller passed an undersized buffer */ + buf[bufsz - 1] = '\0'; + return 0; + } + fix_glyphname(buf + 2); + nhUse(mnum); + return 1; +} + int glyph_to_cmap(int glyph) { @@ -283,152 +514,96 @@ glyph_find_core( } /* - 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. + * glyphname_hashtable is a sorted (hash, glyph) index of canonical + * "G_xxx" identifiers. populate_glyphname_hashtable() allocates one + * block of MAX_GLYPH * sizeof(entry) (some entries go unused for the + * scroll/gem appearance gaps), fills it via compose_glyph_name(), and + * sorts ascending by hash. Lookup is bsearch on the hash with + * compose_glyph_name()+strcmpi to disambiguate the rare collision. + * + * The name "hashtable" is historical; this is a sorted array, not an + * open-addressed hash table. The on-disk API in extern.h is kept so + * that callers (wizcmds.c, options.c) don't need to change. + * + * Memory: one alloc, ~75 KB for the 9577 named slots on a typical 5.0 + * build (struct is 8 bytes, no per-name strings). The same data in + * upstream's open-addressed table cost ~256 KB for the buckets plus + * ~290 KB of dupstr'd names plus malloc overhead. + */ - 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 - over with. -*/ +staticfn int +cmp_glyphname_entry(const void *a, const void *b) +{ + uint32 ha = ((const struct glyphname_hashtable_entry_t *) a)->hash; + uint32 hb = ((const struct glyphname_hashtable_entry_t *) b)->hash; + if (ha < hb) + return -1; + if (ha > hb) + return 1; + return 0; +} void populate_glyphname_hashtable(void) { - int reslt = 0; + int glyph; + size_t n = 0; + char buf[BUFSZ]; - if (!glyphname_hashtable_ptr) { - init_glyphname_hashtable(); - } - if (glyphname_hashtable_ptr) { - glyphname_hashtable_find = zero_find; - glyphname_hashtable_find.findtype = find_nothing; - glyphname_hashtable_find.reserved = (genericptr_t) glyphname_hashtable_ptr; - glyphname_hashtable_find.restype = res_fill_hashtable; - reslt = parse_id((char *) 0, &glyphname_hashtable_find); - if (!reslt) { - empty_glyphname_hashtable(); - glyphname_hashtable_ptr = (struct glyphname_hashtable_entry_t *) 0; + if (glyphname_hashtable_ptr) + return; + glyphname_hashtable_ptr = (struct glyphname_hashtable_entry_t *) alloc( + MAX_GLYPH * sizeof (struct glyphname_hashtable_entry_t)); + + for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { + if (compose_glyph_name(glyph, buf, sizeof buf)) { + glyphname_hashtable_ptr[n].hash = glyph_hash(buf); + glyphname_hashtable_ptr[n].glyphnum = glyph; + ++n; } } -} - -/* - * The glyph ID cache is a simple double-hash table. - * The cache size is a power of two, and two hashes are derived from the - * cache ID. The first is a location in the table, and the second is an - * offset. On any collision, the second hash is added to the first until - * a match or an empty bucket is found. - * The second hash is an odd number, which is necessary and sufficient - * to traverse the entire table. - */ - -staticfn void -init_glyphname_hashtable(void) -{ - size_t glyph; - - /* Cache size of power of 2 not less than 2*MAX_GLYPH */ - glyphname_hashtable_lsize = 0; - glyphname_hashtable_size = 1; - while (glyphname_hashtable_size < 2*MAX_GLYPH) { - ++glyphname_hashtable_lsize; - glyphname_hashtable_size <<= 1; - } - - glyphname_hashtable_ptr = (struct glyphname_hashtable_entry_t *) alloc( - glyphname_hashtable_size * sizeof (struct glyphname_hashtable_entry_t)); - for (glyph = 0; glyph < glyphname_hashtable_size; ++glyph) { - glyphname_hashtable_ptr[glyph].glyphnum = 0; - glyphname_hashtable_ptr[glyph].id = (char *) 0; - } + qsort(glyphname_hashtable_ptr, n, + sizeof glyphname_hashtable_ptr[0], cmp_glyphname_entry); + glyphname_hashtable_count = n; } void empty_glyphname_hashtable(void) { - size_t idx; - if (!glyphname_hashtable_ptr) return; - for (idx = 0; idx < glyphname_hashtable_size; ++idx) { - if (glyphname_hashtable_ptr[idx].id) { - free(glyphname_hashtable_ptr[idx].id); - glyphname_hashtable_ptr[idx].id = (char *) 0; - } - } free(glyphname_hashtable_ptr); glyphname_hashtable_ptr = (struct glyphname_hashtable_entry_t *) 0; -} - -staticfn void -add_glyph_to_glyphname_hashtable(int glyphnum, const char *id) -{ - uint32 hash = glyph_hash(id); - size_t hash1 = (size_t) (hash & (glyphname_hashtable_size - 1)); - size_t hash2 = (size_t) - (((hash >> glyphname_hashtable_lsize) & (glyphname_hashtable_size - 1)) | 1); - size_t i = hash1; - - do { - if (glyphname_hashtable_ptr[i].id == NULL) { - /* Empty bucket found */ - glyphname_hashtable_ptr[i].id = dupstr(id); - glyphname_hashtable_ptr[i].glyphnum = glyphnum; - return; - } - /* For speed, assume that no ID occurs twice */ - i = (i + hash2) & (glyphname_hashtable_size - 1); - } while (i != hash1); - /* This should never happen */ - panic("glyphname_hashtable_ptr full"); + glyphname_hashtable_count = 0; } staticfn int -find_glyph_in_cache(const char *id) +find_glyph_in_hashtable(const char *id) { - uint32 hash = glyph_hash(id); - size_t hash1 = (size_t) (hash & (glyphname_hashtable_size - 1)); - size_t hash2 = (size_t) - (((hash >> glyphname_hashtable_lsize) & (glyphname_hashtable_size - 1)) | 1); - size_t i = hash1; + uint32 want = glyph_hash(id); + size_t lo = 0, hi = glyphname_hashtable_count, mid; + char buf[BUFSZ]; - do { - if (glyphname_hashtable_ptr[i].id == NULL) { - /* Empty bucket found */ - return -1; - } - if (strcmpi(id, glyphname_hashtable_ptr[i].id) == 0) { - /* Match found */ - return glyphname_hashtable_ptr[i].glyphnum; - } - i = (i + hash2) & (glyphname_hashtable_size - 1); - } while (i != hash1); - return -1; -} - -staticfn char * -find_glyphname_in_hashtable_by_glyphnum(int glyphnum) -{ - size_t idx; - - if (!glyphname_hashtable_ptr) - return (char *) 0; - for (idx = 0; idx < glyphname_hashtable_size; ++idx) { - if (glyphname_hashtable_ptr[idx].glyphnum == glyphnum - && glyphname_hashtable_ptr[idx].id != 0) { - /* Match found */ - return glyphname_hashtable_ptr[idx].id; - } + /* Binary-search the sorted array for the first entry whose hash >= want. */ + while (lo < hi) { + mid = (lo + hi) >> 1; + if (glyphname_hashtable_ptr[mid].hash < want) + lo = mid + 1; + else + hi = mid; } - return (char *) 0; + /* Walk forward across any equal-hash neighbours, verifying each by + reconstructing the canonical name and strcmpi'ing it back. */ + while (lo < glyphname_hashtable_count + && glyphname_hashtable_ptr[lo].hash == want) { + int g = glyphname_hashtable_ptr[lo].glyphnum; + + if (compose_glyph_name(g, buf, sizeof buf) && !strcmpi(id, buf)) + return g; + ++lo; + } + return -1; } staticfn uint32 @@ -808,15 +983,11 @@ void wizcustom_glyphnames(winid win) { int glyphnum; - char *id; + char buf[BUFSZ]; - if (!glyphname_hashtable_ptr) - return; for (glyphnum = 0; glyphnum < MAX_GLYPH; ++glyphnum) { - id = find_glyphname_in_hashtable_by_glyphnum(glyphnum); - if (id) { - wizcustom_callback(win, glyphnum, id); - } + if (compose_glyph_name(glyphnum, buf, sizeof buf)) + wizcustom_callback(win, glyphnum, buf); } } @@ -826,12 +997,11 @@ parse_id( struct find_struct *findwhat) { FILE *fp = (FILE *) 0; - int i = 0, j, mnum, glyph, + int i = 0, 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 = FALSE, dump_ids = FALSE, - filling_cache = FALSE, is_S = FALSE, is_G = FALSE; - char buf[4][QBUFSZ]; + boolean dump_ids = FALSE, is_S = FALSE, is_G = FALSE; + char buf[BUFSZ]; if (findwhat->findtype == find_nothing && findwhat->restype) { if (findwhat->restype == res_dump_glyphnames) { @@ -842,20 +1012,12 @@ parse_id( return 0; } } - if (findwhat->restype == res_fill_hashtable) { - if (findwhat->reserved - && findwhat->reserved == (genericptr_t) glyphname_hashtable_ptr) { - 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 && !glyphname_hashtable_ptr) || filling_cache || dump_ids || is_S) { + if (dump_ids || is_S) { while (loadsyms[i].range) { if (!pm_offset && loadsyms[i].range == SYM_MON) pm_offset = i; @@ -872,259 +1034,31 @@ parse_id( i++; } } - if (is_G || filling_cache || dump_ids) { - if (!filling_cache && id && glyphname_hashtable_ptr) { - int val = find_glyph_in_cache(id); + if (is_G && id) { + /* Populate the hash table lazily, on first G_xxx lookup. */ + if (!glyphname_hashtable_ptr) + populate_glyphname_hashtable(); + if (glyphname_hashtable_ptr) { + int val = find_glyph_in_hashtable(id); + if (val >= 0) { findwhat->findtype = find_glyph; findwhat->val = val; findwhat->loadsyms_offset = 0; return 1; - } else { - return 0; } - } else { - const char *buf2, *buf3, *buf4; - - /* individual matching glyph entries */ - for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { - skip_base = FALSE; - skip_this_one = FALSE; - buf[0][0] = buf[1][0] = buf[2][0] = buf[3][0] = '\0'; - if (glyph_is_monster(glyph)) { - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - buf2 = ""; - buf3 = monsdump[glyph_to_mon(glyph)].nm; - - if (glyph_is_normal_male_monster(glyph)) { - buf2 = "male_"; - } else if (glyph_is_normal_female_monster(glyph)) { - buf2 = "female_"; - } else if (glyph_is_ridden_male_monster(glyph)) { - buf2 = "ridden_male_"; - } else if (glyph_is_ridden_female_monster(glyph)) { - buf2 = "ridden_female_"; - } else if (glyph_is_detected_male_monster(glyph)) { - buf2 = "detected_male_"; - } else if (glyph_is_detected_female_monster(glyph)) { - buf2 = "detected_female_"; - } else if (glyph_is_male_pet(glyph)) { - buf2 = "pet_male_"; - } else if (glyph_is_female_pet(glyph)) { - buf2 = "pet_female_"; - } - Strcpy(buf[0], "G_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - } else if (glyph_is_body(glyph)) { - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - buf2 = glyph_is_body_piletop(glyph) - ? "piletop_body_" - : "body_"; - buf3 = monsdump[glyph_to_body_corpsenm(glyph)].nm; - Strcpy(buf[0], "G_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - } else if (glyph_is_statue(glyph)) { - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - buf2 = glyph_is_fem_statue_piletop(glyph) - ? "piletop_statue_of_female_" - : glyph_is_fem_statue(glyph) - ? "statue_of_female_" - : glyph_is_male_statue_piletop(glyph) - ? "piletop_statue_of_male_" - : glyph_is_male_statue(glyph) - ? "statue_of_male_" - : ""; /* shouldn't happen */ - buf3 = monsdump[glyph_to_statue_corpsenm(glyph)].nm; - Strcpy(buf[0], "G_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - } else if (glyph_is_object(glyph)) { - i = glyph_to_obj(glyph); - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - 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)) - buf2 = "wand of "; - else if ((i >= SPE_DIG) && (i < SPE_BLANK_PAPER)) - buf2 = "spellbook of "; - else if ((i >= SCR_ENCHANT_ARMOR) - && (i <= SCR_STINKING_CLOUD)) - buf2 = "scroll of "; - else if ((i >= POT_GAIN_ABILITY) && (i <= POT_WATER)) - buf2 = (i == POT_WATER) ? "flask of n" - : "potion of "; - else if ((i >= RIN_ADORNMENT) - && (i <= RIN_PROTECTION_FROM_SHAPE_CHAN)) - buf2 = "ring of "; - else if (i == LAND_MINE) - buf2 = "unset "; - else - buf2 = ""; - buf3 = (i == SCR_BLANK_PAPER) ? "blank scroll" - : (i == SPE_BLANK_PAPER) ? "blank spellbook" - : (i == SLIME_MOLD) ? "slime mold" - : obj_descr[i].oc_name - ? obj_descr[i].oc_name - : obj_descr[i].oc_descr; - Strcpy(buf[0], "G_"); - if (glyph_is_normal_piletop_obj(glyph)) - Strcat(buf[0], "piletop_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - } - } else if (glyph_is_cmap(glyph) || glyph_is_cmap_zap(glyph) - || glyph_is_swallow(glyph) - || glyph_is_explosion(glyph)) { - int cmap = -1; - - /* buf2 will hold the distinguishing prefix */ - /* buf3 will hold the base name */ - /* buf4 will hold the distinguishing suffix */ - buf2 = ""; - buf3 = ""; - buf4 = ""; - if (glyph == GLYPH_CMAP_OFF) { - cmap = S_stone; - buf3 = "stone substrate"; - skip_base = TRUE; - } else if (glyph_is_cmap_gehennom(glyph)) { - cmap = (glyph - GLYPH_CMAP_GEH_OFF) + S_vwall; - buf4 = "_gehennom"; - } else if (glyph_is_cmap_knox(glyph)) { - cmap = (glyph - GLYPH_CMAP_KNOX_OFF) + S_vwall; - buf4 = "_knox"; - } else if (glyph_is_cmap_main(glyph)) { - cmap = (glyph - GLYPH_CMAP_MAIN_OFF) + S_vwall; - buf4 = "_main"; - } else if (glyph_is_cmap_mines(glyph)) { - cmap = (glyph - GLYPH_CMAP_MINES_OFF) + S_vwall; - buf4 = "_mines"; - } else if (glyph_is_cmap_sokoban(glyph)) { - cmap = (glyph - GLYPH_CMAP_SOKO_OFF) + S_vwall; - buf4 = "_sokoban"; - } else if (glyph_is_cmap_a(glyph)) { - cmap = (glyph - GLYPH_CMAP_A_OFF) + S_ndoor; - } else if (glyph_is_cmap_altar(glyph)) { - static const char *const 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]); - buf2 = buf[2]; - } else { - buf3 = "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 *const 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])); - buf3 = buf[3]; - buf2 = ""; - 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 *const 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); - Strcpy(buf[3], "swallow "); - Strcat(buf[3], monsdump[mnum].nm); - Strcat(buf[3], " "); - Strcat(buf[3], swallow_texts[cmap]); - buf3 = buf[3]; - skip_base = TRUE; - } else if (glyph_is_explosion(glyph)) { - static const char *const expl_type_texts[] = { - "dark", "noxious", "muddy", "wet", - "magical", "fiery", "frosty", - }; - static const char *const expl_texts[] = { - "tl", "tc", "tr", "ml", "mc", - "mr", "bl", "bc", "br", - }; - int expl; - - j = glyph - GLYPH_EXPLODE_OFF; - expl = j / ((S_expl_br - S_expl_tl) + 1); - cmap = glyph_to_explosion(glyph) + S_expl_tl; - i = cmap - S_expl_tl; - Snprintf(buf[2], sizeof buf[2], "%s ", - expl_type_texts[expl]); - buf2 = buf[2]; - Snprintf(buf[3], sizeof buf[3], "%s%s", "expl_", - expl_texts[i]); - buf3 = buf[3]; - skip_base = TRUE; - } - if (!skip_base) { - if (cmap >= 0 && cmap < MAXPCHARS) { - buf3 = loadsyms[cmap + cmap_offset].name + 2; - } - } - Strcpy(buf[0], "G_"); - Strcat(buf[0], buf2); - Strcat(buf[0], buf3); - Strcat(buf[0], buf4); - } else if (glyph_is_invisible(glyph)) { - Strcpy(buf[0], "G_invisible"); - } else if (glyph_is_nothing(glyph)) { - Strcpy(buf[0], "G_nothing"); - } else if (glyph_is_unexplored(glyph)) { - Strcpy(buf[0], "G_unexplored"); - } else if (glyph_is_warning(glyph)) { - j = glyph - GLYPH_WARNING_OFF; - Snprintf(buf[0], sizeof buf[0], "G_%s%d", "warning", j); - } - if (memchr(buf[0], '\0', sizeof buf[0]) == NULL) - panic("parse_id: buf[0] overflowed"); - if (!skip_this_one) { - fix_glyphname(buf[0]+2); - if (dump_ids) { - Fprintf(fp, "(%04d) %s\n", glyph, buf[0]); - } else if (filling_cache) { - add_glyph_to_glyphname_hashtable(glyph, buf[0]); - } else if (id) { - if (!strcmpi(id, buf[0])) { - findwhat->findtype = find_glyph; - findwhat->val = glyph; - findwhat->loadsyms_offset = 0; - return 1; - } - } - } - } - } /* not glyphname_hashtable_ptr */ - } else if (is_S) { + } + return 0; + } + if (dump_ids) { + /* iterate and dump every named glyph */ + for (glyph = 0; glyph < MAX_GLYPH; ++glyph) { + if (compose_glyph_name(glyph, buf, sizeof buf)) + Fprintf(fp, "(%04d) %s\n", glyph, buf); + } + return 1; + } + if (is_S) { /* cmap entries */ for (i = 0; i < cmap_count; ++i) { if (!strcmpi(loadsyms[i + cmap_offset].name + 2, id + 2)) { @@ -1153,8 +1087,6 @@ parse_id( } } } - if (dump_ids || filling_cache) - return 1; findwhat->findtype = find_nothing; findwhat->val = 0; findwhat->loadsyms_offset = 0;