G4Prefs.c

/*
    File:       G4Prefs.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
*/
 
 
//============================================================================
//----------------------------------------------------------------------------
//                                  Prefs.c
//----------------------------------------------------------------------------
//============================================================================
 
// This is a slick little file that I re-use and re-use.  I wrote it toÉ
// seemlessly handle System 6 or System 7 with but a single call.  You needÉ
// to define your own "prefs" struct, but these routines will read and writeÉ
// it to the System folder.
 
#include "G4Externs.h"
#include <Folders.h>                            // Needed for creating a folder.
#include <Gestalt.h>                            // Needed for the Gestalt() call.
#include <Script.h>                             // I can't remember why I needed this.
#include <ToolUtils.h>
#include <TextUtils.h>
 
 
#define kPrefCreatorType    'zade'              // Change this to reflect your apps creator.
#define kPrefFileType       'zadP'              // Change this to reflect your prefs type.
#define kPrefFileName       "\pGlypha Prefs"    // Change this to reflect the name for your prefs.
#define kDefaultPrefFName   "\pPreferences"     // Name of prefs folder (System 6 only).
#define kPrefsStringsID     160                 // For easy localization.
#define kPrefsFNameIndex    1                   // This one works with the previous constant.
 
 
Boolean CanUseFindFolder (void);
Boolean GetPrefsFPath (long *, short *);
Boolean CreatePrefsFolder (short *);
Boolean GetPrefsFPath6 (short *);
Boolean WritePrefs (long *, short *, prefsInfo *);
Boolean WritePrefs6 (short *, prefsInfo *);
OSErr ReadPrefs (long *, short *, prefsInfo *);
OSErr ReadPrefs6 (short *, prefsInfo *);
Boolean DeletePrefs (long *, short *);
Boolean DeletePrefs6 (short *);
 
 
//==============================================================  Functions
//--------------------------------------------------------------  CanUseFindFolder
 
// Returns TRUE if we can use the FindFolder() call (a System 7 nicety).
 
Boolean CanUseFindFolder (void)
{
    OSErr       theErr;
    long        theFeature;
    
    if (!DoWeHaveGestalt())     // Darn, have to check for Gestalt() first.
        return(FALSE);          // If no Gestalt(), probably don't have FindFolder().
    
    theErr = Gestalt(gestaltFindFolderAttr, &theFeature);
    if (theErr != noErr)        // Use selector for FindFolder() attribute.
        return(FALSE);
                                // Now do a bit test specifically for FindFolder().
    if (!BitTst(&theFeature, 31 - gestaltFindFolderPresent))
        return(FALSE);
    else
        return(TRUE);
}
 
//--------------------------------------------------------------  GetPrefsFPath
 
// This function gets the file path to the Preferences folder (for System 7).
// It is called only if we can use FindFolder() (see previous function).
 
Boolean GetPrefsFPath (long *prefDirID, short *systemVolRef)
{
    OSErr       theErr;
                                    // Here's the wiley FindFolder() call.
    theErr = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, 
        systemVolRef, prefDirID);   // It returns to us the directory and volume ref.É
    if (theErr != noErr)            // Assuming it worked at all!
        return(FALSE);
    
    return(TRUE);
}
 
//--------------------------------------------------------------  CreatePrefsFolder
 
// This function won't be necessary for System 7, for System 6 though, it createsÉ
// a folder ("Preferences") in the System folder and returns whether or not it worked.
 
Boolean CreatePrefsFolder (short *systemVolRef)
{
    HFileParam  fileParamBlock;
    Str255      folderName;
    OSErr       theErr;
                                        // Here's our localization.  Rather thanÉ
                                        // hard-code the name "Preferences" in the codeÉ
                                        // we pull up the text from a string resource.
    GetIndString(folderName, kPrefsStringsID, kPrefsFNameIndex);
                                        // Set up a file parameter block.
    fileParamBlock.ioVRefNum = *systemVolRef;
    fileParamBlock.ioDirID = 0;
    fileParamBlock.ioNamePtr = folderName;
    fileParamBlock.ioCompletion = 0L;
                                        // And create a directory (folder).
    theErr = PBDirCreate((HParmBlkPtr)&fileParamBlock, FALSE);
    if (theErr != noErr)                // See that it worked.
    {
        RedAlert("\pPrefs Creation Error");
        return(FALSE);
    }
    return(TRUE);
}
 
//--------------------------------------------------------------  GetPrefsFPath6
 
// If ever there was a case to drop support for System 6 (and require System 7),É
// this is it.  Look at how insidious handling System 6 files can be.  The followingÉ
// function is the "System 6 pedigree" of the above GetPrefsFPath() function.  NoteÉ
// that the GetPrefsFPath() function was ONE CALL!  TWO LINES OF CODE!  The belowÉ
// function is like a page or so.  Anyway, this function is called if Glypha isÉ
// running under System 6 and essentially returns a volume reference pointing toÉ
// the preferences folder.
 
Boolean GetPrefsFPath6 (short *systemVolRef)
{
    Str255      folderName, whoCares;
    SysEnvRec   thisWorld;
    CInfoPBRec  catalogInfoPB;
    DirInfo     *directoryInfo = (DirInfo *) &catalogInfoPB;
    HFileInfo   *fileInfo = (HFileInfo *) &catalogInfoPB;
    WDPBRec     workingDirPB;
    long        prefDirID;
    OSErr       theErr;
                                                // Yokelization.
    GetIndString(folderName, kPrefsStringsID, kPrefsFNameIndex);
                                                // SysEnvirons() for System folder volRef.
    theErr = SysEnvirons(2, &thisWorld);
    if (theErr != noErr)
        return(FALSE);
                                                // Okay, here's the volume reference.
    *systemVolRef = thisWorld.sysVRefNum;
    fileInfo->ioVRefNum = *systemVolRef;        // Set up another parameter block.
    fileInfo->ioDirID  = 0;                     // Ignored.
    fileInfo->ioFDirIndex = 0;                  // Irrelevant.
    fileInfo->ioNamePtr = folderName;           // Directory we're looking for.
    fileInfo->ioCompletion = 0L;
    theErr = PBGetCatInfo(&catalogInfoPB, FALSE);
    if (theErr != noErr)                        // Did we fail to find Prefs folder?
    {
        if (theErr != fnfErr)                   // If it WASN'T a file not found errorÉ
        {                                       // then something more sinister is afoot.
            RedAlert("\pPrefs Filepath Error");
        }                                       // Otherwise, need to create prefs folder.
        if (!CreatePrefsFolder(systemVolRef))
            return(FALSE);
                                                // Again - can we find the prefs folder?
        directoryInfo->ioVRefNum = *systemVolRef;
        directoryInfo->ioFDirIndex = 0;
        directoryInfo->ioNamePtr = folderName;
        theErr = PBGetCatInfo(&catalogInfoPB, FALSE);
        if (theErr != noErr)
        {
            RedAlert("\pPrefs GetCatInfo() Error");
            return(FALSE);
        }
    }
    prefDirID = directoryInfo->ioDrDirID;       // Alright, the dir. ID for prefs folder.
    
    workingDirPB.ioNamePtr = whoCares;          // Now convert working dir. into a "real"É
    workingDirPB.ioVRefNum = *systemVolRef;     // dir. ID so we can get volume number.
    workingDirPB.ioWDIndex = 0;
    workingDirPB.ioWDProcID = 0;
    workingDirPB.ioWDVRefNum = 0;
    workingDirPB.ioCompletion = 0L;
    theErr = PBGetWDInfo(&workingDirPB, FALSE);
    if (theErr != noErr)
    {
        RedAlert("\pPrefs PBGetWDInfo() Error");
    }
                                                // The volume where directory is located.
    *systemVolRef = workingDirPB.ioWDVRefNum;
    
    workingDirPB.ioNamePtr = whoCares;
    workingDirPB.ioWDDirID = prefDirID;         // Okay, finally, with a directory ID, É
    workingDirPB.ioVRefNum = *systemVolRef;     // and a "hard" volume numberÉ
    workingDirPB.ioWDProcID = 0;                // É
    workingDirPB.ioCompletion = 0L;             // É
    theErr = PBOpenWD(&workingDirPB, FALSE);    // we can create a working directoryÉ
    if (theErr != noErr)                        // control block with which to accessÉ
    {                                           // files in the prefs folder.
        RedAlert("\pPrefs PBOpenWD() Error");
    }
    
    *systemVolRef = workingDirPB.ioVRefNum;
    
    return(TRUE);
}
 
//--------------------------------------------------------------  WritePrefs
 
// This is the System 7 version that handles all the above functions when youÉ
// want to write out the preferences file.  It is called by SavePrefs() belowÉ
// if we're running under System 7.  It creates an FSSpec record to holdÉ
// information about where the preferences file is located, creates Glypha'sÉ
// preferences if they are not found, opens the prefences file, writes outÉ
// the preferences, and the closes the prefs.  Bam, bam, bam.
 
Boolean WritePrefs (long *prefDirID, short *systemVolRef, prefsInfo *thePrefs)
{
    OSErr       theErr;
    short       fileRefNum;
    long        byteCount;
    FSSpec      theSpecs;
    Str255      fileName = kPrefFileName;
                                    // Create FSSpec record from volume ref and dir ID.
    theErr = FSMakeFSSpec(*systemVolRef, *prefDirID, fileName, &theSpecs);
    if (theErr != noErr)            // See if it failed.
    {                               // An fnfErr means file not found error (no prefs).
        if (theErr != fnfErr)       // If that weren't the problem, we're cooked.
            RedAlert("\pPrefs FSMakeFSSpec() Error");
                                    // If it was an fnfErr, create the prefs.
        theErr = FSpCreate(&theSpecs, kPrefCreatorType, kPrefFileType, smSystemScript);
        if (theErr != noErr)        // If we fail to create the prefs, bail.
            RedAlert("\pPrefs FSpCreate() Error");
    }                               // Okay, we either found or made a pref file, open it.
    theErr = FSpOpenDF(&theSpecs, fsRdWrPerm, &fileRefNum);
    if (theErr != noErr)            // As per usual, if we fail, bail.
        RedAlert("\pPrefs FSpOpenDF() Error");
    
    byteCount = sizeof(*thePrefs);  // Get number of bytes to write (your prefs struct).
                                    // And, write out the preferences.
    theErr = FSWrite(fileRefNum, &byteCount, thePrefs);
    if (theErr != noErr)            // Say no more.
        RedAlert("\pPrefs FSWrite() Error");
    
    theErr = FSClose(fileRefNum);   // Close the prefs file.
    if (theErr != noErr)            // Tic, tic.
        RedAlert("\pPrefs FSClose() Error");
    
    return(TRUE);
}
 
//--------------------------------------------------------------  WritePrefs6
 
// This is the System 6 equivalent of the above function.  It handles prefsÉ
// opening, writing and closing for System 6.
 
Boolean WritePrefs6 (short *systemVolRef, prefsInfo *thePrefs)
{
    OSErr       theErr;
    short       fileRefNum;
    long        byteCount;
    Str255      fileName = kPrefFileName;
                                    // Attempt to open prefs file.
    theErr = FSOpen(fileName, *systemVolRef, &fileRefNum);
    if (theErr != noErr)            // If it failed, maybe the prefs don't exist.
    {                               // An fnfErr means file not found.
        if (theErr != fnfErr)       // See if in fact that WASN'T the reason.
            RedAlert("\pPrefs FSOpen() Error");
                                    // If fnfErr WAS the problem, create the prefs.
        theErr = Create(fileName, *systemVolRef, kPrefCreatorType, kPrefFileType);
        if (theErr != noErr)
            RedAlert("\pPrefs Create() Error");
                                    // Open the prefs file.
        theErr = FSOpen(fileName, *systemVolRef, &fileRefNum);
        if (theErr != noErr)
            RedAlert("\pPrefs FSOpen() Error");
    }
    
    byteCount = sizeof(*thePrefs);  // Get number of bytes to write out.
                                    // Write the prefs out.
    theErr = FSWrite(fileRefNum, &byteCount, thePrefs);
    if (theErr != noErr)
        RedAlert("\pPrefs FSWrite() Error");
                                    // And close the prefs file.
    theErr = FSClose(fileRefNum);
    if (theErr != noErr)
        RedAlert("\pPrefs FSClose() Error");
    
    return(TRUE);
}
 
//--------------------------------------------------------------  SavePrefs
 
// This is the single function called externally to save the preferences.
// You pass it a pointer to your preferences struct and a version number.
// One of the fields in your preferences struct should be a version numberÉ
// (short prefVersion).  This function determines if we're on System 6 or 7É
// and then calls the appropriate routines.  It returns TRUE if all went wellÉ
// or FALSE if any step failed.
 
Boolean SavePrefs (prefsInfo *thePrefs, short versionNow)
{
    long        prefDirID;
    short       systemVolRef;
    Boolean     canUseFSSpecs;
    
    thePrefs->prefVersion = versionNow;         // Set prefVersion to versionNow.
    
    canUseFSSpecs = CanUseFindFolder();         // See if we can use FindFolder().
    if (canUseFSSpecs)                          // If so (System 7) take this route.
    {                                           // Get a path to Preferences folder.
        if (!GetPrefsFPath(&prefDirID, &systemVolRef))
            return(FALSE);
    }
    else                                        // Here's the System 6 version.
    {
        if (!GetPrefsFPath6(&systemVolRef))
            return(FALSE);
    }
    
    if (canUseFSSpecs)                          // Write out the preferences.
    {
        if (!WritePrefs(&prefDirID, &systemVolRef, thePrefs))
            return(FALSE);
    }
    else
    {
        if (!WritePrefs6(&systemVolRef, thePrefs))
            return(FALSE);
    }
    
    return(TRUE);
}
 
//--------------------------------------------------------------  ReadPrefs
 
// This is the System 7 version for reading in the preferences.  It handlesÉ
// opening the prefs, reading in the data to your prefs struct and closingÉ
// the file.
 
OSErr ReadPrefs (long *prefDirID, short *systemVolRef, prefsInfo *thePrefs)
{
    OSErr       theErr;
    short       fileRefNum;
    long        byteCount;
    FSSpec      theSpecs;
    Str255      fileName = kPrefFileName;
                                    // Get an FSSpec record to the prefs file.
    theErr = FSMakeFSSpec(*systemVolRef, *prefDirID, fileName, &theSpecs);
    if (theErr != noErr)
    {
        if (theErr == fnfErr)       // If it doesn't exist, return - we'll use defaults.
            return(theErr);
        else                        // If some other file error occured, bail.
            RedAlert("\pPrefs FSMakeFSSpec() Error");
    }
                                    // Open the prefs file.
    theErr = FSpOpenDF(&theSpecs, fsRdWrPerm, &fileRefNum);
    if (theErr != noErr)
        RedAlert("\pPrefs FSpOpenDF() Error");
    
    byteCount = sizeof(*thePrefs);  // Determine the number of bytes to read in.
                                    // Read 'em into your prefs struct.
    theErr = FSRead(fileRefNum, &byteCount, thePrefs);
    if (theErr != noErr)            // If there was an error reading the fileÉ
    {                               // close the file and we'll revert to defaults.
        if (theErr == eofErr)
            theErr = FSClose(fileRefNum);
        else                        // If closing failed, bail.
            RedAlert("\pPrefs FSRead() Error");
        return(theErr);
    }
    
    theErr = FSClose(fileRefNum);   // Close the prefs file.
    if (theErr != noErr)
        RedAlert("\pPrefs FSClose() Error");
    
    return(theErr);
}
 
//--------------------------------------------------------------  ReadPrefs6
 
// This is the System 6 version of the above function.  It's basically the same,É
// but doesn't have the luxury of using FSSpec records.
 
OSErr ReadPrefs6 (short *systemVolRef, prefsInfo *thePrefs)
{
    OSErr       theErr;
    short       fileRefNum;
    long        byteCount;
    Str255      fileName = kPrefFileName;
                                // Attempt to open the prefs file.
    theErr = FSOpen(fileName, *systemVolRef, &fileRefNum);
    if (theErr != noErr)        // Did opening the file fail?
    {
        if (theErr == fnfErr)   // It did - did it fail because it doesn't exist?
            return(theErr);     // Okay, then we'll revert to default settings.
        else                    // Otherwise, we have a more serious problem.
            RedAlert("\pPrefs FSOpen() Error");
    }
                                // Get number of bytes to read in.
    byteCount = sizeof(*thePrefs);
                                // Read in the stream of data into prefs struct.
    theErr = FSRead(fileRefNum, &byteCount, thePrefs);
    if (theErr != noErr)        // Did the read fail?
    {                           // Maybe we're reading too much data (new prefs vers).
        if (theErr == eofErr)   // That's okay, we'll use defaults for now.
            theErr = FSClose(fileRefNum);
        else
            RedAlert("\pPrefs FSRead() Error");
        return(theErr);
    }
                                // Close the prefs file.
    theErr = FSClose(fileRefNum);
    if (theErr != noErr)
        RedAlert("\pPrefs FSClose() Error");
    
    return(theErr);
}
 
//--------------------------------------------------------------  DeletePrefs
 
// It can happen that you introduce a game with only a few preference settingsÉ
// but then later update your game and end up having to add additional settingsÉ
// to be stored in your games preferences.  In this case, the size of the oldÉ
// prefs won't match the size of the new.  Or even if the size is the same, youÉ
// may have re-ordered the prefs and attempting to load the old prefs will resultÉ
// in garbage.  It is for this reason that I use the "versionNeed" variable andÉ
// the "prefVersion" field in the prefs struct.  In such a case, the below functionÉ
// will be called to delte the old prefs.  When the prefs are then written out, aÉ
// new pref file will be created.  This particular function is the System 7 versionÉ
// for deleting the old preferences.
 
Boolean DeletePrefs (long *dirID, short *volRef)
{
    FSSpec      theSpecs;
    Str255      fileName = kPrefFileName;
    OSErr       theErr;
                                            // Create an FSSec record.
    theErr = FSMakeFSSpec(*volRef, *dirID, fileName, &theSpecs);
    if (theErr != noErr)                    // Test to see if it worked.
        return(FALSE);
    else                                    // If it workedÉ
        theErr = FSpDelete(&theSpecs);      // delete the file.
    
    if (theErr != noErr)
        return(FALSE);
    
    return(TRUE);
}
 
//--------------------------------------------------------------  DeletePrefs6
 
// This is the System 6 version for deleting a preferences file (see above function).
 
Boolean DeletePrefs6 (short *volRef)
{
    Str255      fileName = kPrefFileName;
    OSErr       theErr;
    
    theErr = FSDelete(fileName, *volRef);   // Delete the prefs file.
    
    if (theErr != noErr)
        return(FALSE);
    
    return(TRUE);
}
 
//--------------------------------------------------------------  LoadPrefs
 
// Here is the single call for loading in preferences.  It handles all theÉ
// above function onvolved with opening and reading in preferences.  ItÉ
// determines whether we are on System 6 or 7 (FSSpecs) and makes the rightÉ
// calls.
 
Boolean LoadPrefs (prefsInfo *thePrefs, short versionNeed)
{
    long        prefDirID;
    OSErr       theErr;
    short       systemVolRef;
    Boolean     canUseFSSpecs, noProblems;
    
    canUseFSSpecs = CanUseFindFolder(); // See if we can use FSSpecs (System 7).
    if (canUseFSSpecs)
    {                                   // Get a path to the prefs file.
        noProblems = GetPrefsFPath(&prefDirID, &systemVolRef);
        if (!noProblems)
            return(FALSE);
    }
    else
    {                                   // Gets path to prefs file (System 6).
        noProblems = GetPrefsFPath6(&systemVolRef);
        if (!noProblems)
            return(FALSE);
    }
    
    if (canUseFSSpecs)
    {                                   // Attempt to read prefs.
        theErr = ReadPrefs(&prefDirID, &systemVolRef, thePrefs);
        if (theErr == eofErr)           // Fail the read?  Maybe an old prefs version.
        {                               // Delete it - we'll create a new one later.
            noProblems = DeletePrefs(&prefDirID, &systemVolRef);
            return(FALSE);              // Meanwhile, we'll use defaults.
        }
        else if (theErr != noErr)
            return(FALSE);
    }
    else
    {                                   // Attempt to read prefs (System 6).
        theErr = ReadPrefs6(&systemVolRef, thePrefs);
        if (theErr == eofErr)           // Fail the read?  Maybe an old prefs version.
        {                               // Delete it - we'll create a new one later.
            noProblems = DeletePrefs6(&systemVolRef);
            return(FALSE);              // Meanwhile, we'll use defaults.
        }
        else if (theErr != noErr)
            return(FALSE);
    }
                                        // Okay, maybe the read worked, but we stillÉ
                                        // need to check the version number to seeÉ
                                        // if it's current.
    if (thePrefs->prefVersion != versionNeed)
    {                                   // We'll delete the file if old version.
        if (canUseFSSpecs)
        {
            noProblems = DeletePrefs(&prefDirID, &systemVolRef);
            return(FALSE);
        }
        else
        {
            noProblems = DeletePrefs6(&systemVolRef);
            return(FALSE);
        }
    }
    
    return(TRUE);
}