tng_format.md 71 KB

TirNanoG File Format

  • File extension: .tng
  • Mime type: application/x-tirnanog
  • Copyright (c) 2021 - 2023 bzt, All Rights Reserved.

This format is used to store Adventure or Action RPG games. It can be created with the TirNanoG Editor tnge, and played with the TirNanoG Player, tngp. For the directory structure used by the editor during development, see the project layout.

I, bzt, the copyright holder of the file format release The TirNanoG File Format with dual licensing:

  • by default, the unencrypted game file format is licensed under CC-by-nc-sa (attribution required, non-commercial, share-alike)

  • with the written permission of the TirNanoG File Format's author, you can you can create proprietary encrypted game files.

The decision and the responsibility of which licensing version to use for the game files is up to the editor's users, and it does not influence the game's license stored in this format. Under no circumstances can I, bzt, the TirNanoG Editor and TirNanoG File Format's author be held responsible for the game files created by others.

Trying to decrypt, disassemble or any other way reverse engineer the proprietary encrypted format is strictly forbidden to protect the rights of the copyright holders of the assets inside the file.

IMPORTANT NOTE: the file format's license has nothing to do with the game's license stored in this format, just like how PKWARE's copyright does not influence the file's licenses stored in a .zip file.

The format was designed in a way that multiple files may store a single game, thus allowing modular expansion packs. The license of separate files of the same game should, but do not need to match; an encrypted file format might have an unencrypted expansion, but an unencrypted game cannot have an encrypted expansion (a technical limitation in the TirNanoG Player).

[[TOC]]

Header

This is a binary format, starting with magic bytes to be identifiable. Integers are stored in little-endian format, floating points as per the IEEE-754 specification. Strings are always stored as zero terminated UTF-8 multibyte characters (except for the game id, which is always 16 bytes and ASCII only).

Offset Length Description
0 16 magic, '#!/usr/bin/tngp' + ASCII 10 (newline)
16 16 game id, padded with zeros (may not be zero terminated)
32 1 file format revision (0 for now)
33 1 game type
34 1 tile width (bits 8-9 in game type byte bits 4-5)
35 1 tile height (bits 8-9 in game type byte bits 6-7)
36 1 map size in power of two
37 1 atlas size in power of two
38 1 audio samples frequency, (0 = 44100 Hz)
39 1 number of frames per second (by default 30)
40 1 number of default action handlers (by default 9)
41 1 number of map layers (by default 11)
42 1 number of transportation methods (by default 3)
43 1 highest bytecode command used in scripts
44 1 character sprites upwards delta y
45 1 number of character sprites per layer (by default 20)
46 2 must be zero
48 8 unique id (used by the client to detect if the server has a newer version)
56 4 CRC of the entire file (zero when calculating)
60 4 must be zero for CC-by-nc-sa licensed files, and it is non-zero for encrypted files

Game type is as follows: 0 - top-down, 1 - isometric, 2 - hex base vertical, 3 - hex based horizontal, 4 - 3D

The header is 64 bytes in length. For commercial and proprietary files, the rest of the file is encrypted.

Limits: one section max 16M (2^24), sections altogether max 4G (2^32), one atlas png max 2G (2^31), number of atlases max 4096 (2^12), one asset max 4G (2^32), tng archive total size max 2^63.

Sections

After the header comes the list of sections, compressed by deflate. It is prefixed by a 4 byte size value, and suffixed by a crc32 (included in the compressed size).

Offset Length Description
64 4 size of the compressed sections (n)
68 n compressed sections + crc

Section Table

The uncompressed buffer starts with a section table, where each entry is 8 bytes, followed by concatenated section data where the data size varies on section kind. The first entry in the section table points to the first section data, and thus its offset encodes the size of the section table as well.

Offset Length Description
0 4 section offset (relative to uncompressed sections block)
4 3 section length
7 1 section type

It is common that section data is a list of asset descriptors, which looks like

Offset Length Description
0 8 asset offset (relative to compressed sections' end in file)
8 4 asset length
12 4 asset name (offset to string section) or sprite index (upper 2 bytes must be zero)

Asset offsets are relative to the end of the compressed table (actual file offset = offset + 68 + compressed sections size).

Hereafter "string index", "font index", "music index", "sound index" etc. refers to an offset into the string section (so not a real record index but a byte offset), but not "text index" (which is the actual index of a string record in translatations section's text asset) nor "sprite index" (which is an actual index to the corresponding sprite section's record).

The first string is an empty string, so string index 0 means not defined. For the others -1 (0xffffff) encodes not set.

Relations are as follows: 0: = equal, 1: != not equal, 2: >= greater or equal, 3: > greater than, 4: <= less or equal, 5: < less, 0xff: set (not a required attribute relation but a provided attribute).

Event handler codes: 0: on click, 1: on touch, 2: on leave, 3: on approach, 4: on action1, 5: on action2, 6: on action3, 7: on action4, 8: on action5, 9: on action6, 10: on action7, 11: on action8, 12: on action9, 13: on using object1, 14: on using object2, 15: on using object3, 16: on timer, 17: on sell, 18: on buy.

Strings (section type 0)

This section always exists and it always must be the first. It points to variable length records, zero terminated ASCII strings. These contain the internal names of entities.

Translations (section type 1)

Has a header, followed by asset descriptors. The asset in the header points to a text asset in the file, encoded as a concatenated list of zero terminated UTF-8 characters compressed with deflate. Other assets point to an audio asset in the file, encoded as OGG vorbis, mono channels.

Offset Length Description
0 2 ISO-639-1 language code
2 8 asset offset (relative to compressed sections' end in file)
10 4 asset length
14 4 language name text index

The very first string in the text asset is always the human readable name of the game (title).

This section is special in a way that it might appear multiple times, but with different language codes. When it does appear multiple times, then the number of assets must be the same in every section, and they point to the same audio, just in a different language. If an audio file is missing for a specific language, then the corresponding asset descriptor contains zero length.

Fonts (section type 2)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a font asset in the file, encoded as SSFN2.

Music (section type 3)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to an audio asset in the file, encoded as OGG vorbis, stereo (or more) channels.

Sounds (section type 4)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to an audio asset in the file, encoded as OGG vorbis, mono channels.

Movie (section type 5)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a movie asset in the file, encoded as Theora OGV.

Sprite Atlases (section type 6)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to an image asset in the file, encoded as PNG.

Offset Length Description
0 8 asset offset (relative to compressed sections' end in file)
8 4 asset length
12 4 must be zero

User Interface Sprites (section type 7)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a sprite map asset in the file, which is compressed binary data.

Offset Length Description
0 8 asset offset (relative to compressed sections' end in file)
8 4 asset length
12 3 must be zero
15 1 number of directions stored for this sprite (d, 1 - 8)

A sprite in dimension is limited to 2048 x 2048 and 32 frames. When uncompressed, it contains (d) animation records:

Offset Length Description
0 2 sprite width
2 2 sprite height
4 1 type (bits 0 - 5: direction, bits 6 - 7: loop type)
5 1 number of frames (n, 1 - 32)
6 n * 14 sprite map descriptors

Loop type is as follows: 0: play once, 1: play forth and back, 2: play in a loop. The sprite map descriptor looks like:

Offset Length Description
0 2 atlas id (index to section type 6 entries) and operation
2 2 position x on atlas
4 2 position y on atlas
6 2 width on atlas
8 2 height on atlas
10 2 left margin
12 2 top margin

It is important that the atlas stores only a cropped version of the sprite, so sprite width and height can be bigger than width and height on atlas. Also it is possible that multiple sprite map descriptor points to the same sprite on the atlas, because atlases are deduplicated.

There can be max. 4096 atlases (referenced as 0 - 0xfff), so the upper tetrad of the atlas id encodes operation: 0 - simple copy, 1 - flip vertically, 2 - flip horizontally, 3 - rotate clock-wise, 4 - rotate 180 degrees, 5 - rotate counter clock-wise.

Tile Sprites (section type 8)

Same as section type 7.

Character Sprites (section type 9)

Same as section type 7.

Portrait Sprites (section type 10)

Same as section type 7.

Background Sprites (section type 11)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to an image asset in the file, encoded as PNG.

NOTE: section types 12 - 15 are reserved for future use.

User Interface Elements (section type 16)

Offset Length Description
0 1 number of color definitions (10)
1 40 10 * 4 bytes ABGR colors
41 1 number of font definitions (6)
42 42 6 * (1 byte style + 1 byte size + 3 bytes font index + 2 bytes padding)
82 1 number of sound effects (3)
83 9 3 * 3 bytes sound index
92 x rest is 3 bytes sprite records, 1 byte category, 2 bytes index

Font index is an offset to strings section. Sprite index selects a record from one of the sprite sections pointed by category (category is the same as section type - 7).

Main Menu (section type 17)

If the "main menu enabled" checkbox isn't set, then this block is only 3 bytes long and contains only the intro cutscene. Otherwise it's length is 141.

Offset Length Description
0 3 intro cutscene index
3 3 background music index
6 3 background movie index
9 3 background image category (1 byte) and index (2 bytes)
12 1 number of background parallax (3)
13 27 3 * (2 bytes dx, 2 bytes dy, 2 bytes time, 1 byte category, 2 bytes sprite index)
40 3 title image sprite, 1 byte category, 2 bytes index
43 1 bit 0: set if title text is shown, bit 1: don't crop background image / video
44 4 title text color
48 4 title text shadow color
52 5 title font (1 byte style, 1 byte size, 3 bytes font index)
57 3 website URL string index
60 2 button top padding
62 5 button font (1 byte style, 1 byte size, 3 bytes font index)
67 1 number of button definitions (5)
68 65 5 * (4 bytes color, 3 bytes left sprite, 3 bytes background, 3 bytes right)
133 4 attribute graph chart color
137 4 attribute graph background color

HUD (section type 18)

Offset Length Description
0 3 navigator circle sprite (1 byte category, 2 bytes index)
3 3 action buttons sprite (1 byte category, 2 bytes index)
6 3 item bar background sprite (1 byte category, 2 bytes index)
9 3 item bar foreground sprite (1 byte category, 2 bytes index)
12 7 one byte each, number of items, width, height, left, top, gap, handheld gap
19 1 zero if status bar isn't combined with item bar
20 3 status bar background sprite (1 byte category, 2 bytes index -1 if combined)
23 3 status bar foreground sprite (1 byte category, 2 bytes index -1 if combined)
26 3 inventory icon sprite (1 byte category, 2 bytes index)
29 3 inventory pressed icon sprite (1 byte category, 2 bytes index)
32 4 one byte each, inventory slot width, height, left, top
36 5 number of items font (1 byte style, 1 byte size, 3 bytes font index)
41 3 inventory slot background sprite (1 byte category, 2 bytes index)
44 3 inventory selected slot background sprite (1 byte category, 2 bytes index)
47 3 equipment slots background sprite (1 byte category, 2 bytes index)
50 1 number of progress bars (n)
51 n * 17 progress bars, n * (1 type, 2 left, 2 top, 3 value attr, 3 max attr, 3 sprite, 3 bg)

Equipment slots (section type 19)

Here each entry is 4 bytes long, thus section length must be multiple of 4. Each entry is a coordinate on equipment slots background sprite (specified in section type 18).

Offset Length Description
0 2 left
2 2 top

Alerts (section type 20)

Offset Length Description
0 5 alert font (1 byte style, 1 byte size, 3 bytes font index)
5 2 alert display time (in 1/100th second units)
7 2 alert fade-in end time (in 1/100th second units)
9 2 alert fade-out start time (in 1/100th second units)
11 1 number of alerts (n, at least 3)
12 n * 10 n * (4 bytes color, 3 bytes sound index, 3 bytes translateable text index)

Credits, Attributions (section type 21)

Offset Length Description
0 3 background music index
3 3 background image sprite, 1 byte category, 2 bytes index
6 4 header text color
10 5 header text font (1 byte style, 1 byte size, 3 bytes font index)
15 4 names text color
19 5 names text font (1 byte style, 1 byte size, 3 bytes font index)

This is followed by variable length records:

Offset Length Description
0 1 category code ("g", "c", "d", "a", "f", "m", "s", "v", "t", "p")
1 2 number of authors in this category (n)
3 n * 3 string indeces

Choosers (section type 22)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a chooser asset in the file, which is compressed binary data. When uncompressed, each asset has a 10 bytes long header:

Offset Length Description
0 2 chooser left margin
2 2 chooser top margin
4 2 chooser horizontal padding
6 2 chooser vertical padding
8 2 number of options in this chooser (n)

This is followed by (n) chooser options, each 31 bytes:

Offset Length Description
0 1 option position, 0 - 13, bit 7 means keep chooser running
1 6 if position 13, then 1 byte x1, y1, x2, y2; 3 * 2 bytes sprite index otherwise
7 4 1 byte type, 3 bytes required attribute index
11 4 required value
15 4 1 byte type, 3 bytes required attribute index
19 4 required value
23 4 1 byte type, 3 bytes provided attribute index
27 4 provided value

If position is less than 13, then sprite index chooses from UI sprites section. If it's 13, then a rectangle with percentage coordinates is stored, and the last 2 bytes are zero.

If type is greater than 1, then it encodes a local variable (2 = a, 3 = b, ... 27 = z). If it's zero (or 1, but that should not be here), then attribute index with offset to strings section is used. If the offset is zero, then required or provided attribute is not set.

Cutscenes (section type 23)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a cutscene asset in the file, which is compressed binary data. When uncompressed, each asset has a 21 bytes long header:

Offset Length Description
0 3 play once movie index
3 3 play once music index
6 3 play in a loop movie index
9 3 play in a loop music index
12 2 number of slide records
14 2 number of speech records
16 2 number of subtitle records
18 3 script bytecode length

This is followed by the specified amount of slide records, speech records, subtitle records and a script bytecode, in this order.

Slide records (19 bytes each):

Offset Length Description
0 3 start time (in 1/100th seconds)
3 3 fade-in time
6 3 fade-out time
9 3 end time
12 4 slide color
16 3 slide image sprite, 1 byte category, 2 bytes index

Speech records (6 bytes each):

Offset Length Description
0 3 start time (in 1/100th seconds)
3 3 speech audio index

Subtitle records (27 bytes each):

Offset Length Description
0 3 start time (in 1/100th seconds)
3 3 fade-in time
6 3 fade-out time
9 3 end time
12 4 text color
16 3 text position, x, y (in percentage) and align (0: left, 1: center, 2: right)
19 5 text font (1 byte style, 1 byte size, 3 bytes font index)
24 3 text index

For the script bytecode, see script assets below.

Startup Scripts (section type 24)

Here each entry has 16 bytes records, this section length must be multiple of 16. Each entry points to a script bytecode asset (see script assets below). The first script runs once when the game runs, the others are executed again in regular intervals. The records must be sorted by ascending interval value order (smaller interval first).

Offset Length Description
0 8 asset offset (relative to compressed sections' end in file)
8 4 asset length
12 4 re-run interval in 1/100th of seconds

NOTE: although looks like a normal asset descriptor, the name encodes the time intervals, and not the asset's name.

Attributes (section type 25)

Starts with a 19 bytes long header:

Offset Length Description
0 2 free-form mode total points
2 2 free-form mode maximum points per attribute
4 1 number of engine specific attribute definitons (4)
5 3 light radius attribute index
8 3 weight attribute index
11 3 speed attribute index
14 3 level up points attribute index
17 2 number of attributes (n)

This is followed (n) varying length attribute records, the record format depends on the first byte.

Offset Length Description
0 1 0, primary attribute
1 3 internal attribute name, string index
4 3 title of the attribute, text index
Offset Length Description
0 1 1, character variable attribute or 3, global variable attribute
1 3 internal attribute name, string index
4 3 title of the attribute, text index
7 4 default value
11 3 on change bytecode length (b)
14 b on change event handler bytecode

For the script bytecode, see script assets below.

Offset Length Description
0 1 2, character calculated attribute or 4, global calculated attribute
1 3 internal attribute name, string index
4 3 title of the attribute, text index
7 2 expression bytecode length (b)
9 b expression bytecode

For the script bytecode, see script assets below.

Default Action Handlers (section type 26)

This section always has fix number of records (9, see number of default action handlers in the header), each 16 bytes long. Each entry points to a script bytecode asset (see script assets below).

Offset Length Description
0 8 asset offset (relative to compressed sections' end in file)
8 4 asset length
12 4 range attribute index

NOTE: although looks like a normal asset descriptor, the name encodes the range attribute, and not the asset's name.

Character Option Groups (section type 27)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a character option group asset in the file, which is compressed binary data. When uncompressed, each asset has a 7 byte long header:

Offset Length Description
0 3 option group name text index
3 3 palettes sprite, 1 byte sprite category, 2 bytes sprite index
6 1 number of options in this group (n)

This is followed by (n) option records:

Offset Length Description
0 3 option name text index
3 3 option description text index
6 1 number of attributes (a)
7 1 number of inventory items (i)
8 1 number of animation sprites (s)
9 a * 8 attributes, each 1 byte relation, 3 bytes attribute index, 4 bytes value
x i * 4 inventory items, each 1 byte quantity, 1 byte category, 2 bytes object sprite index
x s * 3 animation sprites, each 1 byte sprite category, 2 bytes sprite index

If the project have used multiple layers for sprites, those are merged and saved as a single layer. The last one is the portrait sprite.

Non-Player Characters (section type 28)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to an NPC asset in the file, which is compressed binary data. When uncompressed, each asset has a 13 bytes long header, followed by data:

Offset Length Description
0 3 NPC title (name) text index
3 1 default behaviour (movement) type
4 2 speed percentage
6 2 direction change frequency in 1/100th second
8 1 partol path id
9 1 number of attributes (a)
10 1 number of inventory items (i)
11 1 number of animation sprites (s)
12 1 number of event handlers (e)
13 a * 8 attributes, each 1 byte relation, 3 bytes attribute index, 4 bytes value
x i * 10 inventory items, each 1 b chance, 3 b quantity, 3 b resupply freq, 3 b object index
x s * 3 animation sprites, each 1 byte sprite category, 2 bytes sprite index
x e * x event handlers

If the project have used multiple layers for sprites, those are merged and saved as a single layer. The last one is the portrait sprite.

Chance is in percentage, resupply frequency is in minutes (not in 1/100th seconds!). Object index has the category 1 and a 2 bytes long sprite index.

Each event handler record looks like:

Offset Length Description
0 1 event handler type (0 - 18)
1 3 parameter
4 3 event handler bytecode length (b)
7 b event handler bytecode

If type is 2 or 3, then parameter is a number, for 13, 14 and 15 it is an object sprite index (with category 2), for type 16 it is a timer interval code (0 = 1/10 sec, 1 = 1 sec, 2 = 10 sec, 3 = 20 sec, 4 = 30 sec, 5 = 1 min, 6 = 2 min, ... 12 = 1 hour, see project_eventtimers in project.c), otherwise it is zero. For the script bytecode, see script assets below.

NPC Spawners (section type 29)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a spawner asset in the file, which is compressed binary data. When uncompressed, each asset has a 6 bytes long header:

Offset Length Description
0 1 maximum number of NPCs handled by this spawner
1 3 respawn frequency in 1/100th second
4 2 number of spawned NPC kind records (n)

This is followed by (n) NPC specification records:

Offset Length Description
0 3 required player attribute string index
3 1 relation
4 4 required value
8 3 provided NPC attribute string index
11 4 provided value
15 3 spawning animation sprite, 1 byte sprite category, 2 bytes sprite index
18 3 NPC sprite, 1 byte sprite category, 2 bytes sprite index

If the attribute string index is zero, then the corresponding required or provided attribute is not set.

Dialogs (section type 30)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a dialog asset in the file, which is compressed binary data. When uncompressed, each asset has a 14 bytes long header:

Offset Length Description
0 3 dialog title text index
3 3 dialog description text index
6 3 dialog description spoken speech index
9 3 dialog portrait, 1 byte sprite category, 2 bytes sprite index
12 1 0: portrait on right, 1: portrait on left
13 1 number of dialog answer options (n)

The dialog sprite index is special in this case: FFFF encodes no sprite as usual, but FFF0 to FFFE means one of the user's portrait with expression. FFF0 is the normal, FFF1 is the happy, FFF2 is the sad etc. There can be 15 different expressions, but currently only one is supported by the editor.

This is followed by (n) dialog answer option records:

Offset Length Description
0 3 required attribute string index
3 1 relation
4 4 required value
8 3 provided attribute string index
11 4 provided value
15 3 dialog answer spoken speech index
18 3 dialog answer text index

If the attribute string index is zero, then the corresponding required or provided attribute is not set.

Objects Meta (section type 31)

Here each entry is 16 bytes long, thus section length must be multiple of 16.

Offset Length Description
0 8 asset offset (relative to compressed sections' end in file)
8 4 asset length
12 2 object sprite index
14 2 must be zero

NOTE: although looks like a normal asset descriptor, the name encodes a sprite index, and not the asset's name.

Each entry points to an object meta info asset in the file, which is compressed binary data. When uncompressed, each asset has a 39 bytes long header, followed by data:

Offset Length Description
0 3 object title text index
3 2 collision mask width (w)
5 2 collision mask height (h)
7 1 default action group (select default action handler, 0xff if none)
8 3 in inventory sprite, 1 byte sprite category, 2 bytes sprite index
11 3 inventory placement sound index
14 3 base price
17 2 price unit, object sprite index
19 1 object category (freely choosable)
20 3 projectile sprite, 1 byte sprite category, 2 bytes sprite index
23 3 projectile animation duration (in 1/100th seconds)
26 3 projectile ammo, object index, 1 byte sprite category, 2 bytes sprite index
29 1 sprite layering order
30 4 equipment position bit mask (0xffffffff = skill)
34 1 number of sound effects (s)
35 1 number of required attributes (a)
36 1 number of attribute modifiers (m)
37 1 number of character option sprite modifiers (c)
38 1 number of event handlers (e)
39 w * h collision mask, each slot 1 byte
39+w*h 96 32 times equipted sprites, 1 byte category, 2 bytes sprite index
x s * 3 sound effects, each 3 byte index
x 32 * 3 equipment slot sprites, each 1 byte sprite category, 2 bytes sprite index
x a * 8 required attributes, each 1 byte relation, 3 bytes attribute index, 4 bytes value
x m * 8 attribute modifiers, each 1 byte type, 3 bytes attribute index, 4 bytes value
x c * 123 sprite modifiers, each 3 bytes charoption text index, 40 * (1+2) bytes sprite index
x e * x event handlers

For the attribute modifier type, bit 0: set if modifier only applies when equipted, bit 1: set if value is in percentage. If attribute index is zero, then value modifies the transportation method.

For the sprite modifiers, if charoption text index is 0xffffff then it is the default sprite modifier (applies to all character options). Otherwise there are as many records as modified character options. The first 20 are the behind character sprites (per action, 19 basic actions + 1 portrait), and the next 20 is for the above character sprites.

Each event handler record looks like:

Offset Length Description
0 1 event handler type (0 - 16)
1 3 parameter
4 3 event handler bytecode length (b)
7 b event handler bytecode

If type is 2 or 3, then parameter is a number, for 13, 14 and 15 it is an object sprite index (with category 2), for type 16 it is a timer interval code (0 = 1/10 sec, 1 = 1 sec, 2 = 10 sec, 3 = 20 sec, 4 = 30 sec, 5 = 1 min, 6 = 2 min, ... 12 = 1 hour, see project_eventtimers in project.c), otherwise it is zero. For the script bytecode, see script assets below.

Object Crafting Dialogs (section type 32)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a crafting dialog asset in the file, which is compressed binary data. When uncompressed, each asset has a 14 bytes long header:

Offset Length Description
0 3 dialog title text index
3 3 dialog description text index
6 3 item crafted sound effect index
9 3 dialog portrait, 1 byte sprite category, 2 bytes sprite index
12 1 0: ingredients order matters, 1: order irrelevant
13 1 number of recipes (n)

This is followed by (n) crafting recipe records:

Offset Length Description
0 2 produced quantity
2 2 product, 2 bytes object sprite index
4 16*4 ingredients (2 bytes required quantity, 2 bytes object sprite index)

An ingredient slot might contain sprite index 0xffff, meaning no ingredient. All sprites belong to the objects category. Required quantity can be 0, meaning the item won't disappear from the player's inventory, but it is required during crafting (for example a pan).

Tiles Meta (section type 33)

Here each entry is 17 bytes long, thus section length must be multiple of 17. (More precisely, each entry is 2 + number of transportation methods * 5 bytes long, see header).

Offset Length Description
0 2 tile sprite index (category 1) for which this meta belongs
2 2 on walk speed modifier (percentage)
4 3 on walk sound effect string index
7 2 on swim speed modifier (percentage)
9 3 on swim sound effect string index
12 2 on fly speed modifier (percentage)
14 3 on fly sound effect string index
17 2 on drive speed modifier (percentage)
19 3 on drive sound effect string index
22 2 on X speed modifier (percentage)
25 3 on X sound effect string index

Percentage is stored on two bytes, because it can be larger than a byte, for example 300% (3 times the average).

Quests (section type 34)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a quest asset in the file, which is compressed binary data. When uncompressed, each asset has a 10 bytes long header:

Offset Length Description
0 1 0: every player can complete, 1: global quest, only one player can complete
1 3 quest title text index
4 3 quest description text index
7 3 on complete bytecode length (b)
10 b on complete event handler bytecode

Maps (section type 35)

Here each entry is 16 bytes long, thus section length must be multiple of 16. Each entry points to a map asset in the file, which is compressed binary data. When uncompressed, each asset has a 34 bytes long header:

Offset Length Description
0 3 map title (name), text index
3 6 * 3 neightbour maps string index
21 2 daylight length (in minutes)
23 3 daylight sprite, 1 byte sprite category, 2 bytes sprite index
26 3 background music index
29 1 number of parallaxes (p)
30 1 number of the patrol paths (n)
31 3 size of the RLE compressed map block (uncompressed numlayer * mapsize * mapsize * 2)

This is followed by (p) parallax record, each 10 bytes long:

Offset Length Description
0 1 0: foreground parallax, 1: background parallax
1 1 speed x (time based delta x, -100 to 100 it is signed!)
2 1 delta x (player movement based, 0 to 100)
3 1 speed y (time based delta y, -100 to 100 it is signed!)
4 1 delta y (player movement based, 0 to 100)
5 2 time interval (in 1/100th seconds)
7 3 parallax sprite, 1 byte sprite category, 2 bytes sprite index

Followed by (n) patrol paths, varying length records:

Offset Length Description
0 1 patrol path id (0 - 98)
1 2 patrol path length (l)
3 l * 4 path elements (in map coordinates), each 2 bytes x, 2 bytes y

Followed by the map layers, 2 bytes sprite indices without category. 0xffff means not set. The category for the layers are fixed: layer 5 NPCs (section 28), layer 6 spawners (section 29), all the other layers tiles (section 8). This is RLE compressed, 1 byte header (n, bit 7 means repeat), followed by 2 bytes if its a repeat block, otherwise (n + 1) * 2 bytes. The RLE packets do not stop at layer boundaries.

Neightbours are listed differently depending on game type (see header), but basically starting South and counter-clockwise.

  • top-down, isometric: S, W, N, E, zero, zero.
  • hex based vertical: SW, W, NW, NE, E, SE.
  • hex based horizontal: S, SW, NW, N, NE, SE.

Assets

The compressed sections block is followed by inlined assets, each with a size that appears in their asset descriptors in the sections. Except for the assets in some standard format (which is already compressed), the assets defined by this specification are always deflate compressed. The order of the assets is irrelevant, except map assets MUST be the last.

Font Assets

These are in Scalable Screen Font format.

Music Assets

Always in OGG Vorbis format, 44.1 kHz, stereo channels. See sample frequency in header.

Audio Assets

Always in OGG Vorbis format, 44.1 kHz, mono channels. See sample frequency in header.

Video Assets

Always in OGV Theora format. Pixels in planar IYUV420 format. If it has audio, then that must be in Vorbis format, 44100 Hz stereo channels (unless sample frequency specified otherwise in the header).

Image Assets

Always in PNG format with alpha channel. If possible, then in indexed format, otherwise in true-color.

Sprite Map Assets

See User Interface Sprites (section type 7) for details.

Map Assets

See Maps section (section type 34) for details. These must be the last in the file.

Script Assets

Several assets, most notably the script assets contain machine independent bytecode to describe event handlers. This is encoded as a series of varying length instructions. In each instruction the first byte is an opcode, the others are opcode dependent arguments.

OpCode Mnemonic Arguments (bytes) Description
0 END - End of Program
1 SWITCH 1 (n), n * 3 Switch, n + 1 branch with addresses (first is implicit)
2 JMP 3 (address) Jump to address
3 JZ 3 (address) Pop from stack and jump to address if zero
4 FNC0/CALL 1 (cmd) Call command function with zero arguments on stack
5 FNC1/CALL 1 (cmd) Call command function with one argument on stack
6 FNC2/CALL 1 (cmd) Call command function with two arguments on stack
7 FNC3/CALL 1 (cmd) Call command function with three arguments on stack
8 CNT0 - Push the total number of items in inventory
9 CNT1 2 (obj sprite idx) Push the number of a specific object in inventory
10 SUM 3 (attr idx) Push the sum of attributes of objects in inventory
11 CNTO0 - Push the total number of items in opponent's inventory
12 CNTO1 2 (obj sprite idx) Push the number of a specific object in oppo's inventory
13 SUMO 3 (attr idx) Push the sum of attributes of objects in oppos' inventory
14 RND 4 (constant) Push a random value between 0 and constant to stack
15 MIN 1 (n) Pop (n) values from stack, and push the smallest back
16 MAX 1 (n) Pop (n) values from stack, and push the biggest back
17 ADD - Pop two values from stack, and push addition result
18 SUB - Pop two values from stack, and push substract result
19 MUL - Pop two values from stack, and push multiplication result
20 DIV - Pop two values from stack, and push divider result
21 MOD - Pop two values from stack, and push remainder result
22 EQ - Pop two values from stack, and push 0/1 if they are equal
23 NE - Pop two values from stack, and push 0/1 if they are not equal
24 GE - Pop two values from stack, and push 0/1 if greater or equal
25 GT - Pop two values from stack, and push 0/1 if greater than
26 LE - Pop two values from stack, and push 0/1 if less or equal
27 LT - Pop two values from stack, and push 0/1 if less than
28 NOT - Pop one value from stack, and push 0/1 if its zero
29 OR - Pop two values from stack, and push logical or result
30 AND - Pop two values from stack, and push logical and result
31 POP 1 (local var) Pop into a local variable
32 POPA 3 (attr idx) Pop into an attribute
33 POPO 3 (attr idx) Pop into opponent's attribute
34 PUSH 1 (local var) Push a local variable
35 PUSHA 3 (attr idx) Push an attribute
36 PUSHO 3 (attr idx) Push opponent's attribute
37 PUSH8 1 (constant) Push a 8-bit constant
38 PUSH16 2 (constant) Push a 16-bit constant
39 PUSH24 3 (constant) Push a 24-bit constant
40 PUSH32 4 (constant) Push a 32-bit constant
41 PUSHMAP 3 (idx) + 2 + 2 Push map reference, string index, x and y in map coordinates
42 PUSHSPR 1 + 2 Push sprite reference, category, index
43 PUSHMUS 3 (music idx) Push a music reference
44 PUSHSND 3 (sound idx) Push a sound reference
45 PUSHSPC 3 (speech idx) Push a speech reference
46 PUSHCHR 3 (chooser idx) Push a chooser reference
47 PUSHCUT 3 (cutscn idx) Push a cutscene reference
48 PUSHDLG 3 (dialog idx) Push a dialog reference
49 PUSHCFT 3 (craft idx) Push a crafting ruleset reference
50 PUSHQST 3 (quest idx) Push a quest reference
51 PUSHTXT 3 (text idx) Push a translatable text reference

The command functions are defined in ui_cmd.c, in the cmd_types array. One can add new commands to it without modifying the other parts of the source code, bytecode will be generated automatically for these new functions, and the visual script editor will handle them correctly too.

Cmd Command Arguments (on stack) Description
0-4 - - Reserved
5 exit - Exit game, return to main menu
6 delay hsec Delay execution (in 1/100th of seconds)
7 music music Replace background music
8 sound sound Play sound effect
9 speak speech Play translatable speech
10 chooser chooser Run a chooser
11 cutscn cutscene Play a cutscene
12 dialog dialog,hsec Display a dialog for the given time (in 1/100th of seconds)
13 location map,direction Change player's location (map argument includes x,y)
14 quest completed,quest Add a quest or mark one as completed
15 canim action,hsec Play character action animation for given time
16 oanim object sprite,hsec Play object animation for given time
17 npc map,npc Place an NPC on map (also selects it as an actor)
18 actor npc Select an NPC
19 behave behaviour Change NPC's behaviour
20 market - Open NPC's inventory for exchange
21 south number Move (or turn if number is zero) number map slots in direction
22 southwest number Move (or turn if number is zero) number map slots in direction
23 west number Move (or turn if number is zero) number map slots in direction
24 northwest number Move (or turn if number is zero) number map slots in direction
25 north number Move (or turn if number is zero) number map slots in direction
26 northeast number Move (or turn if number is zero) number map slots in direction
27 east number Move (or turn if number is zero) number map slots in direction
28 southeast number Move (or turn if number is zero) number map slots in direction
29 remove Remove current object from map
30 drop object sprite Drop object to map at current object's position
31 delete map Remove object from map (map argument includes x,y)
32 add map,object sprite Place object on map at a given location
33 replace object sprite Replace object on map at current location
34 give object sprite,amount Give object(s) to player
35 take object sprite,amount Take away object(s) from player
36 event object sprite Call another object's "on click" event handler
37 transport method Change player's transportation method (walk, swim, fly, drive)
38 scene map,daytime Select scene for scripted cutscenes (no actors involved)
39 waitnpc - Wait until the selected actor finishes with their tasks
40 craft craftruleset Display an object crafting dialog
41 match map,object sprite Sets a to 1 if the object at location is the given one
42 alert alert Display a custom alert message
43 altitude number Change altitude (z buffer display order)

Map arguments use two values on the stack which are popped together, combined x,y coordinates value and a map id (see PUSHMAP).

A few things about object related commands: 29 and 30 is almost the same as 31 and 32, the difference is, the former two can only be used in an object event handler, and refers to the current object. The latter two has a map argument. Furthermore, while 30 and 33 uses a configured object (with meta info and translatable in-game name) as argument, 32 works with any sprite in the "objects" category.

To quickly decide if a player can decode the scripts in a game file, the largest (cmd) is recorded in the header. This means that future versions of the editor will automatically generate backward compatible game files if those new functions aren't used in a game.

Dialogs are a bit special. Depending wether the bytecode is in a cutscene script or not, it acts differently: in a cutscene the provided hsec is used and no answer options allowed. For everywhere else, hsec doesn't matter, it requires user's interaction to dismiss a dialog and dialogs might have answer options too. The hsec argument is stored regardless, because a player might use it with answer option-less dialogs if it implements some kind of "auto mode" feature.