Skip to content

Instantly share code, notes, and snippets.

@liclac
Created May 27, 2014 17:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save liclac/e7b56db8e86006ba5c94 to your computer and use it in GitHub Desktop.
Save liclac/e7b56db8e86006ba5c94 to your computer and use it in GitHub Desktop.
SWP v1 Format Specification

SWP v1 Format Specification

SWP is a file format used to describe per-tag patches for an SWF file. It is designed to make it possible to apply patches in-stream, without having to keep more than a single tag of an SWF file and the SWP file's index in-memory at a time.

An SWP file is a container consisting of multiple patches. The word "container" here refers to the SWP file, "patch" to an individual entry within it, "SWF" to an SWF file (duh) and "tag" to a tag within said SWF (see the SWF spec). "Payload" is ambiguous, and should always be qualified as "tag payload" or "patch payload" in this context.


Everything is Little Endian. The SWF format uses little endian, and so does every platform on which Flash Player runs.

There must be no implicit padding inside structures. They are padded to 4-byte boundaries by design.

Note that an uint32_t is an unsigned, 32-bit (4 byte) integer, while an int32_t is signed. That u might save your life.

Structure

An container starts with a header, followed by a sequence of patch entries with pointers into the container payload. The container payload is the (compressed) content of every patch inside it, concatenated together. Only areas referred to by index pointers should be read.

A container's compression algorithm does not have to match what the target SWF uses. Tag- and patch payloads are both decompressed before use, and patch payloads are then recompressed in accordance with the target SWF.

File Header

struct swp_header
{
    char[3] magic_number;
    uint8_t compression;
    
    uint32_t count;
}

An SWP file always begins with a single file header, with patch entries immediately following it. There is no footer or anything like that.

magic_number
This is the "magic number" for the file. In the case of SWP v1, this is always SP1 in ASCII (0x53 0x50 0x31).
If the magic number doesn't match, you're not dealing with a valid SWP file.

compression
Determines the compression algorithm for patch payloads:

  • 0 - Uncompressed
  • 1 - zlib compression

Note that the specified compression only applies to patch payloads, not to any part of the index or header.

count
The number of patches in the file.

Patches

struct swp_patch
{
    uint32_t checksum;
    uint32_t offset;
    uint32_t size;
}

The index consists of swp_patch entries, repeated swp_header->count times.

checksum
CRC32 checksum of the uncompressed payload of the tag this patch applies to.

Patches can be considered keyed by their checksum, and no two patches may have the same one. This is so that patches can be put into a map-like structure for fast lookup.

offset
Absolute offset in the file of the patch payload. Specified from the very start of the file.

size
The size of the patch payload in the file. If the patch payload is compressed, this refers to the size of the compressed data, not what it expands to.

Usage

The intended method to apply an SWP file to an SWF file is:

  • Read the SWP file's index (into a map)
  • Read the SWF file's header
  • Loop through the SWF, tag-by-tag
    • Decompress the tag payload
    • Compute a CRC32 of it
    • Look up a matching patch, if there is one
      • Read and decompress the patch payload
      • Substitute tag payload with patch payload
      • Recompress it consistently with the SWF
      • Update the tag header with the new length
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment