/****************************/
/*      MISC ROUTINES       */
/* (c)2004 Pangea Software  */
/* By Brian Greenstone      */
/****************************/


/**********************/
/*     EXTERNS        */
/**********************/

extern	short				gPrefsFolderVRefNum;
extern	long				gPrefsFolderDirID;
extern	IBNibRef 			gNibs;
extern	CFBundleRef 		gBundle;


/**********************/
/*     CONSTANTS      */
/**********************/

#define		MIN_FPS		10.0			// minimum fps that our physics can handle
#define		MAX_FPS		1000.0			// max fps that our physics can handle (we set this high here just for demo purposes - in a real game limit this to like 120)



/**********************/
/*     VARIABLES      */
/**********************/

Boolean			gAltiVec;

float			gFramesPerSecond;
float			gOneOverFramesPerSecond;

FSRef		gMyResourcesFolderFSRef;
FSSpec		gMyResourcesFolderFSSpec;

PrefsType			gGamePrefs;


/*********************** DO FATAL ALERT *******************/
//
// Show an alert dialog with the input string, and then quit the app.
//

void DoFatalAlert(Str255 s)
{
SInt16      alertItemHit;

	Enter2D();					// make sure we can see this
	
	StandardAlert(kAlertNoteAlert, s, NULL, NULL, &alertItemHit);

	CleanQuit();
}


/*********************** DO ALERT *******************/
//
// Show an alert dialog with the input string, and then continue on
//

void DoAlert(Str255 s)
{
SInt16      alertItemHit;

	Enter2D();					// make sure we can see this
	
	StandardAlert(kAlertNoteAlert, s, NULL, NULL, &alertItemHit);

	Exit2D();
}

/********************* CLEAN QUIT ***************************/
//
// Rather than calling ExitToShell(), we should try to  call
// this function to clean stuff up before exiting.
//
// The OS will generally clean everything up for us when we quit, but
// it's good to be safe by disposing of things manually if possible.
//

void CleanQuit(void)
{
	static Boolean	beenHere = false;
	
	if (!beenHere)										// make sure we don't re-enter this function causing an infinite loop
	{
		beenHere = true;
		
		SaveOurPrefs();									// save prefs before bailing

		OGL_DisposeDrawContext();						// free OpenGL draw context

		ShutdownHID();									// shut down HID devices

		InitCursor();

		if (gNibs != NULL)								// dispose our nib and bundle refs
			DisposeNibReference(gNibs);
		
		if (gBundle != NULL)
			CFRelease(gBundle);
	}

	ExitToShell();
}


#pragma mark -

/**************** CALC FRAMES PER SECOND **********************/
//
// Called once per frame to calculate our FPS values.
//

void CalcFramesPerSecond(void)
{
	AbsoluteTime 			currTime,deltaTime;
	static AbsoluteTime 	time = {0,0};
	Nanoseconds			nano;

loop:
			/* GET CURRENT TIMER */

	currTime = UpTime();


		/* GET DELTA FROM PREVIOUS TIME */

	deltaTime 	= SubAbsoluteFromAbsolute(currTime, time);
	nano 		= AbsoluteToNanoseconds(deltaTime);


		/* CONVERT NANOSECONDS TO SECONDS */

	gFramesPerSecond = (float)kSecondScale / (float)nano.lo;


		/* MAKE SURE WE DONÕT GO UNDER THE MINIMUM */

	if (gFramesPerSecond < MIN_FPS)			
		gFramesPerSecond = MIN_FPS;


		/* MAKE SURE WE DONÕT GO OVER THE MAXIMUM */

	if (gFramesPerSecond > MAX_FPS)			
		goto loop;
		

			/* CALC 1.0 / FPS */

	gOneOverFramesPerSecond = 1.0f/gFramesPerSecond;		

	time 		= currTime;			// reset for next time interval
}


/****************** GET MY APPLICATION RESOURCES FOLDER ******************/
//
// Called InitApplication() to find a FSSpec and FSRef to our Resources folder.
//

void GetMyApplicationResourcesFolder(void)
{
	OSErr			iErr;
	CFBundleRef     appBundle;
	CFURLRef        appResourcesURL;

			/* GET REFERENCES TO APPLICATIONÕS BUNDLE */

	appBundle = CFBundleGetMainBundle();


			/* GET THE URL OF THE RESOURCES FOLDER */

	appResourcesURL = CFBundleCopyResourcesDirectoryURL(appBundle);
	if (appResourcesURL == nil)
		DoFatalAlert("\pYou don't seem to have a Resources folder");


			/* CONVERT THE URL TO AN FSREF */

	CFURLGetFSRef(appResourcesURL, &gMyResourcesFolderFSRef);


		/* ALSO GET THE FSSPEC WHILE WEÕRE HERE */
		//
		// FSGetCatalogInfo() will return a FSSpec for the Resources folder,
		// but the volume and directory ID's are for the parent folder, not the
		// actual Resources folder.  So, we do a trick by calling FSMakeFSSpec()
		// and we pass in the parent volume and directory ID along with a hardcoded
		// path to a file that we know is in our Resources folder:  Game.nib.
		// The result is a new FSSpec with volume and directory ID's that point
		// directly to the Resources folder.
		//

	FSGetCatalogInfo(&gMyResourcesFolderFSRef,
					kFSCatInfoNone,				// no catinfo needed
					nil, 
					nil,
					&gMyResourcesFolderFSSpec,	// fsspec to save into
					nil);	


	iErr = FSMakeFSSpec(gMyResourcesFolderFSSpec.vRefNum,	// parent volume/directory info
						gMyResourcesFolderFSSpec.parID,
						"\p:Resources:Game.nib",			// path from parent to resource file
						&gMyResourcesFolderFSSpec);

	if (iErr != noErr)
		DoFatalAlert("\perror converting FSRef to FSSpec");

	gMyResourcesFolderFSSpec.name[0] = 0;					// clear the name out of the FSSpec



			
				
	
							

}


/****************** SET DEFAULT DIRECTORY ********************/
//
// This function needs to be called for OS X because OS X doesnt automatically
// set the default directory to the application directory.
//

void SetDefaultDirectory(void)
{
ProcessSerialNumber serial;
ProcessInfoRec info;
FSSpec	app_spec;
WDPBRec wpb;
OSErr	iErr;		
		
	serial.highLongOfPSN = 0;
	serial.lowLongOfPSN = kCurrentProcess;
	
	
	info.processInfoLength = sizeof(ProcessInfoRec);
	info.processName = NULL;
	info.processAppSpec = &app_spec;

	iErr = GetProcessInformation(&serial, & info);

	wpb.ioVRefNum = app_spec.vRefNum;
	wpb.ioWDDirID = app_spec.parID;
	wpb.ioNamePtr = NULL;
	
	iErr = PBHSetVolSync(&wpb);
	
	
}			


/******************** GET FULL PATH FROM FSSPEC ************************/
//
// Returns the full text pathname for the input FSSpec.
//

void GetFullPathFromFSSpec(const FSSpec *spec, UInt8 *fullpath, int maxBufLen)
{
	OSErr iErr;
	FSRef theRef;

	iErr = FSpMakeFSRef(spec, &theRef);
	if (iErr != noErr)
		DoFatalAlert("\pFSpMakeFSRef failed");
		
	FSRefMakePath(&theRef, (UInt8 *) fullpath, maxBufLen);
}



#pragma mark -

/********************** INIT DEFAULT PREFS ************************/
//
// Sets our gGamePrefs values to the default settings.
//

void InitDefaultPrefs(void)
{	
	gGamePrefs.version				= CURRENT_PREFS_VERS;	// set version of prefs
			
	gGamePrefs.depth				= 32;					// 32-bit depth
	gGamePrefs.screenWidth			= 1024;					// 1024 x 768
	gGamePrefs.screenHeight			= 768;
	gGamePrefs.playInWindow			= false;				// play full screen

	gGamePrefs.dontUseHID			= false;				// try to use HID manager for input
	
	gGamePrefs.reserved[0] 			= 0;
	gGamePrefs.reserved[1] 			= 0;				
	gGamePrefs.reserved[2] 			= 0;
	gGamePrefs.reserved[3] 			= 0;				
	gGamePrefs.reserved[4] 			= 0;
	gGamePrefs.reserved[5] 			= 0;				
	gGamePrefs.reserved[6] 			= 0;				
	gGamePrefs.reserved[7] 			= 0;	
	
}


/******************** LOAD OUR PREFS **********************/
//
// Loads in our game's saved preferences data.
//

OSErr LoadOurPrefs(void)
{
OSErr		iErr;
short		refNum;
FSSpec		file;
long		count;
				
			/* TRY TO OPEN AN EXISTING PREFS FILE */
				
	FSMakeFSSpec(gPrefsFolderVRefNum, gPrefsFolderDirID, "\p:MyGame:Prefs", &file);
	iErr = FSpOpenDF(&file, fsRdPerm, &refNum);	
	if (iErr)
		return(iErr);

				/* READ THE WHOLE THING IN */
				
	count = sizeof(PrefsType);
	iErr = FSRead(refNum, &count, &gGamePrefs);		// read data from file
	if (iErr)
	{
		FSClose(refNum);			
		return(iErr);
	}
	
	FSClose(refNum);			

	
			/* VERIFY PREFS FILE VERSION */
			//
			// If this is an older prefs file then just reset our defaults
			// and save it back out.
			//

	if (gGamePrefs.version != CURRENT_PREFS_VERS)
	{
		InitDefaultPrefs();
		SaveOurPrefs();
	}
		
	
		/* RESTORE THE HID CONTROL SETTINGS */
		//
		// The prefs filec contains the user's control configuration
		// settings, so let's try to restore those settings again.
		//
			
	RestoreHIDControlSettings(&gGamePrefs.controlSettings);
	
	return(noErr);
}



/******************** SAVE PREFS **********************/

void SaveOurPrefs(void)
{
FSSpec				file;
OSErr				iErr;
short				refNum;
long				count;
		
		/* GET THE CURRENT HID CONTROL SETTINGS */
		//
		// We want to save the user's HID control settings, so 
		// we call this function to save the settings into our
		// gGamePrefs structure before saving it.
		//
				
	BuildHIDControlSettings(&gGamePrefs.controlSettings);
	
						
		/* DELETE ANY EXISTING PREFS FILE & CREATE NEW ONE */
				
	FSMakeFSSpec(gPrefsFolderVRefNum, gPrefsFolderDirID, "\p:MyGame:Prefs", &file);
	FSpDelete(&file);															// delete any existing file
	iErr = FSpCreate(&file, kGameID, 'Pref', smSystemScript);					// create blank file
	if (iErr)
		return;

				/* OPEN FILE */
					
	iErr = FSpOpenDF(&file, fsRdWrPerm, &refNum);
	if (iErr)
	{
		FSpDelete(&file);
		return;
	}
		
				/* WRITE DATA */

	count = sizeof(PrefsType);
	FSWrite(refNum, &count, &gGamePrefs);	
	FSClose(refNum);
}


#pragma mark -



/****************** ALLOC PTR ********************/
//
// This is the memory allocation function we'll use since it's safer than just calling malloc().
// Here we add a 16 byte header to the ptr and store a cookie in there so we can verify 
// pointers when we dispose of them.  Must call SafeDisposePtr() to free these!
//

void *AllocPtr(long size)
{
Ptr	pr;
u_long	*cookiePtr;

	size += 16;								// make room for our cookie & whatever else (also keep to 16-byte alignment!)

	pr = malloc(size);
	if (pr == nil)
		DoFatalAlert("\pAllocPtr: NewPtr failed");

	cookiePtr = (u_long *)pr;

	*cookiePtr++ = 'FACE';
	*cookiePtr++ = size;
	*cookiePtr++ = 'PTR3';
	*cookiePtr = 'PTR4';

	pr += 16;
	
	return(pr);
}


/****************** ALLOC PTR CLEAR ********************/

void *AllocPtrClear(long size)
{
Ptr	pr;
u_long	*cookiePtr;

	size += 16;								// make room for our cookie & whatever else (also keep to 16-byte alignment!)

	pr = calloc(1, size);

	if (pr == nil)
		DoFatalAlert("\pAllocPtr: NewPtr failed");

	cookiePtr = (u_long *)pr;

	*cookiePtr++ = 'FACE';
	*cookiePtr++ = size;
	*cookiePtr++ = 'PTC3';
	*cookiePtr = 'PTC4';

	pr += 16;
	
	
	return(pr);
}


/***************** SAFE DISPOSE PTR ***********************/
//
// Need to call this to dispose of a pointer we got from AllocPtr() since it'll
// verify the cookie to be sure we're not doing anything stupid.
// 

void SafeDisposePtr(void *ptr)
{
u_long	*cookiePtr;
Ptr		p = ptr;

	p -= 16;					// back up to pt to cookie
	
	cookiePtr = (u_long *)p;
	
	if (*cookiePtr != 'FACE')
		DoFatalAlert("\pSafeSafeDisposePtr: invalid cookie!");
		
	*cookiePtr = 0;									// zap cookie
	
	free(p);
	
}

/****************** ALLOC HANDLE ********************/

Handle	AllocHandle(long size)
{
Handle	hand;
OSErr	err;

	hand = NewHandle(size);							// alloc in APPL
	if (hand == nil)
	{
		DoAlert("\pAllocHandle: using temp mem");
		hand = TempNewHandle(size,&err);			// try TEMP mem
		if (hand == nil)
		{
			DoAlert("\pAllocHandle: failed!");
			return(nil);
		}
		else
			return(hand);							
	}
	return(hand);		
								
}


#pragma mark -

/***************** CHECK FOR LITTLE SNITCH *********************/

Boolean CheckForLittleSnitch(void)
{
	ProcessSerialNumber psn = {kNoProcess, kNoProcess};
	ProcessInfoRec	info;
	short			i;		
	Str255			s;
	OSErr			iErr;
	const char		snitch[] = "\pLittleSnitchDaemon";
		
			/* SET A PROCINFO REC */
			
	info.processName		= s;						// process name will go here
	info.processInfoLength	= sizeof(ProcessInfoRec);
	info.processAppSpec		= nil;
	
	
	/* SCAN ALL OF THE PROCESSES RUNNING & LOOK FOR LITTLE SNITCH */
			
	while(GetNextProcess(&psn) == noErr)
	{
		iErr = GetProcessInformation(&psn, &info);		// get process's info
		if (iErr)										// if err then no more processes
			break;

		if (s[0] == snitch[0])							// see if name matches
		{
			for (i = 1; i <= s[0]; i++)
			{
				if (s[i] != snitch[i])
					goto next_process;
			}
		}
		else
			goto next_process;

			/* IF GETS HERE THEN LITTLE SNITCH IS RUNNING */

		return(true);
next_process:;			
	}
	
	return(false);
}


