AddFlashActions.c

//////////
//
//  File:       AddFlashActions.c
//
//  Contains:   Sample code for adding actions to a Flash track in a movie.
//
//  Written by: Tim Monroe
//              Based on existing code by Bill Wright.
//
//  Copyright:  © 1999 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <3>      08/24/01    rtm     finally fully working
//     <2>      07/20/99    rtm     further work; removed reliance on AtomUtilities.c
//     <1>      07/16/99    rtm     first file from bw; revised to sample code coding style
//     
//////////
 
#include "AddFlashActions.h"
 
 
extern FlashParserStruct            gFlashParserData;
static long                         gFlashConditions[] = {
                                                            kIdleToOverUp,
                                                            kOverUpToIdle,
                                                            kOverUpToOverDown,
                                                            kOverDownToOverUp,
                                                            
                                                            kOverDownToOutDown,
                                                            kOutDownToOverDown,
                                                            kOutDownToIdle,
                                                            
                                                            kIdleToOverDown,
                                                            kOverDownToIdle
                                                        };
 
 
//////////
//
// main/WinMain 
// The main function for this application.
//
//////////
 
#if TARGET_OS_MAC
void main (void)
#elif TARGET_OS_WIN32
int CALLBACK WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR theCmdLine, int nCmdShow)
#endif
{
    OSType                  myTypeList[1] = {MovieFileType};
    StandardFileReply       myReply;
    OSErr                   myErr = noErr;
 
#if TARGET_OS_WIN32
    InitializeQTML(0L);                                         // initialize QuickTime Media Layer
#endif  
 
#if TARGET_OS_MAC
    MaxApplZone();                                              // init everything
    InitGraf(&qd.thePort);
    InitFonts();
    FlushEvents(everyEvent, 0);
    InitWindows();
    InitMenus();
    InitDialogs(NULL);
    TEInit();
    InitCursor();
#endif  
    
    myErr = EnterMovies();
    if (myErr != noErr)
        goto bail;
 
    // elicit a movie file from the user
    StandardGetFile(NULL, 1, myTypeList, &myReply);
 
    // add some wired actions to it, if it's a movie with a Flash track
    if (myReply.sfGood)
        AddFLAct_AddWiredActionsToFlashMovie(&myReply.sfFile);
        
bail:
    ExitMovies();
 
#if TARGET_OS_WIN32
    // terminate the QuickTime Media Layer
    TerminateQTML();
    return(1);
#endif  
 
#if TARGET_OS_MAC
    return;
#endif  
}
 
 
//////////
//
// AddFLAct_CreateButtonActionContainer 
// Return, through the theActions parameter, an atom container that contains a button action.
//
//////////
 
static OSErr AddFLAct_CreateButtonActionContainer (QTAtomContainer *theActions)
{
    QTAtom          myEventAtom = 0;
    QTAtom          myActionAtom = 0;
    long            myAction;
    Fixed           myRate;
    OSErr           myErr = noErr;
 
    myErr = QTNewAtomContainer(theActions);
    if (myErr != noErr)
        goto bail;
    
    //////////
    //
    // add an action that sets the movie rate to 1 on an up-to-down mouse-click event
    //
    //////////
    
    myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kQTEventType, kOverUpToOverDown, kIndexOne, kZeroDataLength, NULL, &myEventAtom);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
    if (myErr != noErr)
        goto bail;
        
    myAction = EndianS32_NtoB(kActionMovieSetRate);
    myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
    if (myErr != noErr)
        goto bail;
    
    myRate = EndianU32_NtoB(fixed1);
    myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, sizeof(Fixed), &myRate, NULL);
    if (myErr != noErr)
        goto bail;
 
    //////////
    //
    // add an action that sets the movie rate to 0 on a down-to-up mouse-click event
    //
    //////////
    
    myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kQTEventType, kOverDownToOverUp, kIndexOne, kZeroDataLength, NULL, &myEventAtom);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
    if (myErr != noErr)
        goto bail;
    
    myAction = EndianS32_NtoB(kActionMovieSetRate);
    myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
    if (myErr != noErr)
        goto bail;
    
    myRate = EndianU32_NtoB(0);
    myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, sizeof(Fixed), &myRate, NULL);
 
bail:
    return(myErr);
}
 
 
//////////
//
// AddFLAct_CreateFrameLoadedActionContainer 
// Return, through the theActions parameter, an atom container that contains a frame-loaded event action.
//
//////////
 
static OSErr AddFLAct_CreateFrameLoadedActionContainer (QTAtomContainer *theActions)
{
    QTAtom          myEventAtom = 0;
    QTAtom          myActionAtom = 0;
    long            myAction;
    QTAtomID        myCursorID;
    OSErr           myErr = noErr;
    
    myErr = QTNewAtomContainer(theActions);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTInsertChild(*theActions, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myEventAtom);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTInsertChild(*theActions, myEventAtom, kAction, kIndexOne, kIndexOne, kZeroDataLength, NULL, &myActionAtom);
    if (myErr != noErr)
        goto bail;
    
    myAction = EndianS32_NtoB(kActionTrackSetCursor);
    myErr = QTInsertChild(*theActions, myActionAtom, kWhichAction, kIndexOne, kIndexOne, sizeof(long), &myAction, NULL);
    if (myErr != noErr)
        goto bail;
    
    myCursorID = EndianS32_NtoB(kQTCursorOpenHand);
    myErr = QTInsertChild(*theActions, myActionAtom, kActionParameter, kIndexOne, kIndexOne, sizeof(QTAtomID), &myCursorID, NULL);
 
bail:
    return(myErr);
}   
 
 
//////////
//
// AddFLAct_SetWiredActionToButton 
// Modify the flash sample to insert or remove actions for the specified mouse condition for the specified button.
//
//////////
 
static void AddFLAct_SetWiredActionToButton (Handle theSample, long theButtonID, U16 theCondition, QTAtomContainer theAction)
{
    
    short       myActionCount;
    short       myActionIndex;
    U32         myOffset;
    U32         myStart;
    U32         myActionOffset;
    U32         myActionLength;
    U32         myDataLength;
    U32         myStartActionOffset;
    U32         myOffsetLocation;
    U32         myButtonRecordLength;
    U32         myPrevOffset;
    U32         myPrevStart;
    U16         myLocalCondition;
    long        myActionHandleSize;
    long        myDataHandleSize;
    long        myShortenAmount;
    long        myIncreaseAmount;
    long        myMoveAmount;
    long        myDifference;
    U8          myActionCode;
    U8          *myArray;
    Ptr         myPtr = NULL;
    Boolean     isFound = false;
    OSErr       myErr = noErr;
    
    gFlashParserData.m_theData = theSample;
    HLock(theSample);
 
    myOffset = GetOffsetForButton(theButtonID);
    
    gFlashParserData.m_fileBuf = (U8 *)*theSample;
    gFlashParserData.m_filePos = myOffset;
    
    (void)GetTag();
    gFlashParserData.m_filePos += sizeof(U16);      // step over tagID
    gFlashParserData.m_filePos += sizeof(U8);       // step over menu flag
 
    myOffsetLocation = gFlashParserData.m_filePos;
    myButtonRecordLength = 0;
    myActionCount = 0;
    
    myOffset = (U32)GetWord();
    if (myOffset == 0) {
        // no current actions
        myStartActionOffset = gFlashParserData.m_tagEnd;
        myButtonRecordLength = gFlashParserData.m_tagEnd - myOffsetLocation;
    } else {
        myActionOffset = 0;
                
        gFlashParserData.m_filePos += myOffset - sizeof(U16);
        myStartActionOffset = gFlashParserData.m_filePos;
        myPrevStart = 0;
        
        // locate current actions for the specified condition
        while (gFlashParserData.m_filePos < gFlashParserData.m_tagEnd) {
            myStart = gFlashParserData.m_filePos;
            myActionCount++;
            myOffset = (U32)GetWord();
            myLocalCondition = GetWord();
            
            myActionCode = GetByte();
            if ((myActionCode == sactionWiredActions) && (myLocalCondition == theCondition)) {
                myActionIndex = myActionCount;
                myActionOffset = myStart;
                myActionLength = myOffset;
                myPrevOffset = myPrevStart;
                myDataLength = (U32)GetWord();
                isFound = true;         
            }
            
            if (!myOffset)
                break;
 
            myPrevStart = myStart;
            gFlashParserData.m_filePos = myStart + myOffset;
        }
    }
 
    HUnlock(theSample);
    myDataHandleSize = GetHandleSize(theSample);
 
    if (isFound) {
        if (theAction != NULL) {
            // replace with new data
            myActionHandleSize = GetHandleSize((Handle)theAction);  
            
            if (myActionHandleSize == myDataLength) {
                // just replace 
                HLock(theSample);   
                HLock((Handle)theAction);
                BlockMove(  *theAction, 
                            (*theSample + myActionOffset + sizeof(U16) + sizeof(U16) + sizeof(U8) + sizeof(U16)),
                            myActionHandleSize);
                HUnlock((Handle)theAction);
                HUnlock(theSample);
            } else {
                myMoveAmount = myDataHandleSize - (myActionOffset + myActionLength);
 
                if (myActionHandleSize < myDataLength) {
                    // compact space for new data
                    myShortenAmount = myDataLength - myActionHandleSize;
                    myDifference = -myShortenAmount;
                    
                    HLock(theSample);   
                    BlockMove(  *theSample + (myActionOffset + myActionLength), 
                                *theSample + (myActionOffset + myActionLength - myShortenAmount),
                                myMoveAmount);
                    HUnlock(theSample);
 
                    myDataHandleSize -= myShortenAmount;                
 
                    SetHandleSize(theSample, myDataHandleSize);
                    myErr = MemError();
                    if (myErr != noErr)
                        goto bail;
                } else {
                    // expand space for new data
                    myIncreaseAmount = myActionHandleSize - myDataLength;
                    myDataHandleSize += myIncreaseAmount;
                    myDifference = myIncreaseAmount;
                    
                    SetHandleSize(theSample, myDataHandleSize);
                    myErr = MemError();
                    if (myErr != noErr)
                        goto bail;
                    
                    HLock(theSample);   
                    BlockMove(  *theSample + (myActionOffset + myActionLength), 
                                (*theSample + myActionOffset + myActionLength + myIncreaseAmount),
                                myMoveAmount);
                    HUnlock(theSample);             
                }
                                            
                HLock(theSample);   
                HLock((Handle)theAction);
                
                // patch in action length
                myArray = (U8 *)*theSample + myActionOffset;
                myActionLength += myDifference;
                myArray[0] = myActionLength & 0xFF;
                myArray[1] = (myActionLength >> 8) & 0xFF;
                
                // patch in action container length
                myArray = (U8 *)*theSample + myActionOffset + sizeof(U16) + sizeof(U16) + sizeof(U8);
                myArray[0] = myActionHandleSize & 0xFF;
                myArray[1] = (myActionHandleSize >> 8) & 0xFF;
                
                // copy new data
                BlockMove(  *theAction, 
                            (*theSample + myActionOffset + sizeof(U16) + sizeof(U16) + sizeof(U8) + sizeof(U16)),
                            myActionHandleSize);
                HUnlock((Handle)theAction);
                HUnlock(theSample);
                                            
                // adjust tag length and data stream header length
                SetNewHeaderLengthTagLength(myDifference, myDifference);
            }
        } else {                
            // remove old data
            HLock(theSample);
            if (myActionIndex == myActionCount) {   // last action
                if (myActionCount == 1) {           
                    // need to clear all action data
                    myMoveAmount = myDataHandleSize - gFlashParserData.m_tagEnd;
                    BlockMove(*theSample + gFlashParserData.m_tagEnd, *theSample + myStartActionOffset, myMoveAmount);
 
                    myShortenAmount = gFlashParserData.m_tagEnd - myStartActionOffset;
                    myDataHandleSize -= myShortenAmount;
                    
                    myPtr = *theSample + myOffsetLocation;
                    *(U8 *)myPtr++ = (myButtonRecordLength & 0xFF);     
                    *(U8 *)myPtr++ = ((myButtonRecordLength >> 8) & 0xFF);      
                } else {
                    myMoveAmount = myDataHandleSize - gFlashParserData.m_tagEnd;
                    BlockMove(*theSample + gFlashParserData.m_tagEnd, *theSample + myActionOffset, myMoveAmount);
 
                    myShortenAmount = gFlashParserData.m_tagEnd - myActionOffset;                   
                    myDataHandleSize -= myShortenAmount;
                    
                    // fixup previous offset to zero, because it is now last action                 
                    myPtr = *theSample + myPrevOffset;
                    *(U8 *)myPtr++ = (myButtonRecordLength & 0xFF);     
                    *(U8 *)myPtr++ = ((myButtonRecordLength >> 8) & 0xFF);  
                }           
            } else {
                myShortenAmount = sizeof(U16) + sizeof(U16) + sizeof(U8) + sizeof(U16) + myDataLength  + sizeof(U8);
                myMoveAmount = myDataHandleSize - (myActionOffset + myShortenAmount);
 
                BlockMove(*theSample + myActionOffset + myShortenAmount, *theSample + myActionOffset, myMoveAmount);
 
                myDataHandleSize -= myShortenAmount;
            }
            
            // adjust sample length
            HUnlock(theSample);
            SetHandleSize(theSample, myDataHandleSize);
 
            myErr = MemError();
            if (myErr != noErr)
                goto bail;
            
            // fixup button taglength and data stream length
            SetNewHeaderLengthTagLength(-myShortenAmount, -myShortenAmount);
        }
    } else {
        if (theAction != NULL) {
            // insert new data
            myActionHandleSize = GetHandleSize((Handle)theAction);
            myMoveAmount = myDataHandleSize - myStartActionOffset;          
            myIncreaseAmount = sizeof(U16) + sizeof(U16) + sizeof(U8) + sizeof(U16) + myActionHandleSize + sizeof(U8);
            myDataHandleSize += myIncreaseAmount;
            
            SetHandleSize(theSample, myDataHandleSize);
            myErr = MemError();
            if (myErr != noErr)
                goto bail;
 
            HLock(theSample);
            HLock((Handle)theAction);
 
            BlockMove(*theSample + myStartActionOffset, *theSample + myStartActionOffset + myIncreaseAmount, myMoveAmount);
 
            // patch in length of action data which is myOffset to next action
            myPtr = *theSample + myStartActionOffset;
 
            if (myActionCount) {
                *myPtr++ = (myIncreaseAmount & 0xFF);
                *myPtr++ = ((myIncreaseAmount >> 8) & 0xFF);
            } else {
                *(U16 *)myPtr = 0;
                myPtr += sizeof(U16);
            }
            
            // patch in condition
            *(U8 *)myPtr++ = (theCondition & 0xFF);     
            *(U8 *)myPtr++ = ((theCondition >> 8) & 0xFF);      
 
            // patch in wired action constant and length of action container
            *(U8 *)myPtr = sactionWiredActions;
            myPtr += sizeof(U8);
            *(U8 *)myPtr++ = (myActionHandleSize & 0xFF);       
            *(U8 *)myPtr++ = ((myActionHandleSize >> 8) & 0xFF);        
 
            // copy in new data
            BlockMove(*theAction, myPtr, myActionHandleSize);
            *(myPtr + myActionHandleSize) = 0;
            
            // patch in magic myOffset to start of actions at beginning of button data if needed
            if (myActionCount == 0) {
                myPtr = *theSample + myOffsetLocation;
                
                *(U8 *)myPtr++ = (myButtonRecordLength & 0xFF);
                *(U8 *)myPtr++ = ((myButtonRecordLength >> 8) & 0xFF);      
            }
            
            HUnlock((Handle)theAction);
            HUnlock(theSample);         
 
            // fixup button taglength and data stream length
            SetNewHeaderLengthTagLength(myIncreaseAmount, myIncreaseAmount);
        }
    }
    
bail:
    return;
}
 
 
//////////
//
// AddFLAct_SetWiredActionsToButton 
// Step through the action container to add individual actions to the button data stream.
//
// This assumes that the actions are created in an editor for all actions for the button.
//
//////////
 
static OSErr AddFLAct_SetWiredActionsToButton (Handle theSample, long theButtonID, QTAtomContainer theActions)
{
    short               myIndex;
    QTAtomContainer     myActionContainer;
    QTAtom              myEventAtom = 0;
    QTAtomID            myEventID;
    OSErr               myErr;
 
    myErr = QTNewAtomContainer(&myActionContainer);
    if (myErr != noErr)
        goto bail;
    
    for (myIndex = 0; myIndex < (sizeof(gFlashConditions) / sizeof(long)); myIndex++) {
        myEventID = gFlashConditions[myIndex];
        
        myEventAtom = QTFindChildByID(theActions, kParentAtomIsContainer, kQTEventType, myEventID, NULL);
        if (myEventAtom != 0) {     
 
            myErr = AddFLAct_CopyChildren(theActions, myEventAtom, myActionContainer, kParentAtomIsContainer);
            if (myErr != noErr)
                goto bail;
            
            AddFLAct_SetWiredActionToButton(theSample, theButtonID, myEventID, myActionContainer);
            
            myErr = QTRemoveChildren(myActionContainer, kParentAtomIsContainer);
            if (myErr != noErr)
                goto bail;
        } else {
            AddFLAct_SetWiredActionToButton(theSample, theButtonID, myEventID, NULL);
        }
    }
    
    myErr = QTDisposeAtomContainer(myActionContainer);
    if (myErr != noErr)
        goto bail;
    
bail:
    return(myErr);
}
 
 
//////////
//
// AddFLAct_SetFrameLoadedWiredActions 
// Add actions to frame data stream.
//
//////////
 
static OSErr AddFLAct_SetFrameLoadedWiredActions (Handle theSample, long theFrameID, QTAtomContainer theActions)
{
    
    short               myActionCount;
    U32                 myOffset;
    U32                 myEndOffset;
    U32                 myStart;
    U32                 myActionOffset = 0;
    U32                 myActionDataOffset;
    long                myActionHandleSize;
    long                myDataHandleSize;
    long                myNewHandleSize;
    long                myShortenAmount;
    long                myIncreaseAmount;
    long                myMoveAmount;
    long                myDifference = 0;
    long                myFileDifference = 0;
    long                myTagDifference;
    QTAtomContainer     myNewContainer = NULL;
    QTAtom              myEventAtom = 0;
    U8                  myActionCode;
    U8                  myNextActionCode;
    Ptr                 myPtr = NULL;
    OSErr               myErr = noErr;
    
    gFlashParserData.m_theData = theSample;
    HLock(theSample);
 
    myDataHandleSize = GetHandleSize(theSample);
 
    GetOffsetForFrame(theFrameID, &myOffset, &myEndOffset);
    
    if (myOffset != 0) {
        gFlashParserData.m_fileBuf = (U8 *)*theSample;
        gFlashParserData.m_filePos = myOffset;
        
        // find any current flash doAction data
        while (gFlashParserData.m_filePos < myEndOffset) {
            U16         myCode;
            U32         myTagEnd;
            
            myStart = gFlashParserData.m_filePos;
 
            // get the current tag.
            myCode = GetTag();
 
            // Get the tag ending position.
            myTagEnd = gFlashParserData.m_tagEnd;
            
            if (myCode == stagDoAction) {
                if (myActionOffset == 0) {
                    myActionOffset = myStart;
                    myActionDataOffset = gFlashParserData.m_filePos;
                }
                
                myActionCount = 0;
                
                // search for any previous wired actions
                do {
                    myActionCount++;
                    myActionCode = GetByte();
                    if (myActionCode == sactionWiredActions) {
                        U32         myContainerLength = (U32)GetWord();
 
                        myMoveAmount = myDataHandleSize - (gFlashParserData.m_filePos + myContainerLength);
                        
                        if (theActions != NULL) {
                            // replace
                            myEventAtom = QTFindChildByID(theActions, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, NULL);
                            if (myEventAtom == 0)
                                goto bail;
                            
                            myErr = QTNewAtomContainer(&myNewContainer);
                            if (myErr != noErr)
                                goto bail;
                            
                            myErr = AddFLAct_CopyChildren(theActions, myEventAtom, myNewContainer, kParentAtomIsContainer);
                            if (myErr != noErr)
                                goto bail;
                            
                            myActionHandleSize = GetHandleSize(myNewContainer);
                            
                            if (myContainerLength != myActionHandleSize) {
                                // need to create space
 
                                if (myContainerLength > myActionHandleSize) {
                                    // compact space for new data
                                    myShortenAmount = myContainerLength - myActionHandleSize;
                                    myFileDifference = -myShortenAmount;
                                    myTagDifference = myFileDifference;
                                    
                                    BlockMove(  *theSample + gFlashParserData.m_filePos + myContainerLength,
                                                *theSample + gFlashParserData.m_filePos + myContainerLength - myShortenAmount,
                                                myMoveAmount);
 
                                    HUnlock(theSample);
 
                                    myNewHandleSize = myDataHandleSize - myShortenAmount;
                                    
                                    SetHandleSize(theSample, myNewHandleSize);
                                    myErr = MemError();
                                    if (myErr != noErr)
                                        goto bail;
                                    
                                    HLock(theSample);
 
                                } else {
                                    // expand space for new data
                                    myIncreaseAmount = myActionHandleSize - myContainerLength;
                                    myFileDifference = myIncreaseAmount;
                                    myTagDifference = myFileDifference;
                                    
                                    myNewHandleSize = myDataHandleSize + myIncreaseAmount;
                                    
                                    HUnlock(theSample);
 
                                    SetHandleSize(theSample, myNewHandleSize);
                                    myErr = MemError();
                                    if (myErr != noErr)
                                        goto bail;
 
                                    HLock(theSample);
                                    
                                    BlockMove(*theSample + gFlashParserData.m_filePos + myContainerLength,
                                                *theSample + gFlashParserData.m_filePos + myContainerLength + myIncreaseAmount,
                                                myMoveAmount);
                                }
                            }
                            
                            // copy in new data
                            HLock ((Handle) myNewContainer);
                            BlockMove (*myNewContainer, *theSample + gFlashParserData.m_filePos, myActionHandleSize);
                            HUnlock ((Handle) myNewContainer);
                            
                            // patch in action length
                            myPtr = *theSample + gFlashParserData.m_filePos - sizeof(U16);
    
                            myPtr[0] = (myActionHandleSize & 0xFF);
                            myPtr[1] = ((myActionHandleSize >> 8) & 0xFF);
 
                        } else {    
                            // remove
                            myNextActionCode = *(U8 *)(*theSample + gFlashParserData.m_filePos + myContainerLength);
                            if ((myActionCount == 1) && (myNextActionCode == 0)) {
                                // need to remove whole tag
                                
                                myMoveAmount = myDataHandleSize - myTagEnd;
                                myFileDifference = myActionOffset - myTagEnd;
                                myTagDifference = 0;
                                
                                myNewHandleSize = myDataHandleSize - (myTagEnd - myActionOffset);
                                BlockMove(*theSample + myTagEnd, *theSample + myActionOffset, myMoveAmount);
                            } else {
                                // compact space to remove unneeded data
                                
                                myFileDifference = -(myContainerLength + sizeof(U8) + sizeof(U16));         // U32
                                myTagDifference = myFileDifference;
                                
                                myNewHandleSize = myDataHandleSize + myFileDifference;
                                
                                BlockMove(  *theSample + gFlashParserData.m_filePos + myContainerLength,
                                            *theSample + gFlashParserData.m_filePos - (sizeof(U8) + sizeof(U16)),
                                            myMoveAmount);                                                  // U32
                            }
                            
                            SetHandleSize (theSample, myNewHandleSize);
                            myErr = MemError();
                            if (myErr != noErr)
                                goto bail;
                        }
                        
                        if (myFileDifference) {
                            // fixup frame taglength and data stream length
                            SetNewHeaderLengthTagLength (myFileDifference, myTagDifference);
                        }
                        goto bail;
                    } else {
                        if ((myActionCode & 0x80) != 0) {
                            U32     myContainerLength = (U32)GetWord();
                            
                            gFlashParserData.m_filePos += myContainerLength - 1;
                        }
                    }
                }
                while (myActionCode);
            }
            
            // increment past the tag.
            gFlashParserData.m_filePos = myTagEnd;
        }
 
        // not found - therefore insert 
        
        if (theActions != NULL) {
            // do we really have something to insert?
            myEventAtom = QTFindChildByID(theActions, kParentAtomIsContainer, kQTEventFrameLoaded, kIndexOne, NULL);
            if (myEventAtom == 0)
                goto bail;
            
            myErr = QTNewAtomContainer(&myNewContainer);
            if (myErr != noErr)
                goto bail;
            
            myErr = AddFLAct_CopyChildren(theActions, myEventAtom, myNewContainer, kParentAtomIsContainer);
            if (myErr != noErr)
                goto bail;
 
            myActionHandleSize = GetHandleSize(myNewContainer);
 
            if (myActionOffset) {
                // then we already have actions - just insert at front
                
                // expand space for new data
                gFlashParserData.m_tagStart = myActionOffset;
                myDifference = sizeof(U8) + sizeof(U16) + myActionHandleSize;           // U32
                myNewHandleSize = myDataHandleSize + myDifference;
                myMoveAmount = myDataHandleSize - myActionDataOffset;                   // myActionOffset;
                
                HUnlock(theSample);
                SetHandleSize(theSample, myNewHandleSize);
                myErr = MemError();
                if (myErr != noErr)
                    goto bail;
            
                HLock(theSample);
                myPtr = *theSample + myActionDataOffset;                                // myActionOffset;
 
                BlockMove(myPtr, myPtr + sizeof(U8) + sizeof(U16) + myActionHandleSize, myMoveAmount);
                            
                // patch in our action code
                *(U8 *)myPtr = sactionWiredActions;
                myPtr += sizeof(U8);
 
                // patch in data length
                myPtr[0] = (myActionHandleSize & 0xFF);
                myPtr[1] = ((myActionHandleSize >> 8) & 0xFF);
 
                // copy in new data
                myPtr += sizeof(U16);
                
                HLock((Handle)myNewContainer);
                BlockMove(*myNewContainer, myPtr, myActionHandleSize);
                HUnlock((Handle)myNewContainer);
                
                myFileDifference = myDifference;
                myTagDifference = myDifference;
            } else {
                // no other actions - create new doAction event
                U16     myNewCode = stagDoAction << 6;
                
                gFlashParserData.m_tagStart = myEndOffset;
                 
                // expand space for new data
                myDifference = sizeof(U16) + sizeof(U8) + sizeof(U16) + myActionHandleSize + sizeof(U8);
                myNewHandleSize = myDataHandleSize + myDifference;
                myMoveAmount = myDataHandleSize - myEndOffset;
                
                HUnlock(theSample);
                SetHandleSize(theSample, myNewHandleSize);
                myErr = MemError();
                if (myErr != noErr)
                    goto bail;
            
                HLock(theSample);
                myPtr = *theSample + myEndOffset;
 
                if (myMoveAmount != 0)
                    BlockMove(myPtr, myPtr + myDifference, myMoveAmount);
                
                // patch in doAction code
                *(U8 *)myPtr = (myNewCode & 0xFF);
                myPtr += sizeof(U8);
 
                *(U8 *)myPtr = ((myNewCode >> 8) & 0xFF);
                myPtr += sizeof(U8);
 
                // patch in wired action code
                *(U8 *)myPtr = sactionWiredActions;
                myPtr += sizeof(U8);
 
                // patch in data length
                myPtr[0] = (myActionHandleSize & 0xFF);
                myPtr[1] = ((myActionHandleSize >> 8) & 0xFF);
 
                // copy in new data
                myPtr += sizeof(U16);
                
                HLock((Handle) myNewContainer);
                BlockMove(*myNewContainer, myPtr, myActionHandleSize);
                HUnlock((Handle) myNewContainer);
                
                // patch in end marker
                myPtr += myActionHandleSize;
                *(U8 *)myPtr = 0;               // action terminator
                
                myFileDifference = myDifference;
                myTagDifference = myDifference - sizeof(U16);
            }
            
            HUnlock(theSample);
 
            // fixup frame taglength and data stream length
            SetNewHeaderLengthTagLength(myFileDifference, myTagDifference);
        }
        // else     // nothing to do!
    }
 
bail:
    if (myNewContainer != NULL)
        (void)QTDisposeAtomContainer(myNewContainer);
    
    return(myErr);
}
 
 
///////////
//
// AddFLAct_AddWiredActionsToFlashMovie 
// Add some wired actions to the specified Flash movie.
//
//////////
 
static void AddFLAct_AddWiredActionsToFlashMovie (FSSpec *theFSSpec)
{
    short                           myResID = 0;
    short                           myResRefNum;
    Movie                           myMovie = NULL;
    Track                           myTrack = NULL;
    Media                           myMedia = NULL;
    TimeValue                       myTrackOffset;
    TimeValue                       myMediaTime;
    TimeValue                       mySampleDuration;
    TimeValue                       mySelectionDuration;
    TimeValue                       myNewMediaTime;
    FlashDescriptionHandle          myFlashDesc = NULL;
    Handle                          mySample = NULL;
    short                           mySampleFlags;
    Fixed                           myTrackEditRate;
    QTAtomContainer                 myActions = NULL;   
    long                            myButtonID = 0L;                        
    OSErr                           myErr = noErr;
 
    //////////
    //
    // open the movie file and get the first Flash track from the movie
    //
    //////////
    
    // open the movie file for reading and writing
    myErr = OpenMovieFile(theFSSpec, &myResRefNum, fsRdWrPerm);
    if (myErr != noErr)
        goto bail;
 
    myErr = NewMovieFromFile(&myMovie, myResRefNum, &myResID, NULL, newMovieActive, NULL);
    if (myErr != noErr)
        goto bail;
        
    // find the first Flash track in the movie;
    myTrack = GetMovieIndTrackType(myMovie, kIndexOne, FlashMediaType, movieTrackMediaType);
    if (myTrack == NULL)
        goto bail;
    
    //////////
    //
    // get first media sample in the Flash track
    //
    //////////
    
    myMedia = GetTrackMedia(myTrack);
    if (myMedia == NULL)
        goto bail;
    
    myTrackOffset = GetTrackOffset(myTrack);
    myMediaTime = TrackTimeToMediaTime(myTrackOffset, myTrack);
 
    // allocate some storage to hold the sample description for the Flash track
    myFlashDesc = (FlashDescriptionHandle)NewHandle(4);
    if (myFlashDesc == NULL)
        goto bail;
 
    mySample = NewHandle(0);
    if (mySample == NULL)
        goto bail;
 
    myErr = GetMediaSample(myMedia, mySample, 0, NULL, myMediaTime, NULL, &mySampleDuration, (SampleDescriptionHandle)myFlashDesc, NULL, 1, NULL, &mySampleFlags);
    if (myErr != noErr)
        goto bail;
 
    //////////
    //
    // add frame-loaded actions
    //
    //////////
 
    // create an action container for frame-loaded actions
    myErr = AddFLAct_CreateFrameLoadedActionContainer(&myActions);
    if (myErr != noErr)
        goto bail;
    
    // add frame-loaded actions to sample
    myErr = AddFLAct_SetFrameLoadedWiredActions(mySample, kIndexZero, myActions);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTDisposeAtomContainer(myActions);
    if (myErr != noErr)
        goto bail;
 
    //////////
    //
    // add button actions
    //
    //////////
    
    // find the first button
    myErr = LocateFirstButton(mySample, &myButtonID);
    if ((myErr != noErr) || (myButtonID == 0))
        goto bail;
    
    // create an action container for button actions
    myErr = AddFLAct_CreateButtonActionContainer(&myActions);
    if (myErr != noErr)
        goto bail;
    
    // add button actions to sample     
    myErr = AddFLAct_SetWiredActionsToButton(mySample, myButtonID, myActions);
    if (myErr != noErr)
        goto bail;
 
    //////////
    //
    // replace sample in media
    //
    //////////
    
    myTrackEditRate = GetTrackEditRate(myTrack, myTrackOffset);
    if (GetMoviesError() != noErr)
        goto bail;
 
    GetTrackNextInterestingTime(myTrack, nextTimeMediaSample | nextTimeEdgeOK, myTrackOffset, fixed1, NULL, &mySelectionDuration);
    if (GetMoviesError() != noErr)
        goto bail;
 
    myErr = DeleteTrackSegment(myTrack, myTrackOffset, mySelectionDuration);
    if (myErr != noErr)
        goto bail;
        
    myErr = BeginMediaEdits(myMedia);
    if (myErr != noErr)
        goto bail;
    
    myErr = AddMediaSample( myMedia,
                            mySample,
                            0,
                            GetHandleSize(mySample),
                            mySampleDuration,
                            (SampleDescriptionHandle)myFlashDesc, 
                            1,
                            mySampleFlags,
                            &myNewMediaTime);
    if (myErr != noErr)
        goto bail;
    
    myErr = EndMediaEdits(myMedia);
    if (myErr != noErr)
        goto bail;
    
    // add the media to the track
    myErr = InsertMediaIntoTrack(myTrack, myTrackOffset, myNewMediaTime, mySelectionDuration, myTrackEditRate);
    if (myErr != noErr)
        goto bail;
 
    //////////
    //
    // update the movie resource
    //
    //////////
    
    myErr = UpdateMovieResource(myMovie, myResRefNum, myResID, NULL);
                
bail:
    // close the movie file
    if (myResRefNum != -1)
        CloseMovieFile(myResRefNum);
 
    if (myActions != NULL)
        (void)QTDisposeAtomContainer(myActions);
    
    if (mySample != NULL)
        DisposeHandle(mySample);        
    
    if (myFlashDesc != NULL)
        DisposeHandle((Handle)myFlashDesc);     
    
    if (myMovie != NULL)
        DisposeMovie(myMovie);  
}
 
 
///////////
//
// AddFLAct_CopyAtomAndChildren 
// Copy an atom and all its the children from one atom container to another.
//
//////////
 
static OSErr AddFLAct_CopyAtomAndChildren (QTAtomContainer theSrcContainer, QTAtom theSrcAtom, QTAtomContainer theDstContainer, QTAtom theDstAtom)
{
    QTAtomContainer     myCopyAtomContainer = NULL;
    OSErr               myErr = noErr;
 
    myErr = QTCopyAtom(theSrcContainer, theSrcAtom, &myCopyAtomContainer);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTInsertChildren(theDstContainer, theDstAtom, myCopyAtomContainer);
    if (myErr != noErr)
        goto bail;
    
    myErr = QTDisposeAtomContainer(myCopyAtomContainer);
 
bail:
    return(myErr);
}
 
 
///////////
//
// AddFLAct_CopyChildren 
// Copy the children in one atom container to another.
//
//////////
 
static OSErr AddFLAct_CopyChildren (QTAtomContainer theSrcContainer, QTAtom theSrcAtom, QTAtomContainer theDstContainer, QTAtom theDstAtom)
{
    QTAtom      myCurrentAtom = 0;
    QTAtom      myNextAtom = 0;
    OSErr       myErr = noErr;
 
    do {
        myErr = QTNextChildAnyType(theSrcContainer, theSrcAtom, myCurrentAtom, &myNextAtom);
        if (myErr != noErr)
            goto bail;
 
        if (myNextAtom != 0) {
            myErr = AddFLAct_CopyAtomAndChildren(theSrcContainer, myNextAtom, theDstContainer, theDstAtom);
            if (myErr != noErr)
                goto bail;
        }
 
        myCurrentAtom = myNextAtom;
    } while (myNextAtom != 0);
 
bail:
    return(myErr);
}