Posted by Loshadh on May 04, 1998 at 20:43:39: Well, here's what I've got so far. This should get some people going in the right direction for extraction of sprite data. :) Note that this post is not necessarily for the layperson, unfortunately. It is written more with the C coder in mind. The sheer amount of data contained within a single .256 tag for a sprite is amazing (610 K for the ghol sprite data). Hand-hexing something of this magnitude is not recommended. I hope that some of the structure information doesn't confuse you guys, as it's abou't the only way I could represent things. Some of the arrays are dynamically allocated, based on values read in from the sprite data (not all sprites have the same number of rows and columns within them, etc...). The format in which the sprite data is stored is very, very, -VERY- ugly, and I can only hope that my description of its structure (and the notes that follow it) shed sufficient light on the matter for people to be able to duplicate my extraction work. There are still many unknowns within all this data. Anyone who can figure out more of this from what I've posted, by all means make that info known. I am taking a break from fiddling with myth data structures and whatnot for a while. And now, without further ado, the .256 tag for a sprite, revealed (somewhat). ;) The .256 header for a sprite is as follows (A majority of this info prior to the actual sprite data directly follows HeavenŐs .256 data structure. Thanks, Heaven!): ALL OFFSETS ARE FROM THE END OF THE .256 HEADER UNLESS OTHERWISE SPECIFIED IN THE COMMENTS. struct .256Header { long unk01; long type; // 00 00 00 06 for sprites long unk03; long unk04; char unk05[48]; long paletteEntries; // number of palettes (usually 1) long paletteOffset; // offset to the palette data long paletteLength; // length in bytes of the palette // data (usually 2080) long unk06; long swapColors; // number of swapColor entries long swapColorOffset; // offset to the swapColor data long swapColorLength; // length of the swapColor data long unk10; long sprites; // number of individual images in // the .256 data long spriteOffset; // offset to the sprite table long spriteLength; // length of sprite table info long unk11; long bitmapDescriptors; // number of bitmap descriptors long bitmapDesctiptorOffset;// offset to the bitmap descriptors long bitmapDescriptorLength;// length of the bitmap descriptor // data long unk12; long bitmapGroups; // number of bitmap groups long bitmapGroupOffset; // offset to the bitmap group info long bitmapGroupLength; // length of the bitmap group info long unk13; long godKnowsWhat; long godKnowsWhatOffset; long godKnowsWhatLength; long unk14; long unk15[20]; // these seem unused for sprites long unk16; long unk17; long headerLength; long unk18; char unk19[64]; }; // 320 bytes After the header comes the palette. struct .256Palette { long colors; // number of colors in the palette long unk01[7]; // filler? struct RGBAInfo { int redValue; int greenValue; int blueValue; int alphaValue; } palValues[colors]; // 8 bytes per palette entry. }; // usually 2080 bytes (8 * 256 colors + 32 byte 'header') After the palette comes the swapColor data. This seems to be the place where the palette colors that can be changed by a team's primary and secondary colors are stored. It consists of a series of swapColor entries, each 128 bytes long. struct .256SwapColorEntry { char regionName[32]; // Body region where color change // takes place char unk01[32]; char unk02[64]; // I have no idea. This might be // a list of palette values where // the colors are swapped... }; // 128 bytes After the swapColor entries comes the bitmap descriptor listings. I really have no data on this section. Sometimes there is a bit of text intermingled within the entries here. Each entry appears to be 64 bytes long. struct bitmapDescriptorEntry { char unk01[64]; // well, that was easy, wasn't it. }; // 64 bytes Following the bitmapDescriptor entries are the bitmapGroup entries. These entries appear to contain information regarding how the bitmapped sprites are grouped together. For example, for the ghol sprites, there are the following bitmapGroup entries: Idle walking attack arm ax head leg sack torso flinch portrait pick up throw soft death soft corpse idle #2 idle transition celebration sack (full) holding for attack Aside from this small bit, I do not know anything about this section currently. struct bitmapGroupEntry { char bitmapGroupName[32]; char unk01[32]; char unk02[64]; }; The GodKnowsWhat entries are all 32 bytes long. I do not know their purpose. Sometimes there are bits of text interspersed amongst the numbers. struct godKnowsWhatEntry { char GKWNameLength; // how many bytes the text occupies char GKWName[GKWNameLength]; // name of the GodKnowsWhat entry char filler[??]; // this fills out whatever the // GKWName does not use in the // first 16 bytes char unk01[16]; // No idea what this data does }; Now we get to some important stuff... the sprite entries. Each of these entries contains the location for a given sprite. struct spriteEntry { char spriteName[32]; // name of the sprite long unk01[8]; long spriteOffset; // offset to the sprite data long spriteLength; // length of the sprite data long unk02; int spriteWidth; // width of the sprite in pixels int spriteHeight; // height of the sprite in pixels char unk03[48]; // filler? }; //128 bytes The sprite data itself is fairly complex. Here is the structure as I have gleaned it so far. struct spriteData { int width; // width of the sprite in pixels int height; // height of the sprite in pixels int unk01; int unk02; int unk03; char unk04[38]; long rowInfo[height]; // see notes [1] struct rowEntry { int segments; int totalPixels; struct segmentEntry { int segmentStart; int segmentEnd; } segData[segments]; // see notes [2] char pixel[totalPixels] char filler[??] // see notes [3] } rowData[height]; } NOTES: [1] This rowInfo section seems to serve as a memory allocation table, listing the starting points of each row of data within some memory space. The differences between rowInfo(i+1) and rowInfo(i) equals the total number of bytes in rowData(i). [2] This is why I consider the sprite format to be very, very ugly. Instead of just defining a bounding box around the image and filling unused sections with a transparent color, they've decided to just store the regions of the sprite where there is actually data. For example, take the following line of data (the leftmost value is considered position 00): 00 04 04 05 00 02 02 02 01 00 Let the value of 00 be "transparent". Instead of just writing out the entire line, Bungie stores this data as follows: segments: 2 (two unbroken regions of nontransparent pixels) totalPixels: 7 (seven nontransparent pixels on the line) segment 1: segmentStart: 01 (segment 1 starts in position 01 of the line) segmentEnd: 03 (segment 1 ends in position 03 of the line) segment 2: segmentStart: 05 segmentEnd: 08 pixel: 04 04 05 02 02 02 01 filler: ?? (see note [3]) Thus, for that single line of data listed above, the data is stored like this: 00 02 00 07 00 01 00 03 00 05 00 08 04 04 05 02 02 02 01 ?? So we have what would otherwise be a ten-byte long piece of data taking up twenty bytes. When there are a lot of unbroken blocks of pixels on a single line, or when there are a lot of transparent pixels on a line, Bungie's method does save space (kinda like a FAX machine in the way it handles the row data). [3] This filler is either zero or one byte long. Apparently, the pixel data can only be read in in groups of two bytes. If there is an odd number of pixels on a line, a "filler" pixel is tacked onto the end of the line so that the data can be read in properly. Go figure. Well, that's what I've got so far. I'll try to work up a good example of the way the data is stored, rather than what I've rattled out directly above in note [2]. It might make more sense to take this text and step your way through the ghol data, just to see what I'm talking about. The offset to the beginning of the ghol .256 tag is 195110304 (decimal), or BA125A0 (hex). There was a point where the code I had written to extract the ghol sprites simply crashed. I think this is where the ghol's portrait image is located within the .256 tag. I am not sure how that image is represented; it might follow a different format than that of the sprites. In any case, the code I had written simply freaked out at that point. I was wrong in my earlier statements regarding the number of ghol sprites... I had put the number somewhere around 108; it's actually 255 (counting all the body part sprites and the portrait). Loshadh