Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
Add support for DEC graphic set from NetHack.
Browse files Browse the repository at this point in the history
NetHack allows you to use the DEC graphics set to render the dungeon
(https://nethackwiki.com/wiki/DECgraphics). Since DEC graphics mode is
toggled on and off with ESC ( 0, ESC ( B respectively, we add a new
'state' to tmt to indicate when ESC ( have been pressed. Normally
anything following this sequence is ignored, anyway.

Each character then saves whether it was written in DEC mode or not, in
attributes. While this would not work for a general DEC parser (since
some DEC characters include whitespace/new lines etc that would not have
been interpretted correctly by the terminal emulator - cf
https://en.wikipedia.org/wiki/DEC_Special_Graphics) in this case this
is fine because NetHack only uses DEC graphics for rendering particular
glyphs (as suggested in the NetHack Wiki, and as suggested in paxed/dgamelaunch
scriptfor stripping gfx).

A test is added to check rendering.
  • Loading branch information
cdmatters committed Apr 28, 2022
1 parent 2fcf0ab commit d06106a
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 33 deletions.
24 changes: 24 additions & 0 deletions nle/tests/2020-10-16.00_11_28.frame.5.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
It is written in the Book of Amaterasu Omikami:

After the Creation, the cruel god Moloch rebelled
against the authority of Marduk the Creator.
-------------- Moloch stole from Marduk the most powerful of all
.............| the artifacts of the gods, the Amulet of Yendor,
|d..........{| and he hid it in the dark cavities of Gehennom, the
|...........d| Under World, where he now lurks, and bides his time.
|..........@..
---------+---- Your goddess Amaterasu Omikami seeks to possess the Amulet, and with it
to gain deserved ascendance over the other gods.

You, a newly trained Hatamoto, have been heralded
from birth as the instrument of Amaterasu Omikami. You are destined
to recover the Amulet for your deity, or die in the
attempt. Your hour of destiny has come. For the sake
of us all: Go bravely with Amaterasu Omikami!
--More--




[CodeMagic the Hatamoto ] St:16 Dx:14 Co:17 In:11 Wi:9 Ch:7 Lawful
Dlvl:1 $:0 HP:15(15) Pw:2(2) AC:4 Xp:1 T:1
23 changes: 22 additions & 1 deletion nle/tests/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
# https://alt.org/nethack/trd/?file=https://s3.amazonaws.com/altorg/ttyrec/CodeMagic/2020-10-16.00:11:28.ttyrec.bz2 # noqa: B950
# This ttyrec uses DECGraphics (https://en.wikipedia.org/wiki/DEC_Special_Graphics)
TTYREC_DECGRAPHICS = "2020-10-16.00_11_28.ttyrec.bz2"
TTYREC_DECGRAPHICS_FRAME_5 = "2020-10-16.00_11_28.frame.5.txt"

SEQ_LENGTH = 20
ROWS = 25
Expand Down Expand Up @@ -237,7 +238,7 @@ def test_illegal_buffers(self):
with pytest.raises(ValueError, match=r"Numpy array required"):
converter.convert(chars, colors, cursors, timestamps, actions)

def _test_ibm_graphics(self):
def test_ibm_graphics(self):
seq_length = 10
converter = Converter(ROWS, COLUMNS)

Expand All @@ -254,3 +255,23 @@ def _test_ibm_graphics(self):
for row, line in enumerate(f):
actual = chars[-1][row].tobytes().decode("utf-8").rstrip()
assert actual == line.rstrip()

def test_dec_graphics(self):
seq_length = 10
COLUMNS = 120
converter = Converter(ROWS, COLUMNS)

chars = np.zeros((seq_length, ROWS, COLUMNS), dtype=np.uint8)
colors = np.zeros((seq_length, ROWS, COLUMNS), dtype=np.int8)
cursors = np.zeros((seq_length, 2), dtype=np.uint16)
actions = np.zeros((seq_length), dtype=np.uint8)
timestamps = np.zeros((seq_length,), dtype=np.int64)

converter.load_ttyrec(getfilename(TTYREC_DECGRAPHICS))
assert converter.convert(chars, colors, cursors, timestamps, actions) == 0

with open(getfilename(TTYREC_DECGRAPHICS_FRAME_5)) as f:
for row, line in enumerate(f):
actual = chars[5][row].tobytes().decode("utf-8").rstrip()
# print(actual)
assert actual == line.rstrip()
2 changes: 1 addition & 1 deletion third_party/converter/converter.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ void write_to_buffers(Conversion *conv) {
for (size_t c = 0; c < conv->cols; ++c) {
assert(conv->chars.cur < conv->chars.end);
assert(scr->lines[r]->chars[c].c < 256);
*conv->chars.cur++ = strip_gfx(scr->lines[r]->chars[c].c);
*conv->chars.cur++ = strip_gfx(scr->lines[r]->chars[c].c, scr->lines[r]->chars[c].a.dec);
*conv->colors.cur++ = vt_char_color_extract(&(scr->lines[r]->chars[c]));
}
}
Expand Down
30 changes: 3 additions & 27 deletions third_party/converter/stripgfx.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

unsigned char gfx_dec_map[256];
unsigned char gfx_ibm_map[256];
unsigned int state = 0;

/* clang-format off */
static unsigned char no_graphics[MAXPCHARS] = {
Expand Down Expand Up @@ -348,7 +347,6 @@ static unsigned char dec_graphics[MAXPCHARS] = {
void populate_gfx_arrays() {
int i;

state = 0;

memset(gfx_ibm_map, 0, 256);
memset(gfx_dec_map, 0, 256);
Expand All @@ -373,33 +371,11 @@ void populate_gfx_arrays() {
}

unsigned char
strip_gfx(unsigned char inchar)
strip_gfx(unsigned char inchar, int use_dec)
{
if (use_dec && gfx_dec_map[inchar])
return gfx_dec_map[inchar];
if (gfx_ibm_map[inchar])
return gfx_ibm_map[inchar];

if ((inchar == 0x0E) && (state == 0))
{
state = 1;
return 0x00;
}

if ((inchar == 0x0F) && (state == 1))
{
state = 0;
return inchar;
}

if ((inchar == 0x1B) && (state == 1))
{
state = 0;
return inchar;
}

if (gfx_dec_map[inchar] && (state == 1))
{
return gfx_dec_map[inchar];
}

return inchar;
}
2 changes: 1 addition & 1 deletion third_party/converter/stripgfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
#define INCLUDED_stripgfx_h

void populate_gfx_arrays();
unsigned char strip_gfx(unsigned char inchar);
unsigned char strip_gfx(unsigned char inchar, int use_dec);

#endif /* !INCLUDED_stripgfx_h */
10 changes: 7 additions & 3 deletions third_party/libtmt/tmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ struct TMT{
size_t pars[PAR_MAX];
size_t npar;
size_t arg;
enum {S_NUL, S_ESC, S_ARG} state;
enum {S_NUL, S_ESC, S_ARG, S_DEC} state;
};

static TMTATTRS defattrs = {.fg = TMT_COLOR_DEFAULT, .bg = TMT_COLOR_DEFAULT};
static TMTATTRS defattrs = {.fg = TMT_COLOR_DEFAULT, .bg = TMT_COLOR_DEFAULT, .dec = 0};
static void writecharatcurs(TMT *vt, wchar_t w);

static wchar_t
Expand Down Expand Up @@ -273,7 +273,8 @@ handlechar(TMT *vt, char i)
DO(S_ESC, "H", t[c->c].c = L'*')
DO(S_ESC, "7", vt->oldcurs = vt->curs; vt->oldattrs = vt->attrs)
DO(S_ESC, "8", vt->curs = vt->oldcurs; vt->attrs = vt->oldattrs)
ON(S_ESC, "+*()", vt->ignored = true; vt->state = S_ARG)
ON(S_ESC, "(", vt->state = S_DEC)
ON(S_ESC, "+*)", vt->ignored = true; vt->state = S_ARG)
DO(S_ESC, "c", tmt_reset(vt))
ON(S_ESC, "[", vt->state = S_ARG)
ON(S_ARG, "\x1b", vt->state = S_ESC)
Expand Down Expand Up @@ -310,6 +311,9 @@ handlechar(TMT *vt, char i)
DO(S_ARG, "s", vt->oldcurs = vt->curs; vt->oldattrs = vt->attrs)
DO(S_ARG, "u", vt->curs = vt->oldcurs; vt->attrs = vt->oldattrs)
DO(S_ARG, "@", ich(vt))
ON(S_DEC, "\x1b", vt->state = S_ESC)
DO(S_DEC, "0", vt->attrs.dec = true)
DO(S_DEC, "B", vt->attrs.dec = false)

return resetparser(vt), false;
}
Expand Down
1 change: 1 addition & 0 deletions third_party/libtmt/tmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ struct TMTATTRS{
bool invisible;
tmt_color_t fg;
tmt_color_t bg;
bool dec;
};

typedef struct TMTCHAR TMTCHAR;
Expand Down

0 comments on commit d06106a

Please sign in to comment.