wezm.net/v2/content/posts/2023/derez/index.md
2023-03-31 13:43:52 +10:00

8.6 KiB
Raw Blame History

+++ title = "How to use DeRez" date = 2023-03-20T12:44:38+10:00

#[extra] #updated = 2023-03-01T21:54:06+10:00 +++

After my post on trying to run Rust on Classic Mac OS post I continued trying to find a modern language that I can use to build classic Mac OS software. I've had some success with Nim and built a little temperature converter application. As part of this I wanted to be able to use ResEdit to edit the layout of the dialog. The problem was that I need a way to convert the modified resources back into the textual representation used in the source code. In this post I describe how I did this with DeRez.

{{ video(video="posts/2023/derez/mac-nim-temp.mp4", height=227, poster="png", preload="auto", alt="Video of the temperature converter application.", caption="Video of the temperature converter application.") }}

To build the temperature converter I started with the Dialog sample from Retro68, which looks like this:

{{ figure(image="posts/2023/derez/Dialog.png", link="posts/2023/derez/Dialog.png", pixelated=true, alt="TODO", caption="Dialog Sample") }}

I opened it up in ResEdit and edited the DITL (Dialog Template) resource to add the icon and temperature fields. I also added a new ICON resource and drew a little thermometer:

{{ figure(image="posts/2023/derez/DITL.png", link="posts/2023/derez/DITL.png", pixelated=true, alt="Screenshot of the ResEdit DITL editor editing the DITL resource for my temperature converter.", caption="Editing DITL resource in ResEdit") }}

With the changes made, I now wanted to convert the binary resources stored in the resource fork back into the textual format used in the source code. I believe the format is called Rez, here's a snippet of it:

resource 'DITL' (128) {
	{
		{ 190-10-20, 320-10-80, 190-10, 320-10 },
		Button { enabled, "Quit" };

		{ 190-10-20-5, 320-10-80-5, 190-10+5, 320-10+5 },
		UserItem { enabled };

		{ 10, 10, 30, 310 },
		StaticText { enabled, "Static Text Item" };

		{ 40, 10, 56, 310 },
		EditText { enabled, "Edit Text Item" };

		{ 70, 10, 86, 310 },
		CheckBox { enabled, "Check Box" };

		{ 90, 10, 106, 310 },
		RadioButton { enabled, "Radio 1" };

		{ 110, 10, 126, 310 },
		RadioButton { enabled, "Radio 2" };
	}
};

This turned out to be a bit of journey and the motivation for this blog post. As part of the Macintosh Programmers Workshop (MPW) theres is a tool called DeRez that does what I want. First up I had to work out how to operate MPW. It's an editable shell where you run commands with ⌘-Return. Once I worked that out I could run DeRez on my edited application but I only got the fullback hexadecimal representation of the resources, not the structured output I wanted:

data 'DITL' (128) {
	$"0007 0000 0000 00A0 00E6 00B4 0136 0404"            /* ....... .æ.´.6.. */
	$"5175 6974 0000 0000 009B 00E1 00B9 013B"            /* Quit.....›.á.¹.; */
	$"0000 0000 0000 0046 000A 005A 0136 0808"            /* .......F...Z.6.. */
	$"4865 6C6C 6F20 5E30 0000 0000 001E 000A"            /* Hello ^0........ */
	$"003E 002A A002 0597 0000 0000 0014 0032"            /* .>.* ..—.......2 */
	$"0024 007D 8807 4365 6C73 6975 7300 0000"            /* .$.}ˆ.Celsius... */
	$"0000 0014 00AA 0024 00F5 8809 4661 7265"            /* .....ª.$.õˆÆFare */
	$"6E68 6569 7400 0000 0000 0029 0036 0039"            /* nheit......).6.9 */
	$"0081 1009 4564 6974 2054 6578 7400 0000"            /* ..ÆEdit Text... */
	$"0000 002B 00AE 003B 00F9 1009 4564 6974"            /* ...+.®.;.ù.ÆEdit */
	$"2054 6578 7400"                                     /*  Text. */
};

Help DeRez in MPW didn't shed much light on the problem but after a lot of searching online I eventually found some extra details in the man page for DeRez shipped on Mac OS X. Specifically:

The type declarations for the standard Macintosh resources are contained in the Carbon.r resource header file, contained in the Carbon framework. You may use the ${RIncludes} shell environment variable to define a default path to resource header files. If you do not specify any type declaration files, DeRez produces data statements in hexadecimal form.

and

You can also specify resource description files containing type declarations. For each type declaration file on the command line, DeRez applies the following search rules:

  1. DeRez tries to open the file with the name specified as is.

  2. If rule 1 fails and the filename contains no colons or begins with a colon, DeRez appends the filename to each of the pathnames specified by the {RIncludes} environment variable and tries to open the file.

With this information I was able to construct a command that worked:

DeRez -i 'Macintosh HD:MPW-GM:Interfaces&Libraries:Interfaces:RIncludes:' "Macintosh HD:Retro68:Retro68App" Carbon.r

-i sets the include path for type declarations and Carbon.r tells it to use that file for resource descriptions. Running the command I was now rewarded with textual resources:

{{ figure(image="posts/2023/derez/MPW.png", link="posts/2023/derez/MPW.png", pixelated=true, alt="TODO", caption="DeRez output in MPW") }}

To get the text out of the VM I copied and pasted it into a new document in BBEdit (version 5.0) and saved it with Unix line endings to the Unix folder that SheepShaver shares with the host and with that I was able to update the resource file in my temperature converter project.

Honorable Mention

Whilst trying to work out how to do all this I was also reminded of Ninji's mpw-emu project (detailed write-up on their blog). It combines an emulator with implementations of library functions in order to be able to run MPW tools directly (outside a Mac OS emulator). It has gained support for DeRez so you can run DeRez directly on a host system like Linux.

I MacBinaried DeRez in SheepShaver and copied it to my Linux host. Then with a bit of fussing with mpw-emu Rust code I was able to run it:

$ mpw-emu ~/Documents/Classic\ Mac/Shared\ 2/DeRez.bin
[2023-03-20T02:11:07Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C
[2023-03-20T02:11:07Z ERROR stdio] Unimplemented format character: P
[2023-03-20T02:11:07Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4
### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - No filename to de-compile was specified.
### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - Usage: /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin resourceFile [-c] [-d name[=value]] [-e] [-i path] [-m n] [-noResolve [output | include]] [-only type[(id[:id])]] [-p] [-rd] [-s type[(id[:id])]] [-script japanese | tradChinese | simpChinese | korean] [-u name] [file…].

Amazing!

Unfortunately I don't think DeRez will work this way outside a macOS host. It needs to be able to read the resource fork of the application I edited with ResEdit and that is not preserved on Linux:

$ mpw-emu ~/Documents/Classic\ Mac/Shared\ 2/DeRez.bin Dialog.APPL
[2023-03-20T02:14:05Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C
[2023-03-20T02:14:05Z ERROR stdio] Unimplemented format character: P
[2023-03-20T02:14:05Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4
### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - The resource fork of "Dialog.APPL" is empty and uninitialized.

If you're on macOS I think that this would actually work. Although now I think about it Xcode ships (or at least used to) a native version of DeRez so now I'm not sure what Ninji's motivation for making it work in mpw-emu was. Perhaps it is possible to use on Linux somehow...