
//============================================================================
//----------------------------------------------------------------------------
//									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 "Externs.h"
#include <Folders.h>							// Needed for creating a folder.
#include <GestaltEqu.h>							// Needed for the Gestalt() call.
#include <Script.h>								// I can't remember why I needed this.


#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);
}

