{"name":"PAA File Format","title":"PAA File Format","type":"topic","url":"https://community.bistudio.com/wiki/PAA_File_Format","revid":373446,"updated":"2025-05-08T11:27:12Z","aliases":["PAA"],"markdown":"> **Disclaimer: This page describes internal undocumented structures of Bohemia Interactive software.**\n>\n> This page contains unofficial information.\n>\n> Some usage of this information may constitute a violation of the rights of Bohemia Interactive and is in no way endorsed or recommended by Bohemia Interactive.\n> Bohemia Interactive is not willing to tolerate use of such tools if it contravenes any general licenses granted to end users of this community wiki or BI products.\n\nOf the many image file formats out there, such as jpeg, or gif, Bohemia Interactive choose to use a specially developed file format (paa) as the base texture file for all engine types.\n\nThe reason for this is the raw data within the file contain mipmaps which be passed directly to Microsoft's DirectX as a DXT1 picture (e.g) without further processing.\n\nAll engines except Elite also support JPG files (but they have no transparency). Most of encoded mipmap formats in pax files do.\n\n#### PAC files\n\n`.pac` files are (almost) synonomous with `.paa`.\nIn the origins of Operation Flashpoint, `.paa` were meant to be two tone colors (black & white or grayscale) and `.pac` = colour.\n\nThe distinction does not exist. All engines treat pac or paa equally. They are referred to as paX files in the rest of this document.\n\nNote however that Arma 2's TGAviewer *cannot* read a palette index `.paa` extension. only `.pac` extensions (the contents can be identical).\n\n#### OFP Demo Files\n\nThe initial 'proof of concept' for Operation Flashpoint released in 1997, was a demonstration of an island with models and textures.\n\nThese pax files can still be read by tgaviewer! (and pal2pace)\n\nThe *only* difference between these, and subsequent pax files is that the index pallet appears at beginning of file. In other words\n\n- There is NO header type, Pallet index is assumed.\n- There are NO taggs.\n- Other than this, demo files are 100% identical to OFP's Pallet index including the use of run length compression.\n\n#### Main Format\n\nOverall structure of a pax file is\n\n```cpp\nstruct overall\n{\n    ushort        TypeOfPaX;    // OPTIONAL\n    Tagg        Taggs[...];    // OPTIONAL\n    Palette        Palette[...];\n    MipMap        MipMaps[...];\n    ushort        Always0;\n};\n```\n\n### TypeOfPaX (optional)\n\nWith the sole exception of OFP index palettes and demo, all paX files begin with a 2 byte 'type' signature.\nOFP index palettes have *no* type, they begin with tagg structures immediately. demo files have neither, and begin with the index pallet.\n\n| PaX Type | Game |  |  |  |  |  |\n|---|---|---|---|---|---|---|\n| PaX Type | OFP<br>Demo | Operation Flashpoint v1.00 | Armed Assault v1.00 | Arma 2 v1.00 | Oxygen 2 |  |\n| 0xFF01 DXT1 | ✗ | ✓ | ✓ | ✓ | ✓ |  |\n| 0xFF02 DXT2 | ✗ | ✗ | ✗ | ✗ | ✓ |  |\n| 0xFF03 DXT3 | Unknown |  |  |  |  |  |\n| 0xFF04 DXT4 | ✗ | ✗ | ✗ | ✗ | ✓ |  |\n| 0xFF05 DXT5 | ✗ | ✗ | ✓ | ✓ | ✗ |  |\n| 0x4444 RGBA 4:4:4:4 | ✗ | ✓ | ✗ | ✓ | ✗ |  |\n| 0x1555 RGBA 5:5:5:1 | ✗ | ✗ | ✓ | ✓ | ✗ |  |\n| 0x8888 RGBA 8:8:8:8 | ✗ | ✗ | ✗ | ✗ | ✓ |  |\n| 0x8080 GRAY w Alpha | ✗ | ✓ | ✓ | ✓ | ✓ |  |\n\n'**Index Pallets only occur in Operation Flashpoint and demo** Later versions of TGAViewer still 'understand' them IF and only IF they have .pac extensions. Under this circumstance they are translated as ARGB8888 (and saved as such)\n\nNote that information on DXT formats can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression.\n\n### Taggs\n\nTaggs do not exist for demo pax files.\n\n```cpp\nTagg\n{\n    char    signature[4];    // always \"GGAT\"\n    char    name[4];        // name of the tagg in reversed order (for example: \"CGVA\")\n    ulong    dataLen;\n    byte    data[dataLen];\n}\n```\n\nTaggs are read until no \"GGAT\" signature is encountered. Palette structure follows.\n\n#### General\n\nStructurally, all TAGG's are optional and can occur multiple times. In practice:\n\n- At least one TAGG exists for ALL types, including index palettes.\n- Any TAGG if it occurs, only occurs, once.\n- TAGG order of presentation is\n\n```\nAVG        // always first\nMAX        // always 2nd etc...\nFLAG\nSWIZ or PROC (not seen together)\nOFFS    // always last\n```\n\n- ALL TAGG's begin with an AVG with the sole exception of *some* GREY .paX's\n- MAX is almost follows an AVG\n- FLAG, PROC, and SWIZ are entirely optional. They may or may not occur.\n- PROC if present is only used in DXT1 types\n- OFP Palette Indexes ONLY have an AVG\n- OFFS is almost always present (if not, the engine builds it as required, when required, anyway)\n\n#### AVGCTAGG\n\nAverage Colour\n\n- This tag contains average color of texture, probably used in rendering the 8:8 Grey & alpha textures.\n- Always present, and always first in list with the sole exception of *some* GRAY .paX's\n\n```cpp\n{\n    char  \"GGATCGVA\";\n    ulong len;    // 4 bytes\n    ulong RGBA;    // FF443D39\n}\n```\n\n#### MAXCTAGG\n\nGenerally always present. Always follows immediately after an AVG\n\n```cpp\n{\n    char    \"GGATCXAM\";\n    ulong    len;    // 4 bytes\n    ulong    data;    // FFFFFFFFF no other value seen so far\n}\n```\n\nContains color of brightest pixel in texture?\n\n#### FLAGTAGG\n\nOptional, but will always occur immediately after a MAXC when present\n\n```cpp\n{\n    char    \"GGATGALF\";\n    ulong    len;    // 4 bytes\n    ulong    range;    // 0 to 2\n}\n```\n\nMarks if texture contains transparency. Value 1 means basic transparency, 2 means alpha channel is not interpolated.\nThis flag should be always present in LOD textures with 1-bit alpha with value of 2 or there will be \"ghost outlines\" on LOD textures when viewed from distance.\nNote that this flag must be present in texture file when binarizing model, because Binarize stores information about how to render textures in actual P3D file.\n\n#### SWIZTAGG\n\nOptional\n\n```cpp\n{\n    char    \"GGATZIWS\";\n    ulong    len;    // 4 bytes\n    ulong    data;    // 0x05040203\n }\n```\n\nSwizzle is apparently used to modify texture components processing like swizzle modifiers in pixel shaders.\nFor example ArmA's sky texture has green channel stored in alpha channel and inversed to take advantage from feature that in DXT5 64 bits are used for alpha channel in each block and 64 bits for RBG, giving double the accuracy to green channel as opposed to storing texture just normally.\n\nExact format of swizzle data is still unknown.\nswizzle data:\n\n```cpp\nchar channelSwizzleA;\nchar channelSwizzleR;\nchar channelSwizzleG;\nchar channelSwizzleB;\n```\n\nformat of swizzle char:\n\n```\nbits 7-4 = 0;\nbit 3 - \"1\" flag. All channel data must be set to 0xff;\nbit 2 - \"negate flag\". Channel data must be negated.\nbit 1-0 - number of color channel:\n00 - Alpha channel\n01 - Red channel\n10 - Green channel\n11 - Blue channel\n```\n\nfor example (*_nohq.paa textures)\n\nswizzle data is:\n\n```\n0x05 - Alpha, Negated, stored in Red;\n0x04 - Red, Negated, stored in Alpha;\n0x02 - Green, Stored as is;\n0x03 - Blue, Stored as is;\n```\n\n#### PROCTAGG\n\nOnly ever seen with some DXT1 types\n\n```cpp\n{\n    char    \"GGATCORP\";\n    ulong    len;    //  strlen(text)\n    char    text    // NOT ASCIIz\n}\n```\n\nthis is a non asciiz string (not zero terminated)\n\nExample:\n\n```\nx = (u + 1) * 0.5;\ny = (v + 1) * 0.5;\n\nif (y < 0.5) then\n{\n    sharpOut = 20;\n    sharpIn = 60;\n\n    offset = x - 0.5;\n    if (offset < 0) then\n    {\n        edge = (1 + offset)^sharpOut;\n    }\n    else\n    {\n        edge = (1 - offset)^sharpIn;\n    };\n\n    res = edge * y * 2;\n}\nelse\n{\n    sharpOut = 20 - 40 * (y - 0.5);\n    sharpIn = 60 - 125 * (y - 0.5);\n\n    offset = x - 0.5;\n    if (offset < 0) then\n    {\n        edge = (1 + offset)^sharpOut;\n    }\n    else\n    {\n        edge = (1 - offset)^sharpIn;\n    };\n\n    res = edge;\n    sdCoef = y * 2 - 1;\n    shoreDisappear = 1 - sdCoef^2;\n    res = res * shoreDisappear;\n};\n\na = 1;\nr = res;\ng = res;\nb = res;\n```\n\n#### OFFSTAGG\n\nAlmost always present\n\n```cpp\n{\n    char  \"GGATSFFO\";\n    ulong len;            // 16 * sizeof(ulong)\n    ulong offsets[16];\n}\n```\n\nExample:\n\n```\n= 6 entries. last 10 unused\n256 x 128    Size = 16384\n128 x 64    Size = 4096\n64 x 32    Size = 1024\n32 x 16    Size = 256\n16 x 8        Size = 64\n8 x 4        Size = 16\n```\n\nMipMap data is presented in 'blocks'. One or more 'blocks' exist in a pax file.\n\nThis tag declares where each of these blocks are in the file, relative to start of file.\n\nThe location of each block is already known, relative to the size of the previous block (if any). So, although almost always present in pax files, it is use, is redundant.\n\nThis tag always contains 16 ULONG offsets. Each one is a hard offset to actual mipmap data relative to start of file.\n\nNot all entries are used (obviously) since most pax files contain less than 16 mipmaps. Unused offsets contain the value 0x00000000.\nThere are no known examples of splattered offset entries. All offsets after the first 0 entry are 0 as well.\n\n### Palette\n\n```cpp\nPalette\n{\n    ushort    nPaletteTriplets;                    // always 0 except for index palette type\n    bytes    BGR_Palette[nPaletteTriplets][3];    // only exists if nPaletteTriplets > 0\n}\n```\n\npalette triplets when they exist, consist of **BGR** values. Note the reversal from the expected order.\n\n### Mipmap\n\nMipmaps are in contiguous blocks that extend to end of file.\n\nThere is nothing strange about Mipmaps. Each mipmap is simply a rectangular texture (almost always square) and each one conforms to 2^n dimensions.\n\nEach mipmap defines increasingly poorer resolution. The highest (first) mimpap is THE texture, each successively smaller one is one quarter the size.\nIt progresses in smallness down to a minimum of 2 x any other 2^n or, any other 2^n x 2. For a square texture, this is simply a 2 x 2 matrix.\n\n```cpp\nMipmap\n{\n    ushort    width;        // width of this mipmap\n    ushort    height;        // height of this mipmap\n    if (INDEX PALLETE)\n    {\n        if (width == 0x4D2 && height == 0x223D) // special 1234 x 8765 signature\n        {\n            ushort    width;        // actual width\n            ushort    height;        // actual height\n            // use -signed- lzss compression for this block (introduced for for resistance pax files.\n        }\n        //  else use runlength Compression for this block // used by demo and cwc pax files\n    }\n\n    if (width && height)\n    {\n        byte    size[3];    // size of texture data in file. this is 24-bit unsigned integer.\n        byte    data[size];    // actual texture data compressed or otherwise\n    }\n};\n```\n\nThe last mipmap is a dummy consisting of zero width and height with NO FURTHER DATA\n\nThe size triplet reflects the size of **compressed** data in the file **not** necessarily the actual size of the data.\n\n### Compression\n\n**ALL** non DXT signatures are unconditionally compressed using **signed** LZSS. Index Pallets use this too, OR, runlength encoding.\n\nThere are two wrinkles to LZSS compression not encountered in other uses of the same algorithm (pbo, p3d, wrp eg).\n\n- The >1024 rule does not apply. ALL data is compressed. The end result is the smaller box sizes are most often larger on disk.\n- The checksum following the data (all data sizes are actually -4 their 'real' length) is SIGNED addititive. All other lzss checksums used elsewhere in bis are UNsigned additive.\n\nExample:\n\n```\nType: RGBA 4:4:4:4 texture\nAVGC: F5A9A9AB\n256 x  128 Real Size 65536. Size in file  11770\n128 x   64 Real Size 16384. Size in file   3521\n 64 x   32 Real Size  4096. Size in file   1148\n 32 x   16 Real Size  1024. Size in file    407\n 16 x    8 Real Size   256. Size in file    169\n  8 x    4 Real Size    64. Size in file     57\n  4 x    2 Real Size    16. Size in file     22 <<<<<\n```\n\nthe result of decompression of any type (lzo, lzss or runlength is **encoded** data. The encoding is, perhaps obviously,dependent on the paxType:\n\n**encoded** sizes are a further means of compression.\n\n- width x height is the number of pixels to display. Since all output to a screen is ultimately expressed as RGBA 4 byte integers.\n- width x height x 4 is the size of the ultimate output IN BYTES.\n\nNot so the **encoded** array.\n\n- INDEX width * height // each byte is an index to a color table\n- DXT1 width * height / 2\t// 1 nibble per pixel\n- DXT2 width * height // not used\n- DXT3 width * height // not used\n- DXT4 width * height // not used\n- DXT5 width * height // one byte per pixel\n\n- RGB4444\twidth * height * 2\t// 4 nibbles per color\n- RGB5551\twidth * height * 2\t// ditto\n- RGB565\twidth * height * 2\t// ditto (not used)\n- GREY width * height * 2\t// one byte alpha, one byte 'grey'\n- RGB8888\twidth * height * 4\t// straight rgba output (un-encoded)\n\n#### Before Arma 2\n\nDXT textures are stored \"as is\" in the file. Therefore the DXT data could be directly passed to the graphic hardware.\nFor software decoding of DXT textures see [DXTn compress/decompress](http://code.google.com/p/libsquish/)\n\n#### After Arma 2\n\nDXT formats are **potentially** compressed.\n\n(All other formats remain the same with unconditional **lzss** compression)\n\nArma 2 introduces **LZO** compression for DXT. The criteria for DXT-LZO compression is having the top bit of the width paramater set.\n\nNote that under this circumstance, the width bit must be masked before any size calculations\n\nThis bit is set when size on disk for dxt formats != expected size . (See above table). It is either a 'safety bit' for the unusual circumstance of size being same, or, simply a flag to indicate lzo compression.\n\nEither way, there are (currently) no known instances of dxt compression where sizes are same, and quite obviously, all unequal sizes must, by defintion, be lzo compresssed.\n\nNote also, that this is, truly, conditional compression. Only larger dimensions (256x256) eg are treated in this manner. Lesser value mipmaps in the same pax file, remain un altered.\n\n#### Index Palette Compression\n\nIf the file doesn't start with a known PaX type the data array of the mipmaps contain the indices to the color palette (which appears just before mipmaps and only for palette type).\n\n**TWO** (2) types of compression are in force determined by the special structure of index pallette mipmaps (see above)\n\n- Standard LZSS.\n\nif the signature for width and height IS 1234 x 8765 then standard lzss compression is used, using, the next four bytes as width and height.\n\notherwise:\n\nA very basic \"compression\", which even can make the data slightly bigger, is used. The data is split into blocks of the following structure:\n\n```cpp\nstruct block\n{\n    byte flag;\n    byte data[...];\n}\n```\n\nThe flag can be of two different types. If the most significant bit of flag is zero, it tells you how much bytes to read.\nIf flag is 0x05 for example you have to read 6 bytes (always one more then the value of flag).\n\nIf the most significant bit of flag is one it tells you how often the next byte is repeated.\nIf flag is 0x82 for example the value after the flag byte is repeated 3 times (always one more than (flagvalue - 0x80)).\n\nNote that with this, special, compression, there is no checksum.\n\nIndex palette Mipmaps can have mixtures of both types.\n\n### Addenda\n\n#### Decompression Code\n\nsee Compressed LZSS File Format\n\n#### HexDump\n\nThe 0x4747 format mentioned in this picture indicates index palette format, because 0x4747 is the \"GG\" of \"GGAT\" signature.\n\n#### Alpha channel interpolation\n\nThese two images visualize difference between alpha channel interpolation (FLAGTAGG header tag value).\n\n##### FLAGTAGG = 1, interpolated alpha channel (default behaviour)\n\n##### FLAGTAGG = 2, alpha channel interpolation disabled\n\n### Bibliography\n\n- Feersum's original posting on BI forums: *paa/pac texture format documentation* (dead link)\n- MSDN documentation on DXT1 textures: [DirectX: Opaque and 1-Bit Alpha Textures](https://learn.microsoft.com/en-us/windows/win32/direct3d9/opaque-and-1-bit-alpha-textures)\n- Squish Compression [DXT Compression Techniques](https://www.sjbrown.co.uk/posts/dxt-compression-techniques/)","sections":[{"heading":null,"level":1,"markdown":"> **Disclaimer: This page describes internal undocumented structures of Bohemia Interactive software.**\n>\n> This page contains unofficial information.\n>\n> Some usage of this information may constitute a violation of the rights of Bohemia Interactive and is in no way endorsed or recommended by Bohemia Interactive.\n> Bohemia Interactive is not willing to tolerate use of such tools if it contravenes any general licenses granted to end users of this community wiki or BI products.\n\nOf the many image file formats out there, such as jpeg, or gif, Bohemia Interactive choose to use a specially developed file format (paa) as the base texture file for all engine types.\n\nThe reason for this is the raw data within the file contain mipmaps which be passed directly to Microsoft's DirectX as a DXT1 picture (e.g) without further processing.\n\nAll engines except Elite also support JPG files (but they have no transparency). Most of encoded mipmap formats in pax files do."},{"heading":"PAC files","level":4,"markdown":"`.pac` files are (almost) synonomous with `.paa`.\nIn the origins of Operation Flashpoint, `.paa` were meant to be two tone colors (black & white or grayscale) and `.pac` = colour.\n\nThe distinction does not exist. All engines treat pac or paa equally. They are referred to as paX files in the rest of this document.\n\nNote however that Arma 2's TGAviewer *cannot* read a palette index `.paa` extension. only `.pac` extensions (the contents can be identical)."},{"heading":"OFP Demo Files","level":4,"markdown":"The initial 'proof of concept' for Operation Flashpoint released in 1997, was a demonstration of an island with models and textures.\n\nThese pax files can still be read by tgaviewer! (and pal2pace)\n\nThe *only* difference between these, and subsequent pax files is that the index pallet appears at beginning of file. In other words\n\n- There is NO header type, Pallet index is assumed.\n- There are NO taggs.\n- Other than this, demo files are 100% identical to OFP's Pallet index including the use of run length compression."},{"heading":"Main Format","level":4,"markdown":"Overall structure of a pax file is\n\n```cpp\nstruct overall\n{\n    ushort        TypeOfPaX;    // OPTIONAL\n    Tagg        Taggs[...];    // OPTIONAL\n    Palette        Palette[...];\n    MipMap        MipMaps[...];\n    ushort        Always0;\n};\n```"},{"heading":"TypeOfPaX (optional)","level":3,"markdown":"With the sole exception of OFP index palettes and demo, all paX files begin with a 2 byte 'type' signature.\nOFP index palettes have *no* type, they begin with tagg structures immediately. demo files have neither, and begin with the index pallet.\n\n| PaX Type | Game |  |  |  |  |  |\n|---|---|---|---|---|---|---|\n| PaX Type | OFP<br>Demo | Operation Flashpoint v1.00 | Armed Assault v1.00 | Arma 2 v1.00 | Oxygen 2 |  |\n| 0xFF01 DXT1 | ✗ | ✓ | ✓ | ✓ | ✓ |  |\n| 0xFF02 DXT2 | ✗ | ✗ | ✗ | ✗ | ✓ |  |\n| 0xFF03 DXT3 | Unknown |  |  |  |  |  |\n| 0xFF04 DXT4 | ✗ | ✗ | ✗ | ✗ | ✓ |  |\n| 0xFF05 DXT5 | ✗ | ✗ | ✓ | ✓ | ✗ |  |\n| 0x4444 RGBA 4:4:4:4 | ✗ | ✓ | ✗ | ✓ | ✗ |  |\n| 0x1555 RGBA 5:5:5:1 | ✗ | ✗ | ✓ | ✓ | ✗ |  |\n| 0x8888 RGBA 8:8:8:8 | ✗ | ✗ | ✗ | ✗ | ✓ |  |\n| 0x8080 GRAY w Alpha | ✗ | ✓ | ✓ | ✓ | ✓ |  |\n\n'**Index Pallets only occur in Operation Flashpoint and demo** Later versions of TGAViewer still 'understand' them IF and only IF they have .pac extensions. Under this circumstance they are translated as ARGB8888 (and saved as such)\n\nNote that information on DXT formats can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression."},{"heading":"Taggs","level":3,"markdown":"Taggs do not exist for demo pax files.\n\n```cpp\nTagg\n{\n    char    signature[4];    // always \"GGAT\"\n    char    name[4];        // name of the tagg in reversed order (for example: \"CGVA\")\n    ulong    dataLen;\n    byte    data[dataLen];\n}\n```\n\nTaggs are read until no \"GGAT\" signature is encountered. Palette structure follows."},{"heading":"General","level":4,"markdown":"Structurally, all TAGG's are optional and can occur multiple times. In practice:\n\n- At least one TAGG exists for ALL types, including index palettes.\n- Any TAGG if it occurs, only occurs, once.\n- TAGG order of presentation is\n\n```\nAVG        // always first\nMAX        // always 2nd etc...\nFLAG\nSWIZ or PROC (not seen together)\nOFFS    // always last\n```\n\n- ALL TAGG's begin with an AVG with the sole exception of *some* GREY .paX's\n- MAX is almost follows an AVG\n- FLAG, PROC, and SWIZ are entirely optional. They may or may not occur.\n- PROC if present is only used in DXT1 types\n- OFP Palette Indexes ONLY have an AVG\n- OFFS is almost always present (if not, the engine builds it as required, when required, anyway)"},{"heading":"AVGCTAGG","level":4,"markdown":"Average Colour\n\n- This tag contains average color of texture, probably used in rendering the 8:8 Grey & alpha textures.\n- Always present, and always first in list with the sole exception of *some* GRAY .paX's\n\n```cpp\n{\n    char  \"GGATCGVA\";\n    ulong len;    // 4 bytes\n    ulong RGBA;    // FF443D39\n}\n```"},{"heading":"MAXCTAGG","level":4,"markdown":"Generally always present. Always follows immediately after an AVG\n\n```cpp\n{\n    char    \"GGATCXAM\";\n    ulong    len;    // 4 bytes\n    ulong    data;    // FFFFFFFFF no other value seen so far\n}\n```\n\nContains color of brightest pixel in texture?"},{"heading":"FLAGTAGG","level":4,"markdown":"Optional, but will always occur immediately after a MAXC when present\n\n```cpp\n{\n    char    \"GGATGALF\";\n    ulong    len;    // 4 bytes\n    ulong    range;    // 0 to 2\n}\n```\n\nMarks if texture contains transparency. Value 1 means basic transparency, 2 means alpha channel is not interpolated.\nThis flag should be always present in LOD textures with 1-bit alpha with value of 2 or there will be \"ghost outlines\" on LOD textures when viewed from distance.\nNote that this flag must be present in texture file when binarizing model, because Binarize stores information about how to render textures in actual P3D file."},{"heading":"SWIZTAGG","level":4,"markdown":"Optional\n\n```cpp\n{\n    char    \"GGATZIWS\";\n    ulong    len;    // 4 bytes\n    ulong    data;    // 0x05040203\n }\n```\n\nSwizzle is apparently used to modify texture components processing like swizzle modifiers in pixel shaders.\nFor example ArmA's sky texture has green channel stored in alpha channel and inversed to take advantage from feature that in DXT5 64 bits are used for alpha channel in each block and 64 bits for RBG, giving double the accuracy to green channel as opposed to storing texture just normally.\n\nExact format of swizzle data is still unknown.\nswizzle data:\n\n```cpp\nchar channelSwizzleA;\nchar channelSwizzleR;\nchar channelSwizzleG;\nchar channelSwizzleB;\n```\n\nformat of swizzle char:\n\n```\nbits 7-4 = 0;\nbit 3 - \"1\" flag. All channel data must be set to 0xff;\nbit 2 - \"negate flag\". Channel data must be negated.\nbit 1-0 - number of color channel:\n00 - Alpha channel\n01 - Red channel\n10 - Green channel\n11 - Blue channel\n```\n\nfor example (*_nohq.paa textures)\n\nswizzle data is:\n\n```\n0x05 - Alpha, Negated, stored in Red;\n0x04 - Red, Negated, stored in Alpha;\n0x02 - Green, Stored as is;\n0x03 - Blue, Stored as is;\n```"},{"heading":"PROCTAGG","level":4,"markdown":"Only ever seen with some DXT1 types\n\n```cpp\n{\n    char    \"GGATCORP\";\n    ulong    len;    //  strlen(text)\n    char    text    // NOT ASCIIz\n}\n```\n\nthis is a non asciiz string (not zero terminated)\n\nExample:\n\n```\nx = (u + 1) * 0.5;\ny = (v + 1) * 0.5;\n\nif (y < 0.5) then\n{\n    sharpOut = 20;\n    sharpIn = 60;\n\n    offset = x - 0.5;\n    if (offset < 0) then\n    {\n        edge = (1 + offset)^sharpOut;\n    }\n    else\n    {\n        edge = (1 - offset)^sharpIn;\n    };\n\n    res = edge * y * 2;\n}\nelse\n{\n    sharpOut = 20 - 40 * (y - 0.5);\n    sharpIn = 60 - 125 * (y - 0.5);\n\n    offset = x - 0.5;\n    if (offset < 0) then\n    {\n        edge = (1 + offset)^sharpOut;\n    }\n    else\n    {\n        edge = (1 - offset)^sharpIn;\n    };\n\n    res = edge;\n    sdCoef = y * 2 - 1;\n    shoreDisappear = 1 - sdCoef^2;\n    res = res * shoreDisappear;\n};\n\na = 1;\nr = res;\ng = res;\nb = res;\n```"},{"heading":"OFFSTAGG","level":4,"markdown":"Almost always present\n\n```cpp\n{\n    char  \"GGATSFFO\";\n    ulong len;            // 16 * sizeof(ulong)\n    ulong offsets[16];\n}\n```\n\nExample:\n\n```\n= 6 entries. last 10 unused\n256 x 128    Size = 16384\n128 x 64    Size = 4096\n64 x 32    Size = 1024\n32 x 16    Size = 256\n16 x 8        Size = 64\n8 x 4        Size = 16\n```\n\nMipMap data is presented in 'blocks'. One or more 'blocks' exist in a pax file.\n\nThis tag declares where each of these blocks are in the file, relative to start of file.\n\nThe location of each block is already known, relative to the size of the previous block (if any). So, although almost always present in pax files, it is use, is redundant.\n\nThis tag always contains 16 ULONG offsets. Each one is a hard offset to actual mipmap data relative to start of file.\n\nNot all entries are used (obviously) since most pax files contain less than 16 mipmaps. Unused offsets contain the value 0x00000000.\nThere are no known examples of splattered offset entries. All offsets after the first 0 entry are 0 as well."},{"heading":"Palette","level":3,"markdown":"```cpp\nPalette\n{\n    ushort    nPaletteTriplets;                    // always 0 except for index palette type\n    bytes    BGR_Palette[nPaletteTriplets][3];    // only exists if nPaletteTriplets > 0\n}\n```\n\npalette triplets when they exist, consist of **BGR** values. Note the reversal from the expected order."},{"heading":"Mipmap","level":3,"markdown":"Mipmaps are in contiguous blocks that extend to end of file.\n\nThere is nothing strange about Mipmaps. Each mipmap is simply a rectangular texture (almost always square) and each one conforms to 2^n dimensions.\n\nEach mipmap defines increasingly poorer resolution. The highest (first) mimpap is THE texture, each successively smaller one is one quarter the size.\nIt progresses in smallness down to a minimum of 2 x any other 2^n or, any other 2^n x 2. For a square texture, this is simply a 2 x 2 matrix.\n\n```cpp\nMipmap\n{\n    ushort    width;        // width of this mipmap\n    ushort    height;        // height of this mipmap\n    if (INDEX PALLETE)\n    {\n        if (width == 0x4D2 && height == 0x223D) // special 1234 x 8765 signature\n        {\n            ushort    width;        // actual width\n            ushort    height;        // actual height\n            // use -signed- lzss compression for this block (introduced for for resistance pax files.\n        }\n        //  else use runlength Compression for this block // used by demo and cwc pax files\n    }\n\n    if (width && height)\n    {\n        byte    size[3];    // size of texture data in file. this is 24-bit unsigned integer.\n        byte    data[size];    // actual texture data compressed or otherwise\n    }\n};\n```\n\nThe last mipmap is a dummy consisting of zero width and height with NO FURTHER DATA\n\nThe size triplet reflects the size of **compressed** data in the file **not** necessarily the actual size of the data."},{"heading":"Compression","level":3,"markdown":"**ALL** non DXT signatures are unconditionally compressed using **signed** LZSS. Index Pallets use this too, OR, runlength encoding.\n\nThere are two wrinkles to LZSS compression not encountered in other uses of the same algorithm (pbo, p3d, wrp eg).\n\n- The >1024 rule does not apply. ALL data is compressed. The end result is the smaller box sizes are most often larger on disk.\n- The checksum following the data (all data sizes are actually -4 their 'real' length) is SIGNED addititive. All other lzss checksums used elsewhere in bis are UNsigned additive.\n\nExample:\n\n```\nType: RGBA 4:4:4:4 texture\nAVGC: F5A9A9AB\n256 x  128 Real Size 65536. Size in file  11770\n128 x   64 Real Size 16384. Size in file   3521\n 64 x   32 Real Size  4096. Size in file   1148\n 32 x   16 Real Size  1024. Size in file    407\n 16 x    8 Real Size   256. Size in file    169\n  8 x    4 Real Size    64. Size in file     57\n  4 x    2 Real Size    16. Size in file     22 <<<<<\n```\n\nthe result of decompression of any type (lzo, lzss or runlength is **encoded** data. The encoding is, perhaps obviously,dependent on the paxType:\n\n**encoded** sizes are a further means of compression.\n\n- width x height is the number of pixels to display. Since all output to a screen is ultimately expressed as RGBA 4 byte integers.\n- width x height x 4 is the size of the ultimate output IN BYTES.\n\nNot so the **encoded** array.\n\n- INDEX width * height // each byte is an index to a color table\n- DXT1 width * height / 2\t// 1 nibble per pixel\n- DXT2 width * height // not used\n- DXT3 width * height // not used\n- DXT4 width * height // not used\n- DXT5 width * height // one byte per pixel\n\n- RGB4444\twidth * height * 2\t// 4 nibbles per color\n- RGB5551\twidth * height * 2\t// ditto\n- RGB565\twidth * height * 2\t// ditto (not used)\n- GREY width * height * 2\t// one byte alpha, one byte 'grey'\n- RGB8888\twidth * height * 4\t// straight rgba output (un-encoded)"},{"heading":"Before Arma 2","level":4,"markdown":"DXT textures are stored \"as is\" in the file. Therefore the DXT data could be directly passed to the graphic hardware.\nFor software decoding of DXT textures see [DXTn compress/decompress](http://code.google.com/p/libsquish/)"},{"heading":"After Arma 2","level":4,"markdown":"DXT formats are **potentially** compressed.\n\n(All other formats remain the same with unconditional **lzss** compression)\n\nArma 2 introduces **LZO** compression for DXT. The criteria for DXT-LZO compression is having the top bit of the width paramater set.\n\nNote that under this circumstance, the width bit must be masked before any size calculations\n\nThis bit is set when size on disk for dxt formats != expected size . (See above table). It is either a 'safety bit' for the unusual circumstance of size being same, or, simply a flag to indicate lzo compression.\n\nEither way, there are (currently) no known instances of dxt compression where sizes are same, and quite obviously, all unequal sizes must, by defintion, be lzo compresssed.\n\nNote also, that this is, truly, conditional compression. Only larger dimensions (256x256) eg are treated in this manner. Lesser value mipmaps in the same pax file, remain un altered."},{"heading":"Index Palette Compression","level":4,"markdown":"If the file doesn't start with a known PaX type the data array of the mipmaps contain the indices to the color palette (which appears just before mipmaps and only for palette type).\n\n**TWO** (2) types of compression are in force determined by the special structure of index pallette mipmaps (see above)\n\n- Standard LZSS.\n\nif the signature for width and height IS 1234 x 8765 then standard lzss compression is used, using, the next four bytes as width and height.\n\notherwise:\n\nA very basic \"compression\", which even can make the data slightly bigger, is used. The data is split into blocks of the following structure:\n\n```cpp\nstruct block\n{\n    byte flag;\n    byte data[...];\n}\n```\n\nThe flag can be of two different types. If the most significant bit of flag is zero, it tells you how much bytes to read.\nIf flag is 0x05 for example you have to read 6 bytes (always one more then the value of flag).\n\nIf the most significant bit of flag is one it tells you how often the next byte is repeated.\nIf flag is 0x82 for example the value after the flag byte is repeated 3 times (always one more than (flagvalue - 0x80)).\n\nNote that with this, special, compression, there is no checksum.\n\nIndex palette Mipmaps can have mixtures of both types."},{"heading":"Addenda","level":3,"markdown":""},{"heading":"Decompression Code","level":4,"markdown":"see Compressed LZSS File Format"},{"heading":"HexDump","level":4,"markdown":"The 0x4747 format mentioned in this picture indicates index palette format, because 0x4747 is the \"GG\" of \"GGAT\" signature."},{"heading":"Alpha channel interpolation","level":4,"markdown":"These two images visualize difference between alpha channel interpolation (FLAGTAGG header tag value)."},{"heading":"FLAGTAGG = 1, interpolated alpha channel (default behaviour)","level":5,"markdown":""},{"heading":"FLAGTAGG = 2, alpha channel interpolation disabled","level":5,"markdown":""},{"heading":"Bibliography","level":3,"markdown":"- Feersum's original posting on BI forums: *paa/pac texture format documentation* (dead link)\n- MSDN documentation on DXT1 textures: [DirectX: Opaque and 1-Bit Alpha Textures](https://learn.microsoft.com/en-us/windows/win32/direct3d9/opaque-and-1-bit-alpha-textures)\n- Squish Compression [DXT Compression Techniques](https://www.sjbrown.co.uk/posts/dxt-compression-techniques/)"}],"categories":["Real Virtuality File Formats"],"groups":["Tools and File Formats"]}