User Tools

Site Tools


zxt:format

ZXT Extended World Format

This is a draft! Things are still subject to change.

Introduction

Over the past thirty years, many ideas have been theorized and approaches tried with regards to extending and tweaking the behaviour and capabilities of the ZZT and Super ZZT game engines. With the release of the Reconstruction of ZZT greatly lowering the bar for creating such modified engine versions, the complexity and amount of forks, as well as interest in them, has begun to grow significantly. As such, in order to preserve to the extent possible the interoperability ZZT's ecosystem is known for, a standardized way to declare worlds which use or depend on extensions to the original ZZT and Super ZZT game engines is necessary.

A common format for declaring extensions will allow:

  • Creating forks and source ports of ZZT which support a more complete set of the diversified (non-ZZT 3.2-strict) game world catalog,
  • Mixing and matching extensions between forks at will,
  • Broader editor support for handling extended worlds.

The game world types that the ZXT format considers are as follows:

  • Creating completely custom experiences derived from ZZT or Super ZZT. Example: “The King in Yellow Borders”, WiL.
  • Creating games which can benefit from enhanced functionality, but are playable in unmodified ZZT or Super ZZT engines. Example: “Variety”, WiL.
  • Standardizing pre-fork games which modify ZZT or Super ZZT in an incompatible way. Example: “ZZT Enhancer”, Craig Boston; “Banana Quest”, WiL; 64K intros by bitbot.
  • Standardizing pre-fork games which modify ZZT or Super ZZT in partially or fully compatible ways. Example: “Angelis Finale: Episode 1”, Commodore (custom character set); “Daedalus' Obelisk”, Darren Hewer (optional ZZT modification to remove certain sounds and messages).

Definitions

These definitions may be used freely in extension standards without re-introduction.

  • The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this and following documents are to be interpreted as described in RFC 2119.
  • The following types, in data blob specifications, all little-endian (where applicable):
    • u8 - unsigned byte (8 bits),
    • s8 - signed byte,
    • u16 - unsigned word (2 bytes),
    • s16 - signed word,
    • u32 - unsigned dword (4 bytes),
    • s32 - signed dword,
    • u64 - unsigned qword (8 bytes),
    • s64 - signed qword,
    • pstring[X] - a Pascal-style string; a length u8 (N, of range between 0 and X) followed by X characters, of which the first N are considered valid.
    • pstring - a Pascal-style string; a length u8 (N, of range between 0 and 255) followed by N characters.
    • plstring - a Pascal-style long string; a length u16 (N, of range between 0 and 65535) followed by N characters.

File Types

The extension data can be provided in one of two ways:

  • Providing a .ZAX file with the same filename as a world file (for instance, TOWN.ZZT and TOWN.ZAX), containing only the Extension Header.
  • Providing a .ZXT file, being formed as a concatenation of an Extension Header and a world file (cat TOWN.ZAX TOWN.ZZT > TOWN.ZXT is a valid operation).

This is done because neither solution is strictly superior:

  • .ZAX files can be used to extend games with additional functionality (keybind shortcuts, metadata, etc.) without the need to distribute a modified .ZZT file.
  • A .ZXT file cannot be understood by unmodified versions of ZZT or Super ZZT, which would require ZZT-compatible games making use of extensions to distribute two variants - a .ZZT and a .ZXT.
  • Distributing the world as a single .ZXT allows for greater user-friendliness than having to carry around two files, especially for non-ZZT-compatible worlds.

Extension Header

The extension header's format is as follows:

Offset Type Name Description
0 u16 magic 0xF227 for ZZT-style worlds, 0xF527 for Super ZZT-style worlds
2 u32 block_count The number of extension blocks which immediately follow.

After the header follow block_count extension blocks, in sequence:

Offset Type Name Description
0 u16 flags Extension flags; defined below.
2 u32 owner_id Extension owner ID
6 u16 selector_id Extension selector ID
8 u8 reserved_0 Reserved.
9 u16 field_length Field length. Can be between 0 and 65534; if set to 65535, an u32 containing the 32-bit field length follows.
11 u8[field_length] field_data Field data. Extension-defined.

An extension being “understood” is defined as one for whose ID pair the engine provides an implementation compliant with its standard.

The extension flags are as follows:

Bit Name Description Behaviour Layman's terms
0 parsing_must Required for parsing. If set, an implementation which does not understand this extension MUST NOT continue parsing of the extension block. Generally, this one will be set to 0.
1 reading_must Required for reading. If set, an implementation which does not understand this extension MUST NOT read the world file, but may continue parsing the extension block. If you're changing the world format in a breaking way, set this to 1.
2 writing_must Required for writing. If set, an implementation which does not understand this extension MUST NOT try to write the world file. If you're changing the world format in a partially-breaking way, set this to 1. (For instance, Variety redefines the flag section of a .ZZT file in a way which breaks reading only if at least two flags are set; however, a written world could have modified these flags, so we must prevent that. Another example would be re-using the padding fields.)
3 playing_should Recommended for playing. If set, an implementation which does not understand this extension SHOULD signal this to the end user if an attempt at playing the world is made. Non-strictly-breaking gameplay changes. For example, modified messages or sound effects. Sometimes, modified charsets or palettes.
4 playing_must Required for playing. If set, an implementation which does not understand this extension MUST signal this to the end user and prevent an attempt at playing the world. Breaking gameplay changes. New OOP commands if required, new element IDs, etc.
5 editing_should Recommended for editing. If set, an implementation which does not understand this extension SHOULD signal this to the end user if an attempt at editing the world is made. An engine does not have to make any effort to support such a world further. Non-format-breaking (format-breaking go into writing_must, reading_must or both) editor-side changes. Unknown element IDs count, for example.
6 preserve_should Recommended to preserve on resave. If set, an implementation SHOULD preserve the extension unchanged upon resave; if cleared, an implementation MUST NOT do so, and MUST discard the extension. Generally, you want this set to 1 - unless the field data relies on other board, world or extension data outside itself. Meant in particular for metadata.
7 .. 15 reserved Reserved. If set, an implementation MUST NOT continue parsing of the extension block.

It is important to note that the flags can be distinct from the ID pair; for instance, the same ZZT-OOP extension can be defined as “recommended for playing” if optional for gameplay, but “mandatory for playing” if required for gameplay. However, an extension standard may require you to set or clear certain bits.

Interactions between multiple extensions

  • Header/Data Expansion: If an extension extends the board header, the world header, the tile data, the stat data, or any other existing in-file structure outside of the extension block, the order of parsing MUST be in ascending order of the 32-bit owner ID, then the 16-bit selector ID.
  • Repeated Extensions: Extensions SHOULD allow being defined multiple times within a single list of extension blocks. This can be done for a variety of purposes:
    • defining multiple variants of an extension - custom, per-extension lists would lead to increased parsing complexity. For example, a custom character set extension could allow repetitions for different text cell width/height pairs.
    • defining multiple flag variants for the extension. For example, a stat limit change extension could set a different mandatory (playing_must) and optional (playing_should) limit.
  • TODO: Should the extension list be enforced as an ordered list? Is there a strong enough use-case?

Any interactions not listed above are undefined behaviour and MUST NOT be relied upon. For example:

  • If one extension increases the number of Keys from 7 to 16, and another redefines Keys to be able to hold more than one of them at a time, and neither defines its behaviour in the presence of the other extension, they MUST NOT be used in a production world together - a third extension should be defined which joins their behaviour.

Reserved extension owner IDs

  • The range 00000000 - 000000FF is reserved for standard extensions - expansions to this standard, as well as potential “obvious” changes everyone agrees upon.
  • The range FFFFF000 - FFFFFFFF is reserved for private extensions - usage when a standard for a given extension is not yet defined, or for internal/private experiments. Publicly released worlds SHOULD NOT use this range.

Considerations for .ZXT worlds and engines

  • An implementation consuming a .ZXT world, or a world with a .ZAX file, MUST NOT be expected to preserve ZZT's memory layout exactly. (That is to say, out-of-bounds memory reads and writes, with the exception of the Black Key, are not guaranteed to work correctly.)
    • If you're relying on out-of-bounds memory writes, consider making an extension standard for it instead!
zxt/format.txt · Last modified: 2021/03/13 08:45 by asie