WAY files contain navigation information for bots. The file starts with a header, providing basic file information and chunk directory; the header is NULL-padded so the chunk directory starts at an address multiple of 16 (each individual chunk is also padded with NULLs to the same effect). It means that the size in bytes of WAY files is always multiple of 16.

  header_t    Header        File header (16-byte padded with NULLS).
  chunkDir_t  Entry[]       Chunk directory, 16 bytes per entry.
  chunk       Chunk[]       Each chunk (16-byte padded with NULLS).

== HEADER ==

  header_t
    {
    4  CHAR[4]    magic         MUST be "JkBt" or 0x74426B4A
    2  UINT16     version       MUST be 5
    2  UINT16     botversion    If this number is higher than current bot version, print out a warning.
    2  UINT16     revision      Number of times the file has been saved.
    2  UINT16     author        Netname of the last user to save the route, offset in the string bank.
    2  UINT16     banksize      Size of the string bank, in bytes.
    2  UINT16     numchunks     How many chunks are stored in the directory.
    ?? CHAR[??]   stringbank    The whole string bank (16-byte padded).
    }

== STRING BANK ==
  A series of null-terminated strings, this makes it possible to link strings to different structures without creating elements of variable length. Each string is referred to by offset; the bank is padded with NULLs to preserve 16-byte alignment. It may contain node types, entity class names, the alias of the client who created the route, chunk names, etc.

== CHUNK DIRECTORY ==

  chunkDir_t
    {
    2  UINT16  chType       Type of chunk, offset in the string bank
    2  UINT16  chVersion    Sub-version of the chunk (may be used in the future)
    4  UINT32  chLength     Size of the data stream (may or not be compressed)
    4  UINT32  chRLESize    Length in bytes of the RLE (Run-Length Encoding) instructions set
    4  UINT32  chrWRSize    Length in bytes of the WR (Word Repeat) instructions set
    }

  The length in bytes of the whole chunk is: <chLength> + <chRLESize> + <chWRSize> + <padding>. You'll need to write two functions to retrieve the size of the decompressed stream: one for RLE and another for WR.

  chType:
    Type of chunk, it MUST be one of the following (1. any other type is forbidden - 2. there can only be ONE of each type - 3. NODE and LINK are mandatory - 4. Chunk names are case sensitive)
    "NodeLst" - node list
    "RMapDef" - road map, default
    "RMapRck" - UNUSED FOR NOW - road map, rocket jumping
    "RMapDng" - UNUSED FOR NOW - road map, dangerous (allowing dangerous nodes like ACID pits and LAVA - used when bots are stuck in such traps)
    "LinkJmp" - jump links
    "LinkDuk" - duck links
    "LinkUsr" - user links (manual node linking)

== CHUNKS ==

  Chunk:
    Each chunk starts with <chRLESize> bytes of RLE instructions and <chWRSize> bytes of WR instructions; then the chunk contains <chLength> bytes of useful data (may or may not be compressed). Finally, the chunk is padded with NULLs to make its size a multiple of 16.

  Always start by decompressing LB instructions, and then RLE instructions. To store, compress using RLE first, and then LB. You may also store information without compression (simply set both chRLESize and chWRSize to 0).

  RLE (RUN-LENGTH ENCODING) COMPRESSION
  This basic compression aims at grouping identical consecutive words (16-bit elements) together. It is very efficient with the roadmap, but mostly useless with other types of data. Each instruction is stored on a byte, the most significant bit (0x80) defines whether the same word is repeated multiple times (bit is set), or multiple words are written once (bit is unset). The remaining 7 bits (byte & 0x7F) describe the number of words that will be written to the destination buffer. The size of a word is ALWAYS two bytes (short int). By reading the whole set of instructions, it is possible to know how big the decompression buffer must be, and also validate the size of the source buffer.

  WR (WORD REPEAT) COMPRESSION
  An instruction can take one, two or three bytes of information. The two most significant bits of the first byte can be: 0x00 - read forward (6 bits), 0x01 - read backward immediate (6 bits), 0x10 - read backward (6 bits + 8 bits), 0x11 - read backward (6 bits + 16 bits). The remaining six bits count how many words of information will be written to the destination buffer (0 to 63). The following byte or unsigned integer defines how far behind we have to go back to find the words to copy. In immediate mode, we go back as many words as we need to write.

Node list:
Each node is stored on 16 bytes; simply divide the length of the decompressed stream by 16 to know how many nodes are stored (if there's a remainder, the chunk is invalid):
    2  UINT16   Type        Node type, offset in the string bank.
    2  UINT16   Entity      Classname of the linked entity, offset in the string bank.
    4  FLOAT    X           X coordinate
    4  FLOAT    Y           Y coordinate
    4  FLOAT    Z           Z coordinate

Navigation links (default, rocket jump, unsafe):
A buffer of (numNodes * numNodes) entries, each entry is a signed short integer. The decompressed stream must be numNodes * numNodes * 2 bytes long.

Jump links:
Each jump link is stored on 4 bytes:
    2  INT16    from        Starting node
    2  INT16    to          Ending node

Duck links:
Each duck link is stored on 4 bytes:
    2  INT16    from        Starting node
    2  INT16    to          Ending node

User links:
Each duck link is stored on 6 bytes:
    2  INT16    from        Starting node
    2  INT16    to          Ending node
    2  INT16    vehicle     Travel vehicle, offset in the string bank
