The GSkinAnimation (GSA) file format
Last updated: 2002-09-22, current with gsmhacking 3.1.47.
Animations in SWAT3 are stored in GSkinAnimation format, a proprietary format created by Sierra. No documentation or source code for the .gsa file format is availble.
All actions performed by characters (and objects) in the game are controlled by animations. This is easy enough to see in the case of, say, a character walking or running through the map. It is also true for a character standing stationary, pointing his weapon at a target. Here, the character is performing an animation with only one frame but an animation nonetheless.
The .gsa file format is quite simple. Leaving aside the magic, it consists of no more than a skeleton definition (which is identical to that in a .gsm file), a list of bones that are moved or rotated by the animation and then a bunch of frames. For each frame, the file lists each bone's position and orientation. Frames may also contain Events, which I will discuss in a separate section.
And that's it. If an animation has five frames and moves seven bones there are thirty-five bone positions in the file. Nothing more complicated than that.
Sections of the .gsa file
The .gsa file is composed of five sections:
- The .gsa file header
- The framerate (and other stuff)
- Skeleton and bones information
- The animated bone list
- Frame information
Each frame is described in three sections:
If you haven't studied the .gsm file format
Please refer to the .gsm documentation for details on conventions I'll use in this document.
The sections in detail
The .gsa file header
|Description||Size||Comments or example|
|Magic||16 octets||GSA magic is 0xCF 0xD0 0x09 0xA8 0x2A 0x1F 0xD2 0x11 0x87 0x49 0x00 0x20 0xAF 0xE6 0x36 0xEE 0x00 0x20 0xAF 0xE6 0x36 0xEE|
|GSA exporter version||char (minor) char (major)||Always 0x01 0x04 (version 4.1) or 0x02 0x04 (version 4.2)|
There are two types of GSA files in the wild. The earlier (exporter version 4.1) files do not contain event information, whereas the newer (exporter version 4.2, the one available for download) do. Both versions are happily accepted by SWAT but gsmhacking needs to work with the newer format files in order to complete certain operations. Luckily, gsmhacking is capable of converting old files into the new format.
The framerate (and other stuff)
|Framerate||short||In frames per second|
The framerate is actually irrelevant as SWAT always plays the animation at 10 fps. Indeed the GSA exporter won't let you alter this value when you save a .gsa file. As to the two unknown values, they don't appear to be very important whatever they are. gsmhacking is able to process animations without worrying too much about these numbers.
The skeleton and bone descriptions in GSA files are exactly the same as in GSM files. Please refer to the relevant section of the GSM documentation for more details.
In fact, SWAT doesn't pay any attention at all to the skeleton data. It would appear that the bone descriptions are used only to define the number of bones that are available to be animated. Either that or Sierra originally wrote one plugin that could export both GSM and GSA files and then split them into two without removing the redundant skeleton data from the .gsa format.
Animated bones list
Although SWAT pays no attention to the skeleton structure at the start of the .gsa file, you do need to provide a valid skeleton for the character class that will use your animation. So if you want to make an animation for a 52-bone SWT character, for example, you must define 52 bones in your animation. However, you may not always want to animate all those bones. Take the weapon aiming animations, for instance. Only the upper body is relevant to those animations; the character's legs stay still. To reduce the .gsa file size, the animated bones list enables you to list a set of bones that the animation will affect, leaving the others untouched.
|End of bone stream||short||This marker has the value 0xFF 0xFF; it always follows the skeleton data|
|Number of animated bones||short|
|Bone ID||short||Bone ID of the first bone to be animated|
In the special case where the number of animated bones is set to zero, no more values follow (ie there are no bone IDs listed) and all bones are assumed to be animated.
|Number of frames||short|
Each frame looks like this:
|Frame origin X co-ordinate||float||Frame's offset relative to position in first frame|
|Frame origin Y co-ordinate||float||Frame's offset relative to position in first frame|
|Frame origin Z co-ordinate||float||Frame's offset relative to position in first frame|
|Frame origin rotation quaternion X component||float||Frame's orientation relative to first frame|
|Frame origin rotation quaternion Y component||float||Frame's orientation relative to first frame|
|Frame origin rotation quaternion Z component||float||Frame's orientation relative to first frame|
|Frame origin rotation quaternion scalar component||float||Frame's orientation relative to first frame|
|Root bone X co-ordinate||float||Root bone's offset from its start position in the first frame|
|Root bone Y co-ordinate||float||Root bone's offset from its start position in the first frame|
|Root bone Z co-ordinate||float||Root bone's offset from its start position in the first frame|
|Bone position||See below|
|Number of events||short||Events are only present in version 4.2 animations|
The frame position and orientation are doubtless designed to allow for the creation of new co-ordinate systems when performing the animation. However, the is fixed at the origin for all frames in all GSAs in the wild. Another set of complicated maths we don't have to worry about.
Each bone listed in the animated bones list (or "all bones" when the list is empty) has its rotation for the current frame described next.
|Rotation quaternion X component||float|
|Rotation quaternion Y component||float|
|Rotation quaternion Z component||float|
|Rotation quaternion scalar component||float|
Finally, any events in the frame are listed:
|Number of events||short|
Each event looks like this:
|Length of event name||short||Including trailing \0 terminator|
|Event name||See above||Including trailing \0 character (eg <name> 0x00)|
What "events" are
Events are very cool and very powerful but what are they? And why are they called Events anyway?
Simply put, events allow you to trigger certain actions from an animation file. What this amounts to in real terms is the ability to play sounds at certain points in the animation. Six types of event have been observed:
To cut a long story short, no-one really knows what the first four are all about, although the dialogue from Sierra's animation event editor suggests that they have something to do with walking. The sound=XXX and itemsound=XXX types are understood, however.
When a frame containing a sound=XXX event is played, the sound XXX from sounds.dat (or modsounds.dat or missionsounds.dat) is triggered. For example, BG1_Secure.GSA contains the event sound=cuff on frame 12. This calls the cuff sound at that frame. The result is that cuffing a BG1 character (thus calling the animation) plays the handcuffing sound.
An itemsound=XXX event is similar, only sounds.dat is searched for a sound whose name is composed of the character's current inventory item and XXX. If that sounds confusing, consider the example of SWT_Handgun_Reload.GSA - the secondary weapon reloading animation. Frame 11 lists the event itemsound=magout and frame 19 has the event itemsound=magin. If the character playing the animation is holding a 1911 (guns.dat name "1911"), sounds.dat is searched for the sounds magout_1911 and magin_1911 respectively. If he had the HK SOCOM Mk23 suppressed (guns.dat name "mk23_s"), the sounds played would be magout_mk23_s and magin_mk23_s. You can see that using an itemsound event in this way enables you to tailor sounds to weapons (or tactical aids) in animations.
As to the question of why events are called events, it's because the GSA exporter for 3D Studio MAX looks for an object called EventObject at a specific frame when writing the event data. We needed a name for these triggers and "event" (from EventObject) just stuck.
Send any comments to firstname.lastname@example.org.