1. Forum moved (you can use login and pass from old forum)
  2. Many discussions moved to the bugtracker

PCK/TAB image files (sprites)

Discussion in 'Coding' started by JonnyH, Aug 6, 2014.

  1. JonnyH

    JonnyH Well-Known Member Official Developer Administrator

    Joined:
    Jul 17, 2014
    Messages:
    185
    Likes Received:
    39
    Best Answers:
    0
    A quick overview of the pck/tab image format used in apoc:
    NB: This is a work in progress, some things /will/ be wrong/missing!!!

    All sprites are stored in an indexed format, and consist of a .tab/.pck file pair. All multi-byte values are little-endian

    A .tab file is a set of indexes into a .pck file. It is a set of uint32_t offsets, so there are (size_in_bytes(tab file)/4) images stored in the pck/tab set.
    This offset of multiplied by 4, then that is the byte offset for the start of an image in the pck. (NB: Some files are not 4x bytes, instead 1x bytes to get the offset here?)

    The first 12 bytes are the image header:
    {
    uint16_t compressionMode;
    uint16_t unknown;
    uint16_t leftClip;
    uint16_t rightClip;
    uint16_t topClip;
    uint16_t bottomClip;
    }

    Any pixels drawn outside the clip area are ignored.
    Images are initially transparent (index 0) unless a pixel value is specified.

    then, depending on compression mode:

    Mode 1:
    A run-length encoded file. Each chunk consists of a header:
    (THIS HEADER APPEARS TO CHANGE DEPENDING ON FILE - I do not know what causes which to be used. Maybe ufodata/tacdata? - two examples below)
    tacdata/icon_m.pck: Total 12 bytes
    {
    uint32_t pixelSkip;
    uint16_t columnSkip;
    uint16_t pixelCount;
    uint16_t padding;
    uint16_t unknown2; //always zero?
    }
    ufodata/city.pck: Total 8 bytes
    {
    uint32_t pixelSkip;
    uint8_t columnSkip;
    uint16_t pixelCount;
    uint8_t padding;
    }

    Each chunk encodes a run of pixelCount pixels, each being a byte index into the current palette.
    This entire format is based on simplicity writing to a 640x480 paletted display, hence the magic values below.

    If the first 4 bytes of the RLE header are all 0xFF, this is the end of the image.

    Run starting coordinates:
    x = pixelSkip % 640
    y = pixelSkip / 640

    Then pixelCount bytes in increasing-x order. If x == 640, it wraps, so x=0, y++

    padding seems to be the number of '0' values included at the end of the pixel run. This appears to be always sufficient to align the pixels to a 4 byte boundary. The padding pixels appear to be included in pixelCount, but it appears to be harmless to ignore this and just copy the excess transparent '0' indices, as they would be replacing initialized-to-zero pixels anyway.

    Mode 3

    This is a dictionary-compression style image, with lookups into tacdata/xcom.blk file.

    Each row definition starts with a 5 byte header:
    {
    uint8_t rowRecords;
    uint16_t unknown;//Always 128-(num pixels drawn for the first block of the previous row)?
    uint8_t row;
    uint8_t subRecords;
    }

    Then there are subRecords subrecord headers, each 5 bytes:
    {
    uint8_t column;
    uint8_t pixelCount;
    uint24_t blkOffset; //Note, that really is a 3-byte value
    }

    For each subrecord, start at pixel x=column y=row, then copy pixelCount bytes from offset blkOffset of xcom.blk.

    Again, if a header starts with 4 bytes of 0xFF, this is the end of this image.
     
  2. pmprog

    pmprog Well-Known Member Official Developer Administrator

    Joined:
    Jul 8, 2014
    Messages:
    175
    Likes Received:
    12
    Best Answers:
    0
    Probably worth keeping the UFOpaedia page updated.

    In addition, I managed to get ICON_M.PCK to load. It has a slightly modified version of the Compression 1 loading.

    One curious thing about ICON_M.PCK, is that it'll run several "passes" over a single row. I've not had time to properly work out what it's doing, but the latest OpenApoc in my repo will load and display the contents
     
  3. JonnyH

    JonnyH Well-Known Member Official Developer Administrator

    Joined:
    Jul 17, 2014
    Messages:
    185
    Likes Received:
    39
    Best Answers:
    0
    Does your header match my attempts at decoding this above? I used ICOM_M.pck as an example of the 12-byte rle header, and it seems to load OK using that for me...
     
  4. pmprog

    pmprog Well-Known Member Official Developer Administrator

    Joined:
    Jul 8, 2014
    Messages:
    175
    Likes Received:
    12
    Best Answers:
    0
    Yes, though I structured my header differently. I assumed it was still based on the 8byte header, turning the "Reserved" 8 bytes I had to another BytesInRow, then added a 32bit value read before processing.
     
  5. sparkstar

    sparkstar Registered

    Joined:
    Sep 10, 2014
    Messages:
    3
    Likes Received:
    0
    Best Answers:
    0
    i'm making base screen now(i didn't know openapoc.toolset exist..) but pck image is so noisy... i think that the palette is not valid to the pck.

    anyway, hire/fire button is not great but good.. buy/sell is too, but others poor. all buttons are use TACDATA/TACTICAL.PAL palette, others need i think different palette...

    how can i get valid palette-TAB,PCK pair?
     
  6. JonnyH

    JonnyH Well-Known Member Official Developer Administrator

    Joined:
    Jul 17, 2014
    Messages:
    185
    Likes Received:
    39
    Best Answers:
    0
    Yes, that looks like a palette issue. I have not found a 'good' palette for much of the UI - it is possible that it is replaced in code from what is stored in the ufodata/pal_*.dat palettes.

    I seem to remember that the UI only uses the last chunk of the palette - it might be interesting to dump the indices for the ui-only images to confirm this, so we can at least know what part of the palette we are searching for, then we can compare from a dosbox image dump or something to get the values. With that we can search all the data files/exe files for a match...
     
  7. pmprog

    pmprog Well-Known Member Official Developer Administrator

    Joined:
    Jul 8, 2014
    Messages:
    175
    Likes Received:
    12
    Best Answers:
    0
    It exists, but isn't at a usable state. I hope the couple of "forms" I've created already helps you work out how to put the XML together. Please feel free to PM me if you need any assistance.

    I've also added you as a collaborator to the GitHub project... so feel free to pull your own PRs, or work within the main project.

    I'd love to get my feet back into this, just so busy at home, and totally disorganised too, which doesn't help.
     
  8. ringo

    ringo Registered

    Joined:
    Dec 25, 2014
    Messages:
    1
    Likes Received:
    0
    Best Answers:
    0
    Hi guys.
    I've found that there're PCKs starting with 0x0001 (which mean it processed by LoadVersion2Format-method) but offset for this file shouldn't by multiplied by 4. For example see ufodata/over-b.pck. I have no idea to find difference in those files now. May be it's hardecoded?
     
  9. pmprog

    pmprog Well-Known Member Official Developer Administrator

    Joined:
    Jul 8, 2014
    Messages:
    175
    Likes Received:
    12
    Best Answers:
    0
    Sorry, I'm not sure what you're asking, what difference are you looking for?
    This might help
    http://www.ufopaedia.org/index.php?title=Image_Formats_%28Apocalypse%29
     
  10. Skin36

    Skin36 Well-Known Member Official Developer Administrator

    Joined:
    Apr 5, 2015
    Messages:
    65
    Likes Received:
    19
    Best Answers:
    1
    hi. Mode 3 incorrect.Š¯eader is 4 bytes!
     
  11. Skin36

    Skin36 Well-Known Member Official Developer Administrator

    Joined:
    Apr 5, 2015
    Messages:
    65
    Likes Received:
    19
    Best Answers:
    1
    Mode 3

    This is a dictionary-compression style image, with lookups into tacdata/xcom.blk file.

    Each row definition starts with a 4 byte header:
    {
    uint8_t subRecords;
    uint16_t unknown;
    uint8_t row;
    }

    Then there are subRecords subrecord headers, each 5 bytes:
    {
    uint8_t column;
    uint8_t pixelCount;
    uint24_t blkOffset; //Note, that really is a 3-byte value
    }
    that's right
     

Share This Page