Préparer du Yogi Tea maison

Le Yogi Tea à la cannelle et aux épices, c’est bien bon. Pour en faire une version maison, je mélange (pour 4 tasses) :

Faire bouillir le tout pendant 20mn - 1/2h fera un excellent thé de cannelle.

Variante rapide et économique

Si on manque de temps, ou qu’on préfère utiliser des ingrédients plus simples, on peut utiliser des ingrédients en poudre. Pour cela, je mélange (pour une tasse) :

Link's Awakening Disassembly Progress Report part 12

This article is part of an ongoing “Disassembling Link’s Awakening” series, where I attempt to gain some understanding on how special effects were implemented in this game.

✨ New contributors

First let’s congratulate the following new contributors, who made their first commit to the project during the past months:

🔀 Building revisions

For a long time, this project only disassembled the source code for a single version of the game: the US v1.0 release.

A few months ago, Marijn van der Werf started to add support for the German version of the game.

Not an easy task. Not only the dialogs differ from the US version, of course – but there are quite some more differences: a few tilemaps (like the translated Game Over screen), some tiles (e.g. extra alphabet letters), a handful of regional differences…

But in the end, after carefully finding all the differences in the game resources and code, and storing the differences with the baseline English version into German-specific files, Marijn managed to add German support to the disassembly.

Zelda: Link's Awakening File Selection menu in German
The File Selection menu in all its German glory.

But Marijn didn’t stop there.

While he was at it, he casually added support for every version of the game ever released.

In all languages.

Japanese v1.0? Got it. French v1.2? Here you go. English v1.1? There it is.

Zelda: Link's Awakening File Selection menu in Japanese Zelda: Link's Awakening File Selection menu in French
It is now easy to study the Japanese or French games: they will be compiled along the other versions.

These versions all have many small differences between them. Some places were improved, some bugs were patched. Supporting all these versions meant identifying each of those small changes.

Zelda: Link's Awakening Title screen in Japanese Zelda: Link's Awakening Title screen menu in English
Some changes are obvious, like the Title screen between languages.
But other patches are much more subtle.

Moreover, the version are not linear: some patches applied to the Japanese 1.1 and 1.2 versions are not present in other languages’ 1.1 and 1.2 releases.

Fortunately, Xkeeper took some time to research and document these patches: when they were written, what they do. The resulting matrix accurately the complexity of the actual revisions:

|       -       | JP 1.0 | JP 1.1 | JP 1.2 | US 1.0 | US 1.1 | US 1.2 | FR 1.0 | FR 1.1 | DE 1.0 | DE 1.1 |
| `__PATCH_0__` |        |  Yes   |  Yes   |        |  Yes   |  Yes   |  Yes   |  Yes   |  Yes   |  Yes   |
| `__PATCH_1__` |        |        |        |        |        |        |  Yes   |  Yes   |  Yes   |  Yes   |
| `__PATCH_2__` |        |  Yes   |  Yes   |        |        |  Yes   |  Yes   |  Yes   |  Yes   |  Yes   |
| `__PATCH_3__` |        |  Yes   |  Yes   |        |  Yes   |  Yes   |        |        |        |        |
| `__PATCH_4__` |        |        |  Yes   |        |        |  Yes   |        |  Yes   |        |  Yes   |
| `__PATCH_5__` |        |        |        |        |        |        |        |        |  Yes   |  Yes   |
| `__PATCH_6__` |  Yes   |  Yes   |  Yes   |        |        |        |        |        |        |        |
| `__PATCH_7__` |        |        |        |        |        |        |  Yes   |  Yes   |        |        |
| `__PATCH_8__` |        |  Yes   |  Yes   |        |        |        |        |        |        |        |
| `__PATCH_9__` |  Yes   |  Yes   |  Yes   |        |        |        |        |        |  Yes   |  Yes   |
| `__PATCH_A__` |    1   |    1   |    1   |        |        |        |        |        |    2   |    2   |
| `__PATCH_B__` |    1   |    1   |    1   |        |        |        |    2   |    2   |    1   |    1   |
| `__PATCH_C__` |        |        |        |  Yes   |  Yes   |  Yes   |        |        |        |        |

Read the full patches notes to get a idea of what each patch does.

Also, people at The Cutting Room Floor have been documenting the version differences for many years now. So now it’s all a matter of matching the differences in the code to the observable behavior changes.

With this massive work, the ZLADX disassembly can now build ten different revisions of the game, with exact byte-for-byte compatibility.

🧩 Fixing the spritesheets

Sprites modding has been a feature of the ZLADX modding community for a long time. Popular randomizers like Z4R or LADXR allow you to customize the characters visuals by replacing the spritesheets.

However, until now, the spritesheets in the disassembly were not easy to edit. Actually, they were in a sorry state.

Well, that’s not all bad: spritesheets were stored as PNG files, which makes them easy to view, and automatically converted to the Game Boy 2bpp format at compile-time. But many things were confusing in those PNG files. Here’s for instance how the first Link’s sprites appeared in the disassembly:

A sample spritesheet, with anything hardly recognizable
This raw dump of the sprites to a PNG file is not very clear.

It hard to see anything. That’s because this is a raw conversion of the in-ROM 2bpp tiles format to a PNG file. No other conversion is made, which causes the picture to be hard to read.

There are two things missing here.

First, the colors are wrong. Or, more precisely, the grayscale is wrong. When running on a Game Boy Color, the colors are applied at runtime, by matching each tile with a separately-defined color palette. So even on the Game Boy Color, graphics remain stored as grayscale, with 4 possible gray values.

But on the picture above, what should be rendered as the blackest gray value is instead rendered as white. And other grays are wrong to.

Contributor @AriaHiro64 found a fix for this: by tweaking the PNG file to reorder the indexed colors table, they were able to fix the grayscale values – while still retaining compatibility with the tool that transform these PNG files into 2bpp files at compile-time.

The same spritesheet with inverted colors, which makes things slightly easier to see
Proper color indexation already makes it more legible.
Still looks like a puzzle though.

Now it’s easier to see the other missing element: the tiles are not ordered in the most natural way.

This is because on the Game Boy, sprites can be either a single tile (8×8 px) or two tiles (8×16 px). And on Link’s Awakening, most characters made of sprites are at least 16×16 px – that is, each character is composed of two 8×16 sprites stitched together vertically.

So tiles for sprites often differ from tiles used to store background maps. Tiles for background maps are usually stored horizontally, from left to right, as:


So the conversion of background map tiles from 2bpp tilesheets to PNG is straightforward.

But tiles for sprites are usually stored vertically, from top to bottom, as:


So naïvely converting a spritesheet to a PNG file yields tiles ordered as 1️⃣ 3️⃣ 2️⃣ 4️⃣, which will look wrong, exactly like on the picture above.

To solve this, we have to go through a process named interleaving: when extracting the original tiles to a PNG file, we fix the tiles ordering by de-interleaving them. The resulting PNG file then has the tiles in the proper order.

And at compile-time, when transforming the PNG files to to the native 2bpp format, the same Python script interleaves the tiles, back to the original representation.

The same spritesheet inverted *and* interleaved, which makes all sprites appear clearly
When properly de-interleaved, Link’s sprites appear in their full glory.

To make this process easier to automate, a simple Make rule specifies that all PNG files prefixed with oam_ are automatically inverted and interleaved at compile-time.

So thanks to these conversion steps, now all spritesheets of the game can be easily browsed and edited. Have a look!

🏞 Decoding the tilemaps

A few months ago, in the disassembly source code, background tilemaps were all stored sequentially in a single ASM file.

This was suboptimal for many reasons:

  1. The tilemaps were not named, which made identifying a tilemap difficult;
  2. The ASM file format could not be imported into a tilemap editor;
  3. The tilemaps are stored encoded, and it was difficult to write a tool to decode a single tilemap to a format readable by a tilemap editor.

But since June, the situation greatly improved. First, Daid wrote a tool to parse the data format used by the tilemaps, and decode the data as readable instructions.

Then another PR identified and named the tilemaps. And last, all tilemaps are now exported them as individual binary files. So you can simply browse the tilemaps, and peach.tilemap.encoded will contain the data you expect.

Having separate files also makes easier to compare the differences between the successive revisions of the game. Before, all tilemaps were stored for each revision. But now only the tilemaps that actually differ from revision to revision are stored (usually mostly the file menus, because they include text that had to be localized).

A screenshot of Tilemap Studio editing a decoded Link's Awakening tilemap
Tilemaps can now be edited graphically using standard tools, like Tilemap Studio.

Why storing the tilemaps encoded?

Ideally, the tilemaps would be stored in a decoded, easily manipulable format (that is, as a raw sequence of tile identifiers). And at compile-time, they would be re-encoded into the format expected by the game engine.

But unfortunately, when developing the original game, the encoded tilemaps were not machine-generated from decoded files. Instead they were hand-written by the original developers. So if we used an automated tool to encode the tilemaps, we wouldn’t get exactly the same result than the hand-written encoding: it would be functionally similar, and produce the same tilemaps, but the exact bytes wouldn’t be the same. Which means we would no longer have a byte-for-byte identical ROM.

Instead, the files are stored in the original encoded format. And to made them easier to edit, the disassembly now includes a command-line tool to decode the tilemaps to the raw binary format suitable for import into tilemap editors.

Initializing the fishing minigame

On a smaller note, Daid documented the initialization values used by the fishing minigame.

Did you ever want to build a custom version of the game with only the bigger fishes? Now is your chance!

Link's Awakening modded fishing game, with only the bigger fishes
Wow, fishes in this pond sure must be well-fed…

What’s next?

Of course many more improvements were done in the past months, much more than what is presented there.

And as for next steps, although the tilemap values are now decoded, the tilemap attributes are not. As the attributes associate a tile to a color palette, that means editing the tilemap colors is still harder than it should be. Fortunately, the tilemap attributes are stored in the same format than the tilemap values, so writing a tool to decode them should be straightforward.

Also the high-level engine documentation is still evolving. It started as an incomplete description of the various systems of Link’s Awakening game engine, but is becoming more and more fleshed out. Many topics are still waiting to be explained though.

Want to read more? Read the other articles of this series, discover more of Link’s Awakening code, or join the discussion on Discord.

Achieving partial translucency on the Game Boy Color

This article is part of an ongoing “Disassembling Link’s Awakening” series, where I attempt to gain some understanding on how special effects were implemented in this game.

Neither the original Game Boy or the upgraded Game Boy Color had hardware support for partial translucency. This was always achieved with various hacks and workaround.

Let’s have a look at two of the most elaborated examples of these techniques, as showcased on the Title screen and End credits of Zelda Link’s Awakening.

Progressive fade-in on the title screen

On the title screen, after the main title appears, the “DX” logo appears with a nice, smooth fade.

Link's Awakening Title screen

But wait, the GBC doesn’t have variable opacity values. A sprite pixel can be fully transparent, or fully opaque – but there’s no “fade opacity from 0 to 1”.

So how is this gradual fading effect done?

The trick is that, instead of updating the opacity, the game updates the palette of the “DX” logo. Each frame, the “DX” palette is changed to go gradually from the sky shade to the logo shade. Nice.

The palettes of the “DX” sprite during the fade-in

But wait! That shouldn’t work. The “DX” logo doesn’t sit over a flat-colored surface: it overlaps both the sky and the clouds. So how could changing the palette affect both the “sky → logo” and “clouds → logo” color progression?

Indeed, that’s an issue. And to solve it, the “DX” logo is split into two parts: one set of sprites for the part overlapping the sky, and one set for the part overlapping the clouds. Each part has its own palette, with its own gradual progression.

First set of sprites

Second set of sprites

Overall, a lot of effort for a small effect, that looks really easy to perform on modern hardware nowadays. But on the GBC, it actually required a good lot of tricks.

Secret ending: fading Marin in and out

When beating the game without dying once, a portrait of Marin is displayed after the end credits.


We can see the same kind of transparency effects than on the Title screen:

  1. Marin’s face fades in, and displays many colors.
  2. Marin’s face remains half-translucent for a while
  3. A seagull fades in
  4. Marin’s face fades out

That’s a lot to unpack here. Each of these effects is not easily doable using the GBC hardware.

1. Marin’s fades in

The fade-in uses the same palette-update trick than the “DX” logo on the title screen: the sprite palettes are updated every few frames, and move from blue to the actual portrait colors.


However, notice that the portrait is quite colorful. Some 8x8 areas even display up to 6 different colors at the same time, like Marin’s medallion:

There are more colors on this single tile than the Game Boy Color usually allows.

A standard sprite can only display 4 colors on the GBC (that is, 3 actual colors + a transparent one). So how is it done?

The answer is that the portrait is split into two layers of sprites, each with their own palette.

First layer of Marin's sprites
Layer 1

Second layer of Marin's sprites
Layer 2

It is then composited to get the full portrait.

Composited Marin's portrait
The two layers one on top of each other

This solves the “Many different colors” issue: different layers of 3-colors sprites are overlaid on top of each other.

(However, unlike the DX title logo, the difference of fading between the sky and the clouds is not accounted for. The overlapped cloud areas become blue at the beginning of the animation.)

2. Marin’s face remain half-translucent for a while

Neither the GB or the GBC have half-transparent rendering – but they have the latency of the LCD screen. So the good old 50% transparency trick is used: displaying the portrait only every other frame.

Marin's portrait slowed down
The blinking effect, slowed down.

On full speed, the latency of the original screen then creates the half-transparency effect.

3. A seagull fades in

The seagull fades in gradually, on the middle of Marin’s face.

Same trick: this is done by gradually updating the palettes of the seagull sprite.

4. Marin’s face fades out

Again, Marin’s portrait palettes are gradually shifted to becoming blue again.

And that’s it. A neat composition of several tricks, for a good and moving ending.

So there it is: some of the tricks used to unlock partial translucency on the Game Boy Color hardware. Of course these techniques are quite time-consuming, and thus are only used during a handful of key moments.

A few years later, the hardware translucency support of the Game Boy Advance will make these kind of effects much easier to achieve.

Want to read more? Read the other articles of this series, discover more of Link’s Awakening code, or join the discussion on Discord.

An animated Firewatch wallpaper for macOS

A while ago Daka made a series of great Firewatch wallpapers. Then dynamicwallpaper.clup user Jagoba turned them into a nice macOS dynamic wallpaper for macOS.

However the previous dynamic wallpaper was timed to the clock – and so didn’t account for the current sunrise and sunset time at your location. Which means that during winter, the wallpaper would still be a bright blue while outside the night was already dark.

So I edited the wallpaper metadata, to make them dependant on the sun position rather than the time of the day.

Finding out sun coordinates

On macOS, making the wallpaper dependant on the sun position means adding fictive sun altitude and azimuth to the images.

Rather than computing the sun angle and elevation ourselves, an easiest way is to set the time and GPS position of the pictures. Wallpaper-makers can then automatically convert this time+location data into the sun angle and elevation.

First, the location. I chose the wallpapers to be in Shoshone National Forest, for obvious reasons.

Then, the time. The game takes place during summer 1989, so it seems like a good fit. I’ve arbitrarily chosen the 3rd of July 1989, which roughly matches the first days of the game.

Now we need to time the different wallpapers. For the middle of the night, this is easy enough: jut set the time to midnight. But for the various sunrise and sunset pictures, this is harder to define.

Which leaves us with interesting questions. For instance: at what time was the first light of dawn visible in Shoshone Forest on the 3rd of July 1989?

Turns out there are softwares that can tell the light of the day at a given time and location: planetariums. For instance, by using the atmosphere simulation of, we can get a rough idea of the time of each picture.

Here are the exact timing used for the wallpapers:

macOS wallpaper engine will then use these informations to display the correct picture to you, depending on the season at your place. A little trickery though: if the middle-of-the-day picture is timed at 12:00, it’s sun elevation will be very high. Which means that during winter, when the sun is low, the wallpaper engine will simply skip this picture. To make sure the middle-of-the-day will be displayed even during winter, I inserted instead two pictures, timed early in the morning and late in the afternoon.

End result

So here you are! A nice Firewatch dynamic wallpaper for macOS, adjusted to the sun’s position. Enjoy!

EDIT: I fixed some timing issues, and re-uploaded a new version under the name “Firewatch Sun”. It makes sure the ‘Sunset’ variant doesn’t start displaying too soon in the afternoon, even during winter.

Experiments with Gradual Typing in Ruby – Part 1

I recently started toying with Gradual typing in Ruby.

Of course, Ruby is already typed: messages are dispatched to objects depending on their type. That’s dynamic typing, enforced at runtime.

But for larger programs, it can be useful to have some static type-checking, that can be enforced by a type-checker without running the whole program.

Enter type annotations. By adding some explicit informations about the expected types in our program, a type-checker will be able to catch some errors using a static analyzer.

What does it look like?

Gradual typing is not standardized in Ruby yet (although some efforts are ongoing). So there are different tools available. Currently, the best way to add gradual typing to Ruby programs seems to be Sorbet.

Here’s a simple Sorbet example, using an existing Ruby function:

def to_hex(i)
  "%x" % i

We can add type informations to this function using the sig function decorator:

extend T::Sig

sig {params(i: Integer).returns(String)}
def to_hex(i)
  "%x" % i

Granted, the syntax is a bit weird. But it has the merit of being valid Ruby code, which allows it to be accepted by the standard Ruby parser without modifications. And like many syntaxes that seem unusual at first, our eyes quickly get used to it (hi, Objective-C square brackets).

Letting “Gradual” shine

Now, the neat thing with gradual typing is that you don’t have to provide type informations everywhere. This is useful in many ways.

First, you may start adding type checks to an existing codebase. In that case, declaring all types from the start can be a daunting task. Fortunately, in the absence of types, the type-checker will consider that we know what you’re doing. Which means we can start adding a few types here and there, and already have useful type-checks – but the parts of the program which are still type-annotations-free will not results in warnings or errors.

Second, a lot of Ruby elegance and fun comes from its dynamic nature. Sometimes the most elegant way to express some code is to use dynamic method resolution, or other dynamic-oriented constructs which cannot be type-checked. And this is okay! In that case, gradual typing means you have an escape-hatch: as types are not mandatory, they just won’t be type-checked. We can get the benefits of type-checking for 95% of the code, and still use neat dynamic features in the remaining 5%.

And last, at times it can be useful to just experiment and prototype some code quickly, to see how the structure would look. In these cases, you won’t have to fight your way through types: you can just omit type annotations, and quickly let the code flow without thinking too much about production-ready reliability.

Experimenting with a standalone Ruby script

I’ve never used Sorbet before, so I wanted to start small, and get used to the type system.

Fortunately, the Sorbet website provides an online playground: just type in some Ruby code, add some type annotations, and the type checker will start telling you what’s right and wrong with the types you provided. Neat.

I used a standalone Ruby script I wrote some times ago. This script reads assembly source code and debug symbols from the local filesystem, and infers from this the probable location of more debug symbols. Here the initial version of the script, and the final version after finishing adding types.

When I copy-pasted the script, without adding anything yet, Sorbet immediately told me about two errors: <function> is not available on NilClass (

Wow: it detected that in two different places, my code was sending a message to a potentially nil object. And gave me an URL to learn more about the issue.

How to fix this? I followed the URL, and a well-written document explained me that I could either:

So the two reported errors were easy to fix. I was quite impressed that Sorbet found two relevant mistakes without even starting to add types. And even more impressed that it not only does nil-checks, but also type propagation (that is, when some code checks if a value is nil, Sorbet considers that after this point the variable can not longer be nil).

Adding types

After this, I started adding type annotations to a single method (a constructor). That was easy enough: just a matter of adding the correct sig {…} incantation.

But right after that, Sorbet told me about a new error: my signature stated that the method argument was a String, but elsewhere I was calling the constructor with a T.Nilable(String) – that is, an object that may be nil. Interesting. Like before, to fix it, I had to add the proper nil check before calling the constructor.

I then gradually added type annotations to more methods, and found it almost fun. I had the feeling that I was strengthening my program, and uncovering the hidden assumptions that had been there before.

All of this went rather smoothly (except having to convert Ruby Structs, unsupported by Sorbet, into T::Structs). The weird syntax quickly became bearable, and eventually even read like being a part of the documentation.


In the end, this even led me to write better code. For instance, consider this function :

class Address
  def self.from_string(str)
    bank, offset = str.split(':'), offset.to_i(16))

It extracts the two components of a semicolon-separated string – like 03:4A2F.

When adding type annotations, Sorbet initially told me “Hey, offset.to_i(16) is not valid on NilClass”. Because of course, it detected that if the input string is badly formatted, offset may be nil.

So I quickly wrapped the value in T.must(…), to silent the warning away. After all, there’s not so much we can do to prevent badly-formatted input; crashing at runtime seems a sensible option.

class Address
  sig {params(str: String).returns(Address)}
  def self.from_string(str)
    bank, offset = str.split(':'), T.must(offset).to_i(16))

But wait, there’s better than crashing at runtime: and that’s “crashing at runtime with a meaningful error message”. What if we rely on nil-propagation to write instead:

class Address
  sig {params(str: String).returns(Address)}
  def self.from_string(str)
    bank, offset = str.split(':')
    raise "Invalid address format" if bank.nil? || offset.nil?, offset.to_i(16))

Nice: a bad input now gives us a readable error message. And we can even remove the T.must checks, because, thanks to nil-propagation, Sorbet is now sure that offset.to_i(16) is not called on nil.

So far

After toying with the Sorbet’ playground, here are my first impressions:

The good

The bad

The ugly

What’s next

For now I haven’t tried to type-check a program locally, nor to type-check code that relies on external gems.

So my next step is probably to add some minimal type-checking to a small Rails app, and see how Sorbet’s tooling deals with the many dynamic constructs of the framework.