This document describes the basic format of the CC2M file format. The name "CC2M" is short for "Chip's Challenge 2 Map", and this is the native format used for CC2 levels. However, it is also used for the CC1 levels that accompany the CC2 release, and this is what is documented below. Please note that this document only covers those aspects of the file format as used by the CC1 ruleset; CC2-specific features are documented elsewhere.
Please also note that this is a work in progress. There are still several aspects of the CC2M file format that are unexplained below.
The file header is ten bytes in size, and consists of the following three fields:
|0-3||signature bytes: |
|4-7||signature value: 2|
|8-9||file subtype: a value between 0x33 and 0x37|
It is not fully known what the file subtype indicates. The assumption
is that it is a version number that allows the software to be
backwards-compatible with previous file versions. Note that the
observed values map to the ASCII digits
"7". It is
assumed that this is not coincidental, and the rest of this text will
refer to subtypes 3 through 7.
The file header is followed by a list of one or more blocks. These blocks comprise the file's contents.
Each block begins with an eight-byte header:
|0-3||block type, e.g. |
|4-7||size of block contents (not including this header)|
The block type signature is always four ASCII characters. Type names
which are only three characters long are space-padded, such as with
"KEY " and
The header is followed by the block contents, the details of which depend on the block type.
Many blocks consist of a single string. Strings are always terminated with a NUL byte. An empty string block is encoded as a single NUL byte.
The following list covers the details of each block type.
A string containing the level's name.
A string describing the level's provenance, or something similar. In the CC1 levels this block either names Chuck Sommerville, John K. Elion, or Alice Voith.
A string containing the hint text. Line breaks are marked with a CR LF pair (though the CC1 game will wrap text with long lines). Levels with no hints typically have a single line break as the complete text instead of an empty string (or just being omitted, which is what they original Lynx levels did).
A string typically providing the name of the level's author.
An optional string value, presumably with no explicit requirements as to its contents. Typical usage in CC1 is to name the person who recorded the accompanying solution. (Given the frequency of typos in the existing NOTE blocks, this data is not meant to be displayed to the player.) In CC2 it often contains extra hint texts.
A string value, presumably indicating a version number for the map. This block is not used with any CC1 levels, and only appears in a handful of CC2 levels.
A short string of bytes indicating various global settings. The first two bytes gives the level's time (with zero indicating no time limit). The third byte is always one in CC1. Levels of subtype 3, 4, and 5 always have only three bytes in this block. Levels of subtype 6 and 7 have up to 25 bytes. The next three bytes, if present, are all low integers, with zero being the most common (only two CC1 have a single one value). The remaining bytes, if present, show no obvious pattern.
This block defines the level's map and the placement of its initial contents. As the name suggests, the data in this block has been "packed", or compressed. Descriptions of both the data in this block and the unpacking algorithm are given below.
This block is always 16 bytes long. No obvious pattern is present in
the values. Its purpose is unknown. Perhaps it is a checksum of some
kind? Only one level in the CC1 set lacks a
KEY block; presumably this
is due to an oversight rather than that level being actually unique in
This block (whichever one is present) contains a solution replay.
Files of subtype 3 and 4 use
REPL blocks; file of the later subtypes
PRPL blocks. The replay data in each is the same, but the data
PRPL blocks has been packed. See below for descriptions of
the replay data and of the unpacking algorithm.
No content; the
END block always marks the end of the block list.
PACK blocks and the
PRPL blocks are "packed", or
compressed, using a simple scheme. The decompression algorithm works
The first two bytes of the packed data block give the full size of the data after unpacking.
The next byte specifies the number of unpacked bytes at the beginning of the data. This value cannot be greater than 127. (This value also cannot be zero, since packed bytes must be able to refer to bytes that appear earlier in the data stream.)
Unpacked bytes are copied to the output stream without modification.
After a sequence of unpacked bytes comes a sequence of repeated bytes — typically three or more bytes that matches a sequence appearing earlier in the stream. This repeated sequence is encoded by a pair of bytes in the packed data stream.
The first byte of the pair always has the high bit (0x80) set. The other seven bits specify the length of the repeated sequence. The second byte in the pair specifies how many bytes back from the current position that the sequence begins.
(Note that the two instances of the repeated sequence can actually overlap, in which case the bytes before the overlap are repeated throughout the overlapping region — much like using overlap in C's memcpy() function to lay down a repeating sequence. For example, the stream 0x41 0x42 0x88 0x02 will repeat the pair 0x41 0x42 for 10 bytes.)
If the byte immediately after the pair defining the repeated sequence has its high bit set, then it is assumed to be the start of another pair defining another repeated sequence. Otherwise, the third byte instead provides the number of unpacked bytes that follow the triplet.
If, on the other hand, the first byte of a repeated sequence specification does NOT have the high bit set, then it represents a null sequence, expanding to an empty byte string. There is no second or third byte in the specification in this case, and the one byte that is present provides the number of unpacked bytes that follow. This null-specification is used for when there is more than 127 consecutive unpacked bytes in the data stream.
This alternation between unpacked data and strings of repeated sequences continues until the end of the block is reached. The block will always end with an extra NUL byte, which the last encoded repeated sequence points to. (Note that this final NUL byte simply marks the end of the compressed data, and is not copied to the output stream.)
PACK block contains the data that (after being unpacked) defines
the level's map, including all of its contents and their initial
The map data begins with the first two bytes giving the width and height of the map respectively. The bytes following this pair define the contents of each cell of the map in reading order (left-to-right, top-to-bottom).
Most map cells contain a single object or feature, which is specified by a single byte. Some cell contents require a second byte to fully specify them, however. Also, some contents implicitly allow other objects to share the same cell, which appear in the data stream immediately following. The reader of the map data needs to know which values have these features in order to know when to advance to the next cell.
In the following table, the first column gives the byte value,
followed by the name of the object that value represents. If the third
column contains a
+, then that object takes a second byte that
further defines it (see below for details). The fourth column contains
* for objects that can coexist with another object, and so will
always be followed by at least one more byte. In the case when there
is nothing else in the cell, then the following byte will be 0x01 for
|04||ice corner NE||29||green key||
|05||ice corner SE||2A||ic chip (required)||
|06||ice corner SW||2B||ic chip (extra)||
|07||ice corner NW||2C||chip socket|
|0A||force floor north||2F||invisible wall, temporary|
|0B||force floor east||30||blue block wall|
|0C||force floor south||31||blue block floor|
|0D||force floor west||32||dirt|
|0E||toggle wall, closed||33||bug||
|0F||toggle wall, open||34||paramecium||
|1B||partition wall south||
|1C||partition wall east||
|1D||partition wall SE||
|22||red door||43||clone machine|
|23||blue door||45||hint button|
|24||yellow door||46||force floor all|
|25||green door||46||partition wall||
Most contents with an extra byte are creatures, and the second byte indicates the creature's orientation, i.e. which way the creature is initially facing. The orientation byte will be 0x00 for north, 0x01 for east, 0x02 for south, or 0x03 for west.
A different type of byte is used with 0x6D, a generic partition wall value. For this, the following byte indicates which edges have walls. The byte will have one or more bits set, where 0x01 indicates north, 0x02 east, 0x04 south, and 0x08 west. Note that this value is partly redundant with the content values 0x1B, 0x1C, and 0x1D. It is not known why this is. (It is also not know if the CC1 game will actually permit a partition wall to the north or west.)
All creatures permit a second set of contents to occupy the same map cell. (This is consistent with the original Lynx game, which actually stored creatures separately from the basic map data.)
The remaining contents that permit other contents are all objects that can be removed from the cell, i.e. keys, boots, chips, and bombs. (Not fake walls or doors, though.) In the original Lynx game these objects could only share a cell with a creature (usually a block). It's not clear if CC1 would actually handle such combinations as a key atop ice, or a bomb sharing a spot with a toggle wall, but the file format seems to permit such.
The file format also seems to permit an unlimited stacking of contents in a single cell, but presumably this is just for the sake of consistency. In practice the third set of contents in a map cell is always 0x01 for empty.
Note that no metadata is present in this block (other than the map size). This seems to indicate that not only are red and brown buttons implicitly connected via Lynx's reading-order rule, but that it is also impossible to change the ordering of the creature list to differ from the order implied by the map. (This is different from the original Lynx game, which could set an arbitrary monster order, though this possibility was not exercised in the original levels.)
This data contains a recording of a solution for the level. The data begins with a four-byte value. Its purpose is unknown. By far the most common value is all-bits zero. For levels with a nonzero value in this field, often only one of the four bytes is nonzero, so this "field" may actually be four separate byte values.
The remainder of the block is a stream of two-byte entries. The first byte value of an entry identifies the command being input. Possible base values are:
A horizontal and vertical value pair can also be bitwise-combined to indicate a "diagonal" move. The zero value "none" is used to record periods when no commands are selected.
The second byte value indicates how long the command is selected. (That is, how long the key is held down.) The time unit for this value appears to be 1/3 of a frame (or 1/6 of a tick), which would be equal to 1/60 of a second.
The maximum possible value for the time field appears to be 0xFC (equal to 42 ticks). If a key is held down for longer than this, then the entry will have a time value of 0xFC, the next entry in the stream will be the special value 0x80_0x00, and then this will be followed by a second entry with the same command value. Its time will be added to the 0xFC of the initial entry to give the full amount. Presumably, multiple fields could be chained together in this fashion to record a suitable long period of time.
(For many solutions, the stream is a sequence alternating between one key down and no keys down. For many other solutions, the stream alternates between entries for one key down and for two keys down, depending on the user's keying habits.)
The end of the stream is marked with at least one entry that contains the values 0x00 0xFF. Sometimes there is more than one such entry. There is often also a trailing 0x00 byte, but not always. Not sure why.