From cb0c21e91d90c16cd470fa808a93e959aae1ed37 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sat, 7 May 2022 10:25:13 -0400 Subject: [PATCH] ENHANCED_SYMBOLS A new feature, enabled by default to maximize testing, but one which can be disabled by commenting it out in config.h With this, some additional information is added to the glyphmap entries in a new optional substructure called u with these fields: ucolor RGB color for use with truecolor terminals/platforms. A ucolor value of zero means "not set." The actual rgb value of 0 has the 0x1000000 bit set. u256coloridx 256 color index value for use with 256 color terminals, the closest color match to ucolor. utf8str Custom representation via utf-8 string (can be null). There is a new symset included in the symbols file, called enhanced1. Some initial code has been added to parse individual OPTIONS=glyph:glyphid/R-G-B entries in the config file. The glyphid can, in theory, either be an individual glyph (G_* glyphid) for a single glyph, or it can be an existing symbol S_ value (monster, object, or cmap symbol) to store the custom representation for all the glyphs that match that symbol. Examples: OPTIONS=glyph:G_fountain/U+03A8/0-150-255 (Your platform/terminal font needs to be able to include/display the character, of course.) The NetHack core code does parsing and storing the customized entries, and adding them to the glyphmap data structure. Any window port can utilize the additional information in the glyphinfo that is passed to them, once code is added to do so. Also, consolidate some symbol-related code into symbols.c, and remove it from files.c and options.c --- dat/symbols | 104 +++- doc/Guidebook.mn | 37 +- doc/Guidebook.tex | 42 +- include/config.h | 9 + include/decl.h | 13 +- include/extern.h | 58 +- include/flag.h | 4 +- include/hack.h | 2 +- include/optlist.h | 4 + include/sym.h | 65 +- include/wincurs.h | 8 +- include/winprocs.h | 6 +- include/wintty.h | 11 + include/wintype.h | 23 +- src/allmain.c | 43 +- src/decl.c | 7 +- src/display.c | 147 +++-- src/do.c | 2 +- src/drawing.c | 1 + src/files.c | 214 +------ src/hacklib.c | 45 ++ src/mdlib.c | 1 + src/monst.c | 1 + src/options.c | 444 ++++---------- src/symbols.c | 730 +++++++++++++++++++++- src/utf8map.c | 1123 ++++++++++++++++++++++++++++++++++ src/windows.c | 72 ++- sys/libnh/libnhmain.c | 4 +- sys/share/pcmain.c | 4 +- sys/share/unixtty.c | 14 +- sys/unix/Makefile.src | 6 +- sys/unix/hints/linux.370 | 3 + sys/unix/hints/macOS.370 | 3 + sys/unix/unixmain.c | 9 +- sys/vms/vmsmain.c | 4 +- sys/windows/Makefile.mingw32 | 2 +- sys/windows/Makefile.nmake | 17 +- sys/windows/consoletty.c | 644 +++++++------------ sys/windows/windmain.c | 73 ++- win/Qt/qt_map.cpp | 2 +- win/X11/winmap.c | 2 +- win/curses/cursinit.c | 4 +- win/curses/cursmain.c | 22 +- win/curses/curswins.c | 16 +- win/curses/curswins.h | 5 + win/share/tilemap.c | 20 +- win/tty/termcap.c | 84 ++- win/tty/topl.c | 14 +- win/tty/wintty.c | 142 ++++- win/win32/mhmap.c | 4 +- 50 files changed, 3072 insertions(+), 1242 deletions(-) create mode 100644 src/utf8map.c diff --git a/dat/symbols b/dat/symbols index edd9a80ac..99927fb8c 100644 --- a/dat/symbols +++ b/dat/symbols @@ -2,7 +2,7 @@ # Copyright (c) 2006 by Michael Allison # NetHack may be freely redistributed. See license for details. # -# Symbol sets for use in NetHack's text-based display. +# Symbol sets and handlers for use in NetHack's text-based display. # # IBMgraphics works by specifying special characters which reside # outside the range of normal printable characters. It has subsets @@ -22,7 +22,17 @@ # on an old graphics mode for the Curses interface and is the default # on that windowport if no symset is specified. The Curses interface # can also use DECgraphics as-is; IBMgraphics probably won't work. - +# +# Symsets that use the UTF8 handler work a little differently than +# those that do not. When the UTF8 handler is in effect, any NetHack +# glyphs that have a utf8str entry present in the runtime glyphmap[] +# array will utilize that UTF-8 character sequence for the glyph display +# instead of the NetHack ttychar symbol that would otherwise be used. +# Any NetHack glyphs that don't actually have a utf8str sequence present +# in the internal glyphmap will simply fall back to utilizing the typical +# NetHack ttychar symbol as usual. That is true even when the UTF-8 +# handler is in effect. +# # plain looks decent for room+corridor levels where there aren't a lot # of wall corners and ones present tend to be spread out, but it looks # awful for wallified mazes @@ -550,4 +560,94 @@ start: MACgraphics S_pool: \xe0 finish +start: Enhanced1 + Description: Enhanced with Unicode glyphs and 24-bit color + Restrictions: primary + Handling: UTF8 + S_vwall: U+2502 # BOX DRAWINGS LIGHT VERTICAL + S_hwall: U+2500 # BOX DRAWINGS LIGHT HORIZONTAL + S_tlcorn: U+250c # BOX DRAWINGS LIGHT DOWN AND RIGHT + S_trcorn: U+2510 # BOX DRAWINGS LIGHT DOWN AND LEFT + S_blcorn: U+2514 # BOX DRAWINGS LIGHT UP AND RIGHT + S_brcorn: U+2518 # BOX DRAWINGS LIGHT UP AND LEFT + S_crwall: U+253c # BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + S_tuwall: U+2534 # BOX DRAWINGS LIGHT UP AND HORIZONTAL + S_tdwall: U+252c # BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + S_tlwall: U+2524 # BOX DRAWINGS LIGHT VERTICAL AND LEFT + S_trwall: U+251c # BOX DRAWINGS LIGHT VERTICAL AND RIGHT + S_ndoor: U+00b7 # MIDDLE DOT + S_vodoor: U+2592 # MEDIUM SHADE + S_hodoor: U+2592 # MEDIUM SHADE + S_bars: U+2261 # IDENTICAL TO + S_tree: U+03a8 # GREEK CAPITAL LETTER PSI + S_room: U+00b7 # MIDDLE DOT + S_darkroom: U+00b7 # MIDDLE DOT + S_upladder: U+2264 # LESS-THAN OR EQUAL TO + S_dnladder: U+2265 # GREATER-THAN OR EQUAL TO + S_altar: U+03A9 # GREEK CAPITAL LETTER OMEGA + S_grave: U+2020 # DAGGER + G_fountain: U+2320/0-150-255 # TOP HALF INTEGRAL + S_pool: U+224b # TRIPLE TILDE + S_ice: U+00b7 # MIDDLE DOT + S_lava: U+224b # TRIPLE TILDE + S_vodbridge: U+00b7 # MIDDLE DOT + S_hodbridge: U+00b7 # MIDDLE DOT + S_water: U+2248 # ALMOST EQUAL TO + S_web: U+00A4 # CURRENCY SIGN + S_vbeam: U+2502 # BOX DRAWINGS LIGHT VERTICAL + S_hbeam: U+2500 # BOX DRAWINGS LIGHT HORIZONTAL + S_sw_tc: U+2594 # UPPER ONE EIGHTH BLOCK + S_sw_ml: U+258f # LEFT ONE EIGHTH BLOCK + S_sw_mr: U+2595 # RIGHT ONE EIGHTH BLOCK + S_sw_bc: U+2581 # LOWER ONE EIGHTH BLOCK + S_expl_tc: U+2594 # UPPER ONE EIGHTH BLOCK + S_expl_ml: U+258f # LEFT ONE EIGHTH BLOCK + S_expl_mr: U+2595 # RIGHT ONE EIGHTH BLOCK + S_expl_bc: U+2581 # LOWER ONE EIGHTH BLOCK + G_vwall_sokoban: U+2502/0-0-190 + G_hwall_sokoban: U+2500/0-0-190 + G_tlcorn_sokoban: U+250c/0-0-190 + G_trcorn_sokoban: U+2510/0-0-190 + G_blcorn_sokoban: U+2514/0-0-190 + G_brcorn_sokoban: U+2518/0-0-190 + G_crwall_sokoban: U+253C/0-0-190 + G_tuwall_sokoban: U+2500/0-0-190 + G_tdwall_sokoban: U+252C/0-0-190 + G_tlwall_sokoban: U+2524/0-0-190 + G_trwall_sokoban: U+251C/0-0-190 + G_vwall_gehennom: U+2502/190-0-0 + G_hwall_gehennom: U+2500/190-0-0 + G_tlcorn_gehennom: U+250c/190-0-0 + G_trcorn_gehennom: U+2510/190-0-0 + G_blcorn_gehennom: U+2514/190-0-0 + G_brcorn_gehennom: U+2518/190-0-0 + G_crwall_gehennom: U+253C/190-0-0 + G_tuwall_gehennom: U+2500/190-0-0 + G_tdwall_gehennom: U+252C/190-0-0 + G_tlwall_gehennom: U+2524/190-0-0 + G_trwall_gehennom: U+251C/190-0-0 + G_vwall_knox: U+2502/150-75-0 + G_hwall_knox: U+2500/150-75-0 + G_tlcorn_knox: U+250c/150-75-0 + G_trcorn_knox: U+2510/150-75-0 + G_blcorn_knox: U+2514/150-75-0 + G_brcorn_knox: U+2518/150-75-0 + G_crwall_knox: U+253C/150-75-0 + G_tuwall_knox: U+2500/150-75-0 + G_tdwall_knox: U+252C/150-75-0 + G_tlwall_knox: U+2524/150-75-0 + G_trwall_knox: U+251C/150-75-0 + G_vwall_mines: U+2502/113-126-142 + G_hwall_mines: U+2500/113-126-142 + G_tlcorn_mines: U+250c/113-126-142 + G_trcorn_mines: U+2510/113-126-142 + G_blcorn_mines: U+2514/113-126-142 + G_brcorn_mines: U+2518/113-126-142 + G_crwall_mines: U+253C/113-126-142 + G_tuwall_mines: U+2500/113-126-142 + G_tdwall_mines: U+252C/113-126-142 + G_tlwall_mines: U+2524/113-126-142 + G_trwall_mines: U+251C/113-126-142 +finish + # symbols EOF diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index be7f9542c..80aa63355 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -36,7 +36,7 @@ .ds f0 "\*(vr .ds f1 .\"DO NOT REMOVE NH_DATESUB .ds f2 "DATE(%B %-d, %Y) -.ds f2 "February 27, 2022 +.ds f2 "May 7, 2022 . .\" A note on some special characters: .\" \(lq = left double quote @@ -5519,6 +5519,41 @@ So S_rock is only used for boulders and not used at all if overridden by the more specific S_boulder. .pg .hn 2 +Customizing Map Glyph Representations Using Unicode +.pg +If your platform or terminal supports the display of UTF-8 character +sequences, you can customize your game display by assigning Unicode +codepoint values and \fIred-green-blue\fP colors to glyph +representations. The customizations can be specified for use with a symset that +has a UTF8 handler within the symbols file such as the enhanced1 set, or +individually within your nethack.rc file. + +.pg +The format for defining a glyph representation is: +.SD n +\f(CROPTIONS=glyph:glyphid\fIU+nnnn\fP/\fIR-G-B\fP\fP +.ED +.pg +The window port that is active needs to provide support for displaying +UTF-8 character sequences and explicit red-green-blue colors in order +for the glyph representation to be visible. +For example, the following line in your configuration file will cause +the glyph representation for glyphid G_pool to use Unicode codepoint +U+224B and the color represented by R-G-B value 0-0-160 +.SD n +\f(CROPTIONS=glyph:G_pool/U+224B/0-0-160\fP +.ED +The list of acceptable glyphid's can be produced by +\fBnethack \-\-dumpglyphids\fP. +Individual NetHack glyphs can be specified using the G_ prefix, +or you can use an S_ symbol for a glyphid and store the custom +representation for all NetHack glyphs that would map to that +particular symbol. + +You will need to select a symset with a UTF8 handler to enable the +display of the customizations, such as the Enhanced symset. +.pg +.hn 2 Configuring NetHack for Play by the Blind .pg NetHack can be set up to use only standard ASCII characters for making diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 7e2084d8c..27cc137e7 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -46,7 +46,7 @@ \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7 by Mike Stephenson and others)} %DO NOT REMOVE NH_DATESUB \date{DATE(%B %-d, %Y)} -\date{February 27, 2022} +\date{May 7, 2022} \maketitle @@ -6071,6 +6071,46 @@ version 3.6.0, statues are displayed as the monster they depict. So S\verb+_+rock is only used for boulders and not used at all if overridden by the more specific S\verb+_+boulder. +%.lp +%.hn 2 +\subsection*{Customizing Map Glyph Representations Using Unicode} + +%.pg +If your platform or terminal supports the display of UTF-8 character +sequences, you can customize your game display by assigning Unicode +codepoint values and red-green-blue colors to glyph +representations. The customizations can be specified for use with a symset that +has a UTF8 handler within the symbols file such as the enhanced1 set, or +individually within your own nethack.rc file. + +The format for defining a glyph representation is:\\ +\begin{verbatim} +OPTIONS=glyph:glyphid/U+nnnn/R-G-B +\end{verbatim} + +The window port that is active needs to provide support for displaying UTF-8 +character sequences and explicit 24-bit red-green-blue colors in order for the glyph +representation to be visible as specified. + +For example, the following line in your configuration file will cause +the glyph representation for glyphid G_pool to use Unicode codepoint U+224B +and the color represented by R-G-B value 0-0-160:\\ +\begin{verbatim} +OPTIONS=glyph:G_pool/U+224B/0-0-160 +\end{verbatim} + +The list of acceptable glyphid's can be produced by +\begin{verbatim} + nethack --glyphids +\end{verbatim} +Individual NetHack glyphs can be specified using the G\verb+_+ prefix, +or you can use an S\verb+_+ symbol for a glyphid and store the custom +representation for all NetHack glyphs that would map to that +particular symbol. + +You will need to select a symset with a UTF8 handler to enable the +display of the customizations, such as the Enhanced symset. + %.pg %.hn 2 \subsection*{Configuring {\it NetHack\/} for Play by the Blind} diff --git a/include/config.h b/include/config.h index d44f4b9f8..9c59cb9eb 100644 --- a/include/config.h +++ b/include/config.h @@ -280,6 +280,15 @@ */ /* #define NODUMPENUMS */ +/* + * ENHANCED_SYMBOLS + * Support the enhanced display of symbols by utilizing utf8 and 24-bit + * color sequences. Enabled by default, but it can be disabled by + * commenting it out. + */ + +#define ENHANCED_SYMBOLS + /* * If COMPRESS is defined, it should contain the full path name of your * 'compress' program. diff --git a/include/decl.h b/include/decl.h index 04e60f00e..6a00c9e06 100644 --- a/include/decl.h +++ b/include/decl.h @@ -434,7 +434,10 @@ E const char *ARGV0; enum earlyarg {ARG_DEBUG, ARG_VERSION, ARG_SHOWPATHS #ifndef NODUMPENUMS , ARG_DUMPENUMS +#ifdef ENHANCED_SYMBOLS + , ARG_DUMPGLYPHIDS #endif +#endif /* NODUMPENUMS */ #ifdef WIN32 ,ARG_WINDOWS #endif @@ -679,6 +682,11 @@ struct _cmd_queue { struct _cmd_queue *next; }; +struct enum_dump { + int val; + const char *nm; +}; + typedef long cmdcount_nht; /* Command counts */ /* @@ -888,8 +896,11 @@ struct instance_globals { struct rm nowhere; const char *gate_str; - /* drawing */ + /* symbols.c */ struct symsetentry symset[NUM_GRAPHICS]; +#ifdef ENHANCED_SYMBOLS + struct symset_customization sym_customizations[NUM_GRAPHICS + 1]; /* adds UNICODESET */ +#endif int currentgraphics; nhsym showsyms[SYM_MAX]; /* symbols to be displayed */ nhsym primary_syms[SYM_MAX]; /* loaded primary symbols */ diff --git a/include/extern.h b/include/extern.h index 5501505c5..5f84ddf96 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1047,6 +1047,9 @@ extern void nh_snprintf(const char *func, int line, char *str, size_t size, extern int FITSint_(long long, const char *, int); #define FITSuint(x) FITSuint_(x, __func__, __LINE__) extern unsigned FITSuint_(unsigned long long, const char *, int); +#ifdef ENHANCED_SYMBOLS +extern int unicodeval_to_utf8str(int, uint8 *, size_t); +#endif /* ### insight.c ### */ @@ -1281,6 +1284,9 @@ extern int buzzmu(struct monst *, struct attack *); extern void runtime_info_init(void); extern const char *do_runtime_info(int *); extern void release_runtime_info(void); +#ifdef ENHANCED_SYMBOLS +extern void dump_glyphids(void); +#endif /* ### mhitm.c ### */ @@ -1828,6 +1834,10 @@ extern void synch_cursor(void); extern void nethack_enter_consoletty(void); extern void consoletty_exit(void); extern int set_keyhandling_via_option(void); +#ifdef ENHANCED_SYMBOLS +extern void tty_utf8graphics_fixup(void); +extern void tty_ibmgraphics_fixup(void); +#endif /* ENHANCED_SYMBOLS */ #endif /* WIN32 */ /* ### o_init.c ### */ @@ -1947,10 +1957,6 @@ extern void set_wc2_option_mod_status(unsigned long, int); extern void set_option_mod_status(const char *, int); extern int add_autopickup_exception(const char *); extern void free_autopickup_exceptions(void); -extern int load_symset(const char *, int); -extern void free_symsets(void); -extern boolean parsesymbols(char *, int); -extern struct symparse *match_sym(char *); extern void set_playmode(void); extern int sym_val(const char *); extern int query_color(const char *); @@ -2623,11 +2629,25 @@ extern void init_rogue_symbols(void); extern void init_ov_primary_symbols(void); extern void init_ov_rogue_symbols(void); extern void clear_symsetentry(int, boolean); -extern void update_primary_symset(struct symparse *, int); -extern void update_rogue_symset(struct symparse *, int); -extern void update_ov_primary_symset(struct symparse *, int); -extern void update_ov_rogue_symset(struct symparse *, int); +extern void update_primary_symset(const struct symparse *, int); +extern void update_rogue_symset(const struct symparse *, int); +extern void update_ov_primary_symset(const struct symparse *, int); +extern void update_ov_rogue_symset(const struct symparse *, int); extern nhsym get_othersym(int, int); +extern boolean symset_is_compatible(enum symset_handling_types, unsigned long); +extern void set_symhandling(char *handling, int which_set); +extern boolean proc_symset_line(char *); +extern int do_symset(boolean); +extern int load_symset(const char *, int); +extern void free_symsets(void); +extern const struct symparse *match_sym(char *); +extern boolean parsesymbols(char *, int); +#ifdef ENHANCED_SYMBOLS +extern struct customization_detail *find_matching_symset_customization( + const char *symset_name, int custtype, + enum graphics_sets which_set); +extern void apply_customizations_to_symset(enum graphics_sets which_set); +#endif /* ### sys.c ### */ @@ -2910,6 +2930,9 @@ extern void setftty(void); extern void intron(void); extern void introff(void); extern void error (const char *, ...) PRINTF_F(1, 2); +#ifdef ENHANCED_SYMBOLS +extern void tty_utf8graphics_fixup(void); +#endif #endif /* UNIX || __BEOS__ */ /* ### unixunix.c ### */ @@ -2939,6 +2962,24 @@ extern int hide_privileges(boolean); #endif #endif /* UNIX */ +/* ### utf8map.c ### */ + +#ifdef ENHANCED_SYMBOLS +extern int glyphrep(const char *); +extern char *mixed_to_utf8(char *buf, size_t bufsz, const char *str, int *); +extern int match_glyph(char *); +extern void dump_all_glyphids(FILE *fp); +extern void fill_glyphid_cache(void); +extern void free_glyphid_cache(void); +extern boolean glyphid_cache_status(void); +extern int glyphrep_to_custom_map_entries(const char *op, int *glyph); +void free_all_glyphmap_u(void); +int add_custom_urep_entry(const char *symset_name, int glyphidx, + const uint8 *utf8str, long ucolor, + enum graphics_sets which_set); +int set_map_u(glyph_map *gm, const uint8 *utf8str, long ucolor); +#endif /* ENHANCED_SYMBOLS */ + /* ### vault.c ### */ extern void newegd(struct monst *); @@ -3191,6 +3232,7 @@ extern int has_color(int); extern int glyph2ttychar(int); extern int glyph2symidx(int); extern char *encglyph(int); +extern int decode_glyph(const char *str, int *glyph_ptr); extern char *decode_mixed(char *, const char *); extern void genl_putmixed(winid, int, const char *); extern boolean menuitem_invert_test(int, unsigned, boolean); diff --git a/include/flag.h b/include/flag.h index 400c66097..16c936b09 100644 --- a/include/flag.h +++ b/include/flag.h @@ -237,6 +237,8 @@ struct instance_flags { int menuinvertmode; /* 0 = invert toggles every item; 1 = invert skips 'all items' item */ int menu_headings; /* ATR for menu headings */ + uint32_t colorcount; /* store how many colors terminal is capable of */ + boolean use_truecolor; /* force use of truecolor */ #ifdef ALTMETA boolean altmeta; /* Alt-c sends ESC c rather than M-c */ #endif @@ -389,7 +391,7 @@ struct instance_flags { Bitfield(save_uinwater, 1); Bitfield(save_uburied, 1); struct debug_flags debug; - boolean windowtype_locked; /* windowtype can't change from configfile */ + boolean windowtype_locked; /* windowtype can't change from configfile */ boolean windowtype_deferred; /* pick a windowport and store it in chosen_windowport[], but do not switch to it in the midst of options processing */ diff --git a/include/hack.h b/include/hack.h index 4a75195ee..040cb7a37 100644 --- a/include/hack.h +++ b/include/hack.h @@ -183,6 +183,7 @@ typedef struct { #include "align.h" #include "dungeon.h" +#include "wintype.h" #include "sym.h" #include "mkroom.h" @@ -199,7 +200,6 @@ enum misc_arti_nums { #include "objclass.h" #include "youprop.h" -#include "wintype.h" #include "context.h" #include "rm.h" #include "botl.h" diff --git a/include/optlist.h b/include/optlist.h index 3dcfff79f..b0a88c3b8 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -218,6 +218,8 @@ opt_##a, &iflags.wc2_fullscreen) NHOPTC(gender, 8, opt_in, set_gameview, No, Yes, No, No, NoAlias, "your starting gender (male or female)") + NHOPTC(glyph, 40, opt_in, set_in_game, No, Yes, Yes, No, NoAlias, + "set representation of a glyph to a unicode value and color") NHOPTB(goldX, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.goldX) NHOPTB(guicolor, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, @@ -521,6 +523,8 @@ opt_##a, &iflags.wc2_darkgray) NHOPTB(use_inverse, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, &iflags.wc_inverse) + NHOPTB(use_truecolor, 0, opt_in, set_in_config, Off, Yes, No, No, + "use_truecolour", &iflags.use_truecolor) NHOPTC(vary_msgcount, 20, opt_in, set_gameview, No, Yes, No, No, NoAlias, "show more old messages at a time") NHOPTB(verbose, 0, opt_out, set_in_game, On, Yes, No, No, NoAlias, diff --git a/include/sym.h b/include/sym.h index f08bcd32f..5192404ce 100644 --- a/include/sym.h +++ b/include/sym.h @@ -25,7 +25,6 @@ enum mon_syms { }; #ifndef MAKEDEFS_C - /* Default characters for dungeon surroundings and furniture */ enum cmap_symbols { #define PCHAR_S_ENUM @@ -34,6 +33,22 @@ enum cmap_symbols { MAXPCHARS }; +/* + * special symbol set handling types ( for invoking callbacks, etc.) + * Must match the order of the known_handlers strings + * in drawing.c + */ + +enum symset_handling_types { + H_UNK = 0, + H_IBM = 1, + H_DEC = 2, + H_CURS = 3, + H_MAC = 4, /* obsolete; needed so that the listing of available + * symsets by 'O' can skip it for !MAC_GRAPHICS_ENV */ + H_UTF8 = 5 +}; + struct symdef { uchar sym; const char *explanation; @@ -51,7 +66,7 @@ enum symparse_range { }; struct symparse { - unsigned range; + enum symparse_range range; int idx; const char *name; }; @@ -62,7 +77,7 @@ struct symsetentry { char *name; /* ptr to symset name */ char *desc; /* ptr to description */ int idx; /* an index value */ - int handling; /* known handlers value */ + enum symset_handling_types handling; /* known handlers value */ Bitfield(nocolor, 1); /* don't use color if set */ Bitfield(primary, 1); /* restricted for use as primary set */ Bitfield(rogue, 1); /* restricted for use as rogue lev set */ @@ -109,25 +124,41 @@ enum misc_symbols { */ #define DEFAULT_GRAPHICS 0 /* regular characters: '-', '+', &c */ enum graphics_sets { - PRIMARY = 0, /* primary graphics set */ + PRIMARYSET = 0, /* primary graphics set */ ROGUESET = 1, /* rogue graphics set */ - NUM_GRAPHICS + NUM_GRAPHICS, + UNICODESET = NUM_GRAPHICS }; -/* - * special symbol set handling types ( for invoking callbacks, etc.) - * Must match the order of the known_handlers strings - * in drawing.c - */ +#ifdef ENHANCED_SYMBOLS +enum customization_types { custom_none, custom_symbols, custom_ureps }; -enum symset_handling_types { - H_UNK = 0, - H_IBM = 1, - H_DEC = 2, - H_CURS = 3, - H_MAC = 4 /* obsolete; needed so that the listing of available - * symsets by 'O' can skip it for !MAC_GRAPHICS_ENV */ +struct custom_symbol { + const struct symparse *symparse; + unsigned char val; +}; +struct custom_urep { + int glyphidx; + struct unicode_representation u; +}; +union customization_content { + struct custom_symbol sym; + struct custom_urep urep; +}; +struct customization_detail { + union customization_content content; + struct customization_detail *next; +}; + + +/* one set per symset_name */ +struct symset_customization { + const char *customization_name; + int count; + int custtype; + struct customization_detail *details; }; +#endif /* ENHANCED_SYMBOLS */ extern const struct symdef defsyms[MAXPCHARS]; /* defaults */ #define WARNCOUNT 6 /* number of different warning levels */ diff --git a/include/wincurs.h b/include/wincurs.h index 48173f88f..31b61256e 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -127,8 +127,14 @@ extern void curses_refresh_nethack_windows(void); extern void curses_del_nhwin(winid wid); extern void curses_del_wid(winid wid); extern void curs_destroy_all_wins(void); +#ifdef ENHANCED_SYMBOLS extern void curses_putch(winid wid, int x, int y, int ch, - int color, int attrs); + struct unicode_representation *u, int color, + int attrs); +#else +extern void curses_putch(winid wid, int x, int y, int ch, int color, + int attrs); +#endif extern void curses_get_window_size(winid wid, int *height, int *width); extern boolean curses_window_has_border(winid wid); extern boolean curses_window_exists(winid wid); diff --git a/include/winprocs.h b/include/winprocs.h index 820c464ba..08f9ebc72 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -231,8 +231,10 @@ extern * via non-display attribute flag */ #define WC2_SUPPRESS_HIST 0x8000L /* 16 putstr(WIN_MESSAGE) supports history * suppression via non-disp attr */ -#define WC2_MENU_SHIFT 0x010000L /* 17 horizontal menu scrolling */ - /* 15 free bits */ +#define WC2_MENU_SHIFT 0x010000L /* 17 horizontal menu scrolling */ +#define WC2_U_UTF8STR 0x020000L /* 18 utf8str support */ +#define WC2_U_24BITCOLOR 0x040000L /* 19 24-bit color support available */ + /* 13 free bits */ #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 diff --git a/include/wintty.h b/include/wintty.h index eeb87b78c..f28c819be 100644 --- a/include/wintty.h +++ b/include/wintty.h @@ -78,6 +78,7 @@ struct DisplayDesc { int intr; /* non-zero if inread was interrupted */ winid lastwin; /* last window used for I/O */ char dismiss_more; /* extra character accepted at --More-- */ + int topl_utf8; /* non-zero if utf8 in str */ }; #endif /* WINDOW_STRUCTS */ @@ -165,6 +166,10 @@ E void term_end_raw_bold(void); E void term_end_color(void); E void term_start_color(int color); #endif /* TEXTCOLOR */ +#ifdef ENHANCED_SYMBOLS +extern void term_start_24bitcolor(struct unicode_representation *); +extern void term_end_24bitcolor(void); /* termcap.c, consoletty.c */ +#endif /* ### topl.c ### */ @@ -182,6 +187,11 @@ E void setclipped(void); E void docorner(int, int); E void end_glyphout(void); E void g_putch(int); +#ifdef ENHANCED_SYMBOLS +#if defined(WIN32) || defined(UNIX) +E void g_pututf8(uint8 *); +#endif +#endif /* ENHANCED_SYMBOLS */ E void win_tty_init(int); /* external declarations */ @@ -200,6 +210,7 @@ E void tty_dismiss_nhwindow(winid); E void tty_destroy_nhwindow(winid); E void tty_curs(winid, int, int); E void tty_putstr(winid, int, const char *); +E void tty_putmixed(winid window, int attr, const char *str); E void tty_display_file(const char *, boolean); E void tty_start_menu(winid, unsigned long); E void tty_add_menu(winid, const glyph_info *, const ANY_P *, char, char, diff --git a/include/wintype.h b/include/wintype.h index 42f4b0c73..9e54c21e1 100644 --- a/include/wintype.h +++ b/include/wintype.h @@ -5,6 +5,8 @@ #ifndef WINTYPE_H #define WINTYPE_H +#include "integer.h" + typedef int winid; /* a window identifier */ /* generic parameter - must not be any larger than a pointer */ @@ -60,15 +62,28 @@ typedef struct mi { } menu_item; #define MENU_ITEM_P struct mi -/* These would be in display.h if they weren't needed to define - the windowproc interface for X11 which doesn't seem to include - the main NetHack header files */ +/* These would be in sym.h and display.h if they weren't needed to + define the windowproc interface for X11 which doesn't include + most of the main NetHack header files */ -typedef struct glyph_map_entry { +struct classic_representation { int color; int symidx; +}; + +struct unicode_representation { + uint32 ucolor; + uint16 u256coloridx; + uint8 *utf8str; +}; + +typedef struct glyph_map_entry { unsigned glyphflags; + struct classic_representation sym; short int tileidx; +#ifdef ENHANCED_SYMBOLS + struct unicode_representation *u; +#endif } glyph_map; /* glyph plus additional info diff --git a/src/allmain.c b/src/allmain.c index 7505ca3b4..b379c51cd 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -829,7 +829,10 @@ static const struct early_opt earlyopts[] = { {ARG_SHOWPATHS, "showpaths", 9, FALSE}, #ifndef NODUMPENUMS {ARG_DUMPENUMS, "dumpenums", 9, FALSE}, +#ifdef ENHANCED_SYMBOLS + {ARG_DUMPGLYPHIDS, "dumpglyphids", 12, FALSE}, #endif +#endif /* NODUMPENUMS */ #ifdef WIN32 {ARG_WINDOWS, "windows", 4, TRUE}, #endif @@ -913,7 +916,13 @@ argcheck(int argc, char *argv[], enum earlyarg e_arg) dump_enums(); return 2; } +#ifdef ENHANCED_SYMBOLS + case ARG_DUMPGLYPHIDS: { + dump_glyphids(); + return 2; + } #endif +#endif /* NODUMPENUMS */ #ifdef WIN32 case ARG_WINDOWS: { if (extended_opt) { @@ -1006,6 +1015,17 @@ timet_delta(time_t etim, time_t stim) /* end and start times */ } #ifndef NODUMPENUMS +#define DUMP_ENUMS +struct enum_dump monsdump[] = { +#include "monsters.h" + { NUMMONS, "NUMMONS" }, +}; +struct enum_dump objdump[] = { +#include "objects.h" + { NUM_OBJECTS, "NUM_OBJECTS" }, +}; +#undef DUMP_ENUMS + void dump_enums(void) { @@ -1018,21 +1038,6 @@ dump_enums(void) }; static const char *const titles[NUM_ENUM_DUMPS] = { "monnums", "objects_nums" , "misc_object_nums" }; - struct enum_dump { - int val; - const char *nm; - }; - -#define DUMP_ENUMS - struct enum_dump monsdump[] = { -#include "monsters.h" - { NUMMONS, "NUMMONS" }, - }; - struct enum_dump objdump[] = { -#include "objects.h" - { NUM_OBJECTS, "NUM_OBJECTS" }, - }; -#undef DUMP_ENUMS struct enum_dump omdump[] = { { LAST_GEM, "LAST_GEM" }, @@ -1058,6 +1063,14 @@ dump_enums(void) } raw_print(""); } + +#ifdef ENHANCED_SYMBOLS +void +dump_glyphids(void) +{ + dump_all_glyphids(stdout); +} +#endif /* ENHANCED_SYMBOLS */ #endif /* NODUMPENUMS */ /*allmain.c*/ diff --git a/src/decl.c b/src/decl.c index 805df492d..19b87151f 100644 --- a/src/decl.c +++ b/src/decl.c @@ -373,9 +373,12 @@ const struct instance_globals g_init = { UNDEFINED_VALUES, /* nowhere */ UNDEFINED_PTR, /* gate_str */ - /* drawing.c */ + /* symbols.c */ DUMMY, /* symset */ - 0, /* currentgraphics */ +#ifdef ENHANCED_SYMBOLS + DUMMY, /* symset_customizations */ +#endif + 0, /* currentgraphics */ DUMMY, /* showsyms */ DUMMY, /* primary_syms */ DUMMY, /* rogue_syms */ diff --git a/src/display.c b/src/display.c index 5463de710..efe4bf583 100644 --- a/src/display.c +++ b/src/display.c @@ -1363,9 +1363,14 @@ see_traps(void) } } -/* glyph, ttychar, {color, symidx, glyphflags, tileidx } */ +/* glyph, ttychar, { glyphflags, { sym.color, sym.symidx }, + tileidx, u } */ static glyph_info no_ginfo = { - NO_GLYPH, ' ', { NO_COLOR, 0, MG_BADXY, 0 } + NO_GLYPH, ' ', { MG_BADXY, { NO_COLOR, 0 }, 0 +#ifdef ENHANCED_SYMBOLS + , 0 +#endif + } }; #ifndef UNBUFFERED_GLYPHINFO #define Glyphinfo_at(x, y, glyph) \ @@ -1379,12 +1384,17 @@ static glyph_info ginfo; #ifdef USE_TILES extern const glyph_info nul_glyphinfo; /* tile.c */ #else -/* glyph, ttychar, { color, symidx, glyphflags, tileidx} */ +/* glyph, ttychar, { glyphflags, { sym.color, sym.symidx }, + tileidx, 0} */ const glyph_info nul_glyphinfo = { NO_GLYPH, ' ', { /* glyph_map */ - NO_COLOR, SYM_UNEXPLORED + SYM_OFF_X, - MG_UNEXPL, 0 + MG_UNEXPL, + { NO_COLOR, SYM_UNEXPLORED + SYM_OFF_X }, + 0 +#ifdef ENHANCED_SYMBOLS + , 0 +#endif } }; #endif @@ -1393,7 +1403,7 @@ const glyph_info nul_glyphinfo = { extern glyph_map glyphmap[MAX_GLYPH]; /* from tile.c */ #else glyph_map glyphmap[MAX_GLYPH] = { - {0, 0, 0U, 0 } + { 0U, { 0, 0}, 0, 0 } }; #endif @@ -1686,7 +1696,7 @@ show_glyph(int x, int y, int glyph) be thorough and check everything */ || g.gbuf[y][x].glyphinfo.ttychar != glyphinfo.ttychar || g.gbuf[y][x].glyphinfo.gm.glyphflags != glyphinfo.gm.glyphflags - || g.gbuf[y][x].glyphinfo.gm.color != glyphinfo.gm.color + || g.gbuf[y][x].glyphinfo.gm.sym.color != glyphinfo.gm.sym.color || g.gbuf[y][x].glyphinfo.gm.tileidx != glyphinfo.gm.tileidx #endif || iflags.use_background_glyph) { @@ -1721,7 +1731,12 @@ show_glyph(int x, int y, int glyph) static gbuf_entry nul_gbuf = { 0, /* gnew */ { GLYPH_UNEXPLORED, (unsigned) ' ', /* glyphinfo.glyph */ - { (unsigned) NO_COLOR, 0, MG_UNEXPL, 0 } /* glyphinfo.gm */ + /* glyphinfo.gm */ + { MG_UNEXPL, { (unsigned) NO_COLOR, 0 }, 0 +#ifdef ENHANCED_SYMBOLS + , 0 +#endif + } } }; @@ -1743,13 +1758,13 @@ clear_glyph_buffer(void) #endif #ifndef UNBUFFERED_GLYPHINFO nul_gbuf.gnew = (giptr->ttychar != nul_gbuf.glyphinfo.ttychar - || giptr->gm.color != nul_gbuf.glyphinfo.gm.color + || giptr->gm.sym.color != nul_gbuf.glyphinfo.gm.sym.color || giptr->gm.glyphflags != nul_gbuf.glyphinfo.gm.glyphflags || giptr->gm.tileidx != nul_gbuf.glyphinfo.gm.tileidx) #else nul_gbuf.gnew = (giptr->glyphinfo.ttychar != ' ' - || giptr->gm.color != NO_COLOR + || giptr->gm.sym.color != NO_COLOR || (giptr->gm.glyphflags & ~MG_UNEXPL) != 0) #endif ? 1 : 0; @@ -1782,12 +1797,12 @@ row_refresh(int start, int stop, int y) #endif #ifndef UNBUFFERED_GLYPHINFO force = (giptr->ttychar != nul_gbuf.glyphinfo.ttychar - || giptr->gm.color != nul_gbuf.glyphinfo.gm.color + || giptr->gm.sym.color != nul_gbuf.glyphinfo.gm.sym.color || giptr->gm.glyphflags != nul_gbuf.glyphinfo.gm.glyphflags || giptr->gm.tileidx != nul_gbuf.glyphinfo.gm.tileidx) #else force = (giptr->ttychar != ' ' - || giptr->gm.color != NO_COLOR + || giptr->gm.sym.color != NO_COLOR || (giptr->gm.glyphflags & ~MG_UNEXPL) != 0) #endif ? 1 : 0; @@ -2193,7 +2208,7 @@ map_glyphinfo( && glyph_is_monster(glyph)); const glyph_map *gmap = &glyphmap[glyph]; - glyphinfo->gm = *gmap; /* glyphflags, symidx, color, tileidx */ + glyphinfo->gm = *gmap; /* glyphflags, sym.symidx, sym.color, tileidx */ /* * Only hero tinkering permitted on-the-fly (who). * Unique glyphs in glyphmap[] determine everything else (what). @@ -2210,12 +2225,12 @@ map_glyphinfo( } else if (HAS_ROGUE_IBM_GRAPHICS && g.symset[g.currentgraphics].nocolor == 0) { /* actually player should be yellow-on-gray if in corridor */ - glyphinfo->gm.color = CLR_YELLOW; + glyphinfo->gm.sym.color = CLR_YELLOW; } else if (flags.showrace) { /* for showrace, non-human hero is displayed by the symbol of corresponding type of monster rather than by '@' (handled by newsym()); we change the color to same as human hero */ - glyphinfo->gm.color = HI_DOMESTIC; + glyphinfo->gm.sym.color = HI_DOMESTIC; } #endif /* accessibility @@ -2227,7 +2242,7 @@ map_glyphinfo( if ((g.glyphmap_perlevel_flags & GMAP_ROGUELEVEL) ? g.ov_rogue_syms[offset] : g.ov_primary_syms[offset]) - glyphinfo->gm.symidx = offset; + glyphinfo->gm.sym.symidx = offset; } glyphinfo->gm.glyphflags |= MG_HERO; } @@ -2235,9 +2250,9 @@ map_glyphinfo( && (mgflags & MG_FLAG_NOOVERRIDE) && glyph_is_pet(glyph)) { /* one more accessiblity kludge; turn off override symbol if caller has specfieid NOOVERRIDE */ - glyphinfo->gm.symidx = mons[glyph_to_mon(glyph)].mlet + SYM_OFF_M; + glyphinfo->gm.sym.symidx = mons[glyph_to_mon(glyph)].mlet + SYM_OFF_M; } - glyphinfo->ttychar = g.showsyms[glyphinfo->gm.symidx]; + glyphinfo->ttychar = g.showsyms[glyphinfo->gm.sym.symidx]; glyphinfo->glyph = glyph; } @@ -2382,38 +2397,38 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) * offsets. The order is set in display.h. */ if ((offset = (glyph - GLYPH_NOTHING_OFF)) >= 0) { - gmap->symidx = SYM_NOTHING + SYM_OFF_X; + gmap->sym.symidx = SYM_NOTHING + SYM_OFF_X; color = NO_COLOR; gmap->glyphflags |= MG_NOTHING; } else if ((offset = (glyph - GLYPH_UNEXPLORED_OFF)) >= 0) { - gmap->symidx = SYM_UNEXPLORED + SYM_OFF_X; + gmap->sym.symidx = SYM_UNEXPLORED + SYM_OFF_X; color = NO_COLOR; gmap->glyphflags |= MG_UNEXPL; } else if ((offset = (glyph - GLYPH_STATUE_FEM_PILETOP_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = CLR_RED; else obj_color(STATUE); gmap->glyphflags |= (MG_STATUE | MG_FEMALE | MG_OBJPILE); } else if ((offset = (glyph - GLYPH_STATUE_MALE_PILETOP_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = CLR_RED; else obj_color(STATUE); gmap->glyphflags |= (MG_STATUE | MG_MALE | MG_OBJPILE); } else if ((offset = (glyph - GLYPH_BODY_PILETOP_OFF)) >= 0) { - gmap->symidx = objects[CORPSE].oc_class + SYM_OFF_O; + gmap->sym.symidx = objects[CORPSE].oc_class + SYM_OFF_O; if (has_rogue_color) color = CLR_RED; else mon_color(offset); gmap->glyphflags |= (MG_CORPSE | MG_OBJPILE); } else if ((offset = (glyph - GLYPH_OBJ_PILETOP_OFF)) >= 0) { - gmap->symidx = objects[offset].oc_class + SYM_OFF_O; + gmap->sym.symidx = objects[offset].oc_class + SYM_OFF_O; if (offset == BOULDER) - gmap->symidx = SYM_BOULDER + SYM_OFF_X; + gmap->sym.symidx = SYM_BOULDER + SYM_OFF_X; if (has_rogue_color) { switch (objects[offset].oc_class) { case COIN_CLASS: @@ -2430,14 +2445,14 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) obj_color(offset); gmap->glyphflags |= MG_OBJPILE; } else if ((offset = (glyph - GLYPH_STATUE_FEM_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = CLR_RED; else obj_color(STATUE); gmap->glyphflags |= (MG_STATUE | MG_FEMALE); } else if ((offset = (glyph - GLYPH_STATUE_MALE_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = CLR_RED; else @@ -2445,70 +2460,70 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) gmap->glyphflags |= (MG_STATUE | MG_MALE); } else if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) { /* warn flash */ - gmap->symidx = offset + SYM_OFF_W; + gmap->sym.symidx = offset + SYM_OFF_W; if (has_rogue_color) color = NO_COLOR; else warn_color(offset); } else if ((offset = (glyph - GLYPH_EXPLODE_FROSTY_OFF)) >= 0) { - gmap->symidx = S_expl_tl + offset + SYM_OFF_P; + gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_frosty); } else if ((offset = (glyph - GLYPH_EXPLODE_FIERY_OFF)) >= 0) { - gmap->symidx = S_expl_tl + offset + SYM_OFF_P; + gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_fiery); } else if ((offset = (glyph - GLYPH_EXPLODE_MAGICAL_OFF)) >= 0) { - gmap->symidx = S_expl_tl + offset + SYM_OFF_P; + gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_magical); } else if ((offset = (glyph - GLYPH_EXPLODE_WET_OFF)) >= 0) { - gmap->symidx = S_expl_tl + offset + SYM_OFF_P; + gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_wet); } else if ((offset = (glyph - GLYPH_EXPLODE_MUDDY_OFF)) >= 0) { - gmap->symidx = S_expl_tl + offset + SYM_OFF_P; + gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_muddy); } else if ((offset = (glyph - GLYPH_EXPLODE_NOXIOUS_OFF)) >= 0) { - gmap->symidx = S_expl_tl + offset + SYM_OFF_P; + gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_noxious); } else if ((offset = (glyph - GLYPH_EXPLODE_DARK_OFF)) >= 0) { - gmap->symidx = S_expl_tl + offset + SYM_OFF_P; + gmap->sym.symidx = S_expl_tl + offset + SYM_OFF_P; explode_color(expl_dark); } else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) { /* see swallow_to_glyph() in display.c */ - gmap->symidx = (S_sw_tl + (offset & 0x7)) + SYM_OFF_P; + gmap->sym.symidx = (S_sw_tl + (offset & 0x7)) + SYM_OFF_P; if (has_rogue_color) color = NO_COLOR; else mon_color(offset >> 3); } else if ((offset = (glyph - GLYPH_CMAP_C_OFF)) >= 0) { - gmap->symidx = S_digbeam + offset + SYM_OFF_P; + gmap->sym.symidx = S_digbeam + offset + SYM_OFF_P; if (has_rogue_color) color = cmap_to_roguecolor(S_digbeam + offset); else cmap_color(S_digbeam + offset); } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) { /* see zapdir_to_glyph() in display.c */ - gmap->symidx = (S_vbeam + (offset & 0x3)) + SYM_OFF_P; + gmap->sym.symidx = (S_vbeam + (offset & 0x3)) + SYM_OFF_P; if (has_rogue_color) color = NO_COLOR; else zap_color((offset >> 2)); } else if ((offset = (glyph - GLYPH_CMAP_B_OFF)) >= 0) { int cmap = S_grave + offset; - gmap->symidx = cmap + SYM_OFF_P; + gmap->sym.symidx = cmap + SYM_OFF_P; cmap_color(cmap); if (!iflags.use_color) { /* try to provide a visible difference between water and lava if they use the same symbol and color is disabled */ if (cmap == S_lava - && (g.showsyms[gmap->symidx] + && (g.showsyms[gmap->sym.symidx] == g.showsyms[S_pool + SYM_OFF_P] - || g.showsyms[gmap->symidx] + || g.showsyms[gmap->sym.symidx] == g.showsyms[S_water + SYM_OFF_P])) { gmap->glyphflags |= MG_BW_LAVA; /* similar for floor [what about empty doorway?] and ice */ } else if (offset == S_ice - && (g.showsyms[gmap->symidx] + && (g.showsyms[gmap->sym.symidx] == g.showsyms[S_room + SYM_OFF_P] - || g.showsyms[gmap->symidx] + || g.showsyms[gmap->sym.symidx] == g.showsyms[S_darkroom + SYM_OFF_P])) { gmap->glyphflags |= MG_BW_ICE; @@ -2518,14 +2533,14 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) } } else if ((offset = (glyph - GLYPH_ALTAR_OFF)) >= 0) { /* unaligned, chaotic, neutral, lawful, other altar */ - gmap->symidx = S_altar + SYM_OFF_P; + gmap->sym.symidx = S_altar + SYM_OFF_P; if (has_rogue_color) color = cmap_to_roguecolor(S_altar); else altar_color(offset); } else if ((offset = (glyph - GLYPH_CMAP_A_OFF)) >= 0) { int cmap = S_ndoor + offset; - gmap->symidx = cmap + SYM_OFF_P; + gmap->sym.symidx = cmap + SYM_OFF_P; cmap_color(cmap); /* * Some speciality color mappings not hardcoded in data init @@ -2536,36 +2551,36 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) /* provide a visible difference if normal and lit corridor use the same symbol */ } else if ((cmap == S_litcorr) - && g.showsyms[gmap->symidx] + && g.showsyms[gmap->sym.symidx] == g.showsyms[S_corr + SYM_OFF_P]) { color = CLR_WHITE; #endif } } else if ((offset = (glyph - GLYPH_CMAP_SOKO_OFF)) >= 0) { - gmap->symidx = S_vwall + offset + SYM_OFF_P; + gmap->sym.symidx = S_vwall + offset + SYM_OFF_P; wall_color(sokoban_walls); } else if ((offset = (glyph - GLYPH_CMAP_KNOX_OFF)) >= 0) { - gmap->symidx = S_vwall + offset + SYM_OFF_P; + gmap->sym.symidx = S_vwall + offset + SYM_OFF_P; wall_color(knox_walls); } else if ((offset = (glyph - GLYPH_CMAP_GEH_OFF)) >= 0) { - gmap->symidx = S_vwall + offset + SYM_OFF_P; + gmap->sym.symidx = S_vwall + offset + SYM_OFF_P; wall_color(gehennom_walls); } else if ((offset = (glyph - GLYPH_CMAP_MINES_OFF)) >= 0) { - gmap->symidx = S_vwall + offset + SYM_OFF_P; + gmap->sym.symidx = S_vwall + offset + SYM_OFF_P; wall_color(mines_walls); } else if ((offset = (glyph - GLYPH_CMAP_MAIN_OFF)) >= 0) { - gmap->symidx = S_vwall + offset + SYM_OFF_P; + gmap->sym.symidx = S_vwall + offset + SYM_OFF_P; if (has_rogue_color) color = cmap_to_roguecolor(S_vwall + offset); else wall_color(main_walls); } else if ((offset = (glyph - GLYPH_CMAP_STONE_OFF)) >= 0) { - gmap->symidx = SYM_OFF_P; + gmap->sym.symidx = SYM_OFF_P; cmap_color(S_stone); } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) { - gmap->symidx = objects[offset].oc_class + SYM_OFF_O; + gmap->sym.symidx = objects[offset].oc_class + SYM_OFF_O; if (offset == BOULDER) - gmap->symidx = SYM_BOULDER + SYM_OFF_X; + gmap->sym.symidx = SYM_BOULDER + SYM_OFF_X; if (has_rogue_color) { switch (objects[offset].oc_class) { case COIN_CLASS: @@ -2581,7 +2596,7 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) } else obj_color(offset); } else if ((offset = (glyph - GLYPH_RIDDEN_FEM_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) /* This currently implies that the hero is here -- monsters */ /* don't ride (yet...). Should we set it to yellow like in */ @@ -2592,21 +2607,21 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) mon_color(offset); gmap->glyphflags |= (MG_RIDDEN | MG_FEMALE); } else if ((offset = (glyph - GLYPH_RIDDEN_MALE_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = NO_COLOR; else mon_color(offset); gmap->glyphflags |= (MG_RIDDEN | MG_MALE); } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) { - gmap->symidx = objects[CORPSE].oc_class + SYM_OFF_O; + gmap->sym.symidx = objects[CORPSE].oc_class + SYM_OFF_O; if (has_rogue_color) color = CLR_RED; else mon_color(offset); gmap->glyphflags |= MG_CORPSE; } else if ((offset = (glyph - GLYPH_DETECT_FEM_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = NO_COLOR; else @@ -2615,7 +2630,7 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) /* is_reverse = TRUE; */ gmap->glyphflags |= (MG_DETECT | MG_FEMALE); } else if ((offset = (glyph - GLYPH_DETECT_MALE_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = NO_COLOR; else @@ -2624,28 +2639,28 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) /* is_reverse = TRUE; */ gmap->glyphflags |= (MG_DETECT | MG_MALE); } else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) { - gmap->symidx = SYM_INVISIBLE + SYM_OFF_X; + gmap->sym.symidx = SYM_INVISIBLE + SYM_OFF_X; if (has_rogue_color) color = NO_COLOR; else invis_color(offset); gmap->glyphflags |= MG_INVIS; } else if ((offset = (glyph - GLYPH_PET_FEM_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = NO_COLOR; else pet_color(offset); gmap->glyphflags |= (MG_PET | MG_FEMALE); } else if ((offset = (glyph - GLYPH_PET_MALE_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) color = NO_COLOR; else pet_color(offset); gmap->glyphflags |= (MG_PET | MG_MALE); } else if ((offset = (glyph - GLYPH_MON_FEM_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) { color = NO_COLOR; } else { @@ -2653,7 +2668,7 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) } gmap->glyphflags |= MG_FEMALE; } else if ((offset = (glyph - GLYPH_MON_MALE_OFF)) >= 0) { - gmap->symidx = mons[offset].mlet + SYM_OFF_M; + gmap->sym.symidx = mons[offset].mlet + SYM_OFF_M; if (has_rogue_color) { color = CLR_YELLOW; } else { @@ -2669,7 +2684,7 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) : g.ov_primary_syms[SYM_PET_OVERRIDE + SYM_OFF_X]); if (g.showsyms[pet_override] != ' ') - gmap->symidx = SYM_PET_OVERRIDE + SYM_OFF_X; + gmap->sym.symidx = SYM_PET_OVERRIDE + SYM_OFF_X; } #ifdef TEXTCOLOR /* Turn off color if no color defined, or rogue level w/o PC graphics. @@ -2679,7 +2694,7 @@ reset_glyphmap(enum glyphmap_change_triggers trigger) && !has_rogue_color)) || !iflags.use_color) #endif color = NO_COLOR; - gmap->color = color; + gmap->sym.color = color; } } diff --git a/src/do.c b/src/do.c index ab39c438f..104e1a75e 100644 --- a/src/do.c +++ b/src/do.c @@ -1455,7 +1455,7 @@ goto_level( } if (Is_rogue_level(newlevel) || Is_rogue_level(&u.uz)) - assign_graphics(Is_rogue_level(newlevel) ? ROGUESET : PRIMARY); + assign_graphics(Is_rogue_level(newlevel) ? ROGUESET : PRIMARYSET); check_gold_symbol(); /* record this level transition as a potential seen branch unless using * some non-standard means of transportation (level teleport). diff --git a/src/drawing.c b/src/drawing.c index 50589e032..bba568f94 100644 --- a/src/drawing.c +++ b/src/drawing.c @@ -6,6 +6,7 @@ #include "color.h" #include "rm.h" #include "objclass.h" +#include "wintype.h" #include "sym.h" /* Relevant header information in rm.h, objclass.h, sym.h, defsym.h. */ diff --git a/src/files.c b/src/files.c index 63276e764..38429c6da 100644 --- a/src/files.c +++ b/src/files.c @@ -165,8 +165,6 @@ static void wizkit_addinv(struct obj *); boolean proc_wizkit_line(char *buf); void read_wizkit(void); static FILE *fopen_sym_file(void); -boolean proc_symset_line(char *); -static void set_symhandling(char *, int); #ifdef SELF_RECOVER static boolean copy_bytes(int, int); @@ -2678,7 +2676,7 @@ parse_config_line(char *origbuf) } switch_symbols(TRUE); } else if (match_varname(buf, "SYMBOLS", 4)) { - if (!parsesymbols(bufp, PRIMARY)) { + if (!parsesymbols(bufp, PRIMARYSET)) { config_error_add("Error in SYMBOLS definition '%s'", bufp); retval = FALSE; } @@ -3443,7 +3441,7 @@ fopen_sym_file(void) } /* - * Returns 1 if the chose symset was found and loaded. + * Returns 1 if the chosen symset was found and loaded. * 0 if it wasn't found in the sym file or other problem. */ int @@ -3492,214 +3490,6 @@ read_sym_file(int which_set) return 1; } -boolean -proc_symset_line(char *buf) -{ - return !((boolean) parse_sym_line(buf, g.symset_which_set)); -} - -/* returns 0 on error */ -int -parse_sym_line(char *buf, int which_set) -{ - int val, i; - struct symparse *symp; - char *bufp, *commentp, *altp; - - if (strlen(buf) >= BUFSZ) - buf[BUFSZ - 1] = '\0'; - /* convert each instance of whitespace (tabs, consecutive spaces) - into a single space; leading and trailing spaces are stripped */ - mungspaces(buf); - - /* remove trailing comment, if any (this isn't strictly needed for - individual symbols, and it won't matter if "X#comment" without - separating space slips through; for handling or set description, - symbol set creator is responsible for preceding '#' with a space - and that comment itself doesn't contain " #") */ - if ((commentp = rindex(buf, '#')) != 0 && commentp[-1] == ' ') - commentp[-1] = '\0'; - - /* find the '=' or ':' */ - bufp = index(buf, '='); - altp = index(buf, ':'); - if (!bufp || (altp && altp < bufp)) - bufp = altp; - if (!bufp) { - if (strncmpi(buf, "finish", 6) == 0) { - /* end current graphics set */ - if (g.chosen_symset_start) - g.chosen_symset_end = TRUE; - g.chosen_symset_start = FALSE; - return 1; - } - config_error_add("No \"finish\""); - return 0; - } - /* skip '=' and space which follows, if any */ - ++bufp; - if (*bufp == ' ') - ++bufp; - - symp = match_sym(buf); - if (!symp) { - config_error_add("Unknown sym keyword"); - return 0; - } - - if (!g.symset[which_set].name) { - /* A null symset name indicates that we're just - building a pick-list of possible symset - values from the file, so only do that */ - if (symp->range == SYM_CONTROL) { - struct symsetentry *tmpsp, *lastsp; - - for (lastsp = g.symset_list; lastsp; lastsp = lastsp->next) - if (!lastsp->next) - break; - switch (symp->idx) { - case 0: - tmpsp = (struct symsetentry *) alloc(sizeof *tmpsp); - tmpsp->next = (struct symsetentry *) 0; - if (!lastsp) - g.symset_list = tmpsp; - else - lastsp->next = tmpsp; - tmpsp->idx = g.symset_count++; - tmpsp->name = dupstr(bufp); - tmpsp->desc = (char *) 0; - tmpsp->handling = H_UNK; - /* initialize restriction bits */ - tmpsp->nocolor = 0; - tmpsp->primary = 0; - tmpsp->rogue = 0; - break; - case 2: - /* handler type identified */ - tmpsp = lastsp; /* most recent symset */ - for (i = 0; known_handling[i]; ++i) - if (!strcmpi(known_handling[i], bufp)) { - tmpsp->handling = i; - break; /* for loop */ - } - break; - case 3: - /* description:something */ - tmpsp = lastsp; /* most recent symset */ - if (tmpsp && !tmpsp->desc) - tmpsp->desc = dupstr(bufp); - break; - case 5: - /* restrictions: xxxx*/ - tmpsp = lastsp; /* most recent symset */ - for (i = 0; known_restrictions[i]; ++i) { - if (!strcmpi(known_restrictions[i], bufp)) { - switch (i) { - case 0: - tmpsp->primary = 1; - break; - case 1: - tmpsp->rogue = 1; - break; - } - break; /* while loop */ - } - } - break; - } - } - return 1; - } - if (symp->range) { - if (symp->range == SYM_CONTROL) { - switch (symp->idx) { - case 0: - /* start of symset */ - if (!strcmpi(bufp, g.symset[which_set].name)) { - /* matches desired one */ - g.chosen_symset_start = TRUE; - /* these init_*() functions clear symset fields too */ - if (which_set == ROGUESET) - init_rogue_symbols(); - else if (which_set == PRIMARY) - init_primary_symbols(); - } - break; - case 1: - /* finish symset */ - if (g.chosen_symset_start) - g.chosen_symset_end = TRUE; - g.chosen_symset_start = FALSE; - break; - case 2: - /* handler type identified */ - if (g.chosen_symset_start) - set_symhandling(bufp, which_set); - break; - /* case 3: (description) is ignored here */ - case 4: /* color:off */ - if (g.chosen_symset_start) { - if (bufp) { - if (!strcmpi(bufp, "true") || !strcmpi(bufp, "yes") - || !strcmpi(bufp, "on")) - g.symset[which_set].nocolor = 0; - else if (!strcmpi(bufp, "false") - || !strcmpi(bufp, "no") - || !strcmpi(bufp, "off")) - g.symset[which_set].nocolor = 1; - } - } - break; - case 5: /* restrictions: xxxx*/ - if (g.chosen_symset_start) { - int n = 0; - - while (known_restrictions[n]) { - if (!strcmpi(known_restrictions[n], bufp)) { - switch (n) { - case 0: - g.symset[which_set].primary = 1; - break; - case 1: - g.symset[which_set].rogue = 1; - break; - } - break; /* while loop */ - } - n++; - } - } - break; - } - } else { /* !SYM_CONTROL */ - val = sym_val(bufp); - if (g.chosen_symset_start) { - if (which_set == PRIMARY) { - update_primary_symset(symp, val); - } else if (which_set == ROGUESET) { - update_rogue_symset(symp, val); - } - } - } - } - return 1; -} - -static void -set_symhandling(char *handling, int which_set) -{ - int i = 0; - - g.symset[which_set].handling = H_UNK; - while (known_handling[i]) { - if (!strcmpi(known_handling[i], handling)) { - g.symset[which_set].handling = i; - return; - } - i++; - } -} - /* ---------- END SYMSET FILE HANDLING ----------- */ /* ---------- BEGIN SCOREBOARD CREATION ----------- */ diff --git a/src/hacklib.c b/src/hacklib.c index 971f9c4e8..532046c48 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -1378,4 +1378,49 @@ FITSuint_(unsigned long long i, const char *file, int line){ return (unsigned)i; } +#ifdef ENHANCED_SYMBOLS + +/* Unicode routines */ + +int +unicodeval_to_utf8str(int uval, uint8 *buffer, size_t bufsz) +{ + // static uint8 buffer[7]; + uint8 *b = buffer; + + if (bufsz < 5) + return 0; + /* + * Binary Hex Comments + * 0xxxxxxx 0x00..0x7F Only byte of a 1-byte character encoding + * 10xxxxxx 0x80..0xBF Continuation byte : one of 1-3 bytes following + * first 110xxxxx 0xC0..0xDF First byte of a 2-byte character encoding + * 1110xxxx 0xE0..0xEF First byte of a 3-byte character encoding + * 11110xxx 0xF0..0xF7 First byte of a 4-byte character encoding + */ + *b = '\0'; + if (uval < 0x80) { + *b++ = uval; + } else if (uval < 0x800) { + *b++ = 192 + uval / 64; + *b++ = 128 + uval % 64; + } else if (uval - 0xd800u < 0x800) { + return 0; + } else if (uval < 0x10000) { + *b++ = 224 + uval / 4096; + *b++ = 128 + uval / 64 % 64; + *b++ = 128 + uval % 64; + } else if (uval < 0x110000) { + *b++ = 240 + uval / 262144; + *b++ = 128 + uval / 4096 % 64; + *b++ = 128 + uval / 64 % 64; + *b++ = 128 + uval % 64; + } else { + return 0; + } + *b = '\0'; /* NUL terminate */ + return 1; +} +#endif /* ENHANCED_SYMBOLS */ + /*hacklib.c*/ diff --git a/src/mdlib.c b/src/mdlib.c index 770cd8cab..d9d7edda1 100644 --- a/src/mdlib.c +++ b/src/mdlib.c @@ -16,6 +16,7 @@ #include "config.h" #include "permonst.h" #include "objclass.h" +#include "wintype.h" #include "sym.h" #include "artilist.h" #include "dungeon.h" diff --git a/src/monst.c b/src/monst.c index ce1107248..2ac7f0781 100644 --- a/src/monst.c +++ b/src/monst.c @@ -5,6 +5,7 @@ #include "config.h" #include "permonst.h" +#include "wintype.h" #include "sym.h" #ifdef C diff --git a/src/options.c b/src/options.c index 743349bbb..04bb64b0e 100644 --- a/src/options.c +++ b/src/options.c @@ -505,7 +505,7 @@ parseoptions(register char *opts, boolean tinitial, boolean tfrom_file) if (!got_match) { /* Is it a symbol? */ - if (strstr(opts, "S_") == opts && parsesymbols(opts, PRIMARY)) { + if (strstr(opts, "S_") == opts && parsesymbols(opts, PRIMARYSET)) { switch_symbols(TRUE); check_gold_symbol(); optresult = optn_ok; @@ -872,7 +872,7 @@ optfn_boulder(int optidx UNUSED, int req, boolean negated UNUSED, if (!g.opt_initial) { nhsym sym = get_othersym(SYM_BOULDER, Is_rogue_level(&u.uz) ? ROGUESET - : PRIMARY); + : PRIMARYSET); if (sym) g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym; @@ -946,13 +946,13 @@ optfn_cursesgraphics(int optidx, int req, boolean negated, #ifdef BACKWARD_COMPAT if (!negated) { /* There is no rogue level cursesgraphics-specific set */ - if (g.symset[PRIMARY].name) { + if (g.symset[PRIMARYSET].name) { badflag = TRUE; } else { - g.symset[PRIMARY].name = dupstr(allopt[optidx].name); - if (!read_sym_file(PRIMARY)) { + g.symset[PRIMARYSET].name = dupstr(allopt[optidx].name); + if (!read_sym_file(PRIMARYSET)) { badflag = TRUE; - clear_symsetentry(PRIMARY, TRUE); + clear_symsetentry(PRIMARYSET, TRUE); } else switch_symbols(TRUE); } @@ -996,13 +996,13 @@ optfn_DECgraphics(int optidx, int req, boolean negated, #ifdef BACKWARD_COMPAT if (!negated) { /* There is no rogue level DECgraphics-specific set */ - if (g.symset[PRIMARY].name) { + if (g.symset[PRIMARYSET].name) { badflag = TRUE; } else { - g.symset[PRIMARY].name = dupstr(allopt[optidx].name); - if (!read_sym_file(PRIMARY)) { + g.symset[PRIMARYSET].name = dupstr(allopt[optidx].name); + if (!read_sym_file(PRIMARYSET)) { badflag = TRUE; - clear_symsetentry(PRIMARY, TRUE); + clear_symsetentry(PRIMARYSET, TRUE); } else switch_symbols(TRUE); } @@ -1390,6 +1390,43 @@ optfn_gender(int optidx, int req, boolean negated, char *opts, char *op) return optn_ok; } +static int +optfn_glyph(int optidx UNUSED, int req, boolean negated, char *opts, char *op) +{ +#ifdef ENHANCED_SYMBOLS + int glyph; +#endif + + if (req == do_init) { + return optn_ok; + } + if (req == do_set) { + /* OPTION=glyph:G_glyph/U+NNNN/r-g-b */ + if (negated) { + if (op != empty_optstr) { + bad_negation("glyph", TRUE); + return optn_err; + } + } + if (op == empty_optstr) + return optn_err; + /* strip leading/trailing spaces, condense internal ones (3.6.2) */ + mungspaces(op); +#ifdef ENHANCED_SYMBOLS + if (!glyphrep_to_custom_map_entries(op, &glyph)) + return optn_err; +#endif + return optn_ok; + } + if (req == get_val) { + if (!opts) + return optn_err; + Sprintf(opts, "%s", to_be_done); + return optn_ok; + } + return optn_ok; +} + static int optfn_hilite_status( int optidx UNUSED, @@ -1534,13 +1571,13 @@ optfn_MACgraphics(int optidx, int req, boolean negated, char *opts, char *op) if (req == do_set) { /* "MACgraphics" */ if (!negated) { - if (g.symset[PRIMARY].name) { + if (g.symset[PRIMARYSET].name) { badflag = TRUE; } else { - g.symset[PRIMARY].name = dupstr(allopt[optidx].name); - if (!read_sym_file(PRIMARY)) { + g.symset[PRIMARYSET].name = dupstr(allopt[optidx].name); + if (!read_sym_file(PRIMARYSET)) { badflag = TRUE; - clear_symsetentry(PRIMARY, TRUE); + clear_symsetentry(PRIMARYSET, TRUE); } } if (badflag) { @@ -3303,25 +3340,37 @@ optfn_suppress_alert(int optidx, int req, boolean negated, return optn_ok; } +extern const char *known_handling[]; /* symbols.c */ +extern const char *known_restrictions[]; /* symbols.c */ + static int -optfn_symset(int optidx UNUSED, int req, boolean negated UNUSED, - char *opts, char *op) +optfn_symset(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, + char *op) { if (req == do_init) { return optn_ok; } if (req == do_set) { if (op != empty_optstr) { - g.symset[PRIMARY].name = dupstr(op); - if (!read_sym_file(PRIMARY)) { - clear_symsetentry(PRIMARY, TRUE); + g.symset[PRIMARYSET].name = dupstr(op); + if (!read_sym_file(PRIMARYSET)) { + clear_symsetentry(PRIMARYSET, TRUE); config_error_add( "Unable to load symbol set \"%s\" from \"%s\"", op, SYMBOLS); return optn_err; } else { - switch_symbols(g.symset[PRIMARY].name != (char *) 0); - g.opt_need_redraw = TRUE; + if (g.symset[PRIMARYSET].handling) { +#ifndef ENHANCED_SYMBOLS + if (g.symset[PRIMARYSET].handling == H_UTF8) { + config_error_add("Unavailable symset handler \"%s\" for %s", + known_handling[H_UTF8], op); + load_symset("default", PRIMARYSET); + } +#endif + switch_symbols(g.symset[PRIMARYSET].name != (char *) 0); + g.opt_need_redraw = g.opt_need_glyph_reset = TRUE; + } } } else return optn_err; @@ -3331,13 +3380,32 @@ optfn_symset(int optidx UNUSED, int req, boolean negated UNUSED, if (!opts) return optn_err; Sprintf(opts, "%s", - g.symset[PRIMARY].name ? g.symset[PRIMARY].name : "default"); - if (g.currentgraphics == PRIMARY && g.symset[PRIMARY].name) + g.symset[PRIMARYSET].name ? g.symset[PRIMARYSET].name : "default"); + if (g.currentgraphics == PRIMARYSET && g.symset[PRIMARYSET].name) Strcat(opts, ", active"); + if (g.symset[PRIMARYSET].handling) { + Sprintf(eos(opts), ", handler=%s", + known_handling[g.symset[PRIMARYSET].handling]); + } return optn_ok; } if (req == do_handler) { - return handler_symset(optidx); + int reslt; + + if (g.symset[PRIMARYSET].handling == H_UTF8) { +#ifdef ENHANCED_SYMBOLS + if (!glyphid_cache_status()) + fill_glyphid_cache(); +#endif + } + reslt = handler_symset(optidx); + if (g.symset[PRIMARYSET].handling == H_UTF8) { +#ifdef ENHANCED_SYMBOLS + if (glyphid_cache_status()) + free_glyphid_cache(); +#endif + } + return reslt; } return optn_ok; } @@ -5082,187 +5150,16 @@ handler_whatis_filter(void) return optn_ok; } -DISABLE_WARNING_FORMAT_NONLITERAL - static int handler_symset(int optidx) { - winid tmpwin; - anything any; - int n; - char buf[BUFSZ]; - menu_item *symset_pick = (menu_item *) 0; - boolean rogueflag = (optidx == opt_roguesymset), - ready_to_switch = FALSE, - nothing_to_do = FALSE; - char *symset_name, fmtstr[20]; - struct symsetentry *sl; - int res, which_set, setcount = 0, chosen = -2, defindx = 0; - - which_set = rogueflag ? ROGUESET : PRIMARY; - g.symset_list = (struct symsetentry *) 0; - /* clear symset[].name as a flag to read_sym_file() to build list */ - symset_name = g.symset[which_set].name; - g.symset[which_set].name = (char *) 0; - - res = read_sym_file(which_set); - /* put symset name back */ - g.symset[which_set].name = symset_name; - - if (res && g.symset_list) { - int thissize, - biggest = (int) (sizeof "Default Symbols" - sizeof ""), - big_desc = 0; - - for (sl = g.symset_list; sl; sl = sl->next) { - /* check restrictions */ - if (rogueflag ? sl->primary : sl->rogue) - continue; -#ifndef MAC_GRAPHICS_ENV - if (sl->handling == H_MAC) - continue; -#endif - - setcount++; - /* find biggest name */ - thissize = sl->name ? (int) strlen(sl->name) : 0; - if (thissize > biggest) - biggest = thissize; - thissize = sl->desc ? (int) strlen(sl->desc) : 0; - if (thissize > big_desc) - big_desc = thissize; - } - if (!setcount) { - pline("There are no appropriate %s symbol sets available.", - rogueflag ? "rogue level" : "primary"); - return TRUE; - } - - Sprintf(fmtstr, "%%-%ds %%s", biggest + 2); - tmpwin = create_nhwindow(NHW_MENU); - start_menu(tmpwin, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - any.a_int = 1; /* -1 + 2 [see 'if (sl->name) {' below]*/ - if (!symset_name) - defindx = any.a_int; - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - "Default Symbols", - (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED - : MENU_ITEMFLAGS_NONE); - - for (sl = g.symset_list; sl; sl = sl->next) { - /* check restrictions */ - if (rogueflag ? sl->primary : sl->rogue) - continue; -#ifndef MAC_GRAPHICS_ENV - if (sl->handling == H_MAC) - continue; -#endif - if (sl->name) { - /* +2: sl->idx runs from 0 to N-1 for N symsets; - +1 because Defaults are implicitly in slot [0]; - +1 again so that valid data is never 0 */ - any.a_int = sl->idx + 2; - if (symset_name && !strcmpi(sl->name, symset_name)) - defindx = any.a_int; - Sprintf(buf, fmtstr, sl->name, sl->desc ? sl->desc : ""); - add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, buf, - (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED - : MENU_ITEMFLAGS_NONE); - } - } - Sprintf(buf, "Select %ssymbol set:", - rogueflag ? "rogue level " : ""); - end_menu(tmpwin, buf); - n = select_menu(tmpwin, PICK_ONE, &symset_pick); - if (n > 0) { - chosen = symset_pick[0].item.a_int; - /* if picking non-preselected entry yields 2, make sure - that we're going with the non-preselected one */ - if (n == 2 && chosen == defindx) - chosen = symset_pick[1].item.a_int; - chosen -= 2; /* convert menu index to symset index; - * "Default symbols" have index -1 */ - free((genericptr_t) symset_pick); - } else if (n == 0 && defindx > 0) { - chosen = defindx - 2; - } - destroy_nhwindow(tmpwin); - - if (chosen > -1) { - /* chose an actual symset name from file */ - for (sl = g.symset_list; sl; sl = sl->next) - if (sl->idx == chosen) - break; - if (sl) { - /* free the now stale attributes */ - clear_symsetentry(which_set, TRUE); - - /* transfer only the name of the symbol set */ - g.symset[which_set].name = dupstr(sl->name); - ready_to_switch = TRUE; - } - } else if (chosen == -1) { - /* explicit selection of defaults */ - /* free the now stale symset attributes */ - clear_symsetentry(which_set, TRUE); - } else - nothing_to_do = TRUE; - } else if (!res) { - /* The symbols file could not be accessed */ - pline("Unable to access \"%s\" file.", SYMBOLS); - return TRUE; - } else if (!g.symset_list) { - /* The symbols file was empty */ - pline("There were no symbol sets found in \"%s\".", SYMBOLS); - return TRUE; - } - - /* clean up */ - while ((sl = g.symset_list) != 0) { - g.symset_list = sl->next; - if (sl->name) - free((genericptr_t) sl->name), sl->name = (char *) 0; - if (sl->desc) - free((genericptr_t) sl->desc), sl->desc = (char *) 0; - free((genericptr_t) sl); - } - - if (nothing_to_do) - return TRUE; - - /* Set default symbols and clear the handling value */ - if (rogueflag) - init_rogue_symbols(); - else - init_primary_symbols(); - - if (g.symset[which_set].name) { - /* non-default symbols */ - if (read_sym_file(which_set)) { - ready_to_switch = TRUE; - } else { - clear_symsetentry(which_set, TRUE); - return TRUE; - } - } - - if (ready_to_switch) - switch_symbols(TRUE); + int reslt; - if (Is_rogue_level(&u.uz)) { - if (rogueflag) - assign_graphics(ROGUESET); - } else if (!rogueflag) - assign_graphics(PRIMARY); - preference_update("symset"); + reslt = do_symset(optidx == opt_roguesymset); /* symbols.c */ g.opt_need_redraw = TRUE; - return optidx; + return reslt; } -RESTORE_WARNING_FORMAT_NONLITERAL - static int handler_autopickup_exception(void) { @@ -5985,6 +5882,12 @@ initoptions_init(void) memcpy(allopt, allopt_init, sizeof(allopt)); determine_ambiguities(); +#ifdef ENHANCED_SYMBOLS + /* make any symbol parsing quicker */ + if (!glyphid_cache_status()) + fill_glyphid_cache(); +#endif + /* set up the command parsing */ reset_commands(TRUE); /* init */ @@ -6048,8 +5951,8 @@ initoptions_init(void) */ /* this detects the IBM-compatible console on most 386 boxes */ if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) { - if (!g.symset[PRIMARY].explicitly) - load_symset("IBMGraphics", PRIMARY); + if (!g.symset[PRIMARYSET].explicitly) + load_symset("IBMGraphics", PRIMARYSET); if (!g.symset[ROGUESET].explicitly) load_symset("RogueIBM", ROGUESET); switch_symbols(TRUE); @@ -6065,8 +5968,8 @@ initoptions_init(void) /* [could also check "xterm" which emulates vtXXX by default] */ && !strncmpi(opts, "vt", 2) && AS && AE && index(AS, '\016') && index(AE, '\017')) { - if (!g.symset[PRIMARY].explicitly) - load_symset("DECGraphics", PRIMARY); + if (!g.symset[PRIMARYSET].explicitly) + load_symset("DECGraphics", PRIMARYSET); switch_symbols(TRUE); } #endif @@ -6074,14 +5977,14 @@ initoptions_init(void) #if defined(MSDOS) || defined(WIN32) /* Use IBM defaults. Can be overridden via config file */ - if (!g.symset[PRIMARY].explicitly) - load_symset("IBMGraphics_2", PRIMARY); + if (!g.symset[PRIMARYSET].explicitly) + load_symset("IBMGraphics_2", PRIMARYSET); if (!g.symset[ROGUESET].explicitly) load_symset("RogueEpyx", ROGUESET); #endif #ifdef MAC_GRAPHICS_ENV - if (!g.symset[PRIMARY].explicitly) - load_symset("MACGraphics", PRIMARY); + if (!g.symset[PRIMARYSET].explicitly) + load_symset("MACGraphics", PRIMARYSET); switch_symbols(TRUE); #endif /* MAC_GRAPHICS_ENV */ flags.menu_style = MENU_FULL; @@ -6206,7 +6109,7 @@ initoptions_finish(void) obj_descr[SLIME_MOLD].oc_name = "fruit"; sym = get_othersym(SYM_BOULDER, - Is_rogue_level(&u.uz) ? ROGUESET : PRIMARY); + Is_rogue_level(&u.uz) ? ROGUESET : PRIMARYSET); if (sym) g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym; reglyph_darkroom(); @@ -6229,6 +6132,11 @@ initoptions_finish(void) } #endif update_rest_on_space(); +#ifdef ENHANCED_SYMBOLS + if (glyphid_cache_status()) + free_glyphid_cache(); + apply_customizations_to_symset(g.currentgraphics); +#endif g.opt_initial = FALSE; return; } @@ -8195,128 +8103,6 @@ free_autopickup_exceptions(void) } } -/* bundle some common usage into one easy-to-use routine */ -int -load_symset(const char *s, int which_set) -{ - clear_symsetentry(which_set, TRUE); - - if (g.symset[which_set].name) - free((genericptr_t) g.symset[which_set].name); - g.symset[which_set].name = dupstr(s); - - if (read_sym_file(which_set)) { - switch_symbols(TRUE); - } else { - clear_symsetentry(which_set, TRUE); - return 0; - } - return 1; -} - -void -free_symsets(void) -{ - clear_symsetentry(PRIMARY, TRUE); - clear_symsetentry(ROGUESET, TRUE); - - /* symset_list is cleaned up as soon as it's used, so we shouldn't - have to anything about it here */ - /* assert( symset_list == NULL ); */ -} - -/* Parse the value of a SYMBOLS line from a config file */ -boolean -parsesymbols(register char *opts, int which_set) -{ - int val; - char *op, *symname, *strval; - struct symparse *symp; - - if ((op = index(opts, ',')) != 0) { - *op++ = 0; - if (!parsesymbols(op, which_set)) - return FALSE; - } - - /* S_sample:string */ - symname = opts; - strval = index(opts, ':'); - if (!strval) - strval = index(opts, '='); - if (!strval) - return FALSE; - *strval++ = '\0'; - - /* strip leading and trailing white space from symname and strval */ - mungspaces(symname); - mungspaces(strval); - - symp = match_sym(symname); - if (!symp) - return FALSE; - - if (symp->range && symp->range != SYM_CONTROL) { - val = sym_val(strval); - if (which_set == ROGUESET) - update_ov_rogue_symset(symp, val); - else - update_ov_primary_symset(symp, val); - } - return TRUE; -} - -struct symparse * -match_sym(char *buf) -{ - int i; - struct alternate_parse { - const char *altnm; - const char *nm; - } alternates[] = { - { "S_armour" , "S_armor" }, - { "S_explode1" , "S_expl_tl" }, - { "S_explode2" , "S_expl_tc" }, - { "S_explode3" , "S_expl_tr" }, - { "S_explode4" , "S_expl_ml" }, - { "S_explode5" , "S_expl_mc" }, - { "S_explode6" , "S_expl_mr" }, - { "S_explode7" , "S_expl_bl" }, - { "S_explode8" , "S_expl_bc" }, - { "S_explode9" , "S_expl_br" }, - }; - unsigned len = Strlen(buf); - const char *p = index(buf, ':'), *q = index(buf, '='); - struct symparse *sp = loadsyms; - - if (!p || (q && q < p)) - p = q; - if (p) { - /* note: there will be at most one space before the '=' - because caller has condensed buf[] with mungspaces() */ - if (p > buf && p[-1] == ' ') - p--; - len = (int) (p - buf); - } - while (sp->range) { - if ((len >= Strlen(sp->name)) && !strncmpi(buf, sp->name, len)) - return sp; - sp++; - } - for (i = 0; i < SIZE(alternates); ++i) { - if ((len >= strlen(alternates[i].altnm)) - && !strncmpi(buf, alternates[i].altnm, len)) { - sp = loadsyms; - while (sp->range) { - if (!strcmp(alternates[i].nm, sp->name)) - return sp; - sp++; - } - } - } - return (struct symparse *) 0; -} - int sym_val(const char *strval) /* up to 4*BUFSZ-1 long; only first few chars matter */ diff --git a/src/symbols.c b/src/symbols.c index 7b66a4806..f270f055e 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -15,13 +15,18 @@ void (*decgraphics_mode_callback)(void) = 0; /* set in tty_start_screen() */ void (*ibmgraphics_mode_callback)(void) = 0; /* set in tty_start_screen() */ void (*ascgraphics_mode_callback)(void) = 0; /* set in tty_start_screen() */ #endif -#ifdef WIN32 -void (*ibmgraphics_mode_callback)(void) = 0; /* set in tty_start_screen() */ -#endif #ifdef CURSES_GRAPHICS void (*cursesgraphics_mode_callback)(void) = 0; #endif -/* +#if defined(TTY_GRAPHICS) && defined(WIN32) +void (*ibmgraphics_mode_callback)(void) = 0; +#endif +#ifdef ENHANCED_SYMBOLS +void (*utf8graphics_mode_callback)(void) = 0; /* set in tty_start_screen and + found in unixtty, windtty, etc */ +#endif + + /* * Explanations of the functions found below: * * init_symbols() @@ -56,7 +61,7 @@ void (*cursesgraphics_mode_callback)(void) = 0; * If arg is ROGUESET, this places the rogue level * symbols from g.rogue_syms into g.showsyms. * - * If arg is PRIMARY, this places the symbols + * If arg is PRIMARYSET, this places the symbols * from g.primary_syms into g.showsyms. * * update_primary_symset() @@ -97,7 +102,7 @@ init_showsyms(void) for (i = 0; i < WARNCOUNT; i++) g.showsyms[i + SYM_OFF_W] = def_warnsyms[i].sym; for (i = 0; i < MAXOTHER; i++) - g.showsyms[i + SYM_OFF_X] = get_othersym(i, PRIMARY); + g.showsyms[i + SYM_OFF_X] = get_othersym(i, PRIMARYSET); } /* initialize defaults for the overrides to the rogue symset */ @@ -169,9 +174,9 @@ init_primary_symbols(void) for (i = 0; i < WARNCOUNT; i++) g.primary_syms[i + SYM_OFF_W] = def_warnsyms[i].sym; for (i = 0; i < MAXOTHER; i++) - g.primary_syms[i + SYM_OFF_X] = get_othersym(i, PRIMARY); + g.primary_syms[i + SYM_OFF_X] = get_othersym(i, PRIMARYSET); - clear_symsetentry(PRIMARY, FALSE); + clear_symsetentry(PRIMARYSET, FALSE); } /* initialize defaults for the rogue symset */ @@ -224,7 +229,7 @@ assign_graphics(int whichset) g.currentgraphics = ROGUESET; break; - case PRIMARY: + case PRIMARYSET: default: for (i = 0; i < SYM_MAX; i++) g.showsyms[i] = g.ov_primary_syms[i] ? g.ov_primary_syms[i] @@ -234,7 +239,7 @@ assign_graphics(int whichset) if (iflags.grmode) tileview(TRUE); #endif - g.currentgraphics = PRIMARY; + g.currentgraphics = PRIMARYSET; break; } reset_glyphmap(gm_symchange); @@ -267,6 +272,14 @@ switch_symbols(int nondefault) if (SYMHANDLING(H_CURS) && cursesgraphics_mode_callback) (*cursesgraphics_mode_callback)(); # endif +#endif +#ifdef WIN32 + if (SYMHANDLING(H_IBM) && ibmgraphics_mode_callback) + (*ibmgraphics_mode_callback)(); +#endif +#ifdef ENHANCED_SYMBOLS + if (SYMHANDLING(H_UTF8) && utf8graphics_mode_callback) + (*utf8graphics_mode_callback)(); #endif } else { init_primary_symbols(); @@ -275,25 +288,25 @@ switch_symbols(int nondefault) } void -update_ov_primary_symset(struct symparse* symp, int val) +update_ov_primary_symset(const struct symparse* symp, int val) { g.ov_primary_syms[symp->idx] = val; } void -update_ov_rogue_symset(struct symparse* symp, int val) +update_ov_rogue_symset(const struct symparse* symp, int val) { g.ov_rogue_syms[symp->idx] = val; } void -update_primary_symset(struct symparse* symp, int val) +update_primary_symset(const struct symparse* symp, int val) { g.primary_syms[symp->idx] = val; } void -update_rogue_symset(struct symparse* symp, int val) +update_rogue_symset(const struct symparse* symp, int val) { g.rogue_syms[symp->idx] = val; } @@ -318,7 +331,20 @@ clear_symsetentry(int which_set, boolean name_too) } } -/* +boolean symset_is_compatible(enum symset_handling_types handling, unsigned long wincap2) +{ +#ifdef ENHANCED_SYMBOLS + if ((handling == H_UTF8) && + (((wincap2 & WC2_U_UTF8STR) == 0) + || ((wincap2 & WC2_U_24BITCOLOR) == 0))) + return FALSE; +#else + nhUse(handling); + nhUse(wincap2); +#endif + return TRUE; +} + /* * If you are adding code somewhere to be able to recognize * particular types of symset "handling", define a * H_XXX macro in include/sym.h and add the name @@ -330,6 +356,7 @@ const char *known_handling[] = { "DEC", /* H_DEC */ "CURS", /* H_CURS */ "MAC", /* H_MAC -- pre-OSX MACgraphics */ + "UTF8", /* H_UTF8 */ (const char *) 0, }; @@ -377,4 +404,677 @@ const struct symparse loadsyms[] = { { 0, 0, (const char *) 0 } /* fence post */ }; +boolean +proc_symset_line(char *buf) +{ + return !((boolean) parse_sym_line(buf, g.symset_which_set)); +} + +/* returns 0 on error */ +int +parse_sym_line(char *buf, int which_set) +{ + int val, i; + const struct symparse *symp = (struct symparse *) 0; + char *bufp, *commentp, *altp; + int glyph = NO_GLYPH; + boolean enhanced_unavailable = FALSE, is_glyph = FALSE; + + if (strlen(buf) >= BUFSZ) + buf[BUFSZ - 1] = '\0'; + /* convert each instance of whitespace (tabs, consecutive spaces) + into a single space; leading and trailing spaces are stripped */ + mungspaces(buf); + + /* remove trailing comment, if any (this isn't strictly needed for + individual symbols, and it won't matter if "X#comment" without + separating space slips through; for handling or set description, + symbol set creator is responsible for preceding '#' with a space + and that comment itself doesn't contain " #") */ + if ((commentp = rindex(buf, '#')) != 0 && commentp[-1] == ' ') + commentp[-1] = '\0'; + + /* find the '=' or ':' */ + bufp = index(buf, '='); + altp = index(buf, ':'); + if (!bufp || (altp && altp < bufp)) + bufp = altp; + if (!bufp) { + if (strncmpi(buf, "finish", 6) == 0) { + /* end current graphics set */ + if (g.chosen_symset_start) + g.chosen_symset_end = TRUE; + g.chosen_symset_start = FALSE; + return 1; + } + config_error_add("No \"finish\""); + return 0; + } + /* skip '=' and space which follows, if any */ + ++bufp; + if (*bufp == ' ') + ++bufp; + + + symp = match_sym(buf); + if (!symp && buf[0] == 'G' && buf[1] == '_') { +#ifdef ENHANCED_SYMBOLS + is_glyph = match_glyph(buf); +#else + enhanced_unavailable = TRUE; +#endif + } + if (!symp && !is_glyph && !enhanced_unavailable) { + config_error_add("Unknown sym keyword"); + return 0; + } + if (symp) { + if (!g.symset[which_set].name) { + /* A null symset name indicates that we're just + building a pick-list of possible symset + values from the file, so only do that */ + if (symp->range == SYM_CONTROL) { + struct symsetentry *tmpsp, *lastsp; + + for (lastsp = g.symset_list; lastsp; lastsp = lastsp->next) + if (!lastsp->next) + break; + switch (symp->idx) { + case 0: + tmpsp = (struct symsetentry *) alloc(sizeof *tmpsp); + tmpsp->next = (struct symsetentry *) 0; + if (!lastsp) + g.symset_list = tmpsp; + else + lastsp->next = tmpsp; + tmpsp->idx = g.symset_count++; + tmpsp->name = dupstr(bufp); + tmpsp->desc = (char *) 0; + tmpsp->handling = H_UNK; + /* initialize restriction bits */ + tmpsp->nocolor = 0; + tmpsp->primary = 0; + tmpsp->rogue = 0; + break; + case 2: + /* handler type identified */ + tmpsp = lastsp; /* most recent symset */ + for (i = 0; known_handling[i]; ++i) + if (!strcmpi(known_handling[i], bufp)) { + tmpsp->handling = i; + break; /* for loop */ + } + break; + case 3: + /* description:something */ + tmpsp = lastsp; /* most recent symset */ + if (tmpsp && !tmpsp->desc) + tmpsp->desc = dupstr(bufp); + break; + case 5: + /* restrictions: xxxx*/ + tmpsp = lastsp; /* most recent symset */ + for (i = 0; known_restrictions[i]; ++i) { + if (!strcmpi(known_restrictions[i], bufp)) { + switch (i) { + case 0: + tmpsp->primary = 1; + break; + case 1: + tmpsp->rogue = 1; + break; + } + break; /* while loop */ + } + } + break; + } + } + return 1; + } + if (symp->range && symp->range == SYM_CONTROL) { + switch (symp->idx) { + case 0: + /* start of symset */ + if (!strcmpi(bufp, g.symset[which_set].name)) { + /* matches desired one */ + g.chosen_symset_start = TRUE; + /* these init_*() functions clear symset fields too */ + if (which_set == ROGUESET) + init_rogue_symbols(); + else if (which_set == PRIMARYSET) + init_primary_symbols(); + } + break; + case 1: + /* finish symset */ + if (g.chosen_symset_start) + g.chosen_symset_end = TRUE; + g.chosen_symset_start = FALSE; + break; + case 2: + /* handler type identified */ + if (g.chosen_symset_start) + set_symhandling(bufp, which_set); + break; + /* case 3: (description) is ignored here */ + case 4: /* color:off */ + if (g.chosen_symset_start) { + if (bufp) { + if (!strcmpi(bufp, "true") || !strcmpi(bufp, "yes") + || !strcmpi(bufp, "on")) + g.symset[which_set].nocolor = 0; + else if (!strcmpi(bufp, "false") + || !strcmpi(bufp, "no") + || !strcmpi(bufp, "off")) + g.symset[which_set].nocolor = 1; + } + } + break; + case 5: /* restrictions: xxxx*/ + if (g.chosen_symset_start) { + int n = 0; + + while (known_restrictions[n]) { + if (!strcmpi(known_restrictions[n], bufp)) { + switch (n) { + case 0: + g.symset[which_set].primary = 1; + break; + case 1: + g.symset[which_set].rogue = 1; + break; + } + break; /* while loop */ + } + n++; + } + } + break; + } + } else { + /* Not SYM_CONTROL */ + if (g.symset[which_set].handling != H_UTF8) { + val = sym_val(bufp); + if (g.chosen_symset_start) { + if (which_set == PRIMARYSET) { + update_primary_symset(symp, val); + } else if (which_set == ROGUESET) { + update_rogue_symset(symp, val); + } + } +#ifdef ENHANCED_SYMBOLS + } else { + glyphrep_to_custom_map_entries(buf, &glyph); +#endif + } + } + } +#ifndef ENHANCED_SYMBOLS + nhUse(glyph); +#endif + return 1; +} + +void +set_symhandling(char *handling, int which_set) +{ + int i = 0; + + g.symset[which_set].handling = H_UNK; + while (known_handling[i]) { + if (!strcmpi(known_handling[i], handling)) { + g.symset[which_set].handling = i; + return; + } + i++; + } +} + +/* bundle some common usage into one easy-to-use routine */ +int +load_symset(const char *s, int which_set) +{ + clear_symsetentry(which_set, TRUE); + + if (g.symset[which_set].name) + free((genericptr_t) g.symset[which_set].name); + g.symset[which_set].name = dupstr(s); + + if (read_sym_file(which_set)) { + switch_symbols(TRUE); + } else { + clear_symsetentry(which_set, TRUE); + return 0; + } + return 1; +} + +void +free_symsets(void) +{ + clear_symsetentry(PRIMARYSET, TRUE); + clear_symsetentry(ROGUESET, TRUE); + + /* symset_list is cleaned up as soon as it's used, so we shouldn't + have to anything about it here */ + /* assert( symset_list == NULL ); */ +} + +/* Parse the value of a SYMBOLS line from a config file */ +boolean +parsesymbols(register char *opts, int which_set) +{ + int val; + char *op, *symname, *strval; + const struct symparse *symp; + boolean is_glyph = FALSE; + + if ((op = index(opts, ',')) != 0) { + *op++ = 0; + if (!parsesymbols(op, which_set)) + return FALSE; + } + + /* S_sample:string */ + symname = opts; + strval = index(opts, ':'); + if (!strval) + strval = index(opts, '='); + if (!strval) + return FALSE; + *strval++ = '\0'; + + /* strip leading and trailing white space from symname and strval */ + mungspaces(symname); + mungspaces(strval); + + symp = match_sym(symname); +#ifdef ENHANCED_SYMBOLS + if (!symp && symname[0] == 'G' && symname[1] == '_') { + is_glyph = match_glyph(symname); + } +#endif + if (!symp && !is_glyph) + return FALSE; + if (symp) { + if (symp->range && symp->range != SYM_CONTROL) { +#ifdef ENHANCED_SYMBOLS + int glyph; +#endif + + if ((g.symset[which_set].handling == H_UTF8) + || (strval && (lowc(strval[0]) == 'u') && (strval[1] == '+'))) { +#ifdef ENHANCED_SYMBOLS + char buf[BUFSZ]; + + Snprintf(buf, sizeof buf, "%s:%s", opts, strval); + glyphrep_to_custom_map_entries(buf, &glyph); +#endif /* ENHANCED_SYMBOLS */ + } else { + val = sym_val(strval); + if (which_set == ROGUESET) + update_ov_rogue_symset(symp, val); + else + update_ov_primary_symset(symp, val); + } + } + } + return TRUE; +} + +const struct symparse * +match_sym(char *buf) +{ + int i; + struct alternate_parse { + const char *altnm; + const char *nm; + } alternates[] = { + { "S_armour", "S_armor" }, { "S_explode1", "S_expl_tl" }, + { "S_explode2", "S_expl_tc" }, { "S_explode3", "S_expl_tr" }, + { "S_explode4", "S_expl_ml" }, { "S_explode5", "S_expl_mc" }, + { "S_explode6", "S_expl_mr" }, { "S_explode7", "S_expl_bl" }, + { "S_explode8", "S_expl_bc" }, { "S_explode9", "S_expl_br" }, + }; + size_t len = strlen(buf); + const char *p = index(buf, ':'), *q = index(buf, '='); + const struct symparse *sp = loadsyms; + + if (!p || (q && q < p)) + p = q; + if (p) { + /* note: there will be at most one space before the '=' + because caller has condensed buf[] with mungspaces() */ + if (p > buf && p[-1] == ' ') + p--; + len = (int) (p - buf); + } + while (sp->range) { + if ((len >= strlen(sp->name)) && !strncmpi(buf, sp->name, len)) + return sp; + sp++; + } + for (i = 0; i < SIZE(alternates); ++i) { + if ((len >= strlen(alternates[i].altnm)) + && !strncmpi(buf, alternates[i].altnm, len)) { + sp = loadsyms; + while (sp->range) { + if (!strcmp(alternates[i].nm, sp->name)) + return sp; + sp++; + } + } + } + return (struct symparse *) 0; +} + +DISABLE_WARNING_FORMAT_NONLITERAL + +/* + * this is called from options.c to do the symset work. + */ +int +do_symset(boolean rogueflag) +{ + winid tmpwin; + anything any; + int n; + char buf[BUFSZ]; + menu_item *symset_pick = (menu_item *) 0; + boolean ready_to_switch = FALSE, + nothing_to_do = FALSE; + char *symset_name, fmtstr[20]; + struct symsetentry *sl; + int res, which_set, setcount = 0, chosen = -2, defindx = 0; + + which_set = rogueflag ? ROGUESET : PRIMARYSET; + g.symset_list = (struct symsetentry *) 0; + /* clear symset[].name as a flag to read_sym_file() to build list */ + symset_name = g.symset[which_set].name; + g.symset[which_set].name = (char *) 0; + + res = read_sym_file(which_set); + /* put symset name back */ + g.symset[which_set].name = symset_name; + + if (res && g.symset_list) { + int thissize, + biggest = (int) (sizeof "Default Symbols" - sizeof ""), + big_desc = 0; + + for (sl = g.symset_list; sl; sl = sl->next) { + /* check restrictions */ + if (rogueflag ? sl->primary : sl->rogue) + continue; +#ifndef MAC_GRAPHICS_ENV + if (sl->handling == H_MAC) + continue; +#endif +#ifndef ENHANCED_SYMBOLS + if (sl->handling == H_UTF8) + continue; +#endif + setcount++; + /* find biggest name */ + thissize = sl->name ? (int) strlen(sl->name) : 0; + if (thissize > biggest) + biggest = thissize; + thissize = sl->desc ? (int) strlen(sl->desc) : 0; + if (thissize > big_desc) + big_desc = thissize; + } + if (!setcount) { + pline("There are no appropriate %s symbol sets available.", + rogueflag ? "rogue level" : "primary"); + return TRUE; + } + + Sprintf(fmtstr, "%%-%ds %%s", biggest + 2); + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + any.a_int = 1; /* -1 + 2 [see 'if (sl->name) {' below]*/ + if (!symset_name) + defindx = any.a_int; + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + "Default Symbols", + (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + + for (sl = g.symset_list; sl; sl = sl->next) { + /* check restrictions */ + if (rogueflag ? sl->primary : sl->rogue) + continue; +#ifndef MAC_GRAPHICS_ENV + if (sl->handling == H_MAC) + continue; +#endif +#ifndef ENHANCED_SYMBOLS + if (sl->handling == H_UTF8) + continue; +#endif + if (sl->name) { + /* +2: sl->idx runs from 0 to N-1 for N symsets; + +1 because Defaults are implicitly in slot [0]; + +1 again so that valid data is never 0 */ + any.a_int = sl->idx + 2; + if (symset_name && !strcmpi(sl->name, symset_name)) + defindx = any.a_int; + Sprintf(buf, fmtstr, sl->name, sl->desc ? sl->desc : ""); + add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, buf, + (any.a_int == defindx) ? MENU_ITEMFLAGS_SELECTED + : MENU_ITEMFLAGS_NONE); + } + } + Sprintf(buf, "Select %ssymbol set:", + rogueflag ? "rogue level " : ""); + end_menu(tmpwin, buf); + n = select_menu(tmpwin, PICK_ONE, &symset_pick); + if (n > 0) { + chosen = symset_pick[0].item.a_int; + /* if picking non-preselected entry yields 2, make sure + that we're going with the non-preselected one */ + if (n == 2 && chosen == defindx) + chosen = symset_pick[1].item.a_int; + chosen -= 2; /* convert menu index to symset index; + * "Default symbols" have index -1 */ + free((genericptr_t) symset_pick); + } else if (n == 0 && defindx > 0) { + chosen = defindx - 2; + } + destroy_nhwindow(tmpwin); + + if (chosen > -1) { + /* chose an actual symset name from file */ + for (sl = g.symset_list; sl; sl = sl->next) + if (sl->idx == chosen) + break; + if (sl) { + /* free the now stale attributes */ + clear_symsetentry(which_set, TRUE); + + /* transfer only the name of the symbol set */ + g.symset[which_set].name = dupstr(sl->name); + ready_to_switch = TRUE; + } + } else if (chosen == -1) { + /* explicit selection of defaults */ + /* free the now stale symset attributes */ + clear_symsetentry(which_set, TRUE); + } else + nothing_to_do = TRUE; + } else if (!res) { + /* The symbols file could not be accessed */ + pline("Unable to access \"%s\" file.", SYMBOLS); + return TRUE; + } else if (!g.symset_list) { + /* The symbols file was empty */ + pline("There were no symbol sets found in \"%s\".", SYMBOLS); + return TRUE; + } + + /* clean up */ + while ((sl = g.symset_list) != 0) { + g.symset_list = sl->next; + if (sl->name) + free((genericptr_t) sl->name), sl->name = (char *) 0; + if (sl->desc) + free((genericptr_t) sl->desc), sl->desc = (char *) 0; + free((genericptr_t) sl); + } + + if (nothing_to_do) + return TRUE; + + /* Set default symbols and clear the handling value */ + if (rogueflag) + init_rogue_symbols(); + else + init_primary_symbols(); + + if (g.symset[which_set].name) { + /* non-default symbols */ + if (read_sym_file(which_set)) { + ready_to_switch = TRUE; + } else { + clear_symsetentry(which_set, TRUE); + return TRUE; + } + } + + if (ready_to_switch) + switch_symbols(TRUE); + + if (Is_rogue_level(&u.uz)) { + if (rogueflag) + assign_graphics(ROGUESET); + } else if (!rogueflag) + assign_graphics(PRIMARYSET); +#ifdef ENHANCED_SYMBOLS + apply_customizations_to_symset(rogueflag ? ROGUESET : PRIMARYSET); +#endif + preference_update("symset"); + return TRUE; +} + +#ifdef ENHANCED_SYMBOLS + +RESTORE_WARNING_FORMAT_NONLITERAL + +struct customization_detail *find_display_sym_customization( + const char *customization_name, const struct symparse *symparse, + enum graphics_sets which_set); +struct customization_detail *find_matching_symset_customization( + const char *customization_name, int custtype, + enum graphics_sets which_set); +struct customization_detail *find_display_urep_customization( + const char *customization_name, int glyphidx, enum graphics_sets which_set); +void purge_custom_entries(enum graphics_sets which_set); +extern glyph_map glyphmap[MAX_GLYPH]; +static void shuffle_customizations(void); + +void +apply_customizations_to_symset(enum graphics_sets which_set) +{ + glyph_map *gm; + struct customization_detail *details; + + if (g.symset[which_set].handling == H_UTF8 + && g.sym_customizations[UNICODESET].count + && g.sym_customizations[UNICODESET].details) { + /* These UTF-8 customizations get applied to the glyphmap array, + not to symset entries */ + details = g.sym_customizations[UNICODESET].details; + while (details) { + gm = &glyphmap[details->content.urep.glyphidx]; + (void) set_map_u(gm, details->content.urep.u.utf8str, + details->content.urep.u.ucolor); + details = details->next; + } + shuffle_customizations(); + } +} +/* Shuffle the customizations to match shuffled object descriptions, + * so a red potion isn't displayed with a blue customization, and so on. + */ +static void +shuffle_customizations(void) +{ + int i; + struct unicode_representation *tmp_u[2][NUM_OBJECTS]; + + for (i = 0; i < NUM_OBJECTS; i++) { + tmp_u[0][i] = + glyphmap[objects[i].oc_descr_idx + GLYPH_OBJ_OFF].u; + tmp_u[1][i] = + glyphmap[objects[i].oc_descr_idx + GLYPH_OBJ_PILETOP_OFF].u; + } + for (i = 0; i < NUM_OBJECTS; i++) { + glyphmap[i + GLYPH_OBJ_OFF].u = tmp_u[0][i]; + glyphmap[i + GLYPH_OBJ_PILETOP_OFF].u = tmp_u[1][i]; + } +} + +struct customization_detail * +find_matching_symset_customization(const char *customization_name, + int custtype, + enum graphics_sets which_set) +{ + struct symset_customization *gdc = &g.sym_customizations[which_set]; + if ((gdc->custtype == custtype) + && (strcmp(customization_name, gdc->customization_name) != 0)) + return gdc->details; + return (struct customization_detail *) 0; +} + +void +purge_custom_entries(enum graphics_sets which_set) +{ + struct symset_customization *gdc = &g.sym_customizations[which_set]; + struct customization_detail *details = gdc->details, *next; + if (details) { + next = details->next; + if (gdc->custtype == custom_ureps) { + if (details->content.urep.u.utf8str) + free(details->content.urep.u.utf8str); + details->content.urep.u.utf8str = (uint8 *) 0; + details->content.urep.u.ucolor = 0L; + details->content.urep.u.u256coloridx = 0L; + } else if (gdc->custtype == custom_symbols) { + details->content.sym.symparse = (struct symparse *) 0; + details->content.sym.val = 0; + } + free(details); + details = next; + } + gdc->details = 0; + if (gdc->customization_name) { + free((genericptr_t) gdc->customization_name); + gdc->customization_name = 0; + } + gdc->count = 0; +} + +struct customization_detail * +find_display_sym_customization(const char *customization_name, + const struct symparse *symparse, + enum graphics_sets which_set) +{ + struct symset_customization *gdc = &g.sym_customizations[which_set]; + struct customization_detail *symdetails; + if ((gdc->custtype == custom_symbols) + && (strcmp(customization_name, gdc->customization_name) == 0)) { + symdetails = gdc->details; + while (symdetails) { + if (symdetails->content.sym.symparse == symparse) + return symdetails; + symdetails = symdetails->next; + } + } + return (struct customization_detail *) 0; +} +#endif /* ENHANCED_SYMBOLS */ + /*symbols.c*/ diff --git a/src/utf8map.c b/src/utf8map.c new file mode 100644 index 000000000..e386c366b --- /dev/null +++ b/src/utf8map.c @@ -0,0 +1,1123 @@ +/* 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 + +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 uint32_t rgbstr_to_uint32(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_uint32(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 uint32_t +rgbstr_to_uint32(const char *rgbstr) +{ + int r, gn, b, milestone = 0; + char *cp, *c_r,*c_g,*c_b; + uint32_t rgb = 0L; + 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 *) strdup((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 = strdup(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 *) strdup((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 *) strdup((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 = strdup(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 */ + + + diff --git a/src/windows.c b/src/windows.c index 6971a5995..bd2ab4c1c 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1375,7 +1375,7 @@ glyph2symidx(int glyph) glyph_info glyphinfo; map_glyphinfo(0, 0, glyph, 0, &glyphinfo); - return glyphinfo.gm.symidx; + return glyphinfo.gm.sym.symidx; } char * @@ -1387,10 +1387,37 @@ encglyph(int glyph) return encbuf; } +int +decode_glyph(const char *str, int *glyph_ptr) +{ + static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; + int rndchk = 0, dcount = 0, retval = 0; + const char *dp; + + for (; *str && ++dcount <= 4; ++str) { + if ((dp = index(hex, *str)) != 0) { + retval++; + rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); + } else + break; + } + if (rndchk == g.context.rndencode) { + *glyph_ptr = dcount = 0; + for (; *str && ++dcount <= 4; ++str) { + if ((dp = index(hex, *str)) != 0) { + retval++; + *glyph_ptr = (*glyph_ptr * 16) + ((int) (dp - hex) / 2); + } else + break; + } + return retval; + } + return 0; +} + char * decode_mixed(char *buf, const char *str) { - static const char hex[] = "00112233445566778899aAbBcCdDeEfF"; char *put = buf; glyph_info glyphinfo = nul_glyphinfo; @@ -1399,27 +1426,16 @@ decode_mixed(char *buf, const char *str) while (*str) { if (*str == '\\') { - int rndchk, dcount, so, gv; - const char *dp, *save_str; + int dcount, so, gv; + const char *save_str; save_str = str++; switch (*str) { case 'G': /* glyph value \GXXXXNNNN*/ - rndchk = dcount = 0; - for (++str; *str && ++dcount <= 4; ++str) - if ((dp = index(hex, *str)) != 0) - rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); - else - break; - if (rndchk == g.context.rndencode) { - gv = dcount = 0; - for (; *str && ++dcount <= 4; ++str) - if ((dp = index(hex, *str)) != 0) - gv = (gv * 16) + ((int) (dp - hex) / 2); - else - break; + if ((dcount = decode_glyph(str + 1, &gv))) { + str += (dcount + 1); map_glyphinfo(0, 0, gv, 0, &glyphinfo); - so = glyphinfo.gm.symidx; + so = glyphinfo.gm.sym.symidx; *put++ = g.showsyms[so]; /* 'str' is ready for the next loop iteration and '*str' should not be copied at the end of this iteration */ @@ -1429,25 +1445,6 @@ decode_mixed(char *buf, const char *str) str = save_str; } break; -#if 0 - case 'S': /* symbol offset */ - so = rndchk = dcount = 0; - for (++str; *str && ++dcount <= 4; ++str) - if ((dp = index(hex, *str)) != 0) - rndchk = (rndchk * 16) + ((int) (dp - hex) / 2); - else - break; - if (rndchk == g.context.rndencode) { - dcount = 0; - for (; *str && ++dcount <= 2; ++str) - if ((dp = index(hex, *str)) != 0) - so = (so * 16) + ((int) (dp - hex) / 2); - else - break; - } - *put++ = g.showsyms[so]; - break; -#endif case '\\': break; case '\0': @@ -1467,6 +1464,7 @@ decode_mixed(char *buf, const char *str) return buf; } + /* * This differs from putstr() because the str parameter can * contain a sequence of characters representing: diff --git a/sys/libnh/libnhmain.c b/sys/libnh/libnhmain.c index 7ef207d0f..ea8470e32 100644 --- a/sys/libnh/libnhmain.c +++ b/sys/libnh/libnhmain.c @@ -361,7 +361,7 @@ process_options(int argc, char *argv[]) || !strcmpi(*argv, "-debug")) { wizard = TRUE, discover = FALSE; } else if (!strncmpi(*argv, "-DECgraphics", l)) { - load_symset("DECGraphics", PRIMARY); + load_symset("DECGraphics", PRIMARYSET); switch_symbols(TRUE); } else { raw_printf("Unknown option: %.60s", *argv); @@ -391,7 +391,7 @@ process_options(int argc, char *argv[]) case 'I': case 'i': if (!strncmpi(*argv, "-IBMgraphics", l)) { - load_symset("IBMGraphics", PRIMARY); + load_symset("IBMGraphics", PRIMARYSET); load_symset("RogueIBM", ROGUESET); switch_symbols(TRUE); } else { diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 1c3a7ae57..899b26f07 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -561,7 +561,7 @@ process_options(int argc, char *argv[]) case 'I': case 'i': if (!strncmpi(argv[0] + 1, "IBM", 3)) { - load_symset("IBMGraphics", PRIMARY); + load_symset("IBMGraphics", PRIMARYSET); load_symset("RogueIBM", ROGUESET); switch_symbols(TRUE); } @@ -569,7 +569,7 @@ process_options(int argc, char *argv[]) /* case 'D': */ case 'd': if (!strncmpi(argv[0] + 1, "DEC", 3)) { - load_symset("DECGraphics", PRIMARY); + load_symset("DECGraphics", PRIMARYSET); switch_symbols(TRUE); } break; diff --git a/sys/share/unixtty.c b/sys/share/unixtty.c index d731e1038..5d353928d 100644 --- a/sys/share/unixtty.c +++ b/sys/share/unixtty.c @@ -393,7 +393,7 @@ init_sco_cons(void) if (WINDOWPORT("tty") && sco_flag_console) { atexit(sco_mapon); sco_mapoff(); - load_symset("IBMGraphics", PRIMARY); + load_symset("IBMGraphics", PRIMARYSET); load_symset("RogueIBM", ROGUESET); switch_symbols(TRUE); #ifdef TEXTCOLOR @@ -485,3 +485,15 @@ error(const char *s, ...) RESTORE_WARNING_FORMAT_NONLITERAL +#ifdef ENHANCED_SYMBOLS +/* + * set in tty_start_screen() and allows + * OS-specific changes that may be + * required for support of utf8. + */ +void +tty_utf8graphics_fixup(void) +{ +} +#endif + diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 35cb68beb..90000e2b4 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -488,7 +488,7 @@ HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ rip.c rnd.c role.c rumors.c save.c sfstruct.c \ shk.c shknam.c sit.c sounds.c \ sp_lev.c spell.c steal.c steed.c symbols.c sys.c teleport.c \ - timeout.c topten.c track.c trap.c u_init.c \ + timeout.c topten.c track.c trap.c u_init.c utf8map.c \ uhitm.c vault.c version.c vision.c weapon.c were.c wield.c \ windows.c wizard.c worm.c worn.c write.c zap.c @@ -580,8 +580,8 @@ HOBJ = $(TARGETPFX)allmain.o $(TARGETPFX)alloc.o \ $(TARGETPFX)symbols.o $(TARGETPFX)sys.o $(TARGETPFX)steal.o \ $(TARGETPFX)steed.o $(TARGETPFX)teleport.o $(TARGETPFX)timeout.o \ $(TARGETPFX)topten.o $(TARGETPFX)track.o $(TARGETPFX)trap.o \ - $(TARGETPFX)u_init.o $(TARGETPFX)uhitm.o $(TARGETPFX)vault.o \ - $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ + $(TARGETPFX)u_init.o $(TARGETPFX)uhitm.o $(TARGETPFX)utf8map.o \ + $(TARGETPFX)vault.o $(TARGETPFX)vision.o $(TARGETPFX)weapon.o \ $(TARGETPFX)were.o $(TARGETPFX)wield.o $(TARGETPFX)windows.o \ $(TARGETPFX)wizard.o $(TARGETPFX)worm.o $(TARGETPFX)worn.o \ $(TARGETPFX)write.o $(TARGETPFX)zap.o \ diff --git a/sys/unix/hints/linux.370 b/sys/unix/hints/linux.370 index bfa1ca6d0..e24878163 100755 --- a/sys/unix/hints/linux.370 +++ b/sys/unix/hints/linux.370 @@ -105,6 +105,9 @@ NHCFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" #NHCFLAGS+=-DTTY_SOUND_ESCCODES #NHCFLAGS+=-DNO_CHRONICLE #NHCFLAGS+=-DLIVELOG +ifdef WANT_WIN_CURSES +NHCFLAGS+=-DCURSES_UNICODE +endif CFLAGS+= $(WINCFLAGS) #WINCFLAGS set from multiw-2.370 CFLAGS+= $(NHCFLAGS) diff --git a/sys/unix/hints/macOS.370 b/sys/unix/hints/macOS.370 index 90fe5bb39..c85e657cc 100755 --- a/sys/unix/hints/macOS.370 +++ b/sys/unix/hints/macOS.370 @@ -105,6 +105,9 @@ NHCFLAGS+=-DNOMAIL #NHCFLAGS+=-DTTY_SOUND_ESCCODES #NHCFLAGS+=-DNO_CHRONICLE #NHCFLAGS+=-DLIVELOG +ifdef WANT_WIN_CURSES +NHCFLAGS+=-DCURSES_UNICODE +endif CFLAGS+= $(WINCFLAGS) #WINCFLAGS set from multiw-2.370 CFLAGS+= $(NHCFLAGS) diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c index 9cd7dfede..53fe67df1 100644 --- a/sys/unix/unixmain.c +++ b/sys/unix/unixmain.c @@ -115,7 +115,10 @@ main(int argc, char *argv[]) #endif /* CHDIR */ /* handle -dalthackdir, -s , --version, --showpaths */ early_options(&argc, &argv, &dir); - +#ifdef ENHANCED_SYMBOLS + if (argcheck(argc, argv, ARG_DUMPGLYPHIDS) == 2) + exit(EXIT_SUCCESS); +#endif #ifdef CHDIR /* * Change directories before we initialize the window system so @@ -405,7 +408,7 @@ process_options(int argc, char *argv[]) if ((arg[1] == 'D' && !arg[2]) || !strcmpi(arg, "-debug")) { wizard = TRUE, discover = FALSE; } else if (!strncmpi(arg, "-DECgraphics", l)) { - load_symset("DECGraphics", PRIMARY); + load_symset("DECGraphics", PRIMARYSET); switch_symbols(TRUE); } else { config_error_add("Unknown option: %.60s", origarg); @@ -442,7 +445,7 @@ process_options(int argc, char *argv[]) case 'I': case 'i': if (!strncmpi(arg, "-IBMgraphics", l)) { - load_symset("IBMGraphics", PRIMARY); + load_symset("IBMGraphics", PRIMARYSET); load_symset("RogueIBM", ROGUESET); switch_symbols(TRUE); } else { diff --git a/sys/vms/vmsmain.c b/sys/vms/vmsmain.c index 078232aa3..95b1eeb54 100644 --- a/sys/vms/vmsmain.c +++ b/sys/vms/vmsmain.c @@ -281,7 +281,7 @@ process_options(int argc, char *argv[]) case 'I': case 'i': if (!strncmpi(argv[0] + 1, "IBM", 3)) { - load_symset("IBMGraphics", PRIMARY); + load_symset("IBMGraphics", PRIMARYSET); load_symset("RogueIBM", ROGUESET); switch_symbols(TRUE); } @@ -289,7 +289,7 @@ process_options(int argc, char *argv[]) /* case 'D': */ case 'd': if (!strncmpi(argv[0] + 1, "DEC", 3)) { - load_symset("DECGraphics", PRIMARY); + load_symset("DECGraphics", PRIMARYSET); switch_symbols(TRUE); } break; diff --git a/sys/windows/Makefile.mingw32 b/sys/windows/Makefile.mingw32 index c41e46504..63ffe2637 100644 --- a/sys/windows/Makefile.mingw32 +++ b/sys/windows/Makefile.mingw32 @@ -724,7 +724,7 @@ COREOBJS = $(addsuffix .o, allmain alloc apply artifact attrib ball bones botl c pager pickup pline polyself potion pray priest quest questpgr \ random read rect region restore rip rnd role rumors \ safeproc save sfstruct shk shknam sit sounds sp_lev spell steal steed stubs symbols sys \ - teleport timeout topten track trap u_init uhitm vault version vision \ + teleport timeout topten track trap u_init uhitm utf8map vault version vision \ weapon were wield windmain windows windsys wizard worm worn write zap) CFLAGSW = $(CFLAGS) $(COMMONDEF) $(DLBFLG) -DTILES -D_WINDOWS -DMSWIN_GRAPHICS -DSAFEPROCS -DNOTTYGRPHICS diff --git a/sys/windows/Makefile.nmake b/sys/windows/Makefile.nmake index 127f655c5..63b5e78e8 100644 --- a/sys/windows/Makefile.nmake +++ b/sys/windows/Makefile.nmake @@ -435,8 +435,8 @@ HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ rip.c rnd.c role.c rumors.c save.c sfstruct.c \ shk.c shknam.c sit.c sounds.c \ sp_lev.c spell.c steal.c steed.c symbols.c sys.c teleport.c \ - timeout.c topten.c track.c trap.c u_init.c \ - uhitm.c vault.c version.c vision.c weapon.c were.c wield.c \ + timeout.c topten.c track.c trap.c u_init.c uhitm.c utf8map.c \ + vault.c version.c vision.c weapon.c were.c wield.c \ windows.c wizard.c worm.c worn.c write.c zap.c @@ -496,10 +496,10 @@ VOBJ21 = $(O)sfstruct.o $(O)shk.o $(O)shknam.o $(O)sit.o VOBJ22 = $(O)sounds.o $(O)sp_lev.o $(O)spell.o $(O)steal.o VOBJ23 = $(O)steed.o $(O)symbols.o $(O)sys.o $(O)teleport.o VOBJ24 = $(O)timeout.o $(O)topten.o $(O)track.o $(O)trap.o -VOBJ25 = $(O)u_init.o $(O)uhitm.o $(O)vault.o $(O)vision.o -VOBJ26 = $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o -VOBJ27 = $(O)wizard.o $(O)worm.o $(O)worn.o $(O)write.o -VOBJ28 = $(O)zap.o +VOBJ25 = $(O)u_init.o $(O)uhitm.o $(O)utf8map.o $(O)vault.o +VOBJ26 = $(O)vision.o $(O)weapon.o $(O)were.o $(O)wield.o +VOBJ27 = $(O)windows.o $(O)wizard.o $(O)worm.o $(O)worn.o +VOBJ28 = $(O)write.o $(O)zap.o LUAOBJ = $(O)nhlua.o $(O)nhlsel.o $(O)nhlobj.o @@ -510,7 +510,7 @@ DLBOBJ = $(O)dlb.o REGEX = $(O)cppregex.o -TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o +TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o MDLIB = $(O)mdlib.o @@ -723,7 +723,7 @@ ccommon= -c -nologo -D"_CONSOLE" -D"_CRT_NONSTDC_NO_DEPRECATE" -D"_CRT_SECURE_NO -DHAS_STDINT_H -DHAS_INLINE $(CURSESDEF) $(RUNTIMEOPTDEF) \ -EHsc -fp:precise -Gd -GF -GS -Gy \ $(CL_RECENT) -WX- -Zc:forScope -Zc:wchar_t -Zi -cdebug= -analyze- -D"_DEBUG" -MTd -RTC1 -Od +cdebug= -analyze- -D"_DEBUG" -MTd -RTC1 -Od /fsanitize=address crelease= -analyze- -D"_MBCS" -errorReport:prompt -MT -O2 -Ot -Ox -Oy lcommon= /NOLOGO /INCREMENTAL:NO @@ -2246,6 +2246,7 @@ $(TARGETPFX)track.o: track.c $(HACK_H) $(TARGETPFX)trap.o: trap.c $(HACK_H) $(TARGETPFX)u_init.o: u_init.c $(HACK_H) $(TARGETPFX)uhitm.o: uhitm.c $(HACK_H) +$(TARGETPFX)utf8map.o: utf8map.c $(HACK_H) $(TARGETPFX)vault.o: vault.c $(HACK_H) $(TARGETPFX)version.o: version.c $(HACK_H) $(INCL)\dlb.h $(TARGETPFX)vision.o: vision.c $(HACK_H) diff --git a/sys/windows/consoletty.c b/sys/windows/consoletty.c index 3b5be3028..7d3c2e36a 100644 --- a/sys/windows/consoletty.c +++ b/sys/windows/consoletty.c @@ -15,6 +15,7 @@ #ifndef NO_VT #define VIRTUAL_TERMINAL_SEQUENCES +#define UTF8_FROM_CORE #endif #ifdef WIN32 @@ -84,6 +85,7 @@ typedef struct { WCHAR wcharacter; WORD attr; long color24; + int color256idx; const char *colorseq; #endif /* VIRTUAL_TERMINAL_SEQUENCES */ } cell_t; @@ -94,9 +96,9 @@ cell_t undefined_cell = { CONSOLE_UNDEFINED_CHARACTER, CONSOLE_UNDEFINED_ATTRIBUTE }; #else /* VIRTUAL_TERMINAL_SEQUENCES */ cell_t clear_cell = { { CONSOLE_CLEAR_CHARACTER, 0, 0, 0, 0, 0, 0 }, - CONSOLE_CLEAR_CHARACTER, 0, 0L, "\x1b[0m" }; + CONSOLE_CLEAR_CHARACTER, 0, 0L, 0, "\x1b[0m" }; cell_t undefined_cell = { { CONSOLE_UNDEFINED_CHARACTER, 0, 0, 0, 0, 0, 0 }, - CONSOLE_UNDEFINED_CHARACTER, 0, 0L, (const char *) 0 }; + CONSOLE_UNDEFINED_CHARACTER, 0, 0L, 0, (const char *) 0 }; static const uint8 empty_utf8str[MAX_UTF8_SEQUENCE] = { 0 }; #endif /* VIRTUAL_TERMINAL_SEQUENCES */ @@ -137,7 +139,8 @@ static void restore_original_console_font(void); extern void safe_routines(void); void tty_ibmgraphics_fixup(void); #ifdef VIRTUAL_TERMINAL_SEQUENCES -extern void (*ibmgraphics_mode_callback)(void); /* symbols.c */ +extern void (*ibmgraphics_mode_callback)(void); /* symbols.c */ +extern void (*utf8graphics_mode_callback)(void); /* symbols.c */ #endif /* VIRTUAL_TERMINAL_SEQUENCES */ /* Win32 Screen buffer,coordinate,console I/O information */ @@ -160,27 +163,9 @@ static boolean init_ttycolor_completed; #ifdef PORT_DEBUG static boolean display_cursor_info = FALSE; #endif -#ifdef CHANGE_COLOR -static void adjust_palette(void); -static int match_color_name(const char *); -typedef HWND(WINAPI *GETCONSOLEWINDOW)(); -static HWND GetConsoleHandle(void); -static HWND GetConsoleHwnd(void); -static boolean altered_palette; -static COLORREF UserDefinedColors[CLR_MAX]; -static COLORREF NetHackColors[CLR_MAX] = { - 0x00000000, 0x00c80000, 0x0000c850, 0x00b4b432, 0x000000d2, 0x00800080, - 0x000064b4, 0x00c0c0c0, 0x00646464, 0x00f06464, 0x0000ff00, 0x00ffff00, - 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff -}; -static COLORREF DefaultColors[CLR_MAX] = { - 0x00000000, 0x00800000, 0x00008000, 0x00808000, 0x00000080, 0x00800080, - 0x00008080, 0x00c0c0c0, 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00, - 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff -}; -#endif struct console_t { boolean is_ready; + HWND hWnd; WORD background; WORD foreground; WORD attr; @@ -217,8 +202,10 @@ struct console_t { DWORD in_cmode; DWORD out_cmode; long color24; + int color256idx; } console = { FALSE, + 0, (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), /* background */ (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED), /* foreground */ 0, /* attr */ @@ -263,7 +250,8 @@ struct console_t { NULL, /* localestr */ 0, /* in_cmode */ 0, /* out_cmode */ - 0L /* color24 */ + 0L, /* color24 */ + 0 /* color256idx */ #endif /* VIRTUAL_TERMINAL_SEQUENCES */ }; @@ -532,6 +520,7 @@ void emit_stop_underline(void); void emit_start_inverse(void); void emit_stop_inverse(void); void emit_start_24bitcolor(long color24bit); +void emit_start_256color(int u256coloridx); void emit_default_color(void); void emit_return_to_default(void); void emit_hide_cursor(void); @@ -656,22 +645,42 @@ emit_stop_inverse(void) &unused, &reserved); } +#if 0 #define tcfmtstr "\x1b[38;2;%d;%d;%dm" #if 0 #define tcfmtstr "\x1b[38:2:%d:%d:%dm" #endif +#endif + +#ifndef SEP2 +#define tcfmtstr24bit "\x1b[38;2;%ld;%ld;%ldm" +#define tcfmtstr256 "\x1b[38;5;%ldm" +#else +#define tcfmtstr24bit "\x1b[38:2:%ld:%ld:%ldm" +#define tcfmtstr256 "\x1b[38:5:%dm" +#endif + +void +emit_start_256color(int u256coloridx) +{ + DWORD unused, reserved; + static char tcolorbuf[QBUFSZ]; + Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, u256coloridx); + WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, + (int) strlen(tcolorbuf), &unused, &reserved); +} void emit_start_24bitcolor(long color24bit) { DWORD unused, reserved; static char tcolorbuf[QBUFSZ]; - long mcolor24bit = + long mcolor = (color24bit & 0xFFFFFF); /* color 0 has bit 0x1000000 set */ - Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr, - ((mcolor24bit >> 0) & 0xFF), /* red */ - ((mcolor24bit >> 8) & 0xFF), /* green */ - ((mcolor24bit >> 16) & 0xFF)); /* blue */ + Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr24bit, + ((mcolor >> 16) & 0xFF), /* red */ + ((mcolor >> 8) & 0xFF), /* green */ + ((mcolor >> 0) & 0xFF)); /* blue */ WriteConsoleA(console.hConOut, (LPCSTR) tcolorbuf, (int) strlen(tcolorbuf), &unused, &reserved); } @@ -744,9 +753,19 @@ back_buffer_flip(void) do_anything |= do_colorseq; if (back->attr != front->attr) do_anything |= do_newattr; - if (strcmp((const char *) back->utf8str, - (const char *) front->utf8str)) - do_anything |= do_utf8_content; +#ifdef UTF8_FROM_CORE + if (!SYMHANDLING(H_UTF8)) { + if (console.has_unicode + && (back->wcharacter != front->wcharacter)) + do_anything |= do_wide_content; + } else { +#endif + if (strcmp((const char *) back->utf8str, + (const char *) front->utf8str)) + do_anything |= do_utf8_content; +#ifdef UTF8_FROM_CORE + } +#endif if (do_anything) { SetConsoleCursorPosition(console.hConOut, pos); pos_set = TRUE; @@ -771,7 +790,10 @@ back_buffer_flip(void) if (color24_on && back->color24) { did_anything |= did_color24; if (back->color24) { - emit_start_24bitcolor(back->color24); + if (!iflags.use_truecolor && iflags.colorcount == 256) + emit_start_256color(back->color256idx); + else + emit_start_24bitcolor(back->color24); } } else if (back->colorseq) { did_anything |= did_colorseq; @@ -784,9 +806,20 @@ back_buffer_flip(void) } if (did_anything || (do_anything & (do_wide_content | do_utf8_content))) { +#ifdef UTF8_FROM_CORE + if (SYMHANDLING(H_UTF8) || !console.has_unicode) { + WriteConsoleA(console.hConOut, (LPCSTR) back->utf8str, + (int) strlen((char *) back->utf8str), + &unused, &reserved); + did_anything |= did_utf8_content; + } else { +#endif WriteConsoleW(console.hConOut, &back->wcharacter, 1, &unused, &reserved); did_anything |= did_wide_content; +#ifdef UTF8_FROM_CORE + } +#endif } } if (did_anything) { @@ -930,10 +963,6 @@ settty(const char* s) void setftty() { -#ifdef CHANGE_COLOR - if (altered_palette) - adjust_palette(); -#endif start_screen(); } @@ -943,6 +972,8 @@ tty_startup(int *wid, int *hgt) *wid = console.width; *hgt = console.height; set_option_mod_status("mouse_support", set_in_game); + iflags.colorcount = 16777216; +// iflags.colorcount = 256; } void @@ -958,6 +989,11 @@ tty_start_screen() tty_number_pad(1); /* make keypad send digits */ #ifdef VIRTUAL_TERMINAL_SEQUENCES ibmgraphics_mode_callback = tty_ibmgraphics_fixup; +#ifdef ENHANCED_SYMBOLS +#ifdef UTF8_FROM_CORE + utf8graphics_mode_callback = tty_utf8graphics_fixup; +#endif +#endif #endif /* VIRTUAL_TERMINAL_SEQUENCES */ } @@ -1234,13 +1270,25 @@ xputc_core(int ch) // if (console.color24) // __debugbreak(); cell.color24 = 0L; + cell.color256idx = 0; wch[1] = 0; if (console.has_unicode) { wch[0] = (ch >= 0 && ch < SIZE(console.cpMap)) ? console.cpMap[ch] : ch; +#ifdef UTF8_FROM_CORE + if (SYMHANDLING(H_UTF8)) { + /* we have to convert it to UTF-8 for cell.utf8str */ + ccount = WideCharToMultiByte( + CP_UTF8, 0, wch, -1, (char *) cell.utf8str, + (int) sizeof cell.utf8str, NULL, NULL); + } else { +#endif /* store the wide version here also, so we don't slow down back_buffer_flip() with conversions */ cell.wcharacter = wch[0]; +#ifdef UTF8_FROM_CORE + } +#endif } else { /* we can just use the UTF-8 utf8str field, since ascii is a single-byte representation of a small subset of unicode */ @@ -1278,6 +1326,11 @@ xputc_core(int ch) * Overrides wintty.c function of the same name * for win32. It is used for glyphs only, not text. */ +#ifdef UTF8_FROM_CORE + /* See g_pututf8() for a corresponding UTF-8 sequence + * version, rather than a single character. + */ +#endif void g_putch(int in_ch) { @@ -1308,12 +1361,24 @@ g_putch(int in_ch) cell.attr = console.attr; cell.colorseq = esc_seq_colors[console.current_nhcolor]; cell.color24 = console.color24 ? console.color24 : 0L; + cell.color256idx = 0; wch[1] = 0; if (console.has_unicode) { wch[0] = (ch >= 0 && ch < SIZE(console.cpMap)) ? console.cpMap[ch] : ch; +#ifdef UTF8_FROM_CORE + if (SYMHANDLING(H_UTF8)) { + /* we have to convert it to UTF-8 for cell.utf8str */ + ccount = WideCharToMultiByte( + CP_UTF8, 0, wch, -1, (char *) cell.utf8str, + (int) sizeof cell.utf8str, NULL, NULL); + } else { +#endif /* store the wide version here also, so we don't slow down back_buffer_flip() with conversions */ cell.wcharacter = wch[0]; +#ifdef UTF8_FROM_CORE + } +#endif } else { /* we can just use the UTF-8 utf8str field, since ascii is a single-byte representation of a small subset of unicode */ @@ -1326,16 +1391,41 @@ g_putch(int in_ch) } #ifdef VIRTUAL_TERMINAL_SEQUENCES +#ifdef UTF8_FROM_CORE +/* + * Overrides wintty.c function of the same name + * for win32. It is used for glyphs only, not text and + * only when a UTF-8 sequence is involved for the + * representation. Single character representations + * use g_putch() instead. + */ +void +g_pututf8(uint8 *sequence) +{ + set_console_cursor(ttyDisplay->curx, ttyDisplay->cury); + cell_t cell; + cell.attr = console.attr; + cell.colorseq = esc_seq_colors[console.current_nhcolor]; + cell.color24 = console.color24 ? console.color24 : 0L; + cell.color256idx =console.color256idx ? console.color256idx : 0; + Snprintf((char *) cell.utf8str, sizeof cell.utf8str, "%s", + (char *) sequence); + buffer_write(console.back_buffer, &cell, console.cursor); +} +#endif /* UTF8_FROM_CORE */ + void -term_start_24bitcolor(long color24bit) +term_start_24bitcolor(struct unicode_representation *uval) { - console.color24 = color24bit; /* color 0 has bit 0x1000000 set */ + console.color24 = uval->ucolor; /* color 0 has bit 0x1000000 set */ + console.color256idx = uval->u256coloridx; } void term_end_24bitcolor(void) { console.color24 = 0L; + console.color256idx = 0; } #endif /* VIRTUAL_TERMINAL_SEQUENCES */ @@ -1712,7 +1802,15 @@ consoletty_preference_update(const char* pref) #endif } if (stricmp(pref, "symset") == 0) { +#ifdef UTF8_FROM_CORE + if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) { +#ifdef ENHANCED_SYMBOLS + tty_utf8graphics_fixup(); +#endif + } else if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_IBM)) { +#else if (SYMHANDLING(H_IBM)) { +#endif tty_ibmgraphics_fixup(); } check_and_set_font(); @@ -1720,6 +1818,70 @@ consoletty_preference_update(const char* pref) return; } +#ifdef UTF8_FROM_CORE +#ifdef VIRTUAL_TERMINAL_SEQUENCES +/* + * This is called when making the switch to a symset + * with a UTF8 handler to allow any operating system + * specific changes to be carried out. + */ +void +tty_utf8graphics_fixup(void) +{ + CONSOLE_FONT_INFOEX console_font_info; + if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) { + if (!console.hConOut) + console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); + /* the locale */ + if (console.localestr) + free(console.localestr); + console.localestr = strdup(setlocale(LC_ALL, ".UTF8")); + /* the code page */ + SetConsoleOutputCP(65001); + console.code_page = GetConsoleOutputCP(); + /* the font */ + console_font_info.cbSize = sizeof(console_font_info); + BOOL success = GetCurrentConsoleFontEx(console.hConOut, FALSE, + &console_font_info); + /* Try DejaVu Sans Mono for Powerline */ + wcscpy_s(console_font_info.FaceName, + sizeof(console_font_info.FaceName) + / sizeof(console_font_info.FaceName[0]), + L"DejaVu Sans Mono for Powerline"); + console_font_info.cbSize = sizeof(console_font_info); + success = SetCurrentConsoleFontEx(console.hConOut, FALSE, + &console_font_info); + if (!success) { + /* Next, try Lucida Console */ + wcscpy_s(console_font_info.FaceName, + sizeof(console_font_info.FaceName) + / sizeof(console_font_info.FaceName[0]), + L"Lucida Console"); + console_font_info.cbSize = sizeof(console_font_info); + success = SetCurrentConsoleFontEx(console.hConOut, FALSE, + &console_font_info); + } + nhassert(success); + if (success) { + console.font_info = console_font_info; + console.font_changed = TRUE; + } + /* the console mode */ + GetConsoleMode(console.hConOut, &console.out_cmode); +#if 1 + if ((console.out_cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0) { + /* recognize escape sequences */ + console.out_cmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(console.hConOut, console.out_cmode); + } +#else + console.out_cmode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING; +#endif + } +} +#endif +#endif /* UTF8_FROM_CORE */ + /* * This is called when making the switch to a symset * with an IBM handler to allow any operating system @@ -1836,400 +1998,6 @@ synch_cursor(void) really_move_cursor(); } -#ifdef CHANGE_COLOR -void -tty_change_color(color_number, rgb, reverse) -int color_number, reverse; -long rgb; -{ - /* Map NetHack color index to NT Console palette index */ - int idx, win32_color_number[] = { - 0, /* CLR_BLACK 0 */ - 4, /* CLR_RED 1 */ - 2, /* CLR_GREEN 2 */ - 6, /* CLR_BROWN 3 */ - 1, /* CLR_BLUE 4 */ - 5, /* CLR_MAGENTA 5 */ - 3, /* CLR_CYAN 6 */ - 7, /* CLR_GRAY 7 */ - 8, /* NO_COLOR 8 */ - 12, /* CLR_ORANGE 9 */ - 10, /* CLR_BRIGHT_GREEN 10 */ - 14, /* CLR_YELLOW 11 */ - 9, /* CLR_BRIGHT_BLUE 12 */ - 13, /* CLR_BRIGHT_MAGENTA 13 */ - 11, /* CLR_BRIGHT_CYAN 14 */ - 15 /* CLR_WHITE 15 */ - }; - int k; - if (color_number < 0) { /* indicates OPTIONS=palette with no value */ - /* copy the NetHack palette into UserDefinedColors */ - for (k = 0; k < CLR_MAX; k++) - UserDefinedColors[k] = NetHackColors[k]; - } else if (color_number >= 0 && color_number < CLR_MAX) { - if (!altered_palette) { - /* make sure a full suite is available */ - for (k = 0; k < CLR_MAX; k++) - UserDefinedColors[k] = DefaultColors[k]; - } - idx = win32_color_number[color_number]; - UserDefinedColors[idx] = rgb; - } - altered_palette = TRUE; -} - -char * -tty_get_color_string() -{ - return ""; -} - -int -match_color_name(c) -const char *c; -{ - const struct others { - int idx; - const char *colorname; - } othernames[] = { - { CLR_MAGENTA, "purple" }, - { CLR_BRIGHT_MAGENTA, "bright purple" }, - { NO_COLOR, "dark gray" }, - { NO_COLOR, "dark grey" }, - { CLR_GRAY, "grey" }, - }; - - int cnt; - for (cnt = 0; cnt < CLR_MAX; ++cnt) { - if (!strcmpi(c, c_obj_colors[cnt])) - return cnt; - } - for (cnt = 0; cnt < SIZE(othernames); ++cnt) { - if (!strcmpi(c, othernames[cnt].colorname)) - return othernames[cnt].idx; - } - return -1; -} - -/* - * Returns 0 if badoption syntax - */ -int -alternative_palette(op) -char *op; -{ - /* - * palette:color-R-G-B - * OPTIONS=palette:green-4-3-1, palette:0-0-0-0 - */ - int fieldcnt, color_number, rgb, red, green, blue; - char *fields[4], *cp; - - if (!op) { - change_color(-1, 0, 0); /* indicates palette option with - no value meaning "load an entire - hard-coded NetHack palette." */ - return 1; - } - - cp = fields[0] = op; - for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) { - cp = index(cp, '-'); - if (!cp) - return 0; - fields[fieldcnt] = cp; - cp++; - } - for (fieldcnt = 1; fieldcnt < 4; ++fieldcnt) { - *(fields[fieldcnt]) = '\0'; - ++fields[fieldcnt]; - } - rgb = 0; - for (fieldcnt = 0; fieldcnt < 4; ++fieldcnt) { - if (fieldcnt == 0 && isalpha(*(fields[0]))) { - color_number = match_color_name(fields[0]); - if (color_number == -1) - return 0; - } else { - int dcount = 0, cval = 0; - cp = fields[fieldcnt]; - if (*cp == '\\' && index("0123456789xXoO", cp[1])) { - const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF"; - - cp++; - if (*cp == 'x' || *cp == 'X') - for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++) - cval = (int) ((cval * 16) + (dp - hex) / 2); - else if (*cp == 'o' || *cp == 'O') - for (++cp; (index("01234567", *cp)) && (dcount++ < 3); - cp++) - cval = (cval * 8) + (*cp - '0'); - else - return 0; - } else { - for (; *cp && (index("0123456789", *cp)) && (dcount++ < 3); - cp++) - cval = (cval * 10) + (*cp - '0'); - } - switch (fieldcnt) { - case 0: - color_number = cval; - break; - case 1: - red = cval; - break; - case 2: - green = cval; - break; - case 3: - blue = cval; - break; - } - } - } - rgb = RGB(red, green, blue); - if (color_number >= 0 && color_number < CLR_MAX) - change_color(color_number, rgb, 0); - return 1; -} - -/* - * This uses an undocumented method to set console attributes - * at runtime including console palette - * - * VOID WINAPI SetConsolePalette(COLORREF palette[16]) - * - * Author: James Brown at www.catch22.net - * - * Set palette of current console. - * Palette should be of the form: - * - * COLORREF DefaultColors[CLR_MAX] = - * { - * 0x00000000, 0x00800000, 0x00008000, 0x00808000, - * 0x00000080, 0x00800080, 0x00008080, 0x00c0c0c0, - * 0x00808080, 0x00ff0000, 0x0000ff00, 0x00ffff00, - * 0x000000ff, 0x00ff00ff, 0x0000ffff, 0x00ffffff - * }; - */ - -#pragma pack(push, 1) - -/* - * Structure to send console via WM_SETCONSOLEINFO - */ -typedef struct _CONSOLE_INFO { - ULONG Length; - COORD ScreenBufferSize; - COORD WindowSize; - ULONG WindowPosX; - ULONG WindowPosY; - - COORD FontSize; - ULONG FontFamily; - ULONG FontWeight; - WCHAR FaceName[32]; - - ULONG CursorSize; - ULONG FullScreen; - ULONG QuickEdit; - ULONG AutoPosition; - ULONG InsertMode; - - USHORT ScreenColors; - USHORT PopupColors; - ULONG HistoryNoDup; - ULONG HistoryBufferSize; - ULONG NumberOfHistoryBuffers; - - COLORREF ColorTable[16]; - - ULONG CodePage; - HWND Hwnd; - - WCHAR ConsoleTitle[0x100]; -} CONSOLE_INFO; - -#pragma pack(pop) - -BOOL SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci); -static void GetConsoleSizeInfo(CONSOLE_INFO *pci); -VOID WINAPI SetConsolePalette(COLORREF crPalette[16]); - -void -adjust_palette(void) -{ - SetConsolePalette(UserDefinedColors); - altered_palette = 0; -} - -/* -/* only in Win2k+ (use FindWindow for NT4) */ -/* HWND WINAPI GetConsoleWindow(); */ - -/* Undocumented console message */ -#define WM_SETCONSOLEINFO (WM_USER + 201) - -VOID WINAPI -SetConsolePalette(COLORREF palette[16]) -{ - CONSOLE_INFO ci = { sizeof(ci) }; - int i; - HWND hwndConsole = GetConsoleHandle(); - - /* get current size/position settings rather than using defaults.. */ - GetConsoleSizeInfo(&ci); - - /* set these to zero to keep current settings */ - ci.FontSize.X = 0; /* def = 8 */ - ci.FontSize.Y = 0; /* def = 12 */ - ci.FontFamily = 0; /* def = 0x30 = FF_MODERN|FIXED_PITCH */ - ci.FontWeight = 0; /* 0x400; */ - /* lstrcpyW(ci.FaceName, L"Terminal"); */ - ci.FaceName[0] = L'\0'; - - ci.CursorSize = 25; - ci.FullScreen = FALSE; - ci.QuickEdit = TRUE; - ci.AutoPosition = 0x10000; - ci.InsertMode = TRUE; - ci.ScreenColors = MAKEWORD(0x7, 0x0); - ci.PopupColors = MAKEWORD(0x5, 0xf); - - ci.HistoryNoDup = FALSE; - ci.HistoryBufferSize = 50; - ci.NumberOfHistoryBuffers = 4; - - // colour table - for (i = 0; i < 16; i++) - ci.ColorTable[i] = palette[i]; - - ci.CodePage = GetConsoleOutputCP(); - ci.Hwnd = hwndConsole; - - lstrcpyW(ci.ConsoleTitle, L""); - - SetConsoleInfo(hwndConsole, &ci); -} - -/* - * Wrapper around WM_SETCONSOLEINFO. We need to create the - * necessary section (file-mapping) object in the context of the - * process which owns the console, before posting the message - */ -BOOL -SetConsoleInfo(HWND hwndConsole, CONSOLE_INFO *pci) -{ - DWORD dwConsoleOwnerPid; - HANDLE hProcess; - HANDLE hSection, hDupSection; - PVOID ptrView = 0; - HANDLE hThread; - - /* - * Open the process which "owns" the console - */ - GetWindowThreadProcessId(hwndConsole, &dwConsoleOwnerPid); - hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwConsoleOwnerPid); - - /* - * Create a SECTION object backed by page-file, then map a view of - * this section into the owner process so we can write the contents - * of the CONSOLE_INFO buffer into it - */ - hSection = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, - pci->Length, 0); - - /* - * Copy our console structure into the section-object - */ - ptrView = MapViewOfFile(hSection, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, - pci->Length); - memcpy(ptrView, pci, pci->Length); - UnmapViewOfFile(ptrView); - - /* - * Map the memory into owner process - */ - DuplicateHandle(GetCurrentProcess(), hSection, hProcess, &hDupSection, 0, - FALSE, DUPLICATE_SAME_ACCESS); - - /* Send console window the "update" message */ - SendMessage(hwndConsole, WM_SETCONSOLEINFO, (WPARAM) hDupSection, 0); - - /* - * clean up - */ - hThread = CreateRemoteThread(hProcess, 0, 0, - (LPTHREAD_START_ROUTINE) CloseHandle, - hDupSection, 0, 0); - - CloseHandle(hThread); - CloseHandle(hSection); - CloseHandle(hProcess); - - return TRUE; -} - -/* - * Fill the CONSOLE_INFO structure with information - * about the current console window - */ -static void -GetConsoleSizeInfo(CONSOLE_INFO *pci) -{ - CONSOLE_SCREEN_BUFFER_INFO csbi; - - HANDLE hConsoleOut = GetStdHandle(STD_OUTPUT_HANDLE); - - GetConsoleScreenBufferInfo(hConsoleOut, &csbi); - - pci->ScreenBufferSize = csbi.dwSize; - pci->WindowSize.X = csbi.srWindow.Right - csbi.srWindow.Left + 1; - pci->WindowSize.Y = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; - pci->WindowPosX = csbi.srWindow.Left; - pci->WindowPosY = csbi.srWindow.Top; -} - -static HWND -GetConsoleHandle(void) -{ - HMODULE hMod = GetModuleHandle("kernel32.dll"); - GETCONSOLEWINDOW pfnGetConsoleWindow = - (GETCONSOLEWINDOW) GetProcAddress(hMod, "GetConsoleWindow"); - if (pfnGetConsoleWindow) - return pfnGetConsoleWindow(); - else - return GetConsoleHwnd(); -} - -static HWND -GetConsoleHwnd(void) -{ - int iterations = 0; - HWND hwndFound = 0; - char OldTitle[1024], NewTitle[1024], TestTitle[1024]; - - /* Get current window title */ - GetConsoleTitle(OldTitle, sizeof OldTitle); - - (void) sprintf(NewTitle, "NETHACK%d/%d", GetTickCount(), - GetCurrentProcessId()); - SetConsoleTitle(NewTitle); - - GetConsoleTitle(TestTitle, sizeof TestTitle); - while (strcmp(TestTitle, NewTitle) != 0) { - iterations++; - /* sleep(0); */ - GetConsoleTitle(TestTitle, sizeof TestTitle); - } - hwndFound = FindWindow(NULL, NewTitle); - SetConsoleTitle(OldTitle); - /* printf("%d iterations\n", iterations); */ - return hwndFound; -} -#endif /*CHANGE_COLOR*/ - static int CALLBACK EnumFontCallback( const LOGFONTW * lf, const TEXTMETRICW * tm, DWORD fontType, LPARAM lParam) { @@ -2271,8 +2039,8 @@ check_font_widths(void) * NOTE: the DC from the console window does not have the correct * font selected at this point. */ - HWND hWnd = GetConsoleWindow(); - HDC hDC = GetDC(hWnd); + console.hWnd = GetConsoleWindow(); + HDC hDC = GetDC(console.hWnd); LOGFONTW logical_font; logical_font.lfCharSet = DEFAULT_CHARSET; @@ -2574,6 +2342,14 @@ void nethack_enter_consoletty(void) #if 0 /* set up state needed by early_raw_print() */ windowprocs.win_raw_print = early_raw_print; +#endif +#if 0 + /* prevent re-sizing of the console window */ + if (!console.hWnd) + console.hWnd = GetConsoleWindow(); + SetWindowLong(console.hWnd, GWL_STYLE, + GetWindowLong(console.hWnd, GWL_STYLE) + & ~WS_MAXIMIZEBOX & ~WS_SIZEBOX); #endif console.hConOut = GetStdHandle(STD_OUTPUT_HANDLE); nhassert(console.hConOut != NULL); // NOTE: this assert will not print diff --git a/sys/windows/windmain.c b/sys/windows/windmain.c index c6075ce79..3297a695a 100644 --- a/sys/windows/windmain.c +++ b/sys/windows/windmain.c @@ -35,6 +35,16 @@ extern void backsp(void); #endif extern void clear_screen(void); +#if defined(TERMLIB) || defined(CURSES_GRAPHICS) +extern void (*decgraphics_mode_callback)(void); +#endif +#ifdef CURSES_GRAPHICS +extern void (*cursesgraphics_mode_callback)(void); +#endif +#ifdef ENHANCED_SYMBOLS +extern void (*utf8graphics_mode_callback)(void); +#endif + #ifdef _MSC_VER #ifdef kbhit #undef kbhit @@ -393,7 +403,8 @@ copy_hack_content() update_file(g.fqn_prefix[HACKPREFIX], OPTIONFILE, g.fqn_prefix[DATAPREFIX], OPTIONFILE, FALSE); } - +extern const char *known_handling[]; /* symbols.c */ +extern const char *known_restrictions[]; /* symbols.c */ /* * __MINGW32__ Note * If the graphics version is built, we don't need a main; it is skipped @@ -420,7 +431,7 @@ mingw_main(int argc, char *argv[]) safe_routines(); early_init(); #ifdef _MSC_VER -# ifdef DEBUG +#ifdef DEBUG /* set these appropriately for VS debugging */ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, @@ -439,7 +450,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ | _CRTDBG_LEAK_CHECK_DF); _CrtSetBreakAlloc(1423); */ -# endif +#endif #endif g.hname = "NetHack"; /* used for syntax messages */ @@ -480,7 +491,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } /* Finished processing options, lock all directory paths */ - for(int i = 0; i < PREFIX_COUNT; i++) + for (int i = 0; i < PREFIX_COUNT; i++) fqn_prefix_locked[i] = TRUE; if (!validate_prefix_locations(failbuf)) { @@ -491,23 +502,23 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ copy_hack_content(); -/* - * It seems you really want to play. - */ - if (argc >= 1 - && !strcmpi(default_window_sys, "mswin") + /* + * It seems you really want to play. + */ + if (argc >= 1 && !strcmpi(default_window_sys, "mswin") && (strstri(argv[0], "nethackw.exe") || GUILaunched)) iflags.windowtype_locked = TRUE; windowtype = default_window_sys; #ifdef DLB if (!dlb_init()) { - pline("%s\n%s\n%s\n%s\n\n", - copyright_banner_line(1), copyright_banner_line(2), - copyright_banner_line(3), copyright_banner_line(4)); - pline("NetHack was unable to open the required file \"%s\"",DLBFILE); + pline("%s\n%s\n%s\n%s\n\n", copyright_banner_line(1), + copyright_banner_line(2), copyright_banner_line(3), + copyright_banner_line(4)); + pline("NetHack was unable to open the required file \"%s\"", DLBFILE); if (file_exists(DLBFILE)) - pline("\nAre you perhaps trying to run NetHack within a zip utility?"); + pline("\nAre you perhaps trying to run NetHack within a zip " + "utility?"); error("dlb_init failure."); } #endif @@ -540,6 +551,33 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ if (WINDOWPORT("tty")) toggle_mouse_support(); + if (g.symset[PRIMARYSET].handling + && !symset_is_compatible(g.symset[PRIMARYSET].handling, + windowprocs.wincap2)) { + /* current symset handling and windowtype are + not compatible, feature-wise. Use IBM defaults */ + load_symset("IBMGraphics_2", PRIMARYSET); + load_symset("RogueEpyx", ROGUESET); + } + /* Has the callback for the symset been invoked? Config file processing to + load a symset runs too early to accomplish that because + the various *graphics_mode_callback pointers don't get set until + term_start_screen, unfortunately */ +#if defined(TERMLIB) || defined(CURSES_GRAPHICS) + if (SYMHANDLING(H_DEC) && decgraphics_mode_callback) + (*decgraphics_mode_callback)(); +#endif /* TERMLIB || CURSES */ +#if 0 +#ifdef CURSES_GRAPHICS + if (WINDOWPORT("curses")) + (*cursesgraphics_mode_callback)(); +#endif +#endif +#ifdef ENHANCED_SYMBOLS + if (SYMHANDLING(H_UTF8) && utf8graphics_mode_callback) + (*utf8graphics_mode_callback)(); +#endif + /* strip role,race,&c suffix; calls askname() if g.plname[] is empty or holds a generic user name like "player" or "games" */ plnamesuffix(); @@ -656,7 +694,12 @@ process_options(int argc, char * argv[]) #ifndef NODUMPENUMS if (argcheck(argc, argv, ARG_DUMPENUMS) == 2) { nethack_exit(EXIT_SUCCESS); - } + } +#ifdef ENHANCED_SYMBOLS + if (argcheck(argc, argv, ARG_DUMPGLYPHIDS) == 2) { + nethack_exit(EXIT_SUCCESS); + } +#endif #endif if (argcheck(argc, argv, ARG_DEBUG) == 1) { argc--; diff --git a/win/Qt/qt_map.cpp b/win/Qt/qt_map.cpp index f25067fb0..d55da1279 100644 --- a/win/Qt/qt_map.cpp +++ b/win/Qt/qt_map.cpp @@ -547,7 +547,7 @@ void NetHackQtMapViewport::PrintGlyph(int x, int y, { Glyph(x, y) = (unsigned short) glyphinfo->glyph; Glyphttychar(x, y) = (unsigned short) glyphinfo->ttychar; - Glyphcolor(x, y) = (unsigned short) glyphinfo->gm.color; + Glyphcolor(x, y) = (unsigned short) glyphinfo->gm.sym.color; Glyphflags(x, y) = glyphinfo->gm.glyphflags; Glyphtileidx(x, y) = (unsigned short) glyphinfo->gm.tileidx; Changed(x, y); diff --git a/win/X11/winmap.c b/win/X11/winmap.c index a041377ee..c959e868c 100644 --- a/win/X11/winmap.c +++ b/win/X11/winmap.c @@ -113,7 +113,7 @@ X11_print_glyph( register unsigned char *co_ptr; #endif - color = glyphinfo->gm.color; + color = glyphinfo->gm.sym.color; special = glyphinfo->gm.glyphflags; och = glyphinfo->ttychar; ch = (uchar) och; diff --git a/win/curses/cursinit.c b/win/curses/cursinit.c index 1840836c4..8928da9f3 100644 --- a/win/curses/cursinit.c +++ b/win/curses/cursinit.c @@ -741,8 +741,8 @@ curses_init_options(void) set_option_mod_status("eight_bit_tty", set_in_config); /* If we don't have a symset defined, load the curses symset by default */ - if (!g.symset[PRIMARY].explicitly) - load_symset("curses", PRIMARY); + if (!g.symset[PRIMARYSET].explicitly) + load_symset("curses", PRIMARYSET); if (!g.symset[ROGUESET].explicitly) load_symset("default", ROGUESET); diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index ff4a64c0e..69c41c5e1 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -7,6 +7,9 @@ #include "hack.h" #include "color.h" #include "wincurs.h" +#ifdef CURSES_UNICODE +#include "locale.h" +#endif /* define this if not linking with tty.o|.obj for some reason */ #ifdef CURSES_DEFINE_ERASE_CHAR @@ -41,6 +44,9 @@ struct window_procs curses_procs = { #endif | WC_PERM_INVENT | WC_POPUP_DIALOG | WC_SPLASH_SCREEN), (WC2_DARKGRAY | WC2_HITPOINTBAR +#ifdef CURSES_UNICODE + | WC2_U_UTF8STR +#endif #ifdef SELECTSAVED | WC2_SELECTSAVED #endif @@ -150,6 +156,10 @@ curses_init_nhwindows(int *argcp UNUSED, char window_title[BUFSZ]; #endif +#ifdef CURSES_UNICODE + setlocale(LC_CTYPE, ""); +#endif + #ifdef XCURSES base_term = Xinitscr(*argcp, argv); #else @@ -760,7 +770,7 @@ curses_print_glyph(winid wid, xchar x, xchar y, glyph = glyphinfo->glyph; special = glyphinfo->gm.glyphflags; ch = glyphinfo->ttychar; - color = glyphinfo->gm.color; + color = glyphinfo->gm.sym.color; if ((special & MG_PET) && iflags.hilite_pet) { attr = iflags.wc2_petattr; } @@ -789,7 +799,17 @@ curses_print_glyph(winid wid, xchar x, xchar y, } } +#ifdef ENHANCED_SYMBOLS + if (SYMHANDLING(H_UTF8) + && glyphinfo->gm.u + && glyphinfo->gm.u->utf8str) { + curses_putch(wid, x, y, ch, glyphinfo->gm.u, color, attr); + } else { + curses_putch(wid, x, y, ch, NULL, color, attr); + } +#else curses_putch(wid, x, y, ch, color, attr); +#endif } /* diff --git a/win/curses/curswins.c b/win/curses/curswins.c index d4d88d414..b22b5ed18 100644 --- a/win/curses/curswins.c +++ b/win/curses/curswins.c @@ -33,6 +33,7 @@ typedef struct nhchar { int ch; /* character */ int color; /* color info for character */ int attr; /* attributes of character */ + struct unicode_representation *unicode_representation; } nethack_char; static boolean map_clipped; /* Map window smaller than 80x21 */ @@ -363,7 +364,13 @@ curs_destroy_all_wins(void) /* Print a single character in the given window at the given coordinates */ void +#ifdef ENHANCED_SYMBOLS +curses_putch(winid wid, int x, int y, int ch, + struct unicode_representation *unicode_representation, + int color, int attr) +#else curses_putch(winid wid, int x, int y, int ch, int color, int attr) +#endif { static boolean map_initted = FALSE; int sx, sy, ex, ey; @@ -387,6 +394,9 @@ curses_putch(winid wid, int x, int y, int ch, int color, int attr) map[y][x].ch = ch; map[y][x].color = color; map[y][x].attr = attr; +#ifdef ENHANCED_SYMBOLS + map[y][x].unicode_representation = unicode_representation; +#endif nch = map[y][x]; (void) curses_map_borders(&sx, &sy, &ex, &ey, -1, -1); @@ -589,7 +599,10 @@ write_char(WINDOW * win, int x, int y, nethack_char nch) #ifdef PDCURSES mvwaddrawch(win, y, x, nch.ch); #else - mvwaddch(win, y, x, nch.ch); + if (nch.unicode_representation && nch.unicode_representation->utf8str) + mvwprintw(win, y, x, "%s", nch.unicode_representation->utf8str); + else + mvwaddch(win, y, x, nch.ch); #endif curses_toggle_color_attr(win, nch.color, nch.attr, OFF); } @@ -694,6 +707,7 @@ clear_map(void) map[y][x].ch = ' '; map[y][x].color = NO_COLOR; map[y][x].attr = A_NORMAL; + map[y][x].unicode_representation = NULL; } } } diff --git a/win/curses/curswins.h b/win/curses/curswins.h index 616024818..ea599a338 100644 --- a/win/curses/curswins.h +++ b/win/curses/curswins.h @@ -21,7 +21,12 @@ void curses_refresh_nhwin(winid wid); void curses_del_nhwin(winid wid); void curses_del_wid(winid wid); void curs_destroy_all_wins(void); +#ifdef ENHANCED_SYMBOLS +void curses_putch(winid wid, int x, int y, int ch, + struct unicode_representation *ur, int color, int attrs); +#else void curses_putch(winid wid, int x, int y, int ch, int color, int attrs); +#endif void curses_get_window_xy(winid wid, int *x, int *y); boolean curses_window_has_border(winid wid); boolean curses_window_exists(winid wid); diff --git a/win/share/tilemap.c b/win/share/tilemap.c index 2e7021652..02ec1ffe4 100644 --- a/win/share/tilemap.c +++ b/win/share/tilemap.c @@ -1341,22 +1341,32 @@ main(int argc UNUSED, char *argv[] UNUSED) Fprintf(ofp, "int maxmontile = %d,\n", lastmontile); Fprintf(ofp, "%smaxobjtile = %d,\n", indent, lastobjtile); Fprintf(ofp, "%smaxothtile = %d;\n\n", indent, lastothtile); - Fprintf(ofp, - "/* glyph, ttychar, { color, symidx, ovidx, glyphflags, tileidx} */\n"); + Fprintf(ofp, + "/* glyph, ttychar, { glyphflags, {color, symidx}, ovidx, tileidx, 0 } */\n"); Fprintf(ofp, "const glyph_info nul_glyphinfo = { \n"); Fprintf(ofp, "%sNO_GLYPH, ' ',\n", indent); Fprintf(ofp, "%s%s{ /* glyph_map */\n", indent, indent); - Fprintf(ofp, "%s%s%sNO_COLOR, SYM_UNEXPLORED + SYM_OFF_X,\n", + Fprintf(ofp, "%s%s%sMG_UNEXPL, { NO_COLOR, SYM_UNEXPLORED + SYM_OFF_X },\n", indent, indent, indent); - Fprintf(ofp, "%s%s%sMG_UNEXPL, %d\n", indent, indent, indent, +#ifdef ENHANCED_SYMBOLS + Fprintf(ofp, "%s%s%s%d, 0\n", indent, indent, indent, + TILE_unexplored); +#else + Fprintf(ofp, "%s%s%s%d\n", indent, indent, indent, TILE_unexplored); +#endif Fprintf(ofp, "%s%s}\n", indent, indent); Fprintf(ofp, "};\n"); Fprintf(ofp, "\nglyph_map glyphmap[MAX_GLYPH] = {\n"); for (i = 0; i < MAX_GLYPH; i++) { tilenum = tilemap[i].tilenum; - Fprintf(ofp, " { 0, 0, 0U, %4d }, /* [%04d] %s=%03d %s */\n", + Fprintf(ofp, +#ifdef ENHANCED_SYMBOLS + " { 0U, { 0, 0 }, %4d, 0 }, /* [%04d] %s:%03d %s */\n", +#else + " { 0U, { 0, 0 }, %4d}, /* [%04d] %s:%03d %s */\n", +#endif tilenum, i, tilesrc_texts[tilelist[tilenum]->src], tilelist[tilenum]->file_entry, diff --git a/win/tty/termcap.c b/win/tty/termcap.c index 231518fb6..4e78a854b 100644 --- a/win/tty/termcap.c +++ b/win/tty/termcap.c @@ -17,6 +17,9 @@ static char *e_atr2str(int); void cmov(int, int); void nocmov(int, int); +void term_start_24bitcolor(struct unicode_representation *); +void term_end_24bitcolor(void); + #if defined(TEXTCOLOR) && defined(TERMLIB) #if (!defined(UNIX) || !defined(TERMINFO)) && !defined(TOS) static void analyze_seq(char *, int *, int *); @@ -430,11 +433,12 @@ tty_decgraphics_termcap_fixup(void) #endif /* TERMLIB */ #if defined(ASCIIGRAPH) && defined(PC9800) -extern void (*ibmgraphics_mode_callback)(void); /* defined in drawing.c */ +extern void (*ibmgraphics_mode_callback)(void); /* defined in symbols.c */ #endif +extern void (*utf8graphics_mode_callback)(void); /* defined in symbols.c */ #ifdef PC9800 -extern void (*ascgraphics_mode_callback)(void); /* defined in drawing.c */ +extern void (*ascgraphics_mode_callback)(void); /* defined in symbols.c */ static void tty_ascgraphics_hilite_fixup(void); static void @@ -478,6 +482,10 @@ tty_start_screen(void) /* set up callback in case option is not set yet but toggled later */ decgraphics_mode_callback = tty_decgraphics_termcap_fixup; #endif +#ifdef ENHANCED_SYMBOLS + utf8graphics_mode_callback = tty_utf8graphics_fixup; +#endif + if (g.Cmd.num_pad) tty_number_pad(1); /* make keypad send digits */ } @@ -870,14 +878,21 @@ const struct { { COLOR_MAGENTA, CLR_MAGENTA, CLR_BRIGHT_MAGENTA }, { COLOR_CYAN, CLR_CYAN, CLR_BRIGHT_CYAN } }; +typedef struct { + unsigned char r, g, b; +} RGB; + static char nilstring[] = ""; static void init_hilite(void) { + int c, colors; char *setf, *scratch; - int c, md_len = 0; - int colors = tgetnum("Co"); + + colors = tgetnum("Co"); + iflags.colorcount = colors; + int md_len = 0; if (colors < 8 || (MD == NULL) || (strlen(MD) == 0) || ((setf = tgetstr("AF", (char **) 0)) == (char *) 0 @@ -1410,9 +1425,66 @@ term_start_color(int color) if (color < CLR_MAX && hilites[color] && *hilites[color]) xputs(hilites[color]); } - #endif /* TEXTCOLOR */ -#endif /* TTY_GRAPHICS && !NO_TERMS */ +#ifdef ENHANCED_SYMBOLS + +#ifndef SEP2 +#define tcfmtstr "\033[38;2;%ld;%ld;%ldm" +#ifdef UNIX +#define tcfmtstr24bit "\033[38;2;%u;%u;%um" +#define tcfmtstr256 "\033[38;5;%dm" +#else +#define tcfmtstr "\033[38:2:%ld:%ld:%ldm" +#define tcfmtstr24bit "\033[38;2;%lu;%lu;%lum" +#define tcfmtstr256 "\033[38:5:%ldm" +#endif +#endif + +static void emit24bit(long mcolor); +static void emit256(int u256coloridx); + +static void emit24bit(long mcolor) +{ + static char tcolorbuf[QBUFSZ]; + + Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr, + ((mcolor >> 16) & 0xFF), /* red */ + ((mcolor >> 8) & 0xFF), /* green */ + ((mcolor >> 0) & 0xFF)); /* blue */ + xputs(tcolorbuf); +} + +static void emit256(int u256coloridx) +{ + static char tcolorbuf[QBUFSZ]; + + Snprintf(tcolorbuf, sizeof tcolorbuf, tcfmtstr256, + u256coloridx); + xputs(tcolorbuf); +} + +void +term_start_24bitcolor(struct unicode_representation *urep) +{ + if (urep && SYMHANDLING(H_UTF8)) { + /* color 0 has bit 0x1000000 set */ + long mcolor = (urep->ucolor & 0xFFFFFF); + if (iflags.colorcount == 256) + emit256(urep->u256coloridx); + else + emit24bit(mcolor); + } +} + +void +term_end_24bitcolor(void) +{ + if (SYMHANDLING(H_UTF8)) { + xputs("\033[0m"); + } +} +#endif /* ENHANCED_SYMBOLS */ +#endif /* TTY_GRAPHICS && !NO_TERMS */ /*termcap.c*/ diff --git a/win/tty/topl.c b/win/tty/topl.c index 38e556ebc..fecdf665e 100644 --- a/win/tty/topl.c +++ b/win/tty/topl.c @@ -124,13 +124,15 @@ redotoplin(const char *str) int otoplin = ttyDisplay->toplin; home(); - if (*str & 0x80) { - /* kludge for the / command, the only time we ever want a */ - /* graphics character on the top line */ - g_putch((int) *str++); - ttyDisplay->curx++; + if (!ttyDisplay->topl_utf8) { + if (*str & 0x80) { + /* kludge for the / command, the only time we ever want a */ + /* graphics character on the top line */ + g_putch((int) *str++); + ttyDisplay->curx++; + } + end_glyphout(); /* in case message printed during graphics output */ } - end_glyphout(); /* in case message printed during graphics output */ putsyms(str); cl_end(); ttyDisplay->toplin = TOPLINE_NEED_MORE; diff --git a/win/tty/wintty.c b/win/tty/wintty.c index 5ddfabdd4..b4494385b 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -97,7 +97,7 @@ struct window_procs tty_procs = { #ifdef MSDOS | WC_TILED_MAP | WC_ASCII_MAP #endif -#if defined(WIN32CON) +#if defined(WIN32) | WC_MOUSE_SUPPORT #endif | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), @@ -109,7 +109,12 @@ struct window_procs tty_procs = { | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_RESET_STATUS #endif - | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_STATUSLINES), + | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_URGENT_MESG | WC2_STATUSLINES) + | WC2_U_UTF8STR +#if !defined(NO_TERMS) || defined(WIN32) + | WC2_U_24BITCOLOR +#endif + , #ifdef TEXTCOLOR {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ #else @@ -118,7 +123,8 @@ struct window_procs tty_procs = { tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows, tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow, - tty_destroy_nhwindow, tty_curs, tty_putstr, genl_putmixed, + tty_destroy_nhwindow, tty_curs, tty_putstr, + tty_putmixed, tty_display_file, tty_start_menu, tty_add_menu, tty_end_menu, tty_select_menu, tty_message_menu, tty_update_inventory, tty_mark_synch, tty_wait_synch, @@ -159,6 +165,7 @@ struct DisplayDesc *ttyDisplay; /* the tty display descriptor */ extern void cmov(int, int); /* from termcap.c */ extern void nocmov(int, int); /* from termcap.c */ + #if defined(UNIX) || defined(VMS) static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ #endif @@ -231,6 +238,9 @@ static void shrink_dlvl(int); static void status_sanity_check(void); #endif /* NH_DEVEL_STATUS */ #endif +#if !defined(NO_TERMS) && !defined(WIN32) +void g_pututf8(uint8 *utf8str); +#endif /* * A string containing all the default commands -- to add to a list @@ -442,6 +452,7 @@ tty_init_nhwindows(int *argcp UNUSED, char **argv UNUSED) /* set up tty descriptor */ ttyDisplay = (struct DisplayDesc *) alloc(sizeof (struct DisplayDesc)); ttyDisplay->toplin = TOPLINE_EMPTY; + ttyDisplay->topl_utf8 = 0; /* putmixed may set this */ ttyDisplay->rows = hgt; ttyDisplay->cols = wid; ttyDisplay->curx = ttyDisplay->cury = 0; @@ -2344,9 +2355,14 @@ process_text_window(winid window, struct WinDesc *cw) ) { /* message recall for msg_window:full/combination/reverse might have output from '/' in it (see redotoplin()) */ - if (linestart && (*cp & 0x80) != 0) { - g_putch(*cp); - end_glyphout(); + if (linestart) { + if (SYMHANDLING(H_UTF8)) { + /* FIXME: what is actually in that line? is it the \GNNNNNNNN or UTF-8? */ + g_putch(*cp); + } else if ((*cp & 0x80) != 0) { + g_putch(*cp); + end_glyphout(); + } linestart = FALSE; } else { (void) putchar(*cp); @@ -3367,8 +3383,11 @@ g_putch(int in_ch) register char ch = (char) in_ch; HUPSKIP(); + #if defined(ASCIIGRAPH) && !defined(NO_TERMS) - if (SYMHANDLING(H_IBM) + if (SYMHANDLING(H_UTF8)) { + (void) putchar(ch); + } else if (SYMHANDLING(H_IBM) /* for DECgraphics, lower-case letters with high bit set mean switch character set and render with high bit clear; user might want 8-bits for other characters */ @@ -3397,6 +3416,17 @@ g_putch(int in_ch) return; } + +void +g_pututf8(uint8 *utf8str) +{ + HUPSKIP(); + while (*utf8str) { + (void) putchar(*utf8str); + utf8str++; + } + return; +} #endif /* !WIN32 */ #ifdef CLIPPING @@ -3458,6 +3488,11 @@ tty_print_glyph(winid window, xchar x, xchar y, boolean inverse_on = FALSE; int ch, color; unsigned special; +#ifdef ENHANCED_SYMBOLS +#if !defined(NO_TERMS) || defined(WIN32) + boolean color24bit_on = FALSE; +#endif +#endif HUPSKIP(); #ifdef CLIPPING @@ -3468,7 +3503,7 @@ tty_print_glyph(winid window, xchar x, xchar y, #endif /* get glyph ttychar, color, and special flags */ ch = glyphinfo->ttychar; - color = glyphinfo->gm.color; + color = glyphinfo->gm.sym.color; special = glyphinfo->gm.glyphflags; print_vt_code2(AVTC_SELECT_WINDOW, window); @@ -3484,24 +3519,33 @@ tty_print_glyph(winid window, xchar x, xchar y, backsp(); } #endif - + if (iflags.use_color) { #ifdef TEXTCOLOR - if (iflags.wizmgender && (special & MG_FEMALE) && iflags.use_inverse) { - if (ttyDisplay->color != NO_COLOR) - term_end_color(); - term_start_attr(ATR_INVERSE); - inverse_on = TRUE; - ttyDisplay->color = CLR_RED; - term_start_color(ttyDisplay->color); - } else if (color != ttyDisplay->color) { - if (ttyDisplay->color != NO_COLOR) - term_end_color(); - ttyDisplay->color = color; - if (color != NO_COLOR) - term_start_color(color); - } + if (color != ttyDisplay->color) { + if (ttyDisplay->color != NO_COLOR) + term_end_color(); + } +#endif +#if !defined(NO_TERMS) || defined(WIN32) +#ifdef ENHANCED_SYMBOLS + /* we don't link with termcap.o if NO_TERMS is defined */ + if ((tty_procs.wincap2 & WC2_U_24BITCOLOR) && SYMHANDLING(H_UTF8) + && glyphinfo->gm.u && glyphinfo->gm.u->ucolor) { + term_start_24bitcolor(glyphinfo->gm.u); + color24bit_on = TRUE; + } else +#endif +#endif + { +#ifdef TEXTCOLOR + ttyDisplay->color = color; + if (color != NO_COLOR) + term_start_color(color); #endif /* TEXTCOLOR */ - +#if !defined(NO_TERMS) || defined(WIN32) + } +#endif + } /* iflags.use_color aka iflags.wc_color */ /* must be after color check; term_end_color may turn off inverse too; BW_LAVA and BW_ICE won't ever be set when color is on; (tried bold for ice but it didn't look very good; inverse is easier @@ -3519,21 +3563,34 @@ tty_print_glyph(winid window, xchar x, xchar y, xputg(glyphinfo); else #endif - g_putch(ch); /* print the character */ - - if (inverse_on) { +#ifdef ENHANCED_SYMBOLS + if ((tty_procs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8) + && glyphinfo->gm.u && glyphinfo->gm.u->utf8str) { + /* we have a sequence to do */ + g_pututf8(glyphinfo->gm.u->utf8str); + } else +#endif + g_putch(ch); /* print the character */ + + if (inverse_on) term_end_attr(ATR_INVERSE); + if (iflags.use_color) { #ifdef TEXTCOLOR /* turn off color as well, turning off ATR_INVERSE may have done - this already and if so, we won't know the current state unless - we do it explicitly */ + this already and if so, we won't know the current state unless + we do it explicitly */ if (ttyDisplay->color != NO_COLOR) { term_end_color(); ttyDisplay->color = NO_COLOR; } +#endif +#ifdef ENHANCED_SYMBOLS +#if !defined(NO_TERMS) || defined(WIN32) + if (color24bit_on) + term_end_24bitcolor(); +#endif #endif } - print_vt_code1(AVTC_GLYPH_END); wins[window]->curx++; /* one character over */ @@ -3688,6 +3745,31 @@ tty_update_positionbar(char *posbar) } #endif /* POSITIONBAR */ +void +tty_putmixed(winid window, int attr, const char *str) +{ + struct WinDesc *cw; + char buf[BUFSZ]; +#ifdef ENHANCED_SYMBOLS + int utf8flag = 0; +#endif + + if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) { + tty_raw_print(str); + return; + } +#ifdef ENHANCED_SYMBOLS + if ((windowprocs.wincap2 & WC2_U_UTF8STR) && SYMHANDLING(H_UTF8)) { + mixed_to_utf8(buf, sizeof buf, str, &utf8flag); + if (cw->type == NHW_MESSAGE) + ttyDisplay->topl_utf8 = utf8flag; + } else +#endif + decode_mixed(buf, str); + /* now send it to the normal tty_putstr */ + tty_putstr(window, attr, buf); + ttyDisplay->topl_utf8 = 0; +} /* * +------------------+ diff --git a/win/win32/mhmap.c b/win/win32/mhmap.c index 6f13d2404..df8510bdd 100644 --- a/win/win32/mhmap.c +++ b/win/win32/mhmap.c @@ -930,7 +930,7 @@ paintGlyph(PNHMapWindow data, int i, int j, RECT * rect) OldFg = SetTextColor(hDC, nhcolor_to_RGB(color)); #else ch = (char) data->map[i][j].ttychar; - color = (int) data->map[i][j].gm.color; + color = (int) data->map[i][j].gm.sym.color; if (((data->map[i][j].gm.glyphflags & MG_PET) && iflags.hilite_pet) || ((data->map[i][j].gm.glyphflags & (MG_DETECT | MG_BW_LAVA)) && iflags.use_inverse)) { @@ -1000,7 +1000,7 @@ static void setGlyph(PNHMapWindow data, int i, int j, if ((data->map[i][j].glyph != fg->glyph) || (data->bkmap[i][j].glyph != bg->glyph) || data->map[i][j].ttychar != fg->ttychar - || data->map[i][j].gm.color != fg->gm.color + || data->map[i][j].gm.sym.color != fg->gm.sym.color || data->map[i][j].gm.glyphflags != fg->gm.glyphflags || data->map[i][j].gm.tileidx != fg->gm.tileidx) { data->map[i][j] = *fg; -- 2.50.0