TOP | UP: Data Manipulation | NEXT: Yielding, Pausing, Threads, and Semaphores |
On Mac OS, a file consists of both a resource fork and a data fork, either of which may be empty. The data fork is a single stream of data; access to it through UserTalk is described Chapter 19, Datafiles. The resource fork is a collection of individual structured chunks of data, called resources. Each individual resource is identified by a type (a string4) and an index, which is an ID number or a name; every resource has an ID, and it may or may not also have a name.1
Many interface-related data structures, such as sounds, dialog layouts, menus, and so forth, are stored as resources of types that the system knows how to deal with; applications are also free to define their own resource types to use as they wish. Users can access a file's resources, and edit many of them conveniently, with a utility application such as ResEdit.
This chapter describes UserTalk's verbs for accessing resources. It is possible to damage a file or an application's functionality beyond repair by altering its resources, so use caution.
To obtain the number of different resource types in a file:
rez.countResTypes (pathname)
To obtain the string4 identifier of the n th resource type in a file:
rez.getNthResType (pathname, n, addrDestination)
To obtain the number of resources of a given type in a file:
rez.countResources (pathname, type)
To obtain the ID and name of the n th resource of a given type in a file:
rez.getNthResInfo (pathname, type, n, addrID, addrName)
rez.getNthResType() and rez.getNthResInfo() store the requested information by way of address parameters, but they also return a boolean reporting success, so you can loop through all resources without counting them first. For example, this script makes an outline giving the ID (and name if there is one) of every resource in a file chosen by the user.
For a slightly different implementation of this example, refer to suites.sam-ples.basicStuff.resourceMap() .
Verbs that do things with a resource's data generally come in two flavors, to let you refer to the resource either by name or by ID. In getting a resource's data, there is a third option, to refer to the resource as the n th of its type.
To obtain the data of a resource referenced by ID:
rez.getResource (pathname, type, ID, addrDestination)
To obtain the data of a resource referenced by name:
rez.getNamedResource (pathname, type, name, addrDestination)
To obtain the data of a resource referenced as the nth of its type:
rez.getNthResource (pathname, type, n, addrName, addrDestination)
To set the data of a resource referenced by ID:
rez.putResource (pathname, type, ID, addrSource)
To set the data of a resource referenced by name:
rez.putNamedResource (pathname, type, name, addrSource)
To delete a resource referenced by ID:
rez.deleteResource (pathname, type, ID)
To delete a resource referenced by name:
rez.deleteNamedResource (pathname, type, name)
To check the existence of a resource referenced by ID:
rez.resourceExists (pathname, type, ID)
To check the existence of a resource referenced by name:
rez.namedResourceExists (pathname, type, name)
The data of a resource is always a binary. Dealing with this data - parsing it further to extract particular pieces of information, for example - is up to your script. For one type of resource, parsing utilities are supplied, namely resources of type 'STR ' (a Pascal-type string, which starts with a length byte).
To obtain the data, as a string, from a 'STR ' resource:
rez.getStringResource (pathname, ID, addrDestination)
To set the data of a 'STR ' resource:
rez.putStringResource (pathname, ID, theString)
Otherwise, you must know the structure of the resource in order to parse it. Since resources are designed to be parsed by a machine, this is usually simple enough. Consider, for example, a 'STR#' , which is a string collection. It is structured as follows: two bytes stating how many strings there are; then that number of Pascal strings, where a Pascal string is a length byte followed by the characters of the string. Knowing this, it is trivial to parse the resource; here, for example, is a utility that reads a 'STR#' and converts it to a table of strings.
The reverse utility, that writes out a table of strings as a 'STR#' resource, is left as an exercise for the reader.
To illustrate the rez verbs further, here is an example script that aids in the importing of XCMDs into the database; it lets the user choose a file, looks for XCMDs in it (using the technique from Example 20-1), lets the user choose from a list of these, and imports the chosen XCMD into the workspace table.
An interesting utility verb showing the rez verbs in action is suites.sam-ples.basicStuff.resourceStealer() ; you specify a folder, a resource type, and a file pathname, and all resources of that type are copied from all the files in that folder, to a depth of infinity, into the specified file (which is created if necessary), which is then opened by ResEdit.
The resource chain is the set of resources available to the system at any given moment. Adding a resource to the resource fork of an open file brings the resource into the resource chain immediately. When Frontier is running, there is a file known to be open and whose resource fork we can modify: the database. A resource can be added to the database's resource fork, on the fly, while the database is open and Frontier is running, in order to make that resource available to the system. It can then be removed from the database's resource fork when we are finished using it.
Here, for example, is a script that plays a 'snd ' resource stored as a binary in the database; it adds the binary to the database's resource fork, plays it, and removes it from the database's resource fork (the database entry remains).
With every resource is associated a set of resource attributes, coded as a short integer each of whose bits represents an attribute. Two verbs let you get and set this short integer.
To obtain a resource's attributes:
rez.getResourceAttributes (pathname, type, ID)
To change a resource's attributes:
rez.setResourceAttributes (pathname, type, ID, theShort)
The bit verbs (see Chapter 15, Math) can be used to manipulate the individual bits of the short. The bit flag meanings are:
These are very advanced verbs, requiring an intimate understanding of Mac OS resources, and the vast majority of users will never call them.
1. For more information about resources, see http://devworld.apple.com/dev/techsupport/insidemac/MoreToolbox/MoreToolbox-10.html.
TOP | UP: Data Manipulation | NEXT: Yielding, Pausing, Threads, and Semaphores |