G4Utilities.c

/*
    File:       G4Utilities.c
 
    Contains:   xxx put contents here xxx
 
    Version:    xxx put version here xxx
 
    Copyright:  © 1998 by Apple Computer, Inc., all rights reserved.
 
    File Ownership:
 
        DRI:                xxx put dri here xxx
 
        Other Contact:      xxx put other contact here xxx
 
        Technology:         xxx put technology here xxx
 
    Writers:
 
        (sjb)   Steve Bollinger
 
    Change History (most recent first):
 
         <2>      7/1/98    sjb     Update to CWPro 2
*/
 
 
//============================================================================
//----------------------------------------------------------------------------
//                                  Utilities.c
//----------------------------------------------------------------------------
//============================================================================
 
// These functions are sort of universal utility functions.  They aren't specificÉ
// to Glypha per se.  I use these (and others) in many, many games.  Many of themÉ
// as well are useful for any app you might write for the Mac.
 
#include "G4Externs.h"
#include <Processes.h>
#include <SegLoad.h>
#include <Traps.h>
#include <Resources.h>
#include <TextUtils.h>
#include <LowMem.h>
 
#define kActive                     0
#define kInactive                   255
 
 
GDHandle    thisGDevice;
long        tickNext;
 
 
//==============================================================  Functions
//--------------------------------------------------------------  RandomInt
 
// Takes a short (range) and returns a random number from zero to range - 1.
 
short RandomInt (short range)
{
    register long   rawResult;
    
    rawResult = Random();
    if (rawResult < 0L)
        rawResult *= -1L;
    rawResult = (rawResult * (long)range) / 32768L;
    
    return ((short)rawResult);
}
 
//--------------------------------------------------------------  RedAlert
 
// Generic error function.  This is called when there is no hope of recovering
// from the error.  A simple alert is brought up and the text passed in (theStr)
// is displayed.  When the user clicks the Okay button, we quit to the Finder.
 
void RedAlert (StringPtr theStr)
{
    #define     kRedAlertID     128
    short       whoCares;
 
#if GENERATINGPOWERPC
    // make sure we are at full color
    DSpContext_FadeGammaIn( NULL, NULL );
    if( gTheContext )
    {
        DSpContext_SetState( gTheContext, kDSpContextState_Inactive );
        DSpContext_Release( gTheContext );
        DSpShutdown();
    }
    
#endif  
 
    ParamText(theStr, "\p", "\p", "\p");        // Replace ^0 in alert with error mssg.
    whoCares = Alert(kRedAlertID, 0L);          // Bring up alert.
    ExitToShell();                              // Quit to Finder.
}
 
//--------------------------------------------------------------  FindOurDevice
 
// Get a handle to the MainDevice (monitor with the Menubar).
 
void FindOurDevice (void)
{
    thisGDevice = GetMainDevice();
    if (thisGDevice == 0L)                      // If a nil handle is returned...
        RedAlert("\pCouldn't Find Our Device"); // call our universal error alert.
}
 
//--------------------------------------------------------------  LoadGraphic
 
// Handy function that loads a PICT graphic, get's its bounds and draws it.
// The port drawn to is assumed the current port.  No scaling is done.
 
void LoadGraphic (short resID)
{
    Rect        bounds;
    PicHandle   thePicture;
    
    thePicture = GetPicture(resID);             // Load graphic from resource fork.
    if (thePicture == 0L)                       // Check to see if nil (did it load?)
        RedAlert("\pA Graphic Couldn't Be Loaded");
    
    HLock((Handle)thePicture);                  // If we made it this far, lock handle.
    bounds = (*thePicture)->picFrame;           // Get a copy of the picture's bounds.
    HUnlock((Handle)thePicture);                // We can unlock the picture now.
    OffsetRect(&bounds, -bounds.left, -bounds.top); // Offset bounds rect to (0, 0).
    DrawPicture(thePicture, &bounds);           // Draw picture to current port.
    
    ReleaseResource((Handle)thePicture);        // Dispose of picture from heap.
}
 
//--------------------------------------------------------------  CreateOffScreenPixMap
 
// Handles the creation of an offscreen pixmap.  Depth is assumed to be that of theÉ
// current gDevice.  If the allocation fails (low memory, etc.) we quit to Finder.
 
void CreateOffScreenPixMap (Rect *theRect, CGrafPtr *offScreen)
{
    CTabHandle  thisColorTable;
    GDHandle    oldDevice;
    CGrafPtr    newCGrafPtr;
    Ptr         theseBits;
    long        sizeOfOff, offRowBytes;
    OSErr       theErr;
    short       thisDepth;
    
    oldDevice = GetGDevice();
    SetGDevice(thisGDevice);
    newCGrafPtr = 0L;
    newCGrafPtr = (CGrafPtr)NewPtrClear(sizeof(CGrafPort));
    if (newCGrafPtr != 0L)
    {
        OpenCPort(newCGrafPtr);
        thisDepth = (**(*newCGrafPtr).portPixMap).pixelSize;
        offRowBytes = ((((long)thisDepth * 
                (long)(theRect->right - theRect->left)) + 15L) >> 4L) << 1L;
        sizeOfOff = (long)(theRect->bottom - theRect->top) * offRowBytes;
        OffsetRect(theRect, -theRect->left, -theRect->top);
        theseBits = NewPtr(sizeOfOff);
        if (theseBits != 0L)
        {
            (**(*newCGrafPtr).portPixMap).baseAddr = theseBits;
            (**(*newCGrafPtr).portPixMap).rowBytes = (short)offRowBytes + 0x8000;
            (**(*newCGrafPtr).portPixMap).bounds = *theRect;
            thisColorTable = (**(**thisGDevice).gdPMap).pmTable;
            theErr = HandToHand((Handle *)&thisColorTable);
            (**(*newCGrafPtr).portPixMap).pmTable = thisColorTable;
            ClipRect(theRect);
            RectRgn(newCGrafPtr->visRgn, theRect);
            ForeColor(blackColor);
            BackColor(whiteColor);
            EraseRect(theRect);
        }
        else
        {
            CloseCPort(newCGrafPtr);        
            DisposePtr((Ptr)newCGrafPtr);
            newCGrafPtr = 0L;
            RedAlert("\pCouldn't Allocate Enough Memory");
        }
    }
    else
        RedAlert("\pCouldn't Allocate Enough Memory");
    
    *offScreen = newCGrafPtr;
    SetGDevice(oldDevice);
}
 
//--------------------------------------------------------------  CreateOffScreenBitMap
 
// Creates an offscreen bitmap.  Depth is of course 1 (b & w).  If this functionÉ
// fails to create the bitmap, we post an alert and quit to the Finder.
 
void CreateOffScreenBitMap (Rect *theRect, GrafPtr *offScreen)
{
    GrafPtr     theBWPort;
    BitMap      theBitMap;  
    long        theRowBytes;
    
    theBWPort = (GrafPtr)(NewPtr(sizeof(GrafPort)));
    OpenPort(theBWPort);
    theRowBytes = (long)((theRect->right - theRect->left + 15L) / 16L) * 2L;
    theBitMap.rowBytes = (short)theRowBytes;
    theBitMap.baseAddr = NewPtr((long)theBitMap.rowBytes * 
        (theRect->bottom - theRect->top));
    if (theBitMap.baseAddr == 0L)
        RedAlert("\pCouldn't Create Bitmaps");
    theBitMap.bounds = *theRect;
    if (MemError() != noErr)
        RedAlert("\pCouldn't Create Bitmaps");
    SetPortBits(&theBitMap);
    ClipRect(theRect);
    RectRgn(theBWPort->visRgn, theRect);
    EraseRect(theRect);
    *offScreen = theBWPort;
}
 
//--------------------------------------------------------------  ZeroRectCorner
 
// Offset rect to (0, 0).  This means the upper left corner of the rect is 
// moved to the origin - to (0, 0) - to the upperleft corner of the port.
 
void ZeroRectCorner (Rect *theRect)
{
    theRect->right -= theRect->left;    // Move right edge by amount of left.
    theRect->bottom -= theRect->top;    // Move bottom edge by amount of top.
    theRect->left = 0;                  // Can now set left to zero.
    theRect->top = 0;                   // Can set top edge to zero as well.
}
 
//--------------------------------------------------------------  FlashShort
 
// This is a simple debugging function that will display the short passed to itÉ
// in the upper left corner of the screen.  It's a handy way to watch the valueÉ
// of a variable while the program is running.
 
void FlashShort (short theValue)
{
    GrafPtr         wasPort, tempPort;
    Str255          tempStr;
    Rect            tempRect;
    
    GetPort(&wasPort);                      // Remember old grafPort.
    
    tempPort = (GrafPtr)NewPtrClear(sizeof(GrafPort));
    OpenPort(tempPort);                     // Create a new empty port.
    
    NumToString((long)theValue, tempStr);   // Convert value passed in to a string.
    MoveTo(20, 40);                         // Move the pen to the upperleft corner.
    SetRect(&tempRect, 18, 20, 122, 42);    // Create a rect up there as well.
    EraseRect(&tempRect);                   // Erase the rect (to make a white hole).
    DrawString(tempStr);                    // And draw our text into that hole.
    
    ClosePort(tempPort);                    // Get rid of out temp port.
    SetPort((GrafPtr)wasPort);              // And set port back to the old one.
}
 
//--------------------------------------------------------------  LogNextTick
 
// Simple function to set a global (tickNext) to the current TickCount() plusÉ
// some offset.  We'll then wait for TickCount() to exceed that global.  We useÉ
// this function and the function below to regulate animation speeds (rememberÉ
// your game may be run on a slow Mac or a fast one - we need a way to keep theÉ
// motion consistent.  I love when the comments are longer than the function.
// (Not really.)
 
void LogNextTick (long howMany)
{
    tickNext = TickCount() + howMany;       // Get machine's TickCount() and add to it.
}
 
//--------------------------------------------------------------  WaitForNextTick
 
// This is the companion function to the above function (LogNextTick()).
// We do nothing but loop until TickCount() catches up with (or passes) ourÉ
// global variable tickNext.
 
void WaitForNextTick (void)
{
    do
    {
    }
    while (TickCount() < tickNext);         // Loop until TickCount() catches up.
}
 
//--------------------------------------------------------------  TrapExists  
 
// A nice "test function" that test for the existence of some ToolBox trap.
// Returns TRUE if the function exists, FALSE if it doesn't.
 
Boolean TrapExists (short trapNumber)
{
    #define     kUnimpTrap      0x9F
    
                // Test trap number against unimplemented trap number.
    return ((NGetTrapAddress(trapNumber, ToolTrap) !=
            NGetTrapAddress(kUnimpTrap, ToolTrap)));
}
 
//--------------------------------------------------------------  DoWeHaveGestalt  
 
// This function specifically tests for the availablity of the Gestalt() function.
// It returns TRUE if Gestalt() exists, FALSE if it doesn't.
 
Boolean DoWeHaveGestalt (void)
{
    #define     kGestaltTrap    0xAD
    
                // Call above function (TrapExists()) with the Gestalt() trap number.
    return (TrapExists(kGestaltTrap));
}
 
//--------------------------------------------------------------  CenterAlert
 
// Handy function to center any alert within the main monitor.
 
void CenterAlert (short alertID)
{
    AlertTHndl  alertHandle;
    Rect        theScreen, alertRect;
    short       horiOff, vertOff;
    Byte        wasState;
    
    theScreen = qd.screenBits.bounds;       // Get main monitor's bounds.
    theScreen.top += LMGetMBarHeight();     // Account for menubar height.
                                            // Get handle to alert resource.
    alertHandle = (AlertTHndl)GetResource('ALRT', alertID);
    if (alertHandle != 0L)                  // Make sure we got it!
    {                                       // Remember its "state" (locked, etc.)
        wasState = HGetState((Handle)alertHandle);
        HLock((Handle)alertHandle);         // We'll lock it.
                                            // Get a copy of it's bounds and zero.
        alertRect = (**alertHandle).boundsRect;
        OffsetRect(&alertRect, -alertRect.left, -alertRect.top);
                                            // Calculate offsets for centering bounds.
        horiOff = ((theScreen.right - theScreen.left) - alertRect.right) / 2;   
        vertOff = ((theScreen.bottom - theScreen.top) - alertRect.bottom) / 3;
                                            // And offset the bounds copy.
        OffsetRect(&alertRect, horiOff, vertOff + LMGetMBarHeight());
                                            // Set alerts bounds to our centered rect.
        (**alertHandle).boundsRect = alertRect;
        HSetState((Handle)alertHandle, wasState);
    }
}
 
//--------------------------------------------------------------  RectWide
 
// Handy function for returning the absolute width of a rectangle.
 
short RectWide (Rect *theRect)
{
    return (theRect->right - theRect->left);
}
 
//--------------------------------------------------------------  RectTall
 
// Handy function for returning the absolute height of a rectangle.
 
short RectTall (Rect *theRect)
{
    return (theRect->bottom - theRect->top);
}
 
//--------------------------------------------------------------  CenterRectInRect
 
// Nice utility function that takes two rectangles and centers the firstÉ
// rectangle within the second.
 
void CenterRectInRect (Rect *rectA, Rect *rectB)
{
    short   widthA, tallA;
    
    widthA = RectWide(rectA);               // Get width of 1st rect.
    tallA = RectTall(rectA);                // Get height of 1st rect.
                                            // Do the math (center horizontally).
    rectA->left = rectB->left + (RectWide(rectB) - widthA) / 2;
    rectA->right = rectA->left + widthA;
                                            // Do the math (center vertically).
    rectA->top = rectB->top + (RectTall(rectB) - tallA) / 2;
    rectA->bottom = rectA->top + tallA;
}
 
//--------------------------------------------------------------  PasStringCopy
 
// This is a nice function that helps to free you from dealing with C strings.
// It takes one Pascal-style string and copies it to a second.
 
void PasStringCopy (StringPtr p1, StringPtr p2)
{
    register short      stringLength;
    
    stringLength = *p2++ = *p1++;   // Get 1st string's length.
    while (--stringLength >= 0)     // Loop through each character in 1st string.
        *p2++ = *p1++;              // And copy to 2nd string.
}
 
//--------------------------------------------------------------  CenterDialog
 
// Like CenterAlert(), this function centers a Dialog on the main monitor.
 
void CenterDialog (short dialogID)
{
    DialogTHndl dlogHandle;
    Rect        theScreen, dlogBounds;
    short       hPos, vPos;
    Byte        wasState;
    
    theScreen = qd.screenBits.bounds;           // Get main monitor's bounds.
    theScreen.top += LMGetMBarHeight();         // Add menuBar's height.
                                                // Load up dialog from resource.
    dlogHandle = (DialogTHndl)GetResource('DLOG', dialogID);
    if (dlogHandle != 0L)                       // If it loaded....!
    {                                           // Remember handle state.
        wasState = HGetState((Handle)dlogHandle);
        HLock((Handle)dlogHandle);              // We're going to lock it.
                                                // Get a copy of the dialog's bounds.
        dlogBounds = (**dlogHandle).boundsRect;
        OffsetRect(&dlogBounds, -dlogBounds.left, -dlogBounds.top);
                                                // Calculate how much to offset.
        hPos = ((theScreen.right - theScreen.left) - dlogBounds.right) / 2;
        vPos = ((theScreen.bottom - theScreen.top) - dlogBounds.bottom) / 3;
                                                // Offset ourt copy of the bounds.
        OffsetRect(&dlogBounds, hPos, vPos + LMGetMBarHeight());
                                                // Set dlg's bounds to centered rect.
        (**dlogHandle).boundsRect = dlogBounds;
        HSetState((Handle)dlogHandle, wasState);// Restore handle's state.
    }
}
 
//--------------------------------------------------------------  DrawDefaultButton
 
// A nice dialog function.  This draws the bold default outline aroundÉ
// item #1 in the dialog passed in.
 
void DrawDefaultButton (DialogPtr theDialog)
{
    Rect        itemRect;
    Handle      itemHandle;
    short       itemType;
                                        // Get at the item's bounds.
    GetDialogItem(theDialog, 1, &itemType, &itemHandle, &itemRect);
    InsetRect(&itemRect, -4, -4);       // Inset (outset?) bounds by -4 pixels.
    PenSize(3, 3);                      // Set the pen 3 pixels thick.
    FrameRoundRect(&itemRect, 16, 16);  // Draw the button outline.
    PenNormal();                        // And restore pen to 1 pixel thick.
}
 
//--------------------------------------------------------------  PasStringCopyNum
 
// Another function to keep you from using C strings.  This one copies only aÉ
// certain number of characters from one Pascal-style string to a second.
 
void PasStringCopyNum (StringPtr p1, StringPtr p2, short charsToCopy)
{
    short       i;
    
    if (charsToCopy > *p1)      // If trying to copy more chars than there areÉ
        charsToCopy = *p1;      // Reduce the number of chars to copy to this size
    
    *p2 = charsToCopy;          // Set 2nd string's length to charsToCopy.
    
    *p2++;                      // Point to first character in 2nd string.
    *p1++;                      // Point to first character in 1st string.
    
    for (i = 0; i < charsToCopy; i++)
        *p2++ = *p1++;          // Copy the specified number of chars over.
}
 
//--------------------------------------------------------------  GetDialogString
 
// Handy dialog function that returns a dialog item string.  This will beÉ
// especially handy for getting the high score name the player enters.
 
void GetDialogString (DialogPtr theDialog, short item, StringPtr theString)
{
    Rect        itemRect;
    Handle      itemHandle;
    short       itemType;
                                        // Get handle to dialog item.
    GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect);
    GetDialogItemText(itemHandle, theString);   // Extract text from item handle.
}
 
//--------------------------------------------------------------  SetDialogString
 
// Like the above function, but this one sets a dialog items string to whateverÉ
// you pass in.  We'll use this to set a default high score name.
 
void SetDialogString (DialogPtr theDialog, short item, StringPtr theString)
{
    Rect        itemRect;
    Handle      itemHandle;
    short       itemType;
                                        // Get handle to dialog item.
    GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect);
    SetDialogItemText(itemHandle, theString);   // Set the items text to theString.
}
 
//--------------------------------------------------------------  SetDialogNumToStr
 
// This one is like SetDialogString() above, but it takes a number (long)É
// instead of a string (the function will convert the long to a string for us).
 
void SetDialogNumToStr (DialogPtr theDialog, short item, long theNumber)
{
    Str255      theString;
    Rect        itemRect;
    Handle      itemHandle;
    short       itemType;
    
    NumToString(theNumber, theString);  // Convert long to a string.
    GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect);
    SetDialogItemText(itemHandle, theString);   // Set the item's text to this number/string.
}
 
//--------------------------------------------------------------  GetDialogNumFromStr
 
// This one is like GetDialogString() above, but returns a long (number)É
// instead of a string (it does this by converting the string to a long).
 
void GetDialogNumFromStr (DialogPtr theDialog, short item, long *theNumber)
{
    Str255      theString;
    Rect        itemRect;
    Handle      itemHandle;
    short       itemType;
                                        // Get a handle to the dialog item.
    GetDialogItem(theDialog, item, &itemType, &itemHandle, &itemRect);
    GetDialogItemText(itemHandle, theString);   // Get the item's text.
    StringToNum(theString, theNumber);  // Convert the text to a long.
}
 
//--------------------------------------------------------------  DisableControl
 
// Another dialog utility for "graying out" buttons or other controls in a dialog.
 
void DisableControl (DialogPtr theDialog, short whichItem)
{
    Rect        iRect;
    Handle      iHandle;
    short       iType;
                                        // Get a handle to the dialog item.
    GetDialogItem(theDialog, whichItem, &iType, &iHandle, &iRect);
                                        // Set it's "hilite state" to "grayed out".
    HiliteControl((ControlHandle)iHandle, kInactive);
}