home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Games / MoofWars / Tim's Libraries / TGraphicCollection.cp < prev    next >
Encoding:
Text File  |  2000-09-28  |  17.6 KB  |  623 lines  |  [TEXT/CWIE]

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