{"id":1706,"date":"2019-02-23T01:24:20","date_gmt":"2019-02-23T00:24:20","guid":{"rendered":"https:\/\/mehm.net\/blog\/?p=1706"},"modified":"2019-02-23T01:24:20","modified_gmt":"2019-02-23T00:24:20","slug":"porting-tads-to-the-browser","status":"publish","type":"post","link":"https:\/\/mehm.net\/blog\/?p=1706","title":{"rendered":"Porting TADS to the browser"},"content":{"rendered":"\n<figure class=\"wp-block-image\"><img loading=\"lazy\" width=\"1024\" height=\"641\" src=\"https:\/\/mehm.net\/blog\/wp-content\/uploads\/2019\/02\/UncleZebulon-1024x641.png\" alt=\"\" class=\"wp-image-1711\" srcset=\"https:\/\/mehm.net\/blog\/wp-content\/uploads\/2019\/02\/UncleZebulon-1024x641.png 1024w, https:\/\/mehm.net\/blog\/wp-content\/uploads\/2019\/02\/UncleZebulon-300x188.png 300w, https:\/\/mehm.net\/blog\/wp-content\/uploads\/2019\/02\/UncleZebulon-768x480.png 768w, https:\/\/mehm.net\/blog\/wp-content\/uploads\/2019\/02\/UncleZebulon-720x450.png 720w, https:\/\/mehm.net\/blog\/wp-content\/uploads\/2019\/02\/UncleZebulon.png 1282w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This is one of my most recent projects (at least those that I get around to writing about&#8230;). I recently got infatuated again with Interactive Fiction, played through <a href=\"https:\/\/en.wikipedia.org\/wiki\/Ballyhoo_(video_game)\">Ballyhoo<\/a> by Infocom as well as took part in the judging of the <a href=\"https:\/\/ifcomp.org\/\">Interactive Fiction Competition<\/a>. In the course of this, I became more interested in how these games are made and am still blown away by how good results they were able to achieve back then (Google Asssistant and Alexa have nothing on those old interpreters apart from their voice recognition). <\/p>\n\n\n\n<p>This lead me to playing around with the Text Adventure Development System (<a href=\"https:\/\/www.tads.org\/index.htm\">TADS)<\/a> by  Michael J. Roberts. TADS is one of the best-known toolkits to create interactive fiction and probably shares the top one spot with <a href=\"http:\/\/inform7.com\/\">Inform<\/a>.<\/p>\n\n\n\n<p>Apart from starting to build a small game, I noticed that it doesn&#8217;t have a real native browser port. And I had never worked with <a href=\"https:\/\/emscripten.org\/\">emscripten<\/a> before, so this seemed like a good opportunity. Emscripten is a cross-compiler which can compile C++ to javascript in order to run it in a browser, for example.<\/p>\n\n\n\n<p><strong>Process<\/strong><br><\/p>\n\n\n\n<p>In order to run TADS games, I looked at the available source code for TADS interpreters. TADS itself offers the code for the interpreter (see <a href=\"https:\/\/www.tads.org\/t3dl\/t3_src.zip\">https:\/\/www.tads.org\/t3dl\/t3_src.zip<\/a>), but to get the job done it was easier to look for a whole package in which everything required (handling the terminal, loading files, &#8230;), which I found with <a href=\"http:\/\/www.tads.org\/frobtads.htm\">FrobTADS<\/a>.<\/p>\n\n\n\n<p>The next step was to find a compatible <a href=\"https:\/\/en.wikipedia.org\/wiki\/Curses_(programming_library)\">curses<\/a> library in order to render the text to the browser. After some testing, I found <a href=\"https:\/\/github.com\/wmcbrine\/PDCurses\">PDCurses<\/a> which comes with SDL1 and 2 backends, which in turn makes porting them using emscripten easy as SDL is one of the libraries that emscripten ships with.<\/p>\n\n\n\n<p>In order to make both packages work, I had to make a few changes because clang, on which emscripten is built, was complaining about a few things in the code. Also the autoconf configuration couldn&#8217;t figure out that a curses library was present so I had to hardcode it in there.<\/p>\n\n\n\n<p>One tricky thing was that emscripten complained about SDL_Delay being called, which will make the runtime stop. SDL_Delay is used inside PDCurses and then in turn used by FrobTADS as an implementation to pause the program. I solved this by using the <a href=\"https:\/\/github.com\/emscripten-core\/emscripten\/wiki\/Emterpreter\">Emterpreter<\/a>, but I think that a cleaner solution would be to use <a href=\"https:\/\/github.com\/emscripten-core\/emscripten\/wiki\/Asyncify\">emscripten_sleep()<\/a> instead.<\/p>\n\n\n\n<p><strong>Result<\/strong><\/p>\n\n\n\n<p>You can find my fork of FrobTADS over on github: <a href=\"https:\/\/github.com\/SpookyFM\/frobtads\">https:\/\/github.com\/SpookyFM\/frobtads<\/a>. I added a fork of PDCurses as a submodule for convenience. This includes the shell (hardcoded to run the demo game) as well as the changes I made.<\/p>\n\n\n\n<p>In order to build the whole thing, do the following steps:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><em>For setting up your build environment: <\/em><br>source .\/emsdk_env.sh<br><em>To configure and build FrobTADS:<\/em><br>emconfigure .\/configure CPPFLAGS=\"-I\/PATH\/TO\/PDCurses-3.6\"<br>emmake make<br><br><em>To build PDCurses, run inside PDCurses\/sdl1:<\/em><br>emmake make<br><br><em>To build everything (Should have a .gam file in there as well):<\/em><br>emcc frob.bc \/PATH\/TO\/frobtads-1.2.3\/PDCurses-3.6\/sdl1\/libpdcurses.a -o new.html --preload-file pdcicon.bmp --preload-file pdcfont.bmp --preload-file *.gam --use-preload-plugins --shell-file shell.html -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1<br><\/pre>\n\n\n\n<p>To demonstrate the result, I have used the first winner of the Interactive Fiction award, <a href=\"http:\/\/www.ifwiki.org\/index.php\/Uncle_Zebulon%27s_Will\">Uncle Zebulon&#8217;s Will<\/a> by Magnus Olsson. You can find the game playable in the browser <a href=\"https:\/\/mehm.net\/download\/TADS\/new.html\">here<\/a>. Note that the download is actually quite heavy, for a text adventure, so give it a moment.<\/p>\n\n\n\n<p><strong>Possible next steps<\/strong><\/p>\n\n\n\n<p>At the current moment, this is more of a proof of concept than something ready to use. The above-mentioned SDL_sleep is one of the things that should be easy. Other things include:<\/p>\n\n\n\n<ul><li>testing if the curses library is doing everything it should do (some games can go crazy with doing 2D animations on the screen and such things)<\/li><li>supporting dynamically loading games instead of hardcoding them into the build<\/li><li>supporting save games and loading (possibly with <a href=\"https:\/\/en.wikipedia.org\/wiki\/Web_storage\">html 5 web storage<\/a> or cloud providers)<\/li><li>Increasing the resolution of the output and adjusting it to the browser window size<\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This is one of my most recent projects (at least those that I get around to writing about&#8230;). I recently got infatuated again with Interactive Fiction, played through Ballyhoo by Infocom as well as took part in the judging of the Interactive Fiction Competition. In the course of this, I became more interested in how [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1711,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[132,134,133,135],"_links":{"self":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1706"}],"collection":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1706"}],"version-history":[{"count":4,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1706\/revisions"}],"predecessor-version":[{"id":1712,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1706\/revisions\/1712"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=\/wp\/v2\/media\/1711"}],"wp:attachment":[{"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mehm.net\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}