/*****************************************************************************\
     Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
                This file is licensed under the Snes9x License.
   For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/

/***********************************************************************************
  SNES9X for Mac OS (c) Copyright John Stiles

  Snes9x for Mac OS X

  (c) Copyright 2001 - 2011  zones
  (c) Copyright 2002 - 2005  107
  (c) Copyright 2002         PB1400c
  (c) Copyright 2004         Alexander and Sander
  (c) Copyright 2004 - 2005  Steven Seeger
  (c) Copyright 2005         Ryan Vogt
 ***********************************************************************************/


#import "snes9x.h"
#import "memmap.h"
#import "apu.h"
#import "controls.h"
#import "crosshairs.h"
#import "cheats.h"
#import "movie.h"
#import "snapshot.h"
#import "display.h"
#import "blit.h"

#ifdef DEBUGGER
#import "debug.h"
#endif

#import <Cocoa/Cocoa.h>
#import <QuickTime/QuickTime.h>
#import <pthread.h>

#import "mac-prefix.h"
#import "mac-appleevent.h"
#import "mac-audio.h"
#import "mac-cheat.h"
#import "mac-cheatfinder.h"
#import "mac-client.h"
#import "mac-cocoatools.h"
#import "mac-controls.h"
#import "mac-coreimage.h"
#import "mac-dialog.h"
#import "mac-file.h"
#import "mac-gworld.h"
#import "mac-joypad.h"
#import "mac-keyboard.h"
#import "mac-multicart.h"
#import "mac-musicbox.h"
#import "mac-netplay.h"
#import "mac-prefs.h"
#import "mac-quicktime.h"
#import "mac-render.h"
#import "mac-screenshot.h"
#import "mac-server.h"
#import "mac-snes9x.h"
#import "mac-stringtools.h"
#import "mac-os.h"

#define	kRecentMenu_MAX		20
#define KeyIsPressed(km, k)	(1 & (((unsigned char *) km) [(k) >> 3] >> ((k) & 7)))

volatile bool8		running             = false;
volatile bool8		s9xthreadrunning    = false;

volatile bool8		eventQueued         = false;

volatile int		windowResizeCount   = 1;
volatile bool8		windowExtend        = true;

SInt32				systemVersion;

uint32				controlPad[MAC_MAX_PLAYERS];

uint8				romDetect           = 0,
					interleaveDetect    = 0,
					videoDetect         = 0,
					headerDetect        = 0;

WindowRef			gWindow             = NULL;
HIRect				gWindowRect;
int					glScreenW,
					glScreenH;
CGRect				glScreenBounds;
Point				windowPos[kWindowCount];
CGSize				windowSize[kWindowCount];

CGImageRef			macIconImage[118];
int					macPadIconIndex,
					macLegendIconIndex,
					macMusicBoxIconIndex, 
					macFunctionIconIndex;

int					macFrameSkip        = -1;
int32				skipFrames          = 3;
int64				lastFrame           = 0;

int					macFastForwardRate  = 5,
					macFrameAdvanceRate = 1000000;

unsigned long		spcFileCount        = 0,
					pngFileCount        = 0;

bool8				finished            = false,
					cartOpen            = false,
					autofire            = false,
					hidExist            = true,
					directDisplay       = false;

bool8				fullscreen          = false,
					autoRes             = false,
					glstretch           = true,
					gl32bit             = true,
					vsync               = true,
					drawoverscan        = false,
					screencurvature     = false,
					multiprocessor      = false,
					ciFilterEnable      = false;
long				drawingMethod       = kDrawingOpenGL;
int					videoMode           = VIDEOMODE_SMOOTH;

SInt32				macSoundVolume      = 80;	// %
uint32				macSoundBuffer_ms   = 80;	// ms
uint32				macSoundInterval_ms = 16;   // ms
bool8				macSoundLagEnable   = false;
uint16				aueffect            = 0;

uint8				saveInROMFolder     = 2;	// 0 : Snes9x  1 : ROM  2 : Application Support
CFStringRef			saveFolderPath;

int					macCurvatureWarp    = 15,
					macAspectRatio      = 0;

bool8				startopendlog       = false,
					showtimeinfrz       = true,
					enabletoggle        = true,
					savewindowpos       = false,
					onscreeninfo        = true;
int					inactiveMode        = 2;
int					musicboxmode        = kMBXSoundEmulation;

bool8				applycheat          = false;
int					padSetting          = 1,
					deviceSetting       = 1,
					deviceSettingMaster = 1;
int					macControllerOption = SNES_JOYPAD;
AutoFireState		autofireRec[MAC_MAX_PLAYERS];

bool8				macQTRecord         = false;
uint16				macQTMovFlag        = 0;

uint16				macRecordFlag       = 0x3,
					macPlayFlag         = 0x1;
wchar_t				macRecordWChar[MOVIE_MAX_METADATA];

char				npServerIP[256],
					npName[256];

bool8				lastoverscan        = false;

CGPoint				unlimitedCursor;

ExtraOption			extraOptions;

CFStringRef			multiCartPath[2];

#ifdef MAC_PANTHER_SUPPORT
IconRef				macIconRef[118];
#endif

enum
{
	mApple          = 128,
	iAbout          = 1,

	mFile           = 129,
	iOpen           = 1,
	iOpenMulti      = 2,
	iOpenRecent     = 3,
	iClose          = 5,
	iRomInfo        = 7,

	mControl        = 134,
	iKeyboardLayout = 1,
	iISpLayout      = 2,
	iAutoFire       = 4,
	iISpPreset      = 6,

	mEdit           = 130,

	mEmulation      = 131,
	iResume         = 1,
	iSoftReset      = 3,
	iReset          = 4,
	iDevice         = 6,

	mCheat          = 132,
	iApplyCheats    = 1,
	iGameGenie      = 3,
	iCheatFinder    = 4,

	mOption         = 133,
	iFreeze         = 1,
	iDefrost        = 2,
	iFreezeTo       = 4,
	iDefrostFrom    = 5,
	iRecordMovie    = 7,
	iPlayMovie      = 8,
	iQTMovie        = 10,
	iSaveSPC        = 12,
	iSaveSRAM       = 13,
	iCIFilter       = 15,
	iMusicBox       = 17,

	mNetplay        = 135,
	iServer         = 1,
	iClient         = 2,

	mPresets        = 201,

	mDevice         = 202,
	iPad            = 1,
	iMouse			= 2,
	iMouse2         = 3,
	iSuperScope     = 4,
	iMultiPlayer5   = 5,
	iMultiPlayer5_2 = 6,
	iJustifier1     = 7,
	iJustifier2     = 8,

	mRecentItem     = 203
};

enum
{
	kmF1Key    = 0x7A,
	kmF2Key    = 0x78,
	kmF3Key	   = 0x63,
	kmF4Key	   = 0x76,
	kmF5Key	   = 0x60,
	kmF6Key    = 0x61,
	km0Key     = 0x1D,
	km1Key     = 0x12,
	km2Key     = 0x13,
	km3Key     = 0x14,
	km4Key     = 0x15,
	km5Key     = 0x17,
	km6Key     = 0x16,
	km7Key     = 0x1A,
	km8Key     = 0x1C,
	km9Key     = 0x19,
	kmAKey     = 0x00,
	kmBKey     = 0x0B,
	kmCKey     = 0x08,
	kmEscKey   = 0x35,
	kmCtrKey   = 0x3B,
	kmMinusKey = 0x1B,
	kmQKey     = 0x0C,
	kmWKey     = 0x0D,
	kmOKey     = 0x1F,
	kmPKey     = 0x23
};

struct ButtonCommand
{
	char	command[16];
	uint8	keycode;
	bool8	held;
};

struct GameViewInfo
{
	int		globalLeft;
	int		globalTop;
	int		width;
	int		height;
};

static volatile bool8	rejectinput     = false;

static bool8			pauseEmulation  = false,
						frameAdvance    = false;

static pthread_t		s9xthread;

static MenuRef			recentMenu;
static CFStringRef		recentItem[kRecentMenu_MAX + 1];

static EventHandlerUPP	gameWindowUPP,
						gameWUPaneUPP;
static EventHandlerRef	gameWindowEventRef,
						gameWUPaneEventRef;

static int				windowZoomCount = 0;

static int				frameCount      = 0;

static bool8			frzselecting    = false;

static uint16			changeAuto[2] = { 0x0000, 0x0000 };

static GameViewInfo		scopeViewInfo;

static ButtonCommand	btncmd[] =
{
	{ "ToggleBG0",       kmF1Key,    false },
	{ "ToggleBG1",       kmF2Key,    false },
	{ "ToggleBG2",       kmF3Key,    false },
	{ "ToggleBG3",       kmF4Key,    false },
	{ "ToggleSprites",   kmF5Key,    false },
	{ "SwapJoypads",     kmF6Key,    false },
	{ "SoundChannel0",   km1Key,     false },
	{ "SoundChannel1",   km2Key,     false },
	{ "SoundChannel2",   km3Key,     false },
	{ "SoundChannel3",   km4Key,     false },
	{ "SoundChannel4",   km5Key,     false },
	{ "SoundChannel5",   km6Key,     false },
	{ "SoundChannel6",   km7Key,     false },
	{ "SoundChannel7",   km8Key,     false },
	{ "SoundChannelsOn", km9Key,     false },
	{ "_mac1",           km0Key,     false },
	{ "_mac2",           kmMinusKey, false },
	{ "_mac3",           kmQKey,     false },
	{ "_mac4",           kmWKey,     false },
	{ "_mac5",           kmOKey,     false },
	{ "_mac6",           kmPKey,     false }
};

#define	kCommandListSize	(sizeof(btncmd) / sizeof(btncmd[0]))

static void Initialize (void);
static void Deinitialize (void);
static void InitAutofire (void);
static void InitRecentItems (void);
static void DeinitRecentItems (void);
static void ClearRecentItems (void);
static void InitRecentMenu (void);
static void DeinitRecentMenu (void);
static void ProcessInput (void);
static void ResizeGameWindow (void);
static void ChangeAutofireSettings (int, int);
static void ChangeTurboRate (int);
static void ForceChangingKeyScript (void);
static void CFTimerCallback (CFRunLoopTimerRef, void *);
static void UpdateFreezeDefrostScreen (int, CGImageRef, uint8 *, CGContextRef);
static void * MacSnes9xThread (void *);
static OSStatus HandleMenuChoice (UInt32, Boolean *);
static inline void EmulationLoop (void);
static pascal OSStatus MainEventHandler (EventHandlerCallRef, EventRef, void *);
static pascal OSStatus SubEventHandler (EventHandlerCallRef, EventRef, void *);
static pascal OSStatus GameWindowEventHandler (EventHandlerCallRef, EventRef, void *);
static pascal OSStatus GameWindowUserPaneEventHandler (EventHandlerCallRef, EventRef, void *);


int main (int argc, char **argv)
{
#ifdef MAC_PANTHER_SUPPORT
	NSAutoreleasePool	*pool;
#endif
	OSStatus			err;
	EventHandlerRef		eref;
	EventHandlerUPP		eUPP;
	EventTypeSpec		mEvents[] = { { kEventClassCommand, kEventCommandProcess      },
									  { kEventClassCommand, kEventCommandUpdateStatus } },
						sEvents[] = { { kEventClassCommand, kEventCommandProcess      },
									  { kEventClassCommand, kEventCommandUpdateStatus },
									  { kEventClassMouse,   kEventMouseUp             },
									  { kEventClassMouse,   kEventMouseMoved          },
									  { kEventClassMouse,   kEventMouseDragged        } };

#ifdef MAC_PANTHER_SUPPORT
	pool = [[NSAutoreleasePool alloc] init];
#endif
	eUPP = NewEventHandlerUPP(MainEventHandler);
	err = InstallApplicationEventHandler(eUPP, GetEventTypeCount(mEvents), mEvents, NULL, &eref);

	Initialize();

	while (!finished)
	{
		if (cartOpen && running)
		{
		#ifdef DEBUGGER
			CPU.Flags |= DEBUG_MODE_FLAG;
			S9xDoDebug();
		#endif

			eventQueued = false;

			Microseconds((UnsignedWide *) &lastFrame);
			frameCount = 0;
			if (macFrameSkip < 0)
				skipFrames = 3;
			else
				skipFrames = macFrameSkip;

			err = RemoveEventHandler(eref);
			DisposeEventHandlerUPP(eUPP);
		#ifdef MAC_PANTHER_SUPPORT
			[pool release];

			pool = [[NSAutoreleasePool alloc] init];
		#endif
			eUPP = NewEventHandlerUPP(SubEventHandler);
			err = InstallApplicationEventHandler(eUPP, GetEventTypeCount(sEvents), sEvents, NULL, &eref);

			S9xInitDisplay(NULL, NULL);
			ClearGFXScreen();

			if (!fullscreen)
				ForceChangingKeyScript();

			pthread_create(&s9xthread, NULL, MacSnes9xThread, NULL);

			CFRunLoopTimerRef		cftimer    = NULL;
			CFRunLoopTimerContext	cftimerctx = { 0, NULL, NULL, NULL, NULL };

			if (!fullscreen)
			{
				cftimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 30, 0, 0, CFTimerCallback, &cftimerctx);
				if (cftimer)
					CFRunLoopAddTimer(CFRunLoopGetCurrent(), cftimer, kCFRunLoopCommonModes);
			}

			AdjustMenus();

			RunApplicationEventLoop();

			if (!fullscreen)
			{
				if (cftimer)
				{
					CFRunLoopTimerInvalidate(cftimer);
					CFRelease(cftimer);
					cftimer = NULL;
				}
			}

			pthread_join(s9xthread, NULL);

			if (!Settings.NetPlay || Settings.NetPlayServer)
			{
				SNES9X_SaveSRAM();
				S9xResetSaveTimer(false);
				S9xSaveCheatFile(S9xGetFilename(".cht", CHEAT_DIR));
			}

			S9xDeinitDisplay();

			if (Settings.NetPlay)
			{
				if (!Settings.NetPlayServer)
				{
					DeinitGameWindow();
					cartOpen = false;
				}

				Settings.NetPlay = false;
				Settings.NetPlayServer = false;
			}

			err = RemoveEventHandler(eref);
			DisposeEventHandlerUPP(eUPP);
		#ifdef MAC_PANTHER_SUPPORT
			[pool release];

			pool = [[NSAutoreleasePool alloc] init];
		#endif
			eUPP = NewEventHandlerUPP(MainEventHandler);
			err = InstallApplicationEventHandler(eUPP, GetEventTypeCount(mEvents), mEvents, NULL, &eref);
		}

		if (!finished)
		{
			AdjustMenus();
			RunApplicationEventLoop();
		}
	}

	Deinitialize();

	err = RemoveEventHandler(eref);
	DisposeEventHandlerUPP(eUPP);
#ifdef MAC_PANTHER_SUPPORT
	[pool release];
#endif

	return (0);
}

static void CFTimerCallback (CFRunLoopTimerRef timer, void *info)
{
	OSStatus	err;

	err = UpdateSystemActivity(OverallAct);
}

static void * MacSnes9xThread (void *)
{
	Settings.StopEmulation = false;
	s9xthreadrunning = true;

	EmulationLoop();

	s9xthreadrunning = false;
	Settings.StopEmulation = true;

	return (NULL);
}

static inline void EmulationLoop (void)
{
	bool8	olddisplayframerate = false;
	int		storedMacFrameSkip  = macFrameSkip;

	pauseEmulation = false;
	frameAdvance   = false;

	if (macQTRecord)
	{
		olddisplayframerate = Settings.DisplayFrameRate;
		Settings.DisplayFrameRate = false;
	}

	MacStartSound();

	if (Settings.NetPlay)
	{
		if (Settings.NetPlayServer)
		{
			NPServerDetachNetPlayThread();
			NPServerStartClients();

			while (running)
			{
				NPServerProcessInput();
				S9xMainLoop();
			}

			NPServerStopNetPlayThread();
			NPServerStopServer();
		}
		else
		{
			NPClientDetachNetPlayThread();
			NPClientNetPlayWaitStart();

			while (running)
			{
				NPClientProcessInput();
				S9xMainLoop();
			}

			NPClientStopNetPlayThread();
			NPClientDisconnect();

			NPClientRestoreConfig();
		}
	}
	else
	{
		while (running)
		{
			ProcessInput();

			if (!pauseEmulation)
				S9xMainLoop();
			else
			{
				if (frameAdvance)
				{
					macFrameSkip = 1;
					skipFrames = 1;
					frameAdvance = false;
					S9xMainLoop();
					macFrameSkip = storedMacFrameSkip;
				}

				usleep(Settings.FrameTime);
			}
		}
	}

	MacStopSound();

	if (macQTRecord)
	{
		MacQTStopRecording();
		macQTRecord = false;

		Settings.DisplayFrameRate = olddisplayframerate;
	}

	S9xMovieShutdown();
}

static pascal OSStatus MainEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
    OSStatus	err, result = eventNotHandledErr;
	Boolean		done = false;

	if (frzselecting)
		return (result);

	switch (GetEventClass(inEvent))
	{
		case kEventClassCommand:
			switch (GetEventKind(inEvent))
			{
				HICommand	cmd;

				case kEventCommandUpdateStatus:
					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
					if (err == noErr && cmd.commandID == 'clos')
					{
						UpdateMenuCommandStatus(false);
						result = noErr;
					}

					break;

				case kEventCommandProcess:
					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
					if (err == noErr)
					{
						UInt32	modifierkey;

						err = GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifierkey);
						if (err == noErr)
						{
							if ((cmd.commandID == 'pref') && (modifierkey & optionKey))
								cmd.commandID = 'EXTR';

							result = HandleMenuChoice(cmd.commandID, &done);

							if (done)
								QuitApplicationEventLoop();
						}
					}

					break;
			}

			break;
	}

	return (result);
}

static pascal OSStatus SubEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
	OSStatus	err, result = eventNotHandledErr;

	if (frzselecting)
		return (result);

	switch (GetEventClass(inEvent))
	{
		case kEventClassCommand:
			switch (GetEventKind(inEvent))
			{
				HICommand	cmd;

				case kEventCommandUpdateStatus:
					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
					if (err == noErr && cmd.commandID == 'clos')
					{
						UpdateMenuCommandStatus(false);
						result = noErr;
					}

					break;

				case kEventCommandProcess:
					err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &cmd);
					if (err == noErr)
					{
						switch (cmd.commandID)
						{
							case 'Erun':	// Pause
							case 'SubQ':	// Queue from emulation thread
								running = false;
								while (s9xthreadrunning)
									sleep(0);
								QuitApplicationEventLoop();
								result = noErr;
								break;

							case 'Ocif':	// Core Image Filter
								HiliteMenu(0);
								ConfigureCoreImageFilter();
								result = noErr;
								break;
						}
					}

					break;
			}

			break;

		case kEventClassMouse:
			if (fullscreen)
			{
				if ((macControllerOption == SNES_JOYPAD) || (macControllerOption == SNES_MULTIPLAYER5) || (macControllerOption == SNES_MULTIPLAYER5_2))
				{
					if (!(Settings.NetPlay && !Settings.NetPlayServer))
					{
						switch (GetEventKind(inEvent))
						{
							case kEventMouseUp:
								HIPoint	hipt;

								err = GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &hipt);
								if (err == noErr)
								{
									if (CGRectContainsPoint(glScreenBounds, hipt))
									{
										running = false;
										while (s9xthreadrunning)
											sleep(0);
										QuitApplicationEventLoop();
										result = noErr;
									}
								}

								break;
						}
					}
				}
				else
				if ((macControllerOption == SNES_MOUSE) || (macControllerOption == SNES_MOUSE_SWAPPED))
				{
					switch (GetEventKind(inEvent))
					{
						case kEventMouseMoved:
						case kEventMouseDragged:
							HIPoint	hipt;

							err = GetEventParameter(inEvent, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(HIPoint), NULL, &hipt);
							if (err == noErr)
							{
								unlimitedCursor.x += hipt.x;
								unlimitedCursor.y += hipt.y;
							}

							break;
					}
				}
			}

			break;
	}

	return (result);
}

void PostQueueToSubEventLoop (void)
{
	OSStatus	err;
	EventRef	event;

	err = CreateEvent(kCFAllocatorDefault, kEventClassCommand, kEventCommandProcess, 0, kEventAttributeUserEvent, &event);
	if (err == noErr)
	{
		HICommand	cmd;

		cmd.commandID          = 'SubQ';
		cmd.attributes         = kEventAttributeUserEvent;
		cmd.menu.menuRef       = NULL;
		cmd.menu.menuItemIndex = 0;

		err = SetEventParameter(event, kEventParamDirectObject, typeHICommand, sizeof(HICommand), &cmd);
		if (err == noErr)
			err = PostEventToQueue(GetMainEventQueue(), event, kEventPriorityStandard);

		ReleaseEvent(event);
	}
}

void InitGameWindow (void)
{
	OSStatus			err;
	IBNibRef			nibRef;
	WindowAttributes	attr;
	CFStringRef			ref;
	HIViewRef			ctl;
	HIViewID			cid = { 'Pict', 0 };
	Rect				rct;
	char				drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];
	EventTypeSpec		wupaneEvents[] = { { kEventClassControl, kEventControlClick            },
										   { kEventClassControl, kEventControlDraw             } },
						windowEvents[] = { { kEventClassWindow,  kEventWindowDeactivated       },
										   { kEventClassWindow,  kEventWindowActivated         },
										   { kEventClassWindow,  kEventWindowBoundsChanging    },
										   { kEventClassWindow,  kEventWindowBoundsChanged     },
										   { kEventClassWindow,  kEventWindowZoom              },
										   { kEventClassWindow,  kEventWindowToolbarSwitchMode } };

	if (gWindow)
		return;

	err = CreateNibReference(kMacS9XCFString, &nibRef);
	if (err)
		QuitWithFatalError(err, "os 02");

	err = CreateWindowFromNib(nibRef, CFSTR("GameWindow"), &gWindow);
	if (err)
		QuitWithFatalError(err, "os 03");

	DisposeNibReference(nibRef);

	HIViewFindByID(HIViewGetRoot(gWindow), cid, &ctl);

	gameWindowUPP = NewEventHandlerUPP(GameWindowEventHandler);
	err = InstallWindowEventHandler(gWindow, gameWindowUPP, GetEventTypeCount(windowEvents), windowEvents, (void *) gWindow, &gameWindowEventRef);

	gameWUPaneUPP = NewEventHandlerUPP(GameWindowUserPaneEventHandler);
	err = InstallControlEventHandler(ctl, gameWUPaneUPP, GetEventTypeCount(wupaneEvents), wupaneEvents, (void *) gWindow, &gameWUPaneEventRef);

	_splitpath(Memory.ROMFilename, drive, dir, fname, ext);
	ref = CFStringCreateWithCString(kCFAllocatorDefault, fname, kCFStringEncodingUTF8);
	if (ref)
	{
		SetWindowTitleWithCFString(gWindow, ref);
		CFRelease(ref);
	}

	attr = kWindowFullZoomAttribute | kWindowResizableAttribute | kWindowLiveResizeAttribute;
	err = ChangeWindowAttributes(gWindow, attr, kWindowNoAttributes);

	attr = kWindowToolbarButtonAttribute;
	if (!drawoverscan)
		err = ChangeWindowAttributes(gWindow, attr, kWindowNoAttributes);
	else
		err = ChangeWindowAttributes(gWindow, kWindowNoAttributes, attr);

	if (savewindowpos)
	{
		MoveWindow(gWindow, windowPos[kWindowScreen].h, windowPos[kWindowScreen].v, false);

		if ((windowSize[kWindowScreen].width <= 0) || (windowSize[kWindowScreen].height <= 0))
		{
			windowExtend = true;
			windowSize[kWindowScreen].width  = 512;
			windowSize[kWindowScreen].height = kMacWindowHeight;
		}

		if (!lastoverscan && !windowExtend && drawoverscan)
		{
			windowExtend = true;
			windowSize[kWindowScreen].height = (int) ((float) (windowSize[kWindowScreen].height + 0.5) * SNES_HEIGHT_EXTENDED / SNES_HEIGHT);
		}

		SizeWindow(gWindow, (short) windowSize[kWindowScreen].width, (short) windowSize[kWindowScreen].height, false);
	}
	else
	{
		if (drawoverscan)
			windowExtend = true;

		SizeWindow(gWindow, 512, (windowExtend ? kMacWindowHeight : (SNES_HEIGHT << 1)), false);
		RepositionWindow(gWindow, NULL, kWindowCenterOnMainScreen);
	}

	windowZoomCount = 0;

	GetWindowBounds(gWindow, kWindowContentRgn, &rct);
	gWindowRect = CGRectMake((float) rct.left, (float) rct.top, (float) (rct.right - rct.left), (float) (rct.bottom - rct.top));

	ActivateWindow(gWindow, true);
}

void UpdateGameWindow (void)
{
	OSStatus	err;
	HIViewRef	ctl;
	HIViewID	cid = { 'Pict', 0 };

	if (!gWindow)
		return;

	HIViewFindByID(HIViewGetRoot(gWindow), cid, &ctl);
	err = HIViewSetNeedsDisplay(ctl, true);
}

static void ResizeGameWindow (void)
{
	Rect	rct;
	int		ww, wh;

	if (!gWindow)
		return;

	GetWindowBounds(gWindow, kWindowContentRgn, &rct);

	wh = (windowExtend ? SNES_HEIGHT_EXTENDED : SNES_HEIGHT) * ((windowZoomCount >> 1) + 1);

	if (windowZoomCount % 2)
		ww = SNES_NTSC_OUT_WIDTH(SNES_WIDTH) * ((windowZoomCount >> 1) + 1) / 2;
	else
		ww = SNES_WIDTH * ((windowZoomCount >> 1) + 1);

	rct.right  = rct.left + ww;
	rct.bottom = rct.top  + wh;

	SetWindowBounds(gWindow, kWindowContentRgn, &rct);

	printf("Window Size: %d, %d\n", ww, wh);

	windowZoomCount++;
	if (windowZoomCount == 8)
		windowZoomCount = 0;
}

void DeinitGameWindow (void)
{
	OSStatus	err;

	if (!gWindow)
		return;

	SaveWindowPosition(gWindow, kWindowScreen);
	lastoverscan = drawoverscan;

	err = RemoveEventHandler(gameWUPaneEventRef);
	DisposeEventHandlerUPP(gameWUPaneUPP);

	err = RemoveEventHandler(gameWindowEventRef);
	DisposeEventHandlerUPP(gameWindowUPP);

	CFRelease(gWindow);
	gWindow = NULL;
}

static pascal OSStatus GameWindowEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
	OSStatus	err, result = eventNotHandledErr;
	HIRect		rct;
	Rect		r;
	UInt32		attr;

	switch (GetEventClass(inEvent))
	{
		case kEventClassWindow:
			switch (GetEventKind(inEvent))
			{
				case kEventWindowDeactivated:
					if (running)
					{
						if (!(Settings.NetPlay && !Settings.NetPlayServer))
						{
							if (inactiveMode == 3)
							{
								running = false;
								while (s9xthreadrunning)
									sleep(0);
								QuitApplicationEventLoop();
								result = noErr;
							}
							else
							if (inactiveMode == 2)
							{
								rejectinput = true;
								result = noErr;
							}
						}
					}

					break;

				case kEventWindowActivated:
					if (running)
					{
						if (!(Settings.NetPlay && !Settings.NetPlayServer))
						{
							ForceChangingKeyScript();

							if (inactiveMode == 2)
							{
								rejectinput = false;
								result = noErr;
							}
						}
					}

					break;

				case kEventWindowBoundsChanging:
					windowResizeCount = 0x7FFFFFFF;

					err = GetEventParameter(inEvent, kEventParamAttributes, typeUInt32, NULL, sizeof(UInt32), NULL, &attr);
					if ((err == noErr) && (attr & kWindowBoundsChangeSizeChanged))
					{
						err = GetEventParameter(inEvent, kEventParamCurrentBounds, typeHIRect, NULL, sizeof(HIRect), NULL, &rct);
						if (err == noErr)
						{
							if (GetCurrentEventKeyModifiers() & shiftKey)
							{
								HIRect	origRct;

								err = GetEventParameter(inEvent, kEventParamOriginalBounds, typeHIRect, NULL, sizeof(HIRect), NULL, &origRct);
								if (err == noErr)
								{
									rct.size.width = (float) (int) (origRct.size.width * rct.size.height / origRct.size.height);
									err = SetEventParameter(inEvent, kEventParamCurrentBounds, typeHIRect, sizeof(HIRect), &rct);
								}
							}

							gWindowRect = rct;
						}
					}

					result = noErr;
					break;

				case kEventWindowBoundsChanged:
					windowResizeCount = 3;
					result = noErr;
					break;

				case kEventWindowZoom:
					ResizeGameWindow();
					result = noErr;
					break;

				case kEventWindowToolbarSwitchMode:
					windowExtend = !windowExtend;

					GetWindowBounds(gWindow, kWindowContentRgn, &r);

					if (windowExtend)
						r.bottom = r.top + (int) (((float) (r.bottom - r.top) + 0.5) * SNES_HEIGHT_EXTENDED / SNES_HEIGHT);
					else
						r.bottom = r.top + (int) (((float) (r.bottom - r.top) + 0.5) * SNES_HEIGHT / SNES_HEIGHT_EXTENDED);

					SetWindowBounds(gWindow, kWindowContentRgn, &r);

					result = noErr;
					break;
			}

			break;
	}

	return (result);
}

static pascal OSStatus GameWindowUserPaneEventHandler (EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
    OSStatus	err, result = eventNotHandledErr;

	switch (GetEventClass(inEvent))
	{
		case kEventClassControl:
			switch (GetEventKind(inEvent))
			{
				case kEventControlClick:
					if (running)
					{
						if ((macControllerOption == SNES_JOYPAD) || (macControllerOption == SNES_MULTIPLAYER5) || (macControllerOption == SNES_MULTIPLAYER5_2))
						{
							if (!(Settings.NetPlay && !Settings.NetPlayServer))
							{
								if (!frzselecting)
								{
									running = false;
									while (s9xthreadrunning)
										sleep(0);
									QuitApplicationEventLoop();
									result = noErr;
								}
							}
						}
					}
					else
					{
						UInt32	count;

						err = GetEventParameter(inEvent, kEventParamClickCount, typeUInt32, NULL, sizeof(UInt32), NULL, &count);
						if ((err == noErr) && (count == 2))
						{
							SNES9X_Go();
							QuitApplicationEventLoop();
							result = noErr;
						}
					}

					break;

				case kEventControlDraw:
					CGContextRef	ctx;
					HIViewRef		view;
					HIRect			bounds;

					err = GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(ControlRef), NULL, &view);
					if (err == noErr)
					{
						err = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(CGContextRef), NULL, &ctx);
						if (err == noErr)
						{
							if (!running)
							{
								HIViewGetBounds(view, &bounds);
								CGContextTranslateCTM(ctx, 0, bounds.size.height);
								CGContextScaleCTM(ctx, 1.0f, -1.0f);
								DrawPauseScreen(ctx, bounds);
							}
						}
					}

					result = noErr;
					break;
			}

			break;
	}

	return (result);
}

static void InitRecentItems (void)
{
	CFStringRef	keyRef, pathRef;
	int			count;
	char		key[32];

	count = 0;

	for (int i = 0; i <= kRecentMenu_MAX; i++)
		recentItem[i] = NULL;

	for (int i = 0; i <  kRecentMenu_MAX; i++)
	{
		sprintf(key, "RecentItem_%02d", i);
		keyRef = CFStringCreateWithCString(kCFAllocatorDefault, key, CFStringGetSystemEncoding());
		if (keyRef)
		{
			pathRef = (CFStringRef) CFPreferencesCopyAppValue(keyRef, kCFPreferencesCurrentApplication);
			if (pathRef)
			{
				recentItem[count] = pathRef;
				count++;
			}

			CFRelease(keyRef);
		}
	}
}

static void DeinitRecentItems (void)
{
	CFStringRef	keyRef;
	char		key[32];

	for (int i = 0; i < kRecentMenu_MAX; i++)
	{
		sprintf(key, "RecentItem_%02d", i);
		keyRef = CFStringCreateWithCString(kCFAllocatorDefault, key, CFStringGetSystemEncoding());
		if (keyRef)
		{
			if (recentItem[i])
			{
				CFPreferencesSetAppValue(keyRef, recentItem[i], kCFPreferencesCurrentApplication);
				CFRelease(recentItem[i]);
				recentItem[i] = NULL;
			}
			else
				CFPreferencesSetAppValue(keyRef, NULL, kCFPreferencesCurrentApplication);

			CFRelease(keyRef);
		}
	}

	CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
}

static void ClearRecentItems (void)
{
	for (int i = 0; i < kRecentMenu_MAX; i++)
	{
		if (recentItem[i])
		{
			CFRelease(recentItem[i]);
			recentItem[i] = NULL;
		}
	}
}

void AddRecentItem (FSRef *ref)
{
	OSStatus	err;
	char		path[PATH_MAX + 1];

	err = FSRefMakePath(ref, (unsigned char *) path, PATH_MAX);
	if (err == noErr)
	{
		CFStringRef	pathRef;

		pathRef = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
		if (pathRef)
		{
			int	i, j;

			for (i = 0; i < kRecentMenu_MAX; i++)
				if (recentItem[i] && (CFStringCompare(pathRef, recentItem[i], 0) == 0))
					break;

			if (i == kRecentMenu_MAX)
			{
				for (j = kRecentMenu_MAX - 1; j >= 0; j--)
					recentItem[j + 1] = recentItem[j];

				if (recentItem[kRecentMenu_MAX])
				{
					CFRelease(recentItem[kRecentMenu_MAX]);
					recentItem[kRecentMenu_MAX] = NULL;
				}

				recentItem[0] = pathRef;
			}
			else
			{
				CFRelease(pathRef);

				if (i > 0)
				{
					CFStringRef	temp;

					temp = recentItem[i];

					for (j = i - 1; j >= 0; j--)
						recentItem[j + 1] = recentItem[j];

					recentItem[0] = temp;
				}
			}
		}
	}
}

static void InitRecentMenu (void)
{
	OSStatus	err;

	err = CreateNewMenu(mRecentItem, 0, &recentMenu);
	err = SetMenuItemHierarchicalMenu(GetMenuRef(mFile), iOpenRecent, recentMenu);
}

static void DeinitRecentMenu (void)
{
	CFRelease(recentMenu);
}

void BuildRecentMenu (void)
{
	OSStatus	err;
	CFStringRef	str;

	err = DeleteMenuItems(recentMenu, 1, CountMenuItems(recentMenu));

	for (int i = 0; i < kRecentMenu_MAX; i++)
	{
		if (!recentItem[i])
			break;

		Boolean	r;
		char	path[PATH_MAX + 1];

		r = CFStringGetCString(recentItem[i], path, PATH_MAX, kCFStringEncodingUTF8);
		if (r)
		{
			CFStringRef	nameRef;
			char		drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], fname[_MAX_FNAME + 1], ext[_MAX_EXT + 1];

			_splitpath(path, drive, dir, fname, ext);
			snprintf(path, PATH_MAX + 1, "%s%s", fname, ext);
			nameRef = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
			if (nameRef)
			{
				err = AppendMenuItemTextWithCFString(recentMenu, nameRef, 0, 'FRe0' + i, NULL);
				CFRelease(nameRef);
			}
		}
	}

	err = AppendMenuItemTextWithCFString(recentMenu, NULL, kMenuItemAttrSeparator, 'FR__', NULL);

	str = CFCopyLocalizedString(CFSTR("ClearMenu"), "ClearMenu");
	if (str)
	{
		err = AppendMenuItemTextWithCFString(recentMenu, str, 0, 'FRcr', NULL);
		CFRelease(str);
	}
}

void AdjustMenus (void)
{
	OSStatus	err;
	MenuRef		menu;
	CFStringRef	str;

	if (running)
	{
		menu = GetMenuRef(mApple);
		DisableMenuItem(menu, iAbout);
		DisableMenuCommand(NULL, kHICommandPreferences);
		DisableMenuCommand(NULL, kHICommandQuit);

		menu = GetMenuRef(mFile);
		DisableMenuItem(menu, iOpen);
		DisableMenuItem(menu, iOpenMulti);
		DisableMenuItem(menu, iOpenRecent);
		DisableMenuItem(menu, iRomInfo);

		menu = GetMenuRef(mControl);
		DisableMenuItem(menu, iKeyboardLayout);
		DisableMenuItem(menu, iISpLayout);
		DisableMenuItem(menu, iAutoFire);
		DisableMenuItem(menu, iISpPreset);

		menu = GetMenuRef(mEmulation);
		str = CFCopyLocalizedString(CFSTR("PauseMenu"), "pause");
		err = SetMenuItemTextWithCFString(menu, iResume, str);
		CFRelease(str);
		DisableMenuItem(menu, iSoftReset);
		DisableMenuItem(menu, iReset);
		DisableMenuItem(menu, iDevice);

		if (Settings.NetPlay)
		{
			if (Settings.NetPlayServer)
				EnableMenuItem(menu, iResume);
			else
				DisableMenuItem(menu, iResume);
		}
		else
			EnableMenuItem(menu, iResume);

		menu = GetMenuRef(mCheat);
		DisableMenuItem(menu, iApplyCheats);
		DisableMenuItem(menu, iGameGenie);
		DisableMenuItem(menu, iCheatFinder);

		menu = GetMenuRef(mOption);
		DisableMenuItem(menu, iFreeze);
		DisableMenuItem(menu, iDefrost);
		DisableMenuItem(menu, iFreezeTo);
		DisableMenuItem(menu, iDefrostFrom);
		DisableMenuItem(menu, iRecordMovie);
		DisableMenuItem(menu, iPlayMovie);
		DisableMenuItem(menu, iQTMovie);
		DisableMenuItem(menu, iSaveSPC);
		DisableMenuItem(menu, iSaveSRAM);
		DisableMenuItem(menu, iMusicBox);
		if (ciFilterEnable)
			EnableMenuItem(menu, iCIFilter);
		else
			DisableMenuItem(menu, iCIFilter);

		menu = GetMenuRef(mNetplay);
		DisableMenuItem(menu, iServer);
		DisableMenuItem(menu, iClient);
	}
	else
	{
		menu = GetMenuRef(mApple);
		EnableMenuItem(menu, iAbout);
		EnableMenuCommand(NULL, kHICommandPreferences);
		EnableMenuCommand(NULL, kHICommandQuit);

		menu = GetMenuRef(mFile);
		EnableMenuItem(menu, iOpen);
		EnableMenuItem(menu, iOpenMulti);
		EnableMenuItem(menu, iOpenRecent);
		if (cartOpen)
			EnableMenuItem(menu, iRomInfo);
		else
			DisableMenuItem(menu, iRomInfo);

		menu = GetMenuRef(mControl);
		EnableMenuItem(menu, iKeyboardLayout);
		EnableMenuItem(menu, iAutoFire);
		if (hidExist)
		{
			EnableMenuItem(menu, iISpLayout);
			EnableMenuItem(menu, iISpPreset);
		}
		else
		{
			DisableMenuItem(menu, iISpLayout);
			DisableMenuItem(menu, iISpPreset);
		}

		menu = GetMenuRef(mEmulation);
		str = CFCopyLocalizedString(CFSTR("RunMenu"), "run");
		err = SetMenuItemTextWithCFString(menu, iResume, str);
		CFRelease(str);
		EnableMenuItem(menu, iDevice);
		if (cartOpen)
		{
			EnableMenuItem(menu, iResume);
			EnableMenuItem(menu, iSoftReset);
			EnableMenuItem(menu, iReset);
		}
		else
		{
			DisableMenuItem(menu, iResume);
			DisableMenuItem(menu, iSoftReset);
			DisableMenuItem(menu, iReset);
		}

		menu = GetMenuRef(mCheat);
		if (cartOpen)
		{
			EnableMenuItem(menu, iApplyCheats);
			EnableMenuItem(menu, iGameGenie);
			EnableMenuItem(menu, iCheatFinder);
		}
		else
		{
			DisableMenuItem(menu, iApplyCheats);
			DisableMenuItem(menu, iGameGenie);
			DisableMenuItem(menu, iCheatFinder);
		}

		menu = GetMenuRef(mOption);
		DisableMenuItem(menu, iCIFilter);
		if (cartOpen)
		{
			EnableMenuItem(menu, iFreeze);
			EnableMenuItem(menu, iDefrost);
			EnableMenuItem(menu, iFreezeTo);
			EnableMenuItem(menu, iDefrostFrom);
			EnableMenuItem(menu, iRecordMovie);
			EnableMenuItem(menu, iPlayMovie);
			EnableMenuItem(menu, iQTMovie);
			EnableMenuItem(menu, iSaveSPC);
			EnableMenuItem(menu, iSaveSRAM);
			EnableMenuItem(menu, iMusicBox);
		}
		else
		{
			DisableMenuItem(menu, iFreeze);
			DisableMenuItem(menu, iDefrost);
			DisableMenuItem(menu, iFreezeTo);
			DisableMenuItem(menu, iDefrostFrom);
			DisableMenuItem(menu, iRecordMovie);
			DisableMenuItem(menu, iPlayMovie);
			DisableMenuItem(menu, iQTMovie);
			DisableMenuItem(menu, iSaveSPC);
			DisableMenuItem(menu, iSaveSRAM);
			DisableMenuItem(menu, iMusicBox);
		}

		menu = GetMenuRef(mNetplay);
		EnableMenuItem(menu, iClient);
		if (cartOpen)
			EnableMenuItem(menu, iServer);
		else
			DisableMenuItem(menu, iServer);
	}

	DrawMenuBar();
}

void UpdateMenuCommandStatus (Boolean closeMenu)
{
	if (closeMenu)
		EnableMenuItem(GetMenuRef(mFile), iClose);
	else
		DisableMenuItem(GetMenuRef(mFile), iClose);
}

static OSStatus HandleMenuChoice (UInt32 command, Boolean *done)
{
	OSStatus	err, result = noErr;
	MenuRef		mh;
	int			item;
	bool8		isok = true;

	if ((command & 0xFFFFFF00) == 'FRe\0')
	{
		Boolean	r;
		int		index;
		char	path[PATH_MAX + 1];

		index = (int) (command & 0x000000FF) - (int) '0';
		r = CFStringGetCString(recentItem[index], path, PATH_MAX, kCFStringEncodingUTF8);
		if (r)
		{
			FSRef	ref;

			err = FSPathMakeRef((unsigned char *) path, &ref, NULL);
			if (err == noErr)
			{
				if (SNES9X_OpenCart(&ref))
				{
					SNES9X_Go();
					*done = true;
				}
				else
					AdjustMenus();
			}
		}
	}
	else
	{
		switch (command)
		{
			case 'abou':	// About SNES9X
				StartCarbonModalDialog();
				AboutDialog();
				FinishCarbonModalDialog();

				break;

			case 'pref':	// Preferences...
				StartCarbonModalDialog();
				ConfigurePreferences();
				FinishCarbonModalDialog();

				break;

			case 'EXTR':	// Extra Options...
				StartCarbonModalDialog();
				ConfigureExtraOptions();
				FinishCarbonModalDialog();

				break;

			case 'quit':	// Quit SNES9X
				SNES9X_Quit();
				*done = true;

				break;

			case 'open':	// Open ROM Image...
				if (SNES9X_OpenCart(NULL))
				{
					SNES9X_Go();
					*done = true;
				}
				else
					AdjustMenus();

				break;

			case 'Mult':	// Open Multiple ROM Images...
				if (SNES9X_OpenMultiCart())
				{
					SNES9X_Go();
					*done = true;
				}
				else
					AdjustMenus();

				break;

			case 'FRcr':	// Clear Menu
				ClearRecentItems();
				BuildRecentMenu();

				break;

			case 'Finf':	// ROM Information
				StartCarbonModalDialog();
				RomInfoDialog();
				FinishCarbonModalDialog();

				break;

			case 'Ckey':	// Configure Keyboard...
				StartCarbonModalDialog();
				ConfigureKeyboard();
				FinishCarbonModalDialog();

				break;

			case 'Cpad':	// Configure Controllers...
				StartCarbonModalDialog();
				ConfigureHID();
				FinishCarbonModalDialog();

				break;

			case 'Caut':	// Automatic Fire...
				StartCarbonModalDialog();
				ConfigureAutofire();
				FinishCarbonModalDialog();

				break;

			case 'Hapl':	// Apply Cheat Entries
				mh = GetMenuRef(mCheat);
				applycheat = !applycheat;
				CheckMenuItem(mh, iApplyCheats, applycheat);
				Settings.ApplyCheats = applycheat;

				if (!Settings.ApplyCheats)
					S9xCheatsDisable();
				else
					S9xCheatsEnable();

				break;

			case 'Hent':	// Cheat Entry...
				StartCarbonModalDialog();
				ConfigureCheat();
				FinishCarbonModalDialog();

				break;

			case 'Hfnd':	// Cheat Finder...
				StartCarbonModalDialog();
				CheatFinder();
				FinishCarbonModalDialog();

				break;

			case 'Erun':	// Run
				SNES9X_Go();
				*done = true;

				break;

			case 'Esrs':	// Software Reset
				SNES9X_SoftReset();
				SNES9X_Go();
				*done = true;

				break;

			case 'Erst':	// Hardware Reset
				SNES9X_Reset();
				SNES9X_Go();
				*done = true;

				break;

			case 'Ofrz':	// Freeze State
				isok = SNES9X_Freeze();
				*done = true;

				break;

			case 'Odfr':	// Defrost state
				isok = SNES9X_Defrost();
				*done = true;

				break;

			case 'Ofrd':	// Freeze State to...
				StartCarbonModalDialog();
				isok = SNES9X_FreezeTo();
				FinishCarbonModalDialog();

				break;

			case 'Odfd':	// Defrost State From...
				StartCarbonModalDialog();
				isok = SNES9X_DefrostFrom();
				if (gWindow)
					ActivateWindow(gWindow, true);
				FinishCarbonModalDialog();
				*done = true;

				break;

			case 'MVrc':	// Record Movie...
				StartCarbonModalDialog();
				isok = SNES9X_RecordMovie();
				if (gWindow)
					ActivateWindow(gWindow, true);
				FinishCarbonModalDialog();
				*done = true;

				break;

			case 'MVpl':	// Play Movie...
				StartCarbonModalDialog();
				isok = SNES9X_PlayMovie();
				if (isok && (macPlayFlag & 0x2))
				{
					running = false;
					isok = SNES9X_QTMovieRecord();
					running = true;
				}

				if (gWindow)
					ActivateWindow(gWindow, true);
				FinishCarbonModalDialog();
				*done = true;

				break;

			case 'QTmv':	// Record QuickTime Movie...
				StartCarbonModalDialog();
				isok = SNES9X_QTMovieRecord();
				if (gWindow)
					ActivateWindow(gWindow, true);
				FinishCarbonModalDialog();
				*done = true;

				break;

			case 'Ospc':	// Save SPC File at Next Note-on
				S9xDumpSPCSnapshot();

				break;

			case 'Osrm':	// Save SRAM Now
				SNES9X_SaveSRAM();

				break;

			case 'Ombx':	// Music Box
				StartCarbonModalDialog();
				MusicBoxDialog();
				FinishCarbonModalDialog();

				break;

			case 'Nser':	// Server...
				bool8	sr;

				Settings.NetPlay = false;
				Settings.NetPlayServer = false;

				NPServerInit();

				if (!NPServerStartServer(NP_PORT))
				{
					NPServerStopServer();
					break;
				}

				StartCarbonModalDialog();
				sr = NPServerDialog();
				FinishCarbonModalDialog();

				if (sr)
				{
					SNES9X_Reset();
					SNES9X_Go();
					Settings.NetPlay = true;
					Settings.NetPlayServer = true;

					*done = true;
				}
				else
					NPServerStopServer();

				break;

			case 'Ncli':	// Client...
				bool8	cr;

				Settings.NetPlay = false;
				Settings.NetPlayServer = false;

				NPClientInit();

				StartCarbonModalDialog();
				cr = NPClientDialog();
				FinishCarbonModalDialog();

				if (cr)
				{
					SNES9X_Go();
					Settings.NetPlay = true;
					Settings.NetPlayServer = false;

					*done = true;
				}
				else
					AdjustMenus();

				break;

			case 'CPr1':	// Controller Preset
			case 'CPr2':
			case 'CPr3':
			case 'CPr4':
			case 'CPr5':
				item = (int) (command & 0x000000FF) - (int) '0';
				err = GetMenuItemHierarchicalMenu(GetMenuRef(mControl), iISpPreset, &mh);
				CheckMenuItem(mh, padSetting, false);
				padSetting = item;
				CheckMenuItem(mh, padSetting, true);
				ClearPadSetting();
				LoadControllerSettings();

				break;

			case 'EIp1':	// Input Device
			case 'EIp2':
			case 'EIp3':
			case 'EIp4':
			case 'EIp5':
			case 'EIp6':
			case 'EIp7':
			case 'EIp8':
				item = (int) (command & 0x000000FF) - (int) '0';
				err = GetMenuItemHierarchicalMenu(GetMenuRef(mEmulation), iDevice, &mh);
				CheckMenuItem(mh, deviceSetting, false);
				deviceSetting = item;
				deviceSettingMaster = deviceSetting;
				CheckMenuItem(mh, deviceSetting, true);
				ChangeInputDevice();

				break;

			default:
				result = eventNotHandledErr;
				break;
		}
	}

	return (result);
}

void ChangeInputDevice (void)
{
	switch (deviceSetting)
	{
		case iPad:
			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
			S9xSetController(1, CTL_JOYPAD,     1, 0, 0, 0);
			macControllerOption = SNES_JOYPAD;
			break;

		case iMouse:
			S9xSetController(0, CTL_MOUSE,      0, 0, 0, 0);
			S9xSetController(1, CTL_JOYPAD,     1, 0, 0, 0);
			macControllerOption = SNES_MOUSE;
			break;

		case iMouse2:
			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
			S9xSetController(1, CTL_MOUSE,      1, 0, 0, 0);
			macControllerOption = SNES_MOUSE_SWAPPED;
			break;

		case iSuperScope:
			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
			S9xSetController(1, CTL_SUPERSCOPE, 0, 0, 0, 0);
			macControllerOption = SNES_SUPERSCOPE;
			break;

		case iMultiPlayer5:
			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
			S9xSetController(1, CTL_MP5,        1, 2, 3, 4);
			macControllerOption = SNES_MULTIPLAYER5;
			break;

		case iMultiPlayer5_2:
			S9xSetController(0, CTL_MP5,        0, 1, 2, 3);
			S9xSetController(1, CTL_MP5,        4, 5, 6, 7);
			macControllerOption = SNES_MULTIPLAYER5_2;
			break;

		case iJustifier1:
			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
			S9xSetController(1, CTL_JUSTIFIER,  0, 0, 0, 0);
			macControllerOption = SNES_JUSTIFIER;
			break;

		case iJustifier2:
			S9xSetController(0, CTL_JOYPAD,     0, 0, 0, 0);
			S9xSetController(1, CTL_JUSTIFIER,  1, 0, 0, 0);
			macControllerOption = SNES_JUSTIFIER_2;
			break;
	}
}

void ApplyNSRTHeaderControllers (void)
{
	OSStatus	err;
	MenuRef		menu;

	err = GetMenuItemHierarchicalMenu(GetMenuRef(mEmulation), iDevice, &menu);
	if (err)
		return;

	for (int i = 1; i <= CountMenuItems(menu); i++)
	{
		CheckMenuItem(menu, i, false);
		SetItemStyle(menu, i, normal);
	}

	deviceSetting = deviceSettingMaster;

	uint32	valid = 0;

	if (!strncmp((const char *) Memory.NSRTHeader + 24, "NSRT", 4))
	{
		switch (Memory.NSRTHeader[29])
		{
			case 0x00: // Everything goes
				deviceSetting = iPad;
				valid = (1 << iPad);
				break;

			case 0x10: // Mouse in Port 0
				deviceSetting = iMouse;
				valid = (1 << iMouse);
				break;

			case 0x01: // Mouse in Port 1
				deviceSetting = iMouse2;
				valid = (1 << iMouse2);
				break;

			case 0x03: // Super Scope in Port 1
				deviceSetting = iSuperScope;
				valid = (1 << iSuperScope);
				break;

			case 0x06: // Multitap in Port 1
				deviceSetting = iMultiPlayer5;
				valid = (1 << iPad) | (1 << iMultiPlayer5);
				break;

			case 0x66: // Multitap in Ports 0 and 1
				deviceSetting = iMultiPlayer5_2;
				valid = (1 << iPad) | (1 << iMultiPlayer5) | (1 << iMultiPlayer5_2);
				break;

			case 0x08: // Multitap in Port 1, Mouse in new Port 1
				deviceSetting = iMouse2;
				valid = (1 << iPad) | (1 << iMouse2) | (1 << iMultiPlayer5);
				break;

			case 0x04: // Pad or Super Scope in Port 1
				deviceSetting = iSuperScope;
				valid = (1 << iPad) | (1 << iSuperScope);
				break;

			case 0x05: // Justifier - Must ask user...
				deviceSetting = iJustifier1;
				valid = (1 << iJustifier1) | (1 << iJustifier2);
				break;

			case 0x20: // Pad or Mouse in Port 0
				deviceSetting = iMouse;
				valid = (1 << iPad) | (1 << iMouse);
				break;

			case 0x22: // Pad or Mouse in Port 0 & 1
				deviceSetting = iMouse;
				valid = (1 << iPad) | (1 << iMouse) | (1 << iMouse2);
				break;

			case 0x24: // Pad or Mouse in Port 0, Pad or Super Scope in Port 1
				deviceSetting = iSuperScope;
				valid = (1 << iPad) | (1 << iMouse) | (1 << iSuperScope);
				break;

			case 0x27: // Pad or Mouse in Port 0, Pad or Mouse or Super Scope in Port 1
				deviceSetting = iSuperScope;
				valid = (1 << iPad) | (1 << iMouse) | (1 << iMouse2) | (1 << iSuperScope);
				break;

			case 0x99: // Lasabirdie
				break;

			case 0x0A: // Barcode Battler
				break;

			default:
				break;
		}
	}

	CheckMenuItem(menu, deviceSetting, true);

	for (int i = 1; i <= CountMenuItems(menu); i++)
	{
		if (valid & (1 << i))
			SetItemStyle(menu, i, underline);
	}

	ChangeInputDevice();
}

int PromptFreezeDefrost (Boolean freezing)
{
	OSStatus			err;
	CGContextRef		ctx;
	CGColorSpaceRef		color;
	CGDataProviderRef	prov;
	CGImageRef			image;
	CGRect				rct;
	CGPoint				pt;
	CFURLRef			url;
	FSCatalogInfo		info;
	FSRef				ref;
	KeyMap				keys;
	UInt64				newestDate, currentDate;
	UInt32				startTime;
	float				x, y, textw;
	int					result, newestIndex, current_selection, oldInactiveMode;
	char				dateC[256];
	uint8				*back, *draw;

	const UInt32		repeatDelay = 10;
	const int			w = SNES_WIDTH << 1, h = kMacWindowHeight;
	const char			letters[] = "123456789ABC", *filename;
	const uint8			keyCheck[] = { kmEscKey, km1Key, km2Key, km3Key, km4Key, km5Key, km6Key, km7Key, km8Key, km9Key, kmAKey, kmBKey, kmCKey };

	if (!directDisplay)
	{
		S9xInitDisplay(NULL, NULL);
		SNES9X_Go();
	}

	frzselecting = true;
	oldInactiveMode = inactiveMode;
	if (inactiveMode == 3)
		inactiveMode = 2;

	S9xSetSoundMute(true);

	back = (uint8 *) malloc(w * h * 2);
	draw = (uint8 *) malloc(w * h * 2);
	if (!back || !draw)
		QuitWithFatalError(0, "os 04");

	color = CGColorSpaceCreateDeviceRGB();
	if (!color)
		QuitWithFatalError(0, "os 05");

	ctx = CGBitmapContextCreate(back, w, h, 5, w * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0));
	if (!ctx)
		QuitWithFatalError(0, "os 06");

	rct = CGRectMake(0.0f, 0.0f, (float) w, (float) h);
	CGContextClearRect(ctx, rct);

	image = NULL;

	if (freezing)
		url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("logo_freeze"),  CFSTR("png"), NULL);
	else
		url = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("logo_defrost"), CFSTR("png"), NULL);
	if (url)
	{
		prov = CGDataProviderCreateWithURL(url);
		if (prov)
		{
			image = CGImageCreateWithPNGDataProvider(prov, NULL, true, kCGRenderingIntentDefault);
			CGDataProviderRelease(prov);
		}

		CFRelease(url);
	}

	if (image)
	{
		rct = CGRectMake(0.0f, (float) h - 118.0f, 512.0f, 118.0f);
		CGContextDrawImage(ctx, rct, image);
		CGImageRelease(image);
	}

	newestDate  = 0;
	newestIndex = -1;

	CGContextSetLineJoin(ctx, kCGLineJoinRound);

	rct = CGRectMake(0.0f, (float) h - 238.0f, 128.0f, 120.0f);

	for (int count = 0; count < 12; count++)
	{
		filename = S9xGetFreezeFilename(count);
		err = FSPathMakeRef((unsigned char *) filename, &ref, NULL);
		if (err == noErr)
		{
			err = FSGetCatalogInfo(&ref, kFSCatInfoContentMod, &info, NULL, NULL, NULL);
			currentDate = (((UInt64) info.contentModDate.highSeconds << 48) | ((UInt64) info.contentModDate.lowSeconds << 16) | (UInt64) info.contentModDate.fraction);

			if (currentDate > newestDate)
			{
				newestIndex = count;
				newestDate  = currentDate;
			}

			DrawThumbnailResource(&ref, ctx, rct);

			CGContextSetShouldAntialias(ctx, false);
			CGContextSetLineWidth(ctx, 1.0f);

			CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
			x = rct.origin.x + 127.0f;
			y = rct.origin.y + 119.0f;
			CGContextBeginPath(ctx);
			CGContextMoveToPoint(ctx, x, y);
			CGContextAddLineToPoint(ctx, x,          y - 119.0f);
			CGContextAddLineToPoint(ctx, x - 127.0f, y - 119.0f);
			CGContextStrokePath(ctx);

			CGContextSetShouldAntialias(ctx, true);
			CGContextSetLineWidth(ctx, 3.0f);

			CGContextSelectFont(ctx, "Helvetica", 12.0f, kCGEncodingMacRoman);
			x = rct.origin.x +   5.0f;
			y = rct.origin.y + 107.0f;
			CGContextSetTextDrawingMode(ctx, kCGTextStroke);
			CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 0.8f);
			CGContextShowTextAtPoint(ctx, x, y, &letters[count], 1);
			CGContextSetTextDrawingMode(ctx, kCGTextFill);
			CGContextSetRGBFillColor(ctx, 1.0f, 0.7f, 0.7f, 1.0f);
			CGContextShowTextAtPoint(ctx, x, y, &letters[count], 1);

			if (showtimeinfrz)
			{
				CFAbsoluteTime		at;
				CFDateFormatterRef	format;
				CFLocaleRef			locale;
				CFStringRef			datstr;
				Boolean				r;

				err = UCConvertUTCDateTimeToCFAbsoluteTime(&(info.contentModDate), &at);
				locale = CFLocaleCopyCurrent();
				format = CFDateFormatterCreate(kCFAllocatorDefault, locale, kCFDateFormatterShortStyle, kCFDateFormatterMediumStyle);
				datstr = CFDateFormatterCreateStringWithAbsoluteTime(kCFAllocatorDefault, format, at);
				r = CFStringGetCString(datstr, dateC, sizeof(dateC), CFStringGetSystemEncoding());
				CFRelease(datstr);
				CFRelease(format);
				CFRelease(locale);

				CGContextSelectFont(ctx, "Helvetica", 10.0f, kCGEncodingMacRoman);
				x = rct.origin.x +  20.0f;
				y = rct.origin.y + 107.0f;
				CGContextSetTextDrawingMode(ctx, kCGTextInvisible);
				CGContextShowTextAtPoint(ctx, x, y, dateC, strlen(dateC));
				pt = CGContextGetTextPosition(ctx);
				textw = pt.x - x;
				x = rct.origin.x + 122.0f - textw;
				CGContextSetTextDrawingMode(ctx, kCGTextStroke);
				CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 0.8f);
				CGContextShowTextAtPoint(ctx, x, y, dateC, strlen(dateC));
				CGContextSetTextDrawingMode(ctx, kCGTextFill);
				CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 1.0f);
				CGContextShowTextAtPoint(ctx, x, y, dateC, strlen(dateC));
			}
		}
		else
		{
			CGContextSelectFont(ctx, "Helvetica", 12.0f, kCGEncodingMacRoman);
			x = rct.origin.x +   5.0f;
			y = rct.origin.y + 107.0f;
			CGContextSetTextDrawingMode(ctx, kCGTextFill);
			CGContextSetRGBFillColor(ctx, 0.7f, 0.7f, 0.7f, 1.0f);
			CGContextShowTextAtPoint(ctx, x, y, &letters[count], 1);
		}

		if ((count % 4) == 3)
			rct = CGRectOffset(rct, -128.0f * 3.0f, -120.0f);
		else
			rct = CGRectOffset(rct, 128.0f, 0.0f);
	}

	if (newestIndex < 0)
		newestIndex = 0;

	CGContextRelease(ctx);

	image = NULL;

	prov = CGDataProviderCreateWithData(NULL, back, w * h * 2, NULL);
	if (prov)
	{
		image = CGImageCreate(w, h, 5, 16, w * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0), prov, NULL, 0, kCGRenderingIntentDefault);
		CGDataProviderRelease(prov);
	}

	if (!image)
		QuitWithFatalError(0, "os 07");

	ctx = CGBitmapContextCreate(draw, w, h, 5, w * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0));
	if (!ctx)
		QuitWithFatalError(0, "os 08");

	CGContextSetShouldAntialias(ctx, false);

	UpdateFreezeDefrostScreen(newestIndex, image, draw, ctx);

	CocoaPlayFreezeDefrostSound();

	result = -2;
	current_selection = newestIndex;

	do
	{
		if (!rejectinput)
		{
			GetKeys(keys);

			for (int count = 0; count <= 12; count++)
			{
				while (KeyIsPressed(keys, keyCheck[count]))
				{
					result = count - 1;
					GetKeys(keys);
				}
			}

			while (KeyIsPressed(keys, keyCode[k1PRight]))
			{
				startTime = TickCount();
				current_selection += 1;
				if (current_selection > 11)
					current_selection -= 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				while (KeyIsPressed(keys, keyCode[k1PRight]) && (TickCount() < (startTime + repeatDelay)))
					GetKeys(keys);
			}

			while (KeyIsPressed(keys, keyCode[k1PLeft]))
			{
				startTime = TickCount();
				current_selection -= 1;
				if (current_selection < 0)
					current_selection += 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				while (KeyIsPressed(keys, keyCode[k1PLeft])  && (TickCount() < (startTime + repeatDelay)))
					GetKeys(keys);
			}

			while (KeyIsPressed(keys, keyCode[k1PDown]))
			{
				startTime = TickCount();
				current_selection += 4;
				if (current_selection > 11)
					current_selection -= 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				while (KeyIsPressed(keys, keyCode[k1PDown])  && (TickCount() < (startTime + repeatDelay)))
					GetKeys(keys);
			}

			while (KeyIsPressed(keys, keyCode[k1PUp]))
			{
				startTime = TickCount();
				current_selection -= 4;
				if (current_selection < 0)
					current_selection += 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				while (KeyIsPressed(keys, keyCode[k1PUp])    && (TickCount() < (startTime + repeatDelay)))
					GetKeys(keys);
			}

			while (KeyIsPressed(keys, keyCode[k1PA]     ) ||
				   KeyIsPressed(keys, keyCode[k2PA]     ) ||
				   KeyIsPressed(keys, keyCode[k1PB]     ) ||
				   KeyIsPressed(keys, keyCode[k2PB]     ) ||
				   KeyIsPressed(keys, keyCode[k1PX]     ) ||
				   KeyIsPressed(keys, keyCode[k2PX]     ) ||
				   KeyIsPressed(keys, keyCode[k1PY]     ) ||
				   KeyIsPressed(keys, keyCode[k2PY]     ) ||
				   KeyIsPressed(keys, keyCode[k1PStart] ) ||
				   KeyIsPressed(keys, keyCode[k2PStart] ) ||
				   KeyIsPressed(keys, keyCode[k1PSelect]) ||
				   KeyIsPressed(keys, keyCode[k2PSelect]))
			{
				GetKeys(keys);
				result = current_selection;
			}

			uint32	pad1, pad2;

			while (ISpKeyIsPressed(kISpEsc    ) ||
				   ISpKeyIsPressed(kISp1PStart) ||
				   ISpKeyIsPressed(kISp2PStart))
				result = -1;

			pad1 = pad2 = 0;
			JoypadScanDirection(0, &pad1);
			JoypadScanDirection(1, &pad2);
			while ((pad1 & 0x0100) || (pad2 & 0x0100))	// Rt
			{
				startTime = TickCount();
				current_selection += 1;
				if (current_selection > 11)
					current_selection -= 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				do
				{
					pad1 = pad2 = 0;
					JoypadScanDirection(0, &pad1);
					JoypadScanDirection(1, &pad2);
				} while (((pad1 & 0x0100) || (pad2 & 0x0100)) && (TickCount() < (startTime + repeatDelay)));
			}

			pad1 = pad2 = 0;
			JoypadScanDirection(0, &pad1);
			JoypadScanDirection(1, &pad2);
			while ((pad1 & 0x0200) || (pad2 & 0x0200))	// Lf
			{
				startTime = TickCount();
				current_selection -= 1;
				if (current_selection < 0)
					current_selection += 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				do
				{
					pad1 = pad2 = 0;
					JoypadScanDirection(0, &pad1);
					JoypadScanDirection(1, &pad2);
				} while (((pad1 & 0x0200) || (pad2 & 0x0200)) && (TickCount() < (startTime + repeatDelay)));
			}

			pad1 = pad2 = 0;
			JoypadScanDirection(0, &pad1);
			JoypadScanDirection(1, &pad2);
			while ((pad1 & 0x0800) || (pad2 & 0x0800))	// Up
			{
				startTime = TickCount();
				current_selection -= 4;
				if (current_selection < 0)
					current_selection += 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				do
				{
					pad1 = pad2 = 0;
					JoypadScanDirection(0, &pad1);
					JoypadScanDirection(1, &pad2);
				} while (((pad1 & 0x0800) || (pad2 & 0x0800)) && (TickCount() < (startTime + repeatDelay)));
			}

			pad1 = pad2 = 0;
			JoypadScanDirection(0, &pad1);
			JoypadScanDirection(1, &pad2);
			while ((pad1 & 0x0400) || (pad2 & 0x0400))	// Dn
			{
				startTime = TickCount();
				current_selection += 4;
				if (current_selection > 11)
					current_selection -= 12;
				UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
				do
				{
					pad1 = pad2 = 0;
					JoypadScanDirection(0, &pad1);
					JoypadScanDirection(1, &pad2);
				} while (((pad1 & 0x0400) || (pad2 & 0x0400)) && (TickCount() < (startTime + repeatDelay)));
			}

			while (ISpKeyIsPressed(kISp1PA) ||
				   ISpKeyIsPressed(kISp2PA) ||
				   ISpKeyIsPressed(kISp1PB) ||
				   ISpKeyIsPressed(kISp2PB) ||
				   ISpKeyIsPressed(kISp1PX) ||
				   ISpKeyIsPressed(kISp2PX) ||
				   ISpKeyIsPressed(kISp1PY) ||
				   ISpKeyIsPressed(kISp2PY))
				result = current_selection;
		}

		usleep(30000);

		windowResizeCount = 2;
		UpdateFreezeDefrostScreen(current_selection, image, draw, ctx);
	} while (result == -2);

	CocoaPlayFreezeDefrostSound();

	CGContextRelease(ctx);
	CGImageRelease(image);
	CGColorSpaceRelease(color);
	free(draw);
	free(back);

	S9xSetSoundMute(false);

	inactiveMode = oldInactiveMode;
	frzselecting = false;

	windowResizeCount = 2;

	return (result);
}

static void UpdateFreezeDefrostScreen (int newIndex, CGImageRef image, uint8 *draw, CGContextRef ctx)
{
	if (newIndex >= 0 && newIndex < 12)
	{
		CGRect		rct;
		const int	w = SNES_WIDTH << 1, h = kMacWindowHeight;

		CGContextSetLineWidth(ctx, 1.0f);

		rct = CGRectMake(0.0f, 0.0f, (float) w, (float) h);
		CGContextDrawImage(ctx, rct, image);

		rct = CGRectMake(0.0f, (float) h - 238.0f, 128.0f, 120.0f);
		rct = CGRectOffset(rct, (float) (128 * (newIndex % 4)), (float) (-120 * (newIndex / 4)));
		rct.size.width  -= 1.0f;
		rct.size.height -= 1.0f;

		CGContextSetRGBStrokeColor(ctx, 1.0f, 1.0f, 0.0f, 1.0f);
		CGContextStrokeRect(ctx, rct);
		rct = CGRectInset(rct, 1.0f, 1.0f);
		CGContextSetRGBStrokeColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
		CGContextStrokeRect(ctx, rct);
	}

	DrawFreezeDefrostScreen(draw);
}

static void ProcessInput (void)
{
	KeyMap			myKeys;
	bool8			isok, fnbtn, altbtn, tcbtn;
	static bool8	toggleff = false, lastTimeTT = false, lastTimeFn = false, ffUp = false, ffDown = false, ffUpSp = false, ffDownSp = false;

	if (rejectinput)
		return;

	if (ISpKeyIsPressed(kISpEsc))
	{
		if (s9xthreadrunning)
		{
			if (!eventQueued)
			{
				PostQueueToSubEventLoop();
				eventQueued = true;
			}
		}
		else
			running = false;

		return;
	}

	if (ISpKeyIsPressed(kISpFreeze))
	{
		MacStopSound();
		while (ISpKeyIsPressed(kISpFreeze));

		isok = SNES9X_Freeze();
		ClearGFXScreen();
		return;
	}

	if (ISpKeyIsPressed(kISpDefrost))
	{
		MacStopSound();
		while (ISpKeyIsPressed(kISpDefrost));

		isok = SNES9X_Defrost();
		ClearGFXScreen();
		return;
	}

	if (ISpKeyIsPressed(kISpScreenshot))
	{
		Settings.TakeScreenshot = true;
		while (ISpKeyIsPressed(kISpScreenshot));
	}

	if (ISpKeyIsPressed(kISpSPC))
	{
		S9xDumpSPCSnapshot();
		while (ISpKeyIsPressed(kISpSPC));
	}

	if (ISpKeyIsPressed(kISpFFUp))
	{
		if (!ffUpSp)
		{
			ChangeTurboRate(+1);
			ffUpSp = true;
		}
	}
	else
		ffUpSp = false;

	if (ISpKeyIsPressed(kISpFFDown))
	{
		if (!ffDownSp)
		{
			ChangeTurboRate(-1);
			ffDownSp = true;
		}
	}
	else
		ffDownSp = false;

	controlPad[0] = controlPad[1] = 0;

	JoypadScanDirection(0, &(controlPad[0]));
	if (ISpKeyIsPressed(kISp1PR     ))	controlPad[0] |= 0x0010;
	if (ISpKeyIsPressed(kISp1PL     ))	controlPad[0] |= 0x0020;
	if (ISpKeyIsPressed(kISp1PX     ))	controlPad[0] |= 0x0040;
	if (ISpKeyIsPressed(kISp1PA     ))	controlPad[0] |= 0x0080;
	if (ISpKeyIsPressed(kISp1PStart ))	controlPad[0] |= 0x1000;
	if (ISpKeyIsPressed(kISp1PSelect))	controlPad[0] |= 0x2000;
	if (ISpKeyIsPressed(kISp1PY     ))	controlPad[0] |= 0x4000;
	if (ISpKeyIsPressed(kISp1PB     ))	controlPad[0] |= 0x8000;

	JoypadScanDirection(1, &(controlPad[1]));
	if (ISpKeyIsPressed(kISp2PR     ))	controlPad[1] |= 0x0010;
	if (ISpKeyIsPressed(kISp2PL     ))	controlPad[1] |= 0x0020;
	if (ISpKeyIsPressed(kISp2PX     ))	controlPad[1] |= 0x0040;
	if (ISpKeyIsPressed(kISp2PA     ))	controlPad[1] |= 0x0080;
	if (ISpKeyIsPressed(kISp2PStart ))	controlPad[1] |= 0x1000;
	if (ISpKeyIsPressed(kISp2PSelect))	controlPad[1] |= 0x2000;
	if (ISpKeyIsPressed(kISp2PY     ))	controlPad[1] |= 0x4000;
	if (ISpKeyIsPressed(kISp2PB     ))	controlPad[1] |= 0x8000;

	if (((macControllerOption == SNES_MULTIPLAYER5) || (macControllerOption == SNES_MULTIPLAYER5_2)) && Settings.MultiPlayer5Master)
	{
		controlPad[2] = controlPad[3] = controlPad[4] = 0;

		JoypadScanDirection(2, &(controlPad[2]));
		if (ISpKeyIsPressed(kISp3PR     ))	controlPad[2] |= 0x0010;
		if (ISpKeyIsPressed(kISp3PL     ))	controlPad[2] |= 0x0020;
		if (ISpKeyIsPressed(kISp3PX     ))	controlPad[2] |= 0x0040;
		if (ISpKeyIsPressed(kISp3PA     ))	controlPad[2] |= 0x0080;
		if (ISpKeyIsPressed(kISp3PStart ))	controlPad[2] |= 0x1000;
		if (ISpKeyIsPressed(kISp3PSelect))	controlPad[2] |= 0x2000;
		if (ISpKeyIsPressed(kISp3PY     ))	controlPad[2] |= 0x4000;
		if (ISpKeyIsPressed(kISp3PB     ))	controlPad[2] |= 0x8000;

		JoypadScanDirection(3, &(controlPad[3]));
		if (ISpKeyIsPressed(kISp4PR     ))	controlPad[3] |= 0x0010;
		if (ISpKeyIsPressed(kISp4PL     ))	controlPad[3] |= 0x0020;
		if (ISpKeyIsPressed(kISp4PX     ))	controlPad[3] |= 0x0040;
		if (ISpKeyIsPressed(kISp4PA     ))	controlPad[3] |= 0x0080;
		if (ISpKeyIsPressed(kISp4PStart ))	controlPad[3] |= 0x1000;
		if (ISpKeyIsPressed(kISp4PSelect))	controlPad[3] |= 0x2000;
		if (ISpKeyIsPressed(kISp4PY     ))	controlPad[3] |= 0x4000;
		if (ISpKeyIsPressed(kISp4PB     ))	controlPad[3] |= 0x8000;

		JoypadScanDirection(4, &(controlPad[4]));
		if (ISpKeyIsPressed(kISp5PR     ))	controlPad[4] |= 0x0010;
		if (ISpKeyIsPressed(kISp5PL     ))	controlPad[4] |= 0x0020;
		if (ISpKeyIsPressed(kISp5PX     ))	controlPad[4] |= 0x0040;
		if (ISpKeyIsPressed(kISp5PA     ))	controlPad[4] |= 0x0080;
		if (ISpKeyIsPressed(kISp5PStart ))	controlPad[4] |= 0x1000;
		if (ISpKeyIsPressed(kISp5PSelect))	controlPad[4] |= 0x2000;
		if (ISpKeyIsPressed(kISp5PY     ))	controlPad[4] |= 0x4000;
		if (ISpKeyIsPressed(kISp5PB     ))	controlPad[4] |= 0x8000;

		ControlPadFlagsToS9xReportButtons(2, controlPad[2]);
		ControlPadFlagsToS9xReportButtons(3, controlPad[3]);
		ControlPadFlagsToS9xReportButtons(4, controlPad[4]);

		if (macControllerOption == SNES_MULTIPLAYER5_2)
		{
			controlPad[5] = controlPad[6] = controlPad[7] = 0;

			JoypadScanDirection(5, &(controlPad[5]));
			if (ISpKeyIsPressed(kISp6PR     ))	controlPad[5] |= 0x0010;
			if (ISpKeyIsPressed(kISp6PL     ))	controlPad[5] |= 0x0020;
			if (ISpKeyIsPressed(kISp6PX     ))	controlPad[5] |= 0x0040;
			if (ISpKeyIsPressed(kISp6PA     ))	controlPad[5] |= 0x0080;
			if (ISpKeyIsPressed(kISp6PStart ))	controlPad[5] |= 0x1000;
			if (ISpKeyIsPressed(kISp6PSelect))	controlPad[5] |= 0x2000;
			if (ISpKeyIsPressed(kISp6PY     ))	controlPad[5] |= 0x4000;
			if (ISpKeyIsPressed(kISp6PB     ))	controlPad[5] |= 0x8000;

			JoypadScanDirection(6, &(controlPad[6]));
			if (ISpKeyIsPressed(kISp7PR     ))	controlPad[6] |= 0x0010;
			if (ISpKeyIsPressed(kISp7PL     ))	controlPad[6] |= 0x0020;
			if (ISpKeyIsPressed(kISp7PX     ))	controlPad[6] |= 0x0040;
			if (ISpKeyIsPressed(kISp7PA     ))	controlPad[6] |= 0x0080;
			if (ISpKeyIsPressed(kISp7PStart ))	controlPad[6] |= 0x1000;
			if (ISpKeyIsPressed(kISp7PSelect))	controlPad[6] |= 0x2000;
			if (ISpKeyIsPressed(kISp7PY     ))	controlPad[6] |= 0x4000;
			if (ISpKeyIsPressed(kISp7PB     ))	controlPad[6] |= 0x8000;

			JoypadScanDirection(7, &(controlPad[7]));
			if (ISpKeyIsPressed(kISp8PR     ))	controlPad[7] |= 0x0010;
			if (ISpKeyIsPressed(kISp8PL     ))	controlPad[7] |= 0x0020;
			if (ISpKeyIsPressed(kISp8PX     ))	controlPad[7] |= 0x0040;
			if (ISpKeyIsPressed(kISp8PA     ))	controlPad[7] |= 0x0080;
			if (ISpKeyIsPressed(kISp8PStart ))	controlPad[7] |= 0x1000;
			if (ISpKeyIsPressed(kISp8PSelect))	controlPad[7] |= 0x2000;
			if (ISpKeyIsPressed(kISp8PY     ))	controlPad[7] |= 0x4000;
			if (ISpKeyIsPressed(kISp8PB     ))	controlPad[7] |= 0x8000;

			ControlPadFlagsToS9xReportButtons(5, controlPad[5]);
			ControlPadFlagsToS9xReportButtons(6, controlPad[6]);
			ControlPadFlagsToS9xReportButtons(7, controlPad[7]);
		}
	}

	GetKeys(myKeys);

	fnbtn  = (KeyIsPressed(myKeys, keyCode[kKeyFunction]) || ISpKeyIsPressed(kISpFunction));
	altbtn = (KeyIsPressed(myKeys, keyCode[kKeyAlt]     ) || ISpKeyIsPressed(kISpAlt)     );

	if (fnbtn)
	{
		if (!lastTimeFn)
		{
			for (unsigned int i = 0; i < kCommandListSize; i++)
				btncmd[i].held = false;
		}

		lastTimeFn = true;
		lastTimeTT = false;
		ffUp = ffDown = false;

		for (unsigned int i = 0; i < kCommandListSize; i++)
		{
			if (KeyIsPressed(myKeys, btncmd[i].keycode))
			{
				if (!(btncmd[i].held))
				{
					btncmd[i].held = true;

					if (strncmp(btncmd[i].command, "_mac", 4) == 0)
					{
						static char	msg[64];

						switch (btncmd[i].command[4] - '0')
						{
							case 1:
								Settings.DisplayPressedKeys = !Settings.DisplayPressedKeys;
								break;

							case 2:
								if (S9xMovieActive())
									Settings.DisplayMovieFrame = !Settings.DisplayMovieFrame;
								break;

							case 3:
								if (macFrameAdvanceRate < 5000000)
									macFrameAdvanceRate += 100000;
								sprintf(msg, "Emulation Speed: 100/%d", macFrameAdvanceRate / 10000);
								S9xSetInfoString(msg);
								break;

							case 4:
								if (macFrameAdvanceRate > 500000)
									macFrameAdvanceRate -= 100000;
								sprintf(msg, "Emulation Speed: 100/%d", macFrameAdvanceRate / 10000);
								S9xSetInfoString(msg);
								break;

							case 5:
								pauseEmulation = !pauseEmulation;
								break;

							case 6:
								frameAdvance = true;
								break;
						}
					}
					else
					{
						s9xcommand_t	s9xcmd;

						s9xcmd = S9xGetCommandT(btncmd[i].command);
						S9xApplyCommand(s9xcmd, 1, 0);
					}
				}
			}
			else
				btncmd[i].held = false;
		}
	}
	else
	{
		lastTimeFn = false;

		if (KeyIsPressed(myKeys, keyCode[kKeyEsc]))
		{
			if (s9xthreadrunning)
			{
				if (!eventQueued)
				{
					PostQueueToSubEventLoop();
					eventQueued = true;
				}
			}
			else
				running = false;

			return;
		}

		if (KeyIsPressed(myKeys, keyCode[kKeyFreeze]))
		{
			MacStopSound();
			while (KeyIsPressed(myKeys, keyCode[kKeyFreeze]))
				GetKeys(myKeys);

			isok = SNES9X_Freeze();
			ClearGFXScreen();
			return;
		}

		if (KeyIsPressed(myKeys, keyCode[kKeyDefrost]))
		{
			MacStopSound();
			while (KeyIsPressed(myKeys, keyCode[kKeyDefrost]))
				GetKeys(myKeys);

			isok = SNES9X_Defrost();
			ClearGFXScreen();
			return;
		}

		if (KeyIsPressed(myKeys, keyCode[kKeyScreenshot]))
		{
			Settings.TakeScreenshot = true;
			while (KeyIsPressed(myKeys, keyCode[kKeyScreenshot]))
				GetKeys(myKeys);
		}

		if (KeyIsPressed(myKeys, keyCode[kKeySPC]))
		{
			S9xDumpSPCSnapshot();
			while (KeyIsPressed(myKeys, keyCode[kKeySPC]))
				GetKeys(myKeys);
		}

		if (KeyIsPressed(myKeys, keyCode[kKeyFFUp]))
		{
			if (!ffUp)
			{
				ChangeTurboRate(+1);
				ffUp = true;
			}
		}
		else
			ffUp = false;

		if (KeyIsPressed(myKeys, keyCode[kKeyFFDown]))
		{
			if (!ffDown)
			{
				ChangeTurboRate(-1);
				ffDown = true;
			}
		}
		else
			ffDown = false;

		if (KeyIsPressed(myKeys, keyCode[k1PR]     ))	controlPad[0] |= 0x0010;
		if (KeyIsPressed(myKeys, keyCode[k1PL]     ))	controlPad[0] |= 0x0020;
		if (KeyIsPressed(myKeys, keyCode[k1PX]     ))	controlPad[0] |= 0x0040;
		if (KeyIsPressed(myKeys, keyCode[k1PA]     ))	controlPad[0] |= 0x0080;
		if (KeyIsPressed(myKeys, keyCode[k1PRight] ))	controlPad[0] |= 0x0100;
		if (KeyIsPressed(myKeys, keyCode[k1PLeft]  ))	controlPad[0] |= 0x0200;
		if (KeyIsPressed(myKeys, keyCode[k1PDown]  ))	controlPad[0] |= 0x0400;
		if (KeyIsPressed(myKeys, keyCode[k1PUp]    ))	controlPad[0] |= 0x0800;
		if (KeyIsPressed(myKeys, keyCode[k1PStart] ))	controlPad[0] |= 0x1000;
		if (KeyIsPressed(myKeys, keyCode[k1PSelect]))	controlPad[0] |= 0x2000;
		if (KeyIsPressed(myKeys, keyCode[k1PY]     ))	controlPad[0] |= 0x4000;
		if (KeyIsPressed(myKeys, keyCode[k1PB]     ))	controlPad[0] |= 0x8000;

		if (KeyIsPressed(myKeys, keyCode[k2PR]     ))	controlPad[1] |= 0x0010;
		if (KeyIsPressed(myKeys, keyCode[k2PL]     ))	controlPad[1] |= 0x0020;
		if (KeyIsPressed(myKeys, keyCode[k2PX]     ))	controlPad[1] |= 0x0040;
		if (KeyIsPressed(myKeys, keyCode[k2PA]     ))	controlPad[1] |= 0x0080;
		if (KeyIsPressed(myKeys, keyCode[k2PRight] ))	controlPad[1] |= 0x0100;
		if (KeyIsPressed(myKeys, keyCode[k2PLeft]  ))	controlPad[1] |= 0x0200;
		if (KeyIsPressed(myKeys, keyCode[k2PDown]  ))	controlPad[1] |= 0x0400;
		if (KeyIsPressed(myKeys, keyCode[k2PUp]    ))	controlPad[1] |= 0x0800;
		if (KeyIsPressed(myKeys, keyCode[k2PStart] ))	controlPad[1] |= 0x1000;
		if (KeyIsPressed(myKeys, keyCode[k2PSelect]))	controlPad[1] |= 0x2000;
		if (KeyIsPressed(myKeys, keyCode[k2PY]     ))	controlPad[1] |= 0x4000;
		if (KeyIsPressed(myKeys, keyCode[k2PB]     ))	controlPad[1] |= 0x8000;

		if (altbtn)
		{
			if (!lastTimeTT)
				changeAuto[0] = changeAuto[1] = 0;

			for (int i = 0; i < 2; i++)
			{
				for (int j = 0; j < 12; j++)
				{
					uint16	mask = 0x0010 << j;

					if (controlPad[i] & mask & autofireRec[i].toggleMask)
					{
						controlPad[i] &= ~mask;

						if (!(changeAuto[i] & mask))
						{
							changeAuto[i] |= mask;
							ChangeAutofireSettings(i, j);
						}
					}
					else
						changeAuto[i] &= ~mask;
				}
			}

			lastTimeTT = true;
		}
		else
			lastTimeTT = false;
	}

	if (enabletoggle)
	{
		if ((ISpKeyIsPressed(kISpFastForward) || KeyIsPressed(myKeys, keyCode[kKeyFastForward])) && !fnbtn)
		{
			if (!toggleff)
			{
				toggleff = true;
				Settings.TurboMode = !Settings.TurboMode;
				S9xSetInfoString(Settings.TurboMode ? "Turbo mode on" : "Turbo mode off");
				if (!Settings.TurboMode)
					S9xClearSamples();
			}
		}
		else
			toggleff = false;
	}
	else
	{
		bool8	old = Settings.TurboMode;
		Settings.TurboMode = ((ISpKeyIsPressed(kISpFastForward) || KeyIsPressed(myKeys, keyCode[kKeyFastForward])) && !fnbtn) ? true : false;
		if (!Settings.TurboMode && old)
			S9xClearSamples();
	}

	for (int i = 0; i < 2; i++)
		controlPad[i] ^= autofireRec[i].invertMask;

	if (autofire)
	{
		long long	currentTime;
		uint16		changeMask;

		Microseconds((UnsignedWide *) &currentTime);
		tcbtn = (KeyIsPressed(myKeys, keyCode[kKeyTC]) || ISpKeyIsPressed(kISpTC));

		for (int i = 0; i < 2; i++)
		{
			changeMask = (lastTimeTT ? (~changeAuto[i]) : 0xFFFF);

			for (int j = 0; j < 12; j++)
			{
				uint16	mask = (0x0010 << j) & changeMask;

				if (autofireRec[i].tcMask & mask)
				{
					if (!tcbtn)
						continue;
				}

				if (autofireRec[i].buttonMask & mask)
				{
					if (controlPad[i] & mask)
					{
						if (currentTime > autofireRec[i].nextTime[j])
						{
							if (Settings.TurboMode)
								autofireRec[i].nextTime[j] = currentTime + (long long) ((1.0 / (float) autofireRec[i].frequency) * 1000000.0 / macFastForwardRate);
							else
								autofireRec[i].nextTime[j] = currentTime + (long long) ((1.0 / (float) autofireRec[i].frequency) * 1000000.0);
						}
						else
							controlPad[i] &= ~mask;
					}
				}
			}
		}
	}

	ControlPadFlagsToS9xReportButtons(0, controlPad[0]);
	ControlPadFlagsToS9xReportButtons(1, controlPad[1]);

	if (macControllerOption == SNES_JUSTIFIER_2)
		ControlPadFlagsToS9xPseudoPointer(controlPad[1]);
}

static void ChangeAutofireSettings (int player, int btn)
{
	static char	msg[64];
	uint16		mask, m;

	mask = 0x0010 << btn;
	autofireRec[player].buttonMask ^= mask;
	autofire = (autofireRec[0].buttonMask || autofireRec[1].buttonMask);

	m = autofireRec[player].buttonMask;
	if (m)
		snprintf(msg, sizeof(msg), "Autofire %d:%s%s%s%s%s%s%s%s%s%s%s%s%s", player + 1,
			(m & 0xC0F0 ?   " " : ""),
			(m & 0x0080 ?   "A" : ""),
			(m & 0x8000 ?   "B" : ""),
			(m & 0x0040 ?   "X" : ""),
			(m & 0x4000 ?   "Y" : ""),
			(m & 0x0020 ?   "L" : ""),
			(m & 0x0010 ?   "R" : ""),
			(m & 0x0800 ? " Up" : ""),
			(m & 0x0400 ? " Dn" : ""),
			(m & 0x0200 ? " Lf" : ""),
			(m & 0x0100 ? " Rt" : ""),
			(m & 0x1000 ? " St" : ""),
			(m & 0x2000 ? " Se" : ""));
	else
		snprintf(msg, sizeof(msg), "Autofire %d: Off", player + 1);

	S9xSetInfoString(msg);
}

static void ChangeTurboRate (int d)
{
	static char	msg[64];

	macFastForwardRate += d;
	if (macFastForwardRate < 1)
		macFastForwardRate = 1;
	else
	if (macFastForwardRate > 15)
		macFastForwardRate = 15;

	snprintf(msg, sizeof(msg), "Turbo Rate: %d", macFastForwardRate);
	S9xSetInfoString(msg);
}

void GetGameScreenPointer (int16 *x, int16 *y, bool fullmouse)
{
	int	ph;

	ph = !drawoverscan ? ((IPPU.RenderedScreenHeight > 256) ? IPPU.RenderedScreenHeight : (IPPU.RenderedScreenHeight << 1)) : (SNES_HEIGHT_EXTENDED << 1);

	if (fullscreen)
	{
		if (glstretch)
		{
			float   fpw = (float) glScreenH / (float) ph * 512.0f;

			scopeViewInfo.width      = (int) (fpw + ((float) glScreenW - fpw) * (float) macAspectRatio / 10000.0);
			scopeViewInfo.height     = glScreenH;
			scopeViewInfo.globalLeft = (int) glScreenBounds.origin.x + ((glScreenW - scopeViewInfo.width) >> 1);
			scopeViewInfo.globalTop  = (int) glScreenBounds.origin.y;
		}
		else
		{
			scopeViewInfo.width      = 512;
			scopeViewInfo.height     = ph;
			scopeViewInfo.globalLeft = (int) glScreenBounds.origin.x + ((glScreenW - 512) >> 1);
			scopeViewInfo.globalTop  = (int) glScreenBounds.origin.y + ((glScreenH - ph ) >> 1);
		}
	}
	else
	{
		Rect	rct;

		GetWindowBounds(gWindow, kWindowContentRgn, &rct);

		int	ww = rct.right  - rct.left,
			wh = rct.bottom - rct.top;

		scopeViewInfo.width      = ww;
		scopeViewInfo.globalLeft = rct.left;

		if (windowExtend)
		{
			scopeViewInfo.height    = ph * wh / kMacWindowHeight;
			scopeViewInfo.globalTop = rct.top + ((kMacWindowHeight - ph) >> 1) * wh / kMacWindowHeight;
		}
		else
		{
			scopeViewInfo.height    = wh;
			scopeViewInfo.globalTop = rct.top;
		}
	}

	if (!fullmouse)
	{
		Point	pos;

		GetGlobalMouse(&pos);

		*x = (int16) (((float) (pos.h - scopeViewInfo.globalLeft)) / ((float) scopeViewInfo.width ) * (float) IPPU.RenderedScreenWidth);
		*y = (int16) (((float) (pos.v - scopeViewInfo.globalTop )) / ((float) scopeViewInfo.height) * (float) (!drawoverscan ? IPPU.RenderedScreenHeight : SNES_HEIGHT_EXTENDED));
	}
	else
	{
		*x = (int16) (unlimitedCursor.x / (float) scopeViewInfo.width  * (float) IPPU.RenderedScreenWidth);
		*y = (int16) (unlimitedCursor.y / (float) scopeViewInfo.height * (float) (!drawoverscan ? IPPU.RenderedScreenHeight : SNES_HEIGHT_EXTENDED));
	}
}

static void Initialize (void)
{
	OSStatus	err;
	IBNibRef	menuNibRef;
	MenuRef		menu;
	SInt32		qtVersion;

	printf("\nSnes9x for Mac OS X %s (%s), ", VERSION, MAC_VERSION);
#ifdef __BIG_ENDIAN__
	printf("PowerPC\n\n");
#else
	printf("Intel\n\n");
#endif

	err = Gestalt(gestaltSystemVersion, &systemVersion);
	err = Gestalt(gestaltQuickTimeVersion, &qtVersion);

	if ((systemVersion < 0x1039) || (qtVersion < 0x07008000))
	{
		AppearanceAlert(kAlertStopAlert, kS9xMacAlertRequiredSystem, kS9xMacAlertRequiredSystemHint);
		QuitWithFatalError(0, "os 09");
	}

	printf("OS: %x  QuickTime: %x\n\n", (unsigned) systemVersion, (unsigned) qtVersion);

#ifdef ZLIB
	printf("zlib header version: %s\n\n", ZLIB_VERSION);
#endif

	NSApplicationLoad();

	bzero(&Settings, sizeof(Settings));
	Settings.MouseMaster = true;
	Settings.SuperScopeMaster = true;
	Settings.JustifierMaster = true;
	Settings.MultiPlayer5Master = true;
	Settings.FrameTimePAL = 20000;
	Settings.FrameTimeNTSC = 16667;
	Settings.SixteenBitSound = true;
	Settings.Stereo = true;
	Settings.SoundPlaybackRate = 32000;
	Settings.SoundInputRate = 31950;
	Settings.SupportHiRes = true;
	Settings.Transparency = true;
	Settings.AutoDisplayMessages = true;
	Settings.InitialInfoStringTimeout = 120;
	Settings.HDMATimingHack = 100;
	Settings.BlockInvalidVRAMAccessMaster = true;
	Settings.StopEmulation = true;
	Settings.WrongMovieStateProtection = true;
	Settings.DumpStreamsMaxFrames = -1;
	Settings.StretchScreenshots = 1;
	Settings.SnapshotScreenshots = true;
	Settings.OpenGLEnable = true;
	Settings.SuperFXClockMultiplier = 100;
	Settings.InterpolationMethod = DSP_INTERPOLATION_GAUSSIAN;
	Settings.MaxSpriteTilesPerLine = 34;
	Settings.OneClockCycle = 6;
	Settings.OneSlowClockCycle = 8;
	Settings.TwoClockCycles = 12;

	for (int a = 0; a < kWindowCount; a++)
	{
		windowPos[a].h = 40;
		windowPos[a].v = 80;
		windowSize[a].width  = -1.0f;
		windowSize[a].height = -1.0f;
	}

	extraOptions.benchmark = false;
	extraOptions.glForceNoTextureRectangle = false;
	extraOptions.glUseClientStrageApple = true;
	extraOptions.glUseTexturePriority = false;
	extraOptions.glStorageHint = 2;

	npServerIP[0] = 0;
	npName[0] = 0;

	saveFolderPath = NULL;

	CreateIconImages();

	InitAppleEvents();
	InitKeyboard();
	InitAutofire();
	InitCheatFinder();

	LoadPrefs();

	InitGraphics();
	InitMacSound();
	SetUpHID();

	RegisterHelpBook();

	if (systemVersion < 0x1040)
		ciFilterEnable = false;

	err = CreateNibReference(kMacS9XCFString, &menuNibRef);
	err = SetMenuBarFromNib(menuNibRef, CFSTR("MenuBar"));
	DisposeNibReference(menuNibRef);

	EnableMenuCommand(NULL, kHICommandPreferences);

	DisableMenuItem(GetMenuRef(mEdit), 0);

	CheckMenuItem(GetMenuRef(mCheat), iApplyCheats, applycheat);
	Settings.ApplyCheats = applycheat;

	err = GetMenuItemHierarchicalMenu(GetMenuRef(mControl), iISpPreset, &menu);
	CheckMenuItem(menu, padSetting, true);

	err = GetMenuItemHierarchicalMenu(GetMenuRef(mEmulation), iDevice, &menu);
	CheckMenuItem(menu, deviceSetting, true);
	deviceSettingMaster = deviceSetting;

	DisableMenuItem(GetMenuRef(mOption), iCIFilter);

	InitRecentItems();
	InitRecentMenu();
	BuildRecentMenu();

	InitMultiCart();

	DrawMenuBar();

	autofire = (autofireRec[0].buttonMask || autofireRec[1].buttonMask) ? true : false;
	for (int a = 0; a < MAC_MAX_PLAYERS; a++)
		for (int b = 0; b < 12; b++)
			autofireRec[a].nextTime[b] = 0;

	S9xMovieInit();

	S9xUnmapAllControls();
	S9xSetupDefaultKeymap();
	ChangeInputDevice();

	err = EnterMovies();

	if (!Memory.Init() || !S9xInitAPU() || !S9xGraphicsInit())
		QuitWithFatalError(err, "os 01");

	frzselecting = false;

	S9xSetControllerCrosshair(X_MOUSE1, 0, NULL, NULL);
	S9xSetControllerCrosshair(X_MOUSE2, 0, NULL, NULL);

	if (systemVersion >= 0x1040)
	{
		InitCoreImage();
		InitCoreImageFilter();
	}
}

static void Deinitialize (void)
{
	if (systemVersion >= 0x1040)
	{
		DeinitCoreImageFilter();
		DeinitCoreImage();
	}

	deviceSetting = deviceSettingMaster;

	ExitMovies();

	DeinitMultiCart();
	DeinitRecentMenu();
	DeinitRecentItems();
	SavePrefs();
	ReleaseHID();
	DeinitCheatFinder();
	DeinitGraphics();
	DeinitKeyboard();
	DeinitMacSound();
	DeinitAppleEvents();
	ReleaseIconImages();

	S9xGraphicsDeinit();
	S9xDeinitAPU();
	Memory.Deinit();
}

static void InitAutofire (void)
{
	autofire = false;

	for (int i = 0; i < 2; i++)
	{
		for (int j = 0; j < 12; j++)
			autofireRec[i].nextTime[j] = 0;

		autofireRec[i].buttonMask = 0x0000;
		autofireRec[i].toggleMask = 0xFFF0;
		autofireRec[i].tcMask     = 0x0000;
		autofireRec[i].invertMask = 0x0000;
		autofireRec[i].frequency  = 10;
	}
}

static void ForceChangingKeyScript (void)
{
	if (systemVersion >= 0x1050)
	{
		OSStatus			err;
		TISInputSourceRef	tis;

		tis = TISCopyCurrentASCIICapableKeyboardInputSource();
		err = TISSelectInputSource(tis);
		CFRelease(tis);
	}
#ifdef MAC_TIGER_PANTHER_SUPPORT
	else
	{
		long	script;

		script = GetScriptManagerVariable(smKeyScript);
		if (script == smJapanese)
			KeyScript(smRoman | smKeyForceKeyScriptMask);
	}
#endif
}

void S9xSyncSpeed (void)
{
	long long	currentFrame, adjustment;

	if (directDisplay)
	{
		if (extraOptions.benchmark)
			IPPU.RenderThisFrame = true;
		else
		{
			if (Settings.SoundSync)
			{
				while (!S9xSyncSound())
					usleep(0);
			}

			if (!macQTRecord)
			{
				if (macFrameSkip < 0)	// auto skip
				{
					skipFrames--;

					if (skipFrames <= 0)
					{
						adjustment = (Settings.TurboMode ? (macFrameAdvanceRate / macFastForwardRate) : macFrameAdvanceRate) / Memory.ROMFramesPerSecond;
						Microseconds((UnsignedWide *) &currentFrame);

						skipFrames = (int32) ((currentFrame - lastFrame) / adjustment);
						lastFrame += frameCount * adjustment;

						if (skipFrames < 1)
							skipFrames = 1;
						else
						if (skipFrames > 7)
						{
							skipFrames = 7;
							Microseconds((UnsignedWide *) &lastFrame);
						}

						frameCount = skipFrames;

						if (lastFrame > currentFrame)
							usleep((useconds_t) (lastFrame - currentFrame));

						IPPU.RenderThisFrame = true;
					}
					else
						IPPU.RenderThisFrame = false;
				}
				else					// constant
				{
					skipFrames--;

					if (skipFrames <= 0)
					{
						adjustment = macFrameAdvanceRate * macFrameSkip / Memory.ROMFramesPerSecond;
						Microseconds((UnsignedWide *) &currentFrame);

						if (currentFrame - lastFrame < adjustment)
						{
							usleep((useconds_t) (adjustment + lastFrame - currentFrame));
							Microseconds((UnsignedWide *) &currentFrame);
						}

						lastFrame = currentFrame;
						skipFrames = macFrameSkip;
						if (Settings.TurboMode)
							skipFrames *= macFastForwardRate;

						IPPU.RenderThisFrame = true;
					}
					else
						IPPU.RenderThisFrame = false;
				}
			}
			else
			{
				MacQTRecordFrame(IPPU.RenderedScreenWidth, IPPU.RenderedScreenHeight);

				adjustment = macFrameAdvanceRate / Memory.ROMFramesPerSecond;
				Microseconds((UnsignedWide *) &currentFrame);

				if (currentFrame - lastFrame < adjustment)
					usleep((useconds_t) (adjustment + lastFrame - currentFrame));

				lastFrame = currentFrame;

				IPPU.RenderThisFrame = true;
			}
		}
	}
	else
		IPPU.RenderThisFrame = false;
}

void S9xAutoSaveSRAM (void)
{
    SNES9X_SaveSRAM();
}

void S9xMessage (int type, int number, const char *message)
{
	static char	mes[256];

	if (!onscreeninfo)
	{
		printf("%s\n", message);

		if ((type == S9X_INFO) && (number == S9X_ROM_INFO))
			if (strstr(message, "checksum ok") == NULL)
				AppearanceAlert(kAlertCautionAlert, kS9xMacAlertkBadRom, kS9xMacAlertkBadRomHint);
	}
	else
	{
		strncpy(mes, message, 255);
		S9xSetInfoString(mes);
	}
}

const char * S9xStringInput (const char *s)
{
	return (NULL);
}

void S9xToggleSoundChannel (int c)
{
    static int	channel_enable = 255;

	if (c == 8)
		channel_enable = 255;
    else
		channel_enable ^= 1 << c;

	S9xSetSoundControl(channel_enable);
}

void S9xExit (void)
{
	PlayAlertSound();

	running = false;
	cartOpen = false;

	QuitApplicationEventLoop();
}

void QuitWithFatalError (OSStatus err, const char *msg)
{
	printf("Quit. %s  err: %ld\n", msg, err);
	ExitToShell();
}
