======================================= === Mirco Machines v 3.0 === === Level file format === === PC Version === === === === by Richard Bradley === ======================================= Contents ======== 0. Numbers, endianness and other terms 1. Overall file structure, Chunks 2. Files 2.1 The cars.bin file 3. Chunks 3.1. The CARS chunk 3.2. OBJT chunks 3.3. PAGE Chunks 3.4. SAMP Chunks 3.5. PALE and EPAL Chunks 3.6. SHET Chunks 0. Numbers, endianness and other terms ====================================== All numbers are little-endian. I will use the following terms when referring to numbers: int, int32 : 4 byte signed integer short, int16 : 2 byte signed integer A "car" refers to any MMs vehicle, including boats and hovercraft. In the diagrams below, a box like this: +---+ | | <-- the vertical bars might be missing +---+ represents one byte; a box like this: +==============+ | | +==============+ represents a variable number of bytes. 1. Overall file structure, Chunks ================================= The files consist of a sequence of "Chunks", each of which begins with an 8 byte header, and continues with a "body" of 0 or more bytes, whose length is given by the "len" value in the header: 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+===========================+ | CHUNK TYPE | LEN | ... LEN bytes of body ... | +---+---+---+---+---+---+---+---+===========================+ The chunk type headers are specified as 4 ASCII characters, which serve as an abbreviation for the contents of the chunk. There is one special Chunk type, "DUPL", which doesn't follow this pattern. The DUPL chunks have a non-zero LEN field, but always have an empty body. Their meaning is currently unclear. 2. Files ======== 2.1 The cars.bin file ===================== The chunks in the file are arranged as follows: 1) A CARS chunk, serving as a lookup table for the cars 2) A sequence of 32 cars, each of which is made of: i) An OBJT chunk defining the detailed 3D model of the car ii) An OBJT chunk defining the simplified 3D model of the car iii) A PAGE chunk holding the detailed textures for Cherry, Jade, Dwayne and Chen iv) A PAGE chunk holding the detailed textures for Spider, Jethro, Walter and Bonnie v) A PAGE chunk holding the simplified textures for all 8 characters vi) Five SAMP chunks of unknown purpose (sound samples for horns?) 3) A PALE chunk containing the texture palette for all the cars 4) An OBJT chunk of zero length, as an EOF marker See later for descriptions of each of these Chunk types in detail. TODO: fill in details of other files (*.tbs, Levels/*/*[1-9].bin, etc) once their structure is understood. 3. Chunks ========= 3.1. The CARS chunk ================= There is only one CARS chunk, in the cars.bin file. It has a length of 256 bytes, and starts as follows: +-+-+-+-+-+-+-+-+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ |C|A|R|S| 256 | | carStart | carLength | | carStart | carLength | ... +-+-+-+-+-+-+-+-+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ The body contains 32 structs holding a pair of ints: the file offset to the start of a car, and the total length of all the Chunks which make up that car. These structs appear in the same order as the cars in the file. For example, the first car in the file (the wide buggy from Beached Buggies) starts at file offset 264, and runs for 295792 bytes. Note that, for each car struct, (carStart + carLength) is equal to the carStart of the next struct. 3.2. OBJT chunks ================ The OBJT chunks define textured 3D meshes. An OBJT chunk starts with a 40 byte header, as follows: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |O|B|J|T| LEN | nVTX | nFACE | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 0 | 0 | pVTX | pFACE | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ | 0 | 0 | +-+-+-+-+-+-+-+-+ where: nVTX (int) is the number of Vertex structures in the object nFace (int) is the number of Face structures in the object pVTX (int) is the offset to the start of the Vertex array. It is always 32 pFACE (int) is the offset to the start of the Face array. It is always (32 + 8 * nVTX) The two offsets are given relative to the start of the body (i.e. nVTX is at offset zero). The bulk of the header is redundant; only the nVTX and nFACE values are interesting. Next comes an array of Vertices: +-+-+-+-+-+-+-+-+ | x | y | z | 0 | ... repeated nVTX times +-+-+-+-+-+-+-+-+ Each of the coordinates is specified as a signed short integer. We will number the vertices from 0 to nVTX-1, in the order they appear in the file. After the vertices, an array of faces. Each face has a header and N+1 points: +-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ | L | N | | V | T | 0 | x | 0 | y | +-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+ ... \- repeated N+1 times --/ L is the total length of the face structure. N is the number of points listed. (Since each point is 12 bytes, L == ((N+1)*12 + 4) V is the vertex id of the position of the point in 3D space T incidates which texture to use for the face, when used on the first vertex by the following scheme: if T > 0x8000, then the (T-0x8000)th PAGE is used (counting from 0) [qq this is prob. defined in the IPOS chunk] if T < 0xff, then the face has a solid colour, given by the Tth entry in the palette otherwise, unknown. [qq this doesn't seem to be true] If T is non-zero for a vertex other than the first in the face, its meaning is unknown 0 is fixed at zero (except sometimes -- qq maybe related to transparent textures and/or textures from other files?) x is the x position of this point in texture space 0 is fixed at zero (except sometimes -- same qq as other "0") y is the y position of this point in texture space The last point in each face coincides with the first, and is (qq almost) always identical, except T is zero in the last point and non-zero in the first. The face structure represents a textured polygon in 3-space, in the obvious way. Things to note: - The faces are always either rectangles or triangles (hence, always convex) - The vertices are enumerated in an anti-clockwise direction as you look at the external side of the face - How to determine texture space is not completely known, but for the car models, it's simple (x,y) coords into the adjacent PAGE chunks, counting from the top left of the image in the standard way The face structure is repeated nFACE times, which fills the rest of the OBJT's body. 3.3. PAGE Chunks ================ These are simple indexed 8-bit bitmaps, using the palette from the PALE chunk, or sometimes from an EPAL chunk. They are always 256 bytes wide, and 256 bytes high, hence the body is always 65536 bytes long. 3.4. SAMP Chunks ================ These chnks hold raw waveform samples for short sound clips. The sample rate appears to be in the region of 8kHz. mplayer(1) can convert the raw data to a wav file with a command line like: mplayer -rawaudio rate=8000 -demuxer rawaudio -vo null -vc dummy -af \ resample=44100 -ao pcm:waveheader:file=out.wav - 3.5. PALE and EPAL Chunks ========================= These chunks have a fixed length of 1024, and consist of an array of 256 ints, each of which repesents a bit-packed RGB value as (the high byte of each int is always zero). 3.6. SHET Chunks ================ TODO loads of stuff to write up here! QQ intro para. Each level has a number of "Flats", which are large, mostly flat, surfaces, some of which are drivable, and some of which are only for display. All the drivable surfaces in each level seem to be represented by Flats, even those which are also OBJTs (they appear to have transparent Flats over the top of them, when they're drivable). The header is as follows: +-+-+-+-+-+-+-+-+=========+=========+ |S|H|E|T| LEN | str1 \0 | str2 \0 | +-+-+-+-+-+-+-+-+=========+=========+ +-+-+-+-+-+-+-+ | A | B |C| N | +-+-+-+-+-+-+-+ Each SHET chunk starts with two null-terminated strings holding 8.3 filenames, always "XXXobjs.def" and "XXXtims.def", where XXX is a short version of the level type (Breky, Pool etc). Some level types have two different versions of these headers, for example the Lab levels have "str1" equal to either "labobjs.def" or "lab3objs.def". A and B are int16 numbers, with A ranging from 1 to 2752 and B ranging from 500 to 900. C is a byte flag, taking values of either 0 or 1. The meanings of all three are currently unknwon. N indicates how many Flats are contained in the SHET chunk. After the header comes a series of N Flats, with headers like: +-+-+-+-+-+-+-+-+-+-+-+ | N | name\0 | +-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | X | Y | Z | 0 | rX| rY| rZ| 0 | W | H | xS| yS| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Where N is a 2-byte index number, starting from 201 or 301, and counting up (sometimes with some gaps) and "name" is an 8-letter null-terminated string naming the Flat. (X,Y,Z) are signed int16s indicating the origin of the top left corner of the flat in the level's coordinate space. (rX, rY. rZ) are signed int16s indicating a rotation, in a left-handed sense around their respective coordinate axes, in rotation units of 1/1024th of a right angle. The rotations must be applied in the order rZ, rY, rX. W and H are the width and height of the Flat, in texture blocks. xS and yS indicate the width and height of each texture block in the level's coordinate space. After that header comes a square array of (width*height) texture entries, arranged in columns, each in the form: +-+-+-+-+ | T | D | +-+-+-+-+ T is an unsigned int16 which references one of the images defined in the IPOS from the same file, treating the IPOS as a zero-based array of images. That image forms the texture for this square of the Flat. D is a signed int16 representing the depth of the top left corner of this square in the Flat. Normally this is zero. This may be independent of the collision properties of the Flat. After the texture array, there are a series of byte flags and headers, as: +-+-+-+-+-+-+-+-+-+ |A|0|B|0|C|0|D|0|E| +-+-+-+-+-+-+-+-+-+ Each of A,B,C,D and E are either 0 or 1. There are certain patterns: ABCDE Notes ------------- 00010 Remaning body is 6 bytes; all zero except occasionally first two. These are Flats which are for display only and aren't drivable. They do use the textures as defined. 00011 Similar to 00010. First two bytes are non-zero 01110 six zero bytes. Display-only. Uses own textures. (qq does this mean the bottom and right rows/cols are not visible?) 10000 Solid, doesn't use own textures. Variable length body. 10001 Solid, doesn't use own textures. Variable length body (lots of 14, 22..) lots of ramps, but not all are ramps Lots in this category 10010 Solid, uses own textures. Lots of floorshts, some drivable surfaces 11010 Solid, uses own textures, drivable. Not very many. guessed meanings: A Solid B Hide bottom row and right col (?) (why?) (If it is this, ignore for sidenum1) C D Visible (?) (If it is this, is ignored for bkftable) (works for placemt1!) E Ramp? --- uncertain --- if A is zerom, then there's almost always an array of asdfasdfjsadlsadd ----- If A is zero (qq sometimes), then there is an array of 8 byte structs, starting 5 bytes after the end of the flags section. These refer to squares on the flat. each such struct has entries 1234567 1 is the camera position for that square 2 some sort of isobars around the path of the course, and ranges 0-4 or so. This seems to be something to do with the restart position for when you die. 3 is an ascending counter for the position along the course. It's not sufficient to know this to know which corners can be cut 4 is the bump map for that square 5 follows closely the path of the course, except for odd bits 0 elsewhere. It seems to control the restart angle after death, or the offset in the tex square where you restart. 6 also follows the course. No idea 7 seems to always be zero 8 something to do with the corners? No immediately noticeable difference when zeroed out. At the end of all the Flats, there's some shared stuff: * There are 12 bytes of unknown * Then an int16 length header, followed by that many 8x8 byte-indexed bitmaps, with unknown meaning * Then an int16 length header, followed by that many 6 byte entries of "things" (qq) * Then an int16 length header, followed by that many 8x8 byte-indexed bitmaps, each of which is a "bump-map" for the behaviour of a tex square, referred to from entry 4 of the QQ array above * Then a load of zero bytes (1337 (!) on cheesey jumps) Things array ------------ The first one appears to control the camera angle on the placemats (twist about z, 1024=90deg) (distance) (elev angle) Indexed by entry 1 on the QQ array above Weapon types ------------ 0 Invalid (crash!) 1 Grabber 2 Ommer 3 Molotovs 4 The power to turn into some kind of white ball thing?! 5 Flamer 6 Invisibility 7 Mallet 8 Molotovs (again?) 9 Fire trail (odd icon) a Missiles b Mines c Group grabber d Omni ommer e Multi mallet f Group speed-up 10 Invalid (crash!) objt array and weapon array --------------------------- The object arracy is 4 bytes back from where SHETviewer currently has it Immediately following it is an int16 array count, then an array of weapons, each of 12 bytes, with x,y,z,0 and starting with weapon type as an int16 Bump types (not fully catalogued) --------------------------------- 0 Normal 1 Impassable 2 Milk (slippery, white trail) 3 Treacle (slow, sticky noise) 4 Ketchup (slow, red globby trails, goo noise) 5 Bumpy, like road border 6 as 5 7 water (splash, ripples, car dies) c no visible effect, observed in Breaky1 16 Jump + whizz noise 23 Dry sand f4 no visible effect, observed in Breaky1 3.7. ANIM chunks ================ These hold short animated sequences, much like an animated GIF file. They have a short header of the form: +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |A|N|I|M| LEN | unknown | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unknown | H | unk | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+ | pFrm1 | pFrm2 | ... | pFrmN | +-+-+-+-+-+-+-+-+ +-+-+-+-+ Where H is the height of each frame in the animation, and all the bytes marked as "unknown" or "unk" have an unknown meaning. I suspect that they control the frame delay for the animation. There does not appear to be a frame width or a frame count field in the header, however that info can be derived indirectly, as described below. The "pFrm" entries are pointers to frame structures, counting from location of pFrm1. Since the first frame starts immediately after the pFrm array, we can derive the length of the array by: FrameCount = pFrm1 / 4 For almost all ANIM chunks, the frames are of equal sizes, and placed sequentially in the file starting after the pFrm array. Hence the pFrm entries form an arithmetic sequence given by pFrm[N] = FrameCount * 4 + N * FrameLength (Here N is zero-based) Following the pFrm array is a list of frame structs, constructed as follows: +-+-+-+-+-+-+-+-+==========================+ | 0 | FL | ... FL bytes of body ... | +-+-+-+-+-+-+-+-+==========================+ The body is a byte-indexed bitmap, of width (FL / H) and height (H). The palette is taken from the PALE chunk in the same file. 3.8. IPOS chunks ================ IPOS chunks define images from subsections of the bitmaps found in the PAGE chunks in the same file. These definitions are used in SHET chunks (and possibly in OBJT chunks). The chunk body consists of LEN/4 image structs, each of the form: +-+-+-+-+-+-+-+-+ |P|?|?|?|x|y|w|h| +-+-+-+-+-+-+-+-+ Where: P is the index of the PAGE from which the image is drawn x,y,w,h is the x-offset, y-offset, width and height of the image within the PAGE image indicated by the value of P The entries marked with "?" have unknown meaning.