Ttyrec

From NetHackWiki
Jump to navigation Jump to search

A ttyrec is a recording of a terminal. For example, you can play a game of NetHack in a text-mode terminal, and record everything that NetHack shows. nethack.alt.org and hardfought.org record all games.

ttyrec files can be played back with several programs:


Name Can rewind Can play compressed (e.g. .gz) Supports logarithmic time compression Notes
IPBT Yes (including frame by frame control) No Yes May have issues emitting ncurses output, and is very slow to read ttyrecs
playttyrec.c (also see https://www.stack.nl/~jilles/games/) No No No Supports "fake" timing, where ttyrecs don't contain time information
pyttyplay Yes Yes Yes (caps time past a duration) Uses Python. Can load from online. CLI only. Shows a timeline.
ttyplay and ttyrec No No No Part of the original ttyrec package.
termplay Yes (time based control, not frame by frame) Yes No Note that the bundled termcat package may mangle timing
Jettyplay Yes (full timing and frame by frame control) Yes Yes Uses a GUI, but has significantly more features including a timeline and online URL loading. However, it doesn't support some escape codes, scrolling escape sequences, and 'repeat character "N" times' commands, which can lead to unreadable output.

A game may be split into many ttyrec files. There are multiple approaches for merging them into a single game:

Name Notes
cat *.ttyrec > path/to/out.ttyrec Just by using built in Unix commands. Note that large time differences are maintained, so a player with logarithmic compression may be required.
termcat Timing may be mangled. See details.
ttytie Playback may break after the first concatenated file.

ttyrec format

A ttyrec consists of many frames. Each frame is made up of a twelve-byte header and an arbitrarily long data block. The twelve-byte header contains two pieces of information: how much data is in this frame and a timestamp. The timestamp is very precise; it has microsecond precision. The header bytes are aligned like so:

1 2 3 4 5 6 7 8 9 A B C
\-----/ \-----/ \-----/
  sec     usec    len

The bytes are in little-endian order (meaning least significant bytes first). You can portably read and process frames like this, in C:

while (fread(header, 1, 12, stdin) == 12)
{
  sec  = (((((header[ 3] << 8) | header[ 2]) << 8) | header[1]) << 8) | header[0];
  usec = (((((header[ 7] << 8) | header[ 6]) << 8) | header[5]) << 8) | header[4];
  len  = (((((header[11] << 8) | header[10]) << 8) | header[9]) << 8) | header[8];

  received = fread(data, 1, len, stdin)
  if (len != received)
    break;

  /* process data */
}

/* either the ttyrec is done or we had an error */
This page is a stub. Should you wish to do so, you can contribute by expanding this page.