资源说明:Tools for speedrunning ADoM (Ancient Domains of Mystery)
# ADoM TAS (adom-tas) ADoM TAS stands for Ancient Domains of Mystery Tool-Assisted Speedruns. It makes recording deterministic runs as input sequences possible and thus allows creating theoretically perfect speedruns and other demonstrations. Its possibilities are not restricted to speedruns although it's the main focus. The name is not very descriptive since it's built around an acronym. ## Schedule This project is in passive development and the first version was finished by 2012-07-01, the first working binaries were built 2012-07-02 and the pre-relase version was built 2012-09-02. ## Motivation ### ADoM ADoM is a roguelike video game and as such characterized by turn-based movement, random level generation, permanent death, textual graphics, luck-based gameplay, an unnecessarily complicated metagame, an inconvenient user interface and bad programming. Altogether it's an excellent game to disassemble and learn from other people's mistakes. ### Tool-Assisted Speedruns Tool-assisted speedruns combine two concepts: tool-assisting and speedrunning. Tool-assisting means controlling time and the environment to make theoretically flawless execution possible. Speedrunning obviously means playing as fast as possible. Typically being fast means minimizing the time spent, but for turn-based games the goal may be different. ## Installation ADoM TAS is written in C and only works on Linux since it relies on injecting assembly instructions to another executable and makes heavy use of features like `fork` and `execve`. ADoM TAS comes with a copy of ADoM that's unaltered and packaged as it was originally distributed as outlined in its license. ADoM TAS can be installed from binaries (already compiled executables) or sources (text files that need to be compiled). In either case it's recommended to browse through the whole installation instructions and possibly the troubleshoot section. The following examples are from a (freshly installed) Arch Linux. ### Binaries Binaries only need to be downloaded and extracted. [user@arch ~]$ wget https://github.com/Tuplanolla/adom-tas/blob/master/adom-tas.tar.gz [user@arch ~]$ tar -xf adom-tas.tar.gz They may be older than the sources. ### Sources Building ADoM TAS relies on the GNU Compiler Collection and GNU Make although any other C compiler and build automation tool should work as well. GNU extensions are used, but not required. [user@arch ~]$ pacman -S gcc make The libraries ADoM TAS depends on are the C standard library `libc`, the New Cursor Optimization library `libncurses` and a configuration file library `libconfig`. [user@arch ~]$ pacman -S libc libncurses libconfig Acquiring ADoM TAS from GitHub also requires SSH and Git. [user@arch ~]$ pacman -S openssh git Once the required packages are installed the repository can be cloned [user@arch ~]$ git clone git@github.com:Tuplanolla/adom-tas.git and ADoM TAS can be built. [user@arch ~]$ cd adom-tas [user@arch adom-tas]$ make The binaries go in the `bin` directory and temporary objects in the `obj` directory. The object files can be removed after compilation. [user@arch adom-tas]$ rm -f obj/* ## Running Note that the configuration files used by ADoM are overwritten by default to achieve consistency in recorded input sequences. To preserve the configuration files move or copy them elsewhere first. [user@arch adom-tas]$ cp -u ~/.adom.data ~/adom.data Running the launcher for the first time will generate a template configuration file in the current working directory and terminate with a note. [user@arch adom-tas]$ bin/adom-tas The configuration file may need to be edited. The process of doing so is addressed in its own section. [user@arch adom-tas]$ nano adom-tas.cfg After taking care of the configuration file the launcher will start properly. [user@arch adom-tas]$ bin/adom-tas ## Recording Encoding video files requires the Teletypewriter Recorder `ttyrec` for `*.tty` files and FFmpeg `ffmpeg` for `*.avi` files. Some formats also require the Audio/Video Codec library `libavcodec` or the Audio/Video Filter library `libavfilter`. [user@arch adom-tas]$ pacman -S ttyrec ffmpeg Recording is managed by shell scripts. [user@arch adom-tas]$ bin/ttyrec.sh -e bin/adom-tas -o output.tty [user@arch adom-tas]$ bin/ffmpeg.sh -e bin/adom-tas -o output.avi Advanced options like quality and filtering are also available [user@arch adom-tas]$ bin/ffmpeg.sh -e "ttyplay output.tty" -r 32 -m 16 -s sd -o output.avi and can be found in the help. [user@arch adom-tas]$ bin/ffmpeg.sh -h It is currently recommended to first record a `tty` file and then convert it to an `avi` file since `adom-tas` is only synchronized with `nanosleep` so frame processing times skew the frame durations. ## Configuration ADoM TAS uses a configuration file since command-line arguments are passed through to ADoM and the configuration is mostly static. When ADoM TAS is run it checks for the existence of its configuration file and creates the file from a template if it doesn't exist. By default the configuration file is called `adom-tas.cfg` in the current working directory, but the default location can be changed by modifying the variable `def_config_path` in the file `def.c` and rebuilding ADoM TAS. The default file extension is `*.cfg` since the configuration file library uses it by default and `*.conf` was originally planned to be reserved for a configuration language. The configuration file contains * the location of the ADoM `executable`, * the location of ADoM's `data` directory, * the location of ADoM TAS's `loader`, * the location of the C standard library `libc`, * the location of the New Cursor Optimization library `libncurses` and * whether default configuration files are enforced (`true` by default), * the location of the `home` directory, * the amount of save `states` excluding the currently active state (at least `1` and `9` by default), * the height of the terminal in `rows` (at least `25`, at most `127` and `25` by default), * the width of the terminal `cols` in columns (at least `77`, at most `127` and `80` by default), * the location of the shared memory segment lock `shm` (`adom-tas.shm` by default), * the amount of character `generations` (`100` by default), * the `timestamp` of the initial system time (`0` by default), * whether the save-quit-load emulation is enabled (`true` by default), * whether playback starts automatically if an input file is present (`false` by default), * whether the user interface has colorful backgrounds (`true` by default), * the string `iterator` to replace with the save state number when processing output file names (`#` by default), * the location of the `input` file for playback (`input.tas` by default), * the location of the `output` files for recording (recommended to be `output.#.tas` and `output.tas` by default), * the location of the `error` log (`stderr` by default), * the location of the `warning` log (`stderr` by default), * the location of the `note` log (`stderr` by default), * the location of the `call` log (`/dev/null` by default) and * the `key` numbers of various commands. The standard streams are `stdin`, `stdout` and `stderr`. All file paths can be absolute, relative or linked and the shell variable `~` is expanded to the home directory. The shared memory segment lock file has to exist, but its contents don't matter since it's only used for identification. The template configuration should work out of the box. If ADoM TAS is run from the same directory it's maked from (sic) the only values that may be wrong are the library locations. The libraries can be searched for to make life easier. [user@arch ~]$ find /lib /usr/lib -maxdepth 1 \( -name libc.so.\* -o -name libncurses.so.\* \) 2>/dev/null The input file is read when the playback key is pressed on the first frame. The output file is written when the corresponding save state is used. ## User Interface The user interface deserves a mention since it's so intuitive. The status bar looks like I: \M\Cf F: 2/21 T: 0/7 D: 1/2 E: 15 R: 0xe87de001 S: 2/9 P: 16384 and contains the last recorded inputs (Alt Ctrl F), the amount of last recorded frames (2) and the amount of all frames (21), the amount of actual turns elapsed since the last input (0) and the amount of all actual turns (7), the duration of the next frame (half a second), the time elapsed since the last emulated save-quit-load process (15 seconds), the current hash of the random number generator's state and the currently selected save state (#2) and the amount of all save states (9) and the current process identifier. By default * `F2` saves the current save state, * `F3` loads it, * `F8` selects the next save state, * `Shift F8` selects the previous one, * `F5` increases (doubles) the duration of the next frame, * `Shift F5` decreases (halves) it, * `F6` shifts the system time forwards (by one second), * `Shift F6` shifts it backwards (but not to a negative value), * `F9` opens or closes the save state menu, * `Shift F9` opens or closes the information screen, * `F10` condenses or expands the user interface, * `Shift F10` hides or shows it, * `F11` plays or pauses a record (but only starting on the first frame), * `Shift F11` stops a record (and enables appending to it), * `Shift F12` quits and * the save key (typically `S`) emulates the save-quit-load process. The keys only used to interact with ADoM TAS are not recorded. ## Troubleshooting Compilation failed with `No such file or directory`? Consider * ensuring the installed libraries are * `libncurses` instead of `libcurses` and * `libconfig` instead of `libconfig++`, * checking that `gcc` and `make` are installed properly and * making sure the commands `rm`, `mkdir` and `cp` are unaltered. Running failed with `Parsing the configuration file failed`? Consider * making sure the syntax is correct with * an equals sign `=` between keys and values and * strings enclosed in quotation marks `"`, * updating libconfig, * taking care of legacy problems by * adding semicolons `;` to the end of each line and * ensuring the last line ends with a line break `\n`, * removing uncommon whitespace characters like no-break spaces `\xa0` or * generating a new configuration file. Something else happened? Consider * trying to understand the error message better, * looking it up in the well-documented source code, * reinstalling ADoM TAS or * asking for help. ## Notes The attributes of a character are set when the text "You are born to be a male gnome." appears and varied until the text "You are now a fully learned wizard." appears. The items of a character are generated when the talent menu opens. A character is considered generated when the text "You are now a fully learned wizard." appears. Putting the resource file `ADOM.DBG` in the current working directory will enable ADoM's debug mode. I have no idea what it does, but hopefully someone can figure it out and tell me. ## Development ### Tracing ADoM TAS comes with a wrapper for `ltrace` that logs library calls. [user@arch adom-tas]$ pacman -S ltrace The log files it generates are enormous, so use it with care. [user@arch adom-tas]$ bin/ltrace.sh ### Documentation It's possible to generate automated documentation for ADoM TAS with Doxygen. [user@arch adom-tas]$ pacman -S doxygen The process is similar to makeing (sic). [user@arch adom-tas]$ doxygen ### Conventions The naming conventions used in the project follow those of the implementations and the man pages of similar functions in the C standard library. The syntax conventions in the other hand follow the simplest possible consistent set of rules that allows condensed and patterned code. Keywords are always followed by spaces and functions never, binary operators are always separated by spaces and unary never, pointers are always separated from their types and identifiers since their binding varies depending on the context, comments and casts are removable using the simplest possible pattern to leave no traces of their existence and continued lines are always indented twice to separate them from scopes they may start to name a few. ### Directory Structure Files are named and organized in a typical manner. The directories are * `/` for configurations, * `/src` for sources, * `/obj` for temporary build files, * `/lib` for libraries, * `/bin` for binaries, * `/res` for resources, * `/doc` for documentation, * `/doxygen` for automated documentation, * `/cat` for catalogs, * `/rec` for records and * `/adom` for ADoM. ### File Format Records are saved to `*.tas` files in a custom format. The files contain * a 4-byte `char [4]` header (always `54 41 53 00` for "TAS"), * a 256-byte `char [256]` author (for example `54 75 70 6c 61 6e 6f 6c 6c 61 00 ...` for "Tuplanolla"), * a 256-byte `char [256]` executable name (for example `61 64 6f 6d 00 ...` for ADoM), * a 256-byte `char [256]` comments, * a 4-byte `unsigned int` category (where `00 00 00 00` is uncategorized), * a 8-byte `unsigned int` amount of frames, * a 8-byte `unsigned int` time elapsed, * a 8-byte `unsigned int` turns spent, * padding to 1024-byte alignment and * executable-specific `frame_d` chunks. For ADoM the categories are * `01 00 00 00` for minimum actual turns (without negative turns) and * `02 00 00 00` for minimum ideal time (without saving, quitting and loading) * although all categories up to `ff 00 00 00` are reserved for challenge games and the chunks consist of * a 1-byte `unsigned char` duration and * a 4-byte `time_t` excerpt. Additionally characters are catalogued to `*.tac` files. The files contain * a 4-byte `char [4]` header (always `54 41 67 00` for "TAC"), * a 256-byte `char [256]` executable name (for example `61 64 6f 6d 00 ...` for ADoM), * padding to 1024-byte alignment and * an executable-specific `catalog_d` chunk. For ADoM the chunk consists of * a 4-byte `unsigned int` birthday (day of the year minus one), * a 4-byte `unsigned int` sex, * a 4-byte `unsigned int` race, * a 4-byte `unsigned int` profession, * a 52-byte `char [51]` list of answers, * a 4-byte `unsigned int` potential crowning gift, * a 36-byte `unsigned int [9]` list of initial attributes, * a 2788-byte `unsigned int [697]` list of identified items and * a 188-byte `unsigned int [47]` list of identified books. ### Making of ADoM TAS June 2012 was when it all begun... ...and that's it (more will be written later). #### Random Number Generator The best way to record what's essentially a nondeterministic finite automaton is finding the sequence of inputs that result in the desired output. Eliminating entropy guarantees the same inputs always map to the same output, which is essential to avoid desynchronization. Practically it means finding out what kind of a random number generator is used, emulating it to predict how it changes and controlling external factors that affect it. The GNU Debugger is used to create a controlled test environment and find out how the random number generator works. [user@arch ~]$ cd adom [user@arch adom]$ gdb adom Commonly used random number generators are `rand`, `random` and `drand48`. To find out which of them are linked with the executable the `info functions` command is used. (gdb) i fu rand 0x08049380 random 0x080496d0 srandom The nonlinear additive feedback random number generator `random` and its seed function `srandom` are found. Breakpoints set with the `break` command and `ignore`d to help count how many times they're called. The `run` command starts the process. (gdb) b random (gdb) b srandom (gdb) ig 1 0xffff (gdb) ig 2 0xffff (gdb) r Once the process terminates `info break` is used to inspect the breakpoints and `delete breakpoints` to clean up afterwards. (gdb) i b (gdb) d br Both of the functions are only called once, so a custom random number generator is used and the functions are likely there just to initialize it. However `random` is only a pseudo-random number generator and as such requires a source of entropy. Timers are commonly used as such sources. (gdb) i fu time 0x080492e0 localtime 0x08049420 time 0x08049610 notimeout 0x08049700 wtimeout Only `time` and `localtime` are interesting since they're absolute timers and reside in the standard library. Breakpoints help count how many times they're called again. (gdb) b localtime (gdb) b time (gdb) ig 3 0xffff (gdb) ig 4 0xffff (gdb) r (gdb) i b (gdb) d 3 Since `time` is also called once at startup, it's likely that `srandom(time(NULL))` is first called to seed the pseudo-random number generator and `random()` is then called to seed the custom random number generator. The assumption is checked by using `break` and `finish` to position `$pc` (the program counter) and e`x`amine `i`nstructions to read the disassembly. (gdb) ig 4 0 (gdb) r (gdb) fin (gdb) x /17i $pc - 0x13 0x08125d10: push %ebp 0x08125d11: mov %esp, %ebp 0x08125d13: sub $0x8, %esp 0x08125d16: add $0xfffffff4, %esp 0x08125d19: add $0xfffffff4, %esp 0x08125d1c: push $0x0 0x08125d1c: push $0x0 0x08125d1e: call 0x08049420
本源码包内暂不包含可直接显示的源代码文件,请下载源码包。