TweetFollow Us on Twitter

Resource Templates

Volume Number: 14 (1998)
Issue Number: 9
Column Tag: Tools Of The Trade

Resource Templates

by Doug McKenna, Mathemæsthetics, Inc
Edited by the MacTech Editorial Staff

Programming on the Macintosh involves creating and managing a variety of complicated data structures that describe everything from a basic window to the preferences data structure of our specific applications. As Macintosh programmers we rely on resource editors, like Apple's ResEdit and Mathemæsthetics Resorcerer to view and manipulate many of those data structures. In turn, those resource editors rely on TMPL type resources to define and record the structure of all other types of resources. In this article, Doug McKenna (the author of the high performance resource editor Resorcerer) presents an introduction to TMPL resources. - The Editor

What is a Resource Template?

Resorcerer contains a powerful 32-bit data editor that parses blocks of data of a given resource type into the data's constituent editable fields. It does this by following a set of instructions that declares the sequential format of the data. Each set of instructions is called a template, and each template is itself kept in a resource of type 'TMPL'. The binary data format of the template resource is quite simple, and can itself be described by another 'TMPL'. Editing templates, however, is such a common and important activity that there is a dedicated editor within Resorcerer designed specifically for dealing with 'TMPL' resources.

Each 'TMPL' resource has the same internal structure for its own data, but the data itself describes different data formats. The first four characters of the 'TMPL' resource's name, when taken as a resource type, uniquely identify the resource type that the template decribes. Resorcerer pays attention to templates it finds installed in any of several folders next to the application which are reserved for keeping common or private templates; or in resource files that it has opened for editing; or within Resorcerer's own application resources (Figure 1). 'TMPL' resources whose names are less than 4 ASCII characters in length are ignored. 'TMPL' resource IDs are also ignored.

Figure 1. Template Resources in a File.

The data within each template is nothing more than an arbitrarily long list of data parsing instructions, also called fields. Each field consists of two parts: (1) a variable-length Pascal-style string, followed by (2) a four-byte parsing code. Instructions are packed one after the other; there are no pad or alignment bytes anywhere. There is also no initial count, so that the number of instructions in the template can only be determined by parsing the thing until the end of the template data is reached (Figure 2).

Figure 2. TMPL Structure.

The string is called the instruction's label, and, being Pascal, consists of an unsigned length byte, followed by that many bytes of character data. The label primarily serves to identify for the user of the Data Editor the meaning of each parsed data field. Occasionally, the label has certain special characteristics for the benefit of the Data Editor's parser/formatter/interface (for instance, if the label is "Reserved", "Filler", or "Unused", the editor doesn't allow the user to edit the field), but in general the label is equivalent to a comment in a compiled language, and can be set to whatever you like. For instance, in the Japanese version of Resorcerer, the field labels are translated into Kanji.

The four-byte parsing code is called the field's type. It usually encodes both the size and manner of presentation of the next data field to be parsed sequentially from the data; however, there are a variety of special purpose codes that guide Resorcerer's parser in higher-level ways, or which are used to enhance the interactive interface of the Data Editor.

Resorcerer is Upwardly Compatible with ResEdit Templates

Other types of template resources have been designed, notably for MacsBug ('mxwt' resources) and Metrowerks' Constructor ('CTYP' resources), both of which can in turn be described by Resorcerer templates. The descriptive capabilities of Rez templates, which are kept in source code text files rather than resources, are more powerful, but can only be used for compiling.

The simple and elegant 'TMPL' structure described above was first implemented in Apple's resource editor, ResEdit, over a dozen years ago. Although you can edit a minimal set of simple Macintosh resources with ResEdit templates, in the ensuing years Apple and its developers have created much larger and more complicated data structures, which ResEdit cannot support. This is due primarily to reliance on compiling resources with Rez, but also from using the enhanced features that Resorcerer has provided over the years.

ResEdit supports 36 template field parsing codes; Resorcerer supports 128 (or more, if you count a handful of synonym field types implemented for mnemonic consistency). Fortunately, though, 34 of those 128 operate exactly the same as far as properly parsing data built with ResEdit, and the remaining 2 types (Booleans and fixed-length Pascal string buffers) are configurable to behave compatibly or not. Thus if you've invested time in creating your own ResEdit templates and built resources with them, Resorcerer's Data Editor can use your templates unchanged, and you can still edit the resources you or others have previously created.

Differences Among Data Descriptions

To see the differences among various ways of describing a well known resource, consider the different ways that the dialog window ('DLOG') resource can be described using Rez, ResEdit, Resorcerer, and C. The data format is simple enough: a few consecutive numeric fields, followed by a Pascal string for the window title, followed by another numeric field.

Listing 1: Four ways to describe a 'DLOG'

Rez Template
type 'DLOG' {
   rect;
   integer      documentProc, dBoxProc, plainDBox, altDBoxProc,
                noGrowDocProc, movableDBoxProc, zoomDocProc = 8,
                zoomNoGrow = 12, rDocProc = 16;
   byte           invisible, visible;
   fill byte;
   byte               noGoAway, goAway;
   fill byte;
   unsigned hex longint;   
   integer;
   pstring    Untitled = "Untitled";
#if SystemSevenOrLater
   align word;
   unsigned integer   noAutoCenter = 0x0000,
                      centerMainScreen = 0x280a,
                      alertPositionMainScreen = 0x300a,
                      staggerMainScreen = 0x380a,
                      centerParentWindow = 0xa80a,
                      alertPositionParentWindow = 0xb00a,
                      staggerParentWindow = 0xb80a,
                      centerParentWindowScreen = 0x680a,
                      alertPositionParentWindowScreen = 0x700a,
                      staggerParentWindowScreen = 0x780a;
#endif
};

------------------------------------
ResEdit TMPL
   RECT            BoundsRect
   DWRD            ProcID
   BOOL            Visible
   BOOL            GoAway
   DLNG            RefCon
   DWRD            Items ID
   PSTR            Title
   AWRD
   HWRD            Auto Position

------------------------------------
Resorcerer TMPL
   RECT            BoundsRect
   WB12            'WDEF' resource ID
      CASE         Standard system=0
      CASE         Standard desk accessory=1
   WB04            Variation code
      CASE         Document/standard accessory=0
      CASE         Modal dialog=1
      CASE         Plain frame=2
      CASE         Shadow frame=3
      CASE         Non-growable document=4
      CASE         Moveable modal dialog=5
      CASE         Zoomable document=8
      CASE         No grow, zoomable document=12
   BOOL            Visible
   BOOL            GoAway
   DLNG            RefCon
   RSID            Item list ('DITL') resource ID
   ESTR            Title
   HWRD            Window placement
      CASE         Leave it alone=$0000
      CASE         Center on main screen=$280A
      CASE         Center in parent window=$A80A
      CASE         Center on parent's screen=$680A
      CASE         Alert on main screen=$300A
      CASE         Alert in parent window=$B00A
      CASE         Alert in parent's screen=$700A
      CASE         Stagger on main screen=$380A
      CASE         Stagger in parent window=$B80A
      CASE         Stagger on parent's screen=$780A

------------------------------------
C struct
#pragma options align=mac68k
typedef struct {
   Rect            boundsRect;            
   short           procID;
   char            visible;      
   char            filler1;
   char            goAwayFlag;            
   char            filler2;
   long            refCon;      
   Str255          title;
   short           positionCode;            
} WINDResource;
#pragam options align=reset

Note that the C struct is not an accurate description of the resource data format, because the size of the title in the resource data is variable, whereas in the above a fixed-length Str255 buffer of maximum size of 256 bytes has been declared. Because of this, you can't cast the resource data handle to (WINDResource **) and expect the positionCode field to have the correct value in it.

Also worth noting is that both the Resorcerer and Rez descriptions include symbolic definitions of various field constants of interest directly in the format declaration; whereas the ResEdit and C descriptions don't.

The Rez definition of a 'DLOG' actually defines two data structures, depending on whether the constant SystemSevenOrLater is defined to be 1 or not.

Creating a New Template

To create a new template in Resorcerer, launch the resource editor, choose New File (CMD-N) and then choose New Resource (CMD-N) to specify the 'TMPL'. The resource ID can be anything; you can set the resource name to any four-character type you want the template to describe (for instance, 'test'). This brings up Resorcerer's Template Editor, which initally displays nothing since there are no fields defined yet in your new template. You can then choose New Field (CMD-N) from the editor's Template menu (or double-click on the field insertion caret's triangle) and enter the field data parsing type code and its label string. The field window provides you with a complete listing of all possible 4-character field types and their meanings, in case you're not sure what the four-character code is.

The Template Editor in Resorcerer displays and edits 'TMPL' resources (Figure 3); the Data Editor then uses the template to parse your resource as a data structure declared by the template (Figure 4). When a resource is selected in Resorcerer's File Window, and the Data button is enabled, this means that a template for that resource type is available for the Data Editor to use.

Figure 3. Editing a TMPL Field.

Figure 4. The Data Editor uses a TMPL to edit an 'ldes' resource.

Once you're familiar with the various template field types and operation, it is usually easier to type the list of field types and labels into a text file and then copy and paste them all at once into the Template Editor's window. You can do the reverse also: select any set of fields, choose Copy, and you can paste the template fields into a text file (Figure 5).

Figure 5. Editing a TMPL as text.

Resorcerer Template Field Types

The template field types that Resorcerer supports can parse and format many categories of data. The newer types were defined over the years as needed, based on what developers, including myself, were doing in various resource data structures, or for ease-of-use interface reasons in the Data Editor's operation, or for consistency. Since each "opcode" is allowed only 4 characters, it has been somewhat of a struggle to ensure some form of consistency in the type mnemonics. For instance, in general, an initial 'B' stands for "byte", a 'W' stands for a 2-byte "word" (left-over from 68k terminology, not PPC terminology), and 'L' stands for a 4-byte long. 'H' stands for hex, 'U' for unsigned, 'D' for signed decimal, 'P' for Pascal, and 'C' for C (how succinct!). 'S' stands for some form of Sized. In the context of delimiting groups of fields, the suffix 'B' stands for "begin" and 'E' stands for "end". I have studiously avoided using any lower-case characters, as this would add a whole degree of freedom to the difficulty of remembering what code is what.

In the following headings, the Resorcerer-specific extensions to the original ResEdit types are in boldface, and I've added some anecdotes explaining how some of these newer types came about.

Bits, bit fields, and boolean flags

BBIT, WBIT, LBIT, BBnn, WBnn, LBnn, BFLG, WFLG, LFLG, BOOL

The BBIT, WBIT, and LBIT fields each parse one bit out of the current byte, 2-byte word, or 4-byte long. Bits are parsed from most significant to least significant. Each of these is the same as a boolean field in a Rez template, but when Resorcerer's Data Editor displays the data, it numbers the bits appropriately depending on the field type: 7 to 0 for BBITs, 15 to 0 for WBITs, and 31 to 0 for LBITs. The bit-numbering is reversed if little-ending parsing mode is in effect.

The BBnn, WBnn, and LBnn fields operate similarly, except that instead of parsing just 1 bit, they each parse the next nn bits, where nn is a 2-digit ASCII decimal number encoded directly into the field type (sort of an immediate argument for the parsing opcode). Thus, WB01 is the same as WBIT; LB14 parses 14 bits from within a 32-bit long; etc. Each of these is the same as a boolean[] array in Rez.

You can intermix the single-bit with multi-bit parsing fields in series, as long as the total number of bits always exactly reaches a byte, word, or long boundary, depending on whether you are using the 'B...', 'W...', or 'L...' variant. The Data Editor performs no implicit hidden padding in any way, and will complain if a multi-byte field crosses an appropriate storage boundary. For example, the two declarations

Listing 2: Declarations

------------------------------------
Bits declared in C
unsigned long sizeCode : 2,
                   localChannel : 4,
                   isExtinct : 1,
                   isPurple : 1,
                   isDinosaur : 1,
                   isObnoxious : 1,
                   groupCount : 21;

------------------------------------
Bits in Resorcerer TMPL
      LB02         Size code
      LB04         Local channel
      LBIT         Is extinct?
      LBIT         Is a dinosaur?
      LBIT         Is purple?
      LBIT         Is obnoxious?
      LB21         Number of newsgroups
      LBIT         Filler

are basically the same, with the exception that in C the missing low-order bit is silently added by the compiler, whereas in a TMPL, you have to explicitly declare everything: the total number of consecutive 'WB' bits has to add up to a multiple of 16.

I originally implemented the various word and long extensions to the BBIT field type because documentation for various bit flags routinely refers to them by bit number (e.g. bit 24 in this long field), and I couldn't stand sitting in front of multi-thousand dollar computing machine, forced to count bits in order to get things just right.

The BFLG, WFLG, and LFLG fields are designed to correspond to how a single boolean flag has historically been coded in C, which is to say, as the value 0 or 1 stored in the low-order bit of a byte, short, or long integer. The higher order bits are parsed automatically by these three field types, but those bits are not editable and are set to 0. For instance, BFLG is the same as [BB07, BBIT].

The BOOL field actually parses 2 bytes. The value of the field is TRUE if it exactly matches the 16-bit value kept in Resorcerer's own 'BOOL' 128 resource, which is set to 0x0100. The BOOL field type was implemented originally in ResEdit to take care of the hidden padding problem that occurs in C structs and Pascal records. Boolean fields are typed to be single bytes, but because of this, the 68k compilers would add a hidden pad byte after the Boolean field. So a BOOL field is TRUE if its integer value is 256, and FALSE if it's 0. I was never able to find any documentation on its behavior. After watching what ResEdit did, I chose to make the operation configurable with a 'BOOL' resource, since I wasn't sure what it was supposed to do. You should try to avoid using this field type in favor of the more explicit bit types above.

Decimal and hexadecimal integers, and resource IDs

DBYT, DWRD, DLNG, UBYT, UWRD, ULNG, HBYT, HWRD, HLNG, RSID

These instructions simply parse 8-bit, 16-bit, and 32-bit integer fields, depending on whether the field ends in 'BYT', 'WRD', or 'LNG'. The 'D' variants display and edit the data as signed decimal numbers; the 'U' variants as unsigned decimal; and the 'H' variants as unsigned hex. Parsing is subject to the current endian mode.

Since storing the resource ID of some related resource as a 2-byte integer is very common, Resorcerer has a special RSID field just for this purpose, allowing you to edit the related resource easily. The RSID field parses a 2-byte signed short integer, and displays it the same as a DWRD. But since the special field lets Resorcerer's Data Editor know that the number is really a resource ID, the editor can let you quickly interactively link to (i.e. open with an editor) the referenced resource in the same file. To do this, though, it needs to figure out what the resource type is, which it does in one of two ways. For instance, if your template field sequence contains the instructions

Listing 3: Resource References

Resource references in a template
RSID Resource ID of new icon suite ('icns')

TNAM Type of icon suite
RSID Resource ID of icon suite

then the Data Editor will be able to figure out which resource to open or create when you ask it to edit the referenced resource. With the first RSID field, the resource type to use is hardwired into the field's label ('icns'). In the second RSID field, because there is no single-quoted type, the editor searches back in the data for the first TNAM field and takes that data as the resource type.

Fixed-point numbers

FIXD, FRAC, SFRC, FWID, FXYZ

Fixed point numbers are integers whose bits are divided into two sets. The upper (more significant) set is the signed integral part of the number, and the lower (less significant) is the binary fractional part of the fixed-point number. The two parts of the number are separated by an implicit fixed decimal point. FIXD fields correspond the Mac Fixed data type [16.16]. FRAC fields are the same as the Mac Fract data type [2.30]. SFRC fields are the same as SmallFract data types [0.16] used in certain color models. FWID (Font Width) fields are used in various font-related resources [4.12]. Finally, FXYZ fields are used in some other color models [1.15]. All of these except the SFRC type display the data as signed fixed-point numbers.

Floating-point numbers

REAL, DOUB, EXTN, XT96, UNIV

Standard IEEE 32-b it and 64-bit single- and double-precision floating point numbers get parsed by the REAL and DOUB field types. EXTN parses the 68k 80-bit SANE floating point numbers; XT96 parses the 96-bit extended SANE floating point values, and UNIV parses the old THINK C Universal format 96-bit extended numbers. As far as PowerPC code is concerned, you only need to deal with REAL and DOUB floating point data.

Resorcerer's floating point support got added to the template system after developers working for a division of General Electric that sent balloons up into the atmosphere for data collection called saying they were keeping large amounts of floating point radar information in resources and needed to edit their data directly.

Creation and modification times

DATE, MDAT

These two fields parse a 32-bit unsigned long number, and display it as the time and date as the system interprets it (number of seconds since Jan 1, 1904). Any MDAT field (modification date) is automatically adjusted to be the current time and date when you close the resource's editing window after using the Data Editor to change any field in the resource.

Script, language, and region codes

SCPC, LNGC, RGNC

Each of these parses a signed 2-byte integer, the same as a DWRD. However, when the editor displays the value, it automatically provides a popup menu from which to choose all the standard values for the various codes, so that you don't have to remember or lookup which number stands for which language or region. These were implemented so that templates with these codes in them wouldn't also have to have copies of the long list of CASE fields (see below) that normally would be used to build the popups.

RGB colors and color tables

COLR, CLUT

The COLR field parses an RGB triplet: 3 2-byte integers, for the red, green, and blue color components, in that order. The Data Editor then displays the field in both numeric and graphic form, and you can change all three fields simultaneously using the system color picker.

The CLUT field parses an entire Color Lookup Table data structure into one hex block. It was implemented to make it easier to parse embedded cluts in 'PICT' data as single hex blocks.

QuickDraw Rectangles and Points

RECT, PNT (the point field type ends in a space)

The 'PNT' field parses a pair of signed short QuickDraw coordinate integers (vertical coordinate first), as in a Point data type. The RECT field does the same for Rect data types, and the Data Editor allows you to set the rectangle coordinates to new values graphically, using a marching ants marquee. If the marquee is in front of and graphically contained within the content area of any other Resorcerer window, the coordinates recorded are converted to local coordinates for that window; otherwise, the values placed into the RECT field are in global screen coordinates.

Alignment, pad, and filler bytes

AWRD (= AL02), ALNG (= AL04), AL08, AL16, FBYT, FWRD, FLNG, Fnmm)

Unlike C, Resorcerer (as well as Rez and ResEdit) templates perform no hidden data padding or alignment unless you explicitly declare it in the template. Given the myriad problems hidden alignment and padding causes developers, this is a good thing. However, it means that any data field, including numerical integers and floats, can occur at any byte offset, if you design the template that way (usually at some cost in efficiency, which on older 68000 systems can run to a 100% loss when the machine crashes).

The AWRD, ALNG, AL08, and AL16 fields allocate a variable number of 0 or more pad bytes so as to ensure that the following field will start on a byte boundary (as counted from the start of the resource data) divisible by the number 2, 4, 8, or 16 respectively.

The FBYT, FWRD, FLNG, and Fnmm fields declare filler bytes of various fixed lengths (1, 2, 4, and 0xnmm). Filler bytes are uneditable and generally set to 0. Filler bytes are typically used to declare space within the data structure that will be used by code at runtime (for instance, to cache a Handle), but which has no meaning within the context of editing the resource data "offline".

ASCII characters, 4-byte literal type codes, and pure ASCII text

CHAR, TNAM, TXTS, Tnmm

The CHAR field type parses and displays a single-character ASCII byte. The TNAM (Type Name) field parses a 4-character ASCII literal.

The TXTS field parses ASCII data until the end of the resource or until the end of an embedded, previously sized block of data within the resource. This field was implemented to fix the problem in the template distributed with ResEdit, which incorrectly declares a 'TEXT' resource as a null-byte terminated C string.

The Tnmm field declares a fixed-length buffer of 0xnmm bytes in which to expect 0 or more bytes of ASCII text. If the length of the text is less than 0xnmm bytes, the remaining bytes are set to zero-valued pad bytes.

Variable-length ASCII character strings

PSTR (=BSTR), WSTR, LSTR, ESTR, PPST, OSTR, CSTR, ECST, OCST

The PSTR field parses a Pascal-style string: a length byte followed by that many bytes of ASCII data. The WSTR field does the same, only the length is stored in a 2-byte integer; the LSTR field stores the length in a 4-byte long. The ESTR field (even-length string) also parses or creates a Pascal string, but if the length of the data (including the length byte) is odd, it appends an extra zero pad byte. At first glance this might appear the same as the sequence [PSTR, AWRD], but there is a subtle difference: it's the same only if the PSTR begins on an even byte offset to begin with. The OSTR field is the opposite of the ESTR field. It adds the pad byte to make the total Pascal string storage always an odd number of bytes and should only be used for parsing certain ancient resources. Similarly, the PPST field was implemented as a hack in order to be able to describe certain MPW resources whose data was stored ESTR style, except that the extra pad byte was incorrectly included in the length byte (which makes it impossible to tell whether it's a pad byte or a data byte with value 0).

The CSTR, ECST, and OCST fields are equivalent to the PSTR, ESTR, and OSTR fields, only they operate on arbitrary-lengthh, null-byte-terminated C-style strings, as opposed to initial-length-byte Pascal-style strings.

Fixed-length ASCII character string buffers

Pnmm, Cnmm

These two fields each allocate a block of 0xnmm bytes and interpret the dat a in the block as either a Pascal or C string, followed by as many zero-valued pad bytes as are needed to fill the block.

The Pnmm field was designed to work in exactly the same way as all other Xnmm type fields: the field type declares a block of data of length 0xnmm bytes, and whatever gets stored in the block is interpreted according to X. Thus, the Cnmm, Fnmm, Hnmm, Pnmm, and Tnmm fields all indicate their sizes in exactly the same manner. I felt that consistency on this front was important. ResEdit interprets the Pnmm field differently, choosing to break consistency within the TMPL language in favor of making this one field conform to how such fields are declared in Pascal or C (sort of, since Str255's aren't declared as Str0FF's). Resorcerer's Data Editor therefore supports both interpretations, on th e assumption that developers almost never allocate an odd number of bytes for the fixed-length buffer. The editor checks all Pnmm fields and if it finds one where 0xnmm is odd (as it is in ResEdit for an even-length buffer), it will give the user the opportunity to use ResEdit-style parsing.

Raw hex byte dumps

BHEX, WHEX, LHEX, BSHX, WSHX, LSHX, Hnmm, HEXS, HEXD

These fields parse and make editable blocks of bytes and present them as untyped hex data. With the first six types, the size of each block is taken from the initial byte, short, or long field. For instance, the BHEX field could be used to parse a Pascal string, only it would show the data in hex, not ASCII. The 'SHX' variants include the size of the size field in the count; the 'HEX' fields don't.

The HEXD field (Hex Dump), as originally defined in ResEdit, simply says take the rest of the resource data and edit it as untyped hex bytes. The HEXS field is similar, but is used for embedded hex dumps whose block sizes have been declared earlier.

68K disassembled code dumps

CODE

This is the same as HEXD, except that it asks the hex editing display to show the data as disassembled 68K code as well as in hex.

Automatically computed array repeat counts

BCNT, WCNT (= OCNT), LCNT, ZCNT, LZCT, FCNT

You can group a set of fields together and ask that the group be repeated until a condition is met. (In Rez, these are declared as arrays.) Usually this condition is given as an explicit repeat count in the data itself. The BCNT, WCNT, and LCNT fields each parse the count for the next repeated group of data fields, using an unsigned 1-, 2-, or 4-byte integer count (byte, word, or long, respectively). The WCNT type is the same as ResEdit's OCNT (one-based count) type. ZCNT (zero-based count) fields specify a 2-byte count that is 1 less than the number of repeated items that follow. The LZCT does the same, but stores the count (minus 1) in a 4-byte long.

One important difference from ResEdit in how Resorcerer implements these counts is that in ResEdit, the count field has to immediately precede the start of the group of array item; in Resorcerer, as in Rez, the count can occur much earlier in the data, with intervening fields between the initial count and the array of repeated items.

The FCNT (Fixed Count) field specifies a fixed-length count, where the count is not in the data at all, but taken from the field label string in the TMPL itself.

The BCNT field was originally implemented in order to build a TMPL for MPW Commando resources, as well as just plain generality. The FCNT field was first implemented for the benefit of parsing certain ColorSync resources.

Groupings of fields into repeated array items

LSTC, LSTZ, LSTS, LSTB, LSTE

These "field" types don't represent any data fields per se; rather, they declare the bounds of a subsequence of template instructions that represent a repeated item. You should use one of LSTC, LSTZ, LSTB, or LSTS to declare the start of the group, and use LSTE to end it.

LSTC starts a one-based counted list, whose count has previously been parsed from the data using any of the BCNT, WCNT, OCNT, or LCNT fields. The LSTZ field does the same thing, only the counted list is 0-based, meaning the count field contains one less than the number of items. I avoid 0-based lists whenever possible.

The LSTB initiator says the list simply repeats indefinitely, without the benefit of an initial count, until the end of the resource.

The LSTS initiator says the list repeats until the number of bytes parsed reaches a previously declared size field's value. This is called a Sized List as opposed to a Counted List.

All of these list structures can nest. You can have an indefinite list of counted lists of sized lists, etc.

Recursive templates for trees

SELF

You can describe simple recursive trees of similar items by using the SELF template field type as the sole field in a counted list group. A copy of the entire template's set of fields will be used in place of the SELF field when you edit the data. Because the recursive list must be counted, there is no infinite recursive descent: the leaf lists in the tree must all have an item count of 0.

Collections of common, required, key, or default values

CASE

Most data field types in a Resorcerer template can be followed by a set of 0 or more CASE instructions. These do not parse any actual data, but rather declare a set of named values that the previous field data can take on. They function similarly to an 'enum' or set of '#defines'. In addition, the first CASE declaration in the list of CASE fields lets you install a non-zero initialization value into the data whenever you are creating a new resource, or new repeated item.

As we will see in the next section, the set of CASE fields also declare the set of legal key values for a switched alternate record structure.

Integer keys for switch statements

KBYT, KWRD, KLNG, KUBT, KUWD, KULG, KHBT, KHWD, KHLG, KRID

In the more complex resource data formats, it often happens that a field's value determines one of several alternate formats later in the data. The determining field is called the key, and the set of alternates formats are called the switched items. In Rez, such structures are described using a switch statement, and each alternate item is called a case.

KBYT, KWRD, and KLNG each declare a signed 1-, 2-, or 4-byte integer whose value is to be used as a key to choose from amongst several alternate formats.

KUBT, KUWD, and KULG are basically the same, only the key values are treated as unsigned decimal integers.

KHBT, KHWD, and KHLG are again basically the same, only the Data Editor presents the key values as unsigned hex. These fields are particularly useful in Resorcerer's 'PICT' template, since the opcodes in a 'PICT' are all documented in Inside Mac using hex constants, not decimal, and each opcode is really a key value for alternate opcode data formats that follow.

The declaration of the set of legal key values follows the keyed field type as the usual set of CASE fields. This allows the Data Editor to collect the symbolic descriptions of the cases into a popup menu in the usual manner, making interactive editing of the key field very easy.

The KRID field is special: it does not declare a data field. Rather, it declares a pseudo-data field whose value is actually the current resource ID of the resource whose data the template is being used to edit. This makes it possible for the template to declare completely different data formats dependent solely on the resource ID (which is meta-information about the resource data) rather than on a previous key field in the data itself. The KRID field was originally implemented in order to deal with certain MPW and other older resources ('PREC's); however, its use is highly discouraged, since it obviates the reason for having a resource type attribute in the first place.

Character and literal type keys for switch statements

KCHR, KTYP

These declare 1- and 4-byte ASCII character literals whose values are to be keyed off of. The KCHR field is the keyed version of the CHAR field; the KTYP field is the keyed version of the TNAM field (which is the same as an OSType or ResType or FourCharCode).

Groupings of fields into alternate (keyed or switched) data structures

KEYB, KEYE

Following a key field and its set of n CASE declarations, there must be n alternate data formats, one for each CASE. The template field sequences describing each of these alternates must be delimited by a KEYB...KEYE pair. The label of the KEYB field must be the same as the character value of the earlier CASE declaration.

Automatically computed data sizes (in bytes)

BSIZ, WSIZ, LSIZ, BSKP, WSKP, LSKP, SKPE

Many data formats contain embedded sizes of sub-areas of the data. This is very important fo r desiging extensible formats, because they allow old code to traverse newer formats. The BSIZ, WSIZ, and LSIZ fields each store an unsigned byte count into a 8-, 16-, or 32-bit integer, respectively. The count stored is the number of bytes in the data, beginning at the first byte after the size field and continuing up to the next next matching SKPE field. The BSKP, WSKP, and LSKP fields are the same, except that the byte count they store includes their own sizes (e.g. an WSKP data field can never be less than 2 bytes).

The SKP variants were originally implemented first, in order to edit balloon help resources. This flavor of size field allows the format parsing code to skip an internal data block by simply by adding the value of the field at that offset into the data to the offset itself to get to the first byte offset after this sized block of data. Consequently, I called these fields "skip offsets", which is what the "SKP" part of the field types derives from. The SKPE (Skip End) field doesn't parse any data; it simply delineates where to stop counting bytes, and as such terminates the byte-counted area of the template for all 6 flavors of sizes.

Data insertion and deletion

+BYT, +WRD, +LNG, +nmm, +PST, +EST, +CST
-BYT, -WRD, -LNG, -nmm, -PST, -EST, -CST

Changing the structure that a template describes is as easy as inserting or deleting a field type using the Template Editor. Unfortunately, if you've already built resource data with the old template, the newly changed template won't be able to parse the old data correctly. This problem becomes particularly acute when the changed template fields are part of any repeated or keyed items. How do you change the old data at the same time as the old template?

The data insertion and deletion fields help you solve this problem. The insertion field types all begin with the '+' character; the deletion fields all begin with a '-'. An insertion field in a template tells the Data Editor to create the field when the old data is opened for editing, and to leave the data there when closing the resource. The deletion fields work exactly oppositely. It parses the old data field normally when you open the resource, but deletes the field when you close it. If an insertion or deletion field is part of a repeated item, it will do its work on every item in an existing array in a resource.

As usual, the 'BYT', 'WRD', and 'LNG' fields operate on 1-, 2-, and 4-byte fields; the 'nmm' field operates on 0xnmm bytes. The 'PST' fields insert or delete a Pascal string; the 'CST' fields do the same for C strings; and the 'EST' fields insert or delete even-Pascal strings (as declared by the 'ESTR' field).

Once you've changed the data to insert or delete fields, though, you have to remember to go back into the template and either remove any of the deletion fields, or change any of the insertion fields to the non-insertion equivalents, before opening the resource data again. You don't want to make the changes twice!

Template comments

DVDR

You can place a DVDR field anywhere in the TMPL field sequence. It doesn't parse any data, and its label string serves as a comment that the Data Editor draws for you on the right side of the data display.

Endian parsing mode

BNDN, LNDN, BIGE, LTLE

Templates default to parsing multi-byte numerical fields in big-endian style, since this is standard on the Mac. However, you can change the current endian interpretation to little-endian by using the LNDN or LTLE fields (which don't parse any data). The variants that end in "NDN" are invisible in the data editor display; the variants that end in "E" are shown.

Data pre- and post-processing with code plug-in filters

FLTR

Data formats can be arbitrarily complex, and it's quite easy to design a format that the simple parsing fields available in the TMPL "language" can't parse. Typically, any parts of the data format that depend algorithmically on other parts of the data can't be properly edited by a TMPL. Typical examples include encrypted or compressed data; fields whose values are arithmetic formulas dependent on other fields (e.g. rowBytes in a PixMap); checksums; etc.

If the first field in a TMPL is FLTR, the template is considered a filtered template. This means that a code resource of type 'FLTR' should accompany the template in the same file as the TMPL resource was found. The Data Editor will call the code resource to filter the data both on input to the TMPL parsing mechanism, and on output. Thus the FLTR code converts the initial Handle of data into an intermediate form that the TMPL describes. On output, the FLTR unconverts the data the TMPL describes, creating a Handle of data in its final form.

Summary

Resorcerer's TMPL language, which is a superset of ResEdit's, lets you easily describe and then interactively edit many different resource formats. Quite often, there is a one-to-one mapping from a Rez template to a Resorcerer template. Resorcerer is shipped with a great many templates for common Mac system and application resources that Apple engineers have designed using Rez.

A concise listing of all these template field types can be found at http://www.mathemaesthetics.com/ResTemplates.html.


Doug McKenna is the root hierarchæologist of Mathemæsthetics, Inc and has been a programmer for over 25 years. In addition to maintaining and enhancing Resorcerer® for the much of the last decade, he has worked on taxonomic database and music notation software. In his minimally-existent spare time, he enjoys gluing words together, hacks piano compositions, and researches geometric fractal tiling designs. Doug's last article for MacTech was FEZ: Frame Evading ZoomRects, which explained his award-winning "pushing the envelope" hack at MacHack '94. You can reach Doug at resorcerer@mathemaesthetics.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more

Jobs Board

*Apple* Systems Administrator - JAMF - Syste...
Title: Apple Systems Administrator - JAMF ALTA is supporting a direct hire opportunity. This position is 100% Onsite for initial 3-6 months and then remote 1-2 Read more
Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.