Tiberian Sun Voxel Animation Format =================================== Version 1.02 24th May, 2000 Eol (mosikos@online.no) DMZ (dmz@freeuk.com) =================================== Table of Contents ================= 1 - Introduction 2 - File Structure 2.1 - Header 2.2 - Limb Header 2.3 - Limb Body 2.4 - Limb Tailer 3 - Legal 1 - Introduction ================ This document describes the VXL format for storing voxel (volume pixels) models for the game Tiberian Sun by Westwood Studios. The format was first partially cracked by me during September 1999, but as I lost interest in Tiberian Sun, I stopped doing anything with it. However, during May 2000 Dave (DMZ) decided to have a look at the voxels and decided to give me a call (well, actually, he sent me a mail :-o). So we went up on the TS Editing Mailing List (http://www.egroups.com/group/ts-editing/) and finally cracked the format. Notably, he cracked more than me, and if he tries to claim otherwise, he's doing two things: 1) Being modest :-) 2) Lying :-o Some aspects of the format are still unknown to us: 1) We do not know how the multilimbed and multifile voxels are glued together into one unit. 2) We do not know how the voxels are being shaded. (We do know where the shading info is stored, though.) 3) We do not know why Westwood decided something as sluggish as floating points in the tailer, but it does strengthen our belief that Westwood might want to optimize Tiberian Twilight instead of just cutting features one week before the release :-o) If you find out any info concerning the above points, or find our document useful in anyway, we'd love to hear about it :-) 2 - File Structure ================== A voxel animation file consists of 4 distinct sections, a main header and three related body section types, of which there may be more than one of each. "n" is the number of voxel limbs in the voxel file. struct vxl { vxl_header header; vxl_limb_header limb_header[n]; vxl_limb_body limb_body[n]; vxl_limb_tailer limb_tailer[n]; }; Each like numbered limb header, body and tailer belong to the same limb. (So the 2nd limb header will be part of the same limb as the 2nd limb body and the 2nd limb tailer etc...) The order of the limbs appears to not be guarenteed (so, theoretically limb_header[1] can be part of the same limb as limb_body[2]), although all voxel files in Tiberian Sun and Firestorm follow the incremental pattern. In practise this makes little difference as far as the limb bodies are concerned, since they cannot be accessed sequentially, only by reference. Limb headers have limb number data contained within them, which may refer to which corresponding limb tailer to use. In all available examples these numbers merely increment. All numbers within the file are stored in little endian (ie least significant byte first) format. In the following descriptions, treat the structures presented as pseudocode, because that's what they are :-) 2.1 - Header ============ The header is fixed length, 802 bytes, and contains a few fixed size records and a colour palette. struct vxl_header { char filetype[16]; /* ASCIIZ string - "Voxel Animation" */ long unknown; /* Always 1 - number of animation frames? */ long n_limbs; /* Number of limb headers/bodies/tailers */ long n_limbs2; /* Always the same as n_limbs */ long bodysize; /* Total size in bytes of all limb bodies */ short int unknown2; /* Always 0x1f10 - ID or end of header code? */ char palette[256][3]; /* 256 colour palette for the voxel in RGB format */ }; Conjecture regarding the unknown fields - the first may be the number of animation frames (a feature that was cut?) in the file, and the second either an ID marker or an end of header beginning of palette data marker. The number of limbs fields hold the number of limbs contained within the file. e.g. if the value is 1, then there is 1 limb header, followed by 1 limb body, then 1 limb tailer. Note that there are two identical fields holding the number of limbs. It is possible that one holds the number of limb headers, and the other the number of limb tailers, but no example exists where these numbers differ. Finally, the body size gives the combined size of all body sections. This can be used as an offset from the end of the final limb header to find where the tailer sections begin. 2.2 - Limb Header ================= The limb header is an array consisting of fixed-size records. Each record is 28 bytes. struct vxl_limb_header { char limb_name[16]; /* ASCIIZ string - name of section */ long limb_number; /* Limb number */ long unknown; /* Always 1 */ long unknown2; /* Always 0 */ }; Spurious data usually follows the zero terminator in the limb name. This can be safely ignored, since it is a fixed width field. Obviously the buffer space used to write out the limb name wasn't cleared when creating the files. 2.3 - Limb Body =============== The limb body is variable length, and is divided into three sections - the span start index, the span end index and the span data. "n" is the number of spans (vertical columns), which you can calculate by multiplying the x and y dimensions in the tailer. struct vxl_limb_body { long span_start[n]; /* List of span start addresses or -1 */ long span_end[n]; /* List of span end addresses or -1 */ char span_data[]; /* Byte data for each span length */ }; Below is a diagram of how the voxel span data is stored. Some spans contain null-voxel sections - these are present so that the span always equals the z. Basically, that means you can read each span both ways. +------------------+ ¦ SKIP COUNT ¦ 1 byte +------------------¦ ¦ NUM. VOXELS ¦ 1 byte +------------------¦ ¦ VOXEL 1 COLOUR ¦ each 1 byte; palette colour index +------------------¦ ¦ VOXEL 1 NORMAL ¦ each 1 byte +------------------¦ ¦ VOXEL 2 COLOUR ¦ +------------------¦ ¦ VOXEL 2 NORMAL ¦ +------------------¦ ... +------------------¦ ¦ VOXEL n COLOUR ¦ +------------------¦ ¦ VOXEL n NORMAL ¦ +------------------¦ ¦ NUM. VOXELS ¦ 1 byte; same value as previous declaration +------------------+ +------------------+ ¦ SKIP COUNT ¦ etc. +------------------¦ ... 2.4 - Limb Tailer ================= The tailer is an array consisting of fixed-size records. Each record is 92 bytes long. struct vxl_section_tailer { long span_start_off; /* Offset into body section to span start list */ long span_end_off; /* Offset into body section to span end list */ long span_data_off; /* Offset into body section to span data */ float transform[4][4]; /* Inverse(?) transformation matrix */ float scale[3]; /* Scaling vector for the image */ char xsize; /* Width of the voxel limb */ char ysize; /* Breadth of the voxel limb */ char zsize; /* Height of the voxel limb */ char unknown; /* Always 2 - unknown */ }; The transformation matrix is quite wierd. Dave says that the values can be treated like this, but he's a bit unsure...: Inverse transformation: x-offset n/a n/a n/a y-offset n/a n/a n/a z-offset n/a n/a n/a n/a n/a n/a n/a