The Mysterious Case of Jzip Failing to Restore Tethered
Chapter the Last, in which a most curious scheme is revealed.
Ahem. Ladies, gentlemen. I have gathered you here in the drawing room to present a perfectly simple solution to an impossible puzzle. Inspector, have you locked the doors as I instructed? Very good. Now, then.
A game state is saved. After saving, the game still works. The savefile is confirmed to be correct, because it is identical to savefiles produced by another interpreter.
Next, the saved state is restored, and the game no longer works. Did the code for restoring a savefile misbehave? At a first glance, it would appear so.
But! If the story file is deviously modified, such that after restoring, the game state is immediately saved again, then the exact same, perfectly valid savefile is produced.
How, then, could the restore functionality be guilty? Put another way, how is it possible that the state of the game after restoring is both valid (because it can be saved correctly) and invalid (because the game crashes)?
(Dramatic puffing of cigar)
The answer to the conundrum is as baffling as it is simple. You see, inside this particular interpreter, there happens to be not one, but two copies of the game state. The copies are identical, with one exception: Every time the restore operation attempts to reconstruct them from a savefile, it performs a slight miscalculation. The error only affects one of the copies, but itās the one that is involved in the actual gameplay. The other copy, still immaculate, is only used when saving the game.
And that, ladies and gentlemen, is the entire secret of the Mysterious Case of Jzip Failing to Restore Tethered: Identical twins.
Inspector, be a good lad and escort this particularly nefarious bug to the station.
The curtain falls. Of course, I have exaggerated somewhat. There arenāt two full copies of the game state, but thereās one single detail that is represented in two different ways: The number of frames on the call stack. A variable called frame_count
is incremented whenever a stack frame is created, and decremented whenever one is destroyed. But during a save operation, the frame_count
variable isnāt used. Instead, the number of stack frames that are saved depends on the actual contents of the call stack, as deduced by following frame link pointers. When restoring a game, as I suggested, frame_count
is miscalculated. But this does not affect subsequent saves.
But if frame_count
isnāt used during saving, when does it get used at all? Elementary: It is used as the magic cookie returned by catch
, and supplied as a parameter to throw
. When a game has been played through from the beginning, catch
produces cookies based on the correct frame_count
, which is why the savefile is identical to one produced by another interpreter. After a restore, the contents of the stack are correct, but frame_count
is wrong. Throw
computes the difference between its parameter and the old frame_count
, gets the wrong answer, and quietly explodes. And, as weāve seen before, Inform games do not use throw and catch, which is why the bug has remained undetected until now.
The fix is quite simple:
quetzal.c:
403c403
< for ( fp = STACK_SIZE - 1, frame_count = 0; currlen > 0; currlen -= 8 )
---
> for ( fp = STACK_SIZE - 1, frame_count = 0; currlen > 0; currlen -= 8, frame_count++ )