Updating an Old Macintosh Application
, Classic Macintosh, Programming
“Sparks” is the name of a tech demo (or game) I was working on about 20 years ago. It’s supposed to be like Asteroids (1979), or maybe Escape Velocity (1996). The camera follows your ship as you fly around in space. You can shoot a gun, but there’s only one thing to shoot at: a green circle at the center of the world, which is labeled “planet” in the source code.
Was it ever intended to be a complete game? Probably not. I just enjoyed programming it and making cool things appear on screen.
Digging Up the Source Code
I meticulously kept copies of every version of the source code. Apparently, I had good version control habits even when I was younger. Each time I made a change to the code, rather than committing the change, I made a complete copy of the source tree. The folder names for each version described the change. Here’s what that looks like:
$ ls -1 '00 - Debug Only' '01 - Graphics' '02 - Input' '03 - Entities + Firing' '04 - Depth Change' '05 - Color Palette' '06 - Sparks' '07 - Full Screen + Physics' '08 - Stars + Parallax' '09 - Better Stars + Explosions' '10 - Polygons' '11 - Gourad Shading' '12 - Pretty Interface' '13 - Last Classic Mac Version' Sparks
In 2010 I copied these folders into a Git repo for archiving and converted everything to use Unix line endings (line feeds).
Bringing Source Code Back to the Macintosh
It’s easy enough to manually copy source files into Basilisk II or my Quadra 650, but to make things even easier, I wrote a program called SyncFiles. SyncFiles is an MPW tool that copies files to and from a classic Macintosh environment. Files in the Macintosh environment are encoded using the Macintosh character encoding (Mac OS Roman) and use carriage returns, files in the external environment are encoded using UTF-8 and use line feeds.
Next, there is a file that contains resources. In order to check this file into Git, I had moved the resource fork to the data fork, and now I needed to do the reverse. An easy way to do this is to wrap the file in a MacBinary file, copy the MacBinary file to the classic Mac, and extract it using MacBinary. The MacBinary format is extremely simple and it didn’t take long to write a Python script to do the conversion: rsrc2macbinary.py.
I may want to extend SyncFiles to copy resoucre forks this way, but for now, the one-off Python script is simpler.
Finally, I write a makefile for my game. In order to use the resource file, I wrote a trivial Rez file:
Include "Resources.rsrc";
I add some missing #include
directives, add a missing
declaration for QDGlobals qd;
, and it compiles!
Out of Memory
The program does not run.
That is not a descriptive error message. Supposedly, error type 25 indicates an out of memory condition?
I check the Get Info dialog in the Finder:
The amount of memory, 384 KB, is surely not enough.
I should add a SIZE
resource to the application.
The way I’d like to do this is using Rez, rather than ResEdit,
so I’ll first convert some of the resources to Rez.
Running DeRez
DeRez converts a resource fork back into Rez format.
It’s kind of magical.
To run it, you give it the resource fork you want to convert and a list
of Rez source files containing resource type definitions.
I had a file containing ALRT
and DITL
resources, so I passed Dialogs.r as an argument to DeRez.
Be sure to redirect the output to a file, because it can be large.
DeRez Resources.rsrc {RIncludes}Dialogs.r > DeRezOut.r
Here is one of the resources in the output: it’s a dialog box which asks the user if they want to change the monitor depth. Note that it’s pretty easy to clean this up and check it into source control, rather than dealing with large binary blobs.
resource 'DITL' (400) {
{ /* array DITLarray: 4 elements */
/* [1] */
{74, 303, 94, 378},
Button {
enabled,
"Change"
},
/* [2] */
{74, 205, 94, 280},
Button {
enabled,
"Quit"
},
/* [3] */
{13, 78, 61, 378},
StaticText {
disabled,
"This application can only run under 256 "
"colors. Would you like them set to 256 "
"now? They will be set back when the app"
"lication quits. "
},
/* [4] */
{13, 23, 45, 55},
Icon {
disabled,
2
}
}
};
Resources like PICT
should probably remain in binary format.
A Tale of Two Line Endings
I’m spoiling the result here… but I modified my Rez file so it would combine existing resources with resources defined in Rez, and I got all sorts of errors. Here’s what the beginning of my Rez file looked like:
#include "ResourceList.h"
#include "Dialogs.r"
When I built the program, I got the following errors:
# 10:49:28 AM ----- Build of Sparks.68K.
# 10:49:28 AM ----- Analyzing dependencies.
# 10:49:28 AM ----- Executing build commands.
Rez -o Sparks.68k -a Resources.r
File "Resources.r"; Line 1; ### Rez - Expected identifier, but got ‘#’
File "Resources.r"; ### Rez - Since errors occured, Sparks.68k's resource fork was not completely updated.
### MPW Shell - Execution of Sparks.68K.makeout terminated.
### MPW Shell - Execution of BuildProgram terminated.
Frankly, this was driving me insane and I could not figure out
what the problem was.
I deleted the rest of the file besides the two #include
directives, and the error remained.
If I removed either one of the #include
directives, the error
would disappear.
What was happening?
It turns out that I had given the Rez file Unix-style (line feed) line endings, and the Rez tool only recognizes Macintosh (carriage return) line endings. Fortunately, BBEdit saves the day.
Note that only some tools require Macintosh line endings. The C and C++ compilers accept either Macintosh or Unix line endings. When you edit a file with MPW, you won’t even notice what line endings the file uses—and MPW will preserve the original line endings when you save the file. It’s only certain tools, such as Rez, which require carriage returns.
Now, why did my files have carriage returns? I had managed to correctly convert UTF-8 to the Macintosh encoding, so why would something as simple as carriage returns trip me up? Here’s the original code I had written for converting line endings from line feeds to carriage returns:
if (c == '\n') {
c = '\r';
}
Do you see the error? Here’s the fix:
if (c == 0x0a) {
c = 0x0d;
}
It turns out that when you compile a program using the MPW compiler,
then '\n'
is a carriage return, and '\r'
is a line feed.
I had completely forgotten about this.
It may be backwards from every other compiler I use, but it makes sense
that this is how a classic Macintosh compiler encode strings.
Taking a Break
That’s enough for now.