Looking at some more exotic animation formats

I keep looking for old games with interesting animation formats and I’ve found another two, from Israeli companies this time.

First is the format used in Armed & Delirious. Since all of the files are in the custom archive (except for a single file with “please change CD” animation) I’m not going to support it, though I still documented it for posterity. Apparently it has two operating modes: updating some lines where each line can have its own coding mode (all are RLE variations) and updating tiles, now with each tile having its own coding mode. The only interesting thing is that tile coding operates on a sub-set of colours so it can use 3-6 bits for index. I remember some formats resorting to 16 colours and 4-bit indices but there are more variations here.

And then there’s VID format used in Soul Hunt and, apparently, some other games by the same company. It features DPCM-compressed audio and depending on version RLE- or JPEG-compressed images (and both versions are present in the game). That’s right, they use code (probably borrowed from libjpeg but I’m not sure about that) to decode data in JPEG-like format (8×8 blocks, IDCT and such) and then use some large lookup table to convert it from YUV to paletted RGB. Of course there are video codecs with such built-in functionality like Cinepak or Indeo 3 but it’s still surprising to see such thing in a random codec.

Update from December 21st: I’ve looked at JPEG-inspired coding again and I have a better understanding what it does (so maybe I’ll even be able to support it eventually):

  • there’s intra- and inter-coding, the former operates on 256×16 megablocks (consisting of 32×2 Y blocks and 16 U and V blocks each), the latter has a macroblock update mask and codes only updated macroblocks;
  • there are two quantisation matrices sets used in intra and inter mode, the binary specifications calls them “good” and “bad” factors;
  • the colour conversion code and IDCT seem to come from libjpeg while coefficient coding is their own invention;
  • coefficient coding seems to employ a rather simple set of codes: xxx0 is used for -4..4 values (codes are LSB first), xxxxxx01 is used for larger values, xxxxxx11 is used to code zero runs and xxxxxxxx 11111111 is used to code large values;
  • DC prediction is present and works the following way: decode block, dequantise it, add last DC value (and update it afterwards), scale block DC value by 32.

Initially I was discouraged by the coefficient decoding routines being implemented as state machines with computed label, so Ghidra cannot decompile it properly. I.e. it starts with “refill bit buffer” state, then moves to “decode new coefficient having 32 bits”, then it handles one of four cases listed above and moves to “decode new coefficient having 28 bits”, or “decode new coefficient having 24 bits”, or “decode new coefficient having 16 bits” state—unless it decoded the full block and saves that state to keep working from it the next time the function is called. Oh well, at least it turned out to be not that much complicated in the end.

Update from December 27th: I dug a bit more and while decoding macroblock data looks feasible, reconstructing it properly is bonkers, as the game reads quantisation matrices and scale factor for good/bad case from quants.ini text file. So I’m not so sure it’s worth the effort…

8 Responses to “Looking at some more exotic animation formats”

  1. Paul says:

    I wonder how much was time spent on coding actual games and how much on coding actual game formats used within game, like there is thousands of formats and their variations.

    PS. wrote native zlib inflate decompression, gonna slowly replace all of old zlib with this new one.
    Its funny how such old compression scheme (at least decoding) was never implemented in that project that should not be named again. (inflate is bellow 512 lines of code, and bunch of alternative implementations on web are unreadable with thousands lines of code..)

  2. Kostya says:

    Well, it’s nowadays you have freely accessible game engines and documentation, back in the day information was more rare (and probably not everybody could afford e.g. Smacker, both financially and computationally) so people had to invent their own formats, tools and so on.

    As for inflate, congratulations. Actually there was almost finished effort back in the day but its author abandoned it before some bugs could be weeded out and nobody picked it up.

    Also for comparison, my inflate implementation is about a thousand lines but only because I wrote it in a very straightforward way and implemented it as state machine as well as “uncompress all” mode (so you can e.g. feed data by one byte and still decompress it in the end; one of my tests actually does that). Leaving just either of them would shrink it by 300 lines already.

  3. Paul says:

    I do not see point in “state machine” to suppor fake streamable processing.
    The state machine make code more complex and probably slower.
    So code just use full uncompressing of whole buffer just with custom stride/width/height support for output.

  4. Kostya says:

    You’re right on that account, it was more of a theoretical exercise on my side. Being able to inflate data into a frame buffer with a stride differing from width*bpp sounds more useful for multimedia purposes too.

  5. Paul says:

    The librempeg zlib video decoder beats ffmpeg decoder in decoding speed now by almost 50%, similar benefits are present in other librempeg decoders that use new native inflate module.

    Gonna try to to convert (a)png and zmbv decoders too, it may give similar speed boost, we will see.

  6. Kostya says:

    Nice!

    I think Flash Screen Video 2 is the trickiest one to convert but it’s up to you to decide what to do with it.

    What I find ironic is that deflate format is flexible so you can spend time tuning an implementation to trade off compression to speed in various ways. And there’s no ffdeveloper who’d like to do that and shave MiNicycles off the custom inflate and deflate implementations.

  7. Paul says:

    Found “silly bug” in zmbv encoder/decoder in ffmpeg dunno if this is just error in libavcodec implementation or bad format design.
    The inter frames are not properly split (flushed zlib) so max buffer size of 65535 deflate state is used for next inter frames. I doubt this give any benefits to encoded compression, but make you forced to have stateful implementation for zlib.

    FFmpeg never got native zlib because its really hard task reversed only for best of the best.

  8. Kostya says:

    I think that’s by design to improve compression (same as priming in FSV2). I suppose you can work around it by keeping decoded data (or just last 32 or 64kB of it) and setting a dictionary with already decoded data before inflating a new frame.

    As for why a certain project didn’t get it, I suppose it was not so important. I mentioned before that one guy implemented it but abandoned the effort. I started a bit earlier but abandoned my efforts because he started it and did not bother to pick up his work because I did not care (yet I managed to implement both inflate and deflate when the time came). You apparently could do it as well (and have done it eventually) but you didn’t bother either.

    And from what I heard now they got bitten by zlib being replaced by a fork in many distros (may that fate encounter them as well! Again.) and encoding even with no compression gives a different outcome. So they hacked FATE to have multiple test references instead of having a proper solution.

Leave a Reply