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; + int reslt; - 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); - - 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 -term_start_24bitcolor(long color24bit) +g_pututf8(uint8 *sequence) { - console.color24 = color24bit; /* color 0 has bit 0x1000000 set */ + 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(struct unicode_representation *uval) +{ + 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;