7. Output streams and file handling

7.1 Output streams / 7.2 Buffering / 7.3 Selection (V1 and V2) / 7.4 Selection (later versions) / 7.5 Dealing with Unicode or invalid characters / 7.6 File handling


At any given time text is being output through a selection of "output streams" (possibly none, possibly several at once).


Two output streams are common to all Versions: number 1 (the screen) and 2 (the game transcript, usually printed to a printer or a file).

In Versions 1 to 5, the player's input to the read opcode should be echoed to output streams 1 and 2 (if stream 2 is active), so that text typed in appears in any transcript. In Version 6 input should be sent only to stream 1 and it is the game's responsibility to write to the transcript.

In Infocom's Version 4 game 'A Mind Forever Voyaging', which anticipated a printer rather than a file to receive the transcript, stream 2 is turned off and on again several times in quick succession. Thus if an interpreter decides where to send the transcript by asking the player for a filename, this question should only be asked once per game session, not every time stream 2 is selected.


Versions 3 and later supply these and two other output streams, numbered 3 (Z-machine memory) and 4 (a script file of the player's whole commands and of individual keypresses as read by read_char).

Output stream 3 writes to a table in dynamic memory. When the stream is selected, the table may have any contents (even the initial 'size' word will be ignored by the interpreter). While the stream is selected, the table's contents are unspecified (and a game cannot safely read or write to it). When the stream is deselected, the initial word of the table holds the number of characters printed and subsequent bytes hold those characters. Similarly, in Version 6, the total width of printing (in units) will then be stored in the word at $30 in the header. (It is the programmer's responsibility to make the table large enough: the interpreter performs no overflow checking.)

***[1.0] It is possible for stream 3 to be selected while it is already on. If this happens, the previous table address is remembered and the previous table is resumed when the new one is finished. This nesting can reach a depth of up to 16: if stream 3 is opened for a seventeenth time, the interpreter should halt with an error message.

Output stream 3 is unusual in that, while it is selected, no text is sent to any other output streams which are selected. (However, they remain selected.)

Newlines are written to output stream 3 as ZSCII 13. (A game should never print_char the value 10, or any other value which is undefined as a ZSCII output code.)

Output stream 4 is unusual in that, when it is selected, the only text printed to it is that of the player's commands and keypresses (as read by read_char). (Each command is written, in one go, when it has been finished. Time delays and mouse-clicks should ideally be recorded. For suggestions on how this might be achieved, see the remarks section below. Mistypes and uses of 'delete' are not written.)


On output streams 1 and 2 (only), text printing may be "buffered" in that new-lines are automatically printed to ensure that no word (of length less than the width of the screen) spreads across two lines (if the interpreter is able to control this). (This process is sometimes called "word-wrapping".)


In Versions 1 to 3, buffering is always on. In Versions 4 and later it is on by default (at the start of a game) and a game can switch it on or off using the buffer_mode opcode.


In Version 6, each of the eight windows has its own "buffering flag". In Versions 3 to 5, the buffer_mode applies only to the lower window, and buffering never happens in the upper window.


In Versions 1 and 2, output stream 1 is always selected and stream 2 can be selected or deselected by the game, by setting or clearing bit 0 of 'Flags 2'.


In Versions 3 and later, all four output streams can be selected or deselected using the output_stream opcode. In addition, stream 2 can be selected or deselected by setting or clearing bit 0 of 'Flags 2'. Whichever method is used, the interpreter must ensure that this flag holds the current status of stream 2. ('A Mind Forever Voyaging' requires this.)


***[1.0] Because of the print_unicode opcode, it is possible for arbitrary Unicode characters to be sent to the output streams: that is, for characters which are not in the ZSCII set at all, even in the "extra characters" range.


See S for rules on printing Unicode to stream 1.


Interpreters are free to use any representation of non-ASCII Unicode characters in stream 2. For example, they might print "[1a05]" to signify Unicode character $1a05; or they might be configurable to write transcript files which conform to any chosen ISO 8859 set.


When printed to stream 3, Unicode characters should be converted to ZSCII if possible. If this is not possible, a question mark should be printed to stream 3.


Non-ZSCII characters never need to be printed to stream 4.


***[1.0] In Versions 5 and later, the Z-machine has the ability to load and save files (using optional operands with the save and restore opcodes: these operands were not used in Infocom's Version 5 games, but I wish to specify them as in Version 5 anyway).


***[1.0] Filenames have the following format (approximately the MS-DOS 8.3 rule): one to eight alphanumeric characters, a full stop and zero to three alphanumeric characters (the "file extension").

The interpreter must convert all filenames to upper case before use. If no full stop is given, ".AUX" should be appended.

Games should avoid the extensions ".INF", ".H", ".Z" followed by a number or ".SAV": otherwise they may be in danger of erasing their own object code, source code or saved game files.

***[1.1] The interpreter should delete from the filename any characters illegal for a filename. This will include all of the following characters (and more, if the OS requires it): slash, backslash, angle brackets (less-than and greater-than), colon, double-quote, pipe (vertical bar), question-mark, asterisk. The library should also truncate the argument at the first full stop (delete the first full stop and any following characters). If the result is the empty string, change it to the string "NULL".


***[1.0] Saved files are not associated with any particular session of a game. They are not part of the "state of play".


***[1.0] A game may depend on having up to 32 auxiliary files (with different names).


File-handling errors such as "disc corrupt" and "disc full" should be reported directly to the player by the interpreter. The error "file not found" should only cause a failure return code from restore.


Interpreters are allowed to not support access to external files (such as with output_stream 2, or the extra features of save and restore), or to only support some methods of access. Interpreters should support these features if possible, as some games may rely on external files, and in any case transcripts are very useful for testing, but in some environments such access is not feasible.

An attempt by the game to use save or restore in a manner not supported by the interpreter should simply return 0 as with any failure, and the game should notice and take appropriate actions.

An attempt by the game to use streams to access external files which is not supported by the interpreter should ideally print a warning to the user that the functionality is not available, and otherwise do nothing.


The ITF interpreter incorrectly applies buffering when printing to the upper window.

Note that the requirement, that usages of stream 3 can be 'nested', is new in Standard 1.0. This is potentially important for Inform games, as stream 3 is often used to examine text before printing, for instance to choose between the articles "a" and "an" in front of an object name. But the process of printing an object name may itself require a usage of stream 3, and so on.

An ambiguous point about output stream 4 is whether it should contain the answers to interpreter questions like "what file name should your saved game have?": it can actually be quite useful to be able to include such answers in test script files. (When running a long script, I often save the game at several places during it, in order to save time in re-running passages.)

An interpreter should be able to write time delays (for timed input), accented characters or mouse clicks into stream 4 (i.e., to a script file). One possible style to record this information might be:

    take lamp              an ordinary command
    turn it on.[154]       command, full stop, then keypad 9
                           (which might abbreviate for NE)
    look unde[0]           timed out input
    look under the rock    the same input continuing
    [254][10][6]           mouse-click at (10,6)

A typical auxiliary file might be one containing the player's preferred choices. This would be created when he first changed any of the default settings, and loaded (if present) whenever the game started up.

Contents / Preface / Overview

Section 1 / 2 / 3 / 4 / 5 / 6 / 7 / 8 / 9 / 10 / 11 / 12 / 13 / 14 / 15 / 16

Appendix A / B / C / D / E / F