GAF File Format Document version 1.0 This is a description of the GAF file format used by TA to store all kinds of graphic elements, including animations, static pictures, user interface elements, etc. Credits: This document builds on the original by Saruman and Bobban. In addition, I got much helpful info from Bizmut, Kinboat, and Manu. I even figured a couple of things out myself, but the people listed above did most of it. Warning: This is intended for use by people that already know what they're doing. I'm a C programmer, so I'm doing things in C notation here, but I'll try to explain it so that those of you that don't speak C will be able to understand. If you don't understand, write me at joed@cws.org and I'll try to clear things up. I'm also a big believer in examples, so I'll be walking you through a GAF file (Archipelago.GAF) as I explain. The first part of the file is the header, which looks like this: typedef struct _GAFHEADER { long IDVersion; /* Version stamp - always 0x00010100 */ long Entries; /* Number of items contained in this file */ long Unknown1; /* Always 0 */ } GAFHEADER; Let's look at a sample header: 00000000 00 01 01 00 39 00 00 00 00 00 00 00 IDVersion is 0x00010100 like we expect. Entries is 0x39, indicating that there are 57 items contained in this file. Immediately following the header is a list of pointers, one for each entry. The list of pointers here looks like: 00000000 68 EA 04 00 00000010 98 EA 04 00 C8 EA 04 00 F8 EA 04 00 78 EB 04 00 00000020 A0 EB 04 00 C0 ED 04 00 40 EE 04 00 68 EE 04 00 00000030 98 EE 04 00 C8 EE 04 00 F8 EE 04 00 78 EF 04 00 00000040 A8 EF 04 00 C8 F1 04 00 48 F2 04 00 78 F2 04 00 00000050 A8 F2 04 00 D8 F2 04 00 08 F3 04 00 88 F3 04 00 00000060 A8 F5 04 00 28 F6 04 00 58 F6 04 00 88 F6 04 00 00000070 B8 F6 04 00 E8 F6 04 00 18 F7 04 00 B8 F7 04 00 00000080 E8 F7 04 00 20 FB 04 00 C0 FB 04 00 F0 FB 04 00 00000090 20 FC 04 00 50 FC 04 00 80 FC 04 00 20 FD 04 00 000000A0 68 00 05 00 08 01 05 00 38 01 05 00 68 01 05 00 000000B0 98 01 05 00 C8 01 05 00 68 02 05 00 98 02 05 00 000000C0 F0 05 05 00 90 06 05 00 C0 06 05 00 F0 06 05 00 000000D0 68 07 05 00 28 08 05 00 58 08 05 00 88 08 05 00 000000E0 B8 08 05 00 E8 08 05 00 18 09 05 00 48 09 05 00 The next byte after the pointer list is at offset F0. Remember this. Each pointer points to a structure that looks like this: typedef struct _GAFENTRY { short Frames; /* Number of frames in this entry */ short Unknown1; /* Unknown - always 1 */ long Unknown2; /* Unknown - always 0 */ char Name[32]; /* Name of the entry */ } GAFENTRY; The first pointer is directs us to location 0x04EA68. Going there, we find: 0004EA60 01 00 01 00 00 00 00 00 t............... 0004EA70 46 72 6F 6E 64 30 31 00 00 00 00 00 00 00 00 00 Frond01......... 0004EA80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ This entry has 1 frame, and is called 'Frond01'. Following each entry is another list of structures, one for each frame. typedef struct _GAFFRAMEENTRY { long PtrFrameTable; /* Pointer to frame data */ long Unknown1; /* Unknown - varies */ } GAFFRAMEENTRY; The frame entry looks like this: 0004EA90 78 D5 03 00 02 00 00 00 PtrFrameTable is 0x03D578. This points us to a structure containing data about the first frame in this entry. It looks like this: typedef struct _GAFFRAMEDATA { short Width; /* Width of the frame */ short Height; /* Height of the frame */ short XPos; /* X offset */ short YPos; /* Y offset */ char Unknown1; /* Unknown - always 9 */ char Compressed; /* Compression flag */ short FramePointers; /* Count of subframes */ long Unknown2; /* Unknown - always 0 */ long PtrFrameData; /* Pointer to pixels or subframes */ long Unknown3; /* Unknown - value varies */ } GAFFRAMEDATA; Here's the data: 0003D570 31 00 1F 00 15 00 0F 00 0003D580 09 01 00 00 00 00 00 00 F0 00 00 00 00 00 00 00 Width and height are (duh) the width and height of the frame. This frame is 0x31 by 0x1F pixels (49 x 31). XPos and YPos are actually offsets that give the displacement of the frame from the entry's actual position on the map. So if the entry itself was placed at position 100,100, the frame would be at position 100-XPos,100-YPos. These offset can be negative. Here, they place the frame at 21 pixels left, and 15 pixels above the initial placement of the item on the map. Unknown1 is always 9. No idea what it really means. Compressed is the compression flag. If it's 0, the image is not compressed. This image is compressed. More on this in a bit. FramePointers. This is where it gets a little weird. If FramePointers is 0, then PtrFrameData points to pixel data. If it isn't, then PtrFrameData points to a list of that many more pointers to GAFFRAMEDATA structures. Each of these subframes is collectively treated as one frame. More in this in a bit. Unknown2 is always 0. PtrFrameData points to the pixel data or to more GAFFRAMEDATA structures, depending on the value of FramePointers. Here it's pointing to offset 0xF0. If you remember, this is the first byte after the list of entry pointers way back at the start of the file. Unknown3 is a mystery. Sometimes the value is 0. Sometimes it isn't. No idea what it means or how to calculate it. Ok. Now we have this frame entry. Since FramePointers is 0, PtrFrameData points to pixel data. If the frame were not compressed, it'd just be the raw pixels, 31 chunks of 49 bytes each, corresponding to each line. This frame is compressed, so things are a little different. Let's look at the data: 000000F0 07 00 29 00 44 17 00 45 21 0E 00 1B 00 44 17 04 00000100 44 B3 07 00 45 09 00 44 1B 10 00 1B 00 A3 0D 00 00000110 44 07 00 A2 03 00 A2 0F 00 44 1D 16 00 13 00 45 00000120 07 00 44 03 00 45 13 00 44 03 00 45 09 00 45 03 00000130 00 A3 1D 1F 00 15 00 45 07 04 44 A3 07 00 45 03 00000140 00 44 03 00 44 03 00 A3 03 00 45 03 00 45 03 08 00000150 44 B3 A2 1F 1D 00 09 04 45 44 0B 04 44 45 07 08 00000160 44 A3 44 03 00 A2 03 04 A2 B3 05 00 45 05 08 44 00000170 A3 B3 23 25 00 0D 00 45 03 04 B3 44 05 00 45 07 00000180 08 A3 44 B3 03 04 44 5B 05 06 45 05 00 44 03 04 00000190 A2 45 09 04 44 45 07 00 44 0F 2B 00 0F 00 A2 03 000001A0 00 45 03 00 45 03 00 44 03 00 45 03 04 B4 35 03 000001B0 00 B3 05 08 45 B3 44 03 04 A2 46 03 08 B4 44 A2 000001C0 05 00 44 0D 00 44 0D 2B 00 09 00 45 05 34 45 44 000001D0 A3 45 A2 44 B4 45 45 B3 36 45 45 35 03 0C A2 46 000001E0 A3 45 03 04 45 46 03 00 A2 03 04 B3 45 03 00 45 000001F0 0B 00 44 0F 28 00 0B 04 45 A2 05 38 B2 A3 B4 A2 00000200 A2 B4 45 A2 45 45 A3 46 44 46 44 03 14 A2 46 46 00000210 44 B3 37 03 00 35 03 04 A2 45 05 06 45 13 2B 00 00000220 04 A3 44 03 00 44 07 34 B4 A2 44 45 46 45 46 A2 00000230 A2 46 A2 B4 46 A2 03 04 B3 44 03 24 B2 A2 44 A2 00000240 36 A2 45 B4 A2 46 03 04 46 44 17 2A 00 09 08 44 00000250 A3 44 05 08 A3 45 36 03 60 A2 45 B2 45 45 A2 A2 00000260 B4 44 A3 35 45 A3 44 B4 A2 46 B2 45 A2 A2 B2 46 00000270 44 A3 07 04 A2 45 0D 29 00 0B 10 44 45 45 B3 45 00000280 03 60 46 44 45 46 A2 45 B3 45 B2 35 A3 44 B3 A3 00000290 B4 A1 46 45 45 A2 46 A2 B4 45 45 03 0C 46 44 A2 000002A0 45 11 2F 00 00 44 03 0C 44 A3 45 B3 03 04 45 35 000002B0 03 00 44 03 0C 45 B4 45 B4 03 58 46 A2 45 B3 44 000002C0 A3 44 45 A2 46 44 45 45 46 44 44 45 A2 44 45 B4 000002D0 A2 46 13 28 00 00 A3 05 00 45 07 00 46 03 00 45 000002E0 03 04 45 A3 0E 45 48 A2 46 45 A2 46 B4 A2 45 46 000002F0 B3 46 45 46 46 45 A2 A2 B4 A3 0A 45 15 31 00 0B 00000300 20 A2 45 45 35 B3 44 46 45 45 03 06 45 00 46 03 00000310 5C A2 46 44 A2 45 B3 45 45 46 36 B3 A1 A3 44 B2 00000320 A2 45 B4 A2 A2 B3 45 44 45 03 04 A3 44 05 00 44 00000330 2F 00 07 00 45 03 04 45 A3 03 14 B3 36 A3 B2 45 00000340 45 03 70 45 A3 B3 36 45 46 B3 A2 44 A3 45 A3 44 00000350 46 35 B4 45 B3 35 46 A2 36 45 B4 36 A2 A3 B2 45 00000360 0B 2C 00 09 00 45 07 0C A3 45 45 36 03 06 45 20 00000370 B4 45 A2 5B 46 36 45 B4 45 03 24 46 44 46 B3 A2 00000380 45 45 A3 A1 B4 05 0C 46 A2 B4 5A 07 00 45 09 2A 00000390 00 07 00 45 03 04 44 45 03 3C 45 B3 A2 45 45 38 000003A0 A2 45 44 45 45 46 45 45 A2 45 03 28 37 B4 45 A2 000003B0 46 44 46 45 46 45 45 07 00 45 13 26 00 09 28 A3 000003C0 45 B3 A2 46 45 A2 B2 A3 44 38 03 04 B3 45 03 00 000003D0 45 03 00 45 09 18 45 46 A1 36 B4 A2 46 05 08 B3 000003E0 45 45 17 27 00 0F 00 46 0A 45 08 A3 45 46 03 06 000003F0 5B 00 A3 03 0C 5B 45 45 37 05 04 45 A2 03 04 45 00000400 B4 03 04 45 A2 07 00 45 05 00 45 13 24 00 09 06 00000410 45 14 46 44 A3 44 B3 45 03 00 38 05 00 5A 05 04 00000420 44 A3 07 14 46 45 B2 35 45 A2 05 04 44 45 0D 06 00000430 45 0F 23 00 07 00 45 09 04 B3 37 03 00 45 05 00 00000440 36 03 00 44 03 04 45 B3 05 20 45 44 A2 46 44 A3 00000450 B3 44 45 0B 00 44 19 1B 00 0D 0C 45 A2 5A 37 0B 00000460 04 44 A3 03 04 46 A3 03 08 35 B3 45 07 04 B3 45 00000470 0F 00 A3 19 1C 00 0B 04 44 A2 07 00 46 07 08 44 00000480 46 44 07 06 45 03 00 45 03 00 45 03 00 35 03 00 00000490 A2 27 15 00 09 00 45 11 08 44 45 A3 09 00 44 05 000004A0 00 A2 0B 00 46 0F 00 44 17 0B 00 1B 00 A3 11 04 000004B0 B3 44 0D 00 44 25 08 00 19 04 B3 44 0B 00 44 3B 000004C0 07 00 19 00 36 0D 00 45 3B 07 00 27 00 A2 05 00 000004D0 A2 35 04 00 2B 00 44 37 PtrFrameData points to a short integer that is a count of bytes for the first line. Skip ahead that many bytes, and you get to a count for the second line, etc, etc. The first line is 7 bytes long, and consists of 29 00 44 17 00 45 21. The Height parameter tells you how many lines there are, in this case, 31. Broken into lines, minus the length data, we get: Line 0 29 00 44 17 00 45 21 Line 1 1B 00 44 17 04 44 B3 07 00 45 09 00 44 1B Line 2 1B 00 A3 0D 00 44 07 00 A2 03 00 A2 0F 00 44 1D Line 3 13 00 45 07 00 44 03 00 45 13 00 44 03 00 45 09 00 45 03 00 A3 1D Line 4 15 00 45 07 04 44 A3 07 00 45 03 00 44 03 00 44 03 00 A3 03 00 45 03 00 45 03 08 44 B3 A2 1F Line 5 09 04 45 44 0B 04 44 45 07 08 44 A3 44 03 00 A2 03 04 A2 B3 05 00 45 05 08 44 A3 B3 23 Line 6 0D 00 45 03 04 B3 44 05 00 45 07 08 A3 44 B3 03 04 44 5B 05 06 45 05 00 44 03 04 A2 45 09 04 44 45 07 00 44 0F Line 7 0F 00 A2 03 00 45 03 00 45 03 00 44 03 00 45 03 04 B4 35 03 00 B3 05 08 45 B3 44 03 04 A2 46 03 08 B4 44 A2 05 00 44 0D 00 44 0D Line 8 09 00 45 05 34 45 44 A3 45 A2 44 B4 45 45 B3 36 45 45 35 03 0C A2 46 A3 45 03 04 45 46 03 00 A2 03 04 B3 45 03 00 45 0B 00 44 0F Line 9 0B 04 45 A2 05 38 B2 A3 B4 A2 A2 B4 45 A2 45 45 A3 46 44 46 44 03 14 A2 46 46 44 B3 37 03 00 35 03 04 A2 45 05 06 45 13 Line 10 04 A3 44 03 00 44 07 34 B4 A2 44 45 46 45 46 A2 A2 46 A2 B4 46 A2 03 04 B3 44 03 24 B2 A2 44 A2 36 A2 45 B4 A2 46 03 04 46 44 17 Line 11 09 08 44 A3 44 05 08 A3 45 36 03 60 A2 45 B2 45 45 A2 A2 B4 44 A3 35 45 A3 44 B4 A2 46 B2 45 A2 A2 B2 46 44 A3 07 04 A2 45 0D Line 12 0B 10 44 45 45 B3 45 03 60 46 44 45 46 A2 45 B3 45 B2 35 A3 44 B3 A3 B4 A1 46 45 45 A2 46 A2 B4 45 45 03 0C 46 44 A2 45 11 Line 13 00 44 03 0C 44 A3 45 B3 03 04 45 35 03 00 44 03 0C 45 B4 45 B4 03 58 46 A2 45 B3 44 A3 44 45 A2 46 44 45 45 46 44 44 45 A2 44 45 B4 A2 46 13 Line 14 00 A3 05 00 45 07 00 46 03 00 45 03 04 45 A3 0E 45 48 A2 46 45 A2 46 B4 A2 45 46 B3 46 45 46 46 45 A2 A2 B4 A3 0A 45 15 Line 15 0B 20 A2 45 45 35 B3 44 46 45 45 03 06 45 00 46 03 5C A2 46 44 A2 45 B3 45 45 46 36 B3 A1 A3 44 B2 A2 45 B4 A2 A2 B3 45 44 45 03 04 A3 44 05 00 44 Line 16 07 00 45 03 04 45 A3 03 14 B3 36 A3 B2 45 45 03 70 45 A3 B3 36 45 46 B3 A2 44 A3 45 A3 44 46 35 B4 45 B3 35 46 A2 36 45 B4 36 A2 A3 B2 45 0B Line 17 09 00 45 07 0C A3 45 45 36 03 06 45 20 B4 45 A2 5B 46 36 45 B4 45 03 24 46 44 46 B3 A2 45 45 A3 A1 B4 05 0C 46 A2 B4 5A 07 00 45 09 Line 18 07 00 45 03 04 44 45 03 3C 45 B3 A2 45 45 38 A2 45 44 45 45 46 45 45 A2 45 03 28 37 B4 45 A2 46 44 46 45 46 45 45 07 00 45 13 Line 19 09 28 A3 45 B3 A2 46 45 A2 B2 A3 44 38 03 04 B3 45 03 00 45 03 00 45 09 18 45 46 A1 36 B4 A2 46 05 08 B3 45 45 17 Line 20 0F 00 46 0A 45 08 A3 45 46 03 06 5B 00 A3 03 0C 5B 45 45 37 05 04 45 A2 03 04 45 B4 03 04 45 A2 07 00 45 05 00 45 13 Line 21 09 06 45 14 46 44 A3 44 B3 45 03 00 38 05 00 5A 05 04 44 A3 07 14 46 45 B2 35 45 A2 05 04 44 45 0D 06 45 0F Line 22 07 00 45 09 04 B3 37 03 00 45 05 00 36 03 00 44 03 04 45 B3 05 20 45 44 A2 46 44 A3 B3 44 45 0B 00 44 19 Line 23 0D 0C 45 A2 5A 37 0B 04 44 A3 03 04 46 A3 03 08 35 B3 45 07 04 B3 45 0F 00 A3 19 Line 24 0B 04 44 A2 07 00 46 07 08 44 46 44 07 06 45 03 00 45 03 00 45 03 00 35 03 00 A2 27 Line 25 09 00 45 11 08 44 45 A3 09 00 44 05 00 A2 0B 00 46 0F 00 44 17 Line 26 1B 00 A3 11 04 B3 44 0D 00 44 25 Line 27 19 04 B3 44 0B 00 44 3B Line 28 19 00 36 0D 00 45 3B Line 29 27 00 A2 05 00 A2 35 Line 30 2B 00 44 37 To decode the line, to the following: 1. Read a byte. This is a mask. 2. If (mask & 0x01) = 0x01 skip ahead (mask >> 1) pixels. This is transparency, allowing whatever was under the frame to show through. else if (mask & 0x02) = 0x02 copy the next byte (mask >> 2)+1 times to output. else copy the next (mask & 0x02)+1 bytes to output. 3. go back to 1, until there are no more bytes left in the line. A C code fragment to do this is: char *data; // points to pixel data for (y = 0; y < FrameData.Height; y++) { bytes = *((short *) data); data += sizeof(short); count = 0; x = 0; while (count < bytes) { mask = (unsigned char) data[count++]; if ((mask & 0x01) == 0x01) { // transparent x += (mask >> 1); else if ((mask & 0x02) == 0x02) { // repeat next byte repeat = (mask >> 2) + 1; while (repeat--) putpixel(x++, y, data[count]); count++; } else { repeat = (mask >> 2) + 1; while (repeat--) putpixel(x++, y, data[count++]); } } data += bytes; // point to next line } We do this to the above mess of data, and we get: Line 0 44 45 * Line 1 44 44 B3 45 44 * Line 2 A3 44 A2 A2 44 * Line 3 45 44 45 44 45 45 A3 * Line 4 45 44 A3 45 44 44 A3 45 45 44 B3 A2 * Line 5 45 44 44 45 44 A3 44 A2 A2 B3 45 44 A3 B3 * Line 6 45 B3 44 45 A3 44 B3 44 5B 45 45 44 A2 45 44 45 44 * Line 7 A2 45 45 44 45 B4 35 B3 45 B3 44 A2 46 B4 44 A2 44 44 * Line 8 45 45 44 A3 45 A2 44 B4 45 45 B3 36 45 45 35 A2 46 A3 45 45 46 A2 B3 45 45 44 * Line 9 45 A2 B2 A3 B4 A2 A2 B4 45 A2 45 45 A3 46 44 46 44 A2 46 46 44 B3 37 35 A2 45 45 45 * Line 10 A3 44 44 B4 A2 44 45 46 45 46 A2 A2 46 A2 B4 46 A2 B3 44 B2 A2 44 A2 36 A2 45 B4 A2 46 46 44 * Line 11 44 A3 44 A3 45 36 A2 45 B2 45 45 A2 A2 B4 44 A3 35 45 A3 44 B4 A2 46 B2 45 A2 A2 B2 46 44 A3 A2 45 * Line 12 44 45 45 B3 45 46 44 45 46 A2 45 B3 45 B2 35 A3 44 B3 A3 B4 A1 46 45 45 A2 46 A2 B4 45 45 46 44 A2 45 * Line 13 44 44 A3 45 B3 45 35 44 45 B4 45 B4 46 A2 45 B3 44 A3 44 45 A2 46 44 45 45 46 44 44 45 A2 44 45 B4 A2 46 * Line 14 A3 45 46 45 45 A3 45 45 45 45 A2 46 45 A2 46 B4 A2 45 46 B3 46 45 46 46 45 A2 A2 B4 A3 45 45 45 * Line 15 A2 45 45 35 B3 44 46 45 45 45 45 46 A2 46 44 A2 45 B3 45 45 46 36 B3 A1 A3 44 B2 A2 45 B4 A2 A2 B3 45 44 45 A3 44 44* Line 16 45 45 A3 B3 36 A3 B2 45 45 45 A3 B3 36 45 46 B3 A2 44 A3 45 A3 44 46 35 B4 45 B3 35 46 A2 36 45 B4 36 A2 A3 B2 45 * Line 17 45 A3 45 45 36 45 45 B4 45 A2 5B 46 36 45 B4 45 46 44 46 B3 A2 45 45 A3 A1 B4 46 A2 B4 5A 45 * Line 18 45 44 45 45 B3 A2 45 45 38 A2 45 44 45 45 46 45 45 A2 45 37 B4 45 A2 46 44 46 45 46 45 45 45 * Line 19 A3 45 B3 A2 46 45 A2 B2 A3 44 38 B3 45 45 45 45 46 A1 36 B4 A2 46 B3 45 45 * Line 20 46 45 45 45 A3 45 46 5B 5B A3 5B 45 45 37 45 A2 45 B4 45 A2 45 45 * Line 21 45 45 46 44 A3 44 B3 45 38 5A 44 A3 46 45 B2 35 45 A2 44 45 45 45 * Line 22 45 B3 37 45 36 44 45 B3 45 44 A2 46 44 A3 B3 44 45 44 * Line 23 45 A2 5A 37 44 A3 46 A3 35 B3 45 B3 45 A3 * Line 24 44 A2 46 44 46 44 45 45 45 45 35 A2 * Line 25 45 44 45 A3 44 A2 46 44 * Line 26 A3 B3 44 44 * Line 27 B3 44 44 * Line 28 36 45 * Line 29 A2 A2 * Line 30 44 * This is essentially a big green splat, used to represent a patch of reclaimable foliage. This entry is very simple. One frame. No subframes. If there were multiple frames, you'd display each frame in sequence. If the FramePointers member were not 0, then instead of pixels, PtrFrameData would point to a list of pointers that had that many entries. Each pointer would point to another GAFFRAMEDATA entry. When you are assembling that frame, you would paint all the subframes in order, and treat the whole thing as one single frame for animation purposes. I think I've covered everything. If you have any questions, let me know. Joe D joed@cws.org