From 593977397190daca68132269a78b9016b478025c Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Wed, 26 Aug 2020 19:17:40 -0700 Subject: [PATCH 01/42] initial shim graphics --- include/config.h | 8 ++- src/mdlib.c | 3 + src/rip.c | 38 +++++------ src/windows.c | 6 ++ win/shim/winshim.c | 161 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 win/shim/winshim.c diff --git a/include/config.h b/include/config.h index bbcd4b112..a94b38267 100644 --- a/include/config.h +++ b/include/config.h @@ -64,7 +64,7 @@ * Define the default window system. This should be one that is compiled * into your system (see defines above). Known window systems are: * - * tty, X11, mac, amii, BeOS, Qt, Gem, Gnome + * tty, X11, mac, amii, BeOS, Qt, Gem, Gnome, shim */ /* MAC also means MAC windows */ @@ -144,6 +144,12 @@ #endif #endif +#ifdef SHIM_GRAPHICS +#ifndef DEFAULT_WINDOW_SYS +#define DEFAULT_WINDOW_SYS "shim" +#endif +#endif + #ifdef X11_GRAPHICS /* * There are two ways that X11 tiles may be defined. (1) using a custom diff --git a/src/mdlib.c b/src/mdlib.c index 59b94b459..52f6a6d59 100644 --- a/src/mdlib.c +++ b/src/mdlib.c @@ -123,6 +123,9 @@ static struct win_info window_opts[] = { #ifdef MSWIN_GRAPHICS /* win32 */ { "mswin", "Windows GUI", TRUE }, #endif +#ifdef SHIM_GRAPHICS + { "shim", "Nethack Library Windowing Shim", TRUE }, +#endif #if 0 /* remainder have been retired */ #ifdef GNOME_GRAPHICS /* unmaintained/defunct */ diff --git a/src/rip.c b/src/rip.c index dca074720..b61a76254 100644 --- a/src/rip.c +++ b/src/rip.c @@ -1,4 +1,4 @@ -/* NetHack 3.7 rip.c $NHDT-Date: 1597967808 2020/08/20 23:56:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.33 $ */ +/* NetHack 3.7 rip.c $NHDT-Date: 1596498204 2020/08/03 23:43:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -6,7 +6,7 @@ #include "hack.h" #if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \ - || defined(MSWIN_GRAPHICS) || defined(DUMPLOG) || defined(CURSES_GRAPHICS) + || defined(MSWIN_GRAPHICS) || defined(DUMPLOG) || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS) #define TEXT_TOMBSTONE #endif #if defined(mac) || defined(__BEOS__) || defined(WIN32_GRAPHICS) @@ -60,10 +60,12 @@ static const char *rip_txt[] = { }; #define STONE_LINE_CENT 19 /* char[] element of center of stone face */ #endif /* NH320_DEDICATION */ -#define STONE_LINE_LEN 16 /* # chars that fit on one line - * (note 1 ' ' border) */ -#define NAME_LINE 6 /* *char[] line # for player name */ -#define GOLD_LINE 7 /* *char[] line # for amount of gold */ +#define STONE_LINE_LEN \ + 16 /* # chars that fit on one line \ + * (note 1 ' ' border) \ + */ +#define NAME_LINE 6 /* *char[] line # for player name */ +#define GOLD_LINE 7 /* *char[] line # for amount of gold */ #define DEATH_LINE 8 /* *char[] line # for death description */ #define YEAR_LINE 12 /* *char[] line # for year */ @@ -88,9 +90,9 @@ time_t when; register char **dp; register char *dpx; char buf[BUFSZ]; + long year; register int x; - int line, year; - long cash; + int line; g.rip = dp = (char **) alloc(sizeof(rip_txt)); for (x = 0; rip_txt[x]; ++x) @@ -98,15 +100,13 @@ time_t when; dp[x] = (char *) 0; /* Put name on stone */ - Sprintf(buf, "%.*s", (int) STONE_LINE_LEN, g.plname); + Sprintf(buf, "%s", g.plname); + buf[STONE_LINE_LEN] = 0; center(NAME_LINE, buf); /* Put $ on stone */ - cash = max(g.done_money, 0L); - /* arbitrary upper limit; practical upper limit is quite a bit less */ - if (cash > 999999999L) - cash = 999999999L; - Sprintf(buf, "%ld Au", cash); + Sprintf(buf, "%ld Au", g.done_money); + buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */ center(GOLD_LINE, buf); /* Put together death description */ @@ -114,11 +114,11 @@ time_t when; /* Put death type on stone */ for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) { + register int i, i0; char tmpchar; - int i, i0 = (int) strlen(dpx); - if (i0 > STONE_LINE_LEN) { - for (i = STONE_LINE_LEN; (i > 0) && (i0 > STONE_LINE_LEN); --i) + if ((i0 = strlen(dpx)) > STONE_LINE_LEN) { + for (i = STONE_LINE_LEN; ((i0 > STONE_LINE_LEN) && i); i--) if (dpx[i] == ' ') i0 = i; if (!i) @@ -135,8 +135,8 @@ time_t when; } /* Put year on stone */ - year = (int) ((yyyymmdd(when) / 10000L) % 10000L); - Sprintf(buf, "%4d", year); + year = yyyymmdd(when) / 10000L; + Sprintf(buf, "%4ld", year); center(YEAR_LINE, buf); #ifdef DUMPLOG diff --git a/src/windows.c b/src/windows.c index 03bc1e592..f3af21f49 100644 --- a/src/windows.c +++ b/src/windows.c @@ -44,6 +44,9 @@ extern struct window_procs Gnome_procs; #ifdef MSWIN_GRAPHICS extern struct window_procs mswin_procs; #endif +#ifdef SHIM_GRAPHICS +extern struct window_procs shim_procs; +#endif #ifdef WINCHAIN extern struct window_procs chainin_procs; extern void FDECL(chainin_procs_init, (int)); @@ -128,6 +131,9 @@ static struct win_choices { #ifdef MSWIN_GRAPHICS { &mswin_procs, 0 CHAINR(0) }, #endif +#ifdef SHIM_GRAPHICS + { &shim_procs, 0 CHAINR(0) }, +#endif #ifdef WINCHAIN { &chainin_procs, chainin_procs_init, chainin_procs_chain }, { (struct window_procs *) &chainout_procs, chainout_procs_init, diff --git a/win/shim/winshim.c b/win/shim/winshim.c new file mode 100644 index 000000000..d82d21334 --- /dev/null +++ b/win/shim/winshim.c @@ -0,0 +1,161 @@ +/* NetHack 3.7 winshim.c $NHDT-Date: 1596498345 2020/08/03 23:45:45 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.259 $ */ +/* Copyright (c) Adam Powers, 2020 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* not an actual windowing port, but a fake win port for libnethack */ + +#include "hack.h" + +#ifdef SHIM_GRAPHICS + +enum win_types { + WINSTUB_MESSAGE = 1, + WINSTUB_MAP, + WINSTUB_MENU, + WINSTUB_EXT +}; + +#define VSTUB(name, args) \ +void name args { \ + printf ("Running " #name "...\n"); \ +} + +#define STUB(name, retval, args) \ +name args { \ + printf ("Running " #name "...\n"); \ + return retval; \ +} + +#define DECL(name, args) \ +void name args; + +VSTUB(shim_init_nhwindows,(int *argcp, char **argv)) +VSTUB(shim_player_selection,(void)) +VSTUB(shim_askname,(void)) +VSTUB(shim_get_nh_event,(void)) +VSTUB(shim_exit_nhwindows,(const char *a)) +VSTUB(shim_suspend_nhwindows,(const char *a)) +VSTUB(shim_resume_nhwindows,(void)) +winid STUB(shim_create_nhwindow, WINSTUB_MAP, (int a)) +VSTUB(shim_clear_nhwindow,(winid a)) +VSTUB(shim_display_nhwindow,(winid a, BOOLEAN_P b)) +VSTUB(shim_destroy_nhwindow,(winid a)) +VSTUB(shim_curs,(winid a, int x, int y)) +DECL(shim_putstr,(winid w, int attr, const char *str)) +VSTUB(shim_display_file,(const char *a, BOOLEAN_P b)) +VSTUB(shim_start_menu,(winid w, unsigned long mbehavior)) +VSTUB(shim_add_menu,(winid a, int b, const ANY_P *c, CHAR_P d, CHAR_P e, int f, const char *h, unsigned int k)) +VSTUB(shim_end_menu,(winid a, const char *b)) +int STUB(shim_select_menu,0,(winid a, int b, MENU_ITEM_P **c)) +char STUB(shim_message_menu,'y',(CHAR_P a, int b, const char *c)) +VSTUB(shim_update_inventory,(void)) +VSTUB(shim_mark_synch,(void)) +VSTUB(shim_wait_synch,(void)) +VSTUB(shim_cliparound,(int a, int b)) +VSTUB(shim_update_positionbar,(char *a)) +DECL(shim_print_glyph,(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)) +DECL(shim_raw_print,(const char *str)) +VSTUB(shim_raw_print_bold,(const char *a)) +int STUB(shim_nhgetch,0,(void)) +int STUB(shim_nh_poskey,0,(int *a, int *b, int *c)) +VSTUB(shim_nhbell,(void)) +int STUB(shim_doprev_message,0,(void)) +char STUB(shim_yn_function,'y',(const char *a, const char *b, CHAR_P c)) +VSTUB(shim_getlin,(const char *a, char *b)) +int STUB(shim_get_ext_cmd,0,(void)) +VSTUB(shim_number_pad,(int a)) +VSTUB(shim_delay_output,(void)) +VSTUB(shim_change_color,(int a, long b, int c)) +VSTUB(shim_change_background,(int a)) +short STUB(set_shim_font_name,0,(winid a, char *b)) +VSTUB(shim_get_color_string,(void)) + +/* other defs that really should go away (they're tty specific) */ +VSTUB(shim_start_screen, (void)) +VSTUB(shim_end_screen, (void)) +VSTUB(shim_preference_update, (const char *a)) +char *STUB(shim_getmsghistory, (char *)"", (BOOLEAN_P a)) +VSTUB(shim_putmsghistory, (const char *a, BOOLEAN_P b)) +VSTUB(shim_status_init, (void)) +VSTUB(shim_status_enablefield, (int a, const char *b, const char *c, BOOLEAN_P d)) +VSTUB(shim_status_update, (int a, genericptr_t b, int c, int d, int e, unsigned long *f)) + + +/* old: | WC_TILED_MAP */ +/* Interface definition, for windows.c */ +struct window_procs shim_procs = { + "shim", + (0 + | WC_ASCII_MAP + | WC_COLOR | WC_HILITE_PET | WC_INVERSE | WC_EIGHT_BIT_IN), + (0 +#if defined(SELECTSAVED) + | WC2_SELECTSAVED +#endif +#if defined(STATUS_HILITES) + | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS + | WC2_RESET_STATUS +#endif + | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_STATUSLINES), +#ifdef TEXTCOLOR + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ +#else + {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1}, +#endif + shim_init_nhwindows, shim_player_selection, shim_askname, shim_get_nh_event, + shim_exit_nhwindows, shim_suspend_nhwindows, shim_resume_nhwindows, + shim_create_nhwindow, shim_clear_nhwindow, shim_display_nhwindow, + shim_destroy_nhwindow, shim_curs, shim_putstr, genl_putmixed, + shim_display_file, shim_start_menu, shim_add_menu, shim_end_menu, + shim_select_menu, shim_message_menu, shim_update_inventory, shim_mark_synch, + shim_wait_synch, +#ifdef CLIPPING + shim_cliparound, +#endif +#ifdef POSITIONBAR + shim_update_positionbar, +#endif + shim_print_glyph, shim_raw_print, shim_raw_print_bold, shim_nhgetch, + shim_nh_poskey, shim_nhbell, shim_doprev_message, shim_yn_function, + shim_getlin, shim_get_ext_cmd, shim_number_pad, shim_delay_output, +#ifdef CHANGE_COLOR /* the Mac uses a palette device */ + shim_change_color, +#ifdef MAC + shim_change_background, set_shim_font_name, +#endif + shim_get_color_string, +#endif + + /* other defs that really should go away (they're tty specific) */ + shim_start_screen, shim_end_screen, genl_outrip, + shim_preference_update, + shim_getmsghistory, shim_putmsghistory, + shim_status_init, + genl_status_finish, genl_status_enablefield, +#ifdef STATUS_HILITES + shim_status_update, +#else + genl_status_update, +#endif + genl_can_suspend_yes, +}; + +void shim_print_glyph(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph) { + /* map glyph to character and color */ + // (void) mapglyph(glyph, &ch, &color, &special, x, y, 0); + + fprintf(stdout, "shim_print_glyph (%d,%d): %c\n", x,y,(char)glyph); + fflush(stdout); +} + +void shim_raw_print(const char *str) { + fprintf(stdout, "shim_raw_print: %s\n", str); + fflush(stdout); +} + +void shim_putstr(winid w, int attr, const char *str) { + fprintf(stdout, "shim_putstr (win %d): %s\n", w, str); + fflush(stdout); +} + +#endif /* SHIM_GRAPHICS */ \ No newline at end of file From 1aa053d1d8efee01ddd09ab5fb92f5e20065c271 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Wed, 26 Aug 2020 19:22:00 -0700 Subject: [PATCH 02/42] initial libnethack --- .gitignore | 9 +- include/global.h | 11 +- sys/lib/Makefile.dat | 136 ++++ sys/lib/Makefile.src | 1160 +++++++++++++++++++++++++++ sys/lib/Makefile.top | 332 ++++++++ sys/lib/Makefile.utl | 382 +++++++++ sys/lib/hints/include/multiw-1.2020 | 37 + sys/lib/hints/include/multiw-2.2020 | 108 +++ sys/lib/hints/macOS.2020 | 443 ++++++++++ sys/lib/hints/wasm | 153 ++++ sys/lib/libnethackmain.c | 824 +++++++++++++++++++ sys/lib/mkmkfile.sh | 44 + sys/lib/setup.sh | 37 + sys/lib/sysconf | 150 ++++ util/makedefs.c | 8 + 15 files changed, 3831 insertions(+), 3 deletions(-) create mode 100644 sys/lib/Makefile.dat create mode 100644 sys/lib/Makefile.src create mode 100644 sys/lib/Makefile.top create mode 100644 sys/lib/Makefile.utl create mode 100644 sys/lib/hints/include/multiw-1.2020 create mode 100644 sys/lib/hints/include/multiw-2.2020 create mode 100755 sys/lib/hints/macOS.2020 create mode 100644 sys/lib/hints/wasm create mode 100644 sys/lib/libnethackmain.c create mode 100755 sys/lib/mkmkfile.sh create mode 100755 sys/lib/setup.sh create mode 100644 sys/lib/sysconf diff --git a/.gitignore b/.gitignore index 5c64716a3..b9196f2d1 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ Release/ binary/ build/ ipch/ -lib/ +./lib/ Nethack.sln Nethack.sdf Nethack.opensdf @@ -81,3 +81,10 @@ win/share/monthin.txt win/share/objthin.txt win/share/oththin.txt # end of ms-dos + +#libnethack +src/nethack.data +src/nethack.wasm +src/nethack.js +src/wasm-data +src/libnethack.a diff --git a/include/global.h b/include/global.h index 20939108d..8f7ed95a4 100644 --- a/include/global.h +++ b/include/global.h @@ -325,8 +325,15 @@ struct version_info { unsigned long incarnation; /* actual version number */ unsigned long feature_set; /* bitmask of config settings */ unsigned long entity_count; /* # of monsters and objects */ +#ifndef WASM unsigned long struct_sizes1; /* size of key structs */ unsigned long struct_sizes2; /* size of more key structs */ +#else /* WASM */ + /* 'long' in WASM is 4 bytes, which is too small to hold version numbers + * such as: VERSION_SANITY2 */ + unsigned long long struct_sizes1; /* size of key structs */ + unsigned long long struct_sizes2; /* size of more key structs */ +#endif /* !WASM */ }; struct savefile_info { @@ -390,7 +397,7 @@ struct savefile_info { /* PANICTRACE: Always defined for NH_DEVEL_STATUS != NH_STATUS_RELEASED but only for supported platforms. */ -#ifdef UNIX +#if defined(UNIX) && !defined(WASM) #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) /* see end.c */ #ifndef PANICTRACE @@ -405,7 +412,7 @@ struct savefile_info { #if defined(MACOSX) #define PANICTRACE_LIBC #endif -#ifdef UNIX +#if defined(UNIX) && !defined(WASM) /* no popen in WASM */ #define PANICTRACE_GDB #endif diff --git a/sys/lib/Makefile.dat b/sys/lib/Makefile.dat new file mode 100644 index 000000000..a264242ae --- /dev/null +++ b/sys/lib/Makefile.dat @@ -0,0 +1,136 @@ +# NetHack Datafiles Makefile.dat $NHDT-Date: 1596486993 2020/08/03 20:36:33 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ +# Copyright (c) 2018 by Pasi Kallinen +# NetHack may be freely redistributed. See license for details. + +# Root of source tree: +NHSROOT=.. + +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP +# UUDECODE=uudecode + +VARDAT = bogusmon data engrave epitaph rumors oracles options + +all: $(VARDAT) spec_levs quest_levs + +../util/makedefs: + (cd ../util ; $(MAKE) makedefs) + +../util/tile2x11: + (cd ../util ; $(MAKE) tile2x11) + +../util/tile2beos: + (cd ../util ; $(MAKE) tile2beos) + +../util/tile2bmp: + (cd ../util ; $(MAKE) tile2bmp) + +x11tiles: ../util/tile2x11 ../win/share/monsters.txt ../win/share/objects.txt \ + ../win/share/other.txt \ + ../win/share/monsters.txt + ../util/tile2x11 ../win/share/monsters.txt ../win/share/objects.txt \ + ../win/share/other.txt \ + -grayscale ../win/share/monsters.txt + +beostiles: ../util/tile2beos ../win/share/monsters.txt \ + ../win/share/objects.txt \ + ../win/share/other.txt + ../util/tile2beos ../win/share/monsters.txt \ + ../win/share/objects.txt \ + ../win/share/other.txt + +nhtiles.bmp: ../util/tile2bmp ../win/share/monsters.txt \ + ../win/share/objects.txt \ + ../win/share/other.txt + ../util/tile2bmp $@ + +NetHack.ad: ../win/X11/NetHack.ad +# handle "#define foo bar" -lines + grep ^#define ../win/X11/NetHack.ad | \ + sed -e 's/^#define/s/g' -e 's/ */ /g' \ + -e 's/$$/ g/g' > NetHack.ad.tmp + grep -v ^#define ../win/X11/NetHack.ad | \ + sed -f NetHack.ad.tmp > NetHack.ad + -rm -f NetHack.ad.tmp + +pet_mark.xbm: ../win/X11/pet_mark.xbm + cp ../win/X11/pet_mark.xbm pet_mark.xbm + +pilemark.xbm: ../win/X11/pilemark.xbm + cp ../win/X11/pilemark.xbm pilemark.xbm + +rip.xpm: ../win/X11/rip.xpm + cp ../win/X11/rip.xpm rip.xpm + +mapbg.xpm: ../win/gnome/mapbg.xpm + cp ../win/gnome/mapbg.xpm mapbg.xpm + +nhsplash.xpm: ../win/share/nhsplash.xpm + cp ../win/share/nhsplash.xpm nhsplash.xpm + +nethack.icns: ../win/share/nhicns.uu + $(UUDECODE) ../win/share/nhicns.uu + +Info.plist: ../win/Qt/Info.pli + cp ../win/Qt/Info.pli Info.plist + +../util/tile2img.ttp: + (cd ../util ; $(MAKE) tile2img.ttp) + +../util/xpm2img.ttp: + (cd ../util ; $(MAKE) xpm2img.ttp) +nh16.img: ../util/tile2img.ttp ../win/share/monsters.txt \ + ../win/share/objects.txt ../win/share/other.txt + ../util/tile2img.ttp nh16.img + +rip.img: ../util/xpm2img.ttp + ../util/xpm2img.ttp ../win/X11/rip.xpm rip.img +title.img: + # cp ../win/gem/title.img title.img + $(UUDECODE) ../win/gem/title.uu + +GEM_RSC.RSC: + # cp ../win/gem/GEM_RSC.RSC GEM_RSC.RSC + $(UUDECODE) ../win/gem/gem_rsc.uu + + +data: data.base ../util/makedefs + ../util/makedefs -d + +rumors: rumors.tru rumors.fal ../util/makedefs + ../util/makedefs -r + +oracles: oracles.txt ../util/makedefs + ../util/makedefs -h + +engrave: engrave.txt ../util/makedefs + ../util/makedefs -s + +epitaph: epitaph.txt ../util/makedefs + ../util/makedefs -s + +bogusmon: bogusmon.txt ../util/makedefs + ../util/makedefs -s + +# note: 'options' should have already been made when include/date.h was created +options: ../util/makedefs + ../util/makedefs -v + +# these don't actually do anything useful now that levcomp and dngcomp are gone +spec_levs: + touch spec_levs +quest_levs: + touch quest_levs + +# gitinfo.txt is optionally made by src/Makefile when creating date.h +# spec_levs and quest_levs are empty marker files to control 'make' actions +clean: + -rm -f spec_levs quest_levs gitinfo.txt + +spotless: clean + -rm -f nhdat $(VARDAT) \ + x11tiles pet_mark.xbm pilemark.xbm rip.xpm mapbg.xpm \ + rip.img GEM_RSC.RSC title.img nh16.img NetHack.ad \ + nhsplash.xpm nhtiles.bmp beostiles + +#eof# diff --git a/sys/lib/Makefile.src b/sys/lib/Makefile.src new file mode 100644 index 000000000..4a947a0c5 --- /dev/null +++ b/sys/lib/Makefile.src @@ -0,0 +1,1160 @@ +# NetHack Makefile. +# NetHack 3.7 Makefile.src $NHDT-Date: 1597704252 2020/08/17 22:44:12 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.107 $ +# Copyright (c) 2018 by Pasi Kallinen +# NetHack may be freely redistributed. See license for details. + +# Root of source tree: +NHSROOT=.. + +# newer makes predefine $(MAKE) to 'make' and do smarter processing of +# recursive make calls if $(MAKE) is used +# these makes allow $(MAKE) to be overridden by the environment if someone +# wants to (or has to) use something other than the standard make, so we do +# not want to unconditionally set $(MAKE) here +# +# unfortunately, some older makes do not predefine $(MAKE); if you have one of +# these, uncomment the following line +# (you will know that you have one if you get complaints about being unable +# to find 'makedefs') +# MAKE = make + +# This makefile replaces the previous Makefile.unix, Makefile.xenix, +# Makefile.3B2, Makefile.att, and Makefile.tos. +# Set SYSTEM to one of: +# 'Sysunix' -- generic UNIX +# 'Sys3B2' -- AT&T 3B2, 3B5, etc. +# 'Sysatt' -- AT&T UNIXPC, 7300, 3B1 +# 'SysV-AT' -- Microport 286 UNIX (put -DDUMB in CFLAGS) +# 'Systos' -- Atari +# 'SysBe' -- BeOS +SYSTEM = Sysunix + +# +# Make sure that your bourne shell is specified here, as you have to spawn +# some of the commands (eg. depend) in bourne shell for them to work. +# +# For Systos users compiling on the ST, you'll either need a bourne shell +# clone or you'll need to do make depend, etc. by hand. In either case, +# the line below probably needs changing +SHELL=/bin/sh +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP + +# Usually, the C compiler driver is used for linking: +#LINK=$(CC) + +# Pick the SYSSRC and SYSOBJ lines corresponding to your desired operating +# system. +# +# for libnethack +SYSSRC = ../sys/lib/libnethackmain.c \ + ../sys/share/ioctl.c ../sys/share/unixtty.c ../sys/unix/unixunix.c ../sys/unix/unixres.c +SYSOBJ = libnethackmain.o \ + ioctl.o unixtty.o unixunix.o unixres.o +#SYSSRC = ../sys/lib/libnethackmain.c +#SYSOBJ = libnethackmain.o + +# +# for Systos +# SYSSRC = ../sys/atari/tos.c ../sys/share/pcmain.c ../sys/share/pcsys.c \ +# ../sys/share/pctty.c ../sys/share/pcunix.c +# SYSOBJ = tos.o pcmain.o pcsys.o pctty.o pcunix.o +# +# for BeOS +#SYSSRC = ../sys/be/bemain.c ../sys/share/unixtty.c ../sys/share/ioctl.c +#SYSOBJ = bemain.o unixtty.o ioctl.o + + +# if you are using gcc as your compiler: +# uncomment the CC definition below if it's not in your environment +# if you get setcgtty() warnings during execution, you are feeding gcc +# a non-ANSI -- either run fixincludes on it or use +# -traditional in CFLAGS +# CC = gcc +# +# For Bull DPX/2 systems at B.O.S. 2.0 or higher use the following: +# +# CC = gcc -ansi -D_BULL_SOURCE -D_XOPEN_SOURCE -D_POSIX_SOURCE +# +# If you are using GCC 2.2.2 or higher on a DPX/2, just use: +# +# CC = gcc -ansi +# +# For HP/UX 10.20 with GCC: +# CC = gcc -D_POSIX_SOURCE +# +# For cross-compiling, eg. with gcc on Linux (see also CXX further down): +# CC = arm-linux-gcc +# +# +# if you're debugging and want gcc to check as much as possible, use: +# CC = gcc -W -Wimplicit -Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wcast-qual -Wwrite-strings -DGCC_WARN + +# flags may have to be changed as required +# flags for 286 Xenix: +# CFLAGS = -Ml2t16 -O -LARGE -I../include +# LFLAGS = -Ml -F 4000 -SEG 512 + +# flags for 286 Microport SysV-AT +# CFLAGS = -DDUMB -Ml -I../include +# LFLAGS = -Ml + +# flags for Atari gcc (3.2.1) +# CFLAGS = -O -I../include +# LFLAGS = -s +# flags for Atari gcc (3.3) +# CFLAGS = -mshort -O2 -fomit-frame-pointer -I../include +# LFLAGS = -mshort -s + +# flags for AIX 3.1 cc on IBM RS/6000 to define +# a suitable subset of standard libraries +# (note that there is more info regarding the "-qchars=signed" +# switch in file Install.unx note 8) +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -O -I../include -qchars=signed +# +# Some of our subroutines are complex enough that this is required for full +# optimization under AIX 3.2 (I don't know about 3.1). +# +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -D_ALL_SOURCE -O -I../include -qchars=signed -qmaxmem=5000 + +# flags for A/UX 2.01 using native cc or c89 +# gcc predefines AUX so that's not needed there +# Remember to use -lcurses for WINLIB below ! +# CFLAGS = -ZS -D_POSIX_SOURCE -O -I../include -DAUX + +# flags for IRIX 4.0.x using native cc +# The include files are __STDC__, but have bugs involving const +# CFLAGS = -O -I../include -D__STDC__ -Dconst= -woff 100,293 +# LFLAGS = -s + +# flags for BSD/OS 2.0 +# CFLAGS = -O -I../include -I/usr/X11/include +# LFLAGS = -L/usr/X11/lib + +# flags for Linux +# compile normally +# CFLAGS = -O2 -fomit-frame-pointer -I../include +# LFLAGS = -L/usr/X11R6/lib +# OR compile backwards compatible a.out format +# CFLAGS = -O2 -b i486-linuxaout -fomit-frame-pointer -I../include +# LFLAGS = -b i486-linuxaout -L/usr/X11R6/lib + +# flags for BeOS +# on a Mac/BeBox: +#CC = mwcc +#CFLAGS = -r -I../include +#LINK = mwld +#LFLAGS = -map nethack.xMAP +# on Intel: +#CFLAGS = -O -I../include +#LINK = gcc +#LFLAGS = -Xlinker -soname=_APP_ + +# Compile with PDCurses installed in a separate directory that doesn't +# conflict with the system curses/ncurses library +#CFLAGS = -O -I../include -I/usr/local/include/pdcurses +# Same as above, but for XCurses +#CFLAGS = -O -DXCURSES -I../include -I/usr/local/include/pdcurses +# Compile against system curses library, such as ncurses +#CFLAGS = -O -I../include + +# files in ../win/X11 (relative to src) are passed $(CFLAGS) $(X11CFLAGS) +# and by default will find in /usr/include/X11/foo.h; +# can be overridden via hints; post-10.7 OSX with XQuartz uses +# X11CFLAGS=-I/opt/X11/include to find in /opt/X11/include/X11/foo.h +#X11CFLAGS= + +# Only used for the Gnome interface. +# When including the Gnome interface, you need to include gnome specific +# directories. The ones given below is the usual spot for linux systems. +# The paths are for glibconfig.h and gnomesupport.h respectively. +# +#GNOMEINC=-I/usr/lib/glib/include -I/usr/lib/gnome-libs/include -I../win/gnome + +# flags for debugging: +# CFLAGS = -g -I../include + +#CFLAGS = -O -I../include +#LFLAGS = + +# The Qt and Be window systems are written in C++, while the rest of +# NetHack is standard C. If using Qt, uncomment the LINK line here to get +# the C++ libraries linked in. +CXXFLAGS = $(CFLAGS) -I. -I$(QTDIR)/include $(QTCXXFLAGS) +CXX ?= g++ +MOC ?= moc +#LINK=g++ +# For cross-compiling, eg. with gcc on Linux (see also CC further up): +#CXX=arm-linux-g++ +#LINK=arm-linux-gcc + +# we specify C preprocessor flags via CFLAGS; files built with default rules +# might include $(CPPFLAGS) which could get a value from user's environment; +# we avoid that by forcing it empty rather than by overriding default rules +CPPFLAGS = + +# if requested, setup cross-compiler for WASM +ifdef WANT_WASM +CC=$(EMCC) +AR=$(EMAR) +RANLIB=$(EMRANLIB) +CFLAGS+=$(EMCC_CFLAGS) +SYSCFLAGS=$(EMCC_CFLAGS) +endif # WANT_WASM + +# include path for nethack headers +CFLAGS+=-I../include +# compile with library options +CFLAGS+=-DLIBNH +# use "shim" windowing system +CFLAGS+=-DNOTTYGRAPHICS -DSHIM_GRAPHICS -DDEFAULT_WINDOW_SYS=\"shim\" + +# file for regular expression matching +REGEXOBJ = posixregex.o +#REGEXOBJ = pmatchregex.o +#REGEXOBJ = cppregex.o + +# Set the WINSRC, WINOBJ, and WINLIB lines to correspond to your desired +# combination of windowing systems. Also set windowing systems in config.h. +# Note that if you are including multiple tiled window systems, you don't +# want two copies of tile.o, so comment out all but the first. +# +# files for a straight tty port using no native windowing system +WINTTYSRC = ../win/tty/getline.c ../win/tty/termcap.c ../win/tty/topl.c \ + ../win/tty/wintty.c +WINTTYOBJ = getline.o termcap.o topl.o wintty.o +# +# Files for curses interface +WINCURSESSRC = ../win/curses/cursmain.c ../win/curses/curswins.c \ + ../win/curses/cursmisc.c ../win/curses/cursdial.c \ + ../win/curses/cursstat.c ../win/curses/cursinit.c \ + ../win/curses/cursmesg.c ../win/curses/cursinvt.c +WINCURSESOBJ = cursmain.o curswins.o cursmisc.o cursdial.o cursstat.o \ + cursinit.o cursmesg.o cursinvt.o +# +# files for an X11 port +# (tile.c is a generated source file) +WINX11SRC = ../win/X11/Window.c ../win/X11/dialogs.c ../win/X11/winX.c \ + ../win/X11/winmap.c ../win/X11/winmenu.c ../win/X11/winmesg.c \ + ../win/X11/winmisc.c ../win/X11/winstat.c ../win/X11/wintext.c \ + ../win/X11/winval.c tile.c +WINX11OBJ = Window.o dialogs.o winX.o winmap.o winmenu.o winmesg.o \ + winmisc.o winstat.o wintext.o winval.o tile.o +# +# Files for a Qt 3 port (renamed since nethack 3.6.x) +# +#WINQT3SRC = ../win/Qt3/qt3_win.cpp ../win/Qt3/qt3_clust.cpp \ +# ../win/Qt3/qt3tableview.cpp +#WINQT3OBJ = qt3_win.o qt3_clust.o qt3tableview.o tile.o +# empty values for 'make depend' +WINQT3SRC = +WINQT3OBJ = + +# +# Files for a Qt 4 or 5 port +# +WINQTSRC = ../win/Qt/qt_bind.cpp ../win/Qt/qt_click.cpp \ + ../win/Qt/qt_clust.cpp ../win/Qt/qt_delay.cpp \ + ../win/Qt/qt_glyph.cpp ../win/Qt/qt_icon.cpp ../win/Qt/qt_inv.cpp \ + ../win/Qt/qt_key.cpp ../win/Qt/qt_line.cpp ../win/Qt/qt_main.cpp \ + ../win/Qt/qt_map.cpp ../win/Qt/qt_menu.cpp ../win/Qt/qt_msg.cpp \ + ../win/Qt/qt_plsel.cpp ../win/Qt/qt_rip.cpp ../win/Qt/qt_set.cpp \ + ../win/Qt/qt_stat.cpp ../win/Qt/qt_str.cpp ../win/Qt/qt_streq.cpp \ + ../win/Qt/qt_svsel.cpp ../win/Qt/qt_win.cpp ../win/Qt/qt_xcmd.cpp \ + ../win/Qt/qt_yndlg.cpp +WINQTOBJ = qt_bind.o qt_click.o qt_clust.o qt_delay.o qt_glyph.o qt_icon.o \ + qt_inv.o qt_key.o qt_line.o qt_main.o qt_map.o qt_menu.o qt_msg.o \ + qt_plsel.o qt_rip.o qt_set.o qt_stat.o qt_str.o qt_streq.o qt_svsel.o \ + qt_win.o qt_xcmd.o qt_yndlg.o tile.o +# +# Files for a Gnome port +# +#WINGNOMESRC = ../win/gnome/gnaskstr.c ../win/gnome/gnbind.c \ +# ../win/gnome/gnglyph.c ../win/gnome/gnmain.c ../win/gnome/gnmap.c \ +# ../win/gnome/gnmenu.c ../win/gnome/gnmesg.c ../win/gnome/gnopts.c \ +# ../win/gnome/gnplayer.c ../win/gnome/gnsignal.c \ +# ../win/gnome/gnstatus.c ../win/gnome/gntext.c ../win/gnome/gnyesno.c \ +# ../win/gnome/gnworn.c +#WINGNOMEOBJ = gnaskstr.o gnbind.o gnglyph.o gnmain.o gnmap.o gnmenu.o \ +# gnmesg.o gnopts.o gnplayer.o gnsignal.o gnstatus.o gntext.o \ +# gnyesno.o gnworn.o tile.o +# empty values for 'make depend' +WINGNOMESRC = +WINGNOMEOBJ = + +# +# Files for a Gem port +#WINGEMSRC = ../win/gem/wingem.c ../win/gem/wingem1.c ../win/gem/load_img.c \ +# ../win/gem/gr_rect.c tile.c +#WINGEMOBJ = wingem.o wingem1.o load_img.o gr_rect.o tile.o +# empty values for 'make depend' +WINGEMSRC = +WINGEMOBJ = + +# +# Files for Shim windowing interface for libnethack -- doesn't do anything, just passes along the API calls to the library +# +WINSHIMSRC = ../win/shim/winshim.c +WINSHIMOBJ = winshim.o + +# +# Files for a BeOS InterfaceKit port -- not ready for prime time +WINBESRC = +WINBEOBJ = +#WINBESRC = ../win/BeOS/winbe.cpp ../win/BeOS/NHWindow.cpp \ +# ../win/BeOS/NHMenuWindow.cpp ../win/BeOS/NHMapWindow.cpp tile.c +#WINBEOBJ = winbe.o NHWindow.o NHMenuWindow.o NHMapWindow.o tile.o +# +# +#WINSRC = $(WINTTYSRC) +#WINOBJ = $(WINTTYOBJ) +# +# Curses - Karl Garrison, Tangles +#WINSRC = $(WINCURSESSRC) +#WINOBJ = $(WINCURSESOBJ) +# +# on some systems the termcap library is in -ltermcap or -lcurses +# on 386 Xenix, the -ltermlib tputs() seems not to work; use -lcurses instead +# Sysatt uses shared library in lieu of this option +# Systos needs -lcurses16 if you use -mshort +# AIX 3.1 on RS/6000 likes -lcurses if TERMINFO defined in unixconf.h +# and -ltermcap otherwise +# Linux uses -lncurses (newer) or -ltermcap (older) +# Be uses -ltermcap +# +# libraries for tty ports +# WINTTYLIB = -ltermcap +# WINTTYLIB = -lcurses +# WINTTYLIB = -lcurses16 +# WINTTYLIB = -lncurses +#WINTTYLIB = -ltermlib +# +# libraries for X11 +# If USE_XPM is defined in config.h, you will also need -lXpm here. +#WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 +# WINX11LIB = -lXaw -lXmu -lXt -lX11 +# WINX11LIB = -lXaw -lXmu -lXext -lXt -lXpm -lX11 -lm +# WINX11LIB = -lXaw -lXmu -lXpm -lXext -lXt -lX11 -lSM -lICE -lm # BSD/OS 2.0 +# +# +# libraries for Qt 3 +WINQT3LIB = -L$(QTDIR)/lib -lqt +# +# libraries for Qt 4 +WINQT4LIB = `pkg-config QtGui --libs` +# +# libraries for Qt 5 (use with WINQTSRC and WINQTOBJ) +WINQT5LIB = `pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --libs` +# +# libraries for KDE (with Qt) +WINKDELIB = -lkdecore -lkdeui -lXext +# +# libraries for Gnome +WINGNOMELIB = -lgnomeui -lgnome -lart_lgpl -lgtk -lgdk -lpopt +# +# libraries for Gem port +WINGEMLIB = -le_gem -lgem +# +# libraries for BeOS +WINBELIB = -lbe +# +# libraries for curses port +# link with ncurses +WINCURSESLIB = -lncurses +# link with pdcurses for SDL, installed in a separate directory +#WINCURSESLIB = -L/usr/local/lib/pdcurses -lpdcurses -lSDL +# same as above, for XCurses +#WINCURSESLIB = -L/usr/local/lib/pdcurses -lXCurses -lXawM -lXmu -lXext -lXt -lX11 +# +#WINLIB = $(WINTTYLIB) +# +# For Curses +#WINLIB = $(WINCURSESLIB) +# +# any other strange libraries your system needs (for Sysunix only -- the more +# specialized targets should already be right) +# +# on HP-UX 8.x, the malloc(3x) routines in libmalloc.a seem to align things +# better than the malloc(3) ones in libc.a +# LIBS = -lmalloc +# +# DPX/2's also use the malloc(3x) routines. In addition, if you are building +# for X11, you must include libinet.a. +# LIBS = -lmalloc -linet +# +# Linux NetHack uses some bsd style ioctl functions, thus it is necessary to +# use the bsd libs. (Only if still compiling as BSD in unixconf.h; recent +# versions compile fine using SYSV without this.) +# LIBS = -lbsd +# +# for CYGWIN32 aka cygwin 1.1.1 +# LIBS = -lcygwin +# +# Solaris 2.x seems to work with the following +# LIBS = -lsocket -lnsl +# +# IRIX 4.0.x needs -lsun if NIS (YP) is being used for passwd file lookup +# LIBS = -lsun +# +# If ZLIB_COMP is defined in config.h this is necessary to link with zlib. +# LIBS = -lz +# +# LIBS = + +# make libnethack +LIBNH_TARGET=libnethack.a +WASM_TARGET=nethack.js + +ifdef WANT_WASM +MAIN_TARGET=$(WASM_TARGET) +else +MAIN_TARGET=$(LIBNH_TARGET) +endif + +# if you defined RANDOM in unixconf.h since your system did not come +# with a reasonable random number generator +# RANDOBJ = random.o +RANDOBJ = + + +# used by `make depend' to reconstruct this Makefile; you shouldn't need this +AWK = nawk + +# when using 'makedefs -v', also force dat/gitinfo.txt to be up to date; +# changing this to 0 will change the behavior to only make that file if +# it doesn't already exist; to skip it completely, create an empty file +# of that name and also set this to 0; there shouldn't be any need to +# skip it--if nethack's sources don't reside in a git repository than +# the script which creates that file will fail benignly and 'makedefs -v' +# will proceed without it +GITINFO=1 + +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 + +# ---------------------------------------- +# +# Nothing below this line should have to be changed. +# +# Other things that have to be reconfigured are in config.h, +# {unixconf.h, pcconf.h}, and possibly system.h + +# Verbosity definitions, begin +# Set QUIETCC=1 above to output less feedback while building. +# CC and CXX obey verbosity, LD and LINK don't. +# AT is @ when not verbose, empty otherwise +ACTUAL_CC := $(CC) +ACTUAL_CXX := $(CXX) +ACTUAL_LD := $(LD) +ACTUAL_LINK := $(LINK) + +CC_V0 = $(ACTUAL_CC) +CC_V = $(CC_V0) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) + +CXX_V0 = $(ACTUAL_CXX) +CXX_V = $(CXX_V0) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) + +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) +LD = $(ACTUAL_LD) +LINK = $(ACTUAL_LINK) + +AT_V0 := +AT_V := $(AT_V0) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) +# Verbosity, end + +MAKEDEFS = ../util/makedefs + +# -lm required by lua +LUALIB = ../lib/lua/liblua.a -lm + +# timestamp files to reduce `make' overhead and shorten .o dependency lists +CONFIG_H = ../src/config.h-t +HACK_H = ../src/hack.h-t + +# all .c that are part of the main NetHack program and are not operating- or +# windowing-system specific +HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ + botl.c cmd.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c \ + do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c drawing.c \ + dungeon.c eat.c end.c engrave.c exper.c explode.c extralev.c \ + files.c fountain.c hack.c hacklib.c \ + insight.c invent.c isaac64.c light.c \ + lock.c mail.c makemon.c mapglyph.c mcastu.c mdlib.c mhitm.c \ + mhitu.c minion.c mklev.c mkmap.c mkmaze.c mkobj.c mkroom.c mon.c \ + mondata.c monmove.c monst.c mplayer.c mthrowu.c muse.c music.c \ + nhlua.c nhlsel.c nhlobj.c o_init.c objects.c objnam.c \ + options.c pager.c pickup.c pline.c polyself.c potion.c pray.c \ + priest.c quest.c questpgr.c read.c rect.c region.c restore.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 \ + windows.c wizard.c worm.c worn.c write.c zap.c + +# all operating-system-dependent .c (for dependencies and such) +SYSCSRC = ../sys/share/pcmain.c ../sys/share/pcsys.c \ + ../sys/share/pctty.c ../sys/share/pcunix.c \ + ../sys/share/pmatchregex.c ../sys/share/posixregex.c \ + ../sys/share/random.c \ + ../sys/share/ioctl.c ../sys/share/unixtty.c ../sys/unix/libnethackmain.c \ + ../sys/unix/unixunix.c ../sys/unix/unixres.c +SYSCXXSRC = ../sys/share/cppregex.cpp + +# generated source files (tile.c is handled separately via WINxxxSRC) +GENCSRC = vis_tab.c #tile.c + +# all windowing-system-dependent .c (for dependencies and such) +WINCSRC = $(WINTTYSRC) $(WINCURSESSRC) $(WINX11SRC) $(WINGNOMESRC) $(WINGEMSRC) $(WINSHIMSRC) +# all windowing-system-dependent .cpp (for dependencies and such) +WINCXXSRC = $(WINQTSRC) $(WINQT3SRC) $(WINBESRC) + +# Files for window system chaining. Requires SYSCF; include via HINTSRC/HINTOBJ +CHAINSRC = ../win/chain/wc_chainin.c ../win/chain/wc_chainout.c \ + ../win/chain/wc_trace.c +CHAINOBJ = wc_chainin.o wc_chainout.o wc_trace.o + +# .c files for this version (for date.h) +VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(CHAINSRC) $(GENCSRC) + +WINSRC=$(WINSHIMSRC) +WINOBJ=$(WINSHIMOBJ) +# .c files for all versions using this Makefile (for lint and tags) +CSOURCES = $(HACKCSRC) $(SYSCSRC) $(WINCSRC) $(CHAINSRC) $(GENCSRC) + + +# all .h files except date.h, onames.h, pm.h, and vis_tab.h which would +# cause dependency loops if run through "make depend" +# and dgn_file.h, special level & dungeon files. +# +HACKINCL = align.h artifact.h artilist.h attrib.h botl.h \ + color.h config.h config1.h context.h coord.h decl.h \ + display.h dlb.h dungeon.h engrave.h extern.h flag.h fnamesiz.h \ + func_tab.h global.h hack.h lint.h mextra.h mfndpos.h \ + micro.h mkroom.h \ + monattk.h mondata.h monflag.h monst.h monsym.h obj.h objclass.h \ + optlist.h patchlevel.h pcconf.h permonst.h prop.h rect.h \ + region.h rm.h sp_lev.h spell.h sys.h system.h tcap.h timeout.h \ + tradstdc.h trampoli.h trap.h unixconf.h vision.h vmsconf.h \ + wintty.h wincurs.h winX.h winprocs.h wintype.h you.h youprop.h + +HSOURCES = $(HACKINCL) date.h onames.h pm.h vis_tab.h dgn_file.h + +# the following .o's _must_ be made before any others (for makedefs) +FIRSTOBJ = monst.o objects.o + +HOBJ = $(FIRSTOBJ) allmain.o alloc.o apply.o artifact.o attrib.o ball.o \ + bones.o botl.o cmd.o dbridge.o decl.o detect.o dig.o display.o dlb.o \ + do.o do_name.o do_wear.o dog.o dogmove.o dokick.o dothrow.o \ + drawing.o dungeon.o eat.o end.o engrave.o exper.o explode.o \ + extralev.o files.o fountain.o hack.o hacklib.o \ + insight.o invent.o isaac64.o \ + light.o lock.o mail.o makemon.o mapglyph.o mcastu.o mdlib.o mhitm.o \ + mhitu.o minion.o mklev.o mkmap.o mkmaze.o mkobj.o mkroom.o mon.o \ + mondata.o monmove.o mplayer.o mthrowu.o muse.o music.o \ + nhlua.o nhlsel.o nhlobj.o o_init.o objnam.o options.o \ + pager.o pickup.o pline.o polyself.o potion.o pray.o priest.o \ + quest.o questpgr.o read.o rect.o region.o restore.o rip.o rnd.o \ + role.o rumors.o save.o sfstruct.o \ + shk.o shknam.o sit.o sounds.o sp_lev.o spell.o symbols.o sys.o \ + steal.o steed.o teleport.o timeout.o topten.o track.o trap.o u_init.o \ + uhitm.o vault.o vision.o vis_tab.o weapon.o were.o wield.o windows.o \ + wizard.o worm.o worn.o write.o zap.o \ + $(REGEXOBJ) $(RANDOBJ) $(SYSOBJ) $(WINOBJ) $(HINTOBJ) version.o +# the .o files from the HACKCSRC, SYSSRC, and WINSRC lists + +# first target is also the default target for 'make' without any arguments +all: $(MAIN_TARGET) + @echo "Done building main target:" $(MAIN_TARGET) + @echo "" + +libnh: $(LIBNH_TARGET) + @echo "libnethack done." + +wasm: $(WASM_TARGET) + @echo "nethack.js done." + +$(LIBNH_TARGET): $(HOBJ) Makefile + -rm $@ + $(AR) $(LIBNH_TARGET) $(HOBJ) ../lib/lua/liblua.a + +$(WASM_TARGET): $(HOBJ) Makefile $(WASM_DATA_DIR) + -rm $@ + $(EMCC) $(EMCC_LFLAGS) $(EMCC_CFLAGS) -o $(WASM_TARGET) $(HOBJ) ../lib/lua/liblua.a + +$(WASM_DATA_DIR): + -mkdir -p $(WASM_DATA_DIR) + touch $(WASM_DATA_DIR)/perm + touch $(WASM_DATA_DIR)/record + touch $(WASM_DATA_DIR)/logfile + touch $(WASM_DATA_DIR)/xlogfile + ( cd ..; $(MAKE) dofiles-dlb ) + cp ../sys/lib/sysconf $(WASM_DATA_DIR)/sysconf + +Sysunix: $(HOBJ) Makefile + @echo "Linking $(GAME)." + $(AT)$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LIBS) $(LUALIB) + @touch Sysunix + +Sys3B2: $(HOBJ) Makefile + @echo "Linking $(GAME)." + $(AT)$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LUALIB) -lmalloc + @touch Sys3B2 + +Sysatt: $(HOBJ) Makefile + @echo "Loading $(GAME)." + $(AT)$(LD) $(LFLAGS) /lib/crt0s.o /lib/shlib.ifile -o $(GAME) $(HOBJ) \ + $(LUALIB) + @touch Sysatt + +Systos: $(HOBJ) Makefile + @echo "Linking $(GAME)." + $(AT)$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LUALIB) + @touch Systos + +SysV-AT: DUMB.Setup $(HOBJ) Makefile + @echo "Linking $(GAME)." + $(AT)$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LUALIB) + @touch SysV-AT + +SysBe: $(HOBJ) Makefile + @echo "Linking $(GAME)." + $(AT)$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LIBS) $(LUALIB) + @xres -o $(GAME) ../win/BeOS/nethack.rsrc + @mimeset -f $(GAME) + @touch SysBe + +DUMB.Setup: ../include/extern.h + cp ../include/extern.h ../include/extern.h.BAK + cat ../include/extern.h | \ + sed -e '/^E\ int\ /!b' \ + -e '/[^;/ ]$$/N' \ + -e '/[(][*]occupation[)]/b' \ + -e '/[(][*]afternmv[)]/b' \ + -e '/float_down/b' \ + -e '/done1/b' \ + -e '/identify/b' \ + -e '/Hear_again/b' \ + -e '/hangup/b' \ + -e 's/^\(.*\)$$/\/\* \1 \/\*\*\//' | \ + sed -e '/^E\ void\ /!b' \ + -e '/[^;/ ]$$/N' \ + -e 's/^\(.*\)$$/\/\* \1 \/\*\*\//' \ + >../include/extern.DUMB + cp ../include/extern.DUMB ../include/extern.h + @touch DUMB.Setup + +../lib/lua/liblua.a ../include/nhlua.h: + @( cd .. ; $(MAKE) lua_support ) + +# dependencies for makedefs and its outputs, which the util +# Makefile is responsible for keeping up to date +# + +# special rules, to force update of makedefs, real dependencies should be +# below in the 'make depend' output. +monst.o: + $(CC) $(CFLAGS) -c monst.c + @rm -f $(MAKEDEFS) + +objects.o: + $(CC) $(CFLAGS) -c objects.c + @rm -f $(MAKEDEFS) + +# Qt 3 windowport meta-object-compiler output +qt3_kde0.moc: ../win/Qt3/qt3_kde0.h + $(QTDIR)/bin/moc -o qt3kde0.moc ../win/Qt3/qt3_kde0.h +qt3_win.moc: ../win/Qt3/qt3_win.h + $(QTDIR)/bin/moc -o qt3win.moc ../win/Qt3/qt3_win.h +qt3tableview.moc: ../win/Qt3/qt3tableview.h + $(QTDIR)/bin/moc -o qt3tableview.moc ../win/Qt/qt3tableview.h + +# Qt 4 windowport meta-object-compiler output +qt_kde0.moc : ../win/Qt/qt_kde0.h + $(QTDIR)/bin/$(MOC) -o qt_kde0.moc ../win/Qt/qt_kde0.h +qt_main.moc : ../win/Qt/qt_main.h + $(QTDIR)/bin/$(MOC) -o qt_main.moc ../win/Qt/qt_main.h +qt_map.moc : ../win/Qt/qt_map.h + $(QTDIR)/bin/$(MOC) -o qt_map.moc ../win/Qt/qt_map.h +qt_menu.moc : ../win/Qt/qt_menu.h + $(QTDIR)/bin/$(MOC) -o qt_menu.moc ../win/Qt/qt_menu.h +qt_msg.moc : ../win/Qt/qt_msg.h + $(QTDIR)/bin/$(MOC) -o qt_msg.moc ../win/Qt/qt_msg.h +qt_plsel.moc : ../win/Qt/qt_plsel.h + $(QTDIR)/bin/$(MOC) -o qt_plsel.moc ../win/Qt/qt_plsel.h +qt_set.moc : ../win/Qt/qt_set.h + $(QTDIR)/bin/$(MOC) -o qt_set.moc ../win/Qt/qt_set.h +qt_stat.moc : ../win/Qt/qt_stat.h + $(QTDIR)/bin/$(MOC) -o qt_stat.moc ../win/Qt/qt_stat.h +qt_xcmd.moc : ../win/Qt/qt_xcmd.h + $(QTDIR)/bin/$(MOC) -o qt_xcmd.moc ../win/Qt/qt_xcmd.h +qt_yndlg.moc : ../win/Qt/qt_yndlg.h + $(QTDIR)/bin/$(MOC) -o qt_yndlg.moc ../win/Qt/qt_yndlg.h + +# build monst.o and objects.o before executing '$(MAKE) makedefs' +$(MAKEDEFS): $(FIRSTOBJ) \ + ../util/makedefs.c ../src/mdlib.c $(CONFIG_H) \ + ../include/permonst.h \ + ../include/objclass.h ../include/monsym.h \ + ../include/artilist.h ../include/dungeon.h ../include/obj.h \ + ../include/monst.h ../include/you.h ../include/flag.h \ + ../include/dlb.h ../include/patchlevel.h + @( cd ../util ; $(MAKE) makedefs ) + +# Source files created by 'makedefs' at build time. +# Each is given an artificial dependency upon the one before +# so that parallel makes will have to build them sequentially. +# (More for documentation than effect; 'make' should know not +# to try to build $(MAKEDEFS) for bar.h while it is in the +# process of building it for foo.h.) +../include/onames.h: $(MAKEDEFS) + @( cd ../util ; $(MAKE) ../include/onames.h ) +../include/pm.h: $(MAKEDEFS) ../include/onames.h + @( cd ../util ; $(MAKE) ../include/pm.h ) +../include/vis_tab.h: $(MAKEDEFS) ../include/pm.h + @( cd ../util ; $(MAKE) ../include/vis_tab.h ) +# makedefs -z makes both vis_tab.h and vis_tab.c, but writes the .h first +vis_tab.c: ../include/vis_tab.h +# Created at build time for configurations which support tiles, +# but not by makedefs so not connected to the others. +tile.c: ../win/share/tilemap.c $(HACK_H) + @( cd ../util ; $(MAKE) ../src/tile.c ) + +../win/gnome/gn_rip.h: ../win/X11/rip.xpm + cp ../win/X11/rip.xpm ../win/gnome/gn_rip.h + +sfstruct.o: sfstruct.c $(HACK_H) + +# date.h should be remade any time any of the source or include code +# is modified. Unfortunately, this would make the contents of this +# file far more complex. Since "hack.h" depends on most of the include +# files, we kludge around this by making date.h dependent on hack.h, +# even though it doesn't include this file. +# Do NOT include ../dat/gitinfo.txt as either a prerequisite or target. +# 'makedefs -v' processes it when present and ignores it if not. +# +# hack.h depends on makedefs' output, so we know makedefs will be +# up to date before being executed +../include/date.h: $(VERSOURCES) $(HACK_H) + -$(SHELL) ../sys/unix/gitinfo.sh $(GITINFO) #before 'makedefs -v' + ../util/makedefs -v + +lint: +# lint cannot have -p here because (i) capitals are meaningful: +# [Ww]izard, (ii) identifiers may coincide in the first six places: +# doweararm() versus dowearring(). +# _flsbuf comes from , a bug in the system libraries. + @echo lint -axbh -DLINT ... + @lint -axbh -I../include -DLINT $(CSOURCES) | sed '/_flsbuf/d' + + +tags: $(CSOURCES) + @echo ctags -tw ... + @ctags -tw $(CSOURCES) + @( cd ../include ; ctags -tw $(HSOURCES) ) + @( cd ../util ; $(MAKE) tags ) + +clean: + -rm -f *.o $(HACK_H) $(CONFIG_H) $(WASM_TARGET) $(WASM_TARGET:.js=.wasm) $(LIBNH_TARGET) + -rm -rf $(WASM_DATA_DIR) + +spotless: clean + -rm -f a.out core $(GAME) Sys* + -rm -f ../lib/lua/liblua.a ../include/nhlua.h + -rm -f ../include/date.h ../include/onames.h ../include/pm.h + -rm -f ../include/vis_tab.h vis_tab.c tile.c *.moc + -rm -f ../win/gnome/gn_rip.h + + +depend: ../sys/unix/depend.awk \ + $(SYSCSRC) $(WINCSRC) $(SYSCXXSRC) $(WINCXXSRC) \ + $(CHAINSRC) $(GENCSRC) $(HACKCSRC) + $(AWK) -f ../sys/unix/depend.awk ../include/*.h ../win/*/*.h \ + $(SYSCSRC) $(WINCSRC) $(SYSCXXSRC) $(WINCXXSRC) \ + $(CHAINSRC) $(GENCSRC) $(HACKCSRC) >makedep + @echo '/^# DO NOT DELETE THIS LINE OR CHANGE ANYTHING BEYOND IT/+2,$$d' >eddep + @echo '$$r makedep' >>eddep + @echo 'w' >>eddep + @cp Makefile Makefile.bak + ed - Makefile < eddep + @rm -f eddep makedep + @echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile + @echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile + @echo '# see make depend above' >> Makefile + - diff Makefile.bak Makefile + @rm -f Makefile.bak + +# DO NOT DELETE THIS LINE OR CHANGE ANYTHING BEYOND IT + +# config.h timestamp +$(CONFIG_H): ../include/config.h ../include/config1.h ../include/patchlevel.h \ + ../include/tradstdc.h ../include/global.h ../include/coord.h \ + ../include/vmsconf.h ../include/system.h ../include/nhlua.h \ + ../include/unixconf.h ../include/pcconf.h ../include/micro.h \ + ../include/ntconf.h ../include/fnamesiz.h + touch $(CONFIG_H) +# hack.h timestamp +$(HACK_H): ../include/hack.h $(CONFIG_H) ../include/lint.h ../include/align.h \ + ../include/dungeon.h ../include/monsym.h ../include/mkroom.h \ + ../include/objclass.h ../include/youprop.h ../include/prop.h \ + ../include/permonst.h ../include/monattk.h \ + ../include/monflag.h ../include/mondata.h ../include/pm.h \ + ../include/wintype.h ../include/context.h ../include/rm.h \ + ../include/botl.h ../include/rect.h ../include/region.h \ + ../include/decl.h ../include/quest.h ../include/spell.h \ + ../include/color.h ../include/obj.h ../include/engrave.h \ + ../include/you.h ../include/attrib.h ../include/monst.h \ + ../include/mextra.h ../include/skills.h ../include/onames.h \ + ../include/timeout.h ../include/trap.h ../include/flag.h \ + ../include/vision.h ../include/display.h ../include/winprocs.h \ + ../include/sys.h ../include/wintty.h ../include/trampoli.h + touch $(HACK_H) +# +pcmain.o: ../sys/share/pcmain.c $(HACK_H) ../include/dlb.h + $(CC) $(CFLAGS) -c -o $@ ../sys/share/pcmain.c +pcsys.o: ../sys/share/pcsys.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/share/pcsys.c +pctty.o: ../sys/share/pctty.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/share/pctty.c +pcunix.o: ../sys/share/pcunix.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/share/pcunix.c +pmatchregex.o: ../sys/share/pmatchregex.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/share/pmatchregex.c +posixregex.o: ../sys/share/posixregex.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/share/posixregex.c +random.o: ../sys/share/random.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/share/random.c +ioctl.o: ../sys/share/ioctl.c $(HACK_H) ../include/tcap.h + $(CC) $(CFLAGS) -c -o $@ ../sys/share/ioctl.c +unixtty.o: ../sys/share/unixtty.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/share/unixtty.c +libnethackmain.o: ../sys/lib/libnethackmain.c $(HACK_H) ../include/dlb.h + $(CC) $(CFLAGS) -c -o $@ ../sys/lib/libnethackmain.c +unixunix.o: ../sys/unix/unixunix.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/unix/unixunix.c +unixres.o: ../sys/unix/unixres.c $(CONFIG_H) + $(CC) $(CFLAGS) -c -o $@ ../sys/unix/unixres.c +getline.o: ../win/tty/getline.c $(HACK_H) ../include/func_tab.h + $(CC) $(CFLAGS) -c -o $@ ../win/tty/getline.c +termcap.o: ../win/tty/termcap.c $(HACK_H) ../include/tcap.h + $(CC) $(CFLAGS) -c -o $@ ../win/tty/termcap.c +topl.o: ../win/tty/topl.c $(HACK_H) ../include/tcap.h + $(CC) $(CFLAGS) -c -o $@ ../win/tty/topl.c +wintty.o: ../win/tty/wintty.c $(HACK_H) ../include/dlb.h ../include/tcap.h + $(CC) $(CFLAGS) -c -o $@ ../win/tty/wintty.c +cursmain.o: ../win/curses/cursmain.c $(HACK_H) ../include/wincurs.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/cursmain.c +curswins.o: ../win/curses/curswins.c $(HACK_H) ../include/wincurs.h \ + ../win/curses/curswins.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/curswins.c +cursmisc.o: ../win/curses/cursmisc.c $(HACK_H) ../include/wincurs.h \ + ../win/curses/cursmisc.h ../include/func_tab.h ../include/dlb.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/cursmisc.c +cursdial.o: ../win/curses/cursdial.c $(HACK_H) ../include/wincurs.h \ + ../win/curses/cursdial.h ../include/func_tab.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/cursdial.c +cursstat.o: ../win/curses/cursstat.c $(HACK_H) ../include/wincurs.h \ + ../win/curses/cursstat.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/cursstat.c +cursinit.o: ../win/curses/cursinit.c $(HACK_H) ../include/wincurs.h \ + ../win/curses/cursinit.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/cursinit.c +cursmesg.o: ../win/curses/cursmesg.c $(HACK_H) ../include/wincurs.h \ + ../win/curses/cursmesg.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/cursmesg.c +cursinvt.o: ../win/curses/cursinvt.c $(HACK_H) ../include/wincurs.h \ + ../win/curses/cursinvt.h + $(CC) $(CFLAGS) -c -o $@ ../win/curses/cursinvt.c +Window.o: ../win/X11/Window.c ../include/xwindowp.h ../include/xwindow.h \ + $(CONFIG_H) ../include/lint.h ../include/winX.h \ + ../include/color.h ../include/wintype.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/Window.c +dialogs.o: ../win/X11/dialogs.c $(CONFIG_H) ../include/lint.h \ + ../include/winX.h ../include/color.h ../include/wintype.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/dialogs.c +winX.o: ../win/X11/winX.c $(HACK_H) ../include/winX.h ../include/dlb.h \ + ../include/xwindow.h ../win/X11/nh72icon ../win/X11/nh56icon \ + ../win/X11/nh32icon + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winX.c +winmap.o: ../win/X11/winmap.c ../include/xwindow.h $(HACK_H) ../include/dlb.h \ + ../include/winX.h ../include/tile2x11.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmap.c +winmenu.o: ../win/X11/winmenu.c $(HACK_H) ../include/winX.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmenu.c +winmesg.o: ../win/X11/winmesg.c ../include/xwindow.h $(HACK_H) ../include/winX.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmesg.c +winmisc.o: ../win/X11/winmisc.c $(HACK_H) ../include/func_tab.h \ + ../include/winX.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winmisc.c +winshim.o: ../win/shim/winshim.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../win/shim/winshim.c +winstat.o: ../win/X11/winstat.c $(HACK_H) ../include/winX.h ../include/xwindow.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winstat.c +wintext.o: ../win/X11/wintext.c $(HACK_H) ../include/winX.h ../include/xwindow.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/wintext.c +winval.o: ../win/X11/winval.c $(HACK_H) ../include/winX.h + $(CC) $(CFLAGS) $(X11CFLAGS) -c -o $@ ../win/X11/winval.c +tile.o: tile.c $(HACK_H) +cppregex.o: ../sys/share/cppregex.cpp + $(CXX) $(CXXFLAGS) -c -o $@ ../sys/share/cppregex.cpp +qt_bind.o: ../win/Qt/qt_bind.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_kde0.h ../win/Qt/qt_click.h ../win/Qt/qt_delay.h \ + ../win/Qt/qt_xcmd.h ../win/Qt/qt_key.h ../win/Qt/qt_map.h \ + ../win/Qt/qt_win.h ../win/Qt/qt_clust.h ../win/Qt/qt_menu.h \ + ../win/Qt/qt_rip.h ../win/Qt/qt_msg.h ../win/Qt/qt_plsel.h \ + ../win/Qt/qt_svsel.h ../win/Qt/qt_set.h ../win/Qt/qt_stat.h \ + ../win/Qt/qt_icon.h ../win/Qt/qt_streq.h ../win/Qt/qt_line.h \ + ../win/Qt/qt_yndlg.h ../win/Qt/qt_str.h ../include/dlb.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_bind.cpp +qt_click.o: ../win/Qt/qt_click.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_click.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_click.cpp +qt_clust.o: ../win/Qt/qt_clust.cpp ../win/Qt/qt_clust.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_clust.cpp +qt_delay.o: ../win/Qt/qt_delay.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_delay.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_delay.cpp +qt_glyph.o: ../win/Qt/qt_glyph.cpp $(HACK_H) ../include/tile2x11.h \ + ../win/Qt/qt_pre.h ../win/Qt/qt_post.h ../win/Qt/qt_glyph.h \ + ../win/Qt/qt_bind.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_inv.h ../win/Qt/qt_map.h \ + ../win/Qt/qt_win.h ../win/Qt/qt_clust.h ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_glyph.cpp +qt_icon.o: ../win/Qt/qt_icon.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_icon.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_icon.cpp +qt_inv.o: ../win/Qt/qt_inv.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_inv.h ../win/Qt/qt_glyph.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_kde0.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_inv.cpp +qt_key.o: ../win/Qt/qt_key.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_key.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_key.cpp +qt_line.o: ../win/Qt/qt_line.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_line.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_line.cpp +qt_main.o: ../win/Qt/qt_main.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ + qt_main.moc ../win/Qt/qt_bind.h ../win/Qt/qt_glyph.h \ + ../win/Qt/qt_inv.h ../win/Qt/qt_key.h ../win/Qt/qt_map.h \ + ../win/Qt/qt_win.h ../win/Qt/qt_clust.h ../win/Qt/qt_msg.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_stat.h ../win/Qt/qt_icon.h \ + ../win/Qt/qt_str.h qt_kde0.moc + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_main.cpp +qt_map.o: ../win/Qt/qt_map.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_map.h ../win/Qt/qt_win.h \ + ../win/Qt/qt_clust.h qt_map.moc ../win/Qt/qt_click.h \ + ../win/Qt/qt_glyph.h ../win/Qt/qt_xpms.h ../win/Qt/qt_set.h \ + ../win/Qt/qt_bind.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_map.cpp +qt_menu.o: ../win/Qt/qt_menu.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_menu.h ../win/Qt/qt_win.h \ + ../win/Qt/qt_rip.h qt_menu.moc ../win/Qt/qt_glyph.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_kde0.h ../win/Qt/qt_streq.h ../win/Qt/qt_line.h \ + ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_menu.cpp +qt_msg.o: ../win/Qt/qt_msg.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_msg.h ../win/Qt/qt_win.h \ + qt_msg.moc ../win/Qt/qt_map.h ../win/Qt/qt_clust.h \ + ../win/Qt/qt_set.h ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_kde0.h ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_msg.cpp +qt_plsel.o: ../win/Qt/qt_plsel.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_plsel.h qt_plsel.moc \ + ../win/Qt/qt_bind.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_glyph.h ../win/Qt/qt_set.h ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_plsel.cpp +qt_rip.o: ../win/Qt/qt_rip.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_rip.h ../win/Qt/qt_bind.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_rip.cpp +qt_set.o: ../win/Qt/qt_set.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_set.h ../win/Qt/qt_bind.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h qt_set.moc \ + ../win/Qt/qt_glyph.h ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_set.cpp +qt_stat.o: ../win/Qt/qt_stat.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_stat.h ../win/Qt/qt_win.h \ + ../win/Qt/qt_icon.h qt_stat.moc ../win/Qt/qt_set.h \ + ../win/Qt/qt_bind.h ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h \ + ../win/Qt/qt_str.h ../win/Qt/qt_xpms.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_stat.cpp +qt_str.o: ../win/Qt/qt_str.cpp ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_str.cpp +qt_streq.o: ../win/Qt/qt_streq.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_streq.h ../win/Qt/qt_line.h \ + ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_streq.cpp +qt_svsel.o: ../win/Qt/qt_svsel.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_svsel.h ../win/Qt/qt_bind.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_svsel.cpp +qt_win.o: ../win/Qt/qt_win.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_win.h ../win/Qt/qt_bind.h \ + ../win/Qt/qt_main.h ../win/Qt/qt_kde0.h ../win/Qt/qt_click.h \ + ../win/Qt/qt_glyph.h ../win/Qt/qt_inv.h ../win/Qt/qt_key.h \ + ../win/Qt/qt_icon.h ../win/Qt/qt_map.h ../win/Qt/qt_clust.h \ + ../win/Qt/qt_menu.h ../win/Qt/qt_rip.h ../win/Qt/qt_msg.h \ + ../win/Qt/qt_set.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_win.cpp +qt_xcmd.o: ../win/Qt/qt_xcmd.cpp $(HACK_H) ../include/func_tab.h \ + ../win/Qt/qt_pre.h ../win/Qt/qt_post.h ../win/Qt/qt_xcmd.h \ + qt_xcmd.moc ../win/Qt/qt_bind.h ../win/Qt/qt_main.h \ + ../win/Qt/qt_kde0.h ../win/Qt/qt_set.h ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_xcmd.cpp +qt_yndlg.o: ../win/Qt/qt_yndlg.cpp $(HACK_H) ../win/Qt/qt_pre.h \ + ../win/Qt/qt_post.h ../win/Qt/qt_yndlg.h qt_yndlg.moc \ + ../win/Qt/qt_str.h + $(CXX) $(CXXFLAGS) -c -o $@ ../win/Qt/qt_yndlg.cpp +wc_chainin.o: ../win/chain/wc_chainin.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../win/chain/wc_chainin.c +wc_chainout.o: ../win/chain/wc_chainout.c $(HACK_H) + $(CC) $(CFLAGS) -c -o $@ ../win/chain/wc_chainout.c +wc_trace.o: ../win/chain/wc_trace.c $(HACK_H) ../include/func_tab.h + $(CC) $(CFLAGS) -c -o $@ ../win/chain/wc_trace.c +vis_tab.o: vis_tab.c $(CONFIG_H) ../include/vis_tab.h +allmain.o: allmain.c $(HACK_H) +alloc.o: alloc.c $(CONFIG_H) +apply.o: apply.c $(HACK_H) +artifact.o: artifact.c $(HACK_H) ../include/artifact.h ../include/artilist.h +attrib.o: attrib.c $(HACK_H) +ball.o: ball.c $(HACK_H) +bones.o: bones.c $(HACK_H) +botl.o: botl.c $(HACK_H) +cmd.o: cmd.c $(HACK_H) ../include/func_tab.h +dbridge.o: dbridge.c $(HACK_H) +decl.o: decl.c $(HACK_H) +detect.o: detect.c $(HACK_H) ../include/artifact.h +dig.o: dig.c $(HACK_H) +display.o: display.c $(HACK_H) +dlb.o: dlb.c $(CONFIG_H) ../include/dlb.h +do.o: do.c $(HACK_H) +do_name.o: do_name.c $(HACK_H) +do_wear.o: do_wear.c $(HACK_H) +dog.o: dog.c $(HACK_H) +dogmove.o: dogmove.c $(HACK_H) ../include/mfndpos.h +dokick.o: dokick.c $(HACK_H) +dothrow.o: dothrow.c $(HACK_H) +drawing.o: drawing.c $(CONFIG_H) ../include/color.h ../include/rm.h \ + ../include/objclass.h ../include/monsym.h +dungeon.o: dungeon.c $(HACK_H) ../include/dgn_file.h ../include/dlb.h +eat.o: eat.c $(HACK_H) +end.o: end.c $(HACK_H) ../include/dlb.h +engrave.o: engrave.c $(HACK_H) +exper.o: exper.c $(HACK_H) +explode.o: explode.c $(HACK_H) +extralev.o: extralev.c $(HACK_H) +files.o: files.c $(HACK_H) ../include/dlb.h #zlib.h +fountain.o: fountain.c $(HACK_H) +hack.o: hack.c $(HACK_H) +hacklib.o: hacklib.c $(HACK_H) +insight.o: insight.c $(HACK_H) +invent.o: invent.c $(HACK_H) +isaac64.o: isaac64.c $(CONFIG_H) ../include/isaac64.h +light.o: light.c $(HACK_H) +lock.o: lock.c $(HACK_H) +mail.o: mail.c $(HACK_H) ../include/mail.h +makemon.o: makemon.c $(HACK_H) +mapglyph.o: mapglyph.c $(HACK_H) +mcastu.o: mcastu.c $(HACK_H) +mdlib.o: mdlib.c $(CONFIG_H) ../include/permonst.h ../include/align.h \ + ../include/monattk.h ../include/monflag.h \ + ../include/objclass.h ../include/monsym.h \ + ../include/artilist.h ../include/dungeon.h ../include/obj.h \ + ../include/monst.h ../include/mextra.h ../include/you.h \ + ../include/attrib.h ../include/prop.h ../include/skills.h \ + ../include/context.h ../include/flag.h ../include/dlb.h +mhitm.o: mhitm.c $(HACK_H) ../include/artifact.h +mhitu.o: mhitu.c $(HACK_H) ../include/artifact.h +minion.o: minion.c $(HACK_H) +mklev.o: mklev.c $(HACK_H) +mkmap.o: mkmap.c $(HACK_H) ../include/sp_lev.h +mkmaze.o: mkmaze.c $(HACK_H) ../include/sp_lev.h +mkobj.o: mkobj.c $(HACK_H) +mkroom.o: mkroom.c $(HACK_H) +mon.o: mon.c $(HACK_H) ../include/mfndpos.h +mondata.o: mondata.c $(HACK_H) +monmove.o: monmove.c $(HACK_H) ../include/mfndpos.h ../include/artifact.h +monst.o: monst.c $(CONFIG_H) ../include/permonst.h ../include/align.h \ + ../include/monattk.h ../include/monflag.h ../include/monsym.h \ + ../include/color.h +mplayer.o: mplayer.c $(HACK_H) +mthrowu.o: mthrowu.c $(HACK_H) +muse.o: muse.c $(HACK_H) +music.o: music.c $(HACK_H) +nhlua.o: nhlua.c $(HACK_H) ../include/dlb.h +nhlsel.o: nhlsel.c $(HACK_H) ../include/sp_lev.h +nhlobj.o: nhlobj.c $(HACK_H) ../include/sp_lev.h +o_init.o: o_init.c $(HACK_H) +objects.o: objects.c $(CONFIG_H) ../include/obj.h ../include/objclass.h \ + ../include/prop.h ../include/skills.h ../include/color.h +objnam.o: objnam.c $(HACK_H) +options.o: options.c $(CONFIG_H) ../include/objclass.h ../include/flag.h \ + $(HACK_H) ../include/tcap.h ../include/optlist.h +pager.o: pager.c $(HACK_H) ../include/dlb.h +pickup.o: pickup.c $(HACK_H) +pline.o: pline.c $(HACK_H) +polyself.o: polyself.c $(HACK_H) +potion.o: potion.c $(HACK_H) +pray.o: pray.c $(HACK_H) +priest.o: priest.c $(HACK_H) ../include/mfndpos.h +quest.o: quest.c $(HACK_H) +questpgr.o: questpgr.c $(HACK_H) ../include/dlb.h +read.o: read.c $(HACK_H) +rect.o: rect.c $(HACK_H) +region.o: region.c $(HACK_H) +restore.o: restore.c $(HACK_H) ../include/tcap.h +rip.o: rip.c $(HACK_H) +rnd.o: rnd.c $(HACK_H) ../include/isaac64.h +role.o: role.c $(HACK_H) +rumors.o: rumors.c $(HACK_H) ../include/dlb.h +save.o: save.c $(HACK_H) +sfstruct.o: sfstruct.c $(HACK_H) +shk.o: shk.c $(HACK_H) +shknam.o: shknam.c $(HACK_H) +sit.o: sit.c $(HACK_H) ../include/artifact.h +sounds.o: sounds.c $(HACK_H) +sp_lev.o: sp_lev.c $(HACK_H) ../include/sp_lev.h +spell.o: spell.c $(HACK_H) +steal.o: steal.c $(HACK_H) +steed.o: steed.c $(HACK_H) +symbols.o: symbols.c $(HACK_H) ../include/tcap.h +sys.o: sys.c $(HACK_H) +teleport.o: teleport.c $(HACK_H) +timeout.o: timeout.c $(HACK_H) +topten.o: topten.c $(HACK_H) ../include/dlb.h +track.o: track.c $(HACK_H) +trap.o: trap.c $(HACK_H) +u_init.o: u_init.c $(HACK_H) +uhitm.o: uhitm.c $(HACK_H) +vault.o: vault.c $(HACK_H) +version.o: version.c $(HACK_H) ../include/dlb.h ../include/date.h +vision.o: vision.c $(HACK_H) ../include/vis_tab.h +weapon.o: weapon.c $(HACK_H) +were.o: were.c $(HACK_H) +wield.o: wield.c $(HACK_H) +windows.o: windows.c $(HACK_H) ../include/wingem.h ../include/winGnome.h +wizard.o: wizard.c $(HACK_H) +worm.o: worm.c $(HACK_H) +worn.o: worn.c $(HACK_H) +write.o: write.c $(HACK_H) +zap.o: zap.c $(HACK_H) +# DEPENDENCIES MUST END AT END OF FILE +# IF YOU PUT STUFF HERE IT WILL GO AWAY +# see make depend above diff --git a/sys/lib/Makefile.top b/sys/lib/Makefile.top new file mode 100644 index 000000000..c3ca8fda3 --- /dev/null +++ b/sys/lib/Makefile.top @@ -0,0 +1,332 @@ +# NetHack Top-level Makefile. +# NetHack 3.7 Makefile.top $NHDT-Date: 1597031649 2020/08/10 03:54:09 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.52 $ +# Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland +# NetHack may be freely redistributed. See license for details. + +# Root of source tree: +NHSROOT=$(abspath .) + +# newer makes predefine $(MAKE) to 'make' and do smarter processing of +# recursive make calls if $(MAKE) is used +# these makes allow $(MAKE) to be overridden by the environment if someone +# wants to (or has to) use something other than the standard make, so we do +# not want to unconditionally set $(MAKE) here +# +# unfortunately, some older makes do not predefine $(MAKE); if you have one of +# these, uncomment the following line +# (you will know that you have one if you get complaints about unable to +# execute things like 'data' and 'rumors') +# MAKE = make + +# make libnethack +LIBNH = libnethack.a + +# if requested, setup cross-compiler for WASM +ifdef WANT_WASM +CC=$(EMCC) +AR=$(EMAR) +RANLIB=$(EMRANLIB) +CFLAGS=$(EMCC_CFLAGS) +SYSCFLAGS=$(EMCC_CFLAGS) +INSTDIR=$(WASM_DATA_DIR) +endif # WANT_WASM + +# Permissions - some places use setgid instead of setuid, for instance +# See also the option "SECURE" in include/config.h +#GAMEPERM = 04755 +FILEPERM = 0644 +# VARFILEPERM = 0644 +EXEPERM = 0755 +DIRPERM = 0755 +# VARDIRPERM = 0755 + +# VARDIR may also appear in unixconf.h as "VAR_PLAYGROUND" else HACKDIR +# +# note that 'make install' believes in creating a nice tidy HACKDIR for +# installation, free of debris from previous NetHack versions -- +# therefore there should not be anything in HACKDIR that you want to keep +# (if there is, you'll have to do the installation by hand or modify the +# instructions) +#HACKDIR = $(PREFIX)/games/lib/$(GAME)dir +#VARDIR = $(HACKDIR) +# Where nethack.sh in installed. If this is not defined, the wrapper is not used. +#SHELLDIR = $(PREFIX)/games + +# per discussion in Install.X11 and Install.Qt +#VARDATND = +# VARDATND = x11tiles NetHack.ad pet_mark.xbm pilemark.xbm +# VARDATND = x11tiles NetHack.ad pet_mark.xbm pilemark.xbm rip.xpm +# for Atari/Gem +# VARDATND = nh16.img title.img GEM_RSC.RSC rip.img +# for BeOS +# VARDATND = beostiles +# for Gnome +# VARDATND = x11tiles pet_mark.xbm pilemark.xbm rip.xpm mapbg.xpm + +VARDATD = bogusmon data engrave epitaph oracles options quest.lua rumors +VARDAT = $(VARDATD) $(VARDATND) + +# Some versions of make use the SHELL environment variable as the shell +# for running commands. We need this to be a Bourne shell. +# SHELL = /bin/sh +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP + +# Commands for setting the owner and group on files during installation. +# Some systems fail with one or the other when installing over NFS or for +# other permission-related reasons. If that happens, you may want to set the +# command to "true", which is a no-op. Note that disabling chown or chgrp +# will only work if setuid (or setgid) behavior is not desired or required. +#CHOWN = chown +#CHGRP = chgrp + +# Lua version +LUA_VERSION = 5.4.0 + +# +# end of configuration +# + +DATHELP = help hh cmdhelp keyhelp history opthelp wizhelp + +SPEC_LEVS = asmodeus.lua baalz.lua bigrm-*.lua castle.lua fakewiz?.lua \ + juiblex.lua knox.lua medusa-?.lua minend-?.lua minefill.lua \ + minetn-?.lua oracle.lua orcus.lua sanctum.lua soko?-?.lua \ + tower?.lua valley.lua wizard?.lua nhlib.lua themerms.lua \ + astral.lua air.lua earth.lua fire.lua water.lua +QUEST_LEVS = ???-goal.lua ???-fil?.lua ???-loca.lua ???-strt.lua + +DATNODLB = $(VARDATND) license symbols +DATDLB = $(DATHELP) dungeon.lua tribute $(SPEC_LEVS) $(QUEST_LEVS) $(VARDATD) +DAT = $(DATNODLB) $(DATDLB) + +# first target is also the default target for 'make' without any arguments +#all: $(GAME) recover Guidebook $(VARDAT) spec_levs check-dlb +all: libnh + true; $(MOREALL) + @echo "Done." + +libnh: src/$(LIBNH) + @echo "libnethack is up to date." + +src/$(LIBNH): lua_support + ( cd src ; $(MAKE)) + +lua_support: lib/lua/liblua.a include/nhlua.h + @true +lib/lua-$(LUA_VERSION)/src/liblua.a: lib/lua-$(LUA_VERSION)/src/lua.h + ( cd lib/lua-$(LUA_VERSION)/src \ + && make CC='$(CC)' AR='$(AR)' RANLIB='$(RANLIB)' SYSCFLAGS='$(SYSCFLAGS)' a && cd ../../.. ) +lib/lua/liblua.a: lib/lua-$(LUA_VERSION)/src/liblua.a + @( if [ ! -d lib/lua ] ; then mkdir -p lib/lua ; fi ) + cp lib/lua-$(LUA_VERSION)/src/liblua.a $@ +include/nhlua.h: lib/lua/liblua.a + echo '/* nhlua.h - generated by top Makefile */' > $@ + @echo '#include "../lib/lua-$(LUA_VERSION)/src/lua.h"' >> $@ + @sed -e '/(lua_error)/!d' -e '/(lua_error)/s/;/ NORETURN;/1' \ + < lib/lua-$(LUA_VERSION)/src/lua.h >> $@ + @echo '#include "../lib/lua-$(LUA_VERSION)/src/lualib.h"' >> $@ + @echo '#include "../lib/lua-$(LUA_VERSION)/src/lauxlib.h"' >> $@ + @echo '/*nhlua.h*/' >> $@ + +# Note: many of the dependencies below are here to allow parallel make +# to generate valid output + +Guidebook: + ( cd doc ; $(MAKE) Guidebook ) + +manpages: + ( cd doc ; $(MAKE) manpages ) + +data: $(GAME) + ( cd dat ; $(MAKE) data ) + +engrave: $(GAME) + ( cd dat ; $(MAKE) engrave ) + +bogusmon: $(GAME) + ( cd dat ; $(MAKE) bogusmon ) + +epitaph: $(GAME) + ( cd dat ; $(MAKE) epitaph ) + +rumors: $(GAME) + ( cd dat ; $(MAKE) rumors ) + +oracles: $(GAME) + ( cd dat ; $(MAKE) oracles ) + +# Note: options should have already been made with make, but... +options: $(GAME) + ( cd dat ; $(MAKE) options ) + +quest.lua: $(GAME) + +spec_levs: + ( cd dat ; $(MAKE) spec_levs ) + ( cd dat ; $(MAKE) quest_levs ) + +nhtiles.bmp: $(GAME) + ( cd dat ; $(MAKE) nhtiles.bmp ) + +x11tiles: $(GAME) + ( cd util ; $(MAKE) tile2x11 ) + ( cd dat ; $(MAKE) x11tiles ) + +beostiles: $(GAME) + ( cd util ; $(MAKE) tile2beos ) + ( cd dat ; $(MAKE) beostiles ) + +NetHack.ad: $(GAME) + ( cd dat ; $(MAKE) NetHack.ad ) + +pet_mark.xbm: + ( cd dat ; $(MAKE) pet_mark.xbm ) + +pilemark.xbm: + ( cd dat ; $(MAKE) pilemark.xbm ) + +rip.xpm: + ( cd dat ; $(MAKE) rip.xpm ) + +mapbg.xpm: + (cd dat ; $(MAKE) mapbg.xpm ) + +nhsplash.xpm: + ( cd dat ; $(MAKE) nhsplash.xpm ) + +nh16.img: $(GAME) + ( cd util ; $(MAKE) tile2img.ttp ) + ( cd dat ; $(MAKE) nh16.img ) + +rip.img: + ( cd util ; $(MAKE) xpm2img.ttp ) + ( cd dat ; $(MAKE) rip.img ) +GEM_RSC.RSC: + ( cd dat ; $(MAKE) GEM_RSC.RSC ) + +title.img: + ( cd dat ; $(MAKE) title.img ) + +check-dlb: options + @if egrep -s librarian dat/options ; then $(MAKE) dlb ; else true ; fi + +dlb: + ( cd util ; $(MAKE) dlb ) + ( cd dat ; LC_ALL=C ; ../util/dlb cf nhdat $(DATDLB) ) + +# recover can be used when INSURANCE is defined in include/config.h +# and the checkpoint option is true +recover: $(GAME) + ( cd util ; $(MAKE) recover ) + +dofiles: + target=`sed -n \ + -e '/librarian/{' \ + -e 's/.*/dlb/p' \ + -e 'q' \ + -e '}' \ + -e '$$s/.*/nodlb/p' < dat/options` ; \ + $(MAKE) dofiles-$${target-nodlb} + cp src/$(GAME) $(INSTDIR) + cp util/recover $(INSTDIR) + -if test -n '$(SHELLDIR)'; then rm -f $(SHELLDIR)/$(GAME); fi + if test -n '$(SHELLDIR)'; then \ + sed -e 's;/usr/games/lib/nethackdir;$(HACKDIR);' \ + -e 's;HACKDIR/nethack;HACKDIR/$(GAME);' \ + < sys/unix/nethack.sh \ + > $(SHELLDIR)/$(GAME) ; fi +# set up their permissions + -( cd $(INSTDIR) ; $(CHOWN) $(GAMEUID) $(GAME) recover ; \ + $(CHGRP) $(GAMEGRP) $(GAME) recover ) + chmod $(GAMEPERM) $(INSTDIR)/$(GAME) + chmod $(EXEPERM) $(INSTDIR)/recover + -if test -n '$(SHELLDIR)'; then \ + $(CHOWN) $(GAMEUID) $(SHELLDIR)/$(GAME); fi + if test -n '$(SHELLDIR)'; then \ + $(CHGRP) $(GAMEGRP) $(SHELLDIR)/$(GAME); \ + chmod $(EXEPERM) $(SHELLDIR)/$(GAME); fi + +dofiles-dlb: check-dlb + ( cd dat ; cp nhdat $(DATNODLB) $(INSTDIR) ) +# set up their permissions + -( cd $(INSTDIR) ; $(CHOWN) $(GAMEUID) nhdat $(DATNODLB) ; \ + $(CHGRP) $(GAMEGRP) nhdat $(DATNODLB) ; \ + chmod $(FILEPERM) nhdat $(DATNODLB) ) + +dofiles-nodlb: +# copy over the game files + ( cd dat ; cp $(DAT) $(INSTDIR) ) +# set up their permissions + -( cd $(INSTDIR) ; $(CHOWN) $(GAMEUID) $(DAT) ; \ + $(CHGRP) $(GAMEGRP) $(DAT) ; \ + chmod $(FILEPERM) $(DAT) ) +# +# This is not part of the dependency build hierarchy. +# It requires an explicit "make fetch-Lua". +fetch-lua: fetch-Lua + @true + +fetch-Lua: + ( mkdir -p lib && cd lib && \ + curl -R -O http://www.lua.org/ftp/lua-$(LUA_VERSION).tar.gz && \ + tar zxf lua-$(LUA_VERSION).tar.gz && rm -f lua-$(LUA_VERSION).tar.gz ) + +update: $(GAME) recover $(VARDAT) spec_levs +# (don't yank the old version out from under people who're playing it) + -mv $(INSTDIR)/$(GAME) $(INSTDIR)/$(GAME).old + -mv $(INSTDIR)/nhdat $(INSTDIR)/nhdat.old +# set up new versions of the game files + ( $(MAKE) dofiles ) +# touch time-sensitive files + -touch -c $(VARDIR)/bones* $(VARDIR)/?lock* $(VARDIR)/wizard* + -touch -c $(VARDIR)/save/* + touch $(VARDIR)/perm $(VARDIR)/record +# and a reminder + @echo You may also want to install the man pages via the doc Makefile. + +rootcheck: + @true; $(ROOTCHECK) + +install: rootcheck $(GAME) recover $(VARDAT) spec_levs + true; $(PREINSTALL) +# set up the directories +# not all mkdirs have -p; those that don't will create a -p directory + -if test -n '$(SHELLDIR)'; then \ + mkdir -p $(SHELLDIR); fi + rm -rf $(INSTDIR) $(VARDIR) + -mkdir -p $(INSTDIR) $(VARDIR) $(VARDIR)/save + if test -d ./-p; then rmdir ./-p; fi + -$(CHOWN) $(GAMEUID) $(INSTDIR) $(VARDIR) $(VARDIR)/save + $(CHGRP) $(GAMEGRP) $(INSTDIR) $(VARDIR) $(VARDIR)/save +# order counts here: + chmod $(DIRPERM) $(INSTDIR) + chmod $(VARDIRPERM) $(VARDIR) $(VARDIR)/save +# set up the game files + ( $(MAKE) dofiles ) +# set up some additional files + touch $(VARDIR)/perm $(VARDIR)/record $(VARDIR)/logfile $(VARDIR)/xlogfile + -( cd $(VARDIR) ; $(CHOWN) $(GAMEUID) perm record logfile xlogfile ; \ + $(CHGRP) $(GAMEGRP) perm record logfile xlogfile ; \ + chmod $(VARFILEPERM) perm record logfile xlogfile ) + true; $(POSTINSTALL) +# and a reminder + @echo You may also want to reinstall the man pages via the doc Makefile. + + +# 'make clean' removes all the .o files, but leaves around all the executables +# and compiled data files +clean: + ( cd src ; $(MAKE) clean ) + ( cd util ; $(MAKE) clean ) + ( cd dat ; $(MAKE) clean ) + ( cd doc ; $(MAKE) clean ) + ( cd lib/lua-$(LUA_VERSION)/src && $(MAKE) clean ) + +# 'make spotless' returns the source tree to near-distribution condition. +# it removes .o files, executables, and compiled data files +spotless:: + ( cd src ; $(MAKE) spotless ) + ( cd util ; $(MAKE) spotless ) + ( cd dat ; $(MAKE) spotless ) + ( cd doc ; $(MAKE) spotless ) diff --git a/sys/lib/Makefile.utl b/sys/lib/Makefile.utl new file mode 100644 index 000000000..8aaf07c9a --- /dev/null +++ b/sys/lib/Makefile.utl @@ -0,0 +1,382 @@ +# Makefile for NetHack's utility programs. +# NetHack 3.7 Makefile.utl $NHDT-Date: 1596498292 2020/08/03 23:44:52 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.51 $ +# Copyright (c) 2018 by Robert Patrick Rankin +# NetHack may be freely redistributed. See license for details. + +# Root of source tree: +NHSROOT=.. + +# newer makes predefine $(MAKE) to 'make' and do smarter processing of +# recursive make calls if $(MAKE) is used +# these makes allow $(MAKE) to be overridden by the environment if someone +# wants to (or has to) use something other than the standard make, so we do +# not want to unconditionally set $(MAKE) here +# +# unfortunately, some older makes do not predefine $(MAKE); if you have one of +# these, uncomment the following line +# (you will know that you have one if you get complaints about unable to +# execute things like 'foo.o') +# MAKE = make + +# if you are using gcc as your compiler, +# uncomment the CC definition below if it's not in your environment +# CC = gcc +# +# For Bull DPX/2 systems at B.O.S. 2.0 or higher use the following: +# +# CC = gcc -ansi -D_BULL_SOURCE -D_XOPEN_SOURCE -D_POSIX_SOURCE +# +# If you are using GCC 2.2.2 or higher on a DPX/2, just use: +# +# CC = gcc -ansi +# +# For HP/UX 10.20 with GCC: +# CC = gcc -D_POSIX_SOURCE +# +# if your make doesn't define a default SHELL properly, you may need +# the line below (Atari users will need a bourne work-alike) +# SHELL = /bin/sh +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP + +# flags may have to be changed as required +# flags for 286 Xenix: +# CFLAGS = -Ml2t16 -O -LARGE -I../include +# LFLAGS = -Ml -F 4000 -SEG 512 + +# flags for 286 Microport SysV-AT +# CFLAGS = -DDUMB -Ml -I../include +# LFLAGS = -Ml + +# flags for Atari GCC (3.2.1) +# CFLAGS = -O -I../include +# LFLAGS = -s +# flags for Atari GCC (3.3) +# CFLAGS = -mshort -O2 -I../include +# LFLAGS = -mshort -s + +# flags for Apollos using their native cc +# (as long as it claims to be __STDC__ but isn't) +# CFLAGS = -DAPOLLO -O -I../include + +# flags for AIX 3.1 cc on IBM RS/6000 to define +# a suitable subset of standard libraries +# (note that there is more info regarding the "-qchars=signed" +# switch in file Install.unx note 8) +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -O -I../include -qchars=signed +# and for AIX 3.2: +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -D_ALL_SOURCE -O -I../include -qchars=signed + +# flags for A/UX 2.01 using native cc or c89 +# gcc predefines AUX so that's not needed there +# CFLAGS = -ZS -D_POSIX_SOURCE -O -I../include -DAUX + +# flags for IRIX 4.0.x using native cc +# SGI cc 3.10 will fail to compile makedefs with -O +# CFLAGS = -I../include -D__STDC__ -woff 100,293 + +# flags for Linux +# compile normally +# CFLAGS = -O2 -fomit-frame-pointer -I../include +# LFLAGS = -L/usr/X11R6/lib +# OR compile backwards compatible a.out format +# CFLAGS = -O2 -b i486-linuxaout -fomit-frame-pointer -I../include +# LFLAGS = -b i486-linuxaout -L/usr/X11R6/lib + +# flags for BeOS using the command line +# BeOS on a Mac/BeBox: +#CC = mwcc +#CFLAGS = -I../include +# BeOS on Intel: +# the default values are fine + +# flags for debugging: +# CFLAGS = -g -I../include + +#CFLAGS = -O -I../include +#LFLAGS = + +# -lm required by lua +LFLAGS += -lm + +# we specify C preprocessor flags via CFLAGS; files built with default rules +# might include $(CPPFLAGS) which could get a value from user's environment; +# we avoid that by forcing it empty rather than by overriding default rules +CPPFLAGS = + +LIBS = + +# If you are cross-compiling, you must use this: +OBJDIR = . +# otherwise, you can save a little bit of disk space with this: +#OBJDIR = ../src + +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 + +# TODO? the link/load commands below are handled differently from the ones +# in Makefile.src; these use '$(CC) $(LFLAGS)' and ought to be changed to use +# $(LD) or $(LINK) as appropriate [quiet mode echoes a misleading $< value] + +# ---------------------------------------- +# +# Nothing below this line should have to be changed. + +# Verbosity definitions, begin +# Set QUIETCC=1 above to output less feedback while building. +# CC and CXX obey verbosity, LD and LINK don't. +# AT is @ when not verbose, empty otherwise +ACTUAL_CC := $(CC) +ACTUAL_CXX := $(CXX) +ACTUAL_LD := $(LD) +ACTUAL_LINK := $(LINK) + +CC_V0 = $(ACTUAL_CC) +CC_V = $(CC_V0) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) + +CXX_V0 = $(ACTUAL_CXX) +CXX_V = $(CXX_V0) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) + +CFLAGS+=-I../include +ifdef WANT_WASM +CFLAGS+=-DWASM +endif + +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) +LD = $(ACTUAL_LD) +LINK = $(ACTUAL_LINK) + +AT_V0 := +AT_V := $(AT_V0) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) +# Verbosity, end + +# timestamps for primary header files, matching src/Makefile +CONFIG_H = ../src/config.h-t +HACK_H = ../src/hack.h-t + +# utility .c files +MAKESRC = makedefs.c ../src/mdlib.c +RECOVSRC = recover.c +DLBSRC = dlb_main.c +UTILSRCS = $(MAKESRC) panic.c $(DGNCOMPSRC) $(RECOVSRC) $(DLBSRC) + +# files that define all monsters and objects +CMONOBJ = ../src/monst.c ../src/objects.c +OMONOBJ = $(OBJDIR)/monst.o $(OBJDIR)/objects.o +# files that provide access to NetHack's names +CNAMING = ../src/drawing.c $(CMONOBJ) +ONAMING = $(OBJDIR)/drawing.o $(OMONOBJ) +# dynamic memory allocation +CALLOC = ../src/alloc.c panic.c +OALLOC = $(OBJDIR)/alloc.o panic.o + +# object files for makedefs +MAKEOBJS = makedefs.o $(OMONOBJ) + +# object files for recovery utility +RECOVOBJS = recover.o + +# object files for the data librarian +DLBOBJS = dlb_main.o $(OBJDIR)/dlb.o $(OALLOC) + + +# dependencies for makedefs +# +makedefs: $(MAKEOBJS) mdgrep.h + $(CC) $(LFLAGS) -o makedefs $(MAKEOBJS) + +makedefs.o: makedefs.c ../src/mdlib.c $(CONFIG_H) ../include/permonst.h \ + ../include/objclass.h ../include/monsym.h \ + ../include/artilist.h ../include/dungeon.h ../include/obj.h \ + ../include/monst.h ../include/you.h ../include/flag.h \ + ../include/dlb.h ../include/patchlevel.h + +# Don't require perl to build; that is why mdgrep.h is spelled wrong below. +mdgreph: mdgrep.pl + perl mdgrep.pl + +../include/onames.h: makedefs + ./makedefs -o +../include/pm.h: makedefs + ./makedefs -p +../include/vis_tab.h: makedefs + ./makedefs -z +# makedefs -z makes both vis_tab.h and vis_tab.c, but writes the .h first +../src/vis_tab.c: ../include/vis_tab.h + +lintdefs: + @lint -axbh -I../include -DLINT $(MAKESRC) $(CMONOBJ) | sed '/_flsbuf/d' + + +# we defer this makedefs call to the src Makefile, since it knows all about +# the main src and include files date.h is a timestamp for +../include/date.h:: + @( cd ../src ; $(MAKE) ../include/date.h ) + +# support code used by several of the utility programs (but not makedefs) +panic.o: panic.c $(CONFIG_H) + + +# with all of extern.h's functions to complain about, we drown in +# 'defined but not used' without -u +lintdgn: + @lint -axhu -I../include -DLINT $(DGNCOMPSRC) $(CALLOC) | sed '/_flsbuf/d' + + +# dependencies for recover +# +recover: $(RECOVOBJS) + $(CC) $(LFLAGS) -o recover $(RECOVOBJS) $(LIBS) + +recover.o: recover.c $(CONFIG_H) ../include/date.h + + +# dependencies for dlb +# +dlb: $(DLBOBJS) + $(CC) $(LFLAGS) -o dlb $(DLBOBJS) $(LIBS) + +dlb_main.o: dlb_main.c $(CONFIG_H) ../include/dlb.h ../include/date.h + $(CC) $(CFLAGS) -c dlb_main.c + + + +# dependencies for tile utilities +# +TEXT_IO = tiletext.o tiletxt.o $(OALLOC) $(ONAMING) +GIFREADERS = gifread.o +PPMWRITERS = ppmwrite.o + +tileutils: tilemap gif2txt txt2ppm tile2x11 + +gif2txt: $(GIFREADERS) $(TEXT_IO) + $(CC) $(LFLAGS) -o gif2txt $(GIFREADERS) $(TEXT_IO) $(LIBS) +txt2ppm: $(PPMWRITERS) $(TEXT_IO) + $(CC) $(LFLAGS) -o txt2ppm $(PPMWRITERS) $(TEXT_IO) $(LIBS) + +tile2x11: tile2x11.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2x11 tile2x11.o $(TEXT_IO) $(LIBS) + +tile2img.ttp: tile2img.o bitmfile.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2img.ttp tile2img.o bitmfile.o $(TEXT_IO) $(LIBS) + +tile2bmp: tile2bmp.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2bmp tile2bmp.o $(TEXT_IO) + +xpm2img.ttp: xpm2img.o bitmfile.o + $(CC) $(LFLAGS) -o xpm2img.ttp xpm2img.o bitmfile.o $(LIBS) + +tile2beos: tile2beos.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2beos tile2beos.o $(TEXT_IO) -lbe + +#--compiling and linking in one step leaves extra debugging files (in their +# own subdirectories!) on OSX; compile and link separately to suppress +# that without mucking about with extra OS-specific CFLAGS and/or LFLAGS +#tilemap: ../win/share/tilemap.c $(HACK_H) +# $(CC) $(CFLAGS) $(LFLAGS) -o tilemap ../win/share/tilemap.c $(LIBS) +tilemap: tilemap.o + $(CC) $(LFLAGS) -o tilemap tilemap.o $(LIBS) +../src/tile.c: tilemap + ./tilemap + +../include/tile.h: ../win/share/tile.h + cp ../win/share/tile.h ../include/tile.h +tiletext.o: ../win/share/tiletext.c $(CONFIG_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/tiletext.c +tiletxt.c: ./Makefile + @echo '/* alternate compilation for tilemap.c to create tiletxt.o' > tiletxt.c + @echo ' that does not rely on "cc -c -o tiletxt.o tilemap.c"' >> tiletxt.c + @echo ' since many pre-POSIX compilers did not support that */' >> tiletxt.c + echo '#define TILETEXT' >> tiletxt.c + echo '#include "../win/share/tilemap.c"' >> tiletxt.c + @echo '/*tiletxt.c*/' >> tiletxt.c +tiletxt.o: tiletxt.c ../win/share/tilemap.c $(HACK_H) + $(CC) $(CFLAGS) -c tiletxt.c +tilemap.o: ../win/share/tilemap.c $(HACK_H) + $(CC) $(CFLAGS) -c ../win/share/tilemap.c + +gifread.o: ../win/share/gifread.c $(CONFIG_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/gifread.c +ppmwrite.o: ../win/share/ppmwrite.c $(CONFIG_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/ppmwrite.c + +tile2bmp.o: ../win/share/tile2bmp.c $(HACK_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/tile2bmp.c + +tile2x11.o: ../win/X11/tile2x11.c $(HACK_H) ../include/tile.h \ + ../include/tile2x11.h + $(CC) $(CFLAGS) -c ../win/X11/tile2x11.c + +tile2img.o: ../win/gem/tile2img.c $(HACK_H) ../include/tile.h \ + ../include/bitmfile.h + $(CC) $(CFLAGS) -c ../win/gem/tile2img.c +xpm2img.o: ../win/gem/xpm2img.c $(HACK_H) ../include/bitmfile.h + $(CC) $(CFLAGS) -c ../win/gem/xpm2img.c +bitmfile.o: ../win/gem/bitmfile.c ../include/bitmfile.h + $(CC) $(CFLAGS) -c ../win/gem/bitmfile.c + +tile2beos.o: ../win/BeOS/tile2beos.cpp $(HACK_H) ../include/tile.h + $(CXX) $(CFLAGS) -c ../win/BeOS/tile2beos.cpp + +tileedit: tileedit.cpp $(TEXT_IO) + $(QTDIR)/bin/moc -o tileedit.moc tileedit.h + $(CC) -o tileedit -I../include -I$(QTDIR)/include -L$(QTDIR)/lib \ + tileedit.cpp $(TEXT_IO) -lqt + +# using dependencies like +# ../src/foo:: +# @( cd ../src ; $(MAKE) foo ) +# would always force foo to be up-to-date according to the src Makefile +# when it's needed here. Unfortunately, some makes believe this syntax +# means foo always changes, instead of foo should always be checked. +# therefore, approximate via config.h dependencies, and hope that anybody +# changing anything other than basic configuration also knows when not +# to improvise things not in the instructions, like 'make makedefs' here +# in util... + +# make sure object files from src are available when needed +# +$(OBJDIR)/alloc.o: ../src/alloc.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/alloc.c -o $@ +$(OBJDIR)/drawing.o: ../src/drawing.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/drawing.c -o $@ +$(OBJDIR)/decl.o: ../src/decl.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/decl.c -o $@ +$(OBJDIR)/monst.o: ../src/monst.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/monst.c -o $@ +$(OBJDIR)/objects.o: ../src/objects.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/objects.c -o $@ +$(OBJDIR)/dlb.o: ../src/dlb.c $(CONFIG_H) ../include/dlb.h + $(CC) $(CFLAGS) -c ../src/dlb.c -o $@ + +# make sure hack.h dependencies get transitive information +$(HACK_H): $(CONFIG_H) + @( cd ../src ; $(MAKE) $(HACK_H) ) +$(CONFIG_H): ../include/config.h + @( cd ../src ; $(MAKE) $(CONFIG_H) ) + +SYSSHARE=../sys/share/ + +tags: $(UTILSRCS) + @ctags -tw $(UTILSRCS) + +clean: + -rm -f *.o + +spotless: clean + -rm -f ../include/tile.h tiletxt.c + -rm -f makedefs recover dlb + -rm -f gif2txt txt2ppm tile2x11 tile2img.ttp xpm2img.ttp \ + tilemap tileedit tile2bmp diff --git a/sys/lib/hints/include/multiw-1.2020 b/sys/lib/hints/include/multiw-1.2020 new file mode 100644 index 000000000..3fb550882 --- /dev/null +++ b/sys/lib/hints/include/multiw-1.2020 @@ -0,0 +1,37 @@ +#------------------------------------------------------------------------------ +# NetHack 3.7 multiw-1.2020 $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ + +# 1. Which windowing interface(s) should be included in this binary? +# One or more of these can be manually uncommented and/or can be specified +# on the 'make' command line. If none are enabled, tty will be used. +#WANT_WIN_TTY=1 +#WANT_WIN_CURSES=1 +#WANT_WIN_X11=1 +#WANT_WIN_QT=1 + +# 2. What is the default window system? +# Exactly one of these can be manually uncommented and/or can be specified +# on the 'make' command line. If none is enabled, the first among +# WANT_WIN_{tty,curses,X11,Qt} that is enabled will become default. +#WANT_DEFAULT=tty +#WANT_DEFAULT=curses +#WANT_DEFAULT=Qt +#WANT_DEFAULT=X11 + +# 3. compiler detection or optional override +CCISCLANG := $(shell echo `$(CC) --version` | grep clang) +ifeq "$(CCISCLANG)" "" +CXX=g++ -std=gnu++11 +else +CXX=clang++ -std=gnu++11 +endif +# if you want to override the compiler detection just carried out +# uncomment one of the following pairs as desired. +#CC= gcc +#CXX= g++ -std-gnu++11 +# +#CC= clang +#CXX=clang++ -std=gnu++11 + +#end of multiw-1.2020 +#------------------------------------------------------------------------------ diff --git a/sys/lib/hints/include/multiw-2.2020 b/sys/lib/hints/include/multiw-2.2020 new file mode 100644 index 000000000..ad4028aed --- /dev/null +++ b/sys/lib/hints/include/multiw-2.2020 @@ -0,0 +1,108 @@ +#------------------------------------------------------------------------------ +# NetHack 3.7 multiw-2.2020 $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ +# +# Sorts out support for multiple window ports (interfaces) to included in the build. +# +# Included from: +# hints/linux.2020 +# hints/macOS.2020 +# +# The following will be set appropriately following this: +# - WANT_WIN_XXX (at least one will be set; default is TTY) +# - WANT_DEFAULT (set to match one of the enabled WANT_WIN_XXX) +# - WINCFLAGS +# - WINSRC +# - WINOBJ0 +#--- +# User selections could be specified as combinations of any of the following: +# WIN_WANT_TTY=1, WIN_WANT_CURSES=1, WIN_WANT_QT=1, WIN_WANT_X11=1 +# The selections will all be linked into the same binary. +# +# Assuming you have the prerequisite packages mentioned above, you can +# specify, right on the make command line, which window ports (or interfaces) +# to include in your build. Doing it via the make command line means that won't +# have to edit the Makefile. +# +# make WANT_WIN_QT=1 WANT_WIN_X11=1 WANT_WIN_CURSES=1 WANT_WIN_TTY=1 install +# +# Add WANT_DEFAULT=Qt (or other interface) if you want nethack to use +# something other than tty as the default interface. +# + +# Make sure that at least one interface is enabled. +ifndef WANT_WIN_ALL +ifndef WANT_WIN_TTY +ifndef WANT_WIN_CURSES +ifndef WANT_WIN_X11 +ifndef WANT_WIN_QT +WANT_WIN_TTY=1 +endif +endif +endif +endif +endif + +ifdef WANT_WIN_ALL +WANT_WIN_TTY=1 +WANT_WIN_CURSES=1 +WANT_WIN_X11=1 +WANT_WIN_QT=1 +endif + + +# Make sure that a default interface is specified; this doesn't guarantee +# sanity for something like 'make WANT_WIN_CURSES=1 WANT_DEFAULT=X11' but +# 'makedefs -v' would notice, complain, and quit causing 'make' to quit. +ifndef WANT_DEFAULT +# pick the first one enabled among { tty, curses, X11, Qt } +ifdef WANT_WIN_TTY +WANT_DEFAULT=tty +else +ifdef WANT_WIN_CURSES +WANT_DEFAULT=curses +else +ifdef WANT_WIN_X11 +WANT_DEFAULT=X11 +else +ifdef WANT_WIN_QT +WANT_DEFAULT=Qt +else +# ? shouldn't be able to get here... +endif +endif +endif +endif +endif + +WINCFLAGS= +WINSRC = +WINOBJ0 = + +ifdef WANT_WIN_TTY +WINSRC += $(WINTTYSRC) +WINOBJ0 += $(WINTTYOBJ) +else +WINCFLAGS += -DNOTTYGRAPHICS +endif + +ifdef WANT_WIN_CURSES +WINCFLAGS += -DCURSES_GRAPHICS +WINSRC += $(WINCURSESSRC) +WINOBJ0 += $(WINCURSESOBJ) +endif + +ifdef WANT_WIN_X11 +WINCFLAGS += -DX11_GRAPHICS +WINSRC += $(WIINX11SRC) +WINOBJ0 += $(WINX11OBJ) +endif + +ifdef WANT_WIN_QT +WINCFLAGS += -DQT_GRAPHICS +WINSRC += $(WINQTSRC) +WINOBJ0 += $(WINQTOBJ) +endif + +#end of hints/include/multiw-2.2020 +#------------------------------------------------------------------------------ + diff --git a/sys/lib/hints/macOS.2020 b/sys/lib/hints/macOS.2020 new file mode 100755 index 000000000..227f83a3c --- /dev/null +++ b/sys/lib/hints/macOS.2020 @@ -0,0 +1,443 @@ +# NetHack 3.7 macOS.2020 $NHDT-Date: 1597704793 2020/08/17 22:53:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.69 $ +# Copyright (c) Kenneth Lorber, Kensington, Maryland, 2015. +# NetHack may be freely redistributed. See license for details. +# +#--------------------------------------------------------------------- +# MacOS hints file with support for multiple window ports (interfaces) +# Tested on: +# - MacOS Catalina 10.15 +# +# If this doesn't work for some other version of Mac OS X, consider +# making a new hints file it, rather than changing this one. +# And let us know about it. +# Useful info: http://www.opensource.apple.com/darwinsource/index.html + +#-PRE xxxx +# macOS X hints file +# + +####-INCLUDE multiw-1.2020 + +# 4. If you set WANT_WIN_QT, you need to +# A) set QTDIR either here or in the environment to point to the Qt5 +# library installation root. (Qt2, Qt3, Qt4 will not work) +# B) set XPMLIB to point to the Xpm library +ifndef WANT_WIN_QT +ifdef WANT_WIN_ALL +WANT_WIN_QT=1 +endif +endif +ifdef WANT_WIN_QT +#QTDIR=/Developer/Qt +# Qt installed via homebrew +QTDIR=$(shell brew --prefix)/opt/qt +# Qt installed via macports +#QTDIR=/opt/local/libexec/qt5 +endif # WANT_WIN_QT +ifndef LIBXPM +LIBXPM= -L/opt/X11/lib -lXpm +endif + +# 5. Other + +#----------------------------------------------------------------------------- +# You shouldn't need to change anything below here (in the hints file; if +# you're reading this in Makefile augmented by hints, that may not be true). +# + +####-INCLUDE multiw-2.2020 + +# XXX -g vs -O should go here, -I../include goes in the makefile +CFLAGS+=-g -I../include -DNOTPARMDECL +ifndef WANT_WIN_QT +# these are normally used when compiling nethack's core +CFLAGS+=-ansi -pedantic -Wno-long-long +# but -ansi forces -std=c90 for C or -std=c++98 for C++; +# win/Qt/qt_*.cpp compiled with C++98 semantics trigger +#In file included from .../qt5/include/QtCore/qglobal.h:105: +#.../qt5/include/QtCore/qcompilerdetection.h:561:6: +# error Qt requires a C++11 compiler and yours does not seem to be that. +# so we suppress -ansi when the build includes Qt +endif +# As of LLVM build 2336.1.00, this gives dozens of spurious messages, so +# leave it out by default. +#CFLAGS+=-Wunreachable-code +#TODO NHLIB +#CFLAGS+=-Wall -Wextra -Wno-missing-field-initializers -Wimplicit -Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings +CFLAGS+=-Wno-missing-field-initializers -Wimplicit -Wreturn-type -Wformat -Wswitch -Wshadow -Wwrite-strings +CFLAGS+=-DGCC_WARN + +# NetHack sources control +CFLAGS+=-DDLB +CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" +CFLAGS+=-DDLB +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE +#CFLAGS+=-DTIMED_DELAY +#CFLAGS+=-DDUMPLOG +#CFLAGS+=-DCONFIG_ERROR_SECURE=FALSE +CFLAGS+=-DGREPPATH=\"/usr/bin/grep\" +#CFLAGS+=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\" +# older binaries use NOCLIPPING, but that disables SIGWINCH +#CFLAGS+=-DNOCLIPPING +CFLAGS+=-DNOMAIL +#CFLAGS+=-DEXTRA_SANITY_CHECKS +#CFLAGS+=-DEDIT_GETLIN +#CFLAGS+=-DSCORE_ON_BOTL +#CFLAGS+=-DMSGHANDLER +#CFLAGS+=-DTTY_TILES_ESCCODES +#CFLAGS+=-DTTY_SOUND_ESCCODES + +#CFLAGS+=-DDEFAULT_WINDOW_SYS=\"shim\" -DNOTTYGRAPHICS -DLIBNH + +CFLAGS+= $(WINCFLAGS) #WINCFLAGS set from multiw-2.2020 + +VARDATND = +VARDATND0 = +CURSESLIB = + +ifdef WANT_WIN_CHAIN +HINTSRC=$(CHAINSRC) +HINTOBJ=$(CHAINOBJ) +endif # WANT_WIN_CHAIN + +ifdef WANT_WIN_TTY +CURSESLIB = -lncurses +endif + +ifdef WANT_WIN_CURSES +CURSESLIB = -lncurses +endif + +ifdef CURSESLIB +WINLIB += $(CURSESLIB) +endif + +ifdef WANT_WIN_X11 +USE_XPM=1 +WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 +VARDATND0 += x11tiles NetHack.ad pet_mark.xbm pilemark.xbm +# -x: if built without dlb, some versions of mkfontdir think *.lev are fonts +POSTINSTALL += bdftopcf win/X11/nh10.bdf > $(HACKDIR)/nh10.pcf; ( cd $(HACKDIR); mkfontdir -x .lev ); +# separate from CFLAGS so that we don't pass it to every file +X11CFLAGS = -I/opt/X11/include +# avoid repeated complaints about _X_NONNULL(args...) in +X11CFLAGS += -Wno-variadic-macros +ifdef USE_XPM +CFLAGS += -DUSE_XPM +WINX11LIB += -lXpm +VARDATND0 += rip.xpm +endif +WINLIB += $(WINX11LIB) +LFLAGS=-L/opt/X11/lib +endif # WANT_WIN_X11 + +ifdef WANT_WIN_QT +# Qt5 requires C++11 +LINK = $(CXX) +QTCXXFLAGS += -Wno-deprecated-declarations +QTCXXFLAGS += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --cflags) +WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --libs) +WINSRC += $(WINQTSRC) +WINOBJ0 += $(WINQTOBJ) +VARDATND0 += nhtiles.bmp rip.xpm nhsplash.xpm +# XXX if /Developer/qt exists and QTDIR not set, use that +ifndef QTDIR +$(error QTDIR not defined in the environment or Makefile) +endif # QTDIR +# XXX make sure QTDIR points to something reasonable +else # !WANT_WIN_QT +LINK=$(CC) +endif # !WANT_WIN_QT + +# prevent duplicate tile.o in WINOBJ +WINOBJ = $(sort $(WINOBJ0)) +# prevent duplicates in VARDATND if both X11 and Qt are being supported +VARDATND += $(sort $(VARDATND0)) + +WANT_BUNDLE=1 +ifdef WANT_SHARE_INSTALL +# if $GAMEUID is root, we install into roughly proper Mac locations, otherwise +# we install into ~/nethackdir +ifeq ($(GAMEUID),root) +PREFIX:=/Library/NetHack +SHELLDIR=/usr/local/bin +HACKDIR=$(PREFIX)/nethackdir +CHOWN=chown +CHGRP=chgrp +# We run sgid so the game has access to both HACKDIR and user preferences. +GAMEPERM = 02755 +else # ! root +PREFIX:=/Users/$(GAMEUID) +SHELLDIR=$(PREFIX)/bin +HACKDIR=$(PREFIX)/Library/NetHack/nethackdir +CHOWN=/usr/bin/true +CHGRP=/usr/bin/true +GAMEPERM = 0500 +endif # ! root +VARFILEPERM = 0664 +VARDIRPERM = 0775 +ROOTCHECK= [[ `id -u` == 0 ]] || ( echo "Must run install with sudo."; exit 1) +# XXX it's nice we don't write over sysconf, but we've already erased it +# make sure we have group GAMEUID and group GAMEGRP +PREINSTALL= . sys/unix/hints/macosx.sh user2 $(GAMEUID); \ + . sys/unix/hints/macosx.sh group2 $(GAMEGRP); \ + mkdir $(SHELLDIR); chown $(GAMEUID) $(SHELLDIR) +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ + $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ + $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ + chmod $(VARFILEPERM) $(HACKDIR)/sysconf; + +else ifdef WANT_SOURCE_INSTALL + +PREFIX=$(abspath $(NHSROOT)) +# suppress nethack.sh +#SHELLDIR= +HACKDIR=$(PREFIX)/playground +CHOWN=/usr/bin/true +CHGRP=/usr/bin/true +GAMEPERM = 0700 +VARFILEPERM = 0600 +VARDIRPERM = 0700 +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; +# We can use "make all" to build the whole thing - but it misses some things: +MOREALL=$(MAKE) install +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE + +else # !WANT_SOURCE_INSTALL + +PREFIX:=$(wildcard ~) +SHELLDIR=$(PREFIX)/bin +HACKDIR=$(PREFIX)/nethackdir +CHOWN=/usr/bin/true +CHGRP=/usr/bin/true +GAMEPERM = 0700 +VARFILEPERM = 0600 +VARDIRPERM = 0700 +ifdef ($(WANT_DEFAULT),X11) +# install nethack.rc as ~/.nethackrc if no ~/.nethackrc exists +PREINSTALL= cp -n win/X11/nethack.rc ~/.nethackrc || true +endif # WANT_DEFAULT X11 + +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ + $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ + $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ + chmod $(VARFILEPERM) $(HACKDIR)/sysconf; +ifdef WANT_BUNDLE +# +# Bundle +# +# $(HACKDIR)/$(GAME).app/ +# Contents/ +# Frameworks/ +# Info.plist +# MacOS/ +# $(GAME) +# PkgInfo/ +# PlugIns/ +# Resources/ +# SharedFrameWorks/ +# +BUNDLE = mkdir -p $(HACKDIR)/nethack.app/Contents/MacOS; \ + sys/unix/hints/macosx.sh infoplist > $(HACKDIR)/nethack.app/Contents/Info.plist; \ + mv $(HACKDIR)/nethack $(HACKDIR)/nethack.app/Contents/MacOS/nethack; +ifdef WANT_SHARE_INSTALL +BUNDLE+= chmod $(GAMEPERM) $(HACKDIR)/nethack.app/Contents/MacOS/nethack; +endif + +POSTINSTALL+= $(BUNDLE) +POSTINSTALL+= if test -f $(SHELLDIR)/$(GAME); then \ + sed -i '' 's;HACKDIR/$(GAME);HACKDIR/$(GAME).app/Contents/MacOS/$(GAME);' $(SHELLDIR)/$(GAME) ; fi; +endif # WANT_BUNDLE +endif # !WANT_SHARE_INSTALL + +INSTDIR=$(HACKDIR) +VARDIR=$(HACKDIR) + +# ~/Library/Preferences/NetHack Defaults +# OPTIONS=name:player,number_pad,menustyle:partial,!time,showexp +# OPTIONS=hilite_pet,toptenwin,msghistory:200,windowtype:Qt +# +# Install.Qt mentions a patch for macos - it's not there (it seems to be in the Qt binary +# package under the docs directory). + +#-POST +ifdef MAKEFILE_TOP +### +### Packaging +### +# Notes: +# 1) The Apple developer utilities must be installed in the default location. +# 2) Do a normal build before trying to package the game. +# 3) This matches the 3.4.3 Term package, but there are some things that +# should be changed. +# +# Packages that are being distributed must be signed by a Developer ID +# Installer certificate. Set DEVELOPER_CERT to the name of the certificate +# if you wish for your package to be signed for distribution. +# +# If building a package for signing, you must use sudo approriately. +# the binaries and package using sudo but you DO NOT use sudo to sign the +# package. If you use sudo to sign the package, it will fail. +# +# sudo make all +# sudo make build_tty_pkg +# make sign_tty_pkg +# + +ifdef WANT_WIN_TTY +DEVUTIL=/Developer/Applications/Utilities +SVS=$(shell $(NHSROOT)/util/makedefs --svs) +SVSDOT=$(shell $(NHSROOT)/util/makedefs --svs .) + +PKGROOT_UG = PKGROOT/$(PREFIX) +PKGROOT_UGLN = PKGROOT/$(HACKDIR) +PKGROOT_BIN = PKGROOT/$(SHELLDIR) + +#DEVELOPER_CERT = Developer ID Installer: Bart House +DEVELOPER_CERT = NONE + +spotless:: + rm -rf RESOURCES + rm -rf PKG + rm -rf PKGSCRIPTS + rm -rf PKGROOT + rm -f Info.plist + rm -f Distribution.xml + rm -f NetHack-*-mac-Term* + +build_tty_pkg: +ifneq (,$(WANT_WIN_X11)$(WANT_WIN_QT)) + -echo build_tty_pkg only works for a tty-only build + exit 1 +else + rm -rf NetHack-$(SVS)-mac-Term.pkg NetHack-$(SVS)-mac-Term.dmg + $(MAKE) build_package_root + rm -rf RESOURCES + mkdir RESOURCES + #enscript --language=rtf -o - < dat/license >RESOURCES/License.rtf + sys/unix/hints/macosx.sh descplist > RESOURCES/Description.plist + sys/unix/hints/macosx.sh infoplist > Info.plist + + mkdir PKGROOT/Applications + #osacompile -o NetHackQt/NetHackQt.app/nethackdir/NetHackRecover.app \ + # win/macosx/NetHackRecover.applescript + #cp win/macosx/recover.pl NetHackQt/NetHackQt.app/nethackdir + osacompile -o PKGROOT/Applications/NetHackRecover.app \ + win/macosx/NetHackRecover.applescript + cp win/macosx/recover.pl $(PKGROOT_UGLN) + + osacompile -o PKGROOT/Applications/NetHackTerm.app \ + win/macosx/NetHackTerm.applescript + + # XXX integrate into Makefile.doc + (cd doc; cat Guidebook.mn | ../util/makedefs --grep --input - --output - \ + | tbl tmac.n - | groff | pstopdf -i -o Guidebook.pdf) + cp doc/Guidebook.pdf $(PKGROOT_UG)/doc/NetHackGuidebook.pdf + + osacompile -o PKGROOT/Applications/NetHackGuidebook.app \ + win/macosx/NetHackGuidebook.applescript + + mkdir -p PKG + pkgbuild --root PKGROOT --identifier org.nethack.term --scripts PKGSCRIPTS PKG/NH-Term.pkg + productbuild --synthesize --product Info.plist --package PKG/NH-Term.pkg Distribution.xml + productbuild --distribution Distribution.xml --resources RESOURCES --package-path PKG NetHack-$(SVS)-mac-Term-unsigned.pkg +ifeq ($(DEVELOPER_CERT),NONE) + cp NetHack-$(SVS)-mac-Term-unsigned.pkg NetHack-$(SVS)-mac-Term.pkg + hdiutil create -verbose -srcfolder NetHack-$(SVS)-mac-Term-unsigned.pkg NetHack-$(SVS)-mac-Term-unsigned.dmg + @echo ------------------------------------------- + @echo PACKAGE IS NOT SIGNED FOR DISTRIBUTION!!!!! + @echo =========================================== +else + @echo "run 'make sign_tty_pkg' to complete package" +endif + +sign_tty_pkg: + productsign --timestamp=none --sign "$(DEVELOPER_CERT)" NetHack-$(SVS)-mac-Term-unsigned.pkg NetHack-$(SVS)-mac-Term.pkg || (echo "Package signing failed"; exit 1) + spctl -a -v --type install NetHack-$(SVS)-mac-Term.pkg || (echo "Package not signed properly"; exit 1) + hdiutil create -verbose -srcfolder NetHack-$(SVS)-mac-Term.pkg NetHack-$(SVS)-mac-Term.dmg + +build_package_root: + cd src/.. # make sure we are at TOP + rm -rf PKGROOT + mkdir -p $(PKGROOT_UG)/lib $(PKGROOT_BIN) $(PKGROOT_UG)/man/man6 $(PKGROOT_UG)/doc $(PKGROOT_UGLN) + install -p src/nethack $(PKGROOT_BIN) + # XXX should this be called nethackrecover? + install -p util/recover $(PKGROOT_BIN) + install -p doc/nethack.6 $(PKGROOT_UG)/man/man6 + install -p doc/recover.6 $(PKGROOT_UG)/man/man6 + install -p doc/Guidebook $(PKGROOT_UG)/doc + install -p dat/nhdat $(PKGROOT_UGLN) + sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(PKGROOT_UGLN)/sysconf + cd dat; install -p $(DATNODLB) ../$(PKGROOT_UGLN) +# XXX these files should be somewhere else for good Mac form + touch $(PKGROOT_UGLN)/perm $(PKGROOT_UGLN)/record $(PKGROOT_UGLN)/logfile $(PKGROOT_UGLN)/xlogfile + mkdir $(PKGROOT_UGLN)/save +# XXX what about a news file? + + mkdir -p PKGSCRIPTS + echo '#!/bin/sh' > PKGSCRIPTS/postinstall + echo dseditgroup -o create -r '"Games Group"' -s 3600 $(GAMEGRP) >> PKGSCRIPTS/postinstall + echo $(CHOWN) $(GAMEUID) $(HACKDIR) >> PKGSCRIPTS/postinstall + echo $(CHOWN) $(GAMEUID) $(HACKDIR)/* >> PKGSCRIPTS/postinstall + echo $(CHGRP) $(GAMEGRP) $(HACKDIR) >> PKGSCRIPTS/postinstall + echo $(CHGRP) $(GAMEGRP) $(HACKDIR)/* >> PKGSCRIPTS/postinstall + echo $(CHOWN) $(GAMEUID) $(SHELLDIR)/nethack >> PKGSCRIPTS/postinstall + echo $(CHGRP) $(GAMEGRP) $(SHELLDIR)/nethack >> PKGSCRIPTS/postinstall + echo $(CHOWN) $(GAMEUID) $(SHELLDIR)/recover >> PKGSCRIPTS/postinstall + echo $(CHGRP) $(GAMEGRP) $(SHELLDIR)/recover >> PKGSCRIPTS/postinstall + echo chmod $(VARDIRPERM) $(HACKDIR) >> PKGSCRIPTS/postinstall + echo chmod $(VARDIRPERM) $(HACKDIR)/save >> PKGSCRIPTS/postinstall + echo chmod $(FILEPERM) $(HACKDIR)/license >> PKGSCRIPTS/postinstall + echo chmod $(FILEPERM) $(HACKDIR)/nhdat >> PKGSCRIPTS/postinstall + echo chmod $(FILEPERM) $(HACKDIR)/symbols >> PKGSCRIPTS/postinstall + echo chmod $(VARFILEPERM) $(HACKDIR)/perm >> PKGSCRIPTS/postinstall + echo chmod $(VARFILEPERM) $(HACKDIR)/record >> PKGSCRIPTS/postinstall + echo chmod $(VARFILEPERM) $(HACKDIR)/logfile >> PKGSCRIPTS/postinstall + echo chmod $(VARFILEPERM) $(HACKDIR)/xlogfile >> PKGSCRIPTS/postinstall + echo chmod $(VARFILEPERM) $(HACKDIR)/sysconf >> PKGSCRIPTS/postinstall + echo chmod $(GAMEPERM) $(SHELLDIR)/nethack >> PKGSCRIPTS/postinstall + echo chmod $(EXEPERM) $(SHELLDIR)/recover >> PKGSCRIPTS/postinstall + chmod 0775 PKGSCRIPTS/postinstall + +endif # end of build_tty_pkg +endif # WANT_WIN_TTY for packaging + +ifdef WANT_WIN_QT +# XXX untested and incomplete (see below) +build_qt_pkg: +ifneq (,$(WANT_WIN_X11)$(WANT_WIN_TTY)) + -echo build_qt_pkg only works for a qt-only build + exit 1 +else + $(MAKE) build_package_root + rm -rf NetHackQt + mkdir -p NetHackQt/NetHackQt.app/nethackdir/save + mkdir NetHackQt/Documentation + cp doc/Guidebook.txt doc/nethack.txt doc/recover.txt NetHackQt/Documentation + + osacompile -o NetHackQt/NetHackQt.app/nethackdir/NetHackRecover.app \ + win/macosx/NetHackRecover.applescript + cp win/macosx/recover.pl NetHackQt/NetHackQt.app/nethackdir + + mkdir -p NetHackQt/NetHackQt.app/Contents/Frameworks + cp $(QTDIR)/libqt-mt.3.dylib NetHackQt/NetHackQt.app/Contents/Frameworks + + mkdir NetHackQt/NetHackQt.app/Contents/MacOS + mv PKGROOT/nethack NetHackQt/NetHackQt.app/Contents/MacOS + + mv PKGROOT/lib/nethackdir NetHackQt/NetHackQt.app/nethackdir + +# XXX still missing: +#NetHackQt/NetHackQt.app +# /Contents +# Info.plist +# Resources/nethack.icns +#NetHackQt/Documentation +#NetHackQtRecover.txt +#NetHack Defaults.txt +#changes.patch XXX is this still needed? why isn't it part of the tree? +# doesn't go here + hdiutil create -verbose -srcfolder NetHackQt NetHack-$(SVS)-macosx-qt.dmg +endif # end of build_qt_pkg +endif # WANT_WIN_QT for packaging +endif # MAKEFILE_TOP diff --git a/sys/lib/hints/wasm b/sys/lib/hints/wasm new file mode 100644 index 000000000..0591db7ba --- /dev/null +++ b/sys/lib/hints/wasm @@ -0,0 +1,153 @@ + +#-PRE xxxx +# enscripten WebAssembly config + +WANT_WASM=1 +WASM_DEBUG=1 +WASM_DATA_DIR=$(NHSROOT)/src/wasm-data/Users/ampower/nethackdir + +# toolchain +EMCC=emcc +EMAR=emar rcu +EMRANLIB=emranlib + +# link flags +EMCC_LFLAGS=-s SINGLE_FILE=1 +EMCC_LFLAGS=-s WASM=1 +EMCC_LFLAGS+=-s ALLOW_TABLE_GROWTH +EMCC_LFLAGS+=-s ASYNCIFY -s ASYNCIFY_IMPORTS='["_nhmain"]' -O3 +EMCC_LFLAGS+=-s MODULARIZE +EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main"]' +EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString"]' +EMCC_LFLAGS+=-s ERROR_ON_UNDEFINED_SYMBOLS=0 +EMCC_LFLAGS+=--embed-file wasm-data@/ + +# WASM C flags +EMCC_CFLAGS= +EMCC_CFLAGS+=-Wall -Werror +EMCC_CFLAGS+=-DWASM +EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=1 +EMCC_DEBUG_CFLAGS+=-s STACK_OVERFLOW_CHECK=2 +EMCC_DEBUG_CFLAGS+=-s SAFE_HEAP=1 +EMCC_DEBUG_CFLAGS+=-s LLD_REPORT_UNDEFINED +EMCC_PROD_CFLAGS+=-O3 + +# Nethack C flags +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE +CFLAGS+=-g -I../include -DNOTPARMDECL +CFLAGS+=-Wall -Werror +CFLAGS+=-DGCC_WARN + +# NetHack sources control +CFLAGS+=-DDLB +CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" +CFLAGS+=-DDLB +CFLAGS+=-DGREPPATH=\"/usr/bin/grep\" +CFLAGS+=-DNOMAIL + +ifdef WASM_DEBUG +EMCC_CFLAGS+=$(EMCC_DEBUG_CFLAGS) +else +EMCC_CFLAGS+=$(EMCC_PROD_CFLAGS) +endif + +ifdef WANT_SHARE_INSTALL +# if $GAMEUID is root, we install into roughly proper Mac locations, otherwise +# we install into ~/nethackdir +ifeq ($(GAMEUID),root) +PREFIX:=/Library/NetHack +SHELLDIR=/usr/local/bin +HACKDIR=$(PREFIX)/nethackdir +CHOWN=chown +CHGRP=chgrp +# We run sgid so the game has access to both HACKDIR and user preferences. +GAMEPERM = 02755 +else # ! root +PREFIX:=/Users/$(GAMEUID) +SHELLDIR=$(PREFIX)/bin +HACKDIR=$(PREFIX)/Library/NetHack/nethackdir +CHOWN=/usr/bin/true +CHGRP=/usr/bin/true +GAMEPERM = 0500 +endif # ! root +VARFILEPERM = 0664 +VARDIRPERM = 0775 +ROOTCHECK= [[ `id -u` == 0 ]] || ( echo "Must run install with sudo."; exit 1) +# XXX it's nice we don't write over sysconf, but we've already erased it +# make sure we have group GAMEUID and group GAMEGRP +PREINSTALL= . sys/unix/hints/macosx.sh user2 $(GAMEUID); \ + . sys/unix/hints/macosx.sh group2 $(GAMEGRP); \ + mkdir $(SHELLDIR); chown $(GAMEUID) $(SHELLDIR) +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ + $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ + $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ + chmod $(VARFILEPERM) $(HACKDIR)/sysconf; + +else ifdef WANT_SOURCE_INSTALL + +PREFIX=$(abspath $(NHSROOT)) +# suppress nethack.sh +#SHELLDIR= +HACKDIR=$(PREFIX)/playground +CHOWN=/usr/bin/true +CHGRP=/usr/bin/true +GAMEPERM = 0700 +VARFILEPERM = 0600 +VARDIRPERM = 0700 +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; +# We can use "make all" to build the whole thing - but it misses some things: +MOREALL=$(MAKE) install +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE + +else # !WANT_SOURCE_INSTALL + +PREFIX:=$(wildcard ~) +SHELLDIR=$(PREFIX)/bin +HACKDIR=$(PREFIX)/nethackdir +CHOWN=/usr/bin/true +CHGRP=/usr/bin/true +GAMEPERM = 0700 +VARFILEPERM = 0600 +VARDIRPERM = 0700 +ifdef ($(WANT_DEFAULT),X11) +# install nethack.rc as ~/.nethackrc if no ~/.nethackrc exists +PREINSTALL= cp -n win/X11/nethack.rc ~/.nethackrc || true +endif # WANT_DEFAULT X11 + +POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ + $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ + $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ + chmod $(VARFILEPERM) $(HACKDIR)/sysconf; +ifdef WANT_BUNDLE +# +# Bundle +# +# $(HACKDIR)/$(GAME).app/ +# Contents/ +# Frameworks/ +# Info.plist +# MacOS/ +# $(GAME) +# PkgInfo/ +# PlugIns/ +# Resources/ +# SharedFrameWorks/ +# +BUNDLE = mkdir -p $(HACKDIR)/nethack.app/Contents/MacOS; \ + sys/unix/hints/macosx.sh infoplist > $(HACKDIR)/nethack.app/Contents/Info.plist; \ + mv $(HACKDIR)/nethack $(HACKDIR)/nethack.app/Contents/MacOS/nethack; +ifdef WANT_SHARE_INSTALL +BUNDLE+= chmod $(GAMEPERM) $(HACKDIR)/nethack.app/Contents/MacOS/nethack; +endif + +POSTINSTALL+= $(BUNDLE) +POSTINSTALL+= if test -f $(SHELLDIR)/$(GAME); then \ + sed -i '' 's;HACKDIR/$(GAME);HACKDIR/$(GAME).app/Contents/MacOS/$(GAME);' $(SHELLDIR)/$(GAME) ; fi; +endif # WANT_BUNDLE +endif # !WANT_SHARE_INSTALL + +INSTDIR=$(HACKDIR) +VARDIR=$(HACKDIR) + +#-POST +# no post \ No newline at end of file diff --git a/sys/lib/libnethackmain.c b/sys/lib/libnethackmain.c new file mode 100644 index 000000000..ed8142bd7 --- /dev/null +++ b/sys/lib/libnethackmain.c @@ -0,0 +1,824 @@ +/* NetHack 3.7 unixmain.c $NHDT-Date: 1596498297 2020/08/03 23:44:57 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.87 $ */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/*-Copyright (c) Robert Patrick Rankin, 2011. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* main.c - Unix NetHack */ + +#include "hack.h" +#include "dlb.h" + +#include +#include +#include +#include +#ifndef O_RDONLY +#include +#endif + +/* for cross-compiling to WebAssembly (WASM) */ +#ifdef __EMSCRIPTEN__ +#include +#endif + +#if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) +#if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) +#if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) +extern struct passwd *FDECL(getpwuid, (uid_t)); +#else +extern struct passwd *FDECL(getpwuid, (int)); +#endif +#endif +#endif +extern struct passwd *FDECL(getpwnam, (const char *)); +#ifdef CHDIR +static void FDECL(chdirx, (const char *, BOOLEAN_P)); +#endif /* CHDIR */ +static boolean NDECL(whoami); +static void FDECL(process_options, (int, char **)); + +#ifdef _M_UNIX +extern void NDECL(check_sco_console); +extern void NDECL(init_sco_cons); +#endif +#ifdef __linux__ +extern void NDECL(check_linux_console); +extern void NDECL(init_linux_cons); +#endif + +static void NDECL(wd_message); +static boolean wiz_error_flag = FALSE; +static struct passwd *NDECL(get_unix_pw); + +#ifdef __EMSCRIPTEN__ +/* if WebAssembly, export this API and don't optimize it out */ +EMSCRIPTEN_KEEPALIVE +int +main(argc, argv) +int argc; +char *argv[]; + +#else /* !__EMSCRIPTEN__ */ + +int +nhmain(argc, argv) +int argc; +char *argv[]; + +#endif /* __EMSCRIPTEN__ */ +{ +#ifdef CHDIR + register char *dir; +#endif + NHFILE *nhfp; + boolean exact_username; + boolean resuming = FALSE; /* assume new game */ + boolean plsel_once = FALSE; + int i; + + printf ("nhmain\n"); + printf ("argc: %d\n", argc); + printf ("argv: %p\n", (void *)argv); + for (i = 0; i < argc; i++) { + printf ("argv[%d]: %s\n", i, argv[i]); + } + + early_init(); + +#if 0 /* __APPLE__ */ + { +/* special hack to change working directory to a resource fork when + running from finder --sam */ +#define MAC_PATH_VALUE ".app/Contents/MacOS/" + char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; + int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0; + getcwd(mac_cwd, 1024); + if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { + if ((mac_exe = strrchr(mac_exe, '/'))) + mac_exe++; + else + mac_exe = argv[0]; + mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); + if (mac_tmp_len <= arg0_len) { + mac_tmp = malloc(mac_tmp_len + 1); + sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); + if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { + mac_lhs_len = + (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; + if (mac_lhs_len > mac_tmp_len - 1) + mac_tmp = realloc(mac_tmp, mac_lhs_len); + strncpy(mac_tmp, argv[0], mac_lhs_len); + mac_tmp[mac_lhs_len] = '\0'; + chdir(mac_tmp); + } + free(mac_tmp); + } + } + } +#endif /* __APPLE__ */ + + g.hname = argv[0]; + g.hackpid = getpid(); + (void) umask(0777 & ~FCMASK); + + choose_windows(DEFAULT_WINDOW_SYS); + +#ifdef CHDIR /* otherwise no chdir() */ + /* + * See if we must change directory to the playground. + * (Perhaps hack runs suid and playground is inaccessible + * for the player.) + * The environment variable HACKDIR is overridden by a + * -d command line option (must be the first option given). + */ + dir = nh_getenv("NETHACKDIR"); + if (!dir) + dir = nh_getenv("HACKDIR"); + + if (argc > 1) { + if (argcheck(argc, argv, ARG_VERSION) == 2) + exit(EXIT_SUCCESS); + + if (argcheck(argc, argv, ARG_SHOWPATHS) == 2) { +#ifdef CHDIR + chdirx((char *) 0, 0); +#endif + iflags.initoptions_noterminate = TRUE; + initoptions(); + iflags.initoptions_noterminate = FALSE; + reveal_paths(); + exit(EXIT_SUCCESS); + } + if (argcheck(argc, argv, ARG_DEBUG) == 1) { + argc--; + argv++; + } + if (argc > 1 && !strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { + /* avoid matching "-dec" for DECgraphics; since the man page + * says -d directory, hope nobody's using -desomething_else + */ + argc--; + argv++; + dir = argv[0] + 2; + if (*dir == '=' || *dir == ':') + dir++; + if (!*dir && argc > 1) { + argc--; + argv++; + dir = argv[0]; + } + if (!*dir) + error("Flag -d must be followed by a directory name."); + } + } +#endif /* CHDIR */ + + if (argc > 1) { + /* + * Now we know the directory containing 'record' and + * may do a prscore(). Exclude `-style' - it's a Qt option. + */ + if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) { +#ifdef CHDIR + chdirx(dir, 0); +#endif +#ifdef SYSCF + initoptions(); +#endif +#ifdef PANICTRACE + ARGV0 = g.hname; /* save for possible stack trace */ +#ifndef NO_SIGNAL + panictrace_setsignals(TRUE); +#endif +#endif + prscore(argc, argv); + /* FIXME: shouldn't this be using nh_terminate() to free + up any memory allocated by initoptions() */ + exit(EXIT_SUCCESS); + } + } /* argc > 1 */ + +/* + * Change directories before we initialize the window system so + * we can find the tile file. + */ +#ifdef CHDIR + chdirx(dir, 1); +#endif + +#ifdef _M_UNIX + check_sco_console(); +#endif +#ifdef __linux__ + check_linux_console(); +#endif + initoptions(); +#ifdef PANICTRACE + ARGV0 = g.hname; /* save for possible stack trace */ +#ifndef NO_SIGNAL + panictrace_setsignals(TRUE); +#endif +#endif + exact_username = whoami(); + + /* + * It seems you really want to play. + */ + u.uhp = 1; /* prevent RIP on early quits */ + g.program_state.preserve_locks = 1; +#ifndef NO_SIGNAL + sethanguphandler((SIG_RET_TYPE) hangup); +#endif + + process_options(argc, argv); /* command line options */ +#ifdef WINCHAIN + commit_windowchain(); +#endif + init_nhwindows(&argc, argv); /* now we can set up window system */ +#ifdef _M_UNIX + init_sco_cons(); +#endif +#ifdef __linux__ + init_linux_cons(); +#endif + +#ifdef DEF_PAGER + if (!(g.catmore = nh_getenv("HACKPAGER")) + && !(g.catmore = nh_getenv("PAGER"))) + g.catmore = DEF_PAGER; +#endif +#ifdef MAIL + getmailstatus(); +#endif + + /* wizard mode access is deferred until here */ + set_playmode(); /* sets plname to "wizard" for wizard mode */ + /* hide any hyphens from plnamesuffix() */ + g.plnamelen = exact_username ? (int) strlen(g.plname) : 0; + /* strip role,race,&c suffix; calls askname() if plname[] is empty + or holds a generic user name like "player" or "games" */ + plnamesuffix(); + + if (wizard) { + /* use character name rather than lock letter for file names */ + g.locknum = 0; + } else { + /* suppress interrupts while processing lock file */ + (void) signal(SIGQUIT, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + } + + dlb_init(); /* must be before newgame() */ + + /* + * Initialize the vision system. This must be before mklev() on a + * new game or before a level restore on a saved game. + */ + vision_init(); + + display_gamewindows(); + + /* + * First, try to find and restore a save file for specified character. + * We'll return here if new game player_selection() renames the hero. + */ + attempt_restore: + + /* + * getlock() complains and quits if there is already a game + * in progress for current character name (when g.locknum == 0) + * or if there are too many active games (when g.locknum > 0). + * When proceeding, it creates an empty .0 file to + * designate the current game. + * getlock() constructs based on the character + * name (for !g.locknum) or on first available of alock, block, + * clock, &c not currently in use in the playground directory + * (for g.locknum > 0). + */ + if (*g.plname) { + getlock(); + g.program_state.preserve_locks = 0; /* after getlock() */ + } + + if (*g.plname && (nhfp = restore_saved_game()) != 0) { + const char *fq_save = fqname(g.SAVEF, SAVEPREFIX, 1); + + (void) chmod(fq_save, 0); /* disallow parallel restores */ +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif +#ifdef NEWS + if (iflags.news) { + display_file(NEWS, FALSE); + iflags.news = FALSE; /* in case dorecover() fails */ + } +#endif + pline("Restoring save file..."); + mark_synch(); /* flush output */ + if (dorecover(nhfp)) { + resuming = TRUE; /* not starting new game */ + wd_message(); + if (discover || wizard) { + /* this seems like a candidate for paranoid_confirmation... */ + if (yn("Do you want to keep the save file?") == 'n') { + (void) delete_savefile(); + } else { + (void) chmod(fq_save, FCMASK); /* back to readable */ + nh_compress(fq_save); + } + } + } + } + + if (!resuming) { + boolean neednewlock = (!*g.plname); + /* new game: start by choosing role, race, etc; + player might change the hero's name while doing that, + in which case we try to restore under the new name + and skip selection this time if that didn't succeed */ + if (!iflags.renameinprogress || iflags.defer_plname || neednewlock) { + if (!plsel_once) + player_selection(); + plsel_once = TRUE; + if (neednewlock && *g.plname) + goto attempt_restore; + if (iflags.renameinprogress) { + /* player has renamed the hero while selecting role; + if locking alphabetically, the existing lock file + can still be used; otherwise, discard current one + and create another for the new character name */ + if (!g.locknum) { + delete_levelfile(0); /* remove empty lock file */ + getlock(); + } + goto attempt_restore; + } + } + newgame(); + wd_message(); + } + + /* moveloop() never returns but isn't flagged NORETURN */ + moveloop(resuming); + + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +/* caveat: argv elements might be arbitrary long */ +static void +process_options(argc, argv) +int argc; +char *argv[]; +{ + int i, l; + + /* + * Process options. + */ + while (argc > 1 && argv[1][0] == '-') { + argv++; + argc--; + l = (int) strlen(*argv); + /* must supply at least 4 chars to match "-XXXgraphics" */ + if (l < 4) + l = 4; + + switch (argv[0][1]) { + case 'D': + case 'd': + if ((argv[0][1] == 'D' && !argv[0][2]) + || !strcmpi(*argv, "-debug")) { + wizard = TRUE, discover = FALSE; + } else if (!strncmpi(*argv, "-DECgraphics", l)) { + load_symset("DECGraphics", PRIMARY); + switch_symbols(TRUE); + } else { + raw_printf("Unknown option: %.60s", *argv); + } + break; + case 'X': + discover = TRUE, wizard = FALSE; + break; +#ifdef NEWS + case 'n': + iflags.news = FALSE; + break; +#endif + case 'u': + if (argv[0][2]) { + (void) strncpy(g.plname, argv[0] + 2, sizeof g.plname - 1); + g.plnamelen = 0; /* plname[] might have -role-race attached */ + } else if (argc > 1) { + argc--; + argv++; + (void) strncpy(g.plname, argv[0], sizeof g.plname - 1); + g.plnamelen = 0; + } else { + raw_print("Player name expected after -u"); + } + break; + case 'I': + case 'i': + if (!strncmpi(*argv, "-IBMgraphics", l)) { + load_symset("IBMGraphics", PRIMARY); + load_symset("RogueIBM", ROGUESET); + switch_symbols(TRUE); + } else { + raw_printf("Unknown option: %.60s", *argv); + } + break; + case 'p': /* profession (role) */ + if (argv[0][2]) { + if ((i = str2role(&argv[0][2])) >= 0) + flags.initrole = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2role(argv[0])) >= 0) + flags.initrole = i; + } + break; + case 'r': /* race */ + if (argv[0][2]) { + if ((i = str2race(&argv[0][2])) >= 0) + flags.initrace = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2race(argv[0])) >= 0) + flags.initrace = i; + } + break; + case 'w': /* windowtype */ + config_error_init(FALSE, "command line", FALSE); + choose_windows(&argv[0][2]); + config_error_done(); + break; + case '@': + flags.randomall = 1; + break; + default: + if ((i = str2role(&argv[0][1])) >= 0) { + flags.initrole = i; + break; + } + /* else raw_printf("Unknown option: %.60s", *argv); */ + } + } + +#ifdef SYSCF + if (argc > 1) + raw_printf("MAXPLAYERS are set in sysconf file.\n"); +#else + /* XXX This is deprecated in favor of SYSCF with MAXPLAYERS */ + if (argc > 1) + g.locknum = atoi(argv[1]); +#endif +#ifdef MAX_NR_OF_PLAYERS + /* limit to compile-time limit */ + if (!g.locknum || g.locknum > MAX_NR_OF_PLAYERS) + g.locknum = MAX_NR_OF_PLAYERS; +#endif +#ifdef SYSCF + /* let syscf override compile-time limit */ + if (!g.locknum || (sysopt.maxplayers && g.locknum > sysopt.maxplayers)) + g.locknum = sysopt.maxplayers; +#endif +} + +#ifdef CHDIR +static void +chdirx(dir, wr) +const char *dir; +boolean wr; +{ + if (dir /* User specified directory? */ +#ifdef HACKDIR + && strcmp(dir, HACKDIR) /* and not the default? */ +#endif + ) { +#ifdef SECURE + (void) setgid(getgid()); + (void) setuid(getuid()); /* Ron Wessels */ +#endif + } else { + /* non-default data files is a sign that scores may not be + * compatible, or perhaps that a binary not fitting this + * system's layout is being used. + */ +#ifdef VAR_PLAYGROUND + int len = strlen(VAR_PLAYGROUND); + + g.fqn_prefix[SCOREPREFIX] = (char *) alloc(len + 2); + Strcpy(g.fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND); + if (g.fqn_prefix[SCOREPREFIX][len - 1] != '/') { + g.fqn_prefix[SCOREPREFIX][len] = '/'; + g.fqn_prefix[SCOREPREFIX][len + 1] = '\0'; + } + +#endif + } + +#ifdef HACKDIR + if (dir == (const char *) 0) + dir = HACKDIR; +#endif + + if (dir && chdir(dir) < 0) { + perror(dir); + error("Cannot chdir to %s.", dir); + } + + /* warn the player if we can't write the record file + * perhaps we should also test whether . is writable + * unfortunately the access system-call is worthless. + */ + if (wr) { +#ifdef VAR_PLAYGROUND + g.fqn_prefix[LEVELPREFIX] = g.fqn_prefix[SCOREPREFIX]; + g.fqn_prefix[SAVEPREFIX] = g.fqn_prefix[SCOREPREFIX]; + g.fqn_prefix[BONESPREFIX] = g.fqn_prefix[SCOREPREFIX]; + g.fqn_prefix[LOCKPREFIX] = g.fqn_prefix[SCOREPREFIX]; + g.fqn_prefix[TROUBLEPREFIX] = g.fqn_prefix[SCOREPREFIX]; +#endif + check_recordfile(dir); + } +} +#endif /* CHDIR */ + +/* returns True iff we set plname[] to username which contains a hyphen */ +static boolean +whoami() +{ + /* + * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS + * 2. Use $USER or $LOGNAME (if 1. fails) + * 3. Use getlogin() (if 2. fails) + * The resulting name is overridden by command line options. + * If everything fails, or if the resulting name is some generic + * account like "games", "play", "player", "hack" then eventually + * we'll ask him. + * Note that we trust the user here; it is possible to play under + * somebody else's name. + */ + if (!*g.plname) { + register const char *s; + + s = nh_getenv("USER"); + if (!s || !*s) + s = nh_getenv("LOGNAME"); + if (!s || !*s) + s = getlogin(); + + if (s && *s) { + (void) strncpy(g.plname, s, sizeof g.plname - 1); + if (index(g.plname, '-')) + return TRUE; + } + } + return FALSE; +} + +void +sethanguphandler(handler) +void FDECL((*handler), (int)); +{ +#ifdef SA_RESTART + /* don't want reads to restart. If SA_RESTART is defined, we know + * sigaction exists and can be used to ensure reads won't restart. + * If it's not defined, assume reads do not restart. If reads restart + * and a signal occurs, the game won't do anything until the read + * succeeds (or the stream returns EOF, which might not happen if + * reading from, say, a window manager). */ + struct sigaction sact; + + (void) memset((genericptr_t) &sact, 0, sizeof sact); + sact.sa_handler = (SIG_RET_TYPE) handler; + (void) sigaction(SIGHUP, &sact, (struct sigaction *) 0); +#ifdef SIGXCPU + (void) sigaction(SIGXCPU, &sact, (struct sigaction *) 0); +#endif +#else /* !SA_RESTART */ + (void) signal(SIGHUP, (SIG_RET_TYPE) handler); +#ifdef SIGXCPU + (void) signal(SIGXCPU, (SIG_RET_TYPE) handler); +#endif +#endif /* ?SA_RESTART */ +} + +#ifdef PORT_HELP +void +port_help() +{ + /* + * Display unix-specific help. Just show contents of the helpfile + * named by PORT_HELP. + */ + display_file(PORT_HELP, TRUE); +} +#endif + +/* validate wizard mode if player has requested access to it */ +boolean +authorize_wizard_mode() +{ + struct passwd *pw = get_unix_pw(); + + if (pw && sysopt.wizards && sysopt.wizards[0]) { + if (check_user_string(sysopt.wizards)) + return TRUE; + } + wiz_error_flag = TRUE; /* not being allowed into wizard mode */ + return FALSE; +} + +static void +wd_message() +{ + if (wiz_error_flag) { + if (sysopt.wizards && sysopt.wizards[0]) { + char *tmp = build_english_list(sysopt.wizards); + pline("Only user%s %s may access debug (wizard) mode.", + index(sysopt.wizards, ' ') ? "s" : "", tmp); + free(tmp); + } else + pline("Entering explore/discovery mode instead."); + wizard = 0, discover = 1; /* (paranoia) */ + } else if (discover) + You("are in non-scoring explore/discovery mode."); +} + +/* + * Add a slash to any name not ending in /. There must + * be room for the / + */ +void +append_slash(name) +char *name; +{ + char *ptr; + + if (!*name) + return; + ptr = name + (strlen(name) - 1); + if (*ptr != '/') { + *++ptr = '/'; + *++ptr = '\0'; + } + return; +} + +boolean +check_user_string(optstr) +char *optstr; +{ + struct passwd *pw; + int pwlen; + char *eop, *w; + char *pwname = 0; + + if (optstr[0] == '*') + return TRUE; /* allow any user */ + if (sysopt.check_plname) + pwname = g.plname; + else if ((pw = get_unix_pw()) != 0) + pwname = pw->pw_name; + if (!pwname || !*pwname) + return FALSE; + pwlen = (int) strlen(pwname); + eop = eos(optstr); + w = optstr; + while (w + pwlen <= eop) { + if (!*w) + break; + if (isspace(*w)) { + w++; + continue; + } + if (!strncmp(w, pwname, pwlen)) { + if (!w[pwlen] || isspace(w[pwlen])) + return TRUE; + } + while (*w && !isspace(*w)) + w++; + } + return FALSE; +} + +static struct passwd * +get_unix_pw() +{ + char *user; + unsigned uid; + static struct passwd *pw = (struct passwd *) 0; + + if (pw) + return pw; /* cache answer */ + + uid = (unsigned) getuid(); + user = getlogin(); + if (user) { + pw = getpwnam(user); + if (pw && ((unsigned) pw->pw_uid != uid)) + pw = 0; + } + if (pw == 0) { + user = nh_getenv("USER"); + if (user) { + pw = getpwnam(user); + if (pw && ((unsigned) pw->pw_uid != uid)) + pw = 0; + } + if (pw == 0) { + pw = getpwuid(uid); + } + } + return pw; +} + +char * +get_login_name() +{ + static char buf[BUFSZ]; + struct passwd *pw = get_unix_pw(); + + buf[0] = '\0'; + if (pw) + (void)strcpy(buf, pw->pw_name); + + return buf; +} + +#if 0 /* __APPLE__ */ +extern int errno; + +void +port_insert_pastebuf(buf) +char *buf; +{ + /* This should be replaced when there is a Cocoa port. */ + const char *errfmt; + size_t len; + FILE *PB = popen("/usr/bin/pbcopy", "w"); + + if (!PB) { + errfmt = "Unable to start pbcopy (%d)\n"; + goto error; + } + + len = strlen(buf); + /* Remove the trailing \n, carefully. */ + if (buf[len - 1] == '\n') + len--; + + /* XXX Sorry, I'm too lazy to write a loop for output this short. */ + if (len != fwrite(buf, 1, len, PB)) { + errfmt = "Error sending data to pbcopy (%d)\n"; + goto error; + } + + if (pclose(PB) != -1) { + return; + } + errfmt = "Error finishing pbcopy (%d)\n"; + + error: + raw_printf(errfmt, strerror(errno)); +} +#endif /* __APPLE__ */ + +unsigned long +sys_random_seed() +{ + unsigned long seed = 0L; + unsigned long pid = (unsigned long) getpid(); + boolean no_seed = TRUE; +#ifdef DEV_RANDOM + FILE *fptr; + + fptr = fopen(DEV_RANDOM, "r"); + if (fptr) { + fread(&seed, sizeof (long), 1, fptr); + has_strong_rngseed = TRUE; /* decl.c */ + no_seed = FALSE; + (void) fclose(fptr); + } else { + /* leaves clue, doesn't exit */ + paniclog("sys_random_seed", "falling back to weak seed"); + } +#endif + if (no_seed) { + seed = (unsigned long) getnow(); /* time((TIME_type) 0) */ + /* Quick dirty band-aid to prevent PRNG prediction */ + if (pid) { + if (!(pid & 3L)) + pid -= 1L; + seed *= pid; + } + } + return seed; +} + +/*unixmain.c*/ diff --git a/sys/lib/mkmkfile.sh b/sys/lib/mkmkfile.sh new file mode 100755 index 000000000..e1afc0e15 --- /dev/null +++ b/sys/lib/mkmkfile.sh @@ -0,0 +1,44 @@ +#!/bin/sh +# NetHack 3.7 mkmkfile.sh $NHDT-Date: 1597332770 2020/08/13 15:32:50 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.15 $ +# Copyright (c) Kenneth Lorber, Kensington, Maryland, 2007. +# NetHack may be freely redistributed. See license for details. + +# build one makefile +# args are: +# $1 basefile +# $2 basefile tag +# $3 install path +# $4 hints file (path) +# $5 hints file (as given by user) + +echo "#" > $3 +echo "# This file is generated automatically. Do not edit." >> $3 +echo "# Your changes will be lost." >> $3 +echo "# Identify this file:" >> $3 +echo "MAKEFILE_$2=1" >> $3 +echo "" >> $3 + +echo "###" >> $3 +echo "### Start $5 PRE" >> $3 +echo "###" >> $3 +awk '/^#-PRE/,/^#-POST/{ \ + if(index($0, "#-PRE") == 1) print "# (new segment at source line",NR,")"; \ + if(index($0, "#-INCLUDE") == 1) system("cat hints/include/"$2); \ + else if(index($0, "#-P") != 1) print}' $4 >> $3 +echo "### End $5 PRE" >> $3 +echo "" >> $3 + +echo "###" >> $3 +echo "### Start $1" >> $3 +echo "###" >> $3 +cat $1 >> $3 +echo "### End $1" >> $3 +echo "" >> $3 + +echo "###" >> $3 +echo "### Start $5 POST" >> $3 +echo "###" >> $3 +awk '/^#-POST/,/^#-PRE/{ \ + if(index($0, "#-POST") == 1) print "# (new segment at source line",NR,")"; \ + if(index($0, "#-P") != 1) print}' $4 >> $3 +echo "### End $5 POST" >> $3 diff --git a/sys/lib/setup.sh b/sys/lib/setup.sh new file mode 100755 index 000000000..2db51ba69 --- /dev/null +++ b/sys/lib/setup.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# NetHack 3.7 setup.sh $NHDT-Date: 1596498296 2020/08/03 23:44:56 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.17 $ +# Copyright (c) Kenneth Lorber, Kensington, Maryland, 2007. +# NetHack may be freely redistributed. See license for details. +# +# Build and install makefiles. +# +# Argument is the hints file to use (or no argument for traditional setup). +# e.g.: +# sh setup.sh +# or +# sh setup.sh hints/macosx10.5 (from sys/unix) +# or +# sh setup.sh sys/unix/hints/macosx10.5 (from top) + +# Were we started from the top level? Cope. +prefix=. +if [ -f sys/unix/Makefile.top ]; then cd sys/unix; prefix=../..; fi + +case "x$1" in +x) hints=/dev/null + hfile=/dev/null + ;; +*) hints=$prefix/$1 + hfile=$1 + # sanity check + if [ ! -f "$hints" ]; then + echo "Cannot find hints file $hfile" + exit 1 + fi + ;; +esac + +/bin/sh ./mkmkfile.sh Makefile.top TOP ../../Makefile $hints $hfile +/bin/sh ./mkmkfile.sh Makefile.dat DAT ../../dat/Makefile $hints $hfile +/bin/sh ./mkmkfile.sh Makefile.src SRC ../../src/Makefile $hints $hfile +/bin/sh ./mkmkfile.sh Makefile.utl UTL ../../util/Makefile $hints $hfile diff --git a/sys/lib/sysconf b/sys/lib/sysconf new file mode 100644 index 000000000..77b4c383f --- /dev/null +++ b/sys/lib/sysconf @@ -0,0 +1,150 @@ +# NetHack 3.7 sysconf $NHDT-Date: 1596498296 2020/08/03 23:44:56 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.39 $ +# Copyright (c) 2015 by Kenneth Lorber, Kensington, Maryland +# NetHack may be freely redistributed. See license for details. +# +# Sample sysconf file. +# The sysconf file is only used if NetHack is compiled with SYSCF defined. +# It can be used to augment or override certain settings compiled into the +# program. +# +# This file can also be used to set local system defaults for run-time +# options, using the same syntax as an individual user's ./nethackrc file. + +# Which users can use debug mode (aka wizard mode; accessed via '-D' command +# line flag or OPTIONS=playmode:debug in the runtime options config file). +# A value of * allows anyone to enter debugging mode. +WIZARDS=root games + +# Which users can use explore mode (aka discover mode; accessed via '-X' +# command line flag or OPTIONS=playmode:explore in runtime options file or +# via '#exploremode' command during normal play). Same syntax as WIZARDS. +EXPLORERS=* + +# Users allowed to use the '!' (shell escape) and '^Z' (suspend process) +# commands to temporarily leave the game and enter a shell process. +# (To resume play, use the shell command 'exit' (for most shells) to +# return from '!' or the shell command 'fg' to return from '^Z'. +# For the typical multi-user system where players have access to a shell +# prompt when logged in and run the game from their own username, a value +# of 'SHELLERS=*' is appropriate. However, some inexperienced players +# occasionally get stuck outside the game by accidentally typing '!' or +# '^Z' during play and not knowing how to go back.) +# Uses the same syntax as the WIZARDS and EXPLORERS options above. +#SHELLERS= + +# If the user name is found in this list, prompt for username instead. +# Uses the same syntax as the WIZARDS option above. +# A public server should probably disable this. +# ["ec2-user" is the default user name on Amazon Linux] +GENERICUSERS=play player game games nethack nethacker ec2-user + +# Use the player name for matching WIZARDS, EXPLORERS and SHELLERS, +# instead of the user's login name. +#CHECK_PLNAME=1 + +# Limit the number of simultaneous games (see also nethack.sh). +# Valid values are 0-25. +# Commenting this out or setting the value to 0 constructs lock files +# with UID and playername, so each user may have one game at a time, +# but number of different players is not limited. +# Setting this to any other value constructs the lock files with +# letter and "lock" (eg. alock, block, ...) +MAXPLAYERS=10 + +# If not null, added to string "To get local support, " in the support +# information help. +#SUPPORT=call Izchak at extension 42. + +# If not null, displayed at the end of a panic-save sequence. +#RECOVER=Run the recover program. + +# Uncomment the next line to disable the SEDUCE option, causing succubi and +# incubi to use nymphs' charm behavior rather than their own seduce behavior. +#SEDUCE=0 + +# Uncomment the next line to enable some accessibility features such +# as S_hero_override and S_pet_override symbols for screen readers +# in the user config file. +#ACCESSIBILITY=1 + +# Uncomment to disable savefile UID checking. +#CHECK_SAVE_UID=0 + +# Record (high score) file options. +# CAUTION: changing these after people have started playing games can +# lead to lost high scores! +# Maximum entries for one person. +#PERSMAX=10 +# Maximum entries in the record file. +#ENTRYMAX=100 +# Minimum points to get an entry. +#POINTSMIN=1 +# Determine identity of "person" in the score file with name (0) or +# numeric (1) user id. +#PERS_IS_UID=1 + +# Maximum number of score file entries to use for random statue names +#MAX_STATUENAME_RANK=10 + +# Show debugging information originating from these source files. +# Use '*' for all, or list source files separated by spaces. +# Only available if game has been compiled with DEBUG, and can be +# overridden via DEBUGFILES environment variable. +#DEBUGFILES=* + +# Save end of game dump log to this file. +# Only available if NetHack was compiled with DUMPLOG +# Allows following placeholders: +# %% literal '%' +# %v version (eg. "3.7.0-0") +# %u game UID +# %t game start time, UNIX timestamp format +# %T current time, UNIX timestamp format +# %d game start time, YYYYMMDDhhmmss format +# %D current time, YYYYMMDDhhmmss format +# %n player name +# %N first character of player name +#DUMPLOGFILE=/tmp/nethack.%n.%d.log + +# Number of bones file pools. +# The pool you belong to is determined at game start. You will +# load and save bones only from that pool. Generally useful +# for public servers only. +# Changing this might make existing bones inaccessible. +# Disabled by setting to 0, or commenting out. +#BONES_POOLS=10 + +# Try to get more info in case of a program bug or crash. Only used +# if the program is built with the PANICTRACE compile-time option enabled. +# By default PANICTRACE is enabled if (NH_DEVEL_STATUS != NH_STATUS_RELEASED), +# otherwise disabled. +# Using GDB can get more information and works on more systems but requires +# 'gdb' be available; using LIBC only works if NetHack is linked with a +# libc that supports the backtrace(3) API. Both require certain compilation +# options. See src/end.c and sys/unix/hints/* for more information. +#GDBPATH=/usr/bin/gdb +#GREPPATH=/bin/grep +# Values are priorities: 0 - do not use this method, 1 - low priority, +# 2 - high priority. Non-zero priority methods are tried in order. +PANICTRACE_GDB=0 +PANICTRACE_LIBC=0 + +# 'portable_device_paths' is only supported for Windows. Starting with +# 3.6.3, nethack on Windows treats the folder containing nethack.exe and +# nethackW.exe as read-only and puts data files which are generated or +# modified during play or by the user in assorted folders derived from +# user name. 3.6.4 added PORTABLE_DEVICE_PATHS to allow reverting to +# the old behavior of having the run-time configuration file and other +# data in the same directory as the executable so that the whole thing +# can be moved from one machine to another (flash drive or perhaps cloud) +# without updating folder paths. +#PORTABLE_DEVICE_PATHS=0 + +# Ordinary run-time options can be set here to override the builtin-in +# default values. Unlike all the SYSCF values above, individual users +# can override the overridden options set here by choosing their own +# option settings via NETHACKOPTIONS in their environment or via +# ~/.nethackrc run-time configuration file. +#OPTIONS=!autopickup,fruit:tomato,symset:DECgraphics + +#eof diff --git a/util/makedefs.c b/util/makedefs.c index 8b5179c1b..7ff0ec2c9 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -1218,10 +1218,18 @@ do_date() #endif Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count, ul_sfx); +#ifndef WASM Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1, ul_sfx); Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2, ul_sfx); +#else /* WASM */ + Fprintf(ofp, "#define VERSION_SANITY2 0x%08llx%s\n", version.struct_sizes1, + ul_sfx); + Fprintf(ofp, "#define VERSION_SANITY3 0x%08llx%s\n", version.struct_sizes2, + ul_sfx); +#endif /* !WASM */ + Fprintf(ofp, "\n"); Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, ".")); Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n", From d3bbf02d2e62ee019a76c9ea7c715182eb0cab93 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Thu, 27 Aug 2020 21:12:51 -0700 Subject: [PATCH 03/42] fix 'spotless' bug noticed during cross-compile --- sys/unix/Makefile.top | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top index b604bb7ef..60455a384 100644 --- a/sys/unix/Makefile.top +++ b/sys/unix/Makefile.top @@ -315,7 +315,7 @@ clean: # 'make spotless' returns the source tree to near-distribution condition. # it removes .o files, executables, and compiled data files -spotless:: +spotless:: clean ( cd src ; $(MAKE) spotless ) ( cd util ; $(MAKE) spotless ) ( cd dat ; $(MAKE) spotless ) From e5604d575f62501726de79d6a0c620206f376eb7 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Thu, 27 Aug 2020 21:37:16 -0700 Subject: [PATCH 04/42] fix undefined symbol in nhlib --- src/version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index 049b92137..7645969d4 100644 --- a/src/version.c +++ b/src/version.c @@ -265,7 +265,7 @@ boolean pastebuf; raw_printf("%s", buf2); if (pastebuf) { -#ifdef RUNTIME_PASTEBUF_SUPPORT +#if defined(RUNTIME_PASTEBUF_SUPPORT) && !defined(LIBNH) /* * Call a platform/port-specific routine to insert the * version information into a paste buffer. Useful for From 18254eb2912368e25c1ed5485f91576c3d27a94d Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Thu, 27 Aug 2020 21:38:21 -0700 Subject: [PATCH 05/42] make system cleanup --- sys/lib/Makefile.src | 3 +- sys/lib/Makefile.top | 6 +- sys/lib/Makefile.utl | 1 + sys/lib/hints/include/multiw-1.2020 | 37 ---------- sys/lib/hints/include/multiw-2.2020 | 108 ---------------------------- sys/lib/hints/macOS.2020 | 89 +---------------------- sys/lib/hints/wasm | 94 ++---------------------- 7 files changed, 12 insertions(+), 326 deletions(-) delete mode 100644 sys/lib/hints/include/multiw-1.2020 delete mode 100644 sys/lib/hints/include/multiw-2.2020 diff --git a/sys/lib/Makefile.src b/sys/lib/Makefile.src index 4a947a0c5..0d75f1b37 100644 --- a/sys/lib/Makefile.src +++ b/sys/lib/Makefile.src @@ -600,6 +600,7 @@ $(WASM_DATA_DIR): touch $(WASM_DATA_DIR)/record touch $(WASM_DATA_DIR)/logfile touch $(WASM_DATA_DIR)/xlogfile + ( cd ..; $(MAKE) dlb ) ( cd ..; $(MAKE) dofiles-dlb ) cp ../sys/lib/sysconf $(WASM_DATA_DIR)/sysconf @@ -767,7 +768,7 @@ tags: $(CSOURCES) @( cd ../util ; $(MAKE) tags ) clean: - -rm -f *.o $(HACK_H) $(CONFIG_H) $(WASM_TARGET) $(WASM_TARGET:.js=.wasm) $(LIBNH_TARGET) + -rm -f *.o $(HACK_H) $(CONFIG_H) $(WASM_TARGET) $(WASM_TARGET:.js=.wasm) $(WASM_TARGET:.js=.data) $(LIBNH_TARGET) -rm -rf $(WASM_DATA_DIR) spotless: clean diff --git a/sys/lib/Makefile.top b/sys/lib/Makefile.top index c3ca8fda3..34d35fb26 100644 --- a/sys/lib/Makefile.top +++ b/sys/lib/Makefile.top @@ -211,7 +211,7 @@ title.img: check-dlb: options @if egrep -s librarian dat/options ; then $(MAKE) dlb ; else true ; fi -dlb: +dlb: $(VARDATD) ( cd util ; $(MAKE) dlb ) ( cd dat ; LC_ALL=C ; ../util/dlb cf nhdat $(DATDLB) ) @@ -320,13 +320,11 @@ clean: ( cd src ; $(MAKE) clean ) ( cd util ; $(MAKE) clean ) ( cd dat ; $(MAKE) clean ) - ( cd doc ; $(MAKE) clean ) ( cd lib/lua-$(LUA_VERSION)/src && $(MAKE) clean ) # 'make spotless' returns the source tree to near-distribution condition. # it removes .o files, executables, and compiled data files -spotless:: +spotless: clean ( cd src ; $(MAKE) spotless ) ( cd util ; $(MAKE) spotless ) ( cd dat ; $(MAKE) spotless ) - ( cd doc ; $(MAKE) spotless ) diff --git a/sys/lib/Makefile.utl b/sys/lib/Makefile.utl index 8aaf07c9a..ce39a3edf 100644 --- a/sys/lib/Makefile.utl +++ b/sys/lib/Makefile.utl @@ -95,6 +95,7 @@ NHSROOT=.. #CFLAGS = -O -I../include #LFLAGS = +CFLAGS+=-DNOTTYGRAPHICS -DSHIM_GRAPHICS -DDEFAULT_WINDOW_SYS=\"shim\" # -lm required by lua LFLAGS += -lm diff --git a/sys/lib/hints/include/multiw-1.2020 b/sys/lib/hints/include/multiw-1.2020 deleted file mode 100644 index 3fb550882..000000000 --- a/sys/lib/hints/include/multiw-1.2020 +++ /dev/null @@ -1,37 +0,0 @@ -#------------------------------------------------------------------------------ -# NetHack 3.7 multiw-1.2020 $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ - -# 1. Which windowing interface(s) should be included in this binary? -# One or more of these can be manually uncommented and/or can be specified -# on the 'make' command line. If none are enabled, tty will be used. -#WANT_WIN_TTY=1 -#WANT_WIN_CURSES=1 -#WANT_WIN_X11=1 -#WANT_WIN_QT=1 - -# 2. What is the default window system? -# Exactly one of these can be manually uncommented and/or can be specified -# on the 'make' command line. If none is enabled, the first among -# WANT_WIN_{tty,curses,X11,Qt} that is enabled will become default. -#WANT_DEFAULT=tty -#WANT_DEFAULT=curses -#WANT_DEFAULT=Qt -#WANT_DEFAULT=X11 - -# 3. compiler detection or optional override -CCISCLANG := $(shell echo `$(CC) --version` | grep clang) -ifeq "$(CCISCLANG)" "" -CXX=g++ -std=gnu++11 -else -CXX=clang++ -std=gnu++11 -endif -# if you want to override the compiler detection just carried out -# uncomment one of the following pairs as desired. -#CC= gcc -#CXX= g++ -std-gnu++11 -# -#CC= clang -#CXX=clang++ -std=gnu++11 - -#end of multiw-1.2020 -#------------------------------------------------------------------------------ diff --git a/sys/lib/hints/include/multiw-2.2020 b/sys/lib/hints/include/multiw-2.2020 deleted file mode 100644 index ad4028aed..000000000 --- a/sys/lib/hints/include/multiw-2.2020 +++ /dev/null @@ -1,108 +0,0 @@ -#------------------------------------------------------------------------------ -# NetHack 3.7 multiw-2.2020 $NHDT-Date: 1597332785 2020/08/13 15:33:05 $ $NHDT-Branch: NetHack-3.7 $ -# -# Sorts out support for multiple window ports (interfaces) to included in the build. -# -# Included from: -# hints/linux.2020 -# hints/macOS.2020 -# -# The following will be set appropriately following this: -# - WANT_WIN_XXX (at least one will be set; default is TTY) -# - WANT_DEFAULT (set to match one of the enabled WANT_WIN_XXX) -# - WINCFLAGS -# - WINSRC -# - WINOBJ0 -#--- -# User selections could be specified as combinations of any of the following: -# WIN_WANT_TTY=1, WIN_WANT_CURSES=1, WIN_WANT_QT=1, WIN_WANT_X11=1 -# The selections will all be linked into the same binary. -# -# Assuming you have the prerequisite packages mentioned above, you can -# specify, right on the make command line, which window ports (or interfaces) -# to include in your build. Doing it via the make command line means that won't -# have to edit the Makefile. -# -# make WANT_WIN_QT=1 WANT_WIN_X11=1 WANT_WIN_CURSES=1 WANT_WIN_TTY=1 install -# -# Add WANT_DEFAULT=Qt (or other interface) if you want nethack to use -# something other than tty as the default interface. -# - -# Make sure that at least one interface is enabled. -ifndef WANT_WIN_ALL -ifndef WANT_WIN_TTY -ifndef WANT_WIN_CURSES -ifndef WANT_WIN_X11 -ifndef WANT_WIN_QT -WANT_WIN_TTY=1 -endif -endif -endif -endif -endif - -ifdef WANT_WIN_ALL -WANT_WIN_TTY=1 -WANT_WIN_CURSES=1 -WANT_WIN_X11=1 -WANT_WIN_QT=1 -endif - - -# Make sure that a default interface is specified; this doesn't guarantee -# sanity for something like 'make WANT_WIN_CURSES=1 WANT_DEFAULT=X11' but -# 'makedefs -v' would notice, complain, and quit causing 'make' to quit. -ifndef WANT_DEFAULT -# pick the first one enabled among { tty, curses, X11, Qt } -ifdef WANT_WIN_TTY -WANT_DEFAULT=tty -else -ifdef WANT_WIN_CURSES -WANT_DEFAULT=curses -else -ifdef WANT_WIN_X11 -WANT_DEFAULT=X11 -else -ifdef WANT_WIN_QT -WANT_DEFAULT=Qt -else -# ? shouldn't be able to get here... -endif -endif -endif -endif -endif - -WINCFLAGS= -WINSRC = -WINOBJ0 = - -ifdef WANT_WIN_TTY -WINSRC += $(WINTTYSRC) -WINOBJ0 += $(WINTTYOBJ) -else -WINCFLAGS += -DNOTTYGRAPHICS -endif - -ifdef WANT_WIN_CURSES -WINCFLAGS += -DCURSES_GRAPHICS -WINSRC += $(WINCURSESSRC) -WINOBJ0 += $(WINCURSESOBJ) -endif - -ifdef WANT_WIN_X11 -WINCFLAGS += -DX11_GRAPHICS -WINSRC += $(WIINX11SRC) -WINOBJ0 += $(WINX11OBJ) -endif - -ifdef WANT_WIN_QT -WINCFLAGS += -DQT_GRAPHICS -WINSRC += $(WINQTSRC) -WINOBJ0 += $(WINQTOBJ) -endif - -#end of hints/include/multiw-2.2020 -#------------------------------------------------------------------------------ - diff --git a/sys/lib/hints/macOS.2020 b/sys/lib/hints/macOS.2020 index 227f83a3c..3edd3b768 100755 --- a/sys/lib/hints/macOS.2020 +++ b/sys/lib/hints/macOS.2020 @@ -16,28 +16,6 @@ # macOS X hints file # -####-INCLUDE multiw-1.2020 - -# 4. If you set WANT_WIN_QT, you need to -# A) set QTDIR either here or in the environment to point to the Qt5 -# library installation root. (Qt2, Qt3, Qt4 will not work) -# B) set XPMLIB to point to the Xpm library -ifndef WANT_WIN_QT -ifdef WANT_WIN_ALL -WANT_WIN_QT=1 -endif -endif -ifdef WANT_WIN_QT -#QTDIR=/Developer/Qt -# Qt installed via homebrew -QTDIR=$(shell brew --prefix)/opt/qt -# Qt installed via macports -#QTDIR=/opt/local/libexec/qt5 -endif # WANT_WIN_QT -ifndef LIBXPM -LIBXPM= -L/opt/X11/lib -lXpm -endif - # 5. Other #----------------------------------------------------------------------------- @@ -45,25 +23,11 @@ endif # you're reading this in Makefile augmented by hints, that may not be true). # -####-INCLUDE multiw-2.2020 +AR=ar rcu +RANLIB=ranlib # XXX -g vs -O should go here, -I../include goes in the makefile CFLAGS+=-g -I../include -DNOTPARMDECL -ifndef WANT_WIN_QT -# these are normally used when compiling nethack's core -CFLAGS+=-ansi -pedantic -Wno-long-long -# but -ansi forces -std=c90 for C or -std=c++98 for C++; -# win/Qt/qt_*.cpp compiled with C++98 semantics trigger -#In file included from .../qt5/include/QtCore/qglobal.h:105: -#.../qt5/include/QtCore/qcompilerdetection.h:561:6: -# error Qt requires a C++11 compiler and yours does not seem to be that. -# so we suppress -ansi when the build includes Qt -endif -# As of LLVM build 2336.1.00, this gives dozens of spurious messages, so -# leave it out by default. -#CFLAGS+=-Wunreachable-code -#TODO NHLIB -#CFLAGS+=-Wall -Wextra -Wno-missing-field-initializers -Wimplicit -Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wwrite-strings CFLAGS+=-Wno-missing-field-initializers -Wimplicit -Wreturn-type -Wformat -Wswitch -Wshadow -Wwrite-strings CFLAGS+=-DGCC_WARN @@ -87,7 +51,7 @@ CFLAGS+=-DNOMAIL #CFLAGS+=-DTTY_TILES_ESCCODES #CFLAGS+=-DTTY_SOUND_ESCCODES -#CFLAGS+=-DDEFAULT_WINDOW_SYS=\"shim\" -DNOTTYGRAPHICS -DLIBNH +CFLAGS+=-DDEFAULT_WINDOW_SYS=\"shim\" -DNOTTYGRAPHICS -DLIBNH CFLAGS+= $(WINCFLAGS) #WINCFLAGS set from multiw-2.2020 @@ -100,54 +64,7 @@ HINTSRC=$(CHAINSRC) HINTOBJ=$(CHAINOBJ) endif # WANT_WIN_CHAIN -ifdef WANT_WIN_TTY -CURSESLIB = -lncurses -endif - -ifdef WANT_WIN_CURSES -CURSESLIB = -lncurses -endif - -ifdef CURSESLIB -WINLIB += $(CURSESLIB) -endif - -ifdef WANT_WIN_X11 -USE_XPM=1 -WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 -VARDATND0 += x11tiles NetHack.ad pet_mark.xbm pilemark.xbm -# -x: if built without dlb, some versions of mkfontdir think *.lev are fonts -POSTINSTALL += bdftopcf win/X11/nh10.bdf > $(HACKDIR)/nh10.pcf; ( cd $(HACKDIR); mkfontdir -x .lev ); -# separate from CFLAGS so that we don't pass it to every file -X11CFLAGS = -I/opt/X11/include -# avoid repeated complaints about _X_NONNULL(args...) in -X11CFLAGS += -Wno-variadic-macros -ifdef USE_XPM -CFLAGS += -DUSE_XPM -WINX11LIB += -lXpm -VARDATND0 += rip.xpm -endif -WINLIB += $(WINX11LIB) -LFLAGS=-L/opt/X11/lib -endif # WANT_WIN_X11 - -ifdef WANT_WIN_QT -# Qt5 requires C++11 -LINK = $(CXX) -QTCXXFLAGS += -Wno-deprecated-declarations -QTCXXFLAGS += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --cflags) -WINLIB += $(shell PKG_CONFIG_PATH=$(QTDIR)/lib/pkgconfig pkg-config Qt5Gui Qt5Widgets Qt5Multimedia --libs) -WINSRC += $(WINQTSRC) -WINOBJ0 += $(WINQTOBJ) -VARDATND0 += nhtiles.bmp rip.xpm nhsplash.xpm -# XXX if /Developer/qt exists and QTDIR not set, use that -ifndef QTDIR -$(error QTDIR not defined in the environment or Makefile) -endif # QTDIR -# XXX make sure QTDIR points to something reasonable -else # !WANT_WIN_QT LINK=$(CC) -endif # !WANT_WIN_QT # prevent duplicate tile.o in WINOBJ WINOBJ = $(sort $(WINOBJ0)) diff --git a/sys/lib/hints/wasm b/sys/lib/hints/wasm index 0591db7ba..e5cf4bc7b 100644 --- a/sys/lib/hints/wasm +++ b/sys/lib/hints/wasm @@ -4,7 +4,7 @@ WANT_WASM=1 WASM_DEBUG=1 -WASM_DATA_DIR=$(NHSROOT)/src/wasm-data/Users/ampower/nethackdir +WASM_DATA_DIR=$(NHSROOT)/src/wasm-data # toolchain EMCC=emcc @@ -51,100 +51,14 @@ else EMCC_CFLAGS+=$(EMCC_PROD_CFLAGS) endif -ifdef WANT_SHARE_INSTALL -# if $GAMEUID is root, we install into roughly proper Mac locations, otherwise -# we install into ~/nethackdir -ifeq ($(GAMEUID),root) -PREFIX:=/Library/NetHack -SHELLDIR=/usr/local/bin -HACKDIR=$(PREFIX)/nethackdir -CHOWN=chown -CHGRP=chgrp -# We run sgid so the game has access to both HACKDIR and user preferences. -GAMEPERM = 02755 -else # ! root -PREFIX:=/Users/$(GAMEUID) -SHELLDIR=$(PREFIX)/bin -HACKDIR=$(PREFIX)/Library/NetHack/nethackdir -CHOWN=/usr/bin/true -CHGRP=/usr/bin/true -GAMEPERM = 0500 -endif # ! root -VARFILEPERM = 0664 -VARDIRPERM = 0775 -ROOTCHECK= [[ `id -u` == 0 ]] || ( echo "Must run install with sudo."; exit 1) -# XXX it's nice we don't write over sysconf, but we've already erased it -# make sure we have group GAMEUID and group GAMEGRP -PREINSTALL= . sys/unix/hints/macosx.sh user2 $(GAMEUID); \ - . sys/unix/hints/macosx.sh group2 $(GAMEGRP); \ - mkdir $(SHELLDIR); chown $(GAMEUID) $(SHELLDIR) -POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ - $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ - $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ - chmod $(VARFILEPERM) $(HACKDIR)/sysconf; - -else ifdef WANT_SOURCE_INSTALL - -PREFIX=$(abspath $(NHSROOT)) -# suppress nethack.sh -#SHELLDIR= -HACKDIR=$(PREFIX)/playground +# installation config +# hackdir is the wasm / emscripten embed data root directory +HACKDIR=/ CHOWN=/usr/bin/true CHGRP=/usr/bin/true GAMEPERM = 0700 VARFILEPERM = 0600 VARDIRPERM = 0700 -POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; -# We can use "make all" to build the whole thing - but it misses some things: -MOREALL=$(MAKE) install -CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE - -else # !WANT_SOURCE_INSTALL - -PREFIX:=$(wildcard ~) -SHELLDIR=$(PREFIX)/bin -HACKDIR=$(PREFIX)/nethackdir -CHOWN=/usr/bin/true -CHGRP=/usr/bin/true -GAMEPERM = 0700 -VARFILEPERM = 0600 -VARDIRPERM = 0700 -ifdef ($(WANT_DEFAULT),X11) -# install nethack.rc as ~/.nethackrc if no ~/.nethackrc exists -PREINSTALL= cp -n win/X11/nethack.rc ~/.nethackrc || true -endif # WANT_DEFAULT X11 - -POSTINSTALL+= sys/unix/hints/macosx.sh editsysconf sys/unix/sysconf $(HACKDIR)/sysconf; \ - $(CHOWN) $(GAMEUID) $(HACKDIR)/sysconf; \ - $(CHGRP) $(GAMEGRP) $(HACKDIR)/sysconf; \ - chmod $(VARFILEPERM) $(HACKDIR)/sysconf; -ifdef WANT_BUNDLE -# -# Bundle -# -# $(HACKDIR)/$(GAME).app/ -# Contents/ -# Frameworks/ -# Info.plist -# MacOS/ -# $(GAME) -# PkgInfo/ -# PlugIns/ -# Resources/ -# SharedFrameWorks/ -# -BUNDLE = mkdir -p $(HACKDIR)/nethack.app/Contents/MacOS; \ - sys/unix/hints/macosx.sh infoplist > $(HACKDIR)/nethack.app/Contents/Info.plist; \ - mv $(HACKDIR)/nethack $(HACKDIR)/nethack.app/Contents/MacOS/nethack; -ifdef WANT_SHARE_INSTALL -BUNDLE+= chmod $(GAMEPERM) $(HACKDIR)/nethack.app/Contents/MacOS/nethack; -endif - -POSTINSTALL+= $(BUNDLE) -POSTINSTALL+= if test -f $(SHELLDIR)/$(GAME); then \ - sed -i '' 's;HACKDIR/$(GAME);HACKDIR/$(GAME).app/Contents/MacOS/$(GAME);' $(SHELLDIR)/$(GAME) ; fi; -endif # WANT_BUNDLE -endif # !WANT_SHARE_INSTALL INSTDIR=$(HACKDIR) VARDIR=$(HACKDIR) From c9f5bb9ac40600d91c03709b41ea18f9c0750930 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Thu, 27 Aug 2020 22:15:12 -0700 Subject: [PATCH 06/42] replace -DWASM with compiler internal __EMSCRIPTEN__ --- include/global.h | 10 +++++----- sys/lib/Makefile.utl | 3 --- sys/lib/hints/wasm | 3 +-- util/makedefs.c | 6 +++--- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/include/global.h b/include/global.h index 8f7ed95a4..b3f17e996 100644 --- a/include/global.h +++ b/include/global.h @@ -325,15 +325,15 @@ struct version_info { unsigned long incarnation; /* actual version number */ unsigned long feature_set; /* bitmask of config settings */ unsigned long entity_count; /* # of monsters and objects */ -#ifndef WASM +#ifndef __EMSCRIPTEN__ unsigned long struct_sizes1; /* size of key structs */ unsigned long struct_sizes2; /* size of more key structs */ -#else /* WASM */ +#else /* __EMSCRIPTEN__ */ /* 'long' in WASM is 4 bytes, which is too small to hold version numbers * such as: VERSION_SANITY2 */ unsigned long long struct_sizes1; /* size of key structs */ unsigned long long struct_sizes2; /* size of more key structs */ -#endif /* !WASM */ +#endif /* !__EMSCRIPTEN__ */ }; struct savefile_info { @@ -397,7 +397,7 @@ struct savefile_info { /* PANICTRACE: Always defined for NH_DEVEL_STATUS != NH_STATUS_RELEASED but only for supported platforms. */ -#if defined(UNIX) && !defined(WASM) +#if defined(UNIX) && !defined(__EMSCRIPTEN__) #if (NH_DEVEL_STATUS != NH_STATUS_RELEASED) /* see end.c */ #ifndef PANICTRACE @@ -412,7 +412,7 @@ struct savefile_info { #if defined(MACOSX) #define PANICTRACE_LIBC #endif -#if defined(UNIX) && !defined(WASM) /* no popen in WASM */ +#if defined(UNIX) && !defined(__EMSCRIPTEN__) /* no popen in WASM */ #define PANICTRACE_GDB #endif diff --git a/sys/lib/Makefile.utl b/sys/lib/Makefile.utl index ce39a3edf..cd1cca159 100644 --- a/sys/lib/Makefile.utl +++ b/sys/lib/Makefile.utl @@ -148,9 +148,6 @@ CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) CXX = $(CXX_V$(QUIETCC)) CFLAGS+=-I../include -ifdef WANT_WASM -CFLAGS+=-DWASM -endif # LD and LINK might be based on invoking CC and may not be able to substitute # for QUIETCC, so feedback from them is handled differently (via $AT) diff --git a/sys/lib/hints/wasm b/sys/lib/hints/wasm index e5cf4bc7b..8715104c8 100644 --- a/sys/lib/hints/wasm +++ b/sys/lib/hints/wasm @@ -17,7 +17,7 @@ EMCC_LFLAGS=-s WASM=1 EMCC_LFLAGS+=-s ALLOW_TABLE_GROWTH EMCC_LFLAGS+=-s ASYNCIFY -s ASYNCIFY_IMPORTS='["_nhmain"]' -O3 EMCC_LFLAGS+=-s MODULARIZE -EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main"]' +EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main", "_stub_graphics_set_callback"]' EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString"]' EMCC_LFLAGS+=-s ERROR_ON_UNDEFINED_SYMBOLS=0 EMCC_LFLAGS+=--embed-file wasm-data@/ @@ -25,7 +25,6 @@ EMCC_LFLAGS+=--embed-file wasm-data@/ # WASM C flags EMCC_CFLAGS= EMCC_CFLAGS+=-Wall -Werror -EMCC_CFLAGS+=-DWASM EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=1 EMCC_DEBUG_CFLAGS+=-s STACK_OVERFLOW_CHECK=2 EMCC_DEBUG_CFLAGS+=-s SAFE_HEAP=1 diff --git a/util/makedefs.c b/util/makedefs.c index 7ff0ec2c9..68421776f 100644 --- a/util/makedefs.c +++ b/util/makedefs.c @@ -1218,17 +1218,17 @@ do_date() #endif Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count, ul_sfx); -#ifndef WASM +#ifndef __EMSCRIPTEN__ Fprintf(ofp, "#define VERSION_SANITY2 0x%08lx%s\n", version.struct_sizes1, ul_sfx); Fprintf(ofp, "#define VERSION_SANITY3 0x%08lx%s\n", version.struct_sizes2, ul_sfx); -#else /* WASM */ +#else /* __EMSCRIPTEN__ */ Fprintf(ofp, "#define VERSION_SANITY2 0x%08llx%s\n", version.struct_sizes1, ul_sfx); Fprintf(ofp, "#define VERSION_SANITY3 0x%08llx%s\n", version.struct_sizes2, ul_sfx); -#endif /* !WASM */ +#endif /* !__EMSCRIPTEN__ */ Fprintf(ofp, "\n"); Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, ".")); From 97a14eebbe760f7205b8a0bbbd4e9a068ffdda4d Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 29 Aug 2020 10:26:58 -0700 Subject: [PATCH 07/42] functional shim graphics --- sys/lib/hints/wasm | 12 +++-- win/shim/winshim.c | 115 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 23 deletions(-) diff --git a/sys/lib/hints/wasm b/sys/lib/hints/wasm index 8715104c8..799efe03c 100644 --- a/sys/lib/hints/wasm +++ b/sys/lib/hints/wasm @@ -18,13 +18,14 @@ EMCC_LFLAGS+=-s ALLOW_TABLE_GROWTH EMCC_LFLAGS+=-s ASYNCIFY -s ASYNCIFY_IMPORTS='["_nhmain"]' -O3 EMCC_LFLAGS+=-s MODULARIZE EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main", "_stub_graphics_set_callback"]' -EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString"]' +EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString", "getValue"]' EMCC_LFLAGS+=-s ERROR_ON_UNDEFINED_SYMBOLS=0 EMCC_LFLAGS+=--embed-file wasm-data@/ # WASM C flags EMCC_CFLAGS= -EMCC_CFLAGS+=-Wall -Werror +EMCC_CFLAGS+=-Wall +EMCC_CFLAGS+=-Werror EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=1 EMCC_DEBUG_CFLAGS+=-s STACK_OVERFLOW_CHECK=2 EMCC_DEBUG_CFLAGS+=-s SAFE_HEAP=1 @@ -32,16 +33,17 @@ EMCC_DEBUG_CFLAGS+=-s LLD_REPORT_UNDEFINED EMCC_PROD_CFLAGS+=-O3 # Nethack C flags -CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE +CFLAGS+=-DSYSCF -DSYSCF_FILE=\"/sysconf\" -DSECURE CFLAGS+=-g -I../include -DNOTPARMDECL -CFLAGS+=-Wall -Werror +CFLAGS+=-Wall +CFLAGS+=-Werror CFLAGS+=-DGCC_WARN # NetHack sources control CFLAGS+=-DDLB CFLAGS+=-DHACKDIR=\"$(HACKDIR)\" CFLAGS+=-DDLB -CFLAGS+=-DGREPPATH=\"/usr/bin/grep\" +#CFLAGS+=-DGREPPATH=\"/usr/bin/grep\" CFLAGS+=-DNOMAIL ifdef WASM_DEBUG diff --git a/win/shim/winshim.c b/win/shim/winshim.c index d82d21334..085a27684 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -7,6 +7,80 @@ #include "hack.h" #ifdef SHIM_GRAPHICS +#include +/* for cross-compiling to WebAssembly (WASM) */ +#ifdef __EMSCRIPTEN__ +#include +#endif + +#define SHIM_DEBUG + +#ifndef __EMSCRIPTEN__ +typedef void(*stub_callback_t)(const char *name, const char *fmt, void *ret_ptr, ...); +#else /* __EMSCRIPTEN__ */ +/* WASM can't handle a variadic callback, so we pass back an array of pointers instead... */ +typedef void(*stub_callback_t)(const char *name, const char *fmt, void *ret_ptr, void *args[]); +#endif /* !__EMSCRIPTEN__ */ + +/* this is the primary interface to shim graphics, + * call this function with your declared callback function + * and you will receive all the windowing calls + */ +static stub_callback_t shim_graphics_callback = NULL; +#ifdef __EMSCRIPTEN__ + EMSCRIPTEN_KEEPALIVE +#endif +void stub_graphics_set_callback(stub_callback_t cb) { + shim_graphics_callback = cb; +} + +#ifdef __EMSCRIPTEN__ +// A2P = Argument to Pointer +#define A2P & +// P2V = Pointer to Void +#define P2V (void *) +#define DECLCB(ret_type, name, fn_args, fmt, ...) \ +ret_type name fn_args { \ + void *args[] = { __VA_ARGS__ }; \ + ret_type ret; \ + debugf("SHIM GRAPHICS: " #name "\n"); \ + if (!shim_graphics_callback) return; \ + shim_graphics_callback(#name, fmt, (void *)&ret, args); \ + return ret; \ +} + +#define VDECLCB(name, fn_args, fmt, ...) \ +void name fn_args { \ + void *args[] = { __VA_ARGS__ }; \ + debugf("SHIM GRAPHICS: " #name "\n"); \ + if (!shim_graphics_callback) return; \ + shim_graphics_callback(#name, fmt, NULL, args); \ +} +#else /* !__EMSCRIPTEN__ */ +#define A2P +#define P2V +#define DECLCB(ret_type, name, args, fmt, ...) \ +ret_type name args { \ + ret_type ret; \ + debugf("SHIM GRAPHICS: " #name "\n"); \ + if (!shim_graphics_callback) return; \ + shim_graphics_callback(#name, fmt, (void *)&ret, __VA_ARGS__); \ + return ret; \ +} + +void name args { \ + debugf("SHIM GRAPHICS: " #name "\n"); \ + if (!shim_graphics_callback) return; \ + shim_graphics_callback(#name, fmt, NULL, __VA_ARGS__); \ +} +#endif /* __EMSCRIPTEN__ */ + +#ifdef SHIM_DEBUG +#define debugf printf +#else /* !SHIM_DEBUG */ +#define debugf(...) +#endif /* SHIM_DEBUG */ + enum win_types { WINSTUB_MESSAGE = 1, @@ -29,6 +103,7 @@ name args { \ #define DECL(name, args) \ void name args; +// void DECLCB(shim_init_nhwindows,(int *argcp, char **argv), "pp", argcp, argv) VSTUB(shim_init_nhwindows,(int *argcp, char **argv)) VSTUB(shim_player_selection,(void)) VSTUB(shim_askname,(void)) @@ -40,8 +115,10 @@ winid STUB(shim_create_nhwindow, WINSTUB_MAP, (int a)) VSTUB(shim_clear_nhwindow,(winid a)) VSTUB(shim_display_nhwindow,(winid a, BOOLEAN_P b)) VSTUB(shim_destroy_nhwindow,(winid a)) -VSTUB(shim_curs,(winid a, int x, int y)) -DECL(shim_putstr,(winid w, int attr, const char *str)) +VDECLCB(shim_curs,(winid a, int x, int y), "viii", A2P a, A2P x, A2P y) +// VSTUB(shim_curs,(winid a, int x, int y)) +// DECL(shim_putstr,(winid w, int attr, const char *str)) +VDECLCB(shim_putstr,(winid w, int attr, const char *str), "viis", A2P w, A2P attr, P2V str) VSTUB(shim_display_file,(const char *a, BOOLEAN_P b)) VSTUB(shim_start_menu,(winid w, unsigned long mbehavior)) VSTUB(shim_add_menu,(winid a, int b, const ANY_P *c, CHAR_P d, CHAR_P e, int f, const char *h, unsigned int k)) @@ -53,8 +130,10 @@ VSTUB(shim_mark_synch,(void)) VSTUB(shim_wait_synch,(void)) VSTUB(shim_cliparound,(int a, int b)) VSTUB(shim_update_positionbar,(char *a)) -DECL(shim_print_glyph,(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)) -DECL(shim_raw_print,(const char *str)) +// DECL(shim_print_glyph,(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)) +VDECLCB(shim_print_glyph,(winid w, int x, int y, int glyph, int bkglyph), "viiiii", A2P w, A2P x, A2P y, A2P glyph, A2P bkglyph) +// DECL(shim_raw_print,(const char *str)) +VDECLCB(shim_raw_print,(const char *str), "vs", P2V str) VSTUB(shim_raw_print_bold,(const char *a)) int STUB(shim_nhgetch,0,(void)) int STUB(shim_nh_poskey,0,(int *a, int *b, int *c)) @@ -140,22 +219,22 @@ struct window_procs shim_procs = { genl_can_suspend_yes, }; -void shim_print_glyph(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph) { - /* map glyph to character and color */ - // (void) mapglyph(glyph, &ch, &color, &special, x, y, 0); +// void shim_print_glyph(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph) { +// /* map glyph to character and color */ +// // (void) mapglyph(glyph, &ch, &color, &special, x, y, 0); - fprintf(stdout, "shim_print_glyph (%d,%d): %c\n", x,y,(char)glyph); - fflush(stdout); -} +// fprintf(stdout, "shim_print_glyph (%d,%d): %c\n", x,y,(char)glyph); +// fflush(stdout); +// } -void shim_raw_print(const char *str) { - fprintf(stdout, "shim_raw_print: %s\n", str); - fflush(stdout); -} +// void shim_raw_print(const char *str) { +// fprintf(stdout, "shim_raw_print: %s\n", str); +// fflush(stdout); +// } -void shim_putstr(winid w, int attr, const char *str) { - fprintf(stdout, "shim_putstr (win %d): %s\n", w, str); - fflush(stdout); -} +// void shim_putstr(winid w, int attr, const char *str) { +// fprintf(stdout, "shim_putstr (win %d): %s\n", w, str); +// fflush(stdout); +// } #endif /* SHIM_GRAPHICS */ \ No newline at end of file From e04b17911abd49dda5a36b531955c001dcebf663 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 29 Aug 2020 10:27:51 -0700 Subject: [PATCH 08/42] delinting --- win/shim/winshim.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 085a27684..2334e9dec 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -35,9 +35,9 @@ void stub_graphics_set_callback(stub_callback_t cb) { } #ifdef __EMSCRIPTEN__ -// A2P = Argument to Pointer +/* A2P = Argument to Pointer */ #define A2P & -// P2V = Pointer to Void +/* P2V = Pointer to Void */ #define P2V (void *) #define DECLCB(ret_type, name, fn_args, fmt, ...) \ ret_type name fn_args { \ @@ -103,7 +103,6 @@ name args { \ #define DECL(name, args) \ void name args; -// void DECLCB(shim_init_nhwindows,(int *argcp, char **argv), "pp", argcp, argv) VSTUB(shim_init_nhwindows,(int *argcp, char **argv)) VSTUB(shim_player_selection,(void)) VSTUB(shim_askname,(void)) @@ -116,8 +115,6 @@ VSTUB(shim_clear_nhwindow,(winid a)) VSTUB(shim_display_nhwindow,(winid a, BOOLEAN_P b)) VSTUB(shim_destroy_nhwindow,(winid a)) VDECLCB(shim_curs,(winid a, int x, int y), "viii", A2P a, A2P x, A2P y) -// VSTUB(shim_curs,(winid a, int x, int y)) -// DECL(shim_putstr,(winid w, int attr, const char *str)) VDECLCB(shim_putstr,(winid w, int attr, const char *str), "viis", A2P w, A2P attr, P2V str) VSTUB(shim_display_file,(const char *a, BOOLEAN_P b)) VSTUB(shim_start_menu,(winid w, unsigned long mbehavior)) @@ -130,9 +127,7 @@ VSTUB(shim_mark_synch,(void)) VSTUB(shim_wait_synch,(void)) VSTUB(shim_cliparound,(int a, int b)) VSTUB(shim_update_positionbar,(char *a)) -// DECL(shim_print_glyph,(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)) VDECLCB(shim_print_glyph,(winid w, int x, int y, int glyph, int bkglyph), "viiiii", A2P w, A2P x, A2P y, A2P glyph, A2P bkglyph) -// DECL(shim_raw_print,(const char *str)) VDECLCB(shim_raw_print,(const char *str), "vs", P2V str) VSTUB(shim_raw_print_bold,(const char *a)) int STUB(shim_nhgetch,0,(void)) @@ -219,22 +214,4 @@ struct window_procs shim_procs = { genl_can_suspend_yes, }; -// void shim_print_glyph(winid w, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph) { -// /* map glyph to character and color */ -// // (void) mapglyph(glyph, &ch, &color, &special, x, y, 0); - -// fprintf(stdout, "shim_print_glyph (%d,%d): %c\n", x,y,(char)glyph); -// fflush(stdout); -// } - -// void shim_raw_print(const char *str) { -// fprintf(stdout, "shim_raw_print: %s\n", str); -// fflush(stdout); -// } - -// void shim_putstr(winid w, int attr, const char *str) { -// fprintf(stdout, "shim_putstr (win %d): %s\n", w, str); -// fflush(stdout); -// } - #endif /* SHIM_GRAPHICS */ \ No newline at end of file From 1a70f77b2a56c085278fafe6396f6fd58554a4fb Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 29 Aug 2020 11:10:09 -0700 Subject: [PATCH 09/42] more sensible arg order for callback --- win/shim/winshim.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 2334e9dec..9fed47d9c 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -16,10 +16,10 @@ #define SHIM_DEBUG #ifndef __EMSCRIPTEN__ -typedef void(*stub_callback_t)(const char *name, const char *fmt, void *ret_ptr, ...); +typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); #else /* __EMSCRIPTEN__ */ /* WASM can't handle a variadic callback, so we pass back an array of pointers instead... */ -typedef void(*stub_callback_t)(const char *name, const char *fmt, void *ret_ptr, void *args[]); +typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, void *args[]); #endif /* !__EMSCRIPTEN__ */ /* this is the primary interface to shim graphics, @@ -45,7 +45,7 @@ ret_type name fn_args { \ ret_type ret; \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ - shim_graphics_callback(#name, fmt, (void *)&ret, args); \ + shim_graphics_callback(#name, (void *)&ret, fmt, args); \ return ret; \ } @@ -54,7 +54,7 @@ void name fn_args { \ void *args[] = { __VA_ARGS__ }; \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ - shim_graphics_callback(#name, fmt, NULL, args); \ + shim_graphics_callback(#name, NULL, fmt, args); \ } #else /* !__EMSCRIPTEN__ */ #define A2P @@ -64,14 +64,14 @@ ret_type name args { \ ret_type ret; \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ - shim_graphics_callback(#name, fmt, (void *)&ret, __VA_ARGS__); \ + shim_graphics_callback(#name, (void *)&ret, fmt, __VA_ARGS__); \ return ret; \ } void name args { \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ - shim_graphics_callback(#name, fmt, NULL, __VA_ARGS__); \ + shim_graphics_callback(#name, NULL, fmt, __VA_ARGS__); \ } #endif /* __EMSCRIPTEN__ */ From 22bc4ecfc0cab01848610f6730dd9529bc2fd44c Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 29 Aug 2020 11:10:52 -0700 Subject: [PATCH 10/42] initial docs --- sys/lib/README.md | 166 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 sys/lib/README.md diff --git a/sys/lib/README.md b/sys/lib/README.md new file mode 100644 index 000000000..f0347b184 --- /dev/null +++ b/sys/lib/README.md @@ -0,0 +1,166 @@ +# About +This creates a library for NetHack that can be incorporated into other programs. There are two different libraries that are currently available: +* libnethack.a - a binary Unix library +* nethack.js / nethack.wasm - a [WebAssembly / WASM](https://webassembly.org/) library for use in JavaScript programs (both nodejs and browser) + +## API: libnethack.a +The API is two functions: +* `nhmain(int argc, char *argv[])` - The main function for NetHack that configures the program and runs the `moveloop()` until the game is over. The arguments to this function are the [command line arguments](https://nethackwiki.com/wiki/Options) to NetHack. +* `stub_graphics_set_callback(stub_callback_t cb)` - A single function that sets a callback to gather graphics events: write a string to screen, get user input, etc. Your job is to pass in a callback and handle all the requested rendering events to show NetHack on the scrren. The callback is `void stub_callback_t(const char *name, void *ret_ptr, const char *fmt, ...)` + * `name` is the name of the [window function](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc) that needs to be handled + * `ret_ptr` is a pointer to a memory space for the return value. The type expected to be returned in this pointer is described by the first character of the `fmt` string. + * `fmt` is a string that describes the signature of the callback. The first character in the string is the return type and any additional characters describe the variable arguments: `i` for integer, `s` for string, `p` for pointer, `c` for character, `v` for void. For example, if format is "vis" the callback will have no return (void), the first argument will be an integer, and the second argument will be a string. If format is "iii" the callback must return an integer, and both the arguments passed in will be integers. + * [Variadic arguments](https://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html): a variable number and type of arguments depending on the `window function` that is being called. The arguments associated with each `name` are described in the [NetHack window.doc](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc). + +Where is the header file for the API you ask? There isn't one. It's three functions, just drop the forward declarations at the top of your file (or create your own header). It's more work figuring out how to install and copy around header files than it's worth for such a small API. If you disagree, feel free to sumbit a PR to fix it. :) + +## API: nethack.js +Building the WASM module requires that you have the [emscripten toolchain / sdk installed](https://emscripten.org/docs/getting_started/downloads.html). + +The WebAssembly API has a similar signature to `libnethack.a` with minor syntactic differences: +* `main(int argc, char argv[])` - The main function for NetHack +* `stub_graphics_set_callback(stub_callback_t cb)` - The same as above, but the signature of the callback is slightly different because WASM can't handle variadic callbacks. The callback is: `void stub_callback_t(const char *name, void *ret_ptr, const char *fmt, void *args[])` + * `name` - same as above + * `ret_ptr` - same as above + * `fmt` - same as above + * `args` - an array of pointers to the arguments, where each pointer can be de-referenced to a value as specified in the `fmt` string. + + + +## API Stability +The "stub graphics" API should generally be stable. I aspire to replace the command line arguments (argc / argv) with a structure of options, so the `nhmain()` and `main()` functions may change at some point. + +## libnethack.a example +``` c +#include + +int nhmain(int argc, char *argv[]); +typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); +void stub_graphics_set_callback(stub_callback_t cb); + +void window_cb(const char *name, void *ret_ptr, const char *fmt, ...) { + /* TODO -- see windowCallback below for hints */ +} + +int main(int argc, char *argv[]) { + stub_graphics_set_callback(window_cb); + nhmain(argc, argv); +} +``` + +## nethack.js example +``` js +// Module is defined by emscripten +// https://emscripten.org/docs/api_reference/module.html +let Module = { + // if this is true, main() won't be called automatically + // noInitialRun: true, + + // after loading the library, set the callback function + onRuntimeInitialized: function (... args) { + setGraphicsCallback(); + } +}; + +var factory = require("./src/nethack.js"); + +// run NetHack! +factory(Module); + +// register the callback with the WASM library +function setGraphicsCallback() { + console.log("creating WASM callback function"); + let cb = Module.addFunction(windowCallback, "viiii"); + + console.log("setting callback function with library"); + Module.ccall( + "stub_graphics_set_callback", // C function name + null, // return type + ["number"], // arg types + [cb], // arg values + {async: true} // options + ); + + /* TODO: removeFunction */ +} + +// this is the "shim graphics" callback function +// it gets called every time something needs to be displayed +// or input needs to be gathered from the user +function windowCallback(name, fmt, retPtr, args) { + name = Module.UTF8ToString(name); + fmt = Module.UTF8ToString(fmt); + let argTypes = fmt.split(""); + let retType = argTypes.shift(); + + // convert arguments to JavaScript types + let jsArgs = []; + for (let i = 0; i < argTypes.length; i++) { + let ptr = args + (4*i); + let val = typeLookup(argTypes[i], ptr); + jsArgs.push(val); + } + console.log(`graphics callback: ${name} [${jsArgs}]`); + /********** + * YOU HAVE TO IMPLEMENT THIS FUNCTION to render things + **********/ + let ret = yourFunctionToRenderGraphics(name, jsArgs); + setReturn(retPtr, retType, ret); +} + +// takes a character `type` and a WASM pointer and returns a JavaScript value +function typeLookup(type, ptr) { + switch(type) { + case "s": // string + return Module.UTF8ToString(Module.getValue(ptr, "*")); + case "p": // pointer + return Module.getValue(Module.getValue(ptr, "*"), "*"); + case "c": // char + return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + case "0": /* 2^0 = 1 byte */ + return Module.getValue(Module.getValue(ptr, "*"), "i8"); + case "1": /* 2^1 = 2 bytes */ + return Module.getValue(Module.getValue(ptr, "*"), "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return Module.getValue(Module.getValue(ptr, "*"), "i32"); + case "f": // float + return Module.getValue(Module.getValue(ptr, "*"), "float"); + case "d": // double + return Module.getValue(Module.getValue(ptr, "*"), "double"); + default: + throw new TypeError ("unknown type:" + type); + } +} + +// takes a a WASM pointer, a charater `type` and a value and sets the value at pointer +function setReturn(ptr, type, value = 0) { + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + value=value?value:"(no value)"; + var strPtr = Module.getValue(ptr, "i32"); + Module.stringToUTF8(value, strPtr, 1024); + break; + case "i": + Module.setValue(ptr, value, "i32"); + break; + case "c": + Module.setValue(ptr, value, "i8"); + break; + case "f": + // XXX: I'm not sure why 'double' works and 'float' doesn't + Module.setValue(ptr, value, "double"); + break; + case "d": + Module.setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } +} +``` \ No newline at end of file From 4ef48ca49adc684459644ab9f4d7b1002c0c7bb8 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 29 Aug 2020 14:48:02 -0700 Subject: [PATCH 11/42] fix syntax errors for libnethack.a --- win/shim/winshim.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 9fed47d9c..34bdccbaf 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -59,8 +59,8 @@ void name fn_args { \ #else /* !__EMSCRIPTEN__ */ #define A2P #define P2V -#define DECLCB(ret_type, name, args, fmt, ...) \ -ret_type name args { \ +#define DECLCB(ret_type, name, fn_args, fmt, ...) \ +ret_type name fn_args { \ ret_type ret; \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ @@ -68,7 +68,8 @@ ret_type name args { \ return ret; \ } -void name args { \ +#define VDECLCB(name, fn_args, fmt, ...) \ +void name fn_args { \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ shim_graphics_callback(#name, NULL, fmt, __VA_ARGS__); \ From 14084dc50c4751f39c11462f14ef2e244b4abbca Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 29 Aug 2020 14:48:22 -0700 Subject: [PATCH 12/42] fix arg order --- sys/lib/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/lib/README.md b/sys/lib/README.md index f0347b184..3d314eb31 100644 --- a/sys/lib/README.md +++ b/sys/lib/README.md @@ -87,7 +87,7 @@ function setGraphicsCallback() { // this is the "shim graphics" callback function // it gets called every time something needs to be displayed // or input needs to be gathered from the user -function windowCallback(name, fmt, retPtr, args) { +function windowCallback(name, retPtr, fmt, args) { name = Module.UTF8ToString(name); fmt = Module.UTF8ToString(fmt); let argTypes = fmt.split(""); From 863f91433717bb9d4f7dede05b850db69c92f44f Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 29 Aug 2020 15:50:42 -0700 Subject: [PATCH 13/42] added void functions --- win/shim/winshim.c | 92 +++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 34bdccbaf..310a4c315 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -13,7 +13,7 @@ #include #endif -#define SHIM_DEBUG +#undef SHIM_DEBUG #ifndef __EMSCRIPTEN__ typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); @@ -42,9 +42,9 @@ void stub_graphics_set_callback(stub_callback_t cb) { #define DECLCB(ret_type, name, fn_args, fmt, ...) \ ret_type name fn_args { \ void *args[] = { __VA_ARGS__ }; \ - ret_type ret; \ + ret_type ret = (ret_type) 0; \ debugf("SHIM GRAPHICS: " #name "\n"); \ - if (!shim_graphics_callback) return; \ + if (!shim_graphics_callback) return ret; \ shim_graphics_callback(#name, (void *)&ret, fmt, args); \ return ret; \ } @@ -61,9 +61,9 @@ void name fn_args { \ #define P2V #define DECLCB(ret_type, name, fn_args, fmt, ...) \ ret_type name fn_args { \ - ret_type ret; \ + ret_type ret = (ret_type) 0; \ debugf("SHIM GRAPHICS: " #name "\n"); \ - if (!shim_graphics_callback) return; \ + if (!shim_graphics_callback) return ret; \ shim_graphics_callback(#name, (void *)&ret, fmt, __VA_ARGS__); \ return ret; \ } @@ -104,60 +104,68 @@ name args { \ #define DECL(name, args) \ void name args; -VSTUB(shim_init_nhwindows,(int *argcp, char **argv)) -VSTUB(shim_player_selection,(void)) -VSTUB(shim_askname,(void)) -VSTUB(shim_get_nh_event,(void)) -VSTUB(shim_exit_nhwindows,(const char *a)) -VSTUB(shim_suspend_nhwindows,(const char *a)) -VSTUB(shim_resume_nhwindows,(void)) +VDECLCB(shim_init_nhwindows,(int *argcp, char **argv), "vpp", P2V argcp, P2V argv) +VDECLCB(shim_player_selection,(void), "v") +VDECLCB(shim_askname,(void), "v") +VDECLCB(shim_get_nh_event,(void), "v") +VDECLCB(shim_exit_nhwindows,(const char *str), "vs", P2V str) +VDECLCB(shim_suspend_nhwindows,(const char *str), "vs", P2V str) +VDECLCB(shim_resume_nhwindows,(void), "v") +// DECLCB(winid, shim_create_nhwindow, (int type), "ii", A2P type) winid STUB(shim_create_nhwindow, WINSTUB_MAP, (int a)) -VSTUB(shim_clear_nhwindow,(winid a)) -VSTUB(shim_display_nhwindow,(winid a, BOOLEAN_P b)) -VSTUB(shim_destroy_nhwindow,(winid a)) +VDECLCB(shim_clear_nhwindow,(winid window), "vi", A2P window) +VDECLCB(shim_display_nhwindow,(winid window, BOOLEAN_P blocking), "vii", A2P window, A2P blocking) +VDECLCB(shim_destroy_nhwindow,(winid window), "vi", A2P window) VDECLCB(shim_curs,(winid a, int x, int y), "viii", A2P a, A2P x, A2P y) VDECLCB(shim_putstr,(winid w, int attr, const char *str), "viis", A2P w, A2P attr, P2V str) -VSTUB(shim_display_file,(const char *a, BOOLEAN_P b)) -VSTUB(shim_start_menu,(winid w, unsigned long mbehavior)) -VSTUB(shim_add_menu,(winid a, int b, const ANY_P *c, CHAR_P d, CHAR_P e, int f, const char *h, unsigned int k)) -VSTUB(shim_end_menu,(winid a, const char *b)) +VDECLCB(shim_display_file,(const char *name, BOOLEAN_P complain), "vsi", P2V name, A2P complain) +VDECLCB(shim_start_menu,(winid window, unsigned long mbehavior), "vii", A2P window, A2P mbehavior) +VDECLCB(shim_add_menu, + (winid window, int glyph, const ANY_P *identifier, CHAR_P ch, CHAR_P gch, int attr, const char *str, unsigned int itemflags), + "viipiiisi", + A2P window, A2P glyph, P2V identifier, A2P ch, A2P gch, A2P attr, P2V str, A2P itemflags) +VDECLCB(shim_end_menu,(winid window, const char *prompt), "vis", A2P window, P2V prompt) int STUB(shim_select_menu,0,(winid a, int b, MENU_ITEM_P **c)) char STUB(shim_message_menu,'y',(CHAR_P a, int b, const char *c)) -VSTUB(shim_update_inventory,(void)) -VSTUB(shim_mark_synch,(void)) -VSTUB(shim_wait_synch,(void)) -VSTUB(shim_cliparound,(int a, int b)) -VSTUB(shim_update_positionbar,(char *a)) +VDECLCB(shim_update_inventory,(void), "v") +VDECLCB(shim_mark_synch,(void), "v") +VDECLCB(shim_wait_synch,(void), "v") +VDECLCB(shim_cliparound,(int x, int y), "vii", A2P x, A2P y) +VDECLCB(shim_update_positionbar,(char *posbar), "vp", P2V posbar) VDECLCB(shim_print_glyph,(winid w, int x, int y, int glyph, int bkglyph), "viiiii", A2P w, A2P x, A2P y, A2P glyph, A2P bkglyph) VDECLCB(shim_raw_print,(const char *str), "vs", P2V str) -VSTUB(shim_raw_print_bold,(const char *a)) +VDECLCB(shim_raw_print_bold,(const char *str), "vs", P2V str) int STUB(shim_nhgetch,0,(void)) int STUB(shim_nh_poskey,0,(int *a, int *b, int *c)) -VSTUB(shim_nhbell,(void)) +VDECLCB(shim_nhbell,(void), "v") int STUB(shim_doprev_message,0,(void)) char STUB(shim_yn_function,'y',(const char *a, const char *b, CHAR_P c)) -VSTUB(shim_getlin,(const char *a, char *b)) +VDECLCB(shim_getlin,(const char *query, char *bufp), "vsp", P2V query, P2V bufp) int STUB(shim_get_ext_cmd,0,(void)) -VSTUB(shim_number_pad,(int a)) -VSTUB(shim_delay_output,(void)) -VSTUB(shim_change_color,(int a, long b, int c)) -VSTUB(shim_change_background,(int a)) +VDECLCB(shim_number_pad,(int state), "vi", A2P state) +VDECLCB(shim_delay_output,(void), "v") +VDECLCB(shim_change_color,(int color, long rgb, int reverse), "viii", A2P color, A2P rgb, A2P reverse) +VDECLCB(shim_change_background,(int white_or_black), "vi", A2P white_or_black) short STUB(set_shim_font_name,0,(winid a, char *b)) -VSTUB(shim_get_color_string,(void)) +char *STUB(shim_get_color_string,"",(void)) /* other defs that really should go away (they're tty specific) */ -VSTUB(shim_start_screen, (void)) -VSTUB(shim_end_screen, (void)) -VSTUB(shim_preference_update, (const char *a)) +VDECLCB(shim_start_screen, (void), "v") +VDECLCB(shim_end_screen, (void), "v") +VDECLCB(shim_preference_update, (const char *pref), "vp", P2V pref) char *STUB(shim_getmsghistory, (char *)"", (BOOLEAN_P a)) -VSTUB(shim_putmsghistory, (const char *a, BOOLEAN_P b)) -VSTUB(shim_status_init, (void)) -VSTUB(shim_status_enablefield, (int a, const char *b, const char *c, BOOLEAN_P d)) -VSTUB(shim_status_update, (int a, genericptr_t b, int c, int d, int e, unsigned long *f)) +VDECLCB(shim_putmsghistory, (const char *msg, BOOLEAN_P restoring_msghist), "vsi", P2V msg, A2P restoring_msghist) +VDECLCB(shim_status_init, (void), "v") +VDECLCB(shim_status_enablefield, + (int fieldidx, const char *nm, const char *fmt, BOOLEAN_P enable), + "vippi", + A2P fieldidx, P2V nm, P2V fmt, A2P enable) +VDECLCB(shim_status_update, + (int fldidx, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks), + "vipiiip", + A2P fldidx, P2V ptr, A2P chg, A2P percent, A2P color, P2V colormasks) - -/* old: | WC_TILED_MAP */ -/* Interface definition, for windows.c */ +/* Interface definition used in windows.c */ struct window_procs shim_procs = { "shim", (0 From ca9d1c5b974fb570e794e6435180e5e2bc8cea42 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 10:30:03 -0700 Subject: [PATCH 14/42] update build notes --- sys/lib/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sys/lib/README.md b/sys/lib/README.md index 3d314eb31..d27a3e875 100644 --- a/sys/lib/README.md +++ b/sys/lib/README.md @@ -4,6 +4,8 @@ This creates a library for NetHack that can be incorporated into other programs. * nethack.js / nethack.wasm - a [WebAssembly / WASM](https://webassembly.org/) library for use in JavaScript programs (both nodejs and browser) ## API: libnethack.a +This libarary has only been built on MacOS, but should work on Linux and other unix-ish platforms. If you have problems, start by stealing hints files from the `sys/unix/hints` for your platform. Contributions for other platforms are happily accepted. + The API is two functions: * `nhmain(int argc, char *argv[])` - The main function for NetHack that configures the program and runs the `moveloop()` until the game is over. The arguments to this function are the [command line arguments](https://nethackwiki.com/wiki/Options) to NetHack. * `stub_graphics_set_callback(stub_callback_t cb)` - A single function that sets a callback to gather graphics events: write a string to screen, get user input, etc. Your job is to pass in a callback and handle all the requested rendering events to show NetHack on the scrren. The callback is `void stub_callback_t(const char *name, void *ret_ptr, const char *fmt, ...)` From 42f948018d9fd75c6d2948a0716fc72e44258d44 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 10:31:17 -0700 Subject: [PATCH 15/42] initial helpers --- sys/lib/test/README.md | 8 ++ sys/lib/test/libtest.c | 106 +++++++++++++++++++++++ sys/lib/test/run.sh | 40 +++++++++ sys/lib/test/test.js | 186 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 340 insertions(+) create mode 100644 sys/lib/test/README.md create mode 100644 sys/lib/test/libtest.c create mode 100755 sys/lib/test/run.sh create mode 100644 sys/lib/test/test.js diff --git a/sys/lib/test/README.md b/sys/lib/test/README.md new file mode 100644 index 000000000..c9cfed377 --- /dev/null +++ b/sys/lib/test/README.md @@ -0,0 +1,8 @@ +Development helpers and tests for libnethack.a and nethack.js. + +Copy these files to the NetHack root directory. Commands include: +* run.sh wasm - rebuild makefiles and build nethack.js +* run.sh runwasm - simple testing of nethack.js +* run.sh lib - rebuild makefiles and build libnethack.a +* run.sh runlib - simple testing of libnethack.a +* run.sh bin - build the MacOS binary \ No newline at end of file diff --git a/sys/lib/test/libtest.c b/sys/lib/test/libtest.c new file mode 100644 index 000000000..9b554b986 --- /dev/null +++ b/sys/lib/test/libtest.c @@ -0,0 +1,106 @@ +#include + +/* external functions */ +int nhmain(int argc, char *argv[]); +typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); +void stub_graphics_set_callback(stub_callback_t cb); + +/* forward declarations */ +void window_cb(const char *name, void *ret_ptr, const char *fmt, ...); + + +int main(int argc, char *argv[]) { + stub_graphics_set_callback(window_cb); + nhmain(argc, argv); +} + +void window_cb(const char *name, void *ret_ptr, const char *fmt, ...) { + /* TODO -- see windowCallback below for hints */ + + /* DO SOMETHING HERE */ + *ret_ptr = yourFunctionToRenderGraphics(name, va_list args); +} + + + + +#if 0 +function variadicCallback(name, retPtr, fmt, args) { + // console.log ("variadicCallback called..."); + // console.log("typeof name", typeof name); + // console.log("typeof fmt", typeof fmt); + // console.log("typeof args", typeof args); + name = Module.UTF8ToString(name); + fmt = Module.UTF8ToString(fmt); + // console.log ("name:", name); + // console.log ("fmt:", fmt); + let argTypes = fmt.split(""); + let retType = argTypes.shift(); + // console.log ("arg count:", argTypes.length); + // console.log ("arg types:", argTypes); + // console.log ("ret type:", retType); + + let jsArgs = []; + for (let i = 0; i < argTypes.length; i++) { + let ptr = args + (4*i); + let val = typeLookup(argTypes[i], ptr); + jsArgs.push(val); + } + console.log(`graphics callback: ${name} [${jsArgs}]`); + setReturn(retPtr, retType); +} + +function setReturn(ptr, type, value = 0) { + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + value=value?value:"(no value)"; + var strPtr = Module.getValue(ptr, "i32"); + Module.stringToUTF8(value, strPtr, 1024); + break; + case "i": + Module.setValue(ptr, value, "i32"); + break; + case "c": + Module.setValue(ptr, value, "i8"); // 'Z' + break; + case "f": + // XXX: I'm not sure why 'double' works and 'float' doesn't + Module.setValue(ptr, value, "double"); + break; + case "d": + Module.setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } +} + +function typeLookup(type, ptr) { + switch(type) { + case "s": // string + return Module.UTF8ToString(Module.getValue(ptr, "*")); + case "p": // pointer + return Module.getValue(Module.getValue(ptr, "*"), "*"); + case "c": // char + return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + case "0": /* 2^0 = 1 byte */ + return Module.getValue(Module.getValue(ptr, "*"), "i8"); + case "1": /* 2^1 = 2 bytes */ + return Module.getValue(Module.getValue(ptr, "*"), "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return Module.getValue(Module.getValue(ptr, "*"), "i32"); + case "f": // float + return Module.getValue(Module.getValue(ptr, "*"), "float"); + case "d": // double + return Module.getValue(Module.getValue(ptr, "*"), "double"); + default: + throw new TypeError ("unknown type:" + type); + } +} +#endif /* 0 */ \ No newline at end of file diff --git a/sys/lib/test/run.sh b/sys/lib/test/run.sh new file mode 100755 index 000000000..5e2b36dfe --- /dev/null +++ b/sys/lib/test/run.sh @@ -0,0 +1,40 @@ +#!/bin/bash -x + +if [ x$1 == "xlib" ]; then + echo Doing lib... + make spotless + cd sys/lib + ./setup.sh hints/macOS.2020 + cd ../.. + make +fi + +if [ x$1 == "xrunlib" ]; then + LIBS="-Lsrc -lnethack -Llib/lua -llua -lm" + BADLIBS="-lncurses" + gcc -o nhlibtest libtest.c $LIBS $BADLIBS + ./nhlibtest +fi + +if [ x$1 == "xwasm" ]; then + echo Doing wasm... + make spotless + cd sys/lib + ./setup.sh hints/wasm + cd ../.. + make +fi + +if [ x$1 == "xrunwasm" ]; then + node test.js +fi + +if [ x$1 == "xbin" ]; then + echo Doing bin... + make spotless + cd sys/unix + ./setup.sh hints/macOS.2020 + cd ../.. + make +fi + diff --git a/sys/lib/test/test.js b/sys/lib/test/test.js new file mode 100644 index 000000000..146b56d8a --- /dev/null +++ b/sys/lib/test/test.js @@ -0,0 +1,186 @@ +const util = require("util"); + +// Emscripten Module config +let Module = { + // noInitialRun: true, + onRuntimeInitialized: function () { + setGraphicsCallback(); + }, +}; + +var factory = require("./src/nethack.js"); + +// load and run the module +factory(Module); + +function setGraphicsCallback() { + console.log("setting callback function"); + let cb = Module.addFunction(windowCallback, "viiii"); + + console.log("doing call"); + Module.ccall( + "stub_graphics_set_callback", // C function name + null, // return type + ["number"], // arg types + [cb], // arg values + {async: true} // options + ); + + /* TODO: removeFunction */ +} + +function windowCallback(name, retPtr, fmt, args) { + // console.log ("variadicCallback called..."); + // console.log("typeof name", typeof name); + // console.log("typeof fmt", typeof fmt); + // console.log("typeof args", typeof args); + name = Module.UTF8ToString(name); + fmt = Module.UTF8ToString(fmt); + // console.log ("name:", name); + // console.log ("fmt:", fmt); + let argTypes = fmt.split(""); + let retType = argTypes.shift(); + // console.log ("arg count:", argTypes.length); + // console.log ("arg types:", argTypes); + // console.log ("ret type:", retType); + + let jsArgs = []; + for (let i = 0; i < argTypes.length; i++) { + let ptr = args + (4*i); + let val = typeLookup(argTypes[i], ptr); + jsArgs.push(val); + } + console.log(`graphics callback: ${name} [${jsArgs}]`); + let retVal = doGraphics(name, ... jsArgs); + setReturn(name, retPtr, retType, retVal); +} + +function typeLookup(type, ptr) { + switch(type) { + case "s": // string + return Module.UTF8ToString(Module.getValue(ptr, "*")); + case "p": // pointer + ptr = Module.getValue(ptr, "*"); + if(!ptr) return 0; // null pointer + return Module.getValue(ptr, "*"); + case "c": // char + return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + case "0": /* 2^0 = 1 byte */ + return Module.getValue(Module.getValue(ptr, "*"), "i8"); + case "1": /* 2^1 = 2 bytes */ + return Module.getValue(Module.getValue(ptr, "*"), "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return Module.getValue(Module.getValue(ptr, "*"), "i32"); + case "f": // float + return Module.getValue(Module.getValue(ptr, "*"), "float"); + case "d": // double + return Module.getValue(Module.getValue(ptr, "*"), "double"); + default: + throw new TypeError ("unknown type:" + type); + } +} + +function setReturn(name, ptr, type, value = 0) { + + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + if(typeof value !== "string") + throw new TypeError(`expected ${name} return type to be string`); + value=value?value:"(no value)"; + var strPtr = Module.getValue(ptr, "i32"); + Module.stringToUTF8(value, strPtr, 1024); + break; + case "i": + if(typeof value !== "number" || !Number.isInteger(value)) + throw new TypeError(`expected ${name} return type to be integer`); + Module.setValue(ptr, value, "i32"); + break; + case "c": + if(typeof value !== "number" || value < 0 || value > 128) + throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); + Module.setValue(ptr, value, "i8"); + break; + case "f": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + // XXX: I'm not sure why 'double' works and 'float' doesn't + Module.setValue(ptr, value, "double"); + break; + case "d": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + Module.setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } + + function isFloat(n){ + return n === +n && n !== (n|0) && !Number.isInteger(n); + } +} + +let winCount = 0; +function doGraphics(name, ... args) { + switch(name) { + case "shim_create_nhwindow": + winCount++; + console.log("creating window", args, "returning", winCount); + return winCount; + case "shim_yn_function": + case "shim_message_menu": + return 121; // 'y' + case "shim_nhgetch": + case "shim_nh_poskey": + return 46; + default: + return 0; + } +} + +// var Module = null; +// factory(Module).then((ret) => { +// if(ret !== Module) { +// console.log("ret and Module are not the same"); +// } else { +// console.log("ret and Module are the same"); +// } +// // console.log("Module", util.inspect(Module, {depth: null, showHidden: true})); +// // TODO: +// // stub_graphics_set_callback(); + +// // options: +// // https://emscripten.org/docs/api_reference/module.html +// // logReadFiles +// // printWithColors +// // noInitialRun +// // onRuntimeInitialized +// console.log("doing run"); +// // Module.run(); + +// // // main loop +// // console.log("creating function"); +// // let fn = Module.addFunction(wasmCallback, "vii"); + +// // console.log("doing call"); +// // Module.ccall( +// // "mainloop", // C function name +// // null, // return type +// // ["number"], // arg types +// // [fn], // arg values +// // {async: true} // options +// // ); +// // // TODO: removeFunction(fn) + +// // var i = 0; +// // setInterval(function() { +// // console.log("interval", i++); +// // },1000); +// }); + From 85108bf7ef5f5f8346d2b16e60d5185a67cac429 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 10:45:54 -0700 Subject: [PATCH 16/42] add build section, fix naming --- sys/lib/README.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/sys/lib/README.md b/sys/lib/README.md index d27a3e875..cbbd87b51 100644 --- a/sys/lib/README.md +++ b/sys/lib/README.md @@ -3,12 +3,27 @@ This creates a library for NetHack that can be incorporated into other programs. * libnethack.a - a binary Unix library * nethack.js / nethack.wasm - a [WebAssembly / WASM](https://webassembly.org/) library for use in JavaScript programs (both nodejs and browser) -## API: libnethack.a -This libarary has only been built on MacOS, but should work on Linux and other unix-ish platforms. If you have problems, start by stealing hints files from the `sys/unix/hints` for your platform. Contributions for other platforms are happily accepted. +## Build +This library has only been built on MacOS, but should work on Linux and other unix-ish platforms. If you have problems, start by stealing hints files from the `sys/unix/hints` for your platform. Contributions for other platforms are happily accepted. +Building the WASM module requires that you have the [emscripten toolchain / sdk installed](https://emscripten.org/docs/getting_started/downloads.html). + +Generally the build is the same as the unix build: +1. `cd sys/lib` +2. For `libnethack.a`: `./setup.sh hints/macOS.2020`; for `nethack.js`: `./setup.sh hints/wasm` +3. `cd ../..` +4. `make` + +Resulting libaries will be in the `src` directory. + +WASM also has a npm module that can be published out of `sys/lib/npm-library`. After building the `nethack.js` it can be published by: +1. `cd sys/lib/npm-library` +2. `npm publish` + +## API: libnethack.a The API is two functions: * `nhmain(int argc, char *argv[])` - The main function for NetHack that configures the program and runs the `moveloop()` until the game is over. The arguments to this function are the [command line arguments](https://nethackwiki.com/wiki/Options) to NetHack. -* `stub_graphics_set_callback(stub_callback_t cb)` - A single function that sets a callback to gather graphics events: write a string to screen, get user input, etc. Your job is to pass in a callback and handle all the requested rendering events to show NetHack on the scrren. The callback is `void stub_callback_t(const char *name, void *ret_ptr, const char *fmt, ...)` +* `shim_graphics_set_callback(shim_callback_t cb)` - A single function that sets a callback to gather graphics events: write a string to screen, get user input, etc. Your job is to pass in a callback and handle all the requested rendering events to show NetHack on the scrren. The callback is `void shim_callback_t(const char *name, void *ret_ptr, const char *fmt, ...)` * `name` is the name of the [window function](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc) that needs to be handled * `ret_ptr` is a pointer to a memory space for the return value. The type expected to be returned in this pointer is described by the first character of the `fmt` string. * `fmt` is a string that describes the signature of the callback. The first character in the string is the return type and any additional characters describe the variable arguments: `i` for integer, `s` for string, `p` for pointer, `c` for character, `v` for void. For example, if format is "vis" the callback will have no return (void), the first argument will be an integer, and the second argument will be a string. If format is "iii" the callback must return an integer, and both the arguments passed in will be integers. @@ -17,11 +32,9 @@ The API is two functions: Where is the header file for the API you ask? There isn't one. It's three functions, just drop the forward declarations at the top of your file (or create your own header). It's more work figuring out how to install and copy around header files than it's worth for such a small API. If you disagree, feel free to sumbit a PR to fix it. :) ## API: nethack.js -Building the WASM module requires that you have the [emscripten toolchain / sdk installed](https://emscripten.org/docs/getting_started/downloads.html). - The WebAssembly API has a similar signature to `libnethack.a` with minor syntactic differences: * `main(int argc, char argv[])` - The main function for NetHack -* `stub_graphics_set_callback(stub_callback_t cb)` - The same as above, but the signature of the callback is slightly different because WASM can't handle variadic callbacks. The callback is: `void stub_callback_t(const char *name, void *ret_ptr, const char *fmt, void *args[])` +* `shim_graphics_set_callback(shim_callback_t cb)` - The same as above, but the signature of the callback is slightly different because WASM can't handle variadic callbacks. The callback is: `void shim_callback_t(const char *name, void *ret_ptr, const char *fmt, void *args[])` * `name` - same as above * `ret_ptr` - same as above * `fmt` - same as above @@ -30,22 +43,22 @@ The WebAssembly API has a similar signature to `libnethack.a` with minor syntact ## API Stability -The "stub graphics" API should generally be stable. I aspire to replace the command line arguments (argc / argv) with a structure of options, so the `nhmain()` and `main()` functions may change at some point. +The "shim graphics" API should generally be stable. I aspire to replace the command line arguments (argc / argv) with a structure of options, so the `nhmain()` and `main()` functions may change at some point. ## libnethack.a example ``` c #include int nhmain(int argc, char *argv[]); -typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); -void stub_graphics_set_callback(stub_callback_t cb); +typedef void(*shim_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); +void shim_graphics_set_callback(shim_callback_t cb); void window_cb(const char *name, void *ret_ptr, const char *fmt, ...) { /* TODO -- see windowCallback below for hints */ } int main(int argc, char *argv[]) { - stub_graphics_set_callback(window_cb); + shim_graphics_set_callback(window_cb); nhmain(argc, argv); } ``` @@ -76,7 +89,7 @@ function setGraphicsCallback() { console.log("setting callback function with library"); Module.ccall( - "stub_graphics_set_callback", // C function name + "shim_graphics_set_callback", // C function name null, // return type ["number"], // arg types [cb], // arg values From 4ffb8c9ce364e2bed4439ec8ec5c133188a4179e Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 10:46:55 -0700 Subject: [PATCH 17/42] fix naming --- sys/lib/test/test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/lib/test/test.js b/sys/lib/test/test.js index 146b56d8a..d7d8f0144 100644 --- a/sys/lib/test/test.js +++ b/sys/lib/test/test.js @@ -19,7 +19,7 @@ function setGraphicsCallback() { console.log("doing call"); Module.ccall( - "stub_graphics_set_callback", // C function name + "shim_graphics_set_callback", // C function name null, // return type ["number"], // arg types [cb], // arg values @@ -153,7 +153,7 @@ function doGraphics(name, ... args) { // } // // console.log("Module", util.inspect(Module, {depth: null, showHidden: true})); // // TODO: -// // stub_graphics_set_callback(); +// // shim_graphics_set_callback(); // // options: // // https://emscripten.org/docs/api_reference/module.html From efae5590e59141ed853bb836f76c2995cf4dc627 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 11:01:34 -0700 Subject: [PATCH 18/42] completed shim graphics callbacks --- sys/lib/hints/wasm | 4 ++-- win/shim/winshim.c | 39 +++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/sys/lib/hints/wasm b/sys/lib/hints/wasm index 799efe03c..711017bae 100644 --- a/sys/lib/hints/wasm +++ b/sys/lib/hints/wasm @@ -17,8 +17,8 @@ EMCC_LFLAGS=-s WASM=1 EMCC_LFLAGS+=-s ALLOW_TABLE_GROWTH EMCC_LFLAGS+=-s ASYNCIFY -s ASYNCIFY_IMPORTS='["_nhmain"]' -O3 EMCC_LFLAGS+=-s MODULARIZE -EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main", "_stub_graphics_set_callback"]' -EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString", "getValue"]' +EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback"]' +EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString", "getValue", "setValue"]' EMCC_LFLAGS+=-s ERROR_ON_UNDEFINED_SYMBOLS=0 EMCC_LFLAGS+=--embed-file wasm-data@/ diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 310a4c315..05ed09c07 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -16,21 +16,21 @@ #undef SHIM_DEBUG #ifndef __EMSCRIPTEN__ -typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); +typedef void(*shim_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); #else /* __EMSCRIPTEN__ */ /* WASM can't handle a variadic callback, so we pass back an array of pointers instead... */ -typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, void *args[]); +typedef void(*shim_callback_t)(const char *name, void *ret_ptr, const char *fmt, void *args[]); #endif /* !__EMSCRIPTEN__ */ /* this is the primary interface to shim graphics, * call this function with your declared callback function * and you will receive all the windowing calls */ -static stub_callback_t shim_graphics_callback = NULL; +static shim_callback_t shim_graphics_callback = NULL; #ifdef __EMSCRIPTEN__ EMSCRIPTEN_KEEPALIVE #endif -void stub_graphics_set_callback(stub_callback_t cb) { +void shim_graphics_set_callback(shim_callback_t cb) { shim_graphics_callback = cb; } @@ -84,10 +84,10 @@ void name fn_args { \ enum win_types { - WINSTUB_MESSAGE = 1, - WINSTUB_MAP, - WINSTUB_MENU, - WINSTUB_EXT + WINSHIM_MESSAGE = 1, + WINSHIM_MAP, + WINSHIM_MENU, + WINSHIM_EXT }; #define VSTUB(name, args) \ @@ -111,8 +111,7 @@ VDECLCB(shim_get_nh_event,(void), "v") VDECLCB(shim_exit_nhwindows,(const char *str), "vs", P2V str) VDECLCB(shim_suspend_nhwindows,(const char *str), "vs", P2V str) VDECLCB(shim_resume_nhwindows,(void), "v") -// DECLCB(winid, shim_create_nhwindow, (int type), "ii", A2P type) -winid STUB(shim_create_nhwindow, WINSTUB_MAP, (int a)) +DECLCB(winid, shim_create_nhwindow, (int type), "ii", A2P type) VDECLCB(shim_clear_nhwindow,(winid window), "vi", A2P window) VDECLCB(shim_display_nhwindow,(winid window, BOOLEAN_P blocking), "vii", A2P window, A2P blocking) VDECLCB(shim_destroy_nhwindow,(winid window), "vi", A2P window) @@ -125,8 +124,8 @@ VDECLCB(shim_add_menu, "viipiiisi", A2P window, A2P glyph, P2V identifier, A2P ch, A2P gch, A2P attr, P2V str, A2P itemflags) VDECLCB(shim_end_menu,(winid window, const char *prompt), "vis", A2P window, P2V prompt) -int STUB(shim_select_menu,0,(winid a, int b, MENU_ITEM_P **c)) -char STUB(shim_message_menu,'y',(CHAR_P a, int b, const char *c)) +DECLCB(int, shim_select_menu,(winid window, int how, MENU_ITEM_P **menu_list), "iiip", A2P window, A2P how, P2V menu_list) +DECLCB(char, shim_message_menu,(CHAR_P let, int how, const char *mesg), "ciis", A2P let, A2P how, P2V mesg) VDECLCB(shim_update_inventory,(void), "v") VDECLCB(shim_mark_synch,(void), "v") VDECLCB(shim_wait_synch,(void), "v") @@ -135,25 +134,25 @@ VDECLCB(shim_update_positionbar,(char *posbar), "vp", P2V posbar) VDECLCB(shim_print_glyph,(winid w, int x, int y, int glyph, int bkglyph), "viiiii", A2P w, A2P x, A2P y, A2P glyph, A2P bkglyph) VDECLCB(shim_raw_print,(const char *str), "vs", P2V str) VDECLCB(shim_raw_print_bold,(const char *str), "vs", P2V str) -int STUB(shim_nhgetch,0,(void)) -int STUB(shim_nh_poskey,0,(int *a, int *b, int *c)) +DECLCB(int, shim_nhgetch,(void), "i") +DECLCB(int, shim_nh_poskey,(int *x, int *y, int *mod), "ippp", P2V x, P2V y, P2V mod) VDECLCB(shim_nhbell,(void), "v") -int STUB(shim_doprev_message,0,(void)) -char STUB(shim_yn_function,'y',(const char *a, const char *b, CHAR_P c)) +DECLCB(int, shim_doprev_message,(void),"iv") +DECLCB(char, shim_yn_function,(const char *query, const char *resp, CHAR_P def), "cssi", P2V query, P2V resp, A2P def) VDECLCB(shim_getlin,(const char *query, char *bufp), "vsp", P2V query, P2V bufp) -int STUB(shim_get_ext_cmd,0,(void)) +DECLCB(int,shim_get_ext_cmd,(void),"iv") VDECLCB(shim_number_pad,(int state), "vi", A2P state) VDECLCB(shim_delay_output,(void), "v") VDECLCB(shim_change_color,(int color, long rgb, int reverse), "viii", A2P color, A2P rgb, A2P reverse) VDECLCB(shim_change_background,(int white_or_black), "vi", A2P white_or_black) -short STUB(set_shim_font_name,0,(winid a, char *b)) -char *STUB(shim_get_color_string,"",(void)) +DECLCB(short, set_shim_font_name,(winid window_type, char *font_name),"2is", A2P window_type, P2V font_name) +DECLCB(char *,shim_get_color_string,(void),"sv") /* other defs that really should go away (they're tty specific) */ VDECLCB(shim_start_screen, (void), "v") VDECLCB(shim_end_screen, (void), "v") VDECLCB(shim_preference_update, (const char *pref), "vp", P2V pref) -char *STUB(shim_getmsghistory, (char *)"", (BOOLEAN_P a)) +DECLCB(char *,shim_getmsghistory, (BOOLEAN_P init), "si", A2P init) VDECLCB(shim_putmsghistory, (const char *msg, BOOLEAN_P restoring_msghist), "vsi", P2V msg, A2P restoring_msghist) VDECLCB(shim_status_init, (void), "v") VDECLCB(shim_status_enablefield, From ffa5e319afe94ea4851b197f00c34c195f96c7bd Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 11:59:23 -0700 Subject: [PATCH 19/42] ignore libnethack dev files --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b9196f2d1..193f1cfbd 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ Release/ binary/ build/ ipch/ -./lib/ +lib/* Nethack.sln Nethack.sdf Nethack.opensdf @@ -88,3 +88,7 @@ src/nethack.wasm src/nethack.js src/wasm-data src/libnethack.a +libtest.c +nhlibtest +run.sh +test.js From 356e7432e71ae52e112060c5a0af91882b593b72 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 12:00:12 -0700 Subject: [PATCH 20/42] ignore libnethack build files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 193f1cfbd..030d3d92e 100644 --- a/.gitignore +++ b/.gitignore @@ -92,3 +92,5 @@ libtest.c nhlibtest run.sh test.js +sys/lib/npm-package/build/nethack.js +sys/lib/npm-package/build/nethack.wasm From b0e77f77290594ea92939c3771ed4c8fdc88bf1d Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 12:00:39 -0700 Subject: [PATCH 21/42] initial npm package --- sys/lib/npm-package/LICENSE.md | 95 ++++++++++++++++++ sys/lib/npm-package/README.md | 44 +++++++++ sys/lib/npm-package/package.json | 20 ++++ sys/lib/npm-package/src/nethackShim.js | 127 +++++++++++++++++++++++++ 4 files changed, 286 insertions(+) create mode 100644 sys/lib/npm-package/LICENSE.md create mode 100644 sys/lib/npm-package/README.md create mode 100644 sys/lib/npm-package/package.json create mode 100644 sys/lib/npm-package/src/nethackShim.js diff --git a/sys/lib/npm-package/LICENSE.md b/sys/lib/npm-package/LICENSE.md new file mode 100644 index 000000000..5ad7e3417 --- /dev/null +++ b/sys/lib/npm-package/LICENSE.md @@ -0,0 +1,95 @@ + NETHACK GENERAL PUBLIC LICENSE + (Copyright 1989 M. Stephenson) + + (Based on the BISON general public license, + copyright 1988 Richard M. Stallman) + + Everyone is permitted to copy and distribute verbatim copies of this + license, but changing it is not allowed. You can also use this wording to + make the terms for other programs. + + The license agreements of most software companies keep you at the mercy of +those companies. By contrast, our general public license is intended to give +everyone the right to share NetHack. To make sure that you get the rights we +want you to have, we need to make restrictions that forbid anyone to deny you +these rights or to ask you to surrender the rights. Hence this license +agreement. + + Specifically, we want to make sure that you have the right to give away +copies of NetHack, that you receive source code or else can get it if you +want it, that you can change NetHack or use pieces of it in new free +programs, and that you know you can do these things. + + To make sure that everyone has such rights, we have to forbid you to +deprive anyone else of these rights. For example, if you distribute copies +of NetHack, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And you +must tell them their rights. + + Also, for our own protection, we must make certain that everyone finds out +that there is no warranty for NetHack. If NetHack is modified by someone +else and passed on, we want its recipients to know that what they have is +not what we distributed. + + Therefore we (Mike Stephenson and other holders of NetHack copyrights) make +the following terms which say what you must do to be allowed to distribute or +change NetHack. + + + COPYING POLICIES + + 1. You may copy and distribute verbatim copies of NetHack source code as +you receive it, in any medium, provided that you keep intact the notices on +all files that refer to copyrights, to this License Agreement, and to the +absence of any warranty; and give any other recipients of the NetHack +program a copy of this License Agreement along with the program. + + 2. You may modify your copy or copies of NetHack or any portion of it, and +copy and distribute such modifications under the terms of Paragraph 1 above +(including distributing this License Agreement), provided that you also do the +following: + + a) cause the modified files to carry prominent notices stating that you + changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that in + whole or in part contains or is a derivative of NetHack or any part + thereof, to be licensed at no charge to all third parties on terms + identical to those contained in this License Agreement (except that you + may choose to grant more extensive warranty protection to some or all + third parties, at your option) + + c) You may charge a distribution fee for the physical act of + transferring a copy, and you may at your option offer warranty protection + in exchange for a fee. + + 3. You may copy and distribute NetHack (or a portion or derivative of it, +under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete machine-readable source code, which + must be distributed under the terms of Paragraphs 1 and 2 above; or, + + b) accompany it with full information as to how to obtain the complete + machine-readable source code from an appropriate archive site. (This + alternative is allowed only for noncommercial distribution.) + +For these purposes, complete source code means either the full source +distribution as originally released over Usenet or updated copies of the +files in this distribution used to create the object code or executable. + + 4. You may not copy, sublicense, distribute or transfer NetHack except as +expressly provided under this License Agreement. Any attempt otherwise to +copy, sublicense, distribute or transfer NetHack is void and your rights to +use the program under this License agreement shall be automatically +terminated. However, parties who have received computer software programs +from you with this License Agreement will not have their licenses terminated +so long as such parties remain in full compliance. + + +Stated plainly: You are permitted to modify NetHack, or otherwise use parts +of NetHack, provided that you comply with the conditions specified above; +in particular, your modified NetHack or program containing parts of NetHack +must remain freely available as provided in this License Agreement. In +other words, go ahead and share NetHack, but don't try to stop anyone else +from sharing it farther. diff --git a/sys/lib/npm-package/README.md b/sys/lib/npm-package/README.md new file mode 100644 index 000000000..891237492 --- /dev/null +++ b/sys/lib/npm-package/README.md @@ -0,0 +1,44 @@ +# NetHack +This is the ACTUAL NetHack game, originally written in 1982 and one of the longest-standing open source collaborations. This isn't a wrapper around the binary NetHack, but the game itself compiled into [WebAssembly](https://webassembly.org/) (WASM) using [emscripten](https://emscripten.org/). This module should run [anywhere WebAssembly is supported](https://developer.mozilla.org/en-US/docs/WebAssembly#Browser_compatibility) including node.js and modern browsers. + +Since NetHack typically uses the [TTY](https://en.wikipedia.org/wiki/Computer_terminal#Text_terminals) to show depictions of the game and that won't work for WebAssembly, you are required to implement the graphics portion of NetHack to make this work. This allows a wide variety of UIs to be created, both text and graphics based as well as using keyboard and mouse to control the game. The API for implementing graphics is described below. + +## Install + +``` sh +npm install nethack +``` + +## API +The main module returns a setup function: `startNethack(myCallback, moduleOptions)`. + +## Example +``` js +let nethackStart = require("nethack"); + +nethackStart(doGraphics); + +let winCount = 0; +function doGraphics(name, ... args) { + console.log(`shim graphics: ${name} [${args}]`); + + switch(name) { + case "shim_create_nhwindow": + winCount++; + console.log("creating window", args, "returning", winCount); + return winCount; + case "shim_yn_function": + case "shim_message_menu": + return 121; // return 'y' to all questions + case "shim_nhgetch": + case "shim_nh_poskey": + return 0; // simulates a mouse click on "exit up the stairs" + default: + return 0; + } +} +``` + +## Other Notes +* This module isn't small -- the WASM code is about 10MB. It may be slow to load over non-broadband connections. There are some emscripten build optimizations that may help with browser builds (such as dynamic loading), but those aren't currently part of this package. Pull requests are always welcome. :) +* This hasn't been tested on browsers. If you get this to work on a browser, please let me know and I will add notes. Or if anyone wants to help setup automated browser testing, that would be supremely appreciated. \ No newline at end of file diff --git a/sys/lib/npm-package/package.json b/sys/lib/npm-package/package.json new file mode 100644 index 000000000..ae04ac7fe --- /dev/null +++ b/sys/lib/npm-package/package.json @@ -0,0 +1,20 @@ +{ + "name": "nethack", + "version": "1.0.0", + "description": "The original NetHack rogue-like game built as a WebAssembly module", + "main": "src/nethackShim.js", + "scripts": { + "test": "node test/test.js", + "clean": "rm ./build/nethack.js; rm ./build/nethack.wasm; true", + "build": "cp ../../../src/nethack.js ../../../src/nethack.wasm ./build", + "prepack": "npm run build" + }, + "keywords": [ + "nethack", + "rogue", + "rogue-like", + "game" + ], + "author": "Adam Powers ", + "license": "SEE LICENSE IN LICENSE.md" +} diff --git a/sys/lib/npm-package/src/nethackShim.js b/sys/lib/npm-package/src/nethackShim.js new file mode 100644 index 000000000..98d0cf808 --- /dev/null +++ b/sys/lib/npm-package/src/nethackShim.js @@ -0,0 +1,127 @@ +const path = require("path"); + +let Module; +let userCallback; +let savedOnRuntimeInitialized; +function nethackStart(cb, inputModule = {}) { + console.log("cb is", cb); + if(typeof cb !== "function") throw new TypeError("expected first argument to be callback function"); + if(typeof inputModule !== "object") throw new TypeError("expected second argument to be object"); + + // Emscripten Module config + Module = inputModule; + userCallback = cb; + savedOnRuntimeInitialized = Module.onRuntimeInitialized; + Module.onRuntimeInitialized = function (... args) { + // after the WASM is loaded, add the shim graphics callback function + let cb = Module.addFunction(windowCallback, "viiii"); + Module.ccall( + "shim_graphics_set_callback", // C function name + null, // return type + ["number"], // arg types + [cb], // arg values + {async: true} // options + ); + + /* TODO: Module.removeFunction() */ + + // if the user had their own onRuntimeInitialized(), call it now + if (savedOnRuntimeInitialized) savedOnRuntimeInitialized(... args); + }; + + // load and run the module + var factory = require(path.join(__dirname, "../build/nethack.js")); + factory(Module); +} + +function windowCallback(name, retPtr, fmt, args) { + name = Module.UTF8ToString(name); + fmt = Module.UTF8ToString(fmt); + let argTypes = fmt.split(""); + let retType = argTypes.shift(); + + // build array of JavaScript args from WASM parameters + let jsArgs = []; + for (let i = 0; i < argTypes.length; i++) { + let ptr = args + (4*i); + let val = typeLookup(argTypes[i], ptr); + jsArgs.push(val); + } + let retVal = userCallback(name, ... jsArgs); + setReturn(name, retPtr, retType, retVal); +} + +function typeLookup(type, ptr) { + switch(type) { + case "s": // string + return Module.UTF8ToString(Module.getValue(ptr, "*")); + case "p": // pointer + ptr = Module.getValue(ptr, "*"); + if(!ptr) return 0; // null pointer + return Module.getValue(ptr, "*"); + case "c": // char + return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + case "0": /* 2^0 = 1 byte */ + return Module.getValue(Module.getValue(ptr, "*"), "i8"); + case "1": /* 2^1 = 2 bytes */ + return Module.getValue(Module.getValue(ptr, "*"), "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return Module.getValue(Module.getValue(ptr, "*"), "i32"); + case "f": // float + return Module.getValue(Module.getValue(ptr, "*"), "float"); + case "d": // double + return Module.getValue(Module.getValue(ptr, "*"), "double"); + default: + throw new TypeError ("unknown type:" + type); + } +} + +function setReturn(name, ptr, type, value = 0) { + + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + if(typeof value !== "string") + throw new TypeError(`expected ${name} return type to be string`); + value=value?value:"(no value)"; + var strPtr = Module.getValue(ptr, "i32"); + Module.stringToUTF8(value, strPtr, 1024); + break; + case "i": + if(typeof value !== "number" || !Number.isInteger(value)) + throw new TypeError(`expected ${name} return type to be integer`); + Module.setValue(ptr, value, "i32"); + break; + case "c": + if(typeof value !== "number" || value < 0 || value > 128) + throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); + Module.setValue(ptr, value, "i8"); + break; + case "f": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + // XXX: I'm not sure why 'double' works and 'float' doesn't + Module.setValue(ptr, value, "double"); + break; + case "d": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + Module.setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } + + function isFloat(n){ + return n === +n && n !== (n|0) && !Number.isInteger(n); + } +} + +// TODO: ES6 'import' style module +module.exports = nethackStart; + From 0c8e4e555efbdade85abc2c5bd401dab2e3e359e Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 30 Aug 2020 12:09:42 -0700 Subject: [PATCH 22/42] complete API documentation --- sys/lib/npm-package/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/lib/npm-package/README.md b/sys/lib/npm-package/README.md index 891237492..e71f3e5a1 100644 --- a/sys/lib/npm-package/README.md +++ b/sys/lib/npm-package/README.md @@ -10,7 +10,10 @@ npm install nethack ``` ## API -The main module returns a setup function: `startNethack(myCallback, moduleOptions)`. +The main module returns a setup function: `startNethack(uiCallback, moduleOptions)`. +* `uiCallback(name, ... args)` - Your callback function that will handle rendering NetHack on the screen of your choice. The `name` argument is one of the UI functions of the [NetHack Window Interface](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc) and the `args` are corresponding to the window interface function that is being called. You are required to return the correct type of data for the function that is implemented. +* `moduleOptions` - An optional [emscripten Module object](https://emscripten.org/docs/api_reference/module.html) for configuring the WASM that will be run. + * `Module.arguments` - Of note is the [arguments property](https://emscripten.org/docs/api_reference/module.html#Module.arguments) which gets passed to NetHack as its [command line parameters](https://nethackwiki.com/wiki/Options). ## Example ``` js From 490b2604544de227eb7538c47a257ca5fa3727e4 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Tue, 1 Sep 2020 13:11:09 -0700 Subject: [PATCH 23/42] remove stray console.log --- sys/lib/npm-package/src/nethackShim.js | 1 - 1 file changed, 1 deletion(-) diff --git a/sys/lib/npm-package/src/nethackShim.js b/sys/lib/npm-package/src/nethackShim.js index 98d0cf808..638460278 100644 --- a/sys/lib/npm-package/src/nethackShim.js +++ b/sys/lib/npm-package/src/nethackShim.js @@ -4,7 +4,6 @@ let Module; let userCallback; let savedOnRuntimeInitialized; function nethackStart(cb, inputModule = {}) { - console.log("cb is", cb); if(typeof cb !== "function") throw new TypeError("expected first argument to be callback function"); if(typeof inputModule !== "object") throw new TypeError("expected second argument to be object"); From caaa9b058858bfd5c2ca1ca865e654d8605aa9cb Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Wed, 2 Sep 2020 07:27:29 -0700 Subject: [PATCH 24/42] remove stray printfs --- sys/lib/libnethackmain.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sys/lib/libnethackmain.c b/sys/lib/libnethackmain.c index ed8142bd7..5f000847f 100644 --- a/sys/lib/libnethackmain.c +++ b/sys/lib/libnethackmain.c @@ -76,9 +76,6 @@ char *argv[]; boolean plsel_once = FALSE; int i; - printf ("nhmain\n"); - printf ("argc: %d\n", argc); - printf ("argv: %p\n", (void *)argv); for (i = 0; i < argc; i++) { printf ("argv[%d]: %s\n", i, argv[i]); } From a8373f22244f11a0c240b8cdcce7b6026952481e Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Wed, 2 Sep 2020 07:28:01 -0700 Subject: [PATCH 25/42] move package to npm organization --- sys/lib/npm-package/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/lib/npm-package/package.json b/sys/lib/npm-package/package.json index ae04ac7fe..f4e095261 100644 --- a/sys/lib/npm-package/package.json +++ b/sys/lib/npm-package/package.json @@ -1,5 +1,5 @@ { - "name": "nethack", + "name": "@neth_ck/nethack", "version": "1.0.0", "description": "The original NetHack rogue-like game built as a WebAssembly module", "main": "src/nethackShim.js", From 045a5879f141b5cf3504cac39673663d7f4cc93d Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Wed, 2 Sep 2020 07:42:29 -0700 Subject: [PATCH 26/42] npm doesn't allow 'hack' in package names --- sys/lib/npm-package/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/lib/npm-package/package.json b/sys/lib/npm-package/package.json index f4e095261..45cea1739 100644 --- a/sys/lib/npm-package/package.json +++ b/sys/lib/npm-package/package.json @@ -1,5 +1,5 @@ { - "name": "@neth_ck/nethack", + "name": "@neth_ck/neth_ck", "version": "1.0.0", "description": "The original NetHack rogue-like game built as a WebAssembly module", "main": "src/nethackShim.js", From 96cf11ad718eeb61950ab4e6274daed7dd08f391 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 6 Sep 2020 17:32:08 -0700 Subject: [PATCH 27/42] fix JS event loop blocking --- sys/lib/README.md | 144 ++++---------------- sys/lib/hints/wasm | 13 +- sys/lib/npm-package/README.md | 4 +- sys/lib/npm-package/src/nethackShim.js | 113 +++------------- sys/lib/test/libtest.c | 23 +++- win/shim/winshim.c | 179 +++++++++++++++++++++---- 6 files changed, 229 insertions(+), 247 deletions(-) diff --git a/sys/lib/README.md b/sys/lib/README.md index cbbd87b51..3bc7c283a 100644 --- a/sys/lib/README.md +++ b/sys/lib/README.md @@ -34,12 +34,10 @@ Where is the header file for the API you ask? There isn't one. It's three functi ## API: nethack.js The WebAssembly API has a similar signature to `libnethack.a` with minor syntactic differences: * `main(int argc, char argv[])` - The main function for NetHack -* `shim_graphics_set_callback(shim_callback_t cb)` - The same as above, but the signature of the callback is slightly different because WASM can't handle variadic callbacks. The callback is: `void shim_callback_t(const char *name, void *ret_ptr, const char *fmt, void *args[])` - * `name` - same as above - * `ret_ptr` - same as above - * `fmt` - same as above - * `args` - an array of pointers to the arguments, where each pointer can be de-referenced to a value as specified in the `fmt` string. - +* `shim_graphics_set_callback(char *cbName)` - A `String` representing a name of a callback function. The callback function be registered as `globalThis[cbName] = function yourCallback(name, ... args) { /* your stuff */ }`. Note that [globalThis](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis) points to `window` in browsers and `global` in node.js. + * `name` is the name of the [window function](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc) that needs to be handled + * `... args` is a variable number and type of arguments depending on the `window function` that is being called. The arguments associated with each `name` are described in the [NetHack window.doc](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc) + * The function must return the value expected for the specified `name` ## API Stability @@ -54,7 +52,7 @@ typedef void(*shim_callback_t)(const char *name, void *ret_ptr, const char *fmt, void shim_graphics_set_callback(shim_callback_t cb); void window_cb(const char *name, void *ret_ptr, const char *fmt, ...) { - /* TODO -- see windowCallback below for hints */ + /* TODO */ } int main(int argc, char *argv[]) { @@ -65,117 +63,33 @@ int main(int argc, char *argv[]) { ## nethack.js example ``` js -// Module is defined by emscripten -// https://emscripten.org/docs/api_reference/module.html -let Module = { - // if this is true, main() won't be called automatically - // noInitialRun: true, +const path = require("path"); - // after loading the library, set the callback function - onRuntimeInitialized: function (... args) { - setGraphicsCallback(); - } -}; +// starts nethack +function nethackStart(cb, inputModule = {}) { + // set callback + let cbName = cb.name; + if (cbName === "") cbName = "__anonymousNetHackCallback"; + let userCallback = globalThis[cbName] = cb; -var factory = require("./src/nethack.js"); + // Emscripten Module config + let Module = inputModule; + savedOnRuntimeInitialized = Module.onRuntimeInitialized; + Module.onRuntimeInitialized = function (... args) { + // after the WASM is loaded, add the shim graphics callback function + Module.ccall( + "shim_graphics_set_callback", // C function name + null, // return type + ["string"], // arg types + [cbName], // arg values + {async: true} // options + ); + }; -// run NetHack! -factory(Module); - -// register the callback with the WASM library -function setGraphicsCallback() { - console.log("creating WASM callback function"); - let cb = Module.addFunction(windowCallback, "viiii"); - - console.log("setting callback function with library"); - Module.ccall( - "shim_graphics_set_callback", // C function name - null, // return type - ["number"], // arg types - [cb], // arg values - {async: true} // options - ); - - /* TODO: removeFunction */ + // load and run the module + var factory = require(path.join(__dirname, "../build/nethack.js")); + factory(Module); } -// this is the "shim graphics" callback function -// it gets called every time something needs to be displayed -// or input needs to be gathered from the user -function windowCallback(name, retPtr, fmt, args) { - name = Module.UTF8ToString(name); - fmt = Module.UTF8ToString(fmt); - let argTypes = fmt.split(""); - let retType = argTypes.shift(); - - // convert arguments to JavaScript types - let jsArgs = []; - for (let i = 0; i < argTypes.length; i++) { - let ptr = args + (4*i); - let val = typeLookup(argTypes[i], ptr); - jsArgs.push(val); - } - console.log(`graphics callback: ${name} [${jsArgs}]`); - /********** - * YOU HAVE TO IMPLEMENT THIS FUNCTION to render things - **********/ - let ret = yourFunctionToRenderGraphics(name, jsArgs); - setReturn(retPtr, retType, ret); -} - -// takes a character `type` and a WASM pointer and returns a JavaScript value -function typeLookup(type, ptr) { - switch(type) { - case "s": // string - return Module.UTF8ToString(Module.getValue(ptr, "*")); - case "p": // pointer - return Module.getValue(Module.getValue(ptr, "*"), "*"); - case "c": // char - return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); - case "0": /* 2^0 = 1 byte */ - return Module.getValue(Module.getValue(ptr, "*"), "i8"); - case "1": /* 2^1 = 2 bytes */ - return Module.getValue(Module.getValue(ptr, "*"), "i16"); - case "2": /* 2^2 = 4 bytes */ - case "i": // integer - case "n": // number - return Module.getValue(Module.getValue(ptr, "*"), "i32"); - case "f": // float - return Module.getValue(Module.getValue(ptr, "*"), "float"); - case "d": // double - return Module.getValue(Module.getValue(ptr, "*"), "double"); - default: - throw new TypeError ("unknown type:" + type); - } -} - -// takes a a WASM pointer, a charater `type` and a value and sets the value at pointer -function setReturn(ptr, type, value = 0) { - switch (type) { - case "p": - throw new Error("not implemented"); - case "s": - value=value?value:"(no value)"; - var strPtr = Module.getValue(ptr, "i32"); - Module.stringToUTF8(value, strPtr, 1024); - break; - case "i": - Module.setValue(ptr, value, "i32"); - break; - case "c": - Module.setValue(ptr, value, "i8"); - break; - case "f": - // XXX: I'm not sure why 'double' works and 'float' doesn't - Module.setValue(ptr, value, "double"); - break; - case "d": - Module.setValue(ptr, value, "double"); - break; - case "v": - break; - default: - throw new Error("unknown type"); - } -} +nethackStart(yourCallbackFunction); ``` \ No newline at end of file diff --git a/sys/lib/hints/wasm b/sys/lib/hints/wasm index 711017bae..7f85090af 100644 --- a/sys/lib/hints/wasm +++ b/sys/lib/hints/wasm @@ -15,21 +15,30 @@ EMRANLIB=emranlib EMCC_LFLAGS=-s SINGLE_FILE=1 EMCC_LFLAGS=-s WASM=1 EMCC_LFLAGS+=-s ALLOW_TABLE_GROWTH -EMCC_LFLAGS+=-s ASYNCIFY -s ASYNCIFY_IMPORTS='["_nhmain"]' -O3 +EMCC_LFLAGS+=-s ASYNCIFY -s ASYNCIFY_IMPORTS='["local_callback"]' +EMCC_LFLAGS+=-O3 EMCC_LFLAGS+=-s MODULARIZE EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback"]' EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString", "getValue", "setValue"]' EMCC_LFLAGS+=-s ERROR_ON_UNDEFINED_SYMBOLS=0 EMCC_LFLAGS+=--embed-file wasm-data@/ +# For a list of EMCC settings: +# https://github.com/emscripten-core/emscripten/blob/master/src/settings.js + # WASM C flags EMCC_CFLAGS= EMCC_CFLAGS+=-Wall EMCC_CFLAGS+=-Werror +#EMCC_CFLAGS+=-s DISABLE_EXCEPTION_CATCHING=0 EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=1 +#EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=2 EMCC_DEBUG_CFLAGS+=-s STACK_OVERFLOW_CHECK=2 EMCC_DEBUG_CFLAGS+=-s SAFE_HEAP=1 -EMCC_DEBUG_CFLAGS+=-s LLD_REPORT_UNDEFINED +EMCC_DEBUG_CFLAGS+=-s LLD_REPORT_UNDEFINED=1 +#EMCC_DEBUG_CFLAGS+=-s EXCEPTION_DEBUG=1 +#EMCC_DEBUG_CFLAGS+=-fsanitize=undefined -fsanitize=address -fsanitize=leak +#EMCC_DEBUG_CFLAGS+=-s EXIT_RUNTIME EMCC_PROD_CFLAGS+=-O3 # Nethack C flags diff --git a/sys/lib/npm-package/README.md b/sys/lib/npm-package/README.md index e71f3e5a1..e39db8292 100644 --- a/sys/lib/npm-package/README.md +++ b/sys/lib/npm-package/README.md @@ -11,7 +11,7 @@ npm install nethack ## API The main module returns a setup function: `startNethack(uiCallback, moduleOptions)`. -* `uiCallback(name, ... args)` - Your callback function that will handle rendering NetHack on the screen of your choice. The `name` argument is one of the UI functions of the [NetHack Window Interface](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc) and the `args` are corresponding to the window interface function that is being called. You are required to return the correct type of data for the function that is implemented. +* `uiCallback(name, ... args)` - Your callback function that will handle rendering NetHack on the screen of your choice. The `name` argument is one of the UI functions of the [NetHack Window Interface](https://github.com/NetHack/NetHack/blob/NetHack-3.7/doc/window.doc) and the `args` are corresponding to the window interface function that is being called. You are required to return the correct type of data for the function that is implemented. The `uiCallback` may be an `async` function. * `moduleOptions` - An optional [emscripten Module object](https://emscripten.org/docs/api_reference/module.html) for configuring the WASM that will be run. * `Module.arguments` - Of note is the [arguments property](https://emscripten.org/docs/api_reference/module.html#Module.arguments) which gets passed to NetHack as its [command line parameters](https://nethackwiki.com/wiki/Options). @@ -22,7 +22,7 @@ let nethackStart = require("nethack"); nethackStart(doGraphics); let winCount = 0; -function doGraphics(name, ... args) { +async function doGraphics(name, ... args) { console.log(`shim graphics: ${name} [${args}]`); switch(name) { diff --git a/sys/lib/npm-package/src/nethackShim.js b/sys/lib/npm-package/src/nethackShim.js index 638460278..e7a802c30 100644 --- a/sys/lib/npm-package/src/nethackShim.js +++ b/sys/lib/npm-package/src/nethackShim.js @@ -3,27 +3,38 @@ const path = require("path"); let Module; let userCallback; let savedOnRuntimeInitialized; + +// starts nethack function nethackStart(cb, inputModule = {}) { - if(typeof cb !== "function") throw new TypeError("expected first argument to be callback function"); + if(typeof cb !== "string" && typeof cb !== "function") throw new TypeError("expected first argument to be 'Function' or 'String' representing global callback function name"); if(typeof inputModule !== "object") throw new TypeError("expected second argument to be object"); + let cbName; + if(typeof cb === "function") { + cbName = cb.name; + if (cbName === "") cbName = "__anonymousNetHackCallback"; + if (globalThis[cbName] === undefined) globalThis[cbName] = cb; + else if (globalThis[cbName] !== cb) throw new Error (`'globalThis["${cbName}"]' is not the same as specified callback`); + } + + /* global globalThis */ + userCallback = globalThis[cbName]; + if(typeof userCallback !== "function") throw new TypeError(`expected 'globalThis["${cbName}"]' to be a function`); + // if(userCallback.constructor.name !== "AsyncFunction") throw new TypeError(`expected 'globalThis["${cbName}"]' to be an async function`); + // Emscripten Module config Module = inputModule; - userCallback = cb; savedOnRuntimeInitialized = Module.onRuntimeInitialized; Module.onRuntimeInitialized = function (... args) { // after the WASM is loaded, add the shim graphics callback function - let cb = Module.addFunction(windowCallback, "viiii"); Module.ccall( "shim_graphics_set_callback", // C function name null, // return type - ["number"], // arg types - [cb], // arg values + ["string"], // arg types + [cbName], // arg values {async: true} // options ); - /* TODO: Module.removeFunction() */ - // if the user had their own onRuntimeInitialized(), call it now if (savedOnRuntimeInitialized) savedOnRuntimeInitialized(... args); }; @@ -33,94 +44,6 @@ function nethackStart(cb, inputModule = {}) { factory(Module); } -function windowCallback(name, retPtr, fmt, args) { - name = Module.UTF8ToString(name); - fmt = Module.UTF8ToString(fmt); - let argTypes = fmt.split(""); - let retType = argTypes.shift(); - - // build array of JavaScript args from WASM parameters - let jsArgs = []; - for (let i = 0; i < argTypes.length; i++) { - let ptr = args + (4*i); - let val = typeLookup(argTypes[i], ptr); - jsArgs.push(val); - } - let retVal = userCallback(name, ... jsArgs); - setReturn(name, retPtr, retType, retVal); -} - -function typeLookup(type, ptr) { - switch(type) { - case "s": // string - return Module.UTF8ToString(Module.getValue(ptr, "*")); - case "p": // pointer - ptr = Module.getValue(ptr, "*"); - if(!ptr) return 0; // null pointer - return Module.getValue(ptr, "*"); - case "c": // char - return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); - case "0": /* 2^0 = 1 byte */ - return Module.getValue(Module.getValue(ptr, "*"), "i8"); - case "1": /* 2^1 = 2 bytes */ - return Module.getValue(Module.getValue(ptr, "*"), "i16"); - case "2": /* 2^2 = 4 bytes */ - case "i": // integer - case "n": // number - return Module.getValue(Module.getValue(ptr, "*"), "i32"); - case "f": // float - return Module.getValue(Module.getValue(ptr, "*"), "float"); - case "d": // double - return Module.getValue(Module.getValue(ptr, "*"), "double"); - default: - throw new TypeError ("unknown type:" + type); - } -} - -function setReturn(name, ptr, type, value = 0) { - - switch (type) { - case "p": - throw new Error("not implemented"); - case "s": - if(typeof value !== "string") - throw new TypeError(`expected ${name} return type to be string`); - value=value?value:"(no value)"; - var strPtr = Module.getValue(ptr, "i32"); - Module.stringToUTF8(value, strPtr, 1024); - break; - case "i": - if(typeof value !== "number" || !Number.isInteger(value)) - throw new TypeError(`expected ${name} return type to be integer`); - Module.setValue(ptr, value, "i32"); - break; - case "c": - if(typeof value !== "number" || value < 0 || value > 128) - throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); - Module.setValue(ptr, value, "i8"); - break; - case "f": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be float`); - // XXX: I'm not sure why 'double' works and 'float' doesn't - Module.setValue(ptr, value, "double"); - break; - case "d": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be float`); - Module.setValue(ptr, value, "double"); - break; - case "v": - break; - default: - throw new Error("unknown type"); - } - - function isFloat(n){ - return n === +n && n !== (n|0) && !Number.isInteger(n); - } -} - // TODO: ES6 'import' style module module.exports = nethackStart; diff --git a/sys/lib/test/libtest.c b/sys/lib/test/libtest.c index 9b554b986..31ca10e71 100644 --- a/sys/lib/test/libtest.c +++ b/sys/lib/test/libtest.c @@ -1,28 +1,37 @@ #include +#include /* external functions */ int nhmain(int argc, char *argv[]); typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); -void stub_graphics_set_callback(stub_callback_t cb); +void shim_graphics_set_callback(stub_callback_t cb); /* forward declarations */ void window_cb(const char *name, void *ret_ptr, const char *fmt, ...); - +void *yourFunctionToRenderGraphics(const char *name, va_list args); int main(int argc, char *argv[]) { - stub_graphics_set_callback(window_cb); + shim_graphics_set_callback(window_cb); nhmain(argc, argv); } -void window_cb(const char *name, void *ret_ptr, const char *fmt, ...) { - /* TODO -- see windowCallback below for hints */ - +void *yourFunctionToRenderGraphics(const char *name, va_list args) { + printf("yourFunctionToRenderGraphics name %s\n", name); /* DO SOMETHING HERE */ - *ret_ptr = yourFunctionToRenderGraphics(name, va_list args); + return NULL; } +void window_cb(const char *name, void *ret_ptr, const char *fmt, ...) { + void *ret; + va_list args; + /* TODO -- see windowCallback below for hints */ + va_start(args, fmt); + ret = yourFunctionToRenderGraphics(name, args); + // *((int *)ret_ptr = *((int *)ret); // e.g. yourFunctionToRenderGraphics returns an int + va_end(args); +} #if 0 function variadicCallback(name, retPtr, fmt, args) { diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 05ed09c07..eec616f4f 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -5,6 +5,7 @@ /* not an actual windowing port, but a fake win port for libnethack */ #include "hack.h" +#include #ifdef SHIM_GRAPHICS #include @@ -15,26 +16,30 @@ #undef SHIM_DEBUG -#ifndef __EMSCRIPTEN__ -typedef void(*shim_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); -#else /* __EMSCRIPTEN__ */ -/* WASM can't handle a variadic callback, so we pass back an array of pointers instead... */ -typedef void(*shim_callback_t)(const char *name, void *ret_ptr, const char *fmt, void *args[]); -#endif /* !__EMSCRIPTEN__ */ +#ifdef SHIM_DEBUG +#define debugf printf +#else /* !SHIM_DEBUG */ +#define debugf(...) +#endif /* SHIM_DEBUG */ -/* this is the primary interface to shim graphics, + +/* shim_graphics_callback is the primary interface to shim graphics, * call this function with your declared callback function * and you will receive all the windowing calls */ -static shim_callback_t shim_graphics_callback = NULL; #ifdef __EMSCRIPTEN__ - EMSCRIPTEN_KEEPALIVE -#endif -void shim_graphics_set_callback(shim_callback_t cb) { - shim_graphics_callback = cb; +/************ + * WASM interface + ************/ +EMSCRIPTEN_KEEPALIVE +static char *shim_callback_name = NULL; +void shim_graphics_set_callback(char *cbName) { + if (shim_callback_name != NULL) free(shim_callback_name); + shim_callback_name = strdup(cbName); + /* TODO: free(shim_callback_name) during shutdown? */ } +void local_callback (const char *cb_name, const char *shim_name, void *ret_ptr, const char *fmt_str, void *args); -#ifdef __EMSCRIPTEN__ /* A2P = Argument to Pointer */ #define A2P & /* P2V = Pointer to Void */ @@ -44,8 +49,8 @@ ret_type name fn_args { \ void *args[] = { __VA_ARGS__ }; \ ret_type ret = (ret_type) 0; \ debugf("SHIM GRAPHICS: " #name "\n"); \ - if (!shim_graphics_callback) return ret; \ - shim_graphics_callback(#name, (void *)&ret, fmt, args); \ + if (!shim_callback_name) return ret; \ + local_callback(shim_callback_name, #name, (void *)&ret, fmt, args); \ return ret; \ } @@ -53,10 +58,21 @@ ret_type name fn_args { \ void name fn_args { \ void *args[] = { __VA_ARGS__ }; \ debugf("SHIM GRAPHICS: " #name "\n"); \ - if (!shim_graphics_callback) return; \ - shim_graphics_callback(#name, NULL, fmt, args); \ + if (!shim_callback_name) return; \ + local_callback(shim_callback_name, #name, NULL, fmt, args); \ } + #else /* !__EMSCRIPTEN__ */ + +/************ + * libnethack.a interface + ************/ +typedef void(*shim_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); +static shim_callback_t shim_graphics_callback = NULL; +void shim_graphics_set_callback(shim_callback_t cb) { + shim_graphics_callback = cb; +} + #define A2P #define P2V #define DECLCB(ret_type, name, fn_args, fmt, ...) \ @@ -64,7 +80,7 @@ ret_type name fn_args { \ ret_type ret = (ret_type) 0; \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return ret; \ - shim_graphics_callback(#name, (void *)&ret, fmt, __VA_ARGS__); \ + shim_graphics_callback(#name, (void *)&ret, fmt, ## __VA_ARGS__); \ return ret; \ } @@ -72,17 +88,10 @@ ret_type name fn_args { \ void name fn_args { \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ - shim_graphics_callback(#name, NULL, fmt, __VA_ARGS__); \ + shim_graphics_callback(#name, NULL, fmt, ## __VA_ARGS__); \ } #endif /* __EMSCRIPTEN__ */ -#ifdef SHIM_DEBUG -#define debugf printf -#else /* !SHIM_DEBUG */ -#define debugf(...) -#endif /* SHIM_DEBUG */ - - enum win_types { WINSHIM_MESSAGE = 1, WINSHIM_MAP, @@ -222,4 +231,122 @@ struct window_procs shim_procs = { genl_can_suspend_yes, }; +#ifdef __EMSCRIPTEN__ +/* convert the C callback to a JavaScript callback */ +EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *ret_ptr, const char *fmt_str, void *args), { + Asyncify.handleAsync(async () => { + // convert callback arguments to proper JavaScript varaidic arguments + let name = Module.UTF8ToString(shim_name); + let fmt = Module.UTF8ToString(fmt_str); + let cbName = Module.UTF8ToString(cb_name); + // console.log("local_callback:", cbName, fmt, name); + + let argTypes = fmt.split(""); + let retType = argTypes.shift(); + + // build array of JavaScript args from WASM parameters + let jsArgs = []; + for (let i = 0; i < argTypes.length; i++) { + let ptr = args + (4*i); + let val = typeLookup(argTypes[i], ptr); + jsArgs.push(val); + } + + // do the callback + let userCallback = globalThis[cbName]; + let retVal = await runJsLoop(() => userCallback(name, ... jsArgs)); + + // save the return value + setReturn(name, ret_ptr, retType, retVal); + + // convert 'ptr' to the type indicated by 'type' + function typeLookup(type, ptr) { + switch(type) { + case "s": // string + return Module.UTF8ToString(Module.getValue(ptr, "*")); + case "p": // pointer + ptr = Module.getValue(ptr, "*"); + if(!ptr) return 0; // null pointer + return Module.getValue(ptr, "*"); + case "c": // char + return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + case "0": /* 2^0 = 1 byte */ + return Module.getValue(Module.getValue(ptr, "*"), "i8"); + case "1": /* 2^1 = 2 bytes */ + return Module.getValue(Module.getValue(ptr, "*"), "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return Module.getValue(Module.getValue(ptr, "*"), "i32"); + case "f": // float + return Module.getValue(Module.getValue(ptr, "*"), "float"); + case "d": // double + return Module.getValue(Module.getValue(ptr, "*"), "double"); + default: + throw new TypeError ("unknown type:" + type); + } + } + + // setTimeout() with value of '0' is similar to setImmediate() (which isn't standard) + // this lets the JS loop run for a tick so that other events can occur + // XXX: I also tried replacing the for(;;) in allmain.c:moveloop() with emscripten_set_main_loop() + // unfortunately that won't work -- if the simulate_infinite_loop arg is false, it falls through; + // if is true, it throws an exception to break out of main(), but doesn't get caught because + // the stack isn't running under main() anymore... + // I think this is suboptimal, but we will have to live with it + async function runJsLoop(cb) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(cb()); + }, 0); + }); + } + + // sets the return value of the function to the type expected + function setReturn(name, ptr, type, value = 0) { + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + if(typeof value !== "string") + throw new TypeError(`expected ${name} return type to be string`); + value=value?value:"(no value)"; + var strPtr = Module.getValue(ptr, "i32"); + Module.stringToUTF8(value, strPtr, 1024); + break; + case "i": + if(typeof value !== "number" || !Number.isInteger(value)) + throw new TypeError(`expected ${name} return type to be integer`); + Module.setValue(ptr, value, "i32"); + break; + case "c": + if(typeof value !== "number" || value < 0 || value > 128) + throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); + Module.setValue(ptr, value, "i8"); + break; + case "f": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + // XXX: I'm not sure why 'double' works and 'float' doesn't + Module.setValue(ptr, value, "double"); + break; + case "d": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + Module.setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } + + function isFloat(n){ + return n === +n && n !== (n|0) && !Number.isInteger(n); + } + } + }); +}) +#endif /* __EMSCRIPTEN__ */ + #endif /* SHIM_GRAPHICS */ \ No newline at end of file From 7b46e3d12caa3717526157a98cb57c82e7078531 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 6 Sep 2020 17:35:03 -0700 Subject: [PATCH 28/42] update lib tests --- sys/lib/test/run.sh | 3 +- sys/lib/test/test.js | 186 ------------------------------------------- 2 files changed, 2 insertions(+), 187 deletions(-) delete mode 100644 sys/lib/test/test.js diff --git a/sys/lib/test/run.sh b/sys/lib/test/run.sh index 5e2b36dfe..b89ab55d9 100755 --- a/sys/lib/test/run.sh +++ b/sys/lib/test/run.sh @@ -12,6 +12,7 @@ fi if [ x$1 == "xrunlib" ]; then LIBS="-Lsrc -lnethack -Llib/lua -llua -lm" BADLIBS="-lncurses" + rm nhlibtest gcc -o nhlibtest libtest.c $LIBS $BADLIBS ./nhlibtest fi @@ -26,7 +27,7 @@ if [ x$1 == "xwasm" ]; then fi if [ x$1 == "xrunwasm" ]; then - node test.js + cd sys/lib/npm-package && node test/test.js fi if [ x$1 == "xbin" ]; then diff --git a/sys/lib/test/test.js b/sys/lib/test/test.js deleted file mode 100644 index d7d8f0144..000000000 --- a/sys/lib/test/test.js +++ /dev/null @@ -1,186 +0,0 @@ -const util = require("util"); - -// Emscripten Module config -let Module = { - // noInitialRun: true, - onRuntimeInitialized: function () { - setGraphicsCallback(); - }, -}; - -var factory = require("./src/nethack.js"); - -// load and run the module -factory(Module); - -function setGraphicsCallback() { - console.log("setting callback function"); - let cb = Module.addFunction(windowCallback, "viiii"); - - console.log("doing call"); - Module.ccall( - "shim_graphics_set_callback", // C function name - null, // return type - ["number"], // arg types - [cb], // arg values - {async: true} // options - ); - - /* TODO: removeFunction */ -} - -function windowCallback(name, retPtr, fmt, args) { - // console.log ("variadicCallback called..."); - // console.log("typeof name", typeof name); - // console.log("typeof fmt", typeof fmt); - // console.log("typeof args", typeof args); - name = Module.UTF8ToString(name); - fmt = Module.UTF8ToString(fmt); - // console.log ("name:", name); - // console.log ("fmt:", fmt); - let argTypes = fmt.split(""); - let retType = argTypes.shift(); - // console.log ("arg count:", argTypes.length); - // console.log ("arg types:", argTypes); - // console.log ("ret type:", retType); - - let jsArgs = []; - for (let i = 0; i < argTypes.length; i++) { - let ptr = args + (4*i); - let val = typeLookup(argTypes[i], ptr); - jsArgs.push(val); - } - console.log(`graphics callback: ${name} [${jsArgs}]`); - let retVal = doGraphics(name, ... jsArgs); - setReturn(name, retPtr, retType, retVal); -} - -function typeLookup(type, ptr) { - switch(type) { - case "s": // string - return Module.UTF8ToString(Module.getValue(ptr, "*")); - case "p": // pointer - ptr = Module.getValue(ptr, "*"); - if(!ptr) return 0; // null pointer - return Module.getValue(ptr, "*"); - case "c": // char - return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); - case "0": /* 2^0 = 1 byte */ - return Module.getValue(Module.getValue(ptr, "*"), "i8"); - case "1": /* 2^1 = 2 bytes */ - return Module.getValue(Module.getValue(ptr, "*"), "i16"); - case "2": /* 2^2 = 4 bytes */ - case "i": // integer - case "n": // number - return Module.getValue(Module.getValue(ptr, "*"), "i32"); - case "f": // float - return Module.getValue(Module.getValue(ptr, "*"), "float"); - case "d": // double - return Module.getValue(Module.getValue(ptr, "*"), "double"); - default: - throw new TypeError ("unknown type:" + type); - } -} - -function setReturn(name, ptr, type, value = 0) { - - switch (type) { - case "p": - throw new Error("not implemented"); - case "s": - if(typeof value !== "string") - throw new TypeError(`expected ${name} return type to be string`); - value=value?value:"(no value)"; - var strPtr = Module.getValue(ptr, "i32"); - Module.stringToUTF8(value, strPtr, 1024); - break; - case "i": - if(typeof value !== "number" || !Number.isInteger(value)) - throw new TypeError(`expected ${name} return type to be integer`); - Module.setValue(ptr, value, "i32"); - break; - case "c": - if(typeof value !== "number" || value < 0 || value > 128) - throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); - Module.setValue(ptr, value, "i8"); - break; - case "f": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be float`); - // XXX: I'm not sure why 'double' works and 'float' doesn't - Module.setValue(ptr, value, "double"); - break; - case "d": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be float`); - Module.setValue(ptr, value, "double"); - break; - case "v": - break; - default: - throw new Error("unknown type"); - } - - function isFloat(n){ - return n === +n && n !== (n|0) && !Number.isInteger(n); - } -} - -let winCount = 0; -function doGraphics(name, ... args) { - switch(name) { - case "shim_create_nhwindow": - winCount++; - console.log("creating window", args, "returning", winCount); - return winCount; - case "shim_yn_function": - case "shim_message_menu": - return 121; // 'y' - case "shim_nhgetch": - case "shim_nh_poskey": - return 46; - default: - return 0; - } -} - -// var Module = null; -// factory(Module).then((ret) => { -// if(ret !== Module) { -// console.log("ret and Module are not the same"); -// } else { -// console.log("ret and Module are the same"); -// } -// // console.log("Module", util.inspect(Module, {depth: null, showHidden: true})); -// // TODO: -// // shim_graphics_set_callback(); - -// // options: -// // https://emscripten.org/docs/api_reference/module.html -// // logReadFiles -// // printWithColors -// // noInitialRun -// // onRuntimeInitialized -// console.log("doing run"); -// // Module.run(); - -// // // main loop -// // console.log("creating function"); -// // let fn = Module.addFunction(wasmCallback, "vii"); - -// // console.log("doing call"); -// // Module.ccall( -// // "mainloop", // C function name -// // null, // return type -// // ["number"], // arg types -// // [fn], // arg values -// // {async: true} // options -// // ); -// // // TODO: removeFunction(fn) - -// // var i = 0; -// // setInterval(function() { -// // console.log("interval", i++); -// // },1000); -// }); - From 2e7e0c23251f11eb2b29bf3cdb6697f5781ece8c Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 6 Sep 2020 23:19:22 -0700 Subject: [PATCH 29/42] change package name --- sys/lib/npm-package/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/lib/npm-package/package.json b/sys/lib/npm-package/package.json index 45cea1739..b4e931559 100644 --- a/sys/lib/npm-package/package.json +++ b/sys/lib/npm-package/package.json @@ -1,5 +1,5 @@ { - "name": "@neth_ck/neth_ck", + "name": "@neth4ck/neth4ck", "version": "1.0.0", "description": "The original NetHack rogue-like game built as a WebAssembly module", "main": "src/nethackShim.js", From c36b948b2117fcf62896babd2069308821d9a7af Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:23:13 -0700 Subject: [PATCH 30/42] add helpers and constants --- sys/lib/libnethackmain.c | 214 +++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 76 deletions(-) diff --git a/sys/lib/libnethackmain.c b/sys/lib/libnethackmain.c index 5f000847f..da39ee02e 100644 --- a/sys/lib/libnethackmain.c +++ b/sys/lib/libnethackmain.c @@ -19,6 +19,9 @@ /* for cross-compiling to WebAssembly (WASM) */ #ifdef __EMSCRIPTEN__ #include +void js_helpers_init(); +void js_constants_init(); +void js_globals_init(); #endif #if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) @@ -74,46 +77,13 @@ char *argv[]; boolean exact_username; boolean resuming = FALSE; /* assume new game */ boolean plsel_once = FALSE; - int i; - - for (i = 0; i < argc; i++) { - printf ("argv[%d]: %s\n", i, argv[i]); - } + // int i; + // for (i = 0; i < argc; i++) { + // printf ("argv[%d]: %s\n", i, argv[i]); + // } early_init(); -#if 0 /* __APPLE__ */ - { -/* special hack to change working directory to a resource fork when - running from finder --sam */ -#define MAC_PATH_VALUE ".app/Contents/MacOS/" - char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; - int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0; - getcwd(mac_cwd, 1024); - if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { - if ((mac_exe = strrchr(mac_exe, '/'))) - mac_exe++; - else - mac_exe = argv[0]; - mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); - if (mac_tmp_len <= arg0_len) { - mac_tmp = malloc(mac_tmp_len + 1); - sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); - if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { - mac_lhs_len = - (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; - if (mac_lhs_len > mac_tmp_len - 1) - mac_tmp = realloc(mac_tmp, mac_lhs_len); - strncpy(mac_tmp, argv[0], mac_lhs_len); - mac_tmp[mac_lhs_len] = '\0'; - chdir(mac_tmp); - } - free(mac_tmp); - } - } - } -#endif /* __APPLE__ */ - g.hname = argv[0]; g.hackpid = getpid(); (void) umask(0777 & ~FCMASK); @@ -230,6 +200,11 @@ char *argv[]; process_options(argc, argv); /* command line options */ #ifdef WINCHAIN commit_windowchain(); +#endif +#ifdef __EMSCRIPTEN__ + js_helpers_init(); + js_constants_init(); + js_globals_init(); #endif init_nhwindows(&argc, argv); /* now we can set up window system */ #ifdef _M_UNIX @@ -260,9 +235,11 @@ char *argv[]; /* use character name rather than lock letter for file names */ g.locknum = 0; } else { +#ifndef NO_SIGNAL /* suppress interrupts while processing lock file */ (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGINT, SIG_IGN); +#endif } dlb_init(); /* must be before newgame() */ @@ -748,44 +725,6 @@ get_login_name() return buf; } -#if 0 /* __APPLE__ */ -extern int errno; - -void -port_insert_pastebuf(buf) -char *buf; -{ - /* This should be replaced when there is a Cocoa port. */ - const char *errfmt; - size_t len; - FILE *PB = popen("/usr/bin/pbcopy", "w"); - - if (!PB) { - errfmt = "Unable to start pbcopy (%d)\n"; - goto error; - } - - len = strlen(buf); - /* Remove the trailing \n, carefully. */ - if (buf[len - 1] == '\n') - len--; - - /* XXX Sorry, I'm too lazy to write a loop for output this short. */ - if (len != fwrite(buf, 1, len, PB)) { - errfmt = "Error sending data to pbcopy (%d)\n"; - goto error; - } - - if (pclose(PB) != -1) { - return; - } - errfmt = "Error finishing pbcopy (%d)\n"; - - error: - raw_printf(errfmt, strerror(errno)); -} -#endif /* __APPLE__ */ - unsigned long sys_random_seed() { @@ -818,4 +757,127 @@ sys_random_seed() return seed; } -/*unixmain.c*/ +#ifdef __EMSCRIPTEN__ +/*** + * Helpers + ***/ +EM_JS(void, js_helpers_init, (), { + globalThis.nethackGlobal = globalThis.nethackGlobal || {}; + globalThis.nethackGlobal.helpers = globalThis.nethackGlobal.helpers || {}; + + installHelper(mapglyphHelper); + installHelper(displayInventory); + + // used by print_glyph + function mapglyphHelper(glyph, x, y, mgflags) { + let ochar = _malloc(4); + let ocolor = _malloc(4); + let ospecial = _malloc(4); + + _mapglyph(glyph, ochar, ocolor, ospecial, x, y, mgflags); + + let ch = getValue(ochar, "i32"); + let color = getValue(ocolor, "i32"); + let special = getValue(ospecial, "i32"); + + _free (ochar); + _free (ocolor); + _free (ospecial); + + return { + glyph, + ch, + color, + special, + x, + y, + mgflags + }; + } + + // used by update_inventory + function displayInventory() { + // Asyncify.handleAsync(async () => { + return _display_inventory(0, 0); + console.log ("displayInventory done"); + // }); + } + + function installHelper(fn, name) { + name = name || fn.name; + globalThis.nethackGlobal.helpers[name] = fn; + } +}) + +/*** + * Constants + ***/ +#define SET_CONSTANT(scope, name) set_const(scope, #name, name); +EM_JS(void, set_const, (char *scope_str, char *name_str, int num), { + let scope = UTF8ToString(scope_str); + let name = UTF8ToString(name_str); + globalThis.nethackGlobal.constants[scope] = globalThis.nethackGlobal.constants[scope] || {}; + globalThis.nethackGlobal.constants[scope][name] = num; + globalThis.nethackGlobal.constants[scope][num] = name; +}); + +void js_constants_init() { + EM_ASM({ + globalThis.nethackGlobal = globalThis.nethackGlobal || {}; + globalThis.nethackGlobal.constants = globalThis.nethackGlobal.constants || {}; + }); + + // create_nhwindow + SET_CONSTANT("WIN_TYPE", NHW_MESSAGE) + SET_CONSTANT("WIN_TYPE", NHW_STATUS) + SET_CONSTANT("WIN_TYPE", NHW_MAP) + SET_CONSTANT("WIN_TYPE", NHW_MENU) + SET_CONSTANT("WIN_TYPE", NHW_TEXT) + + // status_update + SET_CONSTANT("STATUS_FIELD", BL_CHARACTERISTICS) + SET_CONSTANT("STATUS_FIELD", BL_RESET) + SET_CONSTANT("STATUS_FIELD", BL_FLUSH) + SET_CONSTANT("STATUS_FIELD", BL_TITLE) + SET_CONSTANT("STATUS_FIELD", BL_STR) + SET_CONSTANT("STATUS_FIELD", BL_DX) + SET_CONSTANT("STATUS_FIELD", BL_CO) + SET_CONSTANT("STATUS_FIELD", BL_IN) + SET_CONSTANT("STATUS_FIELD", BL_WI) + SET_CONSTANT("STATUS_FIELD", BL_CH) + SET_CONSTANT("STATUS_FIELD", BL_ALIGN) + SET_CONSTANT("STATUS_FIELD", BL_SCORE) + SET_CONSTANT("STATUS_FIELD", BL_CAP) + SET_CONSTANT("STATUS_FIELD", BL_GOLD) + SET_CONSTANT("STATUS_FIELD", BL_ENE) + SET_CONSTANT("STATUS_FIELD", BL_ENEMAX) + SET_CONSTANT("STATUS_FIELD", BL_XP) + SET_CONSTANT("STATUS_FIELD", BL_AC) + SET_CONSTANT("STATUS_FIELD", BL_HD) + SET_CONSTANT("STATUS_FIELD", BL_TIME) + SET_CONSTANT("STATUS_FIELD", BL_HUNGER) + SET_CONSTANT("STATUS_FIELD", BL_HP) + SET_CONSTANT("STATUS_FIELD", BL_HPMAX) + SET_CONSTANT("STATUS_FIELD", BL_LEVELDESC) + SET_CONSTANT("STATUS_FIELD", BL_EXP) + SET_CONSTANT("STATUS_FIELD", BL_CONDITION) + SET_CONSTANT("STATUS_FIELD", MAXBLSTATS) +} + +/*** + * Globals + ***/ +void js_globals_init() { + // printf("js_globals_init\n"); + + // player name + // g.plname + + // bottom line stats + // g.blstats + // g.now_or_before_idx +} + +#endif + +/*libnethack.c*/ From fc2e6b0b6dd10263382365fed2432c5f072fd3dc Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:24:06 -0700 Subject: [PATCH 31/42] initial commit --- sys/lib/npm-package/package-lock.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 sys/lib/npm-package/package-lock.json diff --git a/sys/lib/npm-package/package-lock.json b/sys/lib/npm-package/package-lock.json new file mode 100644 index 000000000..fb5f7b7e1 --- /dev/null +++ b/sys/lib/npm-package/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "@neth4ck/neth4ck", + "version": "1.0.0", + "lockfileVersion": 1 +} From 233c68391215e7b4a4a3111ceead69a299083a9a Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:24:12 -0700 Subject: [PATCH 32/42] add keywords --- sys/lib/npm-package/package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sys/lib/npm-package/package.json b/sys/lib/npm-package/package.json index b4e931559..79b445e17 100644 --- a/sys/lib/npm-package/package.json +++ b/sys/lib/npm-package/package.json @@ -13,7 +13,12 @@ "nethack", "rogue", "rogue-like", - "game" + "roguelike", + "dungeon", + "dungeons", + "game", + "rpg", + "dnd" ], "author": "Adam Powers ", "license": "SEE LICENSE IN LICENSE.md" From 4e38d8f3297b3ecaaa634204b8c917aed82a79e3 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:27:04 -0700 Subject: [PATCH 33/42] fix reentry and types, enhance debugging, delinting --- win/shim/winshim.c | 131 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 99 insertions(+), 32 deletions(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index eec616f4f..8218b7382 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -51,6 +51,7 @@ ret_type name fn_args { \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_callback_name) return ret; \ local_callback(shim_callback_name, #name, (void *)&ret, fmt, args); \ + debugf("SHIM GRAPHICS: " #name " done.\n"); \ return ret; \ } @@ -60,6 +61,7 @@ void name fn_args { \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_callback_name) return; \ local_callback(shim_callback_name, #name, NULL, fmt, args); \ + debugf("SHIM GRAPHICS: " #name " done.\n"); \ } #else /* !__EMSCRIPTEN__ */ @@ -81,6 +83,7 @@ ret_type name fn_args { \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return ret; \ shim_graphics_callback(#name, (void *)&ret, fmt, ## __VA_ARGS__); \ + debugf("SHIM GRAPHICS: " #name " done.\n"); \ return ret; \ } @@ -89,6 +92,7 @@ void name fn_args { \ debugf("SHIM GRAPHICS: " #name "\n"); \ if (!shim_graphics_callback) return; \ shim_graphics_callback(#name, NULL, fmt, ## __VA_ARGS__); \ + debugf("SHIM GRAPHICS: " #name " done.\n"); \ } #endif /* __EMSCRIPTEN__ */ @@ -133,9 +137,9 @@ VDECLCB(shim_add_menu, "viipiiisi", A2P window, A2P glyph, P2V identifier, A2P ch, A2P gch, A2P attr, P2V str, A2P itemflags) VDECLCB(shim_end_menu,(winid window, const char *prompt), "vis", A2P window, P2V prompt) -DECLCB(int, shim_select_menu,(winid window, int how, MENU_ITEM_P **menu_list), "iiip", A2P window, A2P how, P2V menu_list) +/* XXX: shim_select_menu menu_list is an output */ +DECLCB(int, shim_select_menu,(winid window, int how, MENU_ITEM_P **menu_list), "iiio", A2P window, A2P how, P2V menu_list) DECLCB(char, shim_message_menu,(CHAR_P let, int how, const char *mesg), "ciis", A2P let, A2P how, P2V mesg) -VDECLCB(shim_update_inventory,(void), "v") VDECLCB(shim_mark_synch,(void), "v") VDECLCB(shim_wait_synch,(void), "v") VDECLCB(shim_cliparound,(int x, int y), "vii", A2P x, A2P y) @@ -144,11 +148,11 @@ VDECLCB(shim_print_glyph,(winid w, int x, int y, int glyph, int bkglyph), "viiii VDECLCB(shim_raw_print,(const char *str), "vs", P2V str) VDECLCB(shim_raw_print_bold,(const char *str), "vs", P2V str) DECLCB(int, shim_nhgetch,(void), "i") -DECLCB(int, shim_nh_poskey,(int *x, int *y, int *mod), "ippp", P2V x, P2V y, P2V mod) +DECLCB(int, shim_nh_poskey,(int *x, int *y, int *mod), "iooo", P2V x, P2V y, P2V mod) VDECLCB(shim_nhbell,(void), "v") DECLCB(int, shim_doprev_message,(void),"iv") DECLCB(char, shim_yn_function,(const char *query, const char *resp, CHAR_P def), "cssi", P2V query, P2V resp, A2P def) -VDECLCB(shim_getlin,(const char *query, char *bufp), "vsp", P2V query, P2V bufp) +VDECLCB(shim_getlin,(const char *query, char *bufp), "vso", P2V query, P2V bufp) DECLCB(int,shim_get_ext_cmd,(void),"iv") VDECLCB(shim_number_pad,(int state), "vi", A2P state) VDECLCB(shim_delay_output,(void), "v") @@ -168,11 +172,24 @@ VDECLCB(shim_status_enablefield, (int fieldidx, const char *nm, const char *fmt, BOOLEAN_P enable), "vippi", A2P fieldidx, P2V nm, P2V fmt, A2P enable) +/* XXX: the second argument to shim_status_update is sometimes an integer and sometimes a pointer */ VDECLCB(shim_status_update, (int fldidx, genericptr_t ptr, int chg, int percent, int color, unsigned long *colormasks), - "vipiiip", + "vioiiip", A2P fldidx, P2V ptr, A2P chg, A2P percent, A2P color, P2V colormasks) +#ifdef __EMSCRIPTEN__ +/* XXX: calling display_inventory() from shim_update_inventory() causes reentrancy that breaks emscripten Asyncify */ +/* this should be fine since according to windows.doc, the only purpose of shim_update_inventory() is to call display_inventory() */ +void shim_update_inventory() { + if(iflags.perm_invent) { + display_inventory(NULL, FALSE); + } +} +#else /* !__EMSCRIPTEN__ */ +VDECLCB(shim_update_inventory,(void), "v") +#endif + /* Interface definition used in windows.c */ struct window_procs shim_procs = { "shim", @@ -234,13 +251,19 @@ struct window_procs shim_procs = { #ifdef __EMSCRIPTEN__ /* convert the C callback to a JavaScript callback */ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *ret_ptr, const char *fmt_str, void *args), { - Asyncify.handleAsync(async () => { + // Asyncify.handleAsync() is the more logical choice here; however, the stack unrolling in Asyncify is performed by + // function call analysis during compilation. Since we are using an indirect callback (cb_name), it can't predict the stack + // unrolling and it crashes. Thus we use Asyncify.handleSleep() and wakeUp() to make sure that async doesn't break + // Asyncify. For details, see: https://emscripten.org/docs/porting/asyncify.html#optimizing + Asyncify.handleSleep(wakeUp => { // convert callback arguments to proper JavaScript varaidic arguments - let name = Module.UTF8ToString(shim_name); - let fmt = Module.UTF8ToString(fmt_str); - let cbName = Module.UTF8ToString(cb_name); + let name = UTF8ToString(shim_name); + let fmt = UTF8ToString(fmt_str); + let cbName = UTF8ToString(cb_name); // console.log("local_callback:", cbName, fmt, name); + reentryMutexLock(name); + let argTypes = fmt.split(""); let retType = argTypes.shift(); @@ -252,49 +275,81 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r jsArgs.push(val); } + decodeArgs(name, jsArgs); + // do the callback let userCallback = globalThis[cbName]; - let retVal = await runJsLoop(() => userCallback(name, ... jsArgs)); + runJsEventLoop(() => userCallback.call(this, name, ... jsArgs)).then((retVal) => { + // save the return value + setReturn(name, ret_ptr, retType, retVal); + // return + setTimeout(() => { + reentryMutexUnlock(); + wakeUp(); + }, 0); + }); - // save the return value - setReturn(name, ret_ptr, retType, retVal); // convert 'ptr' to the type indicated by 'type' function typeLookup(type, ptr) { switch(type) { case "s": // string - return Module.UTF8ToString(Module.getValue(ptr, "*")); + return UTF8ToString(getValue(ptr, "*")); case "p": // pointer - ptr = Module.getValue(ptr, "*"); + ptr = getValue(ptr, "*"); if(!ptr) return 0; // null pointer - return Module.getValue(ptr, "*"); + return getValue(ptr, "*"); case "c": // char - return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + return String.fromCharCode(getValue(getValue(ptr, "*"), "i8")); case "0": /* 2^0 = 1 byte */ - return Module.getValue(Module.getValue(ptr, "*"), "i8"); + return getValue(getValue(ptr, "*"), "i8"); case "1": /* 2^1 = 2 bytes */ - return Module.getValue(Module.getValue(ptr, "*"), "i16"); + return getValue(getValue(ptr, "*"), "i16"); case "2": /* 2^2 = 4 bytes */ case "i": // integer case "n": // number - return Module.getValue(Module.getValue(ptr, "*"), "i32"); + return getValue(getValue(ptr, "*"), "i32"); case "f": // float - return Module.getValue(Module.getValue(ptr, "*"), "float"); + return getValue(getValue(ptr, "*"), "float"); case "d": // double - return Module.getValue(Module.getValue(ptr, "*"), "double"); + return getValue(getValue(ptr, "*"), "double"); + case "o": // overloaded: multiple types + return ptr; default: throw new TypeError ("unknown type:" + type); } } - // setTimeout() with value of '0' is similar to setImmediate() (which isn't standard) + // make callback arguments friendly: convert numbers to strings where possible + function decodeArgs(name, args) { + // if (!globalThis.nethackGlobal.makeArgsFriendly) return; + + switch(name) { + case "shim_create_nhwindow": + args[0] = globalThis.nethackGlobal.constants["WIN_TYPE"][args[0]]; + break; + case "shim_status_update": + // which field is being updated? + args[0] = globalThis.nethackGlobal.constants["STATUS_FIELD"][args[0]]; + // arg[1] is a string unless it is BL_CONDITION, BL_RESET, BL_FLUSH, BL_CHARACTERISTICS + if(["BL_CONDITION", "BL_RESET", "BL_FLUSH", "BL_CHARACTERISTICS"].indexOf(args[0] && args[1]) < 0) { + args[1] = typeLookup("s", args[1]); + } else { + args[1] = typeLookup("p", args[1]); + } + break; + } + } + + // setTimeout() with value of '0' is similar to setImmediate() (but setImmediate isn't standard) // this lets the JS loop run for a tick so that other events can occur // XXX: I also tried replacing the for(;;) in allmain.c:moveloop() with emscripten_set_main_loop() - // unfortunately that won't work -- if the simulate_infinite_loop arg is false, it falls through; + // unfortunately that won't work -- if the simulate_infinite_loop arg is false, it falls through + // and the program ends; // if is true, it throws an exception to break out of main(), but doesn't get caught because // the stack isn't running under main() anymore... - // I think this is suboptimal, but we will have to live with it - async function runJsLoop(cb) { + // I think this is suboptimal, but we will have to live with it (for now?) + async function runJsEventLoop(cb) { return new Promise((resolve) => { setTimeout(() => { resolve(cb()); @@ -311,29 +366,29 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r if(typeof value !== "string") throw new TypeError(`expected ${name} return type to be string`); value=value?value:"(no value)"; - var strPtr = Module.getValue(ptr, "i32"); - Module.stringToUTF8(value, strPtr, 1024); + var strPtr = getValue(ptr, "i32"); + stringToUTF8(value, strPtr, 1024); break; case "i": if(typeof value !== "number" || !Number.isInteger(value)) throw new TypeError(`expected ${name} return type to be integer`); - Module.setValue(ptr, value, "i32"); + setValue(ptr, value, "i32"); break; case "c": if(typeof value !== "number" || value < 0 || value > 128) throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); - Module.setValue(ptr, value, "i8"); + setValue(ptr, value, "i8"); break; case "f": if(typeof value !== "number" || isFloat(value)) throw new TypeError(`expected ${name} return type to be float`); // XXX: I'm not sure why 'double' works and 'float' doesn't - Module.setValue(ptr, value, "double"); + setValue(ptr, value, "double"); break; case "d": if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be float`); - Module.setValue(ptr, value, "double"); + throw new TypeError(`expected ${name} return type to be double`); + setValue(ptr, value, "double"); break; case "v": break; @@ -345,6 +400,18 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r return n === +n && n !== (n|0) && !Number.isInteger(n); } } + + function reentryMutexLock(name) { + globalThis.nethackGlobal = globalThis.nethackGlobal || {}; + if(globalThis.nethackGlobal.shimPreventReentry) { + throw new Error(`'${name}' attempting second call to 'local_callback' before '${globalThis.nethackGlobal.shimPreventReentry}' has finished, will crash emscripten Asyncify. For details see: emscripten.org/docs/porting/asyncify.html#reentrancy`); + } + globalThis.nethackGlobal.shimPreventReentry = name; + } + + function reentryMutexUnlock() { + globalThis.nethackGlobal.shimPreventReentry = null; + } }); }) #endif /* __EMSCRIPTEN__ */ From c602ecc5a2c548a19444d19698a0fe54b535b410 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:27:29 -0700 Subject: [PATCH 34/42] default is a permenant inventory window --- sys/lib/sysconf | 1 + 1 file changed, 1 insertion(+) diff --git a/sys/lib/sysconf b/sys/lib/sysconf index 77b4c383f..2e1b66e12 100644 --- a/sys/lib/sysconf +++ b/sys/lib/sysconf @@ -146,5 +146,6 @@ PANICTRACE_LIBC=0 # option settings via NETHACKOPTIONS in their environment or via # ~/.nethackrc run-time configuration file. #OPTIONS=!autopickup,fruit:tomato,symset:DECgraphics +OPTIONS=perm_invent #eof From 34e4b1fae9252296b51bd22256c158d30f20ffa2 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:34:45 -0700 Subject: [PATCH 35/42] js test code --- sys/lib/npm-package/test/test.js | 55 ++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 sys/lib/npm-package/test/test.js diff --git a/sys/lib/npm-package/test/test.js b/sys/lib/npm-package/test/test.js new file mode 100644 index 000000000..3032399e3 --- /dev/null +++ b/sys/lib/npm-package/test/test.js @@ -0,0 +1,55 @@ +let nethackStart = require("../src/nethackShim.js"); +Error.stackTraceLimit = 20; + +// debugging to make sure the JavaScript event loop isn't blocked +// const {performance} = require("perf_hooks"); +// let currentTime = 0; +// let lastTime = 0; +// setInterval(() => { +// lastTime = currentTime; +// currentTime = performance.now(); +// console.log("Time since last JavaScript loop:", currentTime-lastTime); +// }, 10); + +let Module = {}; +let winCount = 0; + +/* global globalThis */ +nethackStart(async function (name, ... args) { + switch(name) { + case "shim_init_nhwindows": + console.log("globalThis.nethackGlobal", globalThis.nethackGlobal); + break; + case "shim_create_nhwindow": + winCount++; + console.log("creating window", args, "returning", winCount); + return winCount; + case "shim_print_glyph": + var x = args[1]; + var y = args[2]; + var glyph = args[3]; + + var ret = globalThis.nethackGlobal.helpers.mapglyphHelper(glyph, x, y, 0); + console.log(`GLYPH (${x},${y}): ${String.fromCharCode(ret.ch)}`); + return; + // case "shim_update_inventory": + // globalThis.nethackGlobal.helpers.displayInventory(); + // return; + case "shim_select_menu": + return await selectMenu(...args); + case "shim_yn_function": + case "shim_message_menu": + return 121; // 'y' + case "shim_nhgetch": + case "shim_nh_poskey": + return 0; + default: + console.log(`called doGraphics: ${name} [${args}]`); + return 0; + } +}, Module); + +async function selectMenu(window, how, selected) { + Module.setValue(selected, 0, "*"); + return -1; +} \ No newline at end of file From 6fcb7c3db3281b75b76b6aa896d08833b37ab861 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:35:01 -0700 Subject: [PATCH 36/42] fix ignored files --- .gitignore | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 030d3d92e..fb74047b9 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,7 @@ Release/ binary/ build/ ipch/ -lib/* +/lib Nethack.sln Nethack.sdf Nethack.opensdf @@ -88,9 +88,9 @@ src/nethack.wasm src/nethack.js src/wasm-data src/libnethack.a -libtest.c -nhlibtest -run.sh -test.js +/libtest.c +/nhlibtest +/run.sh +/test.js sys/lib/npm-package/build/nethack.js sys/lib/npm-package/build/nethack.wasm From d2fdc6f5826204efc725d431a47b1c19ed2611b9 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:40:04 -0700 Subject: [PATCH 37/42] add new exports, cleaner debugging options --- sys/lib/hints/wasm | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/sys/lib/hints/wasm b/sys/lib/hints/wasm index 7f85090af..681c1e693 100644 --- a/sys/lib/hints/wasm +++ b/sys/lib/hints/wasm @@ -18,7 +18,7 @@ EMCC_LFLAGS+=-s ALLOW_TABLE_GROWTH EMCC_LFLAGS+=-s ASYNCIFY -s ASYNCIFY_IMPORTS='["local_callback"]' EMCC_LFLAGS+=-O3 EMCC_LFLAGS+=-s MODULARIZE -EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback"]' +EMCC_LFLAGS+=-s EXPORTED_FUNCTIONS='["_main", "_shim_graphics_set_callback", "_mapglyph", "_display_inventory"]' EMCC_LFLAGS+=-s EXPORTED_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", "removeFunction", "UTF8ToString", "getValue", "setValue"]' EMCC_LFLAGS+=-s ERROR_ON_UNDEFINED_SYMBOLS=0 EMCC_LFLAGS+=--embed-file wasm-data@/ @@ -31,22 +31,35 @@ EMCC_CFLAGS= EMCC_CFLAGS+=-Wall EMCC_CFLAGS+=-Werror #EMCC_CFLAGS+=-s DISABLE_EXCEPTION_CATCHING=0 -EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=1 -#EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=2 +#EMCC_CFLAGS+=-s NO_EXIT_RUNTIME=1 +EMCC_CFLAGS+=-s EXIT_RUNTIME=1 +# if INVOKE_RUN=0, you must Module.callMain() with an optional parameter of commandline args +#EMCC_CFLAGS+=-s INVOKE_RUN=0 + +#EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=1 +EMCC_DEBUG_CFLAGS+=-s ASSERTIONS=2 EMCC_DEBUG_CFLAGS+=-s STACK_OVERFLOW_CHECK=2 EMCC_DEBUG_CFLAGS+=-s SAFE_HEAP=1 EMCC_DEBUG_CFLAGS+=-s LLD_REPORT_UNDEFINED=1 -#EMCC_DEBUG_CFLAGS+=-s EXCEPTION_DEBUG=1 +EMCC_DEBUG_CFLAGS+=-s EXCEPTION_DEBUG=0 #EMCC_DEBUG_CFLAGS+=-fsanitize=undefined -fsanitize=address -fsanitize=leak -#EMCC_DEBUG_CFLAGS+=-s EXIT_RUNTIME +EMCC_DEBUG_CFLAGS+=--profiling + EMCC_PROD_CFLAGS+=-O3 +ifdef WASM_DEBUG +EMCC_CFLAGS+=$(EMCC_DEBUG_CFLAGS) +else +EMCC_CFLAGS+=$(EMCC_PROD_CFLAGS) +endif + # Nethack C flags CFLAGS+=-DSYSCF -DSYSCF_FILE=\"/sysconf\" -DSECURE CFLAGS+=-g -I../include -DNOTPARMDECL CFLAGS+=-Wall CFLAGS+=-Werror CFLAGS+=-DGCC_WARN +CFLAGS+=-DNO_SIGNAL # NetHack sources control CFLAGS+=-DDLB @@ -55,12 +68,6 @@ CFLAGS+=-DDLB #CFLAGS+=-DGREPPATH=\"/usr/bin/grep\" CFLAGS+=-DNOMAIL -ifdef WASM_DEBUG -EMCC_CFLAGS+=$(EMCC_DEBUG_CFLAGS) -else -EMCC_CFLAGS+=$(EMCC_PROD_CFLAGS) -endif - # installation config # hackdir is the wasm / emscripten embed data root directory HACKDIR=/ @@ -74,4 +81,4 @@ INSTDIR=$(HACKDIR) VARDIR=$(HACKDIR) #-POST -# no post \ No newline at end of file +# no post From 176f553b6e54177c21fbeb744bec17cc681805ae Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:48:13 -0700 Subject: [PATCH 38/42] remove stray debug message --- sys/lib/libnethackmain.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sys/lib/libnethackmain.c b/sys/lib/libnethackmain.c index da39ee02e..83899acb9 100644 --- a/sys/lib/libnethackmain.c +++ b/sys/lib/libnethackmain.c @@ -799,7 +799,6 @@ EM_JS(void, js_helpers_init, (), { function displayInventory() { // Asyncify.handleAsync(async () => { return _display_inventory(0, 0); - console.log ("displayInventory done"); // }); } From 1c23434de6710ed89b4f8e978dcde165e8571530 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:48:28 -0700 Subject: [PATCH 39/42] update docs --- sys/lib/npm-package/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sys/lib/npm-package/README.md b/sys/lib/npm-package/README.md index e39db8292..2c9b96938 100644 --- a/sys/lib/npm-package/README.md +++ b/sys/lib/npm-package/README.md @@ -15,6 +15,11 @@ The main module returns a setup function: `startNethack(uiCallback, moduleOption * `moduleOptions` - An optional [emscripten Module object](https://emscripten.org/docs/api_reference/module.html) for configuring the WASM that will be run. * `Module.arguments` - Of note is the [arguments property](https://emscripten.org/docs/api_reference/module.html#Module.arguments) which gets passed to NetHack as its [command line parameters](https://nethackwiki.com/wiki/Options). +There are a number of auxilary functions and variables that may help with building your applications. All of these are under `globalThis.nethackOptions`. Use `console.log(globalThis.nethackOptions)` for a full list of options. Some worth mentioning are: +* `globalThis.nethackOptions.helpers` - Helper functions that are useful for NetHack windowing ports + * `globalThis.nethackOptions.mapglyphHelper` - Converts an integer glyph into a character to be displayed. Useful if you are using ASCII characters for representing NetHack (as opposed to tiles). Interface is `mapglyphHelper(glyph, x, y, mgflags)` and will typically be called as part of the `shim_print_glyph` function. +* `globalThis.nethackOptions.constants` - A Object full of constants that are `#define`'d in NetHack's C code. Useful for translating to / from numbers in the APIs and return values. + ## Example ``` js let nethackStart = require("nethack"); From 5462072ceb2eef1922bcb08ef695a49aab0f908a Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sat, 12 Sep 2020 12:49:10 -0700 Subject: [PATCH 40/42] bump version for minor improvements --- sys/lib/npm-package/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sys/lib/npm-package/package.json b/sys/lib/npm-package/package.json index 79b445e17..e39a7d201 100644 --- a/sys/lib/npm-package/package.json +++ b/sys/lib/npm-package/package.json @@ -1,6 +1,6 @@ { "name": "@neth4ck/neth4ck", - "version": "1.0.0", + "version": "1.0.1", "description": "The original NetHack rogue-like game built as a WebAssembly module", "main": "src/nethackShim.js", "scripts": { From ac4649e63f9daa0d1d9eceac0069559d44239931 Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 4 Oct 2020 14:03:04 -0700 Subject: [PATCH 41/42] add globals, constants, and helpers --- sys/lib/libnethackmain.c | 257 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 251 insertions(+), 6 deletions(-) diff --git a/sys/lib/libnethackmain.c b/sys/lib/libnethackmain.c index 83899acb9..cec8c076e 100644 --- a/sys/lib/libnethackmain.c +++ b/sys/lib/libnethackmain.c @@ -7,6 +7,7 @@ #include "hack.h" #include "dlb.h" +#include "date.h" #include #include @@ -559,6 +560,7 @@ void sethanguphandler(handler) void FDECL((*handler), (int)); { +#ifndef NO_SIGNAL #ifdef SA_RESTART /* don't want reads to restart. If SA_RESTART is defined, we know * sigaction exists and can be used to ensure reads won't restart. @@ -580,6 +582,7 @@ void FDECL((*handler), (int)); (void) signal(SIGXCPU, (SIG_RET_TYPE) handler); #endif #endif /* ?SA_RESTART */ +#endif /* !NO_SIGNAL */ } #ifdef PORT_HELP @@ -767,6 +770,8 @@ EM_JS(void, js_helpers_init, (), { installHelper(mapglyphHelper); installHelper(displayInventory); + installHelper(getPointerValue); + installHelper(setPointerValue); // used by print_glyph function mapglyphHelper(glyph, x, y, mgflags) { @@ -802,6 +807,83 @@ EM_JS(void, js_helpers_init, (), { // }); } + // convert 'ptr' to the type indicated by 'type' + function getPointerValue(name, ptr, type) { + // console.log("getPointerValue", name, "0x" + ptr.toString(16), type); + switch(type) { + case "s": // string + // var value = UTF8ToString(getValue(ptr, "*")); + return UTF8ToString(ptr); + case "p": // pointer + if(!ptr) return 0; // null pointer + return getValue(ptr, "*"); + case "c": // char + return String.fromCharCode(getValue(ptr, "i8")); + case "0": /* 2^0 = 1 byte */ + return getValue(ptr, "i8"); + case "1": /* 2^1 = 2 bytes */ + return getValue(ptr, "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return getValue(ptr, "i32"); + case "f": // float + return getValue(ptr, "float"); + case "d": // double + return getValue(ptr, "double"); + case "o": // overloaded: multiple types + return ptr; + default: + throw new TypeError ("unknown type:" + type); + } + } + + // sets the return value of the function to the type expected + function setPointerValue(name, ptr, type, value = 0) { + // console.log("setPointerValue", name, "0x" + ptr.toString(16), type, value); + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + if(typeof value !== "string") + throw new TypeError(`expected ${name} return type to be string`); + // value=value?value:"(no value)"; + // var strPtr = getValue(ptr, "i32"); + stringToUTF8(value, ptr, 1024); // TODO: uhh... danger will robinson + break; + case "i": + if(typeof value !== "number" || !Number.isInteger(value)) + throw new TypeError(`expected ${name} return type to be integer`); + setValue(ptr, value, "i32"); + break; + case "c": + if(typeof value !== "number" || value < 0 || value > 128) + throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); + setValue(ptr, value, "i8"); + break; + case "f": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + // XXX: I'm not sure why 'double' works and 'float' doesn't + setValue(ptr, value, "double"); + break; + case "d": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be double`); + setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } + + function isFloat(n){ + return n === +n && n !== (n|0) && !Number.isInteger(n); + } + } + + function installHelper(fn, name) { name = name || fn.name; globalThis.nethackGlobal.helpers[name] = fn; @@ -815,10 +897,20 @@ EM_JS(void, js_helpers_init, (), { EM_JS(void, set_const, (char *scope_str, char *name_str, int num), { let scope = UTF8ToString(scope_str); let name = UTF8ToString(name_str); + globalThis.nethackGlobal.constants[scope] = globalThis.nethackGlobal.constants[scope] || {}; globalThis.nethackGlobal.constants[scope][name] = num; globalThis.nethackGlobal.constants[scope][num] = name; }); +#define SET_CONSTANT_STRING(scope, name) set_const_str(scope, #name, name); +EM_JS(void, set_const_str, (char *scope_str, char *name_str, char *input_str), { + let scope = UTF8ToString(scope_str); + let name = UTF8ToString(name_str); + let str = UTF8ToString(input_str); + + globalThis.nethackGlobal.constants[scope] = globalThis.nethackGlobal.constants[scope] || {}; + globalThis.nethackGlobal.constants[scope][name] = str; +}); void js_constants_init() { EM_ASM({ @@ -861,22 +953,175 @@ void js_constants_init() { SET_CONSTANT("STATUS_FIELD", BL_EXP) SET_CONSTANT("STATUS_FIELD", BL_CONDITION) SET_CONSTANT("STATUS_FIELD", MAXBLSTATS) + + // text attributes + SET_CONSTANT("ATTR", ATR_NONE); + SET_CONSTANT("ATTR", ATR_BOLD); + SET_CONSTANT("ATTR", ATR_DIM); + SET_CONSTANT("ATTR", ATR_ULINE); + SET_CONSTANT("ATTR", ATR_BLINK); + SET_CONSTANT("ATTR", ATR_INVERSE); + SET_CONSTANT("ATTR", ATR_URGENT); + SET_CONSTANT("ATTR", ATR_NOHISTORY); + + // conditions + SET_CONSTANT("CONDITION", BL_MASK_BAREH); + SET_CONSTANT("CONDITION", BL_MASK_BLIND); + SET_CONSTANT("CONDITION", BL_MASK_BUSY); + SET_CONSTANT("CONDITION", BL_MASK_CONF); + SET_CONSTANT("CONDITION", BL_MASK_DEAF); + SET_CONSTANT("CONDITION", BL_MASK_ELF_IRON); + SET_CONSTANT("CONDITION", BL_MASK_FLY); + SET_CONSTANT("CONDITION", BL_MASK_FOODPOIS); + SET_CONSTANT("CONDITION", BL_MASK_GLOWHANDS); + SET_CONSTANT("CONDITION", BL_MASK_GRAB); + SET_CONSTANT("CONDITION", BL_MASK_HALLU); + SET_CONSTANT("CONDITION", BL_MASK_HELD); + SET_CONSTANT("CONDITION", BL_MASK_ICY); + SET_CONSTANT("CONDITION", BL_MASK_INLAVA); + SET_CONSTANT("CONDITION", BL_MASK_LEV); + SET_CONSTANT("CONDITION", BL_MASK_PARLYZ); + SET_CONSTANT("CONDITION", BL_MASK_RIDE); + SET_CONSTANT("CONDITION", BL_MASK_SLEEPING); + SET_CONSTANT("CONDITION", BL_MASK_SLIME); + SET_CONSTANT("CONDITION", BL_MASK_SLIPPERY); + SET_CONSTANT("CONDITION", BL_MASK_STONE); + SET_CONSTANT("CONDITION", BL_MASK_STRNGL); + SET_CONSTANT("CONDITION", BL_MASK_STUN); + SET_CONSTANT("CONDITION", BL_MASK_SUBMERGED); + SET_CONSTANT("CONDITION", BL_MASK_TERMILL); + SET_CONSTANT("CONDITION", BL_MASK_TETHERED); + SET_CONSTANT("CONDITION", BL_MASK_TRAPPED); + SET_CONSTANT("CONDITION", BL_MASK_UNCONSC); + SET_CONSTANT("CONDITION", BL_MASK_WOUNDEDL); + SET_CONSTANT("CONDITION", BL_MASK_HOLDING); + + // menu + SET_CONSTANT("MENU_SELECT", PICK_NONE); + SET_CONSTANT("MENU_SELECT", PICK_ONE); + SET_CONSTANT("MENU_SELECT", PICK_ANY); + + // copyright + SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_A); + SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_B); + SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_C); + SET_CONSTANT_STRING("COPYRIGHT", COPYRIGHT_BANNER_D); + + // glyphs + SET_CONSTANT("GLYPH", GLYPH_MON_OFF); + SET_CONSTANT("GLYPH", GLYPH_PET_OFF); + SET_CONSTANT("GLYPH", GLYPH_INVIS_OFF); + SET_CONSTANT("GLYPH", GLYPH_DETECT_OFF); + SET_CONSTANT("GLYPH", GLYPH_BODY_OFF); + SET_CONSTANT("GLYPH", GLYPH_RIDDEN_OFF); + SET_CONSTANT("GLYPH", GLYPH_OBJ_OFF); + SET_CONSTANT("GLYPH", GLYPH_CMAP_OFF); + SET_CONSTANT("GLYPH", GLYPH_EXPLODE_OFF); + SET_CONSTANT("GLYPH", GLYPH_ZAP_OFF); + SET_CONSTANT("GLYPH", GLYPH_SWALLOW_OFF); + SET_CONSTANT("GLYPH", GLYPH_WARNING_OFF); + SET_CONSTANT("GLYPH", GLYPH_STATUE_OFF); + SET_CONSTANT("GLYPH", GLYPH_UNEXPLORED_OFF); + SET_CONSTANT("GLYPH", GLYPH_NOTHING_OFF); + SET_CONSTANT("GLYPH", MAX_GLYPH); + SET_CONSTANT("GLYPH", NO_GLYPH); + SET_CONSTANT("GLYPH", GLYPH_INVISIBLE); + SET_CONSTANT("GLYPH", GLYPH_UNEXPLORED); + SET_CONSTANT("GLYPH", GLYPH_NOTHING); + + // colors + SET_CONSTANT("COLORS", CLR_BLACK); + SET_CONSTANT("COLORS", CLR_RED); + SET_CONSTANT("COLORS", CLR_GREEN); + SET_CONSTANT("COLORS", CLR_BROWN); + SET_CONSTANT("COLORS", CLR_BLUE); + SET_CONSTANT("COLORS", CLR_MAGENTA); + SET_CONSTANT("COLORS", CLR_CYAN); + SET_CONSTANT("COLORS", CLR_GRAY); + SET_CONSTANT("COLORS", NO_COLOR); + SET_CONSTANT("COLORS", CLR_ORANGE); + SET_CONSTANT("COLORS", CLR_BRIGHT_GREEN); + SET_CONSTANT("COLORS", CLR_YELLOW); + SET_CONSTANT("COLORS", CLR_BRIGHT_BLUE); + SET_CONSTANT("COLORS", CLR_BRIGHT_MAGENTA); + SET_CONSTANT("COLORS", CLR_BRIGHT_CYAN); + SET_CONSTANT("COLORS", CLR_WHITE); + SET_CONSTANT("COLORS", CLR_MAX); + + // color attributes (?) + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_DIM); + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_BLINK); + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_ULINE); + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_INVERSE); + SET_CONSTANT("COLOR_ATTR", HL_ATTCLR_BOLD); + SET_CONSTANT("COLOR_ATTR", BL_ATTCLR_MAX); } /*** * Globals ***/ +#define CREATE_GLOBAL(var, type) create_global(#var, (void *)&var, type); +#define CREATE_GLOBAL_FROM_ARRAY(base, iter, path, end_expr, type) \ + for(iter = 0; end_expr; iter++) { \ + snprintf(buf, BUFSZ, #base ".%d." #path, iter); \ + create_global(buf, (void *)(&(base[iter].path)), type); \ + } + +void create_global (char *name, void *ptr, char *type); + void js_globals_init() { - // printf("js_globals_init\n"); + // int i; + // char buf[BUFSZ]; + printf("js_globals_init\n"); - // player name - // g.plname + EM_ASM({ + globalThis.nethackGlobal = globalThis.nethackGlobal || {}; + globalThis.nethackGlobal.globals = globalThis.nethackGlobal.globals || {}; + }); - // bottom line stats - // g.blstats - // g.now_or_before_idx + /* globals */ + CREATE_GLOBAL(g.plname, "s"); + + /* window globals */ + CREATE_GLOBAL(WIN_MAP, "i"); + CREATE_GLOBAL(WIN_MESSAGE, "i"); + CREATE_GLOBAL(WIN_INVEN, "i"); + CREATE_GLOBAL(WIN_STATUS, "i"); } +EM_JS(void, create_global, (char *name_str, void *ptr, char *type_str), { + let name = UTF8ToString(name_str); + let type = UTF8ToString(type_str); + + // get helpers + let getPointerValue = globalThis.nethackGlobal.helpers.getPointerValue; + let setPointerValue = globalThis.nethackGlobal.helpers.setPointerValue; + + let { obj, prop } = createPath(globalThis.nethackGlobal.globals, name); + + // setters / getters with bound pointers + Object.defineProperty(obj, prop, { + get: getPointerValue.bind(null, name, ptr, type), + set: setPointerValue.bind(null, name, ptr, type), + configurable: true, + enumerable: true + }); + + function createPath(obj, path) { + path = path.split("."); + let i; + for (i = 0; i < path.length - 1; i++) { + // obj[path[i]] = obj[path[i]] || {}; + if (obj[path[i]] === undefined) { + obj[path[i]] = {}; + } + obj = obj[path[i]]; + } + + return { obj, prop: path[i] }; + } +}) + #endif /*libnethack.c*/ From dc1c85faa42c2bfc36e31810f55ae45162ffc7cd Mon Sep 17 00:00:00 2001 From: Adam Powers Date: Sun, 4 Oct 2020 14:04:56 -0700 Subject: [PATCH 42/42] more friendly javascript arguments --- win/shim/winshim.c | 158 +++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 83 deletions(-) diff --git a/win/shim/winshim.c b/win/shim/winshim.c index 8218b7382..2910f0be6 100644 --- a/win/shim/winshim.c +++ b/win/shim/winshim.c @@ -262,6 +262,10 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r let cbName = UTF8ToString(cb_name); // console.log("local_callback:", cbName, fmt, name); + // get pointer / type conversion helpers + let getPointerValue = globalThis.nethackGlobal.helpers.getPointerValue; + let setPointerValue = globalThis.nethackGlobal.helpers.setPointerValue; + reentryMutexLock(name); let argTypes = fmt.split(""); @@ -271,7 +275,7 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r let jsArgs = []; for (let i = 0; i < argTypes.length; i++) { let ptr = args + (4*i); - let val = typeLookup(argTypes[i], ptr); + let val = getArg(name, ptr, argTypes[i]); jsArgs.push(val); } @@ -281,7 +285,7 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r let userCallback = globalThis[cbName]; runJsEventLoop(() => userCallback.call(this, name, ... jsArgs)).then((retVal) => { // save the return value - setReturn(name, ret_ptr, retType, retVal); + setPointerValue(name, ret_ptr, retType, retVal); // return setTimeout(() => { reentryMutexUnlock(); @@ -289,37 +293,6 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r }, 0); }); - - // convert 'ptr' to the type indicated by 'type' - function typeLookup(type, ptr) { - switch(type) { - case "s": // string - return UTF8ToString(getValue(ptr, "*")); - case "p": // pointer - ptr = getValue(ptr, "*"); - if(!ptr) return 0; // null pointer - return getValue(ptr, "*"); - case "c": // char - return String.fromCharCode(getValue(getValue(ptr, "*"), "i8")); - case "0": /* 2^0 = 1 byte */ - return getValue(getValue(ptr, "*"), "i8"); - case "1": /* 2^1 = 2 bytes */ - return getValue(getValue(ptr, "*"), "i16"); - case "2": /* 2^2 = 4 bytes */ - case "i": // integer - case "n": // number - return getValue(getValue(ptr, "*"), "i32"); - case "f": // float - return getValue(getValue(ptr, "*"), "float"); - case "d": // double - return getValue(getValue(ptr, "*"), "double"); - case "o": // overloaded: multiple types - return ptr; - default: - throw new TypeError ("unknown type:" + type); - } - } - // make callback arguments friendly: convert numbers to strings where possible function decodeArgs(name, args) { // if (!globalThis.nethackGlobal.makeArgsFriendly) return; @@ -333,14 +306,77 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r args[0] = globalThis.nethackGlobal.constants["STATUS_FIELD"][args[0]]; // arg[1] is a string unless it is BL_CONDITION, BL_RESET, BL_FLUSH, BL_CHARACTERISTICS if(["BL_CONDITION", "BL_RESET", "BL_FLUSH", "BL_CHARACTERISTICS"].indexOf(args[0] && args[1]) < 0) { - args[1] = typeLookup("s", args[1]); + args[1] = getArg(name, args[1], "s"); } else { - args[1] = typeLookup("p", args[1]); + args[1] = getArg(name, args[1], "p"); } break; + case "shim_display_file": + args[1] = !!args[1]; + case "shim_display_nhwindow": + args[0] = decodeWindow(args[0]); + args[1] = !!args[1]; + break; + case "shim_getmsghistory": + args[0] = !!args[0]; + break; + case "shim_putmsghistory": + args[1] = !!args[1]; + break; + case "shim_status_enablefield": + console.log("shim_status_enablefield arg 1:", args[1]); + args[3] = !!args[3]; + break; + case "shim_add_menu": + args[0] = decodeWindow(args[0]); + // args[1] = mapglyphHelper(args[1]); + // args[5] = decodeAttr(args[5]); + break; + case "shim_putstr": + args[0] = decodeWindow(args[0]); + break; + case "shim_select_menu": + args[0] = decodeWindow(args[0]); + args[1] = decodeSelected(args[1]); + break; + case "shim_clear_nhwindow": + case "shim_destroy_nhwindow": + case "shim_curs": + case "shim_start_menu": + case "shim_end_menu": + case "shim_print_glyph": + args[0] = decodeWindow(args[0]); + break; } } + function decodeWindow(winid) { + let { WIN_MAP, WIN_INVEN, WIN_STATUS, WIN_MESSAGE } = globalThis.nethackGlobal.globals; + switch(winid) { + case WIN_MAP: return "WIN_MAP"; + case WIN_MESSAGE: return "WIN_MESSAGE"; + case WIN_STATUS: return "WIN_STATUS"; + case WIN_INVEN: return "WIN_INVEN"; + default: return winid; + } + // return winid; + } + + function decodeSelected(how) { + let { PICK_NONE, PICK_ONE, PICK_ANY } = globalThis.nethackGlobal.constants.MENU_SELECT; + switch(how) { + case PICK_NONE: return "PICK_NONE"; + case PICK_ONE: return "PICK_ONE"; + case PICK_ANY: return "PICK_ANY"; + default: return how; + } + + } + + function getArg(name, ptr, type) { + return (type === "o")?ptr:getPointerValue(name, getValue(ptr, "*"), type); + } + // setTimeout() with value of '0' is similar to setImmediate() (but setImmediate isn't standard) // this lets the JS loop run for a tick so that other events can occur // XXX: I also tried replacing the for(;;) in allmain.c:moveloop() with emscripten_set_main_loop() @@ -357,60 +393,16 @@ EM_JS(void, local_callback, (const char *cb_name, const char *shim_name, void *r }); } - // sets the return value of the function to the type expected - function setReturn(name, ptr, type, value = 0) { - switch (type) { - case "p": - throw new Error("not implemented"); - case "s": - if(typeof value !== "string") - throw new TypeError(`expected ${name} return type to be string`); - value=value?value:"(no value)"; - var strPtr = getValue(ptr, "i32"); - stringToUTF8(value, strPtr, 1024); - break; - case "i": - if(typeof value !== "number" || !Number.isInteger(value)) - throw new TypeError(`expected ${name} return type to be integer`); - setValue(ptr, value, "i32"); - break; - case "c": - if(typeof value !== "number" || value < 0 || value > 128) - throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); - setValue(ptr, value, "i8"); - break; - case "f": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be float`); - // XXX: I'm not sure why 'double' works and 'float' doesn't - setValue(ptr, value, "double"); - break; - case "d": - if(typeof value !== "number" || isFloat(value)) - throw new TypeError(`expected ${name} return type to be double`); - setValue(ptr, value, "double"); - break; - case "v": - break; - default: - throw new Error("unknown type"); - } - - function isFloat(n){ - return n === +n && n !== (n|0) && !Number.isInteger(n); - } - } - function reentryMutexLock(name) { globalThis.nethackGlobal = globalThis.nethackGlobal || {}; - if(globalThis.nethackGlobal.shimPreventReentry) { - throw new Error(`'${name}' attempting second call to 'local_callback' before '${globalThis.nethackGlobal.shimPreventReentry}' has finished, will crash emscripten Asyncify. For details see: emscripten.org/docs/porting/asyncify.html#reentrancy`); + if(globalThis.nethackGlobal.shimFunctionRunning) { + throw new Error(`'${name}' attempting second call to 'local_callback' before '${globalThis.nethackGlobal.shimFunctionRunning}' has finished, will crash emscripten Asyncify. For details see: emscripten.org/docs/porting/asyncify.html#reentrancy`); } - globalThis.nethackGlobal.shimPreventReentry = name; + globalThis.nethackGlobal.shimFunctionRunning = name; } function reentryMutexUnlock() { - globalThis.nethackGlobal.shimPreventReentry = null; + globalThis.nethackGlobal.shimFunctionRunning = null; } }); })