home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / Samples / Moofwars 1.02 / Tim's Libraries / TGraphicCollection.cp < prev    next >
Encoding:
Text File  |  1997-11-05  |  17.5 KB  |  625 lines  |  [TEXT/CWIE]

  1. /*****************************************************************************
  2. TGraphicCollection.cp
  3.  
  4. Author: Timothy Carroll
  5. Apple Developer Technical Support
  6. timc@apple.com
  7.  
  8. Copyright © 1996, 1997 Apple Computer, Inc., All Rights Reserved
  9.  
  10. You may incorporate this sample code into your applications without
  11. restriction, though the sample code has been provided "AS IS" and the
  12. responsibility for its operation is 100% yours.  However, what you are
  13. not permitted to do is to redistribute the source as "DSC Sample Code"
  14. after having made changes. If you're going to re-distribute the source,
  15. we require that you make it clear in the source that the code was
  16. descended from Apple Sample Code, but that you've made changes.
  17.  
  18.  
  19. Description
  20. -----------
  21.  
  22. A TGraphicCollection is a group of related TGraphic objects.  The API is
  23. very similar to that of TGraphic, but includes an index to determine which
  24. TGraphic is receiving the command.
  25.  
  26. This class is tightly coupled to the TGraphic class, so it has all of the
  27. same limitations.  
  28.  
  29. Note that this class isn't currently intended to be subclassed, so all of
  30. the methods are non-virtual.  This might change if at some point we
  31. determine we need to create a subclass.
  32.  
  33.  
  34. Modification History
  35. --------------------
  36.  
  37. 11/5/97        Cleaned up to work with 3.x Universal Headers and CW Pro.
  38.             Cleaned up some comments.  Made same changes to list
  39.             management code as were made in TGraphic.  Should revisit
  40.             making a generic list class, if performance isn't killed.
  41.  
  42. 4/2/97        CreateCollection was calling ReleaseResource twice, because
  43.             I forgot to remove the original call to ReleaseResource when
  44.             I reorganized some of the cleanup code.  Onyx's Spotlight
  45.             spotted this one for me.
  46.  
  47. 2/24/97        Explicitly includes the error macros now.  We should be
  48.             able to build on other compilers than Metrowerks.
  49. *****************************************************************************/
  50.  
  51.  
  52. #include <Memory.h>
  53. #include <Resources.h>
  54.  
  55. #include "Error Macros.h"
  56. #include "TGraphicCollection.h"
  57.  
  58.  
  59. /*************************************************************************************
  60. TGraphicCollections and List Management
  61.  
  62. TGraphicCollections are allocated and maintained in a list internally by this class.
  63. Each object is reference counted, to minimize the need to load resources more than
  64. once.
  65.  
  66. For optimal performance, pre-load the TGraphicCollection once outside the game loop.
  67. This should mean that all searches hit inside the list.  Theoretically, you can also
  68. just create one TGraphicCollection, and everybody who needs it gets a copy of the
  69. handle.  Eliminates the need for reference counting, but then you need to make sure
  70. any references to it are eliminated.
  71.  
  72. The list is sorted by the collections's resource ID, and uses standard C array indexing.
  73. The list is searched using a binary search.    
  74. *************************************************************************************/
  75.  
  76. static Handle gCollectionList = NULL;
  77. static UInt32 gNumberCollections = 0;
  78.  
  79. static OSStatus InsertCollectionIntoList (TGraphicCollection *theCollection, UInt32 index);
  80. static OSStatus DeleteCollectionFromList (UInt32 index);
  81. static OSStatus SearchCollectionList (SInt16 resID, Boolean *found, UInt32 *index);
  82.  
  83. /*****************************************************************************
  84. InsertCollectionIntoList
  85.  
  86. This routine creates the list if it hasn't been initialized.  Otherwise, it
  87. just inserts the collection into the spot pointed to by the index.  If an error
  88. occurs, the list is left in an indeterminate state.
  89. *****************************************************************************/
  90.  
  91. OSStatus InsertCollectionIntoList (TGraphicCollection *theCollection, UInt32 index)
  92. {
  93.     OSStatus theErr = noErr;
  94.  
  95. #if qDebugging
  96.     if (theCollection == NULL)
  97.         SIGNAL_ERROR ("\pAttempting to insert a null collection into the list")    
  98.     if ((index > gNumberCollections) || (index < 0))
  99.         SIGNAL_ERROR ("\pAttempting to insert collection at invalid index")
  100. #endif
  101.     
  102.     // if we don't have a list allocate one
  103.     if (gCollectionList == NULL)
  104.     {
  105.         gNumberCollections = 1;
  106.         
  107.         gCollectionList = NewHandleClear (sizeof (TGraphicCollection *));
  108.         theErr = MemError();
  109.         FAIL_OSERR (theErr, "\pCouldn't allocate a new handle of information")
  110.         FAIL_NIL (gCollectionList, "\pCouldn't allocate a new handle of information")
  111.     }
  112.     else
  113.     // if we do have a list, expand it by one node.
  114.     {
  115.         gNumberCollections++;
  116.         SetHandleSize(gCollectionList,gNumberCollections*sizeof(TGraphicCollection *));
  117.         
  118.         theErr = MemError();
  119.         FAIL_OSERR (theErr, "\pCouldn't resize the list handle")
  120.         FAIL_NIL (gCollectionList, "\pCouldn't resize the list handle")
  121.         
  122.         // Shift the data to make room
  123.         BlockMoveData(    (*(TGraphicCollection ***)gCollectionList)+index,
  124.                         (*(TGraphicCollection ***)gCollectionList)+index+1,
  125.                         (gNumberCollections - index-1) * sizeof(TGraphicCollection *));
  126.     }
  127.     
  128.     // finally, set the new object in place
  129.             *((*(TGraphicCollection ***) gCollectionList)+index)     = theCollection;
  130.  
  131.     goto cleanup;
  132.     
  133. error:
  134.     if (theErr == noErr)
  135.         theErr = paramErr;
  136.         
  137. cleanup:
  138.     return theErr;
  139. }
  140.  
  141.  
  142. /*****************************************************************************
  143. DeleteCollectionFromList
  144.     
  145. This routine removes the collection from the list.  If the list is then empty,
  146. it disposes of the handle.  If an error occurs, the list is left in an 
  147. indeterminate state.
  148. *****************************************************************************/
  149. OSStatus DeleteCollectionFromList (UInt32 index)
  150. {
  151.     OSStatus theErr = noErr;
  152.     
  153. #if qDebugging
  154.     if ((index < 0) || (index > gNumberCollections-1))
  155.         SIGNAL_ERROR ("\pError: Attempted to delete outside of the list.")
  156. #endif
  157.     
  158.     gNumberCollections--;
  159.     
  160.     //  slide remaining elements up
  161.     
  162.     BlockMoveData(    (*(TGraphicCollection ***)gCollectionList)+index+1,
  163.                     (*(TGraphicCollection ***)gCollectionList)+index,
  164.                     (gNumberCollections - index) * sizeof(TGraphicCollection *));
  165.  
  166.     //    Shrink the handle    
  167.     if (gNumberCollections> 0)
  168.     {
  169.         SetHandleSize((Handle) gCollectionList,gNumberCollections*sizeof(TGraphicCollection *));
  170.         theErr = MemError();
  171.         FAIL_OSERR (theErr, "\pCouldn't resize the list handle")
  172.         FAIL_NIL (gCollectionList, "\pCouldn't resize the list handle")
  173.     }
  174.     else
  175.     {
  176.         DisposeHandle (gCollectionList);
  177.         gCollectionList = NULL;
  178.     }
  179.     
  180.     // We succeeded, cleanup and exit.
  181.     goto cleanup;
  182.  
  183. error:
  184.     if (theErr == noErr)
  185.         theErr = paramErr;
  186.         
  187. cleanup:
  188.     return theErr;
  189. }
  190.  
  191.  
  192. /*****************************************************************************
  193. SearchCollectionList
  194.     
  195. This routine searches the list for an existing collection with that resID.
  196. If it doesn't find one, the index points to where that resID should be
  197. inserted into the list.
  198. *****************************************************************************/
  199. OSStatus SearchCollectionList (SInt16 resID, Boolean *found, UInt32 *index)
  200. {    
  201.     OSStatus    theErr = noErr;
  202.     UInt32        low = 0;
  203.     UInt32        high = gNumberCollections;
  204.     UInt32        tempIndex;
  205.     SInt16        tempID;
  206.     TGraphicCollection *theItem;
  207.         
  208.     while (low < high)
  209.     {
  210.         tempIndex = (low+high) >> 1;
  211.         theItem = (*(TGraphicCollection ***)gCollectionList)[tempIndex];
  212. #if qDebugging
  213.         FAIL_NIL (theItem, "\pBad TGraphic object")
  214. #endif
  215.         tempID = theItem->GetResID();
  216.         if (resID == tempID)
  217.         {
  218.             *found = true;
  219.             *index = tempIndex;
  220.             goto cleanup;
  221.         }
  222.         else if (resID < tempID)
  223.         // element must be below the current index
  224.             high = tempIndex;                    
  225.         else
  226.         // element must be above the current index.
  227.             low = tempIndex+1;
  228.     }
  229.     
  230.     // We fell through, so we didn't find the item in the list.
  231.     
  232.     *found = false;
  233.     *index = (low+high) >> 1;
  234.     
  235.     goto cleanup;
  236.     
  237. error:
  238.     if (theErr == noErr)
  239.         theErr = paramErr;
  240. cleanup:
  241.     return theErr;
  242.  
  243.  
  244. }
  245. /*****************************************************************************
  246. TGraphicCollection::NewCollection
  247.  
  248. This uses the list management routines to create and load a TGraphicCollection.
  249. *****************************************************************************/
  250. TGraphicCollection
  251. *TGraphicCollection::NewCollection (SInt16 resID)
  252. {
  253.     OSStatus            theErr = noErr;
  254.     UInt32                listIndex;
  255.     Boolean             collectionExists;
  256.     TGraphicCollection    *newCollection = NULL;
  257.     
  258.     theErr = SearchCollectionList (resID, &collectionExists, &listIndex);
  259.     FAIL_OSERR(theErr,"\pError: Failed to search the collection list")
  260.     
  261.     if (collectionExists)
  262.     {
  263.         // Bump the reference count on the existing collection.
  264.         newCollection = (*(TGraphicCollection ***)gCollectionList)[listIndex];
  265.         newCollection->AddReference();
  266.     }
  267.     else
  268.     {
  269.         // create a new collection
  270.         newCollection = new TGraphicCollection(resID);
  271.         theErr = newCollection->CreateCollection ();
  272.         FAIL_OSERR(theErr,"\pCouldn't create new TGraphicCollection")
  273.         theErr = InsertCollectionIntoList(newCollection, listIndex);
  274.         FAIL_OSERR(theErr, "\pCouldn't add new TGraphicCollection to list")
  275.         newCollection->AddReference();
  276.     }
  277.     
  278.     // We succeeded, cleanup and return the collection.
  279.     goto cleanup;
  280.     
  281. error:
  282.     if (newCollection != NULL)
  283.     {
  284.         delete newCollection;
  285.         newCollection = NULL;
  286.     }
  287.     
  288. cleanup:    
  289.     return newCollection;
  290. }
  291.  
  292.  
  293. /*****************************************************************************
  294. TGraphicCollection::AddReference
  295. *****************************************************************************/
  296.  
  297. void
  298. TGraphicCollection::AddReference (void)
  299.    {
  300.        fReferenceCount++;
  301.    }
  302.    
  303.    
  304. /*****************************************************************************
  305. TGraphicCollection::DisposeReference
  306. *****************************************************************************/
  307.  
  308. void 
  309. TGraphicCollection::DisposeReference (void)
  310. {
  311.        fReferenceCount--;
  312.        if (fReferenceCount == 0)
  313.        {
  314.            UInt32        listIndex;
  315.         Boolean     tableEntry;
  316.         OSStatus    theErr;
  317.  
  318.         theErr = SearchCollectionList (fResID, &tableEntry, &listIndex);
  319.                 
  320.         FAIL_OSERR (theErr, "\pFailed to search the CollectionList")
  321.         FAIL_FALSE (tableEntry, "\pFailed to find an existing TGraphicCollection in list")
  322.                 
  323.         theErr = DeleteCollectionFromList(listIndex);
  324.         FAIL_OSERR (theErr, "\pFailed to delete Collection from the list")
  325.                 
  326.            delete this;
  327.        }
  328.            
  329.     error:
  330.        return;
  331. }
  332.    
  333.  
  334. /*****************************************************************************
  335. TGraphicCollection::TGraphicCollection
  336.  
  337. The constructor just sets a few clean values -- the real work is done later.
  338. *****************************************************************************/
  339.  
  340. TGraphicCollection::TGraphicCollection (SInt16 resID)
  341. {
  342.     fResID = resID;
  343.     fReferenceCount = 0;
  344.     fGraphics = NULL;
  345. }
  346.  
  347.  
  348. /*****************************************************************************
  349. TGraphicCollection::~TGraphicCollection
  350.     
  351. The destructor just disposes of any data and returns.
  352. *****************************************************************************/
  353.  
  354. TGraphicCollection::~TGraphicCollection (void)
  355. {
  356.     (void) DestroyCollection();
  357. }
  358.  
  359.  
  360. /*****************************************************************************
  361. TGraphicCollection::CreateCollection
  362.  
  363. We load the 'SptA' resource, and extract the resource IDs of our TGraphic
  364. objects we want to load.  Assumes those resources are in the current
  365. resource fork.
  366.  
  367. The format of a 'SptA' resource is a 2 byte unsigned integer for the number
  368. of elements in the list, followed by the resource IDs.  In the future, this
  369. should probably have a version number, so we can change the format.
  370. *****************************************************************************/
  371.  
  372. OSStatus TGraphicCollection::CreateCollection(void)
  373. {
  374.     Handle        collectionResource = Get1Resource ('SptA', fResID);
  375.     OSStatus    theErr = ResError();
  376.     UInt32        loop;
  377.     
  378.     FAIL_OSERR (theErr, "\pResource manager couldn't load the sprite resource")
  379.     FAIL_NIL (collectionResource, "\pResource manager returned a null handle")
  380.     
  381.     // Find out how many TGraphcs we need to load, and allocate a handle
  382.     // to hold the TGraphic pointers.
  383.     
  384.     fNumberOfGraphics = *((SInt16 *) (*collectionResource));
  385.     fGraphics = NewHandleClear (fNumberOfGraphics * sizeof (TGraphic *));
  386.     FAIL_NIL (fGraphics, "\pCouldn't allocate collection handle")
  387.  
  388.     // Load each of the TGraphics.
  389.     
  390.     HLock (fGraphics);
  391.     
  392.     for (loop = 1; loop <= fNumberOfGraphics; loop++)
  393.     {
  394.         SInt16    graphicResID = *((SInt16 *) ((*collectionResource)+sizeof (SInt16)*loop));
  395.         TGraphic *theGraphic = TGraphic::NewGraphic (graphicResID);
  396.         FAIL_NIL (theGraphic, "\pFailed to load graphic for collection")
  397.         
  398.         *((*(TGraphic ***)fGraphics)+loop-1) = theGraphic;
  399.     }
  400.     HUnlock (fGraphics);
  401.     
  402.     goto cleanup;
  403.     
  404. error:
  405.     if (theErr == noErr)
  406.         theErr = paramErr;
  407.     
  408.     // We didn't explictly destroy the collection if an error occured, because
  409.     // the object will be deleted anyway.
  410. cleanup:
  411.     if (collectionResource != NULL)
  412.         ReleaseResource (collectionResource);
  413.     
  414.     return theErr;
  415. }
  416.  
  417.  
  418. /*****************************************************************************
  419. TGraphicCollection::DestroyCollection
  420.     
  421. Throw away all of the objects that we're created.  We do check for null TGraphic objects here,
  422. and properly skip null objects that might not have been finished from the create calls.
  423. *****************************************************************************/
  424.  
  425. OSStatus
  426. TGraphicCollection::DestroyCollection (void)
  427. {
  428.     UInt32 loop;
  429.     
  430.     if (fGraphics)
  431.     {
  432.         HLock (fGraphics);
  433.         for (loop = 0; loop < fNumberOfGraphics ; loop++)
  434.         {        
  435.             TGraphic *theGraphic = *((*(TGraphic ***)fGraphics)+loop);
  436.             if (theGraphic != NULL)
  437.                 theGraphic->DisposeReference();
  438.         }
  439.         DisposeHandle (fGraphics);
  440.         fGraphics = NULL;
  441.     }
  442.     return noErr;
  443. }
  444.  
  445.  
  446.  
  447.  
  448.  
  449. /*****************************************************************************
  450. TGraphicCollection::LockCollection
  451. *****************************************************************************/
  452.  
  453. OSStatus
  454. TGraphicCollection::LockCollection (void)
  455. {
  456.     UInt32        loop;
  457.     OSStatus    theErr = noErr;
  458.     
  459.     MoveHHi (fGraphics);
  460.     theErr = MemError();
  461.     FAIL_OSERR (theErr, "\pCouldn't move fGraphics handle high")
  462.     HLock (fGraphics);
  463.     theErr = MemError();
  464.     FAIL_OSERR (theErr, "\pCouldn't lock fGraphics handle")
  465.     
  466.     TGraphic **ptrToGraphics = (TGraphic **) *fGraphics;
  467.     
  468.     for (loop = 0; loop < fNumberOfGraphics ; loop++)
  469.     {
  470.         TGraphic *theGraphic = *ptrToGraphics++;
  471.         theGraphic->LockGraphic();
  472.     }
  473.     
  474.     return noErr;
  475.     
  476.     error:
  477.     return theErr;
  478. }
  479.  
  480.  
  481. /*****************************************************************************
  482. TGraphicCollection::UnlockCollection
  483. *****************************************************************************/
  484.  
  485. OSStatus
  486. TGraphicCollection::UnlockCollection (void)
  487. {
  488.     UInt32 loop;
  489.     OSStatus theErr = noErr;
  490.     
  491.     // Should still be locked from the lock collection call.  We should do some testing
  492.     // to make sure we aren't locking and unlocking things multiple times here.
  493.     TGraphic **ptrToGraphics = (TGraphic **) *fGraphics;
  494.     
  495.     for (loop = 0; loop < fNumberOfGraphics ; loop++)
  496.     {
  497.         TGraphic *theGraphic = *ptrToGraphics++;
  498.         theGraphic->UnlockGraphic();
  499.     }
  500.     
  501.     HUnlock (fGraphics);
  502.     theErr = MemError();
  503.     FAIL_OSERR (theErr, "\pCouldn't unlock fGraphics handle")
  504.     
  505.     return noErr;
  506.     
  507.     error:
  508.     return theErr;
  509. }
  510.  
  511.  
  512. /*****************************************************************************
  513. TGraphicCollection::GetBounds
  514. *****************************************************************************/
  515.  
  516. Rect
  517. TGraphicCollection::GetBounds (UInt32 index)
  518. {
  519.     TGraphic *theGraphic;
  520. #if qDebugging
  521.     if  (index >= fNumberOfGraphics)
  522.         // We're outside of the bounds of the graphics table here.  Debugger time! :-)
  523.             SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds");
  524. #endif
  525.  
  526.     theGraphic = *((*(TGraphic ***)fGraphics)+index);
  527.  
  528. #if qDebugging
  529.     if (theGraphic == NULL)
  530.             SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object")
  531. #endif
  532.     
  533.     return (theGraphic->GetBounds ());
  534.  
  535. #if qDebugging
  536.     error:
  537.     Rect theRect = {0,0,0,0};
  538.     return theRect;
  539. #endif
  540. }
  541.  
  542.  
  543. /*****************************************************************************
  544. TGraphicCollection::CopyImage
  545. *****************************************************************************/
  546.  
  547. void
  548. TGraphicCollection::CopyImage (UInt32 index, SInt32 top, SInt32 left, Boolean useBackground)
  549. {
  550.     TGraphic *theGraphic;
  551. #if qDebugging
  552.     if  (index >= fNumberOfGraphics)
  553.         // We're outside of the bounds of the graphics table here.  Debugger time! :-)
  554.             SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds");
  555. #endif
  556.  
  557.     theGraphic = *((*(TGraphic ***)fGraphics)+index);
  558.  
  559. #if qDebugging
  560.     if (theGraphic == NULL)
  561.             SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object")
  562. #endif
  563.     
  564.     theGraphic->CopyImage(top, left, useBackground);
  565.  
  566.     error:
  567.     return;
  568. }
  569.  
  570.  
  571. /*****************************************************************************
  572.     TGraphicCollection::HitTest
  573.  
  574. *****************************************************************************/
  575.  
  576. Boolean
  577. TGraphicCollection::HitTest (UInt32 index, SInt32 v, SInt32 h)
  578. {
  579.     TGraphic *theGraphic;
  580. #if qDebugging
  581.     if  (index >= fNumberOfGraphics)
  582.         // We're outside of the bounds of the graphics table here.  Debugger time! :-)
  583.             SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds");
  584. #endif
  585.  
  586.     theGraphic = *((*(TGraphic ***)fGraphics)+index);
  587.  
  588. #if qDebugging
  589.     if (theGraphic == NULL)
  590.             SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object")
  591. #endif
  592.     
  593.     return theGraphic->HitTest (v, h);
  594.  
  595.     error:
  596.     return false;
  597. }
  598.  
  599.  
  600. /*****************************************************************************
  601. TGraphicCollection::GetGraphicObject
  602. *****************************************************************************/
  603.  
  604. TGraphic 
  605. *TGraphicCollection::GetTGraphic (UInt32 index)
  606. {
  607.     TGraphic *theGraphic;
  608. #if qDebugging
  609.     if  (index >= fNumberOfGraphics)
  610.         // We're outside of the bounds of the graphics table here.  Debugger time! :-)
  611.             SIGNAL_ERROR("\ptried to index a TGraphic that was out of bounds");
  612. #endif
  613.  
  614.     theGraphic = *((*(TGraphic ***)fGraphics)+index);
  615. #if qDebugging
  616.     if (theGraphic == NULL)
  617.             SIGNAL_ERROR("\pRetrieved a null TGRAPHIC object")
  618. #endif
  619.     
  620.     return theGraphic;
  621.  
  622.     error:
  623.     
  624.     return NULL;
  625. }