binary/
build/
ipch/
-lib/
+lib/*
Nethack.sln
Nethack.sdf
Nethack.opensdf
win/share/objthin.txt
win/share/oththin.txt
# end of ms-dos
+
+#libnethack
+targets/*
+#test.js
+#sys/lib/npm-package/build/nethack.js
+#sys/lib/npm-package/build/nethack.wasm
allow themed room subrooms to be filled (github #347)
allow rereading spellbooks to refresh memory at any time (github #261)
allow themed rooms constrained by level difficulty (github #344)
+add a varied form of LIBNH nethack library contribution (github #385)
+add cross-compile to WASM (github #385)
Code Cleanup and Reorganization
#ifdef UNIX
#if (NH_DEVEL_STATUS != NH_STATUS_RELEASED)
/* see end.c */
+#if !defined(CROSS_TO_WASM)
#ifndef PANICTRACE
#define PANICTRACE
-#endif
-#endif
-#endif
+#endif /* PANICTRACE */
+#endif /* CROSS_TO_WASM */
+#endif /* NH_DEVEL_STATUS != NH_STATUS_RELEASED */
+#endif /* UNIX */
+
/* The following are meaningless if PANICTRACE is not defined: */
#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ >= 2)
#define PANICTRACE_LIBC
#define PANICTRACE_LIBC
#endif
#ifdef UNIX
+#if !defined(CROSS_TO_WASM) /* no popen in WASM */
#define PANICTRACE_GDB
#endif
+#endif
/* Supply nethack_enter macro if not supplied by port */
#ifndef nethack_enter
#endif /* LINUX */
#endif /* GNOME_GRAPHICS */
-#ifdef MACOSX
+#if defined(MACOSX) && !defined(LIBNH)
# define RUNTIME_PASTEBUF_SUPPORT
#endif
{ "mswin", "Windows GUI", TRUE },
#endif
#ifdef SHIM_GRAPHICS
- { "shim", "Nethack Library Windowing Shim", TRUE },
+ { "shim", "NetHack Library Windowing Shim", TRUE },
#endif
#if 0 /* remainder have been retired */
-/* NetHack 3.7 rip.c $NHDT-Date: 1596498204 2020/08/03 23:43:24 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */
+/* NetHack 3.7 rip.c $NHDT-Date: 1597967808 2020/08/20 23:56:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.33 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2017. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \
- || defined(MSWIN_GRAPHICS) || defined(DUMPLOG) || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS)
+ || defined(MSWIN_GRAPHICS) || defined(DUMPLOG) \
+ || defined(CURSES_GRAPHICS) || defined(SHIM_GRAPHICS)
#define TEXT_TOMBSTONE
#endif
#if defined(mac) || defined(__BEOS__) || defined(WIN32_GRAPHICS)
};
#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 */
register char **dp;
register char *dpx;
char buf[BUFSZ];
- long year;
register int x;
- int line;
+ int line, year;
+ long cash;
g.rip = dp = (char **) alloc(sizeof(rip_txt));
for (x = 0; rip_txt[x]; ++x)
dp[x] = (char *) 0;
/* Put name on stone */
- Sprintf(buf, "%s", g.plname);
- buf[STONE_LINE_LEN] = 0;
+ Sprintf(buf, "%.*s", (int) STONE_LINE_LEN, g.plname);
center(NAME_LINE, buf);
/* Put $ on stone */
- Sprintf(buf, "%ld Au", g.done_money);
- buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */
+ 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);
center(GOLD_LINE, buf);
/* Put together death description */
/* 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 = strlen(dpx)) > STONE_LINE_LEN) {
- for (i = STONE_LINE_LEN; ((i0 > STONE_LINE_LEN) && i); i--)
+ if (i0 > STONE_LINE_LEN) {
+ for (i = STONE_LINE_LEN; (i > 0) && (i0 > STONE_LINE_LEN); --i)
if (dpx[i] == ' ')
i0 = i;
if (!i)
}
/* Put year on stone */
- year = yyyymmdd(when) / 10000L;
- Sprintf(buf, "%4ld", year);
+ year = (int) ((yyyymmdd(when) / 10000L) % 10000L);
+ Sprintf(buf, "%4d", year);
center(YEAR_LINE, buf);
#ifdef DUMPLOG
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
--- /dev/null
+# 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)
+
+## 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.
+* `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.
+ * [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
+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(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
+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 <stdio.h>
+
+int nhmain(int argc, char *argv[]);
+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 */
+}
+
+int main(int argc, char *argv[]) {
+ shim_graphics_set_callback(window_cb);
+ nhmain(argc, argv);
+}
+```
+
+## nethack.js example
+``` js
+const path = require("path");
+
+// starts nethack
+function nethackStart(cb, inputModule = {}) {
+ // set callback
+ let cbName = cb.name;
+ if (cbName === "") cbName = "__anonymousNetHackCallback";
+ let userCallback = globalThis[cbName] = cb;
+
+ // 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
+ );
+ };
+
+ // load and run the module
+ var factory = require(path.join(__dirname, "../build/nethack.js"));
+ factory(Module);
+}
+
+nethackStart(yourCallbackFunction);
+```
\ No newline at end of file
--- /dev/null
+# 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
+#
+
+# 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).
+#
+
+AR=ar rcu
+RANLIB=ranlib
+
+# XXX -g vs -O should go here, -I../include goes in the makefile
+CFLAGS+=-g -I../include -DNOTPARMDECL
+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
+
+LINK=$(CC)
+
+# 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
--- /dev/null
+
+#-PRE xxxx
+# enscripten WebAssembly config
+
+WANT_WASM=1
+WASM_DEBUG=1
+WASM_DATA_DIR=$(NHSROOT)/src/wasm-data
+
+# 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='["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=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
+CFLAGS+=-DSYSCF -DSYSCF_FILE=\"/sysconf\" -DSECURE
+CFLAGS+=-g -I../include -DNOTPARMDECL
+CFLAGS+=-Wall
+CFLAGS+=-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
+
+# 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
+
+INSTDIR=$(HACKDIR)
+VARDIR=$(HACKDIR)
+
+#-POST
+# no post
\ No newline at end of file
--- /dev/null
+ 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.
--- /dev/null
+# 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(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. 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).
+
+## Example
+``` js
+let nethackStart = require("nethack");
+
+nethackStart(doGraphics);
+
+let winCount = 0;
+async 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
--- /dev/null
+{
+ "name": "@neth4ck/neth4ck",
+ "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 <apowers@ato.ms>",
+ "license": "SEE LICENSE IN LICENSE.md"
+}
--- /dev/null
+const path = require("path");
+
+let Module;
+let userCallback;
+let savedOnRuntimeInitialized;
+
+// starts nethack
+function nethackStart(cb, inputModule = {}) {
+ 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;
+ 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
+ );
+
+ // 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);
+}
+
+// TODO: ES6 'import' style module
+module.exports = nethackStart;
+
--- /dev/null
+# 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
--- /dev/null
+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
--- /dev/null
+#include <stdio.h>
+#include <stdarg.h>
+
+/* external functions */
+int nhmain(int argc, char *argv[]);
+typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...);
+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[]) {
+ shim_graphics_set_callback(window_cb);
+ nhmain(argc, argv);
+}
+
+void *yourFunctionToRenderGraphics(const char *name, va_list args) {
+ printf("yourFunctionToRenderGraphics name %s\n", name);
+ /* DO SOMETHING HERE */
+ 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) {
+ // 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
--- /dev/null
+#!/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"
+ rm nhlibtest
+ 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
+ cd sys/lib/npm-package && node test/test.js
+fi
+
+if [ x$1 == "xbin" ]; then
+ echo Doing bin...
+ make spotless
+ cd sys/unix
+ ./setup.sh hints/macOS.2020
+ cd ../..
+ make
+fi
+
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 =
GENCSRC = vis_tab.c #tile.c
# all windowing-system-dependent .c (for dependencies and such)
-WINCSRC = $(WINTTYSRC) $(WINCURSESSRC) $(WINX11SRC) $(WINGNOMESRC) $(WINGEMSRC)
+WINCSRC = $(WINTTYSRC) $(WINCURSESSRC) $(WINX11SRC) $(WINGNOMESRC) $(WINGEMSRC) $(WINSHIMSRC)
# all windowing-system-dependent .cpp (for dependencies and such)
WINCXXSRC = $(WINQTSRC) $(WINQT3SRC) $(WINBESRC)
# '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 )
$(TARGETPFX)vidstub.o : ../sys/msdos/vidvesa.c ../sys/msdos/portio.h \
$(HACK_H)
$(TARGETPFX)tile.o : tile.c
-$(GAMEBIN) : $(HOBJ)
+$(GAMEBIN) : $(HOBJ) $(LUACROSSLIB)
$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \
- $(HOBJ) $(WINLIB) $(TARGET_LIBS) $(LUALIB)
+ $(HOBJ) $(WINLIB) $(TARGET_LIBS)
#
.PHONY: dospkg
dospkg: $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp
../util/txt2iff ../win/share/monsters.txt $@
$(TARGETPFX)other.iff: ../win/share/other.txt ../util/txt2iff
../util/txt2iff ../win/share/other.txt $@
-$(GAMEBIN) : $(HOBJ)
+$(GAMEBIN) : $(HOBJ) $(LUACROSSLIB)
$(TARGET_LINK) $(TARGET_LFLAGS) -o $(GAMEBIN) \
- $(HOBJ) $(WINLIB) $(TARGET_LIBS) $(LUALIB)
+ $(HOBJ) $(WINLIB) $(TARGET_LIBS)
#
.PHONY: amigapkg
amigapkg: $(GAMEBIN) $(TARGETPFX)recover.exe ../dat/nhtiles.bmp
@echo amiga package zip file $(TARGETPFX)NH370AMI.ZIP
endif # CROSS_TO_AMIGA
+ifdef CROSS_TO_WASM
+$(WASM_TARGET): $(HOBJ) $(LUACROSSLIB) Makefile $(WASM_DATA_DIR)
+ -rm $@
+ $(TARGET_CC) $(EMCC_LFLAGS) $(EMCC_CFLAGS) -o $@ \
+ $(HOBJ) $(TARGET_LIBS)
+
+$(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) dlb )
+ ( cd ..; $(MAKE) dofiles-dlb )
+ cp ../sys/lib/sysconf $(WASM_DATA_DIR)/sysconf
+#
+.PHONY: wasmpkg
+wasmpkg:
+ @echo "$(WASM_TARGET) done."
+$(TARGETPFX)unixmain.o : ../sys/unix/unixmain.c $(HACK_H)
+$(TARGETPFX)unixres.o : ../sys/unix/unixres.c $(HACK_H)
+$(TARGETPFX)unixunix.o : ../sys/unix/unixunix.c $(HACK_H)
+$(TARGETPFX)ioctl.o : ../sys/share/ioctl.c $(HACK_H)
+$(TARGETPFX)unixtty.o : ../sys/share/unixtty.c $(HACK_H)
+$(TARGETPFX)winshim.o : ../win/shim/winshim.c $(HACK_H)
+endif # CROSS_TO_WASM
+#
+
ifdef CROSS_SHARED
# shared file dependencies
$(TARGETPFX)pcmain.o : ../sys/share/pcmain.c $(HACK_H)
$(TARGET_AR) rcS $@ $(LUAOBJFILES2)
$(TARGET_AR) rcS $@ $(LUAOBJFILES3)
$(TARGET_AR) rcs $@ $(LUAOBJFILES4)
-ifdef WANT_WIN_CURSES
-$(TARGETPFX)pdclib.a : $(PDCLIBOBJS) $(PDCOBJS)
- if [ -f $@ ]; then rm $@; fi;
- $(TARGET_AR) rcs $@ $(PDCLIBOBJS) $(PDCOBJS)
-endif
-#
+
+# $(TARGET_AR) rcs $@ $(LUALIBOBJS)
+
# Lua src
$(TARGETPFX)lapi.o : $(LUATOP)/src/lapi.c
$(TARGETPFX)lauxlib.o : $(LUATOP)/src/lauxlib.c
endif # BUILD_LUA
ifdef BUILD_PDCURSES
+ifdef WANT_WIN_CURSES
+$(TARGETPFX)pdclib.a : $(PDCLIBOBJS) $(PDCOBJS)
+ if [ -f $@ ]; then rm $@; fi;
+ $(TARGET_AR) rcs $@ $(PDCLIBOBJS) $(PDCOBJS)
+endif
# PDCurses src
$(TARGETPFX)addch.o : $(PDCTOP)/pdcurses/addch.c
$(TARGETPFX)addchstr.o : $(PDCTOP)/pdcurses/addchstr.c
override TARGET_LIBS=
endif
+ifdef CROSS_TO_WASM
+BUILD_LUA=1
+override TARGET = wasm
+override TARGETDIR=../targets/$(TARGET)
+override TARGETPFX = $(TARGETDIR)/
+override TARGET_LIBS=
+endif
+
ifdef BUILD_LUA
#===============-=================================================
# LUA library
$(TARGETPFX)ltm.o $(TARGETPFX)lundump.o \
$(TARGETPFX)lutf8lib.o $(TARGETPFX)lvm.o $(TARGETPFX)lzio.o
LUALIBOBJS = $(LUAOBJFILES1) $(LUAOBJFILES2) $(LUAOBJFILES3) $(LUAOBJFILES4)
-LUACROSSLIB = $(TARGETPFX)$(O)lua$(subst .,,$(LUA_VERSION)).a
+LUACROSSLIB = $(TARGETPFX)lua$(subst .,,$(LUA_VERSION)).a
LUAINCL = -I$(LUASRCDIR)
BUILDMORE += $(LUACROSSLIB)
override TARGET_LIBS += $(LUACROSSLIB) -lm
override LUALIB=
override GAMEBIN = $(TARGETPFX)nethack
override PACKAGE= amigapkg
-PREGAME = mkdir -p ../targets/amiga
-CLEANMORE += rm -r ../targets/amiga
+PREGAME = mkdir -p $(TARGETDIR)
+CLEANMORE += rm -r $(TARGETDIR)
# ../util/txt2iff
#
ifdef WANT_WIN_CURSES
$(TARGET_CC) $(TARGET_CFLAGS) -o$@ $<
endif # CROSS_TO_AMIGA
#=================================================================
-ifdef CROSS_SHARED
-ifdef WANT_WIN_CURSES
-# rules for pdcurses dos-specific files
-$(TARGETPFX)%.o : $(PDCTOP)/sdl1/%.c
- $(TARGET_CC) $(PDCINCL) $(TARGET_CFLAGS) -o$@ $<
-endif # WANT_WIN_CURSES
-# Rule for LUA files
-$(TARGETPFX)%.o : $(LUATOP)/src/%.c
- $(TARGET_CC) $(TARGET_CFLAGS) $(LUA_FLAGS) -o$@ $<
+
+ifdef CROSS_TO_WASM
+#===============-=================================================
+# WASM
+# originally from https://github.com/NetHack/NetHack/pull/385
+#===============-=================================================
+#
+WASM_DATA_DIR = $(TARGETPFX)wasm-data/
+WASM_TARGET = $(TARGETPFX)nethack.js
+EMCC_LFLAGS = -s SINGLE_FILE=1
+EMCC_LFLAGS += -s WASM=1
+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_RUNTIME_METHODS='["cwrap", "ccall", "addFunction", \
+ "removeFunction", "UTF8ToString", "getValue", "setValue"]'
+EMCC_LFLAGS += -s ERROR_ON_UNDEFINED_SYMBOLS=0
+EMCC_LFLAGS += --embed-file $(WASM_DATA_DIR)
+# 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=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
+ifdef WASM_DEBUG
+EMCC_CFLAGS += $(EMCC_DEBUG_CFLAGS)
+else
+EMCC_CFLAGS += $(EMCC_PROD_CFLAGS)
+endif
+#
+# Override the build tools and some obj files to
+# reflect emscripten
+override TARGET_CC = emcc
+override TARGET_CXX = emcc
+override TARGET_AR = emar
+override TARGET_CFLAGS = $(EMCC_CFLAGS) -c \
+ -I../include \
+ $(LUAINCL) -DDLB $(PDCURSESDEF) \
+ -DNOTTYGRAPHICS -DSHIM_GRAPHICS -DDEFAULT_WINDOW_SYS=\"shim\" \
+ -DCROSSCOMPILE -DCROSSCOMPILE_TARGET -DCROSS_TO_WASM -DLIBNH
+override TARGET_CXXFLAGS = $(TARGET_CFLAGS)
+override TARGET_LINK = $(TARGET_CC)
+override TARGET_LFLAGS= $(EMCC_LFLAGS)
+override SYSSRC = ../sys/unix/unixmain.c \
+ ../sys/share/ioctl.c ../sys/share/unixtty.c \
+ ../sys/unix/unixunix.c ../sys/unix/unixres.c \
+ ../win/shim/winshim.c
+override SYSOBJ= $(TARGETPFX)unixmain.o \
+ $(TARGETPFX)ioctl.o $(TARGETPFX)unixtty.o \
+ $(TARGETPFX)unixunix.o $(TARGETPFX)unixres.o \
+ $(TARGETPFX)winshim.o
+override WINLIB = emranlib
+override LUALIB=
+override PACKAGE= wasmpkg
+override REGEXOBJ = $(TARGETPFX)posixregex.o
+RANLIB=$(EMRANLIB)
+#VARDATND += nhtiles.bmp
+override GAME=
+MOREALL += ( cd src ; $(MAKE) pregame ; $(MAKE) $(WASM_TARGET) )
+PREGAME = mkdir -p $(TARGETDIR)
+CLEANMORE += rm -f -r $(TARGETDIR)
+# Rule for file in sys/unix
+$(TARGETPFX)%.o : ../sys/unix/%.c
+ $(TARGET_CC) $(TARGET_CFLAGS) -c -o$@ $<
+# Rule for files in win/shim
+$(TARGETPFX)%.o : ../win/shim/%.c
+ $(TARGET_CC) $(TARGET_CFLAGS) -c -o$@ $<
+endif # CROSS_TO_WASM
+#=================================================================
+
ifdef WANT_WIN_CURSES
+ifdef BUILD_PDCURSES
# Rules for PDCurses files
$(TARGETPFX)%.o : $(PDCTOP)/pdcurses/%.c
$(TARGET_CC) $(PDCINCL) $(TARGET_CFLAGS) -o$@ $<
+endif # BUILD_PDCURSES
endif # WANT_WIN_CURSES
+
+ifdef CROSS_SHARED
# Rules for win/share files
$(TARGETPFX)%.o : ../win/share/%.c
$(TARGET_CC) $(TARGET_CFLAGS) -o$@ $<
$(TARGETPFX)%.o : ../util/%.c
$(TARGET_CC) $(TARGET_CFLAGS) -o$@ $<
endif # CROSS_SHARED
+
+ifdef BUILD_LUA
+# Rule for LUA files
+$(TARGETPFX)%.o : $(LUATOP)/src/%.c
+ $(TARGET_CC) $(TARGET_CFLAGS) $(LUA_FLAGS) -o$@ $<
+endif # BUILD_LUA
#
# End of cross-compiling -PRE section
#===============-=================================================
endif
endif
+ifdef WANT_LIBNH
+WANT_DEFAULT=shim
+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.
# prevent duplicates in VARDATND if both X11 and Qt are being supported
VARDATND += $(sort $(VARDATND0))
+ifdef WANT_LIBNH
+CFLAGS += -DSHIM_GRAPHICS -DNOTTYGRAPHICS -DNOSHELL -DLIBNH
+WINOBJ = winshim.o
+MOREALL += ( cd src ; $(MAKE) pregame ; $(MAKE) libnethack.a )
+libnethack.a: $(HOBJ) $(SYSOBJ) $(WINOBJ) ../lib/lua/liblua.a
+ $(AR) rcs $@ $(HOBJ) $(WINOBJ) ../lib/lua/liblua.a
+ @echo "$@ built."
+winshim.o : ../win/shim/winshim.c $(HACK_H)
+ $(CC) $(CFLAGS) -c -o$@ $<
+endif # BUILD_LIBNH
#PREFIX=/usr
PREFIX=$(wildcard ~)/nh/install
CFLAGS=$(CCFLAGS) -I../include -DNOTPARMDECL
ifndef WANT_WIN_QT
+ifndef WANT_LIBNH
# 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++;
#.../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
+#LIBNH's winshim requires C99 for the way it is currently coded
+endif
endif
# As of LLVM build 2336.1.00, this gives dozens of spurious messages, so
# leave it out by default.
# prevent duplicates in VARDATND if both X11 and Qt are being supported
VARDATND += $(sort $(VARDATND0))
+ifdef WANT_LIBNH
+CFLAGS += -DSHIM_GRAPHICS -DNOTTYGRAPHICS -DNOSHELL -DLIBNH
+WINOBJ = winshim.o
+MOREALL += ( cd src ; $(MAKE) pregame ; $(MAKE) libnethack.a )
+libnethack.a: $(HOBJ) $(SYSOBJ) $(WINOBJ) ../lib/lua/liblua.a
+ $(AR) rcs $@ $(HOBJ) $(WINOBJ) ../lib/lua/liblua.a
+ @echo "$@ built."
+winshim.o : ../win/shim/winshim.c $(HACK_H)
+ $(CC) $(CFLAGS) -c -o$@ $<
+endif # BUILD_LIBNH
WANT_BUNDLE=1
ifdef WANT_SHARE_INSTALL
static boolean wiz_error_flag = FALSE;
static struct passwd *NDECL(get_unix_pw);
-int
+#if defined(CROSSCOMPILE_TARGET) && defined(CROSS_TO_WASM)
+/* for cross-compiling to WebAssembly (WASM) */
+#include <emscripten/emscripten.h>
+/* if WebAssembly, export this API and don't optimize it out */
+#define KEEP EMSCRIPTEN_KEEPALIVE
+#else
+#define KEEP
+#endif
+
+
+int KEEP
main(argc, argv)
int argc;
char *argv[];
#endif
Fprintf(ofp, "#define VERSION_SANITY1 0x%08lx%s\n", version.entity_count,
ul_sfx);
+#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 /* __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 /* !__EMSCRIPTEN__ */
+
Fprintf(ofp, "\n");
Fprintf(ofp, "#define VERSION_STRING \"%s\"\n", version_string(buf, "."));
Fprintf(ofp, "#define VERSION_ID \\\n \"%s\"\n",
/* not an actual windowing port, but a fake win port for libnethack */
#include "hack.h"
+#include <string.h>
#ifdef SHIM_GRAPHICS
+#include <stdarg.h>
+/* for cross-compiling to WebAssembly (WASM) */
+#ifdef __EMSCRIPTEN__
+#include <emscripten/emscripten.h>
+#endif
+
+#undef SHIM_DEBUG
+
+#ifdef SHIM_DEBUG
+#define debugf printf
+#else /* !SHIM_DEBUG */
+#define debugf(...)
+#endif /* SHIM_DEBUG */
+
+
+/* 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
+ */
+#ifdef __EMSCRIPTEN__
+/************
+ * 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);
+
+/* 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 = (ret_type) 0; \
+ debugf("SHIM GRAPHICS: " #name "\n"); \
+ if (!shim_callback_name) return ret; \
+ local_callback(shim_callback_name, #name, (void *)&ret, fmt, args); \
+ return ret; \
+}
+
+#define VDECLCB(name, fn_args, fmt, ...) \
+void name fn_args { \
+ void *args[] = { __VA_ARGS__ }; \
+ debugf("SHIM GRAPHICS: " #name "\n"); \
+ 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, ...) \
+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__); \
+ return ret; \
+}
+
+#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__); \
+}
+#endif /* __EMSCRIPTEN__ */
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) \
#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))
+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)
+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)
+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)
+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")
+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)
+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)
+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)
+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)
+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) */
-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 */
+VDECLCB(shim_start_screen, (void), "v")
+VDECLCB(shim_end_screen, (void), "v")
+VDECLCB(shim_preference_update, (const char *pref), "vp", P2V pref)
+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,
+ (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)
+
+/* Interface definition used in windows.c */
struct window_procs shim_procs = {
"shim",
(0
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);
+#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);
- fprintf(stdout, "shim_print_glyph (%d,%d): %c\n", x,y,(char)glyph);
- fflush(stdout);
-}
+ let argTypes = fmt.split("");
+ let retType = argTypes.shift();
-void shim_raw_print(const char *str) {
- fprintf(stdout, "shim_raw_print: %s\n", str);
- fflush(stdout);
-}
+ // 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);
+ }
-void shim_putstr(winid w, int attr, const char *str) {
- fprintf(stdout, "shim_putstr (win %d): %s\n", w, str);
- fflush(stdout);
-}
+ // 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