QTShortcut.c

//////////
//
//  File:       QTShortcut.c
//
//  Contains:   Sample code for creating a shortcut to a QuickTime movie.
//
//  Written by: Tim Monroe
//              Based on the Windows version of MakeRefMovie.c and the code in the Ice Floe Dispatch 15;
//              some comments based on release notes by Chris Flick.
//
//  Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <1>      11/20/98    rtm     first file
//   
//  A shortcut is a QuickTime reference movie that references a single other movie. A typical reference
//  movie references more than one other movie, and the target of the reference movie is determined at
//  runtime depending on certain features of the user's machine, such as the actual components that are
//  available on the machine or the speed of the user's connection to the internet. (Reference movies
//  that select a target depending on the speed of the internet connection are "multiple data rate movies".)
//  A shortcut is essentially the simplest possible reference movie; all it does is refer to some target,
//  which is opened (or imported) when the reference movie is opened (or imported).
//
//  Shortcut movies can be useful as a cross-platform, media-centric aliasing mechanism. The data
//  reference contained in a shortcut can be any one of the data reference types supported by QuickTime
//  (an alias to a file, a URL to an ftp or http movie, and so forth) and that reference can address any
//  kind of data that can be opened by QuickTime. Thus, you could put shortcut movies on a server that
//  point to files elsewhere on the server, on another server, or even on the user's local machine. When
//  a user opens a shortcut movie, its target data is automatically opened. This indirection allows a
//  server administrator to move the files targetted by some shortcut movies to new locations and then
//  update the shortcuts. Any client references to the shortcuts (HTML embeddings, explicit URLs used by
//  an application, and so forth) would be unaffected by this move.
//  
//  Shortcut movies have been supported by QuickTime beginning with version 3.0. QuickTime version 4.0
//  introduced the Movie Toolbox function CreateShortcutMovieFile, which you can use to create a shortcut
//  movie given a data reference, its type, and a specification of the output shortcut movie file. This
//  sample code illustrates how to call the CreateShortcutMovieFile function.
//
//  If you want to create shortcut movies under versions of QuickTime prior to 4.0, then you'll need to
//  build a file containing the reference data yourself. The format of reference movies is currently
//  undocumented, but it's quite simple: the shortcut file should consist entirely of a single movie atom,
//  which in turn contains a movie data reference alias atom (of type MovieDataRefAliasAID). This atom
//  contains a single data reference atom. Finally, the data reference atom should contain the type of the
//  data reference followed immediately by the data reference itself. In short:
//
//                  movie atom -> movie data reference alias atom -> data reference atom
//
//  (where "->" means: "contains exactly one"). This sample code also shows how to assemble a shortcut
//  movie yourself by building these atoms.
//  
//  IMPORTANT: The atoms that we'll create here (for instance, reference movie descriptor atoms)
//  are unrelated to QTAtom and QTAtomContainer structures introduced in QuickTime 2.1. The atoms
//  we need to work with here are more akin to "chunks" as used in AIFF files: an atom is simply
//  a 4-byte atom length, a 4-byte atom type, and some associated data (which itself may be other
//  atoms). All this stuff (length, type, and data) must be in big-endian format.
//
//////////
 
 
#include "QTShortCut.h"
 
 
//////////
//
// QTShortCut_CreateShortcutMovieFile
// Create a movie file that is a shortcut to the specified data reference.
//
//////////
 
OSErr QTShortCut_CreateShortcutMovieFile (Handle theDataRef, OSType theDataRefType, FSSpecPtr theFSSpecPtr)
{
    long        myVersion = 0L;
    OSErr       myErr = noErr;
    
    myErr = Gestalt(gestaltQuickTime, &myVersion);
    if (myErr != noErr)
        goto bail;
 
    if (((myVersion >> 16) & 0xffff) >= 0x0400) {
        // we're running under QuickTime 4.0 or greater; we can use the function CreateShortcutMovieFile
 
        myErr = CreateShortcutMovieFile(theFSSpecPtr,
                                        kShortcutFileCreator,
                                        smCurrentScript,
                                        createMovieFileDeleteCurFile | createMovieFileDontCreateResFile,
                                        theDataRef,
                                        theDataRefType);
                                        
    } else {
        // we're running under a version of QuickTime prior to 4.0; do the grunt work ourselves
        
        OSType                          myDataRefType;
        unsigned long                   myAtomHeaderSize;       
        Ptr                             myData = NULL;
        Handle                          myMoovAtom = NULL;
 
        //////////
        //
        // create the atom data that goes into a data reference atom (we will create this atom's
        // header when we create the movie atom that contains it); the atom data is the data reference
        // type followed by the data reference itself
        //
        //////////
 
        myDataRefType = EndianU32_NtoB(theDataRefType);
        myAtomHeaderSize = 2 * sizeof(long);        
 
        // allocate a data block and copy the data reference type and data reference into it
        myData = NewPtrClear(sizeof(OSType) + GetHandleSize(theDataRef));
        if (myData == NULL)
            goto bail;
                
        BlockMove(&myDataRefType, myData, sizeof(OSType));
        BlockMove(*theDataRef, (Ptr)(myData + sizeof(OSType)), GetHandleSize(theDataRef));
 
        //////////
        //
        // create a handle to contain the size and type fields of the movie atom, as well as
        // the size and type fields of the movie data reference alias atom contained in it
        // and of the data reference atom contained in the movie data reference alias atom
        //
        //////////
        
        myMoovAtom = NewHandleClear(3 * myAtomHeaderSize);
        if (myMoovAtom == NULL)
            goto bail;
        
        // fill in the size and type fields of the three atoms
        *((long *)(*myMoovAtom + 0x00)) = EndianU32_NtoB((3 * myAtomHeaderSize) + GetPtrSize(myData));
        *((long *)(*myMoovAtom + 0x04)) = EndianU32_NtoB(MovieAID);
        *((long *)(*myMoovAtom + 0x08)) = EndianU32_NtoB((2 * myAtomHeaderSize) + GetPtrSize(myData));
        *((long *)(*myMoovAtom + 0x0C)) = EndianU32_NtoB(MovieDataRefAliasAID);
        *((long *)(*myMoovAtom + 0x10)) = EndianU32_NtoB((1 * myAtomHeaderSize) + GetPtrSize(myData));
        *((long *)(*myMoovAtom + 0x14)) = EndianU32_NtoB(DataRefAID);
 
        // concatenate the data in myData onto the end of the movie atom
        myErr = PtrAndHand(myData, myMoovAtom, GetPtrSize(myData));
        if (myErr != noErr)
            goto bail;
 
        //////////
        //
        // create the shortcut movie file
        //
        //////////
        
        myErr = QTShortCut_WriteHandleToFile(myMoovAtom, theFSSpecPtr);
        
bail:
        if (myData != NULL) 
            DisposePtr(myData);
 
        if (myMoovAtom != NULL)
            DisposeHandle(myMoovAtom);
    }
 
    return(myErr);
}
 
 
//////////
//
// QTShortCut_WriteHandleToFile
// Write the data in the specified handle into the specified file;
// if the file already exists, it is overwritten.
//
//////////
 
OSErr QTShortCut_WriteHandleToFile (Handle theHandle, FSSpecPtr theFSSpecPtr)
{
    short           myRefNum = 0;
    short           myVolNum;
    long            mySize = 0;
    OSErr           myErr = paramErr;
 
    if (theHandle == NULL)
        goto bail;
 
    mySize = GetHandleSize(theHandle);
    if (mySize == 0)
        goto bail;
 
    HLock(theHandle);
    
    // delete the file;
    // if it doesn't exist yet, we'll get an error (fnfErr), which we just ignore
    myErr = FSpDelete(theFSSpecPtr);
    
    // create and open the file
    myErr = FSpCreate(theFSSpecPtr, kShortcutFileCreator, kShortcutFileType, smSystemScript);
 
    if (myErr == noErr)
        myErr = FSpOpenDF(theFSSpecPtr, fsRdWrPerm, &myRefNum);
    
    // position the file mark to the beginning of the file and write the data
    if (myErr == noErr)
        myErr = SetFPos(myRefNum, fsFromStart, 0);
 
    if (myErr == noErr)
        myErr = FSWrite(myRefNum, &mySize, *theHandle);
 
    if (myErr == noErr)
        myErr = SetFPos(myRefNum, fsFromStart, mySize);
 
    // resize the file to the number of bytes written
    if (myErr == noErr)
        myErr = SetEOF(myRefNum, mySize);
                
    // close the file            
    if (myErr == noErr)     
        myErr = FSClose(myRefNum);
 
#if TARGET_OS_MAC   
    // flush the volume
    if (myErr == noErr)     
        myErr = GetVRefNum(myRefNum, &myVolNum);
 
    if (myErr == noErr)     
        myErr = FlushVol(NULL, myVolNum);
#endif  // TARGET_OS_MAC    
 
bail:
    HUnlock(theHandle);
 
    return(myErr);
}