Z-machine/Blorb adaptive palettes in Glk

I’ve been updating Bocfel to work with Infocom’s graphical games, including supporting the adaptive palette chunk of Blorb.

There is a problem, though: the APal chunk requires you to apply one PNG file’s palette to another PNG file, but Glk does not provide access to images directly. That means you can neither read nor write image palettes. I’ve got a workaround via a new BPal chunk. The idea is to precompute all possible combinations of non-APal × APal images, and store them in the Blorb file. Interpreters can look up whether there is an entry for the current palette plus the requested image, and if so, plot that image instead.

The chunk is tentatively documented as follows:


Because Glk does not give access to the underlying images in a Blorb file (instead allowing you to refer to them by number only), using the Adaptive Palette chunk from a Blorb file (see the Blorb specification, §11.3) is not possible: you cannot read a palette from an image, and you cannot apply a palette to an image.

To work around this, Bocfel introduces the Bocfel Adaptive Palette Chunk, with type ‘BPal’. All requirements for APal chunks apply to BPal as well, and BPal serves as a replacement for APal when APal is not able to be used.

4 bytes         'BPal'          chunk ID
4 bytes         num*12          chunk length
num*12 bytes    ...             adaptive palette entries

Each entry is 12 bytes, of the form:

4 bytes         current         current palette image number
4 bytes         adaptive        requested APal image number
4 bytes         replacement     the image with palette pre-applied

Blorb’s adaptive palette works by applying the “Current Palette” (the last-drawn non-adaptive image’s palette) to a plotted image, if that image is listed in the APal chunk.

The BPal chunk operates by pre-applying all possible palettes to APal images, and storing these in the Blorb file. Each entry is a combination of a current palette image (which must not be in the APal chunk), an adaptive palette image (which is from the APal chunk), and the replacement image ID, which has the palette pre-applied. In short, “replacement” is the ID of an image representing “adaptive” with the “current” palette applied.

The “Current Palette” should be tracked as in APal. Whenever a picture is plotted, the BPal chunk should be consulted. If there is an entry corresponding to the current palette and the requested image, the replacement image should be plotted instead.

All replacement images must be the same size as the image they are replacing. No new Reso entries are added for the replacement images, even if the original image has a corresponding Reso entry. Instead, interpreters must look up a Reso entry for the original image, and apply it to the replacement.


This is a ridiculously niche case: Z-machine interpreters supporting Arthur and/or Zork Zero, but which don’t have access to the images directly, which effectively means using Glk. Be that as it may, I’m looking for feedback from anybody interested in this area.

This does increase the size of Blorb files, of course: Arthur jumps from 271K to 340K, and Zork Zero from 229K to 960K. There’s room for optimization, by including only combinations that are known to be generated by the game, but it’s hardly worth it as far as I’m concerned, when we’re still talking under a meg. I’d rather have a larger Blorb file than accidentally miss an image.

One thing I discovered about Arthur, too, while working on this: the Blorb spec’s recommendation for how to track the current palette and apply it is not sufficient: Arthur does not tend to redraw the banner/staffs, which are the only APal images in the game: rather, it draws them once and then keeps drawing room images on top. At game start, it draws the room first, though, so the first room’s palette is applied and then it sticks, unless you force a redraw, e.g. with F1. You can see this in SDL Frotz: head east at the beginning of the game and hit F1. The banner will change from purple to gold.

I’ve worked around this by manually redrawing the banner image first whenever a room image is drawn, which is hacky, but so’s the rest of my version 6 support, so it’s par for the course.

2 Likes

As a fan of ridiculously niche cases (and, at least theoretically, of people creating new graphical z6 games), thanks for working on this!

This whole Blorb-patch idea seems like a hack to work around this central problem, which is really a problem with Glk, not with the blorb specification. As far as I understand it, the blorb specification already contains all the information needed to encode adaptive palettes, it just doesn’t play nicely with Glk. So why not fix this by making Glk prettier rather than by making blorb files uglier?

It’s a obscure change that will not benefit any other games.

Changing Glk means requiring an update for every Glk library. What Chris is proposing is really only his code. It might be adopted by a different Glk-based Z-machine interpreter that wants to handle Arthur and Zork Zero, but that’s pretty hypothetical right now.

1 Like

My feeling is that, insofar that it is a problem, it’s a problem with ancient stuff being run on modern systems. Glk very clearly was designed with an eye on the Z-machine’s requirements, but I think Zarf made the very pragmatic decision to basically ignore version 6 games.

Glk’s screen model is better suited to modern games, and I have a feeling that, if it were focused on the Z-machine’s screen model, there would be fewer graphical Glulx games than there are today. And yes, you could add a special case for applying one palette to another in Glk, without supporting all of version 6’s screen model, but if you don’t support the rest of it, then it’s not of all that much use—I mean, I’ve been able to get version 6 games working, more or less, with Glk, but that’s because I’ve added a bunch of special cases to handle specific games. Theoretical new version 6 games that try to do anything fancy with graphics simply won’t work.

In the end, yes, Blorb contains all the info necessary to handle the adaptive palette, but it just isn’t compatible with Glk. By using a non-standard Blorb, I just have to generate that once and it’ll work with any graphical Glk implementation, so long as the interpreter supports it.

2 Likes