Some words about the oldest QuickTime

February 19th, 2026

Since apparently discmaster.textfiles.com ran out of space (because storage is not cheap now thanks to “AI” companies), I have no new formats to be distracted with and have to resort to looking at the old ones.

As we all know, A**le single-handedly invented multimedia and the existence of IFF ANIM (that pre-dates it by a couple of years) should not confuse you. From what I could find, the earliest QuickTime samples can be found on Apple Reference & Presentation Library 8 CD-ROM that was intended for demonstration but not for the consumers. That disc actually contains three flavours of QT MOV: ordinary MOVs that can be decoded without problems, slightly older MOV that looks almost the same but has slightly different format (and features version -1 both in the header and in the metadata strings) and some extremely old files that do not correspond to the usual MOV structure. Those apparently come from the alpha version of QuickTime around year 1990 when it was still known by its code-name after USian artist of Ukrainian origin.

The first glaring difference is that atoms are not strictly nested but may contain some data beforehand. Essentially all atoms that normally have special header atoms have it before the rest. So instead of (pardon my S-expressions)

(moov (mvhd (trak tkhd (mdia mdhd (minf …))))

there is

(moov [moov header data] (trak [trak header data] (mdia [mdia header data] [all codec description data])))

Data structure is flatter too. In release QT version data for the individual tracks is grouped into chunks, so the header describes both the chunks and what tracks use what chunks (and what are frame sizes in the chunk). Here frames are stored as (offset, size) pairs.

From the accompanying decoder I can see it supported raw video, RLE and motion JPEG at the time—fancy vector quantisation codecs came later.

I hope to support it one day but there’s no hurry. Currently I’m more or less done improving NihAV tools and want to add some practical demuxers, muxers (and maybe even impractical encoders like for Indeo 4 or RedHat Ultimotion). And there’s na_eofdec still waiting to collect enough supported formats for its first release (tangential fun fact: recently I’ve added support for Electric Image format there which is mostly a simple RLE but its frames apparently have timestamps in floating-point format like 0.266 or 0.5).

P.S. I’ve also discovered MoviePak QT codec which looks like JPEG variant. Maybe one day when I’ll have nothing better to do I’ll look closer at it, but for now I have no desire to reverse engineer M68k code in unknown format.

More codecs to look at

February 11th, 2026

Here’s a list of video codecs I found with the help of discmaster.textfiles.com (mostly by looking for .inf files with “vidc” in them). Maybe I’ll look at them one day, maybe somebody will beat me to it. In either case, it’s a nice reminder for myself.

  • CSMX.dll—reportedly used for CSM0 codec, was bundled with some player, no known samples. Considering its small size it’s likely to be either raw YUV format or lossless codec;
  • d3dgearcodec.dll—D3DGear lossless codec;
  • elsaeqkx.dll—some Elsa quick codec;
  • Esdll.dll—bundled with the same player, the strings inside it hint on it being an unholy mix of ZIP and MELP-based speech codec;
  • ICS422.DRVS422 or SuperMatch YUV 4:2:2 codec (I suspect it’s raw YUV);
  • ICSMS0.DRVSMS0 or SuperMatch VideoSpigot codec (from SuperMac, later bought by Radius);
  • MCTCOD.DRV—MCTPlus Draw driver, supposedly offering 2:1 compression and has a bunch of FOURCCs registered to it: DRAW, MC16, MC24, MR16, MR24, MY16, MY24
  • MyFlashZip0.axMFZ0 lossless codec. Looks like ordinary deflate wrapper really;
  • NTCodec.dll—NewTek nt00 codec (I expect lossless or an intermediate codec). Looks like a simple fixed-fields packing;
  • RGBACodec.dll—apparently Lightworks lossless RGBA codec. Simple RLE inside;
  • Sx73p32.dll—Lucent Technologies SX7300P speech codec;
  • TRICODC.DRV—Trident draw codec with a bunch of compressed RGB and YUV formats: rtc3, rtc5, rtc6, ty0n, ty2d, ty2n, ty2c, r0y1… Compressed in this context means packed using fixed-length bitfields;
  • UCLZSS.DRV—Ulead LZSS codec aka uclz (obviously a simple lossless codec). As expected, it’s yuyv data compressed with LZSS;
  • UCYUVC.DRV—Ulead compressed YUV 411 aka yuvc. Apparently it’s just 8/12/16-bpp YUV (fixed packing scheme, and 16-bit is simply YUYV/YUY2 and such);
  • V422.DRV—Vitec Multimedia V422. Most likely simply raw YUV;
  • V655.DRV—Vitec Multimedia V655 (I’ll venture a guess this means 6-bit luma and 5-bit chroma instead of improbable YUV 6:5:5 subsampling);
  • VDCT.DRV—Vitec Multimedia VDCT.

Oh, and I’ve also found Eloquent elvid32.dll for EL02 FOURCC but it’s different since it has samples and it’s yet another H.263 rip-off.

That’s it for now, I’ll talk about FLAC video some other time.

Random NihAV news

February 8th, 2026

As I mentioned previously, after making 0.5.0 na_game_tool release I’d rather work on something else for a change (if at all), so here are some bits about what I’ve been doing since:

  • first of all, I’ve implemented support for AV, a Polished AVI format. By that I mean the format comes apparently from a Polish company and it’s simplified remux of AVI data: first there’s BITMAPINFOHEADER (and optionally palette) followed by video stream header (starting with the familiar vids tag), then there may be WAVEFORMAT structure with auds stream header, then you have table of contents (video and audio frame sizes plus flags) followed by video and audio chunks. The format was trivial to guess and I’ve added support for it because why not;
  • I’ve also finally implemented functions for reading arrays of integers. First I’ve introduced them to na_game_tool and tried them in some decoders, and then ported them to the main NihAV codebase. The idea behind it is that reading data directly into destination array (with optional byte-swapping) is faster than reading data, re-interpreting it as an integer and finally copying it into the destination buffer. I had a specific version of it implemented in MP4 demuxer already (because otherwise opening a 2-3 hour long video would cause a noticeable delay) but overall it’s nicer to have just one call instead of a loop;
  • in other things, I’ve re-started na_eofdec development using the current na_game_tool codebase. That does not mean I’m starting developing it (or going to do so in the nearest feature) but at least when I eventually get to it, I can add some archive extraction modules as well. Beside that it should be a sandbox for testing MOV muxer that I want to write sooner or later (kinda like OpenDML AVI muxer in NihAV is a backport of one from na_game_tool). Of course it would need to get a couple more formats to test it on but I’m not in a hurry;
  • speaking of MOV, I’ve improved support for MOV in MacBinary II. So far I’ve seen four flavours of it: essentially flat MOV with MacBin header, MOV with mdat box in data fork and moov atom in resource fork, the same but data fork containing mdat contents without size and tag, and even older format (with samples from 1990) with much flatter structure (i.e. lots of nesting chunks are not present at all). The first three are detected and handled more or less fine now, I’ll try to support it even if as a historical curiosity;
  • there was one major code refactoring in NihAV, namely I’ve put demuxing handling code into a new nihav_hlblocks crate and made all my tools use it instead of dragging local copies of it. If you wonder why this was necessary, that’s because you can have normal demuxers (that return full packets for the streams) as well as raw stream demuxers (that return pieces of data belonging to a stream and you need to form full packet using a packetiser). And of course you can have simply raw streams like MP3 that needs a packetiser but no demuxer. That’s why I’ve made a DemuxerObject (yes, very original name) that encapsulates them all and represents all of them as a normal demuxer;
  • and finally I’ve discovered that I can call SDL_UpdateYUVTexture instead of copying frame data into the texture manually. And then I discovered that it did not work with the time display (because it also wrote to the texture directly in presumption that it will update some pixels on the frame; there is a note in the documentation telling not to do so but it worked by chance before). So I’ve changed it to render a separate small RGBA texture and blit it over the frame instead—like it should’ve been done from the start. I somewhat wonder when I’ll have to adapt it all to SDL3 but apparently I can postpone it for now.

That’s all for now. There’s a lot to do, maybe some of it will actually be done.

Quicker look at QuickTime

February 1st, 2026

Since I’ve done looking at game formats for a while (I’ve updated na_game_tool 0.5.0 release with the fixed extraction of old-style Cryo Interactive archives BTW), I decided to look at my backlog of unsupported (either mis-detected as audio-only or not detected at all) MOV files from discmaster instead. Of course the majority of them are files missing their resource fork (and majority of the rest are poorly-recognised data+resource forks in MacBinary II format; that reminds me I should improve support for it), and majority of the rest are files that I support already (like Duck Truemotion 1 or Eidos Escape codecs). And a good deal of the rest are Flash in MOV (not going to touch it). And yet there are a couple of files worth talking about.

  • 911602.MOV—remarkable for having JPEG frame embedded in QuickDraw stream. It made me finally write a decoder for it (but without JPEG support, maybe one day…);
  • a couple of .flm files turned out to have obfuscated moov atom (with something almost, but not quite, entirely unlike TEA if you care). Not something I’d like to support;
  • La88110mN1Hz2_20-07.mov—it turned out to be raw YUV420 codec;
  • Omni_324.mov—compressed apparently by MicroWavelet on NeXT. Intriguing but I doubt anything else can be found about that codec;
  • Music Chase .tmv—a very weird little-endian MOV format with custom video codec (I think I mentioned it before but I haven’t progressed since);
  • there was one Flip4Mac-produced sample (which name eludes me), but considering that it stores ASF packets inside MOV it’s not something anybody is eager to support;
  • some undecodeable SVQ3 files—apparently they are encrypted;
  • and finally there are some QT Sprite files, which seem to be (often deflate-compressed) frames containing several atoms with commands and occasionally image data as well. Sounds too hairy to support but again, maybe one day…

With AVI situation is somewhat better, there are some poorly formatted and encrypted/obfuscated files as well (plus damaged files from AOL archives that start at random position, so contents may be AVI file with some additional garbage in the beginning or missing few dozens kilobytes of initial data). Beside that I’ll probably implement PDQ2 decoder just for completeness sake. Eventually.

Blood & Lace unpacker

January 23rd, 2026

Since somebody took an interest in my year-old post mentioning that I looked at that game (and found its resources not interesting enough), I’ve made a quick and dirty unpacker for its compressed files. You can grab it here.

It is based on lhasa, which is a LHarc/LZH archiver that I could easily extract code from (I considered supporting it in na_game_tool properly but decided that I have no desire to write LH1 unpacker, even with many references being available). Essentially I ripped out LH1 decoding code from it, made it stand-alone and added a small driver to open and unpack JFX1/JGF5 files.

Compiling executable should be as easy as using C compiler on it (I repeated instructions in the beginning of the file just in case). For that only guy who cares (if he still does and actually sees this post)—happy hacking!

na_game_tool 0.5.0 released

January 19th, 2026

Grab it from the usual place if you’re interested in it for some unfathomable reason. I’ll try to document the formats when I have time (and multimedia.cx server does not suffer from crawlers).

Update from January 30: I’ve uploaded a fixed version as I broke extracting old-style Cryo archives during refactoring (and before committing), so now it should work. The rest will (not) work in the same way as before.

na_game_tool: final stretch before 0.5.0

January 15th, 2026

As I’m somewhat tired with na_game_tool development (for now), so I’m just picking bits for the release goal to do that and switch to something else (I’ve found a couple of interesting MOV files to analyse, maybe I can improve my Indeo 3 encoder and such). I’ve added support for HNM5 and HNM6 formats, reached the self-imposed limit of at least twelve original decoders and essentially I need to add 7th Level archive format and that’s it. Meanwhile I can talk about the formats I added support for, I want to add support for (but not right now) and the formats I’m hesitating to add support for.

So, supported formats first.

There’s GRN format used in Genesia game. Frames are stored raw, compressed with RLE (which looks a lot like FLI delta RLE compression but working on pixel pairs) and the data may optionally be compressed with LZSS afterwards. And if would’ve not been a format from French company without some weirdness. In this case audio stream data is interleaved with video in the simplest way: first you have 2kB header, then a certain number of 2kB sectors with initial audio data (the number is in the header), then it is 8kB of video stream followed by 2kB of audio followed by 8kB of video stream etc etc. So video frame data may suddenly have 2kB of audio inside it. Of course it was easily solved with a custom de-interleaver but you’d expect it to see in industry-standard applications and not in a game from early 1990s.

And there’s MGIF from Gates of Skeldal. Despite the name it was only inspired by GIF in the sense it uses similar LZW compression scheme but not the format (unlike HAF). Another curious detail that I haven’t seen elsewhere is using a simple pre-processing: bytes are coded as a difference to the previous ones. What made it annoying is that this prediction resets when LZW decoder resets (i.e. when the “reset dictionary” code is encountered), so you have to implement it inside LZW decompressor instead of making a pass afterwards. Still, it gets a point for the originality.

Finally there is Celestial Impact intro.dxv. This file employs RLE to compress not merely image but its palette and some additional data (it did not look like a sound to me and I have no idea what it is) as a single array.

Now for the formats I want to support in some (distant) future. Beside the usual “whatever I can find” it definitely includes AVI and SMS—two Smacker-based formats. Maybe I can implement some Cinepak-based console formats while at it.

And speaking of console formats, here’s a fun format called DDV and used in Oddworld games (or maybe just one of them). There’s a reverse-engineered implementation of the decoder and by the look of it this format encapsulates MDEC—which makes it a perfect candidate for librempeg. It has support for some MDEC-based formats already after all.

That’s it, hopefully the next post will be about the release already.

na_game_tool: more FLICy formats

December 27th, 2025

I’ve added another bunch of formats to na_game_tool, including both old video formats and game archive support to extract some of those (and other) formats from.

For example, I’ve finally added an extractor for Conquest Earth WAD with its 16-bit FLH (a variant of FLI with RNC compression; it has been supported since version 0.2.0) but there are other variants that justify this post title:

  • Alien Virus animation—almost standard FLI with changed tag and some jitter in subchunk sizes (i.e. they may be a byte too short or a byte too long compared to the chunk size);
  • Bureau 13—here it is a super-format with chunks comprising FLI headers, FLI chunks or PCM audio—and it may be several FLIs with different resolution too. And it is put into its own archive format (which I also added an extractor for);
  • C13 for Hammer of the Gods—at first I thought it’s just yet another hack of the format but after looking closer at the data I realised it’s merely LZSS-compressed. Essentially I just hacked my FLI decoder to decompress data first and operate on decoded data instead of file in this case;
  • Stargunner FLC—like other files in the game archive it was compressed using byte-pair encoding (and it may be the only case of such method being used for compression in the wild). In this case file is split into small chunks and unused byte values are used to code pairs (of used byte values or other pairs). Adding support for it was as trivial as in the previous case, but the fun thing here is that after I figured out the decompression algorithm I found out that it’s been known and supported by various extraction tools, it’s just discmaster picked up the laziest one.

Beside that I’ve REd HUF format from Johnny Bazookatone which employs RLE compression and then further compresses video frame data with global Huffman codes. And I’ve also added support for Goosebumps: Escape from Horrorland archive among other things. The curious thing about it is that such archives contain .gvd files which are essentially remuxed AVIs, so my plug-in reconstructs AVI from them by default (but this can be disabled if you prefer the original files instead). Almost all of the files use Indeo 3 but a couple of them seem to use their own codec with no decoder available so here’s that.

In general I still have at least four original formats to add (plus HNM6 and some extractors) but there seems to be enough candidate games to RE for it to be feasible.

P.S. I also looked at SMV format from AGON: The Lost Sword of Toledo but after looking at the binary specification and discovering MPEG-4 ASP decoder there (also if you remove the SMV header it can be played as an elementary MPEG-4 ASP stream) I lost any interest. Maybe Paul will have some interest supporting it in librempeg, maybe not—and I definitely don’t want to mess with such format (the same applies to KSV as well).

These weeks in na_game_tool

December 14th, 2025

Last time I talked about MVI formats, mentioning that I had one more equally German MVI format. Well, the official specification uses CauseWay DOS extender which compresses executables (and I still haven’t found a way to make DosBox debugger dump loaded 32-bit segments, otherwise I would’ve REd a bunch of Psygnosis formats too). Luckily the demo version did not use it and I was able to find how it is coded. Apparently they employed some non-standard LZW which for some reason shuffled low codes, so 0/1/2 are used as special signals—dictionary bump (followed by byte aligning for some reason; other implementations bump dictionary size implicitly and do not insert bits), dictionary reset and EOF correspondingly—while codes 3..257 are used to code bytes. Beside that it’s nothing special: intra frames are (optionally) LZW-compressed, inter frames consist of pixels and mask telling where on frame to update those pixels (both parts with optional LZW compression).

Speaking of LZW, there was this FLK format (apparently Italian) that also employed LZW to compress pixel data along with the command stream with rather simple commands like “repeat pixel N times, skip next M pixels, copy following P pixels” and additionally “restore Q pixels of the background”. As you can guess, the format is used for animations overlaid on something else in addition to video clips. In case of the latter command stream may be absent and you simply put decompressed pixels into a new frame.

Other codecs are Ascon SKS (which is essentially a collection of JPEGs with swapped chroma planes; I did it mostly as a test of JPEG decoder module that I plan to use in other decoders) and Interactive Pictures VID (which I encountered in .evd, .fvd and .gvd files—for files with English version of the video, French version, and general version without speech). This one is curious not only because its binary specification for some reason contains compression for it (which I encountered first) but for the format features itself. Images are split into 8×8 blocks that may be coded raw, with 2-16 colours and a pattern, and a custom-scan RLE I remember seeing in Bink (and XCF but like Bink there are 16 patterns here as well and not just four). Probably nobody cares about it but it was fun to discover.

In addition to that I’ve implemented support for a couple of well-known formats, namely HNM5 (aka UBB2—UBB is apparently the same but lacks headers) and RoQ. The former is a typical French format (you can tell it by the fact it uses crazy motion compensation modes with mirroring and transposing), I implemented it mostly for completeness sake (so I have support for all HNM flavours in my tool, all is left is HNM6). The latter has the same rationale as when I looked at it four years ago: its support in libavformat/libavcodec sucks i.e. it’s adequate for videos intended for id Tech 4 engine but not for the original Trilobyte games (not handling JPEG-compressed frames and not descending into 0x1030 chunk are two most glaring deficiencies). Plus there is a can of worms related to the fact that audio in Trilobyte games uses unsigned predictor while id Tech games use signed predictor and to the fact that for some files you need to scale motion vector twice (and for some files it should be done on a picture scaled twice vertically). I try my best to detect such situations but they do not offer work correctly (and if you wonder, in games engine may have a hard-coded list of files that should be treated differently).

And that is actually not all! I’m still working on supporting some game archives as well (for example, the majority of RoQ files I encountered were hidden in .gjd archives, same as VDX files). One interesting example is Escal compressor. One funny aspect of it is that it is employed in Pumuckl Klabauterjagd, a game for about 8-year olds, while the rest of titles using it are definitely 18+. From technical point it’s interesting as it seems to be inspired by the dynamic variant of deflate with some simplifications: instead of blocks you have codebook definition valid only for the next specified amount of symbols (which are still combined literals and copy symbols, with their codebook coded using code lengths packed with another codebook). Overall it’s distinct enough but the source of inspiration is obvious too.

Overall it means I have only four more original decoders to create plus some of the archive formats to support (like Coktel Vision STK/STK2 as well as the previously mentioned extractor/converter for Monty Python & the Quest for Holy Grail). Probably it will be the last release too as I’m not sure I’ll have enough game formats for another release. Well, there’s also na_eofdec which should get more Amiga formats before 0.1.0 release. And in theory there are console formats if Paul has not written decoders for them all

Being right for wrong reasons

December 6th, 2025

I consider 2020 to be the cut-off point after which it was time for various idiots to shine and make impact on the world. And they’ve been doing an outstanding work since. Among their achievements is something I didn’t expect to see and yet here it is: being right (for completely wrong reasons).

Normally you see the opposite: somebody takes a sane idea and uses it as a pretext for some bad proposals (let’s not even touch all those proposals with the justification of “but think of the children!”, the famous Chinese Four Pests campaign should serve as a good example too—you start with a sane idea of increasing harvests by eliminating pests that hurt crops, you decide to eradicate all species seen near fields and you end up discovering a truth known to any biologist, that birds are much better at hunting insects than people and killing them was a bad idea). Occasionally you see the proverbial broken clock being right (if you generate inane ideas sooner or later one of them might be sane for a change). But recently I’ve started to notice a significant amount of ideas that are actually right but for all the wrong reasons. Most of them are political ones but you can spot some in other areas (like medicine—it is hard to explain why wearing masks was proposed during COVID-19 pandemic when they didn’t believe in the virus spreading by air).

Before moving to the powerhouse of right ideas for wrong reasons, I’d like to mention Hungarian prime minister who claims that Ukraine won’t be allowed to the EU until it conquers corruption. Of course he’s right except for one detail: it is Hungarian corruption that blocks Ukrainian membership and it’s EU that should do something about it (and yet does nothing—so it’s up to Ukraine to fight against at least one of major “patrons” of Hungarian government).

And of course the majority of right ideas for wrong reasons come from the USA. For starters, USian president had a proposal to Europe to stop buying russian gas and oil. There’s nothing wrong with that idea except when you look at the motives, and they’re not about undermining the aggressor’s main income but rather for making Europe dependent on USian fossil fuel (but still dependent on single supplier). A similar proposal for Europe to be more self-reliant in the defence sector instead of relying on USA is equally good unless you see the implied part, namely that Europe can’t trust USA to honour its obligations and that “self-reliance” means buying USian equipment to replace what currently garrisoned forces use and probably paying for the e.g. USian military intelligence services. I’m starting to see a pattern here…

For more IT-related stuff, banning TikTok is an excellent idea. I have not used it myself but from what I heard about it from different sources it’s essentially a digital drug with controlled narratives (and with a vehemently denied role of CPC in that process). Both addictiveness and propaganda are good reasons to ban it and similar creations outright. And yet we see that the main reason is not that it is bad but rather because it is not controlled by US government or corporations (is there still a difference?), so if the USian segment is sold to a USian company it’s all fine.

The last one would be the warrant put by the International Criminal Court on Netanyahu. Regardless of what you think about his recent actions against Gaza, it is his politics that created this situation and endangered lives of the people he is supposed to protect (the same applies to Iran with its mostly peaceful nuclear programme that previously was stopped by Israeli military forces at much earlier stages than “couple of weeks before a bomb”). Throw in his behaviour of a typical dictator (nominally just a prime minister but considering their recent judicial reform and similar things any other dictator in the club does), he really deserves an international warrant (especially since local courts are not likely to do anything about him). But the way ICC handled it feels more like they were politically (or even religiously) motivated instead of trying to bring justice. Sadly this is a likely story with all those international organisations—they tend to outlive their usefulness and become an empty formality often driven by petty interests of the only members who care enough to do anything.

As usual, I can’t do anything about it so I simply point out what’s wrong with this world and move on. The solution exists though: just make people remember old deeds and claims, think, and call others on their bullshit—this will make things much better in just one generation. Implementation details are left to the reader as an exercise.