Technical: QuickTime
Advanced Search
Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

Resource Endian Flippers

Dispatch 25

Resources are always stored big-endian (Mac-style) on disk, regardless of whether you are running on Macintosh or Windows. However, when you call the Resource Manager to read a resource, for example using GetResource, the returned Handle magically contains platform native endian numbers. Well, almost always. It works for any resource types that QuickTime knows how to endian flip. It won't work for resource types that you define. Here's a description of what's going on behind the scenes, and how you can leverage the magic for your own resource types.

When the Resource Manager reads a resource off disk, it looks up the resource type (eg. 'STR ') in a table of ResourceEndianFilter routines. If there is such a routine, it calls it to flip the resource to native-endian. A similar thing happens when the Resource Manager is writing a resource back to disk. It finds the same routine, and calls it again to flip the resource back to big-endian. Inside the Resource Manager, we have set up a table full of routines for flipping all sorts of different resource types.

So, now there are two things you need to know:

How do you write a ResourceEndianFilter?

How do you get it into the Resource Manager's lookup table?

The definition of the ResourceEndianFilter, and the API you call to register it are both in Resources.h (but are only defined on non-Mac targets, since this is completely unnecessary on the Mac). They look like this:

typedef OSErr (*ResourceEndianFilterPtr)(Handle theResource,
                      Boolean currentlyNativeEndian);
   
OSErr RegisterResourceEndianFilter(ResType theType,
                      ResourceEndianFilterPtr theFilterProc);

The ResourceEndianFilter you write is called both when converting from native to big endian and when converting from big to native endian. The "currentlyNativeEndian" parameter determines if the resource data is native endian upon entry. The filter should flip the resource data to the other endianism.

Here is an example ResourceEndianFilter routine, designed to flip the fictitious 'blah' resource, consisting of a short (2-byte) numLongs field, followed by "length" longs (4 bytes each).

#include <Endian.h>
   
OSErr MyBlahEndianFilter(Handle theHandle, Boolean currentlyNativeEndian)
{
    if (theHandle && *theHandle) {
        // flip the handle in place
        Ptr p = *theHandle;
        short numLongs = *(short *)p;
   
        // If we're flipping from big to native, then we need to make numLongs
        // native before we use it in our loop (or we'll flip out of control)
        if (!currentlyNativeEndian) {
           numLongs = EndianS16_Swap(numLongs);
        }
   
        // Now flip the Handle in place.
   
        // First, the short numLongs field.
        *(short *)p = EndianS16_Swap(*(short *)p);
        p += 2;
 
        // Now the longs.
        for (i=0; i < numLongs; i++) {
            *(long *)p = EndianS32_Swap(*(long *)p);
            p += 4;
        }
        return noErr;
    } else {
        return nilHandleErr;
    }
}

 

Here's the API you call to register a ResourceEndianFilter routine:

OSErr RegisterResourceEndianFilter(ResType theType, ResourceEndianFilterPtr theFilterProc);

In our case, the call looks like:

err = RegisterResourceEndianFilter(OSTypeConst('blah'), MyBlahEndianFilter);

Once you've called this API, any reads or writes of a 'blah' resource will call MyBlahEndianFilter to do the appropriate flipping. Calling this registration API after a read, and before a write is not recommended, obviously. Call it early.

A Warning About the Endian Flipping Macros

The *_Swap macros in Endian.h evaluate their parameters more than once (look at the definitions in Endian.h, if you want to know the details). Do not increment the pointer in the parameter expression, or it will be incremented more than once. Here's some bad code that is trying to flip 3 shorts in a row into some local variables (leaving the Handle untouched):

    short *p1 = (short *)(*theHandle);
    short the1stShort, the2ndShort, the3rdShort;

    the1stShort = EndianS16_Swap(*p1++);  // incremented p1 twice!
    the2ndShort = EndianS16_Swap(*p1++);  // flipped the 3rd short here!
    the3rdShort = EndianS16_Swap(*p1++);  // flipped off the end of the Handle!

Here's a much better way, one that will actually work as expected:

    short *p1 = (short *)(*theHandle);
    short the1stShort, the2ndShort, the3rdShort;

    the1stShort = EndianS16_Swap(*p1);  p1++;   // the first short
    the2ndShort = EndianS16_Swap(*p1);  p1++;   // the second short
    the3rdShort = EndianS16_Swap(*p1);  p1++;   // the third short

Built-in Resource Flippers

As of QuickTime 4.0.3, resource flippers are built-in for the following resource types: ALRT, BNDL, cdci, cicn, clut, CNTL, cpix, crsr, CURS, DITL, DLOG, FREF, fttl, gnht, icl4, icl8, ICN#, ICON, ics4, ics8, MBAR, MENA, MENU, nini, pnot, qfmt, qmtr, qtet, snd , stg# stgp,STR ,STR#, styl, TEXT, thar, thga, thnd, thng, thnr, vers, WIND, and wxim. If you want to check to see if a resource flipper is installed for a particular resource type, you can do so by attempting to install a new resource flipper for the type as demonstrated above. If the resource flipper is already present, the RegisterResourceEndianFilter function will fail, returning a value of 1.

See Also

Inside Macintosh: QuickTime

Change History

2/23/00 - grc - First published
Topics
Previous | Next
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2004 Apple Computer, Inc.
All rights reserved. | Terms of use | Privacy Notice