Westwood WSA Format
Large animations or animated pictures in Westwood Studios games are typically saved as .WSA files. The extension stands for "Westwood Studios Animation".
WSA is a straightforward implementation of Westwood's XOR Delta compression. All frame information is saved as byte commands resembling code-based RLE that apply XOR operations on the data in the graphics buffer to transform it into the next frame in the sequence. These batches of byte commands are then compressed with Westwood's LCW compression.
This file format has remained in use in more or less the same form over a long range of games. Because of this, many different versions exist, with subtle changes between each. On this page, these versions will be named after the game / game version they are most known for. These versions are:
- Dune II v1.00
- Dune II v1.07
- Command & Conquer
The easiest way to distinguish between these versions is simply to try reading the header in each of the formats and checking whether the width and height are not zero, and if the end offset value that matches the file size can be found where it is expected.
The format starts with the following header. The shown information matches the header in the Command & Conquer version. Differences with other versions are noted in the list.
|UINT16LE||NrOfFrames||Number of frames.|
|UINT16LE||XPos||X-offset of the frame data. This field does not appear in the Dune II versions of the format.|
|UINT16LE||YPos||Y-offset of the frame data. This field does not appear in the Dune II versions of the format.|
|UINT16LE||Width||Width of the frames.|
|UINT16LE||Height||Height of the frames.|
|UINT16LE||DeltaBufferSize||Size of the buffer required to unpack the frame data. Through some development quirk, this value is always 37 bytes smaller than the actual required buffer size. This field is a UINT32LE in the Monopoly version of the format.|
|UINT16LE||Flags||Extra load options. The only available option is HasPalette, which is bit 1. This field does not appear in the Dune II v1.00 version of the format, meaning that version can never have an embedded palette.|
|UINT32LE[NrOfFrames+2]||FrameOffsets||Addresses of the frame offsets. The addresses are relative to the start of the file, but do not take the palette into account, meaning that if the HasPalette flag is enabled, 768 bytes need to be added to these offsets to find the actual data.|
|BYTE||Palette||A 256-colour 6-bit RGB VGA palette. Only occurs if the HasPalette flag is enabled. This is an 8-bit RGB VGA palette in the Monopoly version of the format.|
Following this header is the actual frames data.
Missing initial state
The very first entry in FrameOffsets can be set to 0. In this case, the WSA file has no initial state, and is supposed to build on the final frame of a previously-played WSA file. This format is commonly used in Dune II, with several chained files with consecutive alphabetic suffixes.
For example, to correctly load INTRO8C.WSA, the last frame of INTRO8B.WSA needs to be used. However, since that one is also lacking its initial state, the chain needs to be followed back to INTRO8A.WSA to get a valid start. The final frame of INTRO8A.WSA can then serve as initial state for INTRO8B.WSA, the final frame of which can then be used to correctly load INTRO8C.WSA.
Two final entries
As you see, the table has two more entries than the amount of frames. These two can be handled in two ways:
- If the final entry is zero, then there is no special behaviour. The entry before it will contain the end address, behind the data of the final frame. This address (plus the palette size) should match the end of the file.
- If the final entry is not zero, then that entry will contain the data end. In that case, the entry before that contains the offset to the XOR data for an extra 'loop frame'. This loop frame data will transform the final frame back into the first one, so looping animations can avoid the much heavier operation of clearing the graphics buffer and applying a full-image XOR Delta operation on it.
Some games contain files that don't properly end their LCW data with an end marker. To ensure the data is handled well, use the offset of the next entry as data end when decompression the LCW data. Note that the LCW compression used in the Monopoly WSA files supports commands with relative offsets to support larger compressed files. The use of this relative system is indicated by an extra byte 0x00 at the start of the LCW data.
After the LCW decompression, the XOR Delta data needs to be be applied to the previously-loaded frame. If the file has an initial state, this initial state is also normal XOR Delta data, which should be applied to a cleared buffer containing only 0x00-bytes.
The following tools are able to work with files in this format.
|Name||Platform||View images in this format?||Convert/export to another file/format?||Import from another file/format?||Access hidden data?||Edit metadata?||Notes|
|Engie File Converter||Windows||Yes||Yes||Yes||No||N/A||Can chain the missing initial states from previous files. Supports an additional unofficial flag 0x02 on the Monopoly type that indicates that its palette is saved as 6-bit rather than 8-bit, a feature requested by the Chronoshift (formerly RedAlert++) team.|
|XCC Mixer||Windows||Yes||Yes||No||No||N/A||Only supports the Dune II v1.07 and C&C types. Does not support file chaining.|
|Mix Manager||DOS||Yes||Yes||No||No||N/A||Only supports the C&C type. Fails to identify some files, like the ones from the game's setup program.|
|RAMIX||DOS||Yes||Yes||No||No||N/A||Only supports the C&C type. Has corruption in some files from the C&C1 setup program.|
|Red Horizon Utilities||Java (command line)||No||Yes||Yes||No||N/A||Original site is defunct. Backups of the tools can be found here. This implementation may have problems from misinterpreting the DeltaBufferSize as frame rate.|