|
|
Line 1: |
Line 1: |
− | ELVL Format now superecedes the [[RGN Format]].
| + | apple cows |
− | | |
− | The latest version can always be found at http://sscx.net/asss/extended-lvl-format.txt
| |
− | | |
− | <pre>
| |
− | extended level file format
| |
− | version 0.4
| |
− | grelminar@yahoo.com
| |
− | ------------------------
| |
− | | |
− | 0. note
| |
− | | |
− | this document is a work in progress. it often speaks about things being
| |
− | implemented in asss. it's often wrong: implementation of the features
| |
− | described in this document is still ongoing, and many features described
| |
− | here are not yet implemented.
| |
− | | |
− | | |
− | 1. background
| |
− | | |
− | the basic level format, in use for many years, supports two main pieces
| |
− | of data: a bitmapped tileset for the map, and the tile data. the tileset
| |
− | bitmap is optional. the file layout is relatively simple: the bitmap
| |
− | comes first, if it's present, followed by tile data to the end of the
| |
− | file. if the bitmap signature isn't present, applications assume the
| |
− | whole file contains tile data.
| |
− | | |
− | to support advanced features that are already, or will soon be, possible
| |
− | in asss, i'd like to propose a backwards-compatible extension of the
| |
− | basic level format.
| |
− | | |
− | | |
− | 2. goals
| |
− | | |
− | the primary goal is to make it possible to embed more metadata in level
| |
− | files. this includes descriptive information about the creator of the
| |
− | file and its intended use, but also some functional information.
| |
− | | |
− | the most important, and complicated, type of metadata that i'd like to
| |
− | embed are regions. a region is just a set of tiles, usually but not
| |
− | necessarily contiguous, plus some information describing how the server
| |
− | (and maybe eventually the client) should treat that set of tiles.
| |
− | | |
− | | |
− | 3. file format
| |
− | | |
− | 3.1. embedding metadata in lvl files
| |
− | | |
− | currently, level files have one of two formats:
| |
− | | |
− | <bitmapfileheader>
| |
− | <bitmapinfoheader>
| |
− | <color table>
| |
− | <bitmap data>
| |
− | <tile data>
| |
− | | |
− | or just:
| |
− | | |
− | <tile data>
| |
− | | |
− | for now, i'm just going to talk about level files that contain tilesets.
| |
− | tileset-less files will be discussed later.
| |
− | | |
− | to preserve backwards compatibility with all the programs that read or
| |
− | write level files (subspace, continuum, ssme, ssled, facts, subgame,
| |
− | etc.), the beginning of the file will have to remain the same. the tile
| |
− | data will also have to remain the last item in the file, since all the
| |
− | level-file-reading code assumes that all the data from a specific point
| |
− | to the end of the file is tile data. thus, the new data will have to be
| |
− | inserted in the middle of the file, making it look like this:
| |
− | | |
− | <bitmapfileheader>
| |
− | <bitmapinfoheader>
| |
− | <color table>
| |
− | <bitmap data>
| |
− | <metadata/regions> <-- new stuff
| |
− | <tile data>
| |
− | | |
− | the trick that makes this work is the fact that the bitmapfileheader
| |
− | contains a field that specifies the total file size, which programs use
| |
− | to locate the start of the tile data. (at least i hope they do. if any
| |
− | program assumes the tile data directly follows the bitmap data, these
| |
− | extended files will break it.) another nice feature is that header also
| |
− | contains 4 bytes of reserved space, which we will use to point to the
| |
− | metadata section.
| |
− | | |
− | for reference, the windows BITMAPFILEHEADER struct looks like this:
| |
− | | |
− | struct BITMAPFILEHEADER {
| |
− | u16 bfType; // == 0x4D42 ("BM")
| |
− | u32 bfSize; // the total size of the file, which is also
| |
− | // the offset of the tile data
| |
− | u16 bfReserved1; // previously reserved, but we're going to
| |
− | // use this for our format
| |
− | u16 bfReserved2; // this one is still unused, should be 0
| |
− | u32 bfOffBits; // the offset to the bitmap data
| |
− | };
| |
− | | |
− | so, to make an extended level file, lay out your two bitmap headers,
| |
− | color table, and bitmap data as before, then the metadata section, and
| |
− | finally the tile data. the metadata section should be aligned to start
| |
− | at a multiple of 4 bytes from the start of the file, so you might need
| |
− | 0-3 bytes of padding between the end of the bitmap data and the start of
| |
− | the metadata. the metadata section will always be a multiple of 4 bytes
| |
− | long, so the tile data will also always start on a multiple of 4 bytes.
| |
− | | |
− | set the bfReserved1 field to the byte position of the start of the
| |
− | metadata section. typically, this will be 49720 bytes. also, the bfSize
| |
− | value, which was previously almost always 49718, will now point to the
| |
− | tile data, which will be located right after the extended metadata. so
| |
− | for a file containing 4000 bytes of metadata, bfSize will be 53720.
| |
− | | |
− | note that this means from the perspective of a program that only
| |
− | understands bmp files, or one that only understands plain lvl files, the
| |
− | metadata is part of the bitmap. it just happens that the bitmap contains
| |
− | more bits than is necessary for its dimensions and color depth.
| |
− | | |
− | to make an extended level file without a tileset, you simply start off
| |
− | the file with the metadata chunk, followed by the tile data. WARNING:
| |
− | currently no programs except asss understand extended level files
| |
− | without tilesets.
| |
− | | |
− | | |
− | 3.2. metadata format
| |
− | | |
− | now, the format of the metadata section itself. this is designed to be
| |
− | extensible, so new features can get added to lvl files without
| |
− | redefining the whole format. it's somewhat inspired by the png file
| |
− | format, which was inspired by iff, etc.
| |
− | | |
− | first, there's a header:
| |
− | | |
− | struct metadata_header {
| |
− | u32 magic; // == 0x6c766c65 ("elvl", for "extended lvl")
| |
− | u32 totalsize; // the number of bytes in the whole metadata section
| |
− | u32 reserved; // reserved, should be 0
| |
− | };
| |
− | | |
− | magic should always be equal to 0x6c766c65. if it isn't, the file is
| |
− | incorrect or corrupted. incidentally, the magic value is how extended
| |
− | level files without tilesets can be identified. (that is, an initial
| |
− | "BM" means an initial tileset, which may or may not contain extended
| |
− | data, an initial "elvl" means an extended level file without a tileset,
| |
− | and anything else is a plain level file without a tileset.) also note
| |
− | that the magic value is an illegal value for tile data.
| |
− | | |
− | totalsize is the number of bytes in the whole metadata section,
| |
− | including the header. note that the tile data will start at an offset of
| |
− | totalsize bytes from the start of the metadata_header.
| |
− | | |
− | directly following the header is a series of "chunks". there is no limit
| |
− | to the number of chunks. each chunk contains a chunk header:
| |
− | | |
− | struct chunk_header {
| |
− | u32 type; // describes what this chunk represents
| |
− | u32 size; // the number of bytes in the data portion of this
| |
− | // chunk, _not_ including the header.
| |
− | };
| |
− | | |
− | the header is followed by arbitrary data whose interpretation is
| |
− | determined by the chunk type. the data portion of a chunk may be of any
| |
− | length, including 0 bytes and odd numbers of bytes. if a chunk isn't a
| |
− | multiple of 4 bytes long, it should be padded up to a multiple of 4
| |
− | bytes so that the next chunk starts on a 4-byte boundary (but the
| |
− | padding isn't counted in the size field, it's just understood to be
| |
− | there).
| |
− | | |
− | an application can ignore chunk types that it doesn't know about. if
| |
− | it's an editor that's loading and saving elvl files, it should probably
| |
− | save the extra chunks and write them back to the file when it gets
| |
− | saved.
| |
− | | |
− | there are several chunk types defined so far:
| |
− | | |
− | "ATTR" - defines a textual attribute
| |
− | "REGN" - defines a region
| |
− | "TSET" - defines a tileset
| |
− | "TILE" - defines tile data
| |
− | | |
− | i'll describe their function and format one by one:
| |
− | | |
− | 3.2.1. "ATTR" chunks
| |
− | | |
− | these define miscelaneous textual attributes. the format is ascii text,
| |
− | not null terminated, in this form: "<key>=<value>". each "ATTR" chunk
| |
− | should contain just one key/value pair. multiple chunks of this type may
| |
− | be present in one file.
| |
− | | |
− | some keys that might be typically found in a level file are:
| |
− | | |
− | "NAME" - a descriptive name for this map
| |
− | "VERSION" - a version number for this map
| |
− | "ZONE" - the zone this file is intended to be used with
| |
− | "MAPCREATOR" - the person who created this map (the format of the value
| |
− | should be "name <email>")
| |
− | "TILESETCREATOR" - the person who created the tileset
| |
− | "PROGRAM" - the name of the program that was used to create this level
| |
− | file
| |
− | | |
− | 3.2.2. "REGN" chunks
| |
− | | |
− | these chunks define regions. to recap, a region is a set of tiles,
| |
− | usually but not always contiguous, with certain properties. asss
| |
− | understands regions and can implement some advanced features using them.
| |
− | currently continuum doesn't understand regions, but it would be nice if
| |
− | it did, and we'll be able to do even cooler things when it does.
| |
− | | |
− | there's a lot of stuff that you might want to say about a region, so to
| |
− | support all the varied uses, and also future uses, we'll use the chunk
| |
− | model again: each region gets its own set of "subchunks" describing its
| |
− | function. to avoid confusion, all sub-chunk types that go inside the
| |
− | "REGN" superchunk start with "r". the data of the big "REGN" chunk is
| |
− | simply a series of subchunks.
| |
− | | |
− | here are some defined sub-chunk types:
| |
− | | |
− | "rNAM" - a descriptive name for the region
| |
− | "rTIL" - tile data, the definition of the region
| |
− | "rBSE" - whether the region represents a base in a flag game
| |
− | "rNAW" - no antiwarp
| |
− | "rNWP" - no weapons
| |
− | "rNFL" - no flag drops
| |
− | "rAWP" - auto-warp
| |
− | "rPYC" - code to be executed when a player enters or leaves this region
| |
− | | |
− | 3.2.2.1. "rNAM" chunks
| |
− | | |
− | this is just a plain ascii string, not null terminated. every chunk
| |
− | should have exactly one of these.
| |
− | | |
− | 3.2.2.2. "rTIL" chunks
| |
− | | |
− | this subchunk describes the tiles that make up the region. it's stored
| |
− | in a compact rle-ish representation.
| |
− | | |
− | conceptually, a region is some subset of the 1024x1024 tiles in the map.
| |
− | to encode a region, encode it row by row, left to right, top to bottom.
| |
− | | |
− | for each row, split it into runs of empty tiles and present tiles. for
| |
− | each run, output one of these bit sequences:
| |
− | | |
− | 000nnnnn - n+1 (1-32) empty tiles in a row
| |
− | 001000nn nnnnnnnn - n+1 (1-1024) empty tiles in a row
| |
− | 010nnnnn - n+1 (1-32) present tiles in a row
| |
− | 011000nn nnnnnnnn - n+1 (1-1024) present tiles in a row
| |
− | | |
− | for the two-byte sequences, the bits in the second byte are the low
| |
− | bits, and the two bits at the end of the first byte are the high bits.
| |
− | | |
− | for example, if you have a run of 10 present tiles, you'd output the
| |
− | byte 01001001, or 73 decimal. if you have a run of 300 empty tiles,
| |
− | you'd output 00100001 00101011, or 33 43.
| |
− | | |
− | for each row, you must output a sequence of runs that sum to exactly
| |
− | 1024 tiles. anything else (missing tiles at the end of a row, or a run
| |
− | that extends past the end of the line) is an error.
| |
− | | |
− | there are two exceptions to the above rule: first, rows that contain no
| |
− | tiles at all can (optionally) be encoded specially:
| |
− | | |
− | 100nnnnn - n+1 (1-32) rows of all empty
| |
− | 101000nn nnnnnnnn - n+1 (1-1024) rows of all empty
| |
− | | |
− | second, if the same pattern of tiles appears in more than one
| |
− | consecutive row, you can use these special codes to save more space:
| |
− | | |
− | 110nnnnn - repeat last row n+1 (1-32) times
| |
− | 111000nn nnnnnnnn - repeat last row n+1 (1-1024) times
| |
− | | |
− | the rTIL chunk must describe exactly 1024 rows. this might require only
| |
− | two bytes (163 255 for the totally empty region), or it might require
| |
− | much more.
| |
− | | |
− | note: several bit patterns don't correspond to any of the above codings.
| |
− | that's deliberate. don't use them.
| |
− | | |
− | 3.2.2.3. "rBSE" chunks
| |
− | | |
− | this is a 0-byte chunk. its presence signifies that this region should
| |
− | be considered a "base".
| |
− | | |
− | 3.2.2.4. "rNAW" chunks
| |
− | | |
− | this is a 0-byte chunk. if present, antiwarp should be disabled for
| |
− | ships in this region. currently, this disabling happens on the server,
| |
− | and players whose antiwarp is being disabled aren't necessarily aware of
| |
− | it. it would be nice if in the future continuum understood this feature
| |
− | so that it could inform the player that antiwarp is unavailable in this
| |
− | location.
| |
− | | |
− | 3.2.2.5. "rNWP" chunks
| |
− | | |
− | this is a 0-byte chunk. if present, all weapons are non-functional in
| |
− | this region. the same notes apply to this feature as to the no antiwarp
| |
− | feature.
| |
− | | |
− | 3.2.2.6. "rNFL" chunks
| |
− | | |
− | this is a 0-byte chunk. if present, any flags dropped in this region are
| |
− | respawned as neutral flags in the center of the map (or wherever the
| |
− | settings indicate they should be spawned).
| |
− | | |
− | 3.2.2.7. "rAWP" chunks
| |
− | | |
− | this chunk, if present, turns on the auto-warping feature. any player
| |
− | entering this region will be immediately warped to the specified
| |
− | destination.
| |
− | | |
− | the data for this chunk specifies the destination, in this format:
| |
− | | |
− | struct autowarp_destination {
| |
− | i16 x; // the destination x coordinate, 1-1023, 0 for the
| |
− | // default, or -1 for the player's current x coordinate.
| |
− | i16 y; // the destination y coordinate.
| |
− | char arena[16]; // the destination arena name. null if the warp
| |
− | // does not cross arenas.
| |
− | };
| |
− | | |
− | as a simple space optimization, if the warp does not cross arenas, you
| |
− | can leave out the 16 bytes of arena name, so that the whole chunk data
| |
− | is 4 bytes long.
| |
− | | |
− | 3.2.2.8. "rPYC" chunks
| |
− | | |
− | this chunk lets you embed code in level files. the exact semantics of
| |
− | this data haven't yet been determined, but it'll probably go something
| |
− | like this:
| |
− | | |
− | this chunk should contain ascii data representing some python code. the
| |
− | code will be executed when the map is loaded, and it may define several
| |
− | functions: a function named "enter", if it exists, will be call each
| |
− | time a player enters this region. it will be called with one argument,
| |
− | which is the player who entered the region. a function named "exit"
| |
− | works similarly, except of course it gets called when someone leaves.
| |
− | | |
− | | |
− | 3.2.3. "TSET" and "TILE" chunks
| |
− | | |
− | these chunk types are intented to be used only in the far future, when
| |
− | all lvl files use the extended format, and the backwards-compatible
| |
− | format described so far doesn't have to be used. such files will start
| |
− | with the "elvl" metadata header described above, and they will contain
| |
− | the tileset bitmap and tile data as chunks.
| |
− | | |
− | the format of a "TSET" chunk is a windows format bitmap, _without_ the
| |
− | bitmapfileheader stuff (because its function is taken care of by the
| |
− | chunk header). that is, it starts with a bitmapinfoheader, which is
| |
− | followed directly by the color table, which is followed directly by the
| |
− | bitmap data. the bitmap data should be in 8-bit format with no
| |
− | compression.
| |
− | | |
− | the format of a "TILE" chunk is just tile data, in the same format it's
| |
− | always been in:
| |
− | | |
− | struct tile {
| |
− | unsigned x : 12; // range 0-1023, upper two bits should be 0
| |
− | unsigned y : 12;
| |
− | unsigned tile : 8;
| |
− | };
| |
− | | |
− | there should be no more than one of each of these chunks in a file. one
| |
− | file should not contain both an ETST and a TSET chunk. if a file
| |
− | contains neither of those two types, the default tileset should be used.
| |
− | | |
− | | |
− | 4. comments
| |
− | | |
− | if you have an suggestions for improving the file format or this
| |
− | document, email them to grelminar@yahoo.com, or post them on the forums
| |
− | at http://forums.minegoboom.com/
| |
− | | |
− | | |
− | 5. history
| |
− | | |
− | version 0.3:
| |
− | - removed ETST chunks, since cont basically supports this
| |
− | functionality already through tiles.bm2
| |
− | | |
− | version 0.2:
| |
− | - better format for region tile data
| |
− | | |
− | version 0.1:
| |
− | - initial version
| |
− | </pre>
| |
− | | |
− | [[Category: Formats]]
| |
− | [[Category: Map]]
| |