This perl script unpacks the binary savegame file into an editable text file, with most of the interesting parts decoded to be amenable to changes, and can repack the text file into a working savegame. The pack/unpack capability was important, and I set up regression tests to unpack and repack all available savegame files to verify that I had not lost the format. The savegame files appear to have been designed to be somewhat unfriendly to this process, as variable length strings appear at the front and back of the file. [ Thus nothing in the large fixed length center section of the file can be referred to with a simple offset from the front or back of the file. ]
The interesting parts of this project were in figuring out what referred to what, and how to flexibly design the text file.
Staring at the savegame file in a hex editor, I came to see that it was in blocks of mostly repeating units; one particular section had 128 similar looking chunks of data, then the data would change, indicating a new section. I named the sections, initially with letters, and later with descriptive names like Ship and City. Other sections turned out to be maps on a 462x293 standard.
The simplest decode was to declare a section to be a particular length in bytes, then hex decode. The text file was designed to be line based for use with grep. Recursively I would break the section down... instead of calling it 1 section of 1024 bytes, I might decide that the section consists of 128 lines of 8 bytes. Then each of those lines might be broken into sublines as the meanings of the different bytes became clear. The key was to make the text file usable while my decoding was evolving. I used a system for the decoded lines sort of like the Dewey Decimal system - adding detail to the decoding in one section would not change the numbering in any other section.
To figure out what different bytes could do, simply editing a few bytes was not very efficient; if a change affected the number of crew on a ship in a far part of the Caribbean, I would never be able to detect it. The method I developed was to use a lot of splicing... replacing lines in one savegame file with those from another. Splicing over one section would move my ship from the location in the original savegame file into the location in a donor savegame file. Splicing nearby sections changed the nearby ships. To make this more efficient (particularly for obscure differences), I added a binary search splicing option to the code - given two savegame files that differed in one feature that I wanted to understand, I could create a range of savegame files splicing the disparate lines from one to the other in. After opening each savegame file in turn in the Pirates! game, I could rerun the script marking which tests were positive and which were negative, narrowing the search exponentially. This uncovered a few interesting cases where the data was held not in one place in the savegame file, but in several places.
The decoded text file included all of the same information as the original (to enable packing) with explanations, but was not a line-by-line translation. In particular, maps were kept marking particular features. For a human editing the file, it was much more useful for the text file to list the features by their coordinates, rather than in map form. Of course, maps are also good to see visually, so I provided .bmp file outputs as well.