Features


Macintosh-to-Windows Resource Conversion

Jeff Heaton

Converting data formats between systems is hard enough, particularly between a Mac and a PC. This tool helps you with the other half of the job.


Introduction

Both the Windows and Macintosh platforms use resources to store graphical user interface objects, such as dialog boxes, pictures, icons and sounds. Although the same sort of resource data is stored on both platforms, the underlying file structure is very different between the two systems. As a result, resource file conversion becomes an immediate challenge when porting an application from Macintosh to Windows (or vise versa).

This article presents a Macintosh-to-Windows resource conversion program. This program will traverse the Macintosh resource file and extract the binary data for every translatable dialog resource. The program will then translate this extracted data to the equivalent windows resource and store it in the standard Windows resource script (.RC) file.

Macbinary Format

Under most operating systems, including Windows, an executable file is stored in exactly the same format as a data file — as a flat array of numbers. This is not the case in the Macintosh operating system. On the Mac, each file is a "compound file," which is really two files, each of which is known as a fork. Each compound file has a resource fork and a data fork. The data fork is the part that most closely resembles the general file structure found on other operating systems. When you do an fopen on a Macintosh file you are affecting the data fork. The resource fork, being a separate file, is visible to the Mac file system but not to the Standard C/C++ file routines.

The Mac's forked file format presents a problem when trying to store a Macintosh file under a file system such as FAT (File Allocation Table), the system used by Windows. Because FAT does not allow a "two-forked" file, the Macintosh file must somehow be encoded into a single sequential data file for storage under the FAT.

The Macintosh provides no official format for flat files. However, there is a very common public domain format for encoding a Macintosh file. This format, which came to be known as Macbinary, was developed by early Macintosh users who wished to send Macintosh files over the Internet. The nice thing about Macbinary is that it's just one file, not two. Apple File Exchange, which comes with the Mac system software, is the basic utility used for converting to/from Macbinary format. (Note that a file is almost never stored in Macbinary on the Mac itself.) Once you've converted the file to Macbinary format, you can get to all the pieces from standard library calls such as fopen.

Macbinary files contain three basic parts. The first is a 128-byte header that contains a variety of information about the file. The second part is usually the data fork, and following that comes the resource fork. This is the normal order, but the order of the data and resource forks can be switched around. The order of data and resource forks is indicated by their offsets, which are stored in the Macbinary header. For a resource conversion program the only item of interest in the Macbinary header is the offset to the resource fork.

Macintosh Resource Fork Format

The format for a Macintosh resource fork is documented in Apple's More Macintosh Toolbox Essentials manual. The resource fork consists of a resource map and resource records. The map indexes individual resource records, by both a resource type and resource ID. The resource type is a four-character case-sensitive name. The resource ID is a signed 16-bit integer. All Macintosh resources are referenced in this way. For example dialog 128 would be stored as resource type DLOG, and resource ID number 128. Windows resources differ in that the resource type may be any length, and both numeric and string names may be used as resource IDs.

The Mac resource fork format is fairly flexible. Resource data can (theoretically) be stored within a record in practically any format. There are some conventions, however, and as defined by the conventions an ICON has a different structure than a DLOG. The application presented in this article translates only dialog (DLOG) and alert (ALRT) resources.

For most resource types the entire resource object is encapsulated inside a single resource record. This is not the case for dialog and alert resources. Instead, each dialog box resource record references a second resource of the type DITL (dialog item list). The dialog box record itself holds only the dimensions of the dialog box, and a DITL number indicating where the dialog item list is stored.

Apple chose not to include DITL information inside the DLOG resource record because other types of resources have dialog item lists as well. Once such resource is an alert (ALRT). Windows makes no real distinction between an alert and a dialog box — both are dialog boxes having the same functionality, the only difference being in the type of icon displayed. Macintosh treats alerts as a subset of the dialog box. A Mac alert is a dialog box that has no data collection elements other than buttons. The only information you can get back from a Mac alert is which button was clicked. A common example of a Mac alert is a dialog box that asks "Do you want to save this file?"

Another challenge that comes up in resource translation concerns integer storage formats. Macintosh computers store integers differently than Windows systems running on a PC — this is more a function of the underlying hardware than of the two operating systems. Macs are based on Motorola CPUs, which are big-endian; Windows typically runs on Intel CPUs, which are little-endian. Big-endian CPUs store integers with the most significant byte first (lowest in memory), little-endian CPUs store least signficant byte first. A method of conversion is needed.

My program performs the conversion via the macros MAC_WORD, MAC_LONG, and MAC_TRI. (Macintosh has a 3-byte integer format.) These macros are defined in MACRES.H (Listing 1) . Note that these macros do not just do a blind byte swap. If they did, the resource translation program would run fine on the Intel platform, but not on the Macintosh or a RISC. That's why the macros do mathematical swaps rather than byte swaps.

Conversion Program Operation

Listing 3 shows the conversion program (preceded by a few supplementary functions). Listing 2 shows the file MACRES.CPP, which contains all the routines used to access the records stored in a Macbinary file. The conversion program starts by opening the Macbinary file and reading in the Macbinary header. Both of these operations occur in the call res.Open. Open extracts a single long from the Macbinary header, and uses it to construct a file pointer to the beginning of the resource fork. The resource fork begins with a header that shows where all the major data structures are located. In the simplest sense a Macbinary file is a database of variable sized resource records indexed by the resource type/ID primary key.

The rest of the program is basically just a loop that finds and converts resources one at a time. The class MAC_RESOURCE encapsulates a single Macintosh resource. Member functions GetLength and GetBuffer enable access to each individual resource record. GetBuffer is used within the functions DumpDITL, DumpAlrt, and DumpDLOG (discussed below); GetLength is currently not used.

The program uses the class MAC_RESOURCE_FILE to read MAC_RESOURCE records from the resource file. MAC_RESOURCE_FILE can traverse all the resources, or dynamically pull up one by its type and resource ID.

To look up dialog number 128, the process is as follows: First the program looks through each entry in the resource map within the resource fork header. The resource map contains a list of every unique type of resource in the file. Because dialogs are stored as type DLOG the search begins by looking through each entry in the map until it locates the DLOG entry. Inside the DLOG entry the program then finds the offset to the Resource Type List. The Resource Type List is a listing of every resource ID having the same type — in this case, DLOG. The program then searches through the Resource Type List until it finds one with a resource ID of 128. Once the program has located this record it can use its Offset to Resource Data element to locate the exact location of the resource data for dialog 128. This offset points to a four-byte integer holding the length (in bytes) of the resource. The bytes immediately following contain the actual resource data.

Recall that data for DLOG resources is stored in a special format. Each item in a dialog box is stored in a separate DITL resource. Because of this, the resource format for the DLOG record itself is very simple: the bounding rectangle, the title, and the DITL resource number. The program calls function DumpDLOG (Listing 3) to process the DLOG record and write out a Microsoft compatible dialog. DumpDLOG calls DumpDITL to handle the item list associated with that dialog.

Alert processing is very similar to dialog box processing, since ALRT resources have a very similar structure to the DLOG structure. ALRTs also reference a DITL for their type lists, so the program uses the same DumpDITL function to process ALRTs as it uses to process DLOGs.

Conclusion

Because the source code is all written in standard C++, it should compile and execute on most platforms. The code presented here can easily be modified to process other sorts of Macintosh resources. This sample program could, for example, be extended into a general purpose utility for Macintosh file processing. For the adventurous, modifying the output routines will enable translation of Macintosh resource to other non-Windows platforms.

Bibliography

Apple Computer. More Macintosh Toolbox Essentials. Addison-Wesley, 1993. ISBN 0-201-63299-3.

Microsoft Win32 Programmer's Reference, Volumes 1 & 2. Microsoft Press, 1993. ISBN 1-55615-515-8 (vol 1) and 1-55615-516-6 (vol 2).

Jeff Heaton is a consultant for Daugherty Systems in St. Louis, MO. He has been programming in C/C++, graphics, databases, and Windows/Mac GUIs since 1986. He can be reached at http://members.aol.com/jeffheaton or jeffheaton@aol.com.