ACT Image File Author: Jagged Fel (jaggedfel621@gmail.com) Site: http://idmr.empirereborn.net Updated: 2009.10.05 ===== The .ACT file is primarily used for backdrops and explosion graphics in TIE95 and XvT/BoP. The ACT format is also used in the XACT LFD Resource, although this seems to be deprecated in TIE95 in favor of separate *.ACT files. For the purpose of this document, the use of *.ACT files will be assumed. To use for XACTs, add the standard LFD header information. ===== ACT Structure The following values are used through this and all of my file definitions unless otherwise specified: NAME LENGTH DESC ---- ------ ---- BYTE 1 unsigned 8-bit SHORT 2 signed Int16 INT 4 signed Int32 LONG 8 signed Int64 -- FileHeader INT[FrameCount] FrameOffsets Frame[FrameCount] ImageData -- struct FileHeader (size 0x34) { 0x0000 INT Length 0x0004 INT TotalColorCount 0x0008 LONG Reserved (0) 0x0010 INT HeaderLength (0x34) 0x0014 INT Reserved (0) 0x0018 INT FrameCount 0x001C INT ImageWidth [zero-indexed] 0x0020 INT ImageHeight [zero-indexed] 0x0024 INT CenterX 0x0028 INT CenterY 0x002C LONG Reserved(0) } struct Frame { 0x0000 FrameHeader 0x002C Color[ColorCount] INT FrameLeft [rel to CENTER_X, likely -] INT FrameTop [rel to CENTER_Y, likely -] INT FrameRight [rel to CENTER_X, likely +] INT FrameTop Row[FrameHeight] BYTE EndFrame (0xFF) } struct FrameHeader (size 0x2C) { 0x00 INT FrameLength 0x04 INT FrameHeaderLength (0x2C) 0x08 INT ImageDataOffset 0x0C INT FrameLength 0x10 INT FrameWidth 0x14 INT FrameHeight 0x18 LONG Reserved (0) 0x20 INT Shift 0x24 INT Reserved (0x18) 0x28 INT ColorCount } struct Color (size 0x4) { 0x00 BYTE Red 0x01 BYTE Green 0x02 BYTE Blue 0x03 BYTE Reserved (0) } struct Row { OpCode[] BYTE EndRow (0xFE) } struct OpCode { BYTE Value #if (Value==0xFB) (Shift) BYTE IndexShift BYTE NumRepeat #elseif (Value==0xFC) (Blank) BYTE NumRepeat #elseif (Value==0xFD) (Repeat) BYTE NumRepeat BYTE ColorIndex #else (Short) #endif } ===== ACT Structure Details Yet another graphics format for the X-wing series; I think for TIE95 this is my 7th graphic write-up. This format borrows from some of the other formats, which makes sense seeing that this came around later. The ACT is capable of holding multiple images like the ANIM and uses some of the same header info, and the pixel data is stored much like PANL. The file iself is broken into 2.5 sections. The first is the FileHeader of a fixed size. Next is a half section, the offset table, which is really nothing more than a few INT offsets pointing to each frame. The list is only as long as it needs to be, so if there's only one image, there's only one offset. FrameOffset[0] = 0x34 + FrameCount * 4 FrameOffset[i] = FrameOffset[i-1] + Frame[i-1].FrameLength The remaining section is the Frames, which contain all of the image data. -- FileHeader -- To start it off we have the Length value, which for an *.ACT file is just the filesize, simple enough. TotalColorCount is the sum of ColorCount values from all frames. FrameCount is one-indexed, nothing fancy there. ImageWidth and ImageHeight however are zero-indexed, so add 1 to get the true size. The Center values define the pixel used to "pin" the image at a location. Typically they're just the Width and Height divided in half. -- Frame -- Frames have a header of their own, starting off with the expected FrameLength and FrameHeaderLength values. ImageDataOffset is the offset within the frame to the first Row. ImageDataOffset = 0x2C + ColorCount * 4 FrameLength is repeated for good measure, followed by the frame's true size (one-indexed). The frame can have a size smaller than the entire image. Shift is handy value, and the concept was taken from cockpit PANL image data. This will be explained in detail near the end. ColorCount gives the number of Colors defined in the Frame. Nothing tricky. The Color values should be simple enough, each frame contains a palette of 24-bit RGB values. Color[0] is always treated as transparent, so the actual color is moot. The FrameLeft and FrameTop values are really there if the frame size is smaller than the image size, and the image is not intended to be displayed centered. Usually the values are very close to -(FrameWidth/2) and -(FrameHeight/2). The FrameRight value just makes up the difference, and Top is repeated for some reason. -- Row -- Now we're into the actual pixel data. This is mostly the same as PANL image compression methods, and where Shift can come to play. But first, the special OpCode values. The FC (Blank) op code is slightly different from PANL, in that it only takes one argument, NumRepeat. The ColorIndex value is always zero. FD (Repeat) however is the same, and requires both NumRepeat and ColorIndex in that order. FE is still EndRow, and FF is similarly EndFrame. The pixel data is store right-to-left, so a FlipX is required if you're reading the pixels normally. Idmr.ActImage.dll takes this into account so what you import is what you see in-game. FB (Shift) is a new op code. It takes two arguments, IndexShift and NumRepeat. The Shift code typically preceeds a Short code (explained below) and usually does not repeat. The IndexShift value is added to the Index value of the Short code and the resulting value is used as the Short's new Index value. Now, I think I understand why this op code was created, as it technically allows high-Index values to use the Short codes, but it actually hurts compression, as this method uses four bytes per operation, when using the Repeat code only takes up three bytes. The Shift value comes into play for all other values, designated as Short codes. Unlike PANL which only has two bits for NumRepeat, Shift determines how many bits can be used for that value. A Shift value of 03 will allow 1-8 pixels while Shift 04 will allow 1-16 pixels. Short codes can only be used for ColorIndex values of (1<<(8-Shift))-1. If ColorCount is higher, especially when using 255 colors, then the Repeat code should be used for all ColorIndex values 0x10 and up for Shift 04, 0x20 and up for Shift 03. As mentioned in the previous paragraph, the Shift op code can be used to extend the Short code's range, but for the sake of filesize compression the Repeat code is better. You'll note that in the Row definition, OpCode[] is of an undefined size. This is the only portion of the file that cannot be determined without iteration, both the total byte length and number of OpCodes. The true count of each code is NumRepeat+1 (as NumRepeat is zero-indexed), and the sum of all codes in a row will equal Frame.Width. =====