Page 1 of 1

ZZT File Format for Dummies

Posted: Wed Feb 13, 2013 5:40 pm
by plasticsurgeon
This is hard to phrase considering I'm a novice, but:

How exactly are the location/values supposed to be interpreted? I pretty much just want to create a basic .sav game editor for educational purposes(then possibly expand upon it after understanding these basic concepts). Are there any more indepth guides out there?

Do I load the file and look for specific ranges then convert those values? Do I load it as if it were a text file? Are there any specific ways to look for the locations, like a "hex range" or "hex value"? I guess I understand how it would work just not sure how to access these puzzle pieces.

Pretty scattered, I know, but any help would be appreciated.

Re: ZZT File Format for Dummies

Posted: Wed Feb 13, 2013 6:15 pm
by Commodore
First off, if you're not looking at this version of the file format, you should.

The offsets tell you how many bytes from the beginning of the program or section (looking at it in a hex editor) the value is found, then it tells you what sort of value it is.

Example: at the begining we have:

0 SINT16 WorldType
2 SINT16 NumBoards

The begining of the program + 0 bytes is the value for the world type, Its a signed sixteen bit interger, so it take up two bytes, the offset for the next value is then 2.

Some values have a number in brackets:

8 UINT8[7] PlayerKeys

This means that there are 7 unsigned 8-bit integers in a row.

After the world header, the offsets go back to 0 for every board, since the boards can be of different lengths.

Re: ZZT File Format for Dummies

Posted: Thu Feb 14, 2013 12:12 am
by plasticsurgeon
I've made some progress, but what I don't quite get is the taking up 2 bytes thing. The difference between ZZT and SuperZZT seems to be FF and FE, while the "1" byte will always be FF. Then we get to the number of boards, when I just read the value at 2 for this test world I used, it said it had 2 boards(which is correct), and that's without reading the value at 3.

Are the values at 1 and 3 etc. really needed?

Re: ZZT File Format for Dummies

Posted: Thu Feb 14, 2013 5:05 am
by Saxxon
Not necessarily. But I wouldn't say it's good practice to ignore half the bits of any value. The values you read typically don't go above the values that one byte can represent. But when you get to such things as ammo, gems, etc.. you'll definitely see some larger values.

Re: ZZT File Format for Dummies

Posted: Thu Feb 14, 2013 5:28 am
by Spectere
There really isn't a good reason for the additional byte itself. Given ZZT's internal limitations, a single byte would be more than enough. There is something that you will need to know regarding how numeric variables are stored if you want to be able to consistently read to and write from them successfully. This will explain why that 16-bit value appears to be saved backwards (0x0200 instead of 0x0002).

The reason this is that the Intel-compatible processors are what is referred to as a little-endian. This means that the least significant byte is stored first in memory (and, indirectly, in files). It's a bit harder to demonstrate endianness with a small number like 2, so let's use 0x1234 instead (with "0x" indicating that the following number is expressed in hexadecimal).

0x1234 is a 16-bit value. If you split it into two bytes, it would consist of 0x12 and 0x34. These are, respectably, the most significant byte and the least significant byte. The "significance" of the value is purely based on place value (for example, in decimal, the 100's place would considered to be more significant than the 1's place).

However, if you were to create a 16-bit variable with the contents 0x1234, you would be surprised to see that saving that variable to a file and opening it in a hex editor would reveal the file's contents to be 0x3412. The two bytes are reversed! If you were to read the file back into your program, you'd get the expected 0x1234. This is caused by the Intel architecture storing the contents of the variable "backwards" in memory. The same thing happens with larger values as well: 0x12345678 becomes 0x78563412, for example. This is why the number of boards in your example world, 0x0002, is being saved as 0x0200, and why that extra byte doesn't appear to be in the correct place.

This a reason why the data types need to match when you're reading/writing to/from a file. If ZZT did support enough boards to require a 16-bit value and you were only reading the first byte, your program would break when the variable hit 0x0100 (since it would be stored in the file as 0x0001, your program would read the first byte and think that there were no boards in the file).

Regarding your other question:
plasticsurgeon wrote:Are there any specific ways to look for the locations, like a "hex range" or "hex value"?
When it comes to undocumented save formats, prepare to do a ton of testing and experimentation. Save files generally contain a snapshot of some of the game's state (or, in ZZT and MegaZeux's case, the entire world's state), so the best way to figure them out is to save at different times, change something, save again, and compare the two.

Here's a rough set of guidelines that you can follow to find the value that "Health" is stored in with ZZT, and I encourage you to follow along so that you can get an idea of the process:

First of all, start up ZZT, load up TOWN, and press P. Take one step forward, press S, and name the save file (I called mine TOWNBASE.SAV). Since this is a new game, our health is set to 100. Next, keep ZZT open and load the save file in your hex editor.

The first thing that we can do is get a feel for the maximum size of the value. Since we know that the player's health in ZZT can exceed 255, we can assume that the value is most likely at least 16-bits in length (pedantic: it is possible to have values with an arbitrary precision by stuffing values -- i.e. combining a 4-bit and 12-bit value into one 16-bit word -- but that's very rare and is beyond the scope of this exercise). If we search only for 16-bit words, we get 3 results for the search.

Now, jump back into ZZT. Enter the health cheat (?HEALTH) a few times (I did it until I had 300 health, though you can do it with any number as long as you know what it is) and save the file under a different name (I used TOWNSAV2.SAV). Open the new save in your hex editor and search for our new health value -- 300. After doing that, I came up with one result, at offset 0x0F. If I change that to something else -- say, 10000 -- and load that file in ZZT, I'll have 10000 health.

In practice, it's rarely this easy. In some cases, you'll have to do multiple checks and compare offsets. Sometimes you'll have to be a bit more clever (like in SuperZZT, where you can't see your numeric health value) and compare which bytes changed between multiple files in a given way (for instance, if you save a file, get hurt, save a file, then give yourself the health cheat, you'll be looking for a value that drops in file 2, then goes back up in file 3).

Other times, you simply can't shake the additional values and have to just plug in values. If the game goes belly up, refuses to load your file, or you don't get the expected result, revert and try another.

Finally, sometimes you can rely on observation, particular when it comes to data files. For instance, just using some observation and a little bit of manual validation, it's fairly easy to reverse engineer the file format for the GXL file that is used in the DOS version of Oregon Trail without even going into the program.

That being said, encrypting save files seems to be a popular thing to do now, so many of these tricks probably won't work on the latest AAA titles. Still, it is a lot of fun to tear apart older games and see what makes them tick, and possibly do a few little mods to assert your dominance. :)

I hope this little novel helps. Let me know if you have any questions!

Re: ZZT File Format for Dummies

Posted: Thu Feb 14, 2013 7:56 am
by Commodore
I think much of what spectre says is spot on, but really just look at the file format. It's says that health is 15($F) bytes from 0. Zero is relative, but here it means the beginning of the file. The locations of the important values you want to edit to cheat in a save file will not change (health, flags set, etc.)

Once the level data starts, that's where it get tricky because the size of each board is not constant. The object info, playerx/y, et cetera, are stored after this variable amount of data. In a hex editor it might be a simpler task to see the beginnings and ends of boards (it should show the ascii names) but from a programming prospective, the algorithm would need to figure out when the Run Length Encoded tile data ends. That is, decoding it until it fills the size of the board (given in the file format).

Also I'm left wondering if the excessive variable types are a remnant of the original pascal.

Re: ZZT File Format for Dummies

Posted: Thu Feb 14, 2013 8:19 am
by Saxxon
Commodore wrote:Also I'm left wondering if the excessive variable types are a remnant of the original pascal.
The types used are really just a memory dump (the save routines literally copy the world memory.) Tim decided to use a 16-bit value for board count. I really have no idea why.