/* File: main-crb.c */

/*
 * Copyright (c) 1997 Ben Harrison, Keith Randall, Peter Ammon, Ron Anderson
 * and others
 *
 * This software may be copied and distributed for educational, research,
 * and not for profit purposes provided that this copyright and statement
 * are included in all such copies.
 */


/*
 * This file helps Angband work with Macintosh computers running OS X,
 * or OS 8/9 with CarbonLib system extention.
 *
 * To use this file, use an appropriate "Makefile" or "Project File", which
 * should define "MACINTOSH".
 *
 * The official compilation uses the CodeWarrior Pro compiler.
 *
 * If you are never going to use "graphics" (especially if you are not
 * compiling support for graphics anyway) then you can delete the "pict"
 * resources with id "1001", "1002", "1003" and "1004" with no dangerous
 * side effects.
 *
 *
 * This file assumes that you will be using a PPC Mac running OS X
 * or OS 8/9 (8.6 or greater) with CarbonLib system extention enabled.
 * In fact, the game will refuse to run unless these features are available.
 *
 * MACH_O_CARBON code pushes the system requirement a bit further, and
 * I don't think it works on System 8, even with CarbonLib, because it uses
 * the Bundle services, but I may be wrong.
 *
 * Note that the "preference" file is now a simple XML text file
 * called "<program name>.plist" in case of PEF Carbon, and "<Java-style
 * program id defined in Info.plist>.plist" for Mach-O Carbon, which contains
 * key-value paris, so it no longer has to check version stamp to validate
 * its contents.
 *
 *
 * Note that "init1.c", "init2.c", "load1.c", "load2.c", and "birth.c"
 * should probably be "unloaded" as soon as they are no longer needed,
 * to save space, but I do not know how to do this.  XXX XXX XXX
 *
 * Stange bug -- The first "ClipRect()" call crashes if the user closes
 * all the windows, switches to another application, switches back, and
 * re-opens the main window, for example, using "command-a".  XXX XXX XXX
 *
 *
 * Initial framework (and most code) by Ben Harrison (benh@phial.com).
 *
 * Some code adapted from "MacAngband 2.6.1" by Keith Randall
 *
 * Initial PowerMac port by Maarten Hazewinkel (mmhazewi@cs.ruu.nl).
 *
 * Most Apple Event code provided by Steve Linberg (slinberg@crocker.com).
 *
 * Most of the graphics code is adapted from an extremely minimal subset of
 * the "Sprite World II" package, an amazing (and free) animation package.
 *
 * Carbon code adapted from works by Peter Ammon and Ron Anderson.
 *
 * (List of changes made by "pelpel" follow)
 * Some API calls are updated to OS 8.x-- ones.
 *
 * Pixmap locking code in Term_pict_map() follows Carbon Porting Guide
 * by Apple.
 *
 * The idle loop in TERM_XTRA_DELAY is rewritten to sleep on WaitNextEvent
 * for a couple of reasons.
 *
 * CheckEvent now really blocks whenever asked to wait.
 *
 * The unused buffer GWorld is completely removed. It has long been pure waste
 * of memory.
 *
 * The default font-size combination was changed because the old one, Monaco
 * at 12 points causes the redraw artefact problem on OS X.
 *
 * Characters in the ASCII mode are clipped by their bounding rects to reduce
 * redraw artefacts that were quite annoying in certain font-point combos.
 *
 * Transparency effect now avoids double bitblts whenever possible.
 *
 * Old tiles were drawn in a wrong fashion by the USE_TRANSPARENCY code.
 *
 * ASCII and the two graphics modes are now controlled by single graf_mode
 * variable.  arg_* and use_* variables are set when requested mode is
 * successfully initialised.
 *
 * Most of the menus are now loaded from resources.
 *
 * Moved TileWidth and TileHeight menus into Special. There were too many menus.
 *
 * Added support for 32x32 tiles, now for [V] only.
 *
 * Related to the above, globe_init no longer loads tile images twice if
 * a tileset doesn't have corresponding masks.
 *
 * Added support for POSIX-style pathnames, for Mach-O Carbon (gcc, CW >= 7).
 * We can finally live without Pascal strings to handle files this way.
 *
 * (Mach-O Carbon) Graphics tiles are moved out of the resource fork into
 * bundle-based data fork files.
 *
 * Changed size-related menu code, because they no longer function because
 * some APIs have been changed to return Unicode in some cases.
 *
 * Changed the transparency code again, this time using Ron Anderson's code,
 * which makes more sound assumption about background colour and is more
 * efficient.
 *
 * The old asynchronous sound player could try to lock the same handle more
 * than once, load same sound resource already in use, or unlock and release
 * currently playing sound.
 *
 * hook_quit() now releases memory-related resources dynamically allocated by
 * the graphics and sound code.
 *
 * Important Resources in the resource file:
 *
 *   FREF 130 = ANGBAND_CREATOR / 'APPL' (application)
 *   FREF 129 = ANGBAND_CREATOR / 'SAVE' (save file)
 *   FREF 130 = ANGBAND_CREATOR / 'TEXT' (bone file, generic text file)
 *   FREF 131 = ANGBAND_CREATOR / 'DATA' (binary image file, score file)
 *
 *   DLOG 128 = "About Angband..."
 *
 *   ALRT 128 = unused (?)
 *   ALRT 129 = "Warning..."
 *
 *   DITL 128 = body for DLOG 128
 *   DITL 129 = body for ALRT 129
 *   DITL 130 = body for ALRT 130
 *
 *   ICON 128 = "warning" icon
 *
 *   MBAR 128 = array of MENU id's (128, 129, 130, 131, 132, 133, 134)
 *   MENU 128 = apple (about, -, ...)
 *   MENU 129 = File (new, open, close, save, -, score, quit)
 *     (If SAVEFILE_SCREEN is defined)
 *   MENU 129 = File (close, save, -, score, quit)
 *   MENU 130 = Edit (undo, -, cut, copy, paste, clear)
 *   MENU 131 = Font (bold, wide, -)
 *   MENU 132 = Size ()
 *   MENU 133 = Windows ()
 *   MENU 134 = Special (Sound, Graphics, TileWidth, TileHeight, -, Fiddle,
 *                       Wizard)
 *              Graphics have following submenu attached:
 *   MENU 144 = Graphics (None, 8x8, 16x16, 32x32, enlarge tiles)
 *              TileWidth and TileHeight submenus are filled in by this program.
 *   MENU 145 = TileWidth ()
 *   MENU 146 = TileHeight ()
 *
 *   On CFM(PEF) Carbon only:
 *   PICT 1001 = Graphics tile set (8x8)
 *   PICT 1002 = Graphics tile set (16x16 images)
 *   PICT 1004 = Graphics tile set (32x32)
 *
 *   Mach-O Carbon now uses data fork resources:
 *   8x8.png   = Graphics tile set (8x8)
 *   16x16.png = Graphics tile set (16x16 images)
 *   32x32.png = Graphics tile set (32x32)
 *   These files should go into the Resources subdirectory of an application
 *   bundle.
 *
 *   STR# 128 = "Please select the "lib" folder"
 *
 *   plst 0   can be empty, but required for single binary Carbon apps on OS X
 *            Isn't necessary for Mach-O Carbon.
 *
 *
 * File name patterns:
 *   all 'APEX' files have a filename of the form "*:apex:*" (?)
 *   all 'BONE' files have a filename of the form "*:bone:*" (?)
 *   all 'DATA' files have a filename of the form "*:data:*"
 *   all 'SAVE' files have a filename of the form "*:save:*"
 *   all 'USER' files have a filename of the form "*:user:*" (?)
 *
 * Perhaps we should attempt to set the "_ftype" flag inside this file,
 * to avoid nasty file type information being spread all through the
 * rest of the code.  (?)  This might require adding hooks into the
 * "fd_open()" and "my_fopen()" functions in "util.c".  XXX XXX XXX
 *
 *
 * Reasons for each header file:
 *
 *   angband.h = Angband header file
 *
 *   Types.h = (included anyway)
 *   Gestalt.h = gestalt code
 *   QuickDraw.h = (included anyway)
 *   OSUtils.h = (included anyway)
 *   Files.h = file code
 *   Fonts.h = font code
 *   Menus.h = menu code
 *   Dialogs.h = dialog code
 *   Windows.h = (included anyway)
 *   Palettes.h = palette code
 *   ToolUtils.h = HiWord() / LoWord()
 *   Events.h = event code
 *   Resources.h = resource code
 *   Controls.h = button code
 *   SegLoad.h = ExitToShell(), AppFile, etc
 *   Memory.h = NewPtr(), etc
 *   QDOffscreen.h = GWorld code
 *   Sound.h = Sound code
 *   Navigation.h = save file / lib locating dialogues
 *   CFPreferences.h = Preferences
 *   CFNumber.h = read/write short values from/to preferences
 */

/*
 * Yet another main-xxx.c for Carbon (pelpel) - revision 11d
 *
 * Since I'm using CodeWarrior, the traditional header files are
 * #include'd below.
 *
 * I also compiled Angband 3.0.2 successfully with OS X's gcc.
 * Please follow these instructions if you are interested.
 *
 * ---(developer CD gcc + makefile porting notes, for Angband 3.0.2)-------
 * 1. Compiling the binary
 *
 * If you try this on OS X + gcc, please use makefile.std, replacing
 * main.c and main.o with main-crb.c and main-crb.o, removing all main-xxx.c
 * and main-xxx.o from SRCS and OBJS, and, and use these settings:
 *
 * COPTS = -Wall -O1 -g -fpascal-strings
 * INCLUDES =
 * DEFINES = -DMACH_O_CARBON -DANGBAND30X
 * LIBS = -framework CoreFoundation -framework QuickTime -framework Carbon
 *
 * -DANGBAND30X only affects main-crb.c. This is because I'm also compiling
 * a couple of variants, and this arrangement makes my life easier.
 *
 * Never, ever #define MACINTOSH.  It'll wreck havoc in system interface
 * (mostly because of totally different pathname convention).
 *
 * You might wish to disable some SET_UID features for various reasons:
 * to have user folder within the lib folder, savefile names etc.
 *
 * For the best compatibility with the Classic ports and my PEF Carbon
 * ports, my_fopen, fd_make and fd_open [in util.c] should call
 *   (void)fsetfileinfo(buf, _fcreator, _ftype);
 * when a file is successfully opened.  Or you'll see odd icons for some files
 * in the lib folder.  In order to do so, extern.h should contain these lines,
 * within #ifdef MACH_O_CARBON:
 *   extern int fsetfileinfo(char *path, u32b fcreator, u32b ftype);
 *   extern u32b _fcreator;
 *   extern u32b _ftype;
 * And enable the four FILE_TYPE macros in h-config.h for defined(MACH_O_CARBON)
 * in addition to defined(MACINTOSH) && !defined(applec), i.e.
 *  #if defined(MACINTOSH) && !defined(applec) || defined(MACH_O_CARBON)
 *
 * This is a very good way to spot bugs in use of these macros, btw.
 *
 * 2. Installation
 *
 * The "angband" binary must be arranged this way for it to work:
 *
 * lib/ <- the lib folder
 * Angband (OS X).app/
 *   Contents/
 *     MacOS/
 *       angband <- the binary you've just compiled
 *     Info.plist <- to be explained below
 *     Resources/
 *       Angband.icns
 *       Data.icns
 *       Edit.icns
 *       Save.icns
 *       8x8.png <- 8x8 tiles
 *       16x16.png <- 16x16 tiles
 *       angband.rsrc <- see below
 *
 * 3. Preparing Info.plist
 *
 * Info.plist is an XML file describing some attributes of an application,
 * and this is appropriate for Angband:
 *
 * <?xml version="1.0" encoding="UTF-8"?>
 * <plist version="1.0">
 *   <dict>
 *     <key>CFBundleName</key><string>Angband</string>
 *     <key>CFBundleDisplayName</key><string>Angband (OS X)</string>
 *     <key>CFBundleExecutable</key><string>angband</string>
 *     <key>CFBundlePackageType</key><string>APPL</string>
 *     <key>CFBundleSignature</key><string>A271</string>
 *     <key>CFBundleVersion</key><string>3.0.2</string>
 *     <key>CFBundleShortVersionString</key><string>3.0.2</string>
 *     <key>CFBundleIconFile</key><string>Angband</string>
 *     <key>CFBundleIdentifier</key><string>net.thangorodrim.Angband</string>
 *     <key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
 *     <key>CFBundleDocumentTypes</key>
 *       <array>
 *         <dict>
 *           <key>CFBundleTypeExtentions</key><array><string>*</string></array>
 *           <key>CFBundleTypeIconFile</key><string>Save</string>
 *           <key>CFBundleTypeName</key><string>Angband saved game</string>
 *           <key>CFBundleTypeOSTypes</key><array><string>SAVE</string></array>
 *           <key>CFBundleTypeRole</key><string>Editor</string>
 *         </dict>
 *         <dict>
 *           <key>CFBundleTypeExtentions</key><array><string>*</string></array>
 *           <key>CFBundleTypeIconFile</key><string>Edit</string>
 *           <key>CFBundleTypeName</key><string>Angband game data</string>
 *           <key>CFBundleTypeOSTypes</key><array><string>TEXT</string></array>
 *           <key>CFBundleTypeRole</key><string>Editor</string>
 *         </dict>
 *         <dict>
 *           <key>CFBundleTypeExtentions</key><array><string>raw</string></array>
 *           <key>CFBundleTypeIconFile</key><string>Data</string>
 *           <key>CFBundleTypeName</key><string>Angband game data</string>
 *           <key>CFBundleTypeOSTypes</key><array><string>DATA</string></array>
 *           <key>CFBundleTypeRole</key><string>Editor</string>
 *         </dict>
 * 	</array>
 *   </dict>
 * </plist>
 *
 * 4. Menu, diaglogue and gfx resources
 *
 * The binary assumes angband.rsrc should be in the traditional resource
 * mangager format.  Please run this command to create it from its textual
 * description:
 *
 * Rez -i /Developer/Headers/FlatCarbon -d MACH_O -o angband.rsrc Angband.r
 *
 * The command is in /Developer/Tools.  You might wish to include it in your
 * PATH.
 *
 * It's better to comment out the definitions of BNDL and plst resources
 * before you do that.  I think you can DeRez the resulting tome.rsrc and
 * feed it to the Interface Builder to produce a set of compatible .nib files,
 * but this file also needs to be updated to understand .nib...  On the other
 * hand, I really don't like to hardcode UI definitions in C.
 *
 * Graphics resources are moved out of the resource fork and become ordinary
 * PNG files.  Make sure to set its resolution to 72 dpi (<- VERY important)
 * while keeping vertical and horizontal scaling factor to 100% (<- VERY
 * important), when you convert tiles in any formats to PNG.  This means
 * that the real size of an image must shrink or grow when you change it's dpi.
 *
 * Sound resources are a bit more complicated.
 * The easiest way is:
 * 1) Grab recent Mac Angband binary.
 * 2) Run this command:
 *   DeRez -only 'snd ' (Angband binary) > sound.r
 * 3) And specify sound.r files in addition to Angband.r when you run Rez.
 *
 * ---(end of OS X + gcc porting note)--------------------------------------
 *
 * Code adapted from Peter Ammon's work on 2.8.3 and some modifications
 * are made when Apple's Carbon Porting Guide says they are absolutely
 * necessary. Other arbirary changes are mostly because of my hatred
 * of deep nestings and indentations. The code for controlling graphics modes
 * have been thoroughly revised simply because I didn't like it (^ ^;).
 * A bonus of this is that graphics settings can be loaded from Preferences
 * quite easily.
 *
 * I also took Ron Anderson's (minimising the use of local-global coordinate
 * conversions). Some might say his QuickTime multimedia is the most
 * significant achievement... Play your favourite CD instead, if you really
 * miss that (^ ^;) I might consider incorporating it if it makes use of
 * event notification.
 *
 * I replaced some old API calls with new (OS 8.x--) ones, especially
 * when I felt Apple is strongly against their continued usage.
 *
 * Similarly, USE_SFL_CODE should be always active, so I removed ifdef's
 * just to prevent accidents, as well as to make the code a bit cleaner.
 *
 * On the contrary, I deliberately left traditional resource interfaces.
 * Whatever Apple might say, I abhor file name extentions. And keeping two
 * different sets of resources for Classic and Carbon is just too much for
 * a personal project XXX
 *
 * Because Carbon forbids the use of 68K code, ANGBAND_LITE_MAC sections
 * are removed.
 *
 * Because the default font-size combination causes redraw artefact problem
 * (some characters, even in monospace fonts, have negative left bearings),
 * I introduced rather crude hack to clip all character drawings within
 * their bounding rects. If you don't like this, please comment out the line
 * #define CLIP_HACK
 * below.
 *
 * The check for return values of AEProcessAppleEvent is removed,
 * because it results in annoying dialogues on OS X, also because
 * Apple says it isn't usually necessary.
 *
 * Because the always_pict code is *so* slow, I changed the graphics
 * mode selection a bit to use higher_pict when a user chooses a fixed
 * width font and doesn't changes tile width / height.
 *
 * Added support for David Gervais' 32x32 tiles.
 *
 * Replaced transparency effect code by Ron Anderson's.
 *
 * Added support for gcc & make compilation. They come free with OS X
 * (on the developer CD).  This means that it can be compiled as a bundle.
 *
 * For Mach-O Carbon binary, moved graphics tiles out of the
 * resource fork and made them plain PNG files, to be stored in the application
 * bundle's "Resources" subdirectory.
 *
 * For Mach-O Carbon binary, provided a compile-time option (USE_QT_SOUND) to
 * move sound effect samples out of the resource fork and use *.wav files in
 * the bundle's "Resources" subdirectory. The "*" part must match the names in
 * angband_sound_name (variable.c) exactly. This doesn't hurt performance
 * a lot in [V] (it's got ~25 sound events), but problematic in [Z]-based
 * ones (they have somewhere around 70 sound events).
 *
 * You still can use the resource file that comes with the ext-mac archive
 * on the Angband FTP server, with these additions:
 * - MENUs 131--134 and 144--146, as described above
 * - MBAR 128: just a array of 128 through 134
 * - plst 0 : can be empty, although Apple recommends us to fill it in.
 * - STR# 128 : something like "Please select your lib folder"
 *
 * Since this involves considerable amount of work, I attached
 * a plain text resource definition (= Rez format) .
 * I heavily commented on the file, hoping it could be easily adapted
 * to future versions of Angband as well as variants.
 * I omitted sound effects and graphic tiles to make it reasonably small.
 * Please copy them from any recent Mac binaries - you can use, say ZAngband
 * ones for Vanilla or [V]-based variants quite safely. T.o.M.E. uses fairly
 * extended 16x16 tileset and it is maintained actively. IIRC Thangorodrim
 * compile page has an intruction explaining how to convert tiles for
 * use on the Mac... It can be tricky, depending on your choice of
 * graphics utility. Remember setting resolution to 72 pixels per inch,
 * while keeping vertical/horizontal scale factor to 100% and dump the
 * result as a PICT from resource.
 *
 * To build Carbonised Angband with CodeWarrior, copy your PPC project
 * and
 * - replace main-mac.c in the project with this file (in the link order tab)
 * - remove InterfaceLib and MathLib
 * - add CarbonLib (found in Carbon SDK or CW's UniversalInterfaces) --
 *   if you have compiler/linker errors, you'll need Carbon SDK 1.1 or greater
 * - replace MSL C.PPC.Lib with MSL C.Carbon.Lib (both found in
 *   MSL:MSL_C:MSL_MacOS:Lib:PPC)
 * - leave MSL RuntimePPC.Lib as it is
 * - don't forget to update resource file, as described above
 * - as in Classic targets, you may have to include <unistd.h> and
 *   <fcntl.h>. The most convinient place for them is the first
 *   #ifdef MACINTOSH in h-system.h
 * - check variant dependent ifdef's explained below, and add
 *   appropriate one(s) in your A-mac-h.pch.
 */


/*
 * Force Carbon-compatible APIs
 */
#ifndef MACH_O_CARBON

/* Can be CodeWarrior or MPW */
# define TARGET_API_MAC_CARBON 1

#else

/*
* Must be Mach-O Carbon target with OS X gcc.
* No need to set TARGET_API_MAC_CARBON to 1 here, but I assume it should
* be able to make efficient use of BSD functions, hence:
*/
# define USE_MALLOC
/* Not yet */
/* # define USE_NIB */

#endif /* !MACH_O_CARBON */


#include "angband.h"

#if defined(MACINTOSH) || defined(MACH_O_CARBON)

#ifdef PRIVATE_USER_PATH

/*
 * Check and create if needed the directory dirpath
 */
bool private_check_user_directory(cptr dirpath)
{
	/* Is this used anywhere else in *bands? */
	struct stat stat_buf;

	int ret;

	/* See if it already exists */
	ret = stat(dirpath, &stat_buf);

	/* It does */
	if (ret == 0)
	{
		/* Now we see if it's a directory */
		if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) return (TRUE);

		/*
		 * Something prevents us from create a directory with
		 * the same pathname
		 */
		return (FALSE);
	}

	/* No - this maybe the first time. Try to create a directory */
	else
	{
		/* Create the ~/.ToME directory */
		ret = mkdir(dirpath, 0700);

		/* An error occured */
		if (ret == -1) return (FALSE);

		/* Success */
		return (TRUE);
	}
}

/*
 * Check existence of ".ToME/" directory in the user's
 * home directory or try to create it if it doesn't exist.
 * Returns FALSE if all the attempts fail.
 */
static bool check_create_user_dir(void)
{
	char dirpath[1024];
	char versionpath[1024];
	char savepath[1024];
#ifdef PRIVATE_USER_PATH_DATA
	char datapath[1024];
#endif
#ifdef PRIVATE_USER_PATH_APEX
	char apexpath[1024];
#endif

	/* Get an absolute path from the filename */
	path_parse(dirpath, 1024, PRIVATE_USER_PATH);
	strcpy(versionpath, dirpath);
	strcat(versionpath, USER_PATH_VERSION);
	strcpy(savepath, versionpath);
	strcat(savepath, "/save");
#ifdef PRIVATE_USER_PATH_DATA
	strcpy(datapath, versionpath);
	strcat(datapath, "/data");
#endif
#ifdef PRIVATE_USER_PATH_APEX
	strcpy(apexpath, versionpath);
	strcat(apexpath, "/apex");
#endif

	return /* don't forget, the dirpath muts come first */
	       private_check_user_directory(dirpath) &&
	       private_check_user_directory(versionpath) &&
#ifdef PRIVATE_USER_PATH_DATA
	       private_check_user_directory(datapath) &&
#endif
#ifdef PRIVATE_USER_PATH_APEX
	       private_check_user_directory(apexpath) &&
#endif
	       private_check_user_directory(savepath);
}

#endif /* PRIVATE_USER_PATH */


/*
 * Variant-dependent features:
 *
 * #define ALLOW_BIG_SCREEN (V, Ey, O, T.o.M.E., and Z.  Dr's big screen needs
 * more work.  New S one is too idiosyncratic...)
 * #define ANG281_RESET_VISUALS (Cth, Gum, T.o.M.E., Z)
 * #define SAVEFILE_SCREEN (T.o.M.E.)
 * #define ZANG_AUTO_SAVE (O and Z)
 * #define HAS_SCORE_MENU (V and T.o.M.E.)
 * #define ANGBAND_CREATOR four letter code for your variant, if any.
 * or use the default one.
 *
 * For [Z], you also have to say -- #define inkey_flag (p_ptr->inkey_flag)
 * but before that, please, please consider using main-mac-carbon.c in [Z],
 * that has some interesting features.
 */

/* Some porting examples */
#ifdef ANGBAND30X
# define USE_DOUBLE_TILES
# define ALLOW_BIG_SCREEN
# define HAS_SCORE_MENU
# define NEW_ZVIRT_HOOKS
/* I can't ditch this, yet, because there are many variants */
# define USE_TRANSPARENCY
#endif /* ANGBAND30X */

# define USE_DOUBLE_TILES
# define SAVEFILE_SCREEN
# define ANG281_RESET_VISUALS
# define ALLOW_BIG_SCREEN
# define HAS_SCORE_MENU
# define ANGBAND_CREATOR 'PrnA'

/* Default creator signature */
#ifndef ANGBAND_CREATOR
# define ANGBAND_CREATOR 'A271'
#endif


/*
 * Use rewritten asynchronous sound player
 */
#define USE_ASYNC_SOUND


/*
 * A rather crude fix to reduce amount of redraw artefacts.
 * Some fixed width fonts (i.e. Monaco) has characters with negative
 * left bearings, so Term_wipe_mac or overwriting cannot completely
 * erase them. This could be introduced to Classic Mac OS ports too,
 * but since I've never heard any complaints and I don't like to
 * make 68K ports even slower, I won't do so there.
 */
#define CLIP_HACK /* */

/*
 * To cope with pref file related problems.  It no longer has to be acculate,
 * because preferences are stored in plist.
 */
#define PREF_VER_MAJOR VERSION_MAJOR
#define PREF_VER_MINOR VERSION_MINOR
#define PREF_VER_PATCH VERSION_PATCH
#define PREF_VER_EXTRA VERSION_EXTRA


/*
 * In OS X + gcc, use <Carbon/Carbon.h>, <CoreServices/CoreServices.h> and
 * <CoreFoundation/CoreFoundation.h> for ALL of these, including the Apple
 * Event ones.  <QuickTime/QuickTime.h> is used by the tile loading code.
 */
#ifdef MACH_O_CARBON

#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#include <CoreServices/CoreServices.h>
#include <CoreFoundation/CoreFoundation.h>

#else /* MACH_O_CARBON */

#include <Types.h>
#include <Gestalt.h>
#include <QuickDraw.h>
#include <Files.h>
#include <Fonts.h>
#include <Menus.h>
#include <Dialogs.h>
#include <Windows.h>
#include <Palettes.h>
#include <ToolUtils.h>
#include <Events.h>
#include <SegLoad.h>
#include <Resources.h>
#include <Controls.h>
#include <Memory.h>
#include <QDOffscreen.h>
#include <Sound.h>
#include <Navigation.h>
#include <CFPreferences.h>
#include <CFNumber.h>
#include <AppleEvents.h>
#include <EPPC.h>
#include <Folders.h>

#endif /* MACH_O_CARBON */

/* MacOSX == Unix == Good */
#ifdef USE_MACOSX
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#endif


/*
 * Use "malloc()" instead of "NewPtr()"
 */
/* #define USE_MALLOC */


/*
 * Information about each of the 256 available colors
 */
static RGBColor color_info[256];


#ifdef MACH_O_CARBON

/*
 * Creator signature and file type - Didn't I say that I abhor file name
 * extentions?  Names and metadata are entirely different set of notions.
 */
OSType _fcreator;
OSType _ftype;

#endif /* MACH_O_CARBON */


/*
 * Forward declare
 */
typedef struct term_data term_data;

/*
 * Extra "term" data
 */
struct term_data
{
	term *t;

	Rect r;

	WindowPtr w;


	short padding;

	short pixelDepth;

	GWorldPtr theGWorld; 	/* not used ... */

	GDHandle theGDH;

	GDHandle mainSWGDH; 	/* not used ... */

	Str15 title;

	s16b oops;

	s16b keys;

	s16b last;

	s16b mapped;

	s16b rows;
	s16b cols;

	s16b font_id;
	s16b font_size;
	s16b font_face;
	s16b font_mono;

	s16b font_o_x;
	s16b font_o_y;
	s16b font_wid;
	s16b font_hgt;

	s16b tile_o_x;
	s16b tile_o_y;
	s16b tile_wid;
	s16b tile_hgt;

	s16b size_wid;
	s16b size_hgt;

	s16b size_ow1;
	s16b size_oh1;
	s16b size_ow2;
	s16b size_oh2;
};




/*
 * Forward declare -- see below
 */
static bool CheckEvents(bool wait);


#ifndef MACH_O_CARBON

/*
 * Hack -- location of the main directory
 */
static short app_vol;
static long app_dir;

#endif /* !MACH_O_CARBON */


/*
 * Delay handling of double-clicked savefiles
 */
Boolean open_when_ready = FALSE;

/*
 * Delay handling of pre-emptive "quit" event
 */
Boolean quit_when_ready = FALSE;


/*
 * Aqua automatically supplies the Quit menu.
 */
static Boolean is_aqua = FALSE;

/*
 * Version of Mac OS - for version specific bug workarounds (; ;)
 */
static long mac_os_version;


/*
 * Hack -- game in progress
 */
static int game_in_progress = 0;


/*
 * Only do "SetPort()" when needed
 */
static WindowPtr active = NULL;


/*
 * Maximum number of terms
 */
#define MAX_TERM_DATA 8


/*
 * An array of term_data's
 */
static term_data data[MAX_TERM_DATA];


/*
 * Note when "open"/"new" become valid
 */
static bool initialized = FALSE;



/*
 * Convert a C string to a pascal string in place
 *
 * This function may be defined elsewhere, but since it is so
 * small, it is not worth finding the proper function name for
 * all the different platforms.
 */
static void ctopstr(StringPtr src)
{
	int i;
	byte len;

	/* Hack -- pointer */
	char *s = (char*)(src);

	len = strlen(s);

	/* Hack -- convert the string */
	for (i = len; i > 1; i--) s[i] = s[i - 1];

	/* Hack -- terminate the string */
	s[0] = len;
}


#ifdef MACH_O_CARBON

/* Carbon File Manager utilities by pelpel */

/*
 * (Carbon)
 * Convert a pathname to a corresponding FSSpec.
 * Returns noErr on success.
 */
static OSErr path_to_spec(const char *path, FSSpec *spec)
{
	OSErr err;
	FSRef ref;

	/* Convert pathname to FSRef ... */
	err = FSPathMakeRef(path, &ref, NULL);
	if (err != noErr) return (err);

	/* ... then FSRef to FSSpec */
	err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);

	/* Inform caller of success or failure */
	return (err);
}


/*
 * (Carbon)
 * Convert a FSSpec to a corresponding pathname.
 * Returns noErr on success.
 */
static OSErr spec_to_path(const FSSpec *spec, char *buf, size_t size)
{
	OSErr err;
	FSRef ref;

	/* Convert FSSpec to FSRef ... */
	err = FSpMakeFSRef(spec, &ref);
	if (err != noErr) return (err);

	/* ... then FSRef to pathname */
	err = FSRefMakePath(&ref, buf, size);

	/* Inform caller of success or failure */
	return (err);
}


/*
 * (Carbon) [via path_to_spec]
 * Set creator and filetype of a file specified by POSIX-style pathname.
 * Returns 0 on success, -1 in case of errors.
 */
int fsetfileinfo(char *pathname, OSType fcreator, OSType ftype)
{
	OSErr err;
	FSSpec spec;
	FInfo info;

	/* Convert pathname to FSSpec */
	if (path_to_spec(pathname, &spec) != noErr) return ( -1);

	/* Obtain current finder info of the file */
	if (FSpGetFInfo(&spec, &info) != noErr) return ( -1);

	/* Overwrite creator and type */
	info.fdCreator = fcreator;
	info.fdType = ftype;
	err = FSpSetFInfo(&spec, &info);

	/* Inform caller of success or failure */
	return ((err == noErr) ? 0 : -1);
}


#else /* MACH_O_CARBON */

/*
* Convert refnum+vrefnum+fname into a full file name
* Store this filename in 'buf' (make sure it is long enough)
* Note that 'fname' looks to be a "pascal" string
*/
static void refnum_to_name(char *buf, long refnum, short vrefnum, char *fname)
{
	DirInfo pb;
	Str255 name;
	int err;
	int i, j;

	char res[1000];

	i = 999;

	res[i] = 0;
	i--;
	for (j = 1; j <= fname[0]; j++)
	{
		res[i - fname[0] + j] = fname[j];
	}
	i -= fname[0];

	pb.ioCompletion = NULL;
	pb.ioNamePtr = name;
	pb.ioVRefNum = vrefnum;
	pb.ioDrParID = refnum;
	pb.ioFDirIndex = -1;

	while (1)
	{
		pb.ioDrDirID = pb.ioDrParID;
		err = PBGetCatInfoSync((CInfoPBPtr) & pb);
		res[i] = ':';
		i--;
		for (j = 1; j <= name[0]; j++)
		{
			res[i - name[0] + j] = name[j];
		}
		i -= name[0];

		if (pb.ioDrDirID == fsRtDirID) break;
	}

	/* Extract the result */
	for (j = 0, i++; res[i]; j++, i++) buf[j] = res[i];
	buf[j] = 0;
}


/*
* Convert a pascal string in place
*
* This function may be defined elsewhere, but since it is so
* small, it is not worth finding the proper function name for
* all the different platforms.
*/
static void ptocstr(StringPtr src)
{
	int i;

	/* Hack -- pointer */
	char *s = (char*)(src);

	/* Hack -- convert the string */
	for (i = s[0]; i; i--, s++) s[0] = s[1];

	/* Hack -- terminate the string */
	s[0] = '\0';
}


/*
* Utility routines by Steve Linberg
*
* The following three routines (pstrcat, pstrinsert, and PathNameFromDirID)
* were taken from the Think Reference section called "Getting a Full Pathname"
* (under the File Manager section).  We need PathNameFromDirID to get the
* full pathname of the opened savefile, making no assumptions about where it
* is.
*
* I had to hack PathNameFromDirID a little for MetroWerks, but it's awfully
* nice.
*/
static void pstrcat(StringPtr dst, StringPtr src)
{
	/* copy string in */
	BlockMove(src + 1, dst + *dst + 1, *src);

	/* adjust length byte */
	*dst += *src;
}


/*
* pstrinsert - insert string 'src' at beginning of string 'dst'
*/
static void pstrinsert(StringPtr dst, StringPtr src)
{
	/* make room for new string */
	BlockMove(dst + 1, dst + *src + 1, *dst);

	/* copy new string in */
	BlockMove(src + 1, dst + 1, *src);

	/* adjust length byte */
	*dst += *src;
}


static void PathNameFromDirID(long dirID, short vRefNum, StringPtr fullPathName)
{
	CInfoPBRec block;
	Str255 directoryName;
	OSErr err;

	fullPathName[0] = '\0';

	block.dirInfo.ioDrParID = dirID;
	block.dirInfo.ioNamePtr = directoryName;

	while (1)
	{
		block.dirInfo.ioVRefNum = vRefNum;
		block.dirInfo.ioFDirIndex = -1;
		block.dirInfo.ioDrDirID = block.dirInfo.ioDrParID;
		err = PBGetCatInfoSync(&block);
		pstrcat(directoryName, (StringPtr)"\p:");
		pstrinsert(fullPathName, directoryName);
		if (block.dirInfo.ioDrDirID == 2) break;
	}
}

#endif /* MACH_O_CARBON */




/*
 * Center a rectangle inside another rectangle
 *
 * Consider using RepositionWindow() whenever possible
 */
static void center_rect(Rect *r, Rect *s)
{
	int centerx = (s->left + s->right) / 2;
	int centery = (2 * s->top + s->bottom) / 3;
	int dx = centerx - (r->right - r->left) / 2 - r->left;
	int dy = centery - (r->bottom - r->top) / 2 - r->top;
	r->left += dx;
	r->right += dx;
	r->top += dy;
	r->bottom += dy;
}


/*
 * Activate a given window, if necessary
 */
static void activate(WindowPtr w)
{
	/* Activate */
	if (active != w)
	{
		/* Activate */
		if (w) SetPort(GetWindowPort(w));

		/* Remember */
		active = w;
	}
}


/*
 * Display a warning message
 */
static void mac_warning(cptr warning)
{
	Str255 text;
	int len, i;

	/* Limit of 250 chars */
	len = strlen(warning);
	if (len > 250) len = 250;

	/* Make a "Pascal" string */
	text[0] = len;
	for (i = 0; i < len; i++) text[i + 1] = warning[i];

	/* Prepare the dialog box values */
	ParamText(text, "\p", "\p", "\p");

	/* Display the Alert, wait for Okay */
	Alert(129, 0L);
}



/*** Some generic functions ***/

/*
 * Hack -- activate a color (0 to 255)
 */
static void term_data_color(term_data *td, int a)
{
	/* Activate the color */
	if (td->last != a)
	{
		/* Activate the color */
		RGBForeColor(&color_info[a]);

		/* Memorize color */
		td->last = a;
	}
}


/*
 * Hack -- Apply and Verify the "font" info
 *
 * This should usually be followed by "term_data_check_size()"
 *
 * XXX XXX To force (re)initialisation of td->tile_wid and td->tile_hgt
 * you have to reset them to zero before this function is called.
 * XXX XXX This is automatic when the program starts because the term_data
 * array is WIPE'd by term_data_hack, but isn't in the other cases, i.e.
 * font, font style and size changes.
 */
static void term_data_check_font(term_data *td)
{
	int i;

	FontInfo info;

	WindowPtr old = active;


	/* Activate */
	activate(td->w);

	/* Instantiate font */
	TextFont(td->font_id);
	TextSize(td->font_size);
	TextFace(td->font_face);

	/* Extract the font info */
	GetFontInfo(&info);

	/* Assume monospaced */
	td->font_mono = TRUE;

	/* Extract the font sizing values XXX XXX XXX */
	td->font_wid = CharWidth('@');  /* info.widMax; */
	td->font_hgt = info.ascent + info.descent;
	td->font_o_x = 0;
	td->font_o_y = info.ascent;

	/* Check important characters */
	for (i = 33; i < 127; i++)
	{
		/* Hack -- notice non-mono-space */
		if (td->font_wid != CharWidth(i)) td->font_mono = FALSE;

		/* Hack -- collect largest width */
		if (td->font_wid < CharWidth(i)) td->font_wid = CharWidth(i);
	}

	/* Set default offsets */
	td->tile_o_x = td->font_o_x;
	td->tile_o_y = td->font_o_y;

	/* Set default tile size */
	if (td->tile_wid == 0) td->tile_wid = td->font_wid;
	if (td->tile_hgt == 0) td->tile_hgt = td->font_hgt;

	/* Re-activate the old window */
	activate(old);
}


/*
 * Hack -- Apply and Verify the "size" info
 */
static void term_data_check_size(term_data *td)
{
	if (td == &data[0])
	{
#ifndef ALLOW_BIG_SCREEN

		/* Forbid resizing of the Angband window */
		td->cols = 80;
		td->rows = 24;

#else

		/* Enforce minimal size */
		if (td->cols < 80) td->cols = 80;
		if (td->rows < 24) td->rows = 24;

#endif /* !ALLOW_BIG_SCREEN */
	}

	/* Information windows can be much smaller */
	else
	{
		if (td->cols < 1) td->cols = 1;
		if (td->rows < 1) td->rows = 1;
	}

	/* Enforce maximal sizes */
	if (td->cols > 255) td->cols = 255;
	if (td->rows > 255) td->rows = 255;

	/* Minimal tile size */
	if (td->tile_wid < td->font_wid) td->tile_wid = td->font_wid;
	if (td->tile_hgt < td->font_hgt) td->tile_hgt = td->font_hgt;

	/* Default tile offsets */
	td->tile_o_x = (td->tile_wid - td->font_wid) / 2;
	td->tile_o_y = (td->tile_hgt - td->font_hgt) / 2;

	/* Minimal tile offsets */
	if (td->tile_o_x < 0) td->tile_o_x = 0;
	if (td->tile_o_y < 0) td->tile_o_y = 0;

	/* Apply font offsets */
	td->tile_o_x += td->font_o_x;
	td->tile_o_y += td->font_o_y;

	/* Calculate full window size */
	td->size_wid = td->cols * td->tile_wid + td->size_ow1 + td->size_ow2;
	td->size_hgt = td->rows * td->tile_hgt + td->size_oh1 + td->size_oh2;

	{
		BitMap tScreen;

		/* Get current screen */
		(void)GetQDGlobalsScreenBits(&tScreen);

		/* Verify the top */
		if (td->r.top > tScreen.bounds.bottom - td->size_hgt)
		{
			td->r.top = tScreen.bounds.bottom - td->size_hgt;
		}

		/* Verify the top */
		if (td->r.top < tScreen.bounds.top + GetMBarHeight())
		{
			td->r.top = tScreen.bounds.top + GetMBarHeight();
		}

		/* Verify the left */
		if (td->r.left > tScreen.bounds.right - td->size_wid)
		{
			td->r.left = tScreen.bounds.right - td->size_wid;
		}

		/* Verify the left */
		if (td->r.left < tScreen.bounds.left)
		{
			td->r.left = tScreen.bounds.left;
		}
	}

	/* Calculate bottom right corner */
	td->r.right = td->r.left + td->size_wid;
	td->r.bottom = td->r.top + td->size_hgt;

	/* Assume no graphics */
	td->t->higher_pict = FALSE;
	td->t->always_pict = FALSE;


	/* Handle graphics */
	if (use_graphics)
	{
		/* Use higher pict whenever possible */
		if (td->font_mono) td->t->higher_pict = TRUE;

		/* Use always_pict only when necessary */
		else td->t->always_pict = TRUE;
	}

	/* Fake mono-space */
	if (!td->font_mono ||
	                (td->font_wid != td->tile_wid) ||
	                (td->font_hgt != td->tile_hgt))
	{
		/*
		 * Handle fake monospace
		 *
		 * pelpel: This is SLOW. Couldn't we use CharExtra
		 * and SpaceExtra for monospaced fonts?
		 */
		if (td->t->higher_pict) td->t->higher_pict = FALSE;
		td->t->always_pict = TRUE;
	}
}


/*
 * Hack -- resize a term_data
 *
 * This should normally be followed by "term_data_redraw()"
 */
static void term_data_resize(term_data *td)
{
	/*
	 * Actually resize the window
	 *
	 * ResizeWindow is the preferred API call, but it cannot
	 * be used here.
	 */
	SizeWindow(td->w, td->size_wid, td->size_hgt, 0);
}



/*
 * Hack -- redraw a term_data
 *
 * Note that "Term_redraw()" calls "TERM_XTRA_CLEAR"
 */
static void term_data_redraw(term_data *td)
{
	term *old = Term;
	Rect tRect;

	/* Activate the term */
	Term_activate(td->t);

	/* Redraw the contents */
	Term_redraw();

	/* Flush the output */
	Term_fresh();

	/* Restore the old term */
	Term_activate(old);

	/* No need to redraw */
	ValidWindowRect(td->w, GetPortBounds(GetWindowPort(td->w), &tRect));
}


/*
 * Graphics support
 */

/* Set by Term_xtra_mac_react */
#ifdef MACH_O_CARBON
static CFStringRef pict_id; 		/* PICT id of image tiles */
#else
static int pict_id; 		/* PICT id of image tiles */
#endif /* MACH_O_CARBON */

static int graf_width; 	/* Width of a tile in pixels */
static int graf_height; 	/* Height of a tile in pixels */

/* Calculated by PICT loading code */
static int pict_cols; 	/* Number of columns in tiles */
static int pict_rows; 	/* Number of rows in tiles */

/* Available graphics modes */
#define GRAF_MODE_NONE	0	/* plain ASCII */
#define GRAF_MODE_8X8	1	/* 8x8 tiles */
#define GRAF_MODE_16X16	2	/* 16x16 tiles */
#define GRAF_MODE_32X32	3	/* 32x32 tiles */

static int graf_mode = GRAF_MODE_NONE; 		/* current graphics mode */
static int graf_mode_req = GRAF_MODE_NONE; 	/* requested graphics mode */

#define TR_NONE	0	/* No transparency */
#define TR_OVER 1	/* Overwriting with transparent black pixels */
static int transparency_mode = TR_NONE; 	/* types of transparency effect */


/*
 * Forward Declare
 */
typedef struct FrameRec FrameRec;

/*
 * Frame
 *
 *	- GWorld for the frame image
 *	- Handle to pix map (saved for unlocking/locking)
 *	- Pointer to color pix map (valid only while locked)
 */
struct FrameRec
{
	GWorldPtr framePort;
	PixMapHandle framePixHndl;
	PixMapPtr framePix;
};


/*
 * The global picture data
 */
static FrameRec *frameP = NULL;


/*
 * Lock a frame
 */
static void BenSWLockFrame(FrameRec *srcFrameP)
{
	PixMapHandle pixMapH;

	pixMapH = GetGWorldPixMap(srcFrameP->framePort);
	(void)LockPixels(pixMapH);
	HLockHi((Handle)pixMapH);
	srcFrameP->framePixHndl = pixMapH;
	srcFrameP->framePix = (PixMapPtr)(*(Handle)pixMapH);
}


/*
 * Unlock a frame
 */
static void BenSWUnlockFrame(FrameRec *srcFrameP)
{
	if (srcFrameP->framePort != NULL)
	{
		HUnlock((Handle)srcFrameP->framePixHndl);
		UnlockPixels(srcFrameP->framePixHndl);
	}

	srcFrameP->framePix = NULL;
}



#ifdef MACH_O_CARBON

/* Moving graphics resources into data fork -- pelpel */

/*
 * (Carbon, Bundle)
 * Given base and type names of a resource, find a file in the
 * current application bundle and return its FSSpec in the third argument.
 * Returns true on success, false otherwise.
 * e.g. get_resource_spec(CFSTR("8x8"), CFSTR("png"), &spec);
 */
static Boolean get_resource_spec(
        CFStringRef base_name, CFStringRef type_name, FSSpec *spec)
{
	CFURLRef res_url;
	FSRef ref;

	/* Find the tile resource specified in the current bundle */
	res_url = CFBundleCopyResourceURL(
	                  CFBundleGetMainBundle(), base_name, type_name, NULL);

	/* Oops */
	if (res_url == NULL) return (false);

	/* Convert CFURL to FSRef */
	(void)CFURLGetFSRef(res_url, &ref);

	/* Convert FSRef to FSSpec */
	(void)FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);

	/* Free allocated CF data */
	CFRelease(res_url);

	/* Success */
	return (true);
}


/*
 * (QuickTime)
 * Create a off-screen GWorld from contents of a file specified by a FSSpec.
 *
 * Globals referenced: data[0], graf_height, graf_width
 * Globals updated: pict_rows, pict_cols.
 */
static OSErr create_gworld_from_spec(
        GWorldPtr *tile_gw, FSSpec *tile_spec)
{
	OSErr err;
	GraphicsImportComponent gi;
	GWorldPtr gw, tmp_gw;
	GDHandle gdh, tmp_gdh;
	Rect r;
	SInt16 depth;

	/* See if QuickTime understands the file format */
	err = GetGraphicsImporterForFile(tile_spec, &gi);

	/* Oops */
	if (err != noErr) return (err);

	/* Get depth */
	depth = data[0].pixelDepth;

	/* Get GDH */
	gdh = data[0].theGDH;

	/* Retrieve the rect of the image */
	err = GraphicsImportGetNaturalBounds(gi, &r);

	/* Adjust it, so that the upper left corner becomes (0, 0) */
	OffsetRect(&r, -r.left, -r.top);

	/* Calculate and set numbers of rows and columns */
	pict_rows = r.bottom / graf_height;
	pict_cols = r.right / graf_width;

	/* Create a GWorld */
	err = NewGWorld(&gw, depth, &r, NULL, gdh, noNewDevice);

	/* Oops */
	if (err != noErr) return (err);

	/* Save the pointer to the GWorld */
	*tile_gw = gw;

	/* Save the current GWorld */
	GetGWorld(&tmp_gw, &tmp_gdh);

	/* Activate the newly created GWorld */
	(void)GraphicsImportSetGWorld(gi, gw, NULL);

	/* Prevent pixmap from moving while drawing */
	(void)LockPixels(GetGWorldPixMap(gw));

	/* Clear the pixels */
	EraseRect(&r);

	/* Draw the image into it */
	(void)GraphicsImportDraw(gi);

	/* Release the lock*/
	UnlockPixels(GetGWorldPixMap(gw));

	/* Restore GWorld */
	SetGWorld(tmp_gw, tmp_gdh);

	/* Close the image importer */
	CloseComponent(gi);

	/* Success */
	return (noErr);
}

#else /* MACH_O_CARBON */

static OSErr BenSWCreateGWorldFromPict(
        GWorldPtr *pictGWorld, PicHandle pictH)
{
	OSErr err;
	GWorldPtr saveGWorld;
	GDHandle saveGDevice;
	GWorldPtr tempGWorld;
	Rect pictRect;
	short depth;
	GDHandle theGDH;

	tempGWorld = NULL;

	/* Reset */
	*pictGWorld = NULL;

	/* Get depth */
	depth = data[0].pixelDepth;

	/* Get GDH */
	theGDH = data[0].theGDH;

	/* Obtain size rectangle */
	pictRect = (**pictH).picFrame;
	OffsetRect(&pictRect, -pictRect.left, -pictRect.top);

	/* Calculate and set numbers of rows and columns */
	pict_rows = pictRect.bottom / graf_height;
	pict_cols = pictRect.right / graf_width;

	/* Create a GWorld */
	err = NewGWorld(&tempGWorld, depth, &pictRect, nil, theGDH, noNewDevice);

	/* Oops */
	if (err != noErr) return (err);

	/* Save pointer */
	*pictGWorld = tempGWorld;

	/* Save GWorld */
	GetGWorld(&saveGWorld, &saveGDevice);

	/* Activate */
	SetGWorld(tempGWorld, nil);

	/* Dump the pict into the GWorld */
	(void)LockPixels(GetGWorldPixMap(tempGWorld));
	EraseRect(&pictRect);
	DrawPicture(pictH, &pictRect);
	UnlockPixels(GetGWorldPixMap(tempGWorld));

	/* Restore GWorld */
	SetGWorld(saveGWorld, saveGDevice);

	/* Success */
	return (0);
}

#endif /* MACH_O_CARBON */


/*
 * Init the global "frameP"
 */
static errr globe_init(void)
{
	OSErr err;

	GWorldPtr tempPictGWorldP;

#ifdef MACH_O_CARBON
	FSSpec pict_spec;
#else
	PicHandle newPictH;
#endif /* MACH_O_CARBON */


	/* Use window XXX XXX XXX */
	SetPort(GetWindowPort(data[0].w));


#ifdef MACH_O_CARBON

	/* Get the tile resources */
	if (!get_resource_spec(pict_id, CFSTR("png"), &pict_spec)) return ( -1);

	/* Create GWorld */
	err = create_gworld_from_spec(&tempPictGWorldP, &pict_spec);

#else /* MACH_O_CARBON */

	/* Get the pict resource */
	if ((newPictH = GetPicture(pict_id)) == 0) return ( -1);

	/* Create GWorld */
	err = BenSWCreateGWorldFromPict(&tempPictGWorldP, newPictH);

	/* Release resource */
	ReleaseResource((Handle)newPictH);

#endif /* MACH_O_CARBON */

	/* Error */
	if (err != noErr) return (err);

	/* Create the frame */
	frameP = (FrameRec*)NewPtrClear((Size)sizeof(FrameRec));

	/* Analyze result */
	if (frameP == NULL) return ( -1);

	/* Save GWorld */
	frameP->framePort = tempPictGWorldP;

	/* Lock it */
	BenSWLockFrame(frameP);

	/* Success */
	return (noErr);
}


/*
 * Nuke the global "frameP"
 */
static errr globe_nuke(void)
{
	/* Dispose */
	if (frameP)
	{
		/* Unlock */
		BenSWUnlockFrame(frameP);

		/* Dispose of the GWorld */
		DisposeGWorld(frameP->framePort);

		/* Dispose of the memory */
		DisposePtr((Ptr)frameP);

		/* Forget */
		frameP = NULL;
	}

	/* Flush events */
	FlushEvents(everyEvent, 0);

	/* Success */
	return (0);
}


#ifdef USE_ASYNC_SOUND

/*
 * Asynchronous sound player - completely revised (beta)
 */
#if defined(USE_QT_SOUND) && !defined(MACH_O_CARBON)
# undef USE_QT_SOUND
#endif /* USE_QT_SOUND && !MACH_O_CARBON */

/*
 * How many sound channels will be pooled
 *
 * Was: 20, but I don't think we need 20 sound effects playing
 * simultaneously :) -- pelpel
 */
#define MAX_CHANNELS		8

/*
 * A pool of sound channels
 */
static SndChannelPtr channels[MAX_CHANNELS];

/*
 * Status of the channel pool
 */
static Boolean channel_initialised = FALSE;

/*
 * Data handles containing sound samples
 */
static SndListHandle samples[SOUND_MAX];

/*
 * Reference counts of sound samples
 */
static SInt16 sample_refs[SOUND_MAX];

#define SOUND_VOLUME_MIN	0	/* Default minimum sound volume */
#define SOUND_VOLUME_MAX	255	/* Default maximum sound volume */
#define VOLUME_MIN			0	/* Minimum sound volume in % */
#define VOLUME_MAX			100	/* Maximum sound volume in % */
#define VOLUME_INC			5	/* Increment sound volume in % */

/*
 * I'm just too lazy to write a panel for this XXX XXX
 */
static int sound_volume = SOUND_VOLUME_MAX;


#ifdef USE_QT_SOUND

/*
 * QuickTime sound, by Ron Anderson
 *
 * I didn't choose to use Windows-style .ini files (Ron wrote a parser
 * for it, but...), nor did I use lib/xtra directory, hoping someone
 * would code plist-based configuration code in the future -- pelpel
 */

/*
 * (QuickTime)
 * Load sound effects from data-fork resources.  They are wav files
 * with the same names as angband_sound_name[] (variable.c)
 *
 * Globals referenced: angband_sound_name[]
 * Globals updated: samples[] (they can be *huge*)
 */
static void load_sounds(void)
{
	OSErr err;
	int i;

	/* Start QuickTime */
	err = EnterMovies();

	/* Error */
	if (err != noErr) return;

	/*
	 * This loop may take a while depending on the count and size of samples
	 * to load.
	 *
	 * We should use a progress dialog for this.
	 */
	for (i = 1; i < SOUND_MAX; i++)
	{
		/* Apple APIs always give me headacke :( */
		CFStringRef name;
		FSSpec spec;
		SInt16 file_id;
		SInt16 res_id;
		Str255 movie_name;
		Movie movie;
		Track track;
		Handle h;
		Boolean res;

		/* Allocate CFString with the name of sound event to be processed */
		name = CFStringCreateWithCString(NULL, angband_sound_name[i],
		                                 kTextEncodingUS_ASCII);

		/* Error */
		if (name == NULL) continue;

		/* Find sound sample resource with the same name */
		res = get_resource_spec(name, CFSTR("wav"), &spec);

		/* Free the reference to CFString */
		CFRelease(name);

		/* Error */
		if (!res) continue;

		/* Open the sound file */
		err = OpenMovieFile(&spec, &file_id, fsRdPerm);

		/* Error */
		if (err != noErr) continue;

		/* Create Movie from the file */
		err = NewMovieFromFile(&movie, file_id, &res_id, movie_name,
		                       newMovieActive, NULL);

		/* Error */
		if (err != noErr) goto close_file;

		/* Get the first track of the movie */
		track = GetMovieIndTrackType(movie, 1, AudioMediaCharacteristic,
		                             movieTrackCharacteristic | movieTrackEnabledOnly );

		/* Error */
		if (track == NULL) goto close_movie;

		/* Allocate a handle to store sample */
		h = NewHandle(0);

		/* Error */
		if (h == NULL) goto close_track;

		/* Dump the sample into the handle */
		err = PutMovieIntoTypedHandle(movie, track, soundListRsrc, h, 0,
		                              GetTrackDuration(track), 0L, NULL);

		/* Success */
		if (err == noErr)
		{
			/* Store the handle in the sample list */
			samples[i] = (SndListHandle)h;
		}

		/* Failure */
		else
		{
			/* Free unused handle */
			DisposeHandle(h);
		}

		/* Free the track */
close_track:
		DisposeMovieTrack(track);

		/* Free the movie */
close_movie:
		DisposeMovie(movie);

		/* Close the movie file */
close_file:
		CloseMovieFile(file_id);
	}

	/* Stop QuickTime */
	ExitMovies();
}

#else /* USE_QT_SOUND */

/*
* Return a handle of 'snd ' resource given Angband sound event number,
* or NULL if it isn't found.
*
* Globals referenced: angband_sound_name[] (variable.c)
*/
static SndListHandle find_sound(int num)
{
Str255 sound;

/* Get the proper sound name */
strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[num]);
sound[0] = strlen((char*)sound + 1);

/* Obtain resource XXX XXX XXX */
return ((SndListHandle)GetNamedResource('snd ', sound));
}

#endif /* USE_QT_SOUND */


/*
 * Clean up sound support - to be called when the game exits.
 *
 * Globals referenced: channels[], samples[], sample_refs[].
 */
static void cleanup_sound(void)
{
	int i;

	/* No need to clean it up */
	if (!channel_initialised) return;

	/* Dispose channels */
	for (i = 0; i < MAX_CHANNELS; i++)
	{
		/* Drain sound commands and free the channel */
		SndDisposeChannel(channels[i], TRUE);
	}

	/* Free sound data */
	for (i = 1; i < SOUND_MAX; i++)
	{
		/* Still locked */
		if ((sample_refs[i] > 0) && (samples[i] != NULL))
		{
			/* Unlock it */
			HUnlock((Handle)samples[i]);
		}

#ifndef USE_QT_SOUND

		/* Release it */
		if (samples[i]) ReleaseResource((Handle)samples[i]);

#else
/* Free handle */
		if (samples[i]) DisposeHandle((Handle)samples[i]);

#endif /* !USE_QT_SOUND */
	}
}


/*
 * Play sound effects asynchronously -- pelpel
 *
 * I don't believe those who first started using the previous implementations
 * imagined this is *much* more complicated as it may seem.  Anyway,
 * introduced round-robin scheduling of channels and made it much more
 * paranoid about HLock/HUnlock.
 *
 * XXX XXX de-refcounting, HUnlock and ReleaseResource should be done
 * using channel's callback procedures, which set global flags, and
 * a procedure hooked into CheckEvents does housekeeping.  On the other
 * hand, this lazy reclaiming strategy keeps things simple (no interrupt
 * time code) and provides a sort of cache for sound data.
 *
 * Globals referenced: channel_initialised, channels[], samples[],
 *   sample_refs[].
 * Globals updated: channel_initialised, channels[], sample_refs[].
 *   Only in !USE_QT_SOUND, samples[].
 */
static void play_sound(int num, int vol)
{
	OSErr err;
	int i;
	int prev_num;
	SndListHandle h;
	SndChannelPtr chan;
	SCStatus status;

	static int next_chan;
	static SInt16 channel_occupants[MAX_CHANNELS];
	static SndCommand volume_cmd, quiet_cmd;


	/* Initialise sound channels */
	if (!channel_initialised)
	{
		for (i = 0; i < MAX_CHANNELS; i++)
		{
			/* Paranoia - Clear occupant table */
			/* channel_occupants[i] = 0; */

			/* Create sound channel for all sounds to play from */
			err = SndNewChannel(&channels[i], sampledSynth, initMono, NULL);

			/* Error */
			if (err != noErr)
			{
				/* Free channels */
				while (--i >= 0)
				{
					SndDisposeChannel(channels[i], TRUE);
				}

				/* Notify error */
				plog("Cannot initialise sound channels!");

				/* Cancel request */
				use_sound = arg_sound = FALSE;

				/* Failure */
				return;
			}
		}

		/* First channel to use */
		next_chan = 0;

		/* Prepare volume command */
		volume_cmd.cmd = volumeCmd;
		volume_cmd.param1 = 0;
		volume_cmd.param2 = 0;

		/* Prepare quiet command */
		quiet_cmd.cmd = quietCmd;
		quiet_cmd.param1 = 0;
		quiet_cmd.param2 = 0;

		/* Initialisation complete */
		channel_initialised = TRUE;
	}

	/* Paranoia */
	if ((num <= 0) || (num >= SOUND_MAX)) return;

	/* Prepare volume command */
	volume_cmd.param2 = (SInt16)((vol << 4) | vol);

	/* Channel to use (round robin) */
	chan = channels[next_chan];

	/* See if the resource is already in use */
	if (sample_refs[num] > 0)
	{
		/* Resource in use */
		h = samples[num];

		/* Increase the refcount */
		sample_refs[num]++;
	}

	/* Sound is not currently in use */
	else
	{
		/* Get handle for the sound */
#ifdef USE_QT_SOUND
		h = samples[num];
#else
		h = find_sound(num);
#endif /* USE_QT_SOUND */

		/* Sample not available */
		if (h == NULL) return;

#ifndef USE_QT_SOUND

		/* Load resource */
		LoadResource((Handle)h);

		/* Remember it */
		samples[num] = h;

#endif /* !USE_QT_SOUND */

		/* Lock the handle */
		HLock((Handle)h);

		/* Initialise refcount */
		sample_refs[num] = 1;
	}

	/* Poll the channel */
	err = SndChannelStatus(chan, sizeof(SCStatus), &status);

	/* It isn't available */
	if ((err != noErr) || status.scChannelBusy)
	{
		/* Shut it down */
		SndDoImmediate(chan, &quiet_cmd);
	}

	/* Previously played sound on this channel */
	prev_num = channel_occupants[next_chan];

	/* Process previously played sound */
	if (prev_num != 0)
	{
		/* Decrease refcount */
		sample_refs[prev_num]--;

		/* We can free it now */
		if (sample_refs[prev_num] <= 0)
		{
			/* Unlock */
			HUnlock((Handle)samples[prev_num]);

#ifndef USE_QT_SOUND

			/* Release */
			ReleaseResource((Handle)samples[prev_num]);

			/* Forget handle */
			samples[prev_num] = NULL;

#endif /* !USE_QT_SOUND */

			/* Paranoia */
			sample_refs[prev_num] = 0;
		}
	}

	/* Remember this sound as the current occupant of the channel */
	channel_occupants[next_chan] = num;

	/* Set up volume for channel */
	SndDoImmediate(chan, &volume_cmd);

	/* Play new sound asynchronously */
	SndPlay(chan, h, TRUE);

	/* Schedule next channel (round robin) */
	next_chan++;
	if (next_chan >= MAX_CHANNELS) next_chan = 0;
}

#endif /* USE_ASYNC_SOUND */




/*** Support for the "z-term.c" package ***/


/*
 * Initialize a new Term
 *
 * Note also the "window type" called "noGrowDocProc", which might be more
 * appropriate for the main "screen" window.
 *
 * Note the use of "srcCopy" mode for optimized screen writes.
 */
static void Term_init_mac(term *t)
{
	term_data *td = (term_data*)(t->data);
	WindowAttributes wattrs;
	OSStatus err;

	static RGBColor black = {0x0000, 0x0000, 0x0000};
	static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};

#ifndef ALLOW_BIG_SCREEN

	/* Every window has close and collapse boxes */
	wattrs = kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute;

	/* Information windows are resizable */
	if (td != &data[0]) wattrs |= kWindowResizableAttribute;

#else

	/* Big screen - every window has close, collapse and resize boxes */
	wattrs = kWindowCloseBoxAttribute |
	         kWindowCollapseBoxAttribute |
	         kWindowResizableAttribute;

#endif /* !ALLOW_BIG_SCREEN */

	/* Make the window  */
	err = CreateNewWindow(
	              kDocumentWindowClass,
	              wattrs,
	              &td->r,
	              &td->w);

	/*
	 * XXX XXX Although the original main-mac.c doesn't perform error
	 * checking, it should be done here.
	 */

	/* Set window title */
	SetWTitle(td->w, td->title);

	/* Activate the window */
	activate(td->w);

	/* Erase behind words */
	TextMode(srcCopy);

	/* Apply and Verify */
	term_data_check_font(td);
	term_data_check_size(td);

	/* Resize the window */
	term_data_resize(td);


	/* Prepare the colors (real colors) */
	RGBBackColor(&black);
	RGBForeColor(&white);

	/* Block */
	{
		Rect globalRect;
		GDHandle mainGDH;
		GDHandle currentGDH;
		GWorldPtr windowGWorld;
		PixMapHandle basePixMap;

		/* Obtain the global rect */
		GetWindowBounds((WindowRef)td->w, kWindowContentRgn, &globalRect);

		/* Obtain the proper GDH */
		mainGDH = GetMaxDevice(&globalRect);

		/* Extract GWorld and GDH */
		GetGWorld(&windowGWorld, &currentGDH);

		/* Obtain base pixmap */
		basePixMap = (**mainGDH).gdPMap;

		/* Save pixel depth */
		td->pixelDepth = (**basePixMap).pixelSize;

		/* Save Window GWorld - unused */
		td->theGWorld = windowGWorld;

		/* Save Window GDH */
		td->theGDH = currentGDH;

		/* Save main GDH - unused */
		td->mainSWGDH = mainGDH;
	}

	{
		Rect portRect;

		/* Get current Rect */
		GetPortBounds(GetWindowPort(td->w), &portRect);

		/* Clip to the window */
		ClipRect(&portRect);

		/* Erase the window */
		EraseRect(&portRect);

		/* Invalidate the window */
		InvalWindowRect(td->w, &portRect);
	}

	/*
	 * A certain release of OS X fails to display windows at proper
	 * locations (_ _#)
	 */
	if ((mac_os_version >= 0x1000) && (mac_os_version < 0x1010))
	{
		/* Hack - Make sure the window is displayed at (r.left,r.top) */
		MoveWindow(td->w, td->r.left, td->r.top, 1);
	}

	/* Display the window if needed */
	if (td->mapped)
	{
		TransitionWindow(td->w,
		                 kWindowZoomTransitionEffect, kWindowShowTransitionAction, NULL);
	}

	/* Hack -- set "mapped" flag */
	t->mapped_flag = td->mapped;

	/* Forget color */
	td->last = -1;
}



/*
 * Nuke an old Term
 */
static void Term_nuke_mac(term *t)
{
	/* XXX */
}



/*
 * Unused
 */
static errr Term_user_mac(int n)
{
	/* Success */
	return (0);
}



/*
 * React to changes
 */
static errr Term_xtra_mac_react(void)
{
	term_data *td = (term_data*)(Term->data);

	int i;


	/* Reset color */
	td->last = -1;

	/* Update colors */
	for (i = 0; i < 256; i++)
	{
		u16b rv, gv, bv;

		/* Extract the R,G,B data */
		rv = angband_color_table[i][1];
		gv = angband_color_table[i][2];
		bv = angband_color_table[i][3];

		/* Save the actual color */
		color_info[i].red = (rv | (rv << 8));
		color_info[i].green = (gv | (gv << 8));
		color_info[i].blue = (bv | (bv << 8));
	}


	/* Handle sound */
	if (use_sound != arg_sound)
	{
		/* Apply request */
		use_sound = arg_sound;
	}


	/* Handle graphics */
	if (graf_mode_req != graf_mode)
	{
		/* dispose old GWorld's if present */
		globe_nuke();

		/*
		 * Setup parameters according to request
		 *
		 * In [Z], you have to set use_graphics and arg_graphics to
		 * GRAPHICS_NONE, GRAPHICS_ORIGINAL or GRAPHICS_ADAM_BOLT, and
		 * comment ANGBAND_GRAF out.
		 */
		switch (graf_mode_req)
		{
			/* ASCII - no graphics whatsoever */
		case GRAF_MODE_NONE:
			{
				use_graphics = arg_graphics = FALSE;
				transparency_mode = TR_NONE;
				break;
			}

			/*
			 * 8x8 tiles (PICT id 1001)
			 * no transparency effect
			 * "old" graphics definitions
			 */
		case GRAF_MODE_8X8:
			{
				use_graphics = arg_graphics = TRUE;
				ANGBAND_GRAF = "old";
				transparency_mode = TR_NONE;
#ifdef MACH_O_CARBON
				pict_id = CFSTR("8x8");
#else
				pict_id = 1001;
#endif /* MACH_O_CARBON */
				graf_width = graf_height = 8;
				break;
			}

			/*
			 * 16x16 tiles (images: PICT id 1002, masks: PICT id 1003)
			 * with transparency effect
			 * "new" graphics definitions
			 */
		case GRAF_MODE_16X16:
			{
				use_graphics = arg_graphics = TRUE;
				ANGBAND_GRAF = "new";
				transparency_mode = TR_OVER;
#ifdef MACH_O_CARBON
				pict_id = CFSTR("16x16");
#else
				pict_id = 1002;
#endif /* MACH_O_CARBON */
				graf_width = graf_height = 16;
				break;
			}

			/*
			 * 32x32 tiles (images: PICT id 1004)
			 * with transparency effect
			 * "david" graphics definitions
			 * Vanilla-specific
			 */
		case GRAF_MODE_32X32:
			{
				use_graphics = arg_graphics = TRUE;
				ANGBAND_GRAF = "david";
				transparency_mode = TR_OVER;
#ifdef MACH_O_CARBON
				pict_id = CFSTR("32x32");
#else
				pict_id = 1004;
#endif /* MACH_O_CARBON */
				graf_width = graf_height = 32;
				break;
			}
		}

		/* load tiles and setup GWorlds if tiles are requested */
		if ((graf_mode_req != GRAF_MODE_NONE) && (globe_init() != 0))
		{
			/* Oops */
			plog("Cannot initialize graphics!");

			/* reject request */
			graf_mode_req = GRAF_MODE_NONE;

			/* reset graphics flags */
			use_graphics = arg_graphics = FALSE;

			/* reset transparency mode */
			transparency_mode = TR_NONE;
		}

		/* update current graphics mode */
		graf_mode = graf_mode_req;

		/* Apply and Verify */
		term_data_check_size(td);

		/* Resize the window */
		term_data_resize(td);

		/* Reset visuals */
#ifndef ANG281_RESET_VISUALS
		reset_visuals(TRUE);
#else
		reset_visuals();
#endif /* !ANG281_RESET_VISUALS */
	}

	/* Success */
	return (0);
}


/*
 * Do a "special thing"
 */
static errr Term_xtra_mac(int n, int v)
{
	term_data *td = (term_data*)(Term->data);

	Rect r;

	/* Analyze */
	switch (n)
	{
		/* Make a noise */
	case TERM_XTRA_NOISE:
		{
			/* Make a noise */
			SysBeep(1);

			/* Success */
			return (0);
		}

		/* Make a sound */
	case TERM_XTRA_SOUND:
		{
#ifndef USE_ASYNC_SOUND

			/*
			 * This may not be your choice, but much safer and much less
			 * resource hungry.  Existing implementations can quite easily
			 * crash, by starting asynchronous playing and immediately
			 * unlocking and releasing the sound data just started playing...
			 * -- pelpel
			 */
			Handle handle;
			Str255 sound;

			/* Get the proper sound name */
			strnfmt((char*)sound + 1, 255, "%.16s.wav", angband_sound_name[v]);
			sound[0] = strlen((char*)sound + 1);

			/* Obtain resource XXX XXX XXX */
			handle = GetNamedResource('snd ', sound);

			/* Oops -- it is a failure, but we return 0 anyway */
			if (handle == NULL) return (0);

			/* Load and Lock */
			LoadResource(handle);
			HLock(handle);

			/* Play sound (wait for completion) */
			SndPlay(NULL, (SndListHandle)handle, FALSE);

			/* Unlock and release */
			HUnlock(handle);
			ReleaseResource(handle);

#else /* !USE_ASYNC_SOUND */

			/* Play sound */
			play_sound(v, sound_volume);

#endif /* !USE_ASYNC_SOUND */

			/* Success */
			return (0);
		}

		/* Process random events */
	case TERM_XTRA_BORED:
		{
			/* Process an event */
			(void)CheckEvents(FALSE);

			/* Success */
			return (0);
		}

		/* Process pending events */
	case TERM_XTRA_EVENT:
		{
			/* Process an event */
			(void)CheckEvents(v);

			/* Success */
			return (0);
		}

		/* Flush all pending events (if any) */
	case TERM_XTRA_FLUSH:
		{
			/* Hack -- flush all events */
			while (CheckEvents(FALSE)) /* loop */;

			/* Success */
			return (0);
		}

		/* Hack -- Change the "soft level" */
	case TERM_XTRA_LEVEL:
		{
			/* Activate if requested */
			if (v) activate(td->w);

			/* Success */
			return (0);
		}

		/* Clear the screen */
	case TERM_XTRA_CLEAR:
		{
			Rect portRect;

			/* Get current Rect */
			GetPortBounds(GetWindowPort(td->w), &portRect);

			/* No clipping XXX XXX XXX */
			ClipRect(&portRect);

			/* Erase the window */
			EraseRect(&portRect);

			/* Set the color */
			term_data_color(td, TERM_WHITE);

			/* Frame the window in white */
			MoveTo(0, 0);
			LineTo(0, td->size_hgt - 1);
			LineTo(td->size_wid - 1, td->size_hgt - 1);
			LineTo(td->size_wid - 1, 0);

			/* Clip to the new size */
			r.left = portRect.left + td->size_ow1;
			r.top = portRect.top + td->size_oh1;
			r.right = portRect.right - td->size_ow2;
			r.bottom = portRect.bottom - td->size_oh2;
			ClipRect(&r);

			/* Success */
			return (0);
		}

		/* React to changes */
	case TERM_XTRA_REACT:
		{
			/* React to changes */
			return (Term_xtra_mac_react());
		}

		/* Delay (milliseconds) */
	case TERM_XTRA_DELAY:
		{
			/*
			 * WaitNextEvent relinquishes CPU as well as
			 * induces a screen refresh on OS X
			 */

			/* If needed */
			if (v > 0)
			{
				EventRecord tmp;
				UInt32 ticks;

				/* Convert millisecs to ticks */
				ticks = (v * 60L) / 1000;

				/*
				 * Hack? - Put the programme into sleep.
				 * No events match ~everyEvent, so nothing
				 * should be lost in Angband's event queue.
				 * Even if ticks are 0, it's worth calling for
				 * the above mentioned reasons.
				 */
				WaitNextEvent((EventMask)~everyEvent, &tmp, ticks, nil);
			}

			/* Success */
			return (0);
		}

		/* Rename main window */
	case TERM_XTRA_RENAME_MAIN_WIN:
		{
			char *s = strdup(angband_term_name[0]);

			ctopstr((StringPtr)s);
			SetWTitle(data[0].w, (StringPtr)s);

			free(s);
			return (0);
		}

/* MacOSX == Unix == Good */
#ifdef USE_MACOSX
		/* Get Delay of some milliseconds */
	case TERM_XTRA_GET_DELAY:
		{
			int ret;
			struct timeval tv;

			ret = gettimeofday(&tv, NULL);
			Term_xtra_long = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);

			return ret;
		}

		/* Subdirectory scan */
	case TERM_XTRA_SCANSUBDIR:
		{
			DIR *directory;
			struct dirent *entry;

			scansubdir_max = 0;

			directory = opendir(scansubdir_dir);
			if (!directory)
				return 1;

			while ((entry = readdir(directory)))
			{
				char file[PATH_MAX + NAME_MAX + 2];
				struct stat filedata;

				file[PATH_MAX + NAME_MAX] = 0;
				strncpy(file, scansubdir_dir, PATH_MAX);
				strncat(file, "/", 2);
				strncat(file, entry->d_name, NAME_MAX);
				if (!stat(file, &filedata) && S_ISDIR((filedata.st_mode)))
				{
					string_free(scansubdir_result[scansubdir_max]);
					scansubdir_result[scansubdir_max] = string_make(entry->d_name);
					++scansubdir_max;
				}
			}

			closedir(directory);
			return 0;
		}
#endif
	}

	/* Oops */
	return (1);
}



/*
 * Low level graphics (Assumes valid input).
 * Draw a "cursor" at (x,y), using a "yellow box".
 * We are allowed to use "Term_what()" to determine
 * the current screen contents (for inverting, etc).
 */
static errr Term_curs_mac(int x, int y)
{
	Rect r;

	term_data *td = (term_data*)(Term->data);

	/* Set the color */
	term_data_color(td, TERM_YELLOW);

	/* Frame the grid */
	r.left = x * td->tile_wid + td->size_ow1;
	r.right = r.left + td->tile_wid;
	r.top = y * td->tile_hgt + td->size_oh1;
	r.bottom = r.top + td->tile_hgt;

#ifdef USE_DOUBLE_TILES

	/* Mogami's bigtile patch */

	/* Adjust it if double width tiles are requested */
	if (use_bigtile &&
	                (x + 1 < Term->wid) &&
	                (Term->old->a[y][x + 1] == 255))
	{
		r.right += td->tile_wid;
	}

#endif /* USE_DOUBLE_TILES */

	FrameRect(&r);

	/* Success */
	return (0);
}


/*
 * Low level graphics (Assumes valid input)
 *
 * Erase "n" characters starting at (x,y)
 */
static errr Term_wipe_mac(int x, int y, int n)
{
	Rect r;

	term_data *td = (term_data*)(Term->data);


	/* Erase the block of characters */
	r.left = x * td->tile_wid + td->size_ow1;
	r.right = r.left + n * td->tile_wid;
	r.top = y * td->tile_hgt + td->size_oh1;
	r.bottom = r.top + td->tile_hgt;
	EraseRect(&r);

	/* Success */
	return (0);
}


/*
 * Low level graphics.  Assumes valid input.
 *
 * Draw several ("n") chars, with an attr, at a given location.
 */
static errr Term_text_mac(int x, int y, int n, byte a, const char *cp)
{
	int xp, yp;

#ifdef CLIP_HACK
	Rect r;
#endif /* CLIP_HACK */

	term_data *td = (term_data*)(Term->data);


	/* Set the color */
	term_data_color(td, a);

#ifdef CLIP_HACK
	/* Hack - only draw within the bounding rect */
	r.left = x * td->tile_wid + td->size_ow1;
	r.right = r.left + n * td->tile_wid;
	r.top = y * td->tile_hgt + td->size_oh1;
	r.bottom = r.top + td->tile_hgt;
	ClipRect(&r);

	/* Hack - clear the content of the bounding rect */
	EraseRect(&r);
#endif /* CLIP_HACK */

	/* Starting pixel */
	xp = x * td->tile_wid + td->tile_o_x + td->size_ow1;
	yp = y * td->tile_hgt + td->tile_o_y + td->size_oh1;

	/* Move to the correct location */
	MoveTo(xp, yp);

	/* Draw the character */
	if (n == 1) DrawChar(*cp);

	/* Draw the string */
	else DrawText(cp, 0, n);

#ifdef CLIP_HACK
	/* Obtain current window's rect */
	GetPortBounds(GetWindowPort(td->w), &r);

	/* Clip to the window again */
	ClipRect(&r);
#endif /* CLIP_HACK */

	/* Success */
	return (0);
}


/*
 * Low level graphics (Assumes valid input)
 *
 * Erase "n" characters starting at (x,y)
 */
#ifdef USE_TRANSPARENCY
# ifdef USE_EGO_GRAPHICS
static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
                          const byte *tap, const char *tcp,
                          const byte *eap, const char *ecp)
# else /* USE_EGO_GRAPHICS */
static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp,
                          const byte *tap, const char *tcp)
# endif  /* USE_EGO_GRAPHICS */
#else /* USE_TRANSPARENCY */
static errr Term_pict_mac(int x, int y, int n, const byte *ap, const char *cp)
#endif /* USE_TRANSPARENCY */
{
	int i;
	Rect dst_r;
	GrafPtr port;
	PixMapHandle pixmap_h;

#ifdef CLIP_HACK
	Rect portRect;
#endif /* CLIP_HACK */

	term_data *td = (term_data*)(Term->data);

	static RGBColor black = {0x0000, 0x0000, 0x0000};
	static RGBColor white = {0xFFFF, 0xFFFF, 0xFFFF};


#ifdef CLIP_HACK
	/* Remember current window's rect */
	GetPortBounds(GetWindowPort(td->w), &portRect);
#endif /* CLIP_HACK */

	/* Destination rectangle */
	dst_r.left = x * td->tile_wid + td->size_ow1;
#ifndef USE_DOUBLE_TILES
	dst_r.right = dst_r.left + td->tile_wid;
#endif /* !USE_DOUBLE_TILES */
	dst_r.top = y * td->tile_hgt + td->size_oh1;
	dst_r.bottom = dst_r.top + td->tile_hgt;

	/* Scan the input */
	for (i = 0; i < n; i++)
	{
		bool done = FALSE;

		byte a = *ap++;
		char c = *cp++;

#ifdef USE_TRANSPARENCY
		byte ta = *tap++;
		char tc = *tcp++;
# ifdef USE_EGO_GRAPHICS
		byte ea = *eap++;
		char ec = *ecp++;
		bool has_overlay = (ea && ec);
# endif  /* USE_EGO_GRAPHICS */
#endif


#ifdef USE_DOUBLE_TILES

		/* Hack -- a filler for double-width tile */
		if (use_bigtile && (a == 255))
		{
			/* Advance */
			dst_r.left += td->tile_wid;

			/* Ignore */
			continue;
		}

		/* Prepare right side of rectagle now */
		dst_r.right = dst_r.left + td->tile_wid;

#endif /* USE_DOUBLE_TILES */

		/* Graphics -- if Available and Needed */
		if (use_graphics && ((byte)a & 0x80) && ((byte)c & 0x80))
		{
			int col, row;
			Rect src_r;
#ifdef USE_TRANSPARENCY
			int t_col, t_row;
			Rect terrain_r;
# ifdef USE_EGO_GRAPHICS
			int e_col, e_row;
			Rect ego_r;
# endif  /* USE_EGO_GRAPHICS */
#endif /* USE_TRANSPARENCY */

			/* Row and Col */
			row = ((byte)a & 0x7F) % pict_rows;
			col = ((byte)c & 0x7F) % pict_cols;

			/* Source rectangle */
			src_r.left = col * graf_width;
			src_r.top = row * graf_height;
			src_r.right = src_r.left + graf_width;
			src_r.bottom = src_r.top + graf_height;

#ifdef USE_TRANSPARENCY
			/* Row and Col */
			t_row = ((byte)ta & 0x7F) % pict_rows;
			t_col = ((byte)tc & 0x7F) % pict_cols;

			/* Source rectangle */
			terrain_r.left = t_col * graf_width;
			terrain_r.top = t_row * graf_height;
			terrain_r.right = terrain_r.left + graf_width;
			terrain_r.bottom = terrain_r.top + graf_height;

# ifdef USE_EGO_GRAPHICS

			/* If there's an overlay */
			if (has_overlay)
			{
				/* Row and Col */
				e_row = ((byte)ea & 0x7F) % pict_rows;
				e_col = ((byte)ec & 0x7F) % pict_cols;

				/* Source rectangle */
				ego_r.left = e_col * graf_width;
				ego_r.top = e_row * graf_height;
				ego_r.right = ego_r.left + graf_width;
				ego_r.bottom = ego_r.top + graf_height;
			}

# endif  /* USE_EGO_GRAPHICS */

#endif /* USE_TRANSPARENCY */

			/* Hardwire CopyBits */
			RGBBackColor(&white);
			RGBForeColor(&black);

#ifdef USE_DOUBLE_TILES

			/* Double width tiles */
			if (use_bigtile) dst_r.right += td->tile_wid;

#endif /* USE_DOUBLE_TILES */

			/*
			 * OS X requires locking and unlocking of window port
			 * when we draw directly to its pixmap.
			 * The Lock/Unlock protocol is described in the Carbon
			 * Porting Guide.
			 */

			/* Obtain current window's graphic port */
			port = GetWindowPort(td->w);

			/* Lock pixels, so we can use handle safely */
			LockPortBits(port);

			/* Get Pixmap handle */
			pixmap_h = GetPortPixMap(port);

#ifdef USE_TRANSPARENCY

			/* Transparency effect */
			switch (transparency_mode)
			{
				/* No transparency effects */
			case TR_NONE:
			default:
				{
					/* Draw the picture */
					CopyBits((BitMap*)frameP->framePix,
					         (BitMap*)*pixmap_h,
					         &src_r, &dst_r, srcCopy, NULL);

					break;
				}

				/* Overwriting with transparent black pixels */
			case TR_OVER:
				{
					/* Draw the terrain */
					CopyBits((BitMap*)frameP->framePix,
					         (BitMap*)*pixmap_h,
					         &terrain_r, &dst_r, srcCopy, NULL);

					/* Make black pixels transparent */
					RGBBackColor(&black);

					/* Draw mon/obj if there's one */
					if ((row != t_row) || (col != t_col))
						CopyBits((BitMap*)frameP->framePix,
						         (BitMap*)*pixmap_h,
						         &src_r, &dst_r, transparent, NULL);

# ifdef USE_EGO_GRAPHICS

					/* Draw overlay if there's one */
					if (has_overlay)
					{
						CopyBits((BitMap*)frameP->framePix,
						         (BitMap*)*pixmap_h,
						         &ego_r, &dst_r, transparent, NULL);
					}

# endif  /* USE_EGO_GRAPHICS */

					break;
				}
			}

#else /* USE_TRANSPARENCY */

			/* Draw the picture */
			CopyBits((BitMap*)frameP->framePix,
			         (BitMap*)*pixmap_h,
			         &src_r, &dst_r, srcCopy, NULL);

#endif /* USE_TRANSPARENCY */

			/* Release the lock and dispose the PixMap handle */
			UnlockPortBits(port);

			/* Restore colors */
			RGBBackColor(&black);
			RGBForeColor(&white);

			/* Forget color */
			td->last = -1;

			/* Done */
			done = TRUE;
		}

		/* Normal */
		if (!done)
		{
			int xp, yp;

#ifdef CLIP_HACK
			/* Hack - avoid writing outside of dst_r */
			ClipRect(&dst_r);
			/* Some characters do not match dst_r, therefore we have to... */
#endif /* CLIP_HACK */

			/* Erase */
			EraseRect(&dst_r);

			/* Set the color */
			term_data_color(td, a);

			/* Starting pixel */
			xp = dst_r.left + td->tile_o_x;
			yp = dst_r.top + td->tile_o_y;

			/* Move to the correct location */
			MoveTo(xp, yp);

			/* Draw the character */
			DrawChar(c);

#ifdef CLIP_HACK
			/* Clip to the window - inefficient (; ;) XXX XXX */
			ClipRect(&portRect);
#endif /* CLIP_HACK */
		}

		/* Advance */
		dst_r.left += td->tile_wid;
#ifndef USE_DOUBLE_TILES
		dst_r.right += td->tile_wid;
#endif /* !USE_DOUBLE_TILES */
	}

	/* Success */
	return (0);
}





/*
 * Create and initialize window number "i"
 */
static void term_data_link(int i)
{
	term *old = Term;

	term_data *td = &data[i];

	/* Only once */
	if (td->t) return;

	/* Require mapped */
	if (!td->mapped) return;

	/* Allocate */
	MAKE(td->t, term);

	/* Initialize the term */
	term_init(td->t, td->cols, td->rows, td->keys);

	/* Use a "software" cursor */
	td->t->soft_cursor = TRUE;

	/* Erase with "white space" */
	td->t->attr_blank = TERM_WHITE;
	td->t->char_blank = ' ';

	/* Prepare the init/nuke hooks */
	td->t->init_hook = Term_init_mac;
	td->t->nuke_hook = Term_nuke_mac;

	/* Prepare the function hooks */
	td->t->user_hook = Term_user_mac;
	td->t->xtra_hook = Term_xtra_mac;
	td->t->wipe_hook = Term_wipe_mac;
	td->t->curs_hook = Term_curs_mac;
	td->t->text_hook = Term_text_mac;
	td->t->pict_hook = Term_pict_mac;

#if 0

	/* Doesn't make big difference? */
	td->t->never_bored = TRUE;

#endif

	/* Link the local structure */
	td->t->data = (void *)(td);

	/* Activate it */
	Term_activate(td->t);

	/* Global pointer */
	angband_term[i] = td->t;

	/* Activate old */
	Term_activate(old);
}




#ifdef MACH_O_CARBON

/*
 * (Carbon, Bundle)
 * Return a POSIX pathname of the lib directory, or NULL if it can't be
 * located.  Caller must supply a buffer along with its size in bytes,
 * where returned pathname will be stored.
 * I prefer use of goto's to several nested if's, if they involve error
 * handling.  Sorry if you are offended by their presence.  Modern
 * languages have neater constructs for this kind of jobs -- pelpel
 */
static char *locate_lib(char *buf, size_t size)
{
	CFURLRef main_url = NULL;
	CFStringRef main_str = NULL;
	char *p;
	char *res = NULL;

	/* Obtain the URL of the main bundle */
	main_url = CFBundleCopyBundleURL(CFBundleGetMainBundle());

	/* Oops */
	if (main_url == NULL) goto ret;

	/* Convert it to POSIX pathname */
	main_str = CFURLCopyFileSystemPath(main_url, kCFURLPOSIXPathStyle);

	/* Oops */
	if (main_str == NULL) goto ret;

	/* Convert it again from darn unisomething encoding to ASCII */
	if (CFStringGetCString(main_str, buf, size, kTextEncodingUS_ASCII) == FALSE)
		goto ret;

	/*
	 * Paranoia - bounds check
	 */
	if (strlen(buf) + 25 + 1 > size) goto ret;

	/* Location of the data */
	strcat(buf, "/Contents/Resources/");

	/* Set result */
	res = buf;

ret:

	/* Release objects allocated and implicitly retained by the program */
	if (main_str) CFRelease(main_str);
	if (main_url) CFRelease(main_url);

	/* pathname of the lib folder or NULL */
	return (res);
}


#else /* MACH_O_CARBON */

/*
* Set the "current working directory" (also known as the "default"
* volume/directory) to the location of the current application.
*
* Original code by: Maarten Hazewinkel (mmhazewi@cs.ruu.nl)
*
* Completely rewritten to use Carbon Process Manager.  It retrieves the
* volume and direcotry of the current application and simply stores it
* in the (static) global variables app_vol and app_dir, but doesn't
* mess with the "current working directory", because it has long been
* an obsolete (and arcane!) feature.
*/
static void SetupAppDir(void)
{
	OSErr err;
	ProcessSerialNumber curPSN;
	ProcessInfoRec procInfo;
	FSSpec cwdSpec;

	/* Initialise PSN info for the current process */
	curPSN.highLongOfPSN = 0;
	curPSN.lowLongOfPSN = kCurrentProcess;

	/* Fill in mandatory fields */
	procInfo.processInfoLength = sizeof(ProcessInfoRec);
	procInfo.processName = nil;
	procInfo.processAppSpec = &cwdSpec;

	/* Obtain current process information */
	err = GetProcessInformation(&curPSN, &procInfo);

	/* Oops */
	if (err != noErr)
	{
		mac_warning("Unable to get process information");

		/* Quit without writing anything */
		ExitToShell();
	}

	/* Extract and save the Vol and Dir */
	app_vol = cwdSpec.vRefNum;
	app_dir = cwdSpec.parID;
}

#endif /* MACH_O_CARBON */




/*
 * Using Core Foundation's Preferences services -- pelpel
 *
 * Requires OS 8.6 or greater with CarbonLib 1.1 or greater. Or OS X,
 * of course.
 *
 * Without this, we can support older versions of OS 8 as well
 * (with CarbonLib 1.0.4).
 *
 * Frequent allocation/deallocation of small chunks of data is
 * far from my liking, but since this is only called at the
 * beginning and the end of a session, I hope this hardly matters.
 */


/*
 * Store "value" as the value for preferences item name
 * pointed by key
 */
static void save_pref_short(const char *key, short value)
{
	CFStringRef cf_key;
	CFNumberRef cf_value;

	/* allocate and initialise the key */
	cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);

	/* allocate and initialise the value */
	cf_value = CFNumberCreate(NULL, kCFNumberShortType, &value);

	if ((cf_key != NULL) && (cf_value != NULL))
	{
		/* Store the key-value pair in the applications preferences */
		CFPreferencesSetAppValue(
		        cf_key,
		        cf_value,
		        kCFPreferencesCurrentApplication);
	}

	/*
	 * Free CF data - the reverse order is a vain attempt to
	 * minimise memory fragmentation.
	 */
	if (cf_value) CFRelease(cf_value);
	if (cf_key) CFRelease(cf_key);
}


/*
 * Load preference value for key, returns TRUE if it succeeds with
 * vptr updated appropriately, FALSE otherwise.
 */
static bool query_load_pref_short(const char *key, short *vptr)
{
	CFStringRef cf_key;
	CFNumberRef cf_value;

	/* allocate and initialise the key */
	cf_key = CFStringCreateWithCString(NULL, key, kTextEncodingUS_ASCII);

	/* Oops */
	if (cf_key == NULL) return (FALSE);

	/* Retrieve value for the key */
	cf_value = CFPreferencesCopyAppValue(
	                   cf_key,
	                   kCFPreferencesCurrentApplication);

	/* Value not found */
	if (cf_value == NULL)
	{
		CFRelease(cf_key);
		return (FALSE);
	}

	/* Convert the value to short */
	CFNumberGetValue(
	        cf_value,
	        kCFNumberShortType,
	        vptr);

	/* Free CF data */
	CFRelease(cf_value);
	CFRelease(cf_key);

	/* Success */
	return (TRUE);
}


/*
 * Update short data pointed by vptr only if preferences
 * value for key is located.
 */
static void load_pref_short(const char *key, short *vptr)
{
	short tmp;

	if (query_load_pref_short(key, &tmp)) *vptr = tmp;
	return;
}


/*
 * Save preferences to preferences file for current host+current user+
 * current application.
 */
static void cf_save_prefs()
{
	int i;

	/* Version stamp */
	save_pref_short("version.major", PREF_VER_MAJOR);
	save_pref_short("version.minor", PREF_VER_MINOR);
	save_pref_short("version.patch", PREF_VER_PATCH);
	save_pref_short("version.extra", PREF_VER_EXTRA);

	/* Gfx settings */
	save_pref_short("arg.arg_sound", arg_sound);
	save_pref_short("arg.graf_mode", graf_mode);
#ifdef USE_DOUBLE_TILES
	save_pref_short("arg.big_tile", use_bigtile);
#endif /* USE_DOUBLE_TILES */

	/* Windows */
	for (i = 0; i < MAX_TERM_DATA; i++)
	{
		term_data *td = &data[i];

		save_pref_short(format("term%d.font_mono", i), td->font_mono); 
		save_pref_short(format("term%d.font_o_x", i), td->font_o_x);
		save_pref_short(format("term%d.font_o_y", i), td->font_o_y);
		save_pref_short(format("term%d.font_wid", i), td->font_wid);
		save_pref_short(format("term%d.font_hgt", i), td->font_hgt);
		save_pref_short(format("term%d.tile_o_x", i), td->tile_o_x);
		save_pref_short(format("term%d.tile_o_y", i), td->tile_o_y);
		save_pref_short(format("term%d.right", i), td->r.right);
		save_pref_short(format("term%d.bottom", i), td->r.bottom);
		save_pref_short(format("term%d.ow1", i), td->size_ow1);
		save_pref_short(format("term%d.oh1", i), td->size_oh1);
		save_pref_short(format("term%d.ow2", i), td->size_ow2);
		save_pref_short(format("term%d.oh2", i), td->size_oh2);

		save_pref_short(format("term%d.mapped", i), td->mapped);

		save_pref_short(format("term%d.font_id", i), td->font_id);
		save_pref_short(format("term%d.font_size", i), td->font_size);
		save_pref_short(format("term%d.font_face", i), td->font_face);

		save_pref_short(format("term%d.tile_wid", i), td->tile_wid);
		save_pref_short(format("term%d.tile_hgt", i), td->tile_hgt);

		save_pref_short(format("term%d.cols", i), td->cols);
		save_pref_short(format("term%d.rows", i), td->rows);
		save_pref_short(format("term%d.left", i), td->r.left);
		save_pref_short(format("term%d.top", i), td->r.top);
	}

	/*
	 * Make sure preferences are persistent
	 */
	CFPreferencesAppSynchronize(
	        kCFPreferencesCurrentApplication);
}


/*
 * Load preferences from preferences file for current host+current user+
 * current application.
 */
static void cf_load_prefs()
{
	bool ok;
	short pref_major, pref_minor, pref_patch, pref_extra;
	int i;

	/* Assume nothing is wrong, yet */
	ok = TRUE;

	/* Load version information */
	ok &= query_load_pref_short("version.major", &pref_major);
	ok &= query_load_pref_short("version.minor", &pref_minor);
	ok &= query_load_pref_short("version.patch", &pref_patch);
	ok &= query_load_pref_short("version.extra", &pref_extra);

	/* Any of the above failed */
	if (!ok)
	{
		/* This may be the first run */
		mac_warning("Preferences are not found.");

		/* Ignore the rest */
		return;
	}

#if 0

	/* Check version */
	if ((pref_major != PREF_VER_MAJOR) ||
	                (pref_minor != PREF_VER_MINOR) ||
	                (pref_patch != PREF_VER_PATCH) ||
	                (pref_extra != PREF_VER_EXTRA))
	{
		/* Message */
		mac_warning(
		        format("Ignoring %d.%d.%d.%d preferences.",
		               pref_major, pref_minor, pref_patch, pref_extra));

		/* Ignore */
		return;
	}

#endif

	/* Gfx settings */
	{
		short pref_tmp;

		/* sound */
		if (query_load_pref_short("arg.arg_sound", &pref_tmp))
			arg_sound = pref_tmp;

		/* graphics */
		if (query_load_pref_short("arg.graf_mode", &pref_tmp))
			graf_mode_req = pref_tmp;

#ifdef USE_DOUBLE_TILES

		/* double-width tiles */
		if (query_load_pref_short("arg.big_tile", &pref_tmp))
		{
			use_bigtile = pref_tmp;
		}

#endif /* USE_DOUBLE_TILES */

	}

	/* Windows */
	for (i = 0; i < MAX_TERM_DATA; i++)
	{
		term_data *td = &data[i];

		load_pref_short(format("term%d.mapped", i), &td->mapped);

		load_pref_short(format("term%d.font_id", i), &td->font_id);
		load_pref_short(format("term%d.font_size", i), &td->font_size);
		load_pref_short(format("term%d.font_face", i), &td->font_face);

		load_pref_short(format("term%d.tile_wid", i), &td->tile_wid);
		load_pref_short(format("term%d.tile_hgt", i), &td->tile_hgt);

		load_pref_short(format("term%d.cols", i), &td->cols);
		load_pref_short(format("term%d.rows", i), &td->rows);
		load_pref_short(format("term%d.left", i), &td->r.left);
		load_pref_short(format("term%d.top", i), &td->r.top);

		load_pref_short(format("term%d.font_mono", i), &td->font_mono); 
		load_pref_short(format("term%d.font_o_x", i), &td->font_o_x);
		load_pref_short(format("term%d.font_o_y", i), &td->font_o_y);
		load_pref_short(format("term%d.font_wid", i), &td->font_wid);
		load_pref_short(format("term%d.font_hgt", i), &td->font_hgt);
		load_pref_short(format("term%d.tile_o_x", i), &td->tile_o_x);
		load_pref_short(format("term%d.tile_o_y", i), &td->tile_o_y);
		load_pref_short(format("term%d.right", i), &td->r.right);
		load_pref_short(format("term%d.bottom", i), &td->r.bottom);
		load_pref_short(format("term%d.ow1", i), &td->size_ow1);
		load_pref_short(format("term%d.oh1", i), &td->size_oh1);
		load_pref_short(format("term%d.ow2", i), &td->size_ow2);
		load_pref_short(format("term%d.oh2", i), &td->size_oh2);
	}
}




/*
 * Hack -- default data for a window
 */
static void term_data_hack(term_data *td)
{
	short fid;

	/* Default to Monaco font */
	GetFNum("\pmonaco", &fid);

	/* Wipe it */
	WIPE(td, term_data);

	/* No color */
	td->last = -1;

	/* Default borders */
	td->size_ow1 = 2;
	td->size_ow2 = 2;
	td->size_oh1 = 2;
	td->size_oh2 = 2;

	/* Start hidden */
	td->mapped = FALSE;

	/* Default font */
	td->font_id = fid;

	/* Default font size - was 12 */
	td->font_size = 14;

	/* Default font face */
	td->font_face = 0;

	/* Default size */
	td->rows = 24;
	td->cols = 80;

	/* Default position */
	td->r.left = 10;
	td->r.top = 40;

	/* Minimal keys */
	td->keys = 16;
}


/*
 * Read the preference file, Create the windows.
 *
 * We attempt to use "FindFolder()" to track down the preference file.
 */
static void init_windows(void)
{
	int i, b = 0;

	term_data *td;


	/*** Default values ***/

	/* Initialize (backwards) */
	for (i = MAX_TERM_DATA; i-- > 0; )
	{
		int n;

		cptr s;

		/* Obtain */
		td = &data[i];

		/* Defaults */
		term_data_hack(td);

		/* Obtain title */
		s = angband_term_name[i];

		/* Get length */
		n = strlen(s);

		/* Maximal length */
		if (n > 15) n = 15;

		/* Copy the title */
		strncpy((char*)(td->title) + 1, s, n);

		/* Save the length */
		td->title[0] = n;

		/* Tile the windows */
		td->r.left += (b * 30);
		td->r.top += (b * 30);

		/* Tile */
		b++;
	}


	/*** Load preferences ***/

	cf_load_prefs();


	/*** Instantiate ***/

	/* Main window */
	td = &data[0];

	/* Many keys */
	td->keys = 1024;

	/* Start visible */
	td->mapped = TRUE;

	/* Link (backwards, for stacking order) */
	for (i = MAX_TERM_DATA; i-- > 0; )
	{
		term_data_link(i);
	}

	/* Main window */
	td = &data[0];

	/* Main window */
	Term_activate(td->t);
}


/*
 * Save preferences
 */
static void save_pref_file(void)
{
	cf_save_prefs();
}




#ifndef SAVEFILE_SCREEN

/*
 * Prepare savefile dialogue and set the variable
 * savefile accordingly. Returns true if it succeeds, false (or
 * aborts) otherwise. If all is false, only allow files whose type
 * is 'SAVE'.
 * Originally written by Peter Ammon
 */
static bool select_savefile(bool all)
{
	OSErr err;
	FSSpec theFolderSpec;
	FSSpec savedGameSpec;
	NavDialogOptions dialogOptions;
	NavReplyRecord reply;
	/* Used only when 'all' is true */
	NavTypeList types = {ANGBAND_CREATOR, 1, 1, {'SAVE'}};
	NavTypeListHandle myTypeList;
	AEDesc defaultLocation;

#ifdef MACH_O_CARBON

	/* Find the save folder */
	err = path_to_spec(ANGBAND_DIR_SAVE, &theFolderSpec);

#else

	/* Find :lib:save: folder */
	err = FSMakeFSSpec(
	              app_vol,
	              app_dir,
	              "\p:lib:save:",
	              &theFolderSpec);

#endif

	/* Oops */
	if (err != noErr) quit("Unable to find the folder :lib:save:");

	/* Get default Navigator dialog options */
	err = NavGetDefaultDialogOptions(&dialogOptions);

	/* Clear preview option */
	dialogOptions.dialogOptionFlags &= ~kNavAllowPreviews;

	/* Disable multiple file selection */
	dialogOptions.dialogOptionFlags &= ~kNavAllowMultipleFiles;

	/* Make descriptor for default location */
	err = AECreateDesc(
	              typeFSS,
	              &theFolderSpec,
	              sizeof(FSSpec),
	              &defaultLocation);

	/* Oops */
	if (err != noErr) quit("Unable to allocate descriptor");

	/* We are indifferent to signature and file types */
	if (all)
	{
		myTypeList = (NavTypeListHandle)nil;
	}

	/* Set up type handle */
	else
	{
		err = PtrToHand(&types, (Handle *) & myTypeList, sizeof(NavTypeList));

		/* Oops */
		if (err != noErr) quit("Error in PtrToHand. Try enlarging heap");

	}

	/* Call NavGetFile() with the types list */
	err = NavChooseFile(
	              &defaultLocation,
	              &reply,
	              &dialogOptions,
	              nil,
	              nil,
	              nil,
	              myTypeList,
	              nil);

	/* Free type list */
	DisposeHandle((Handle)myTypeList);

	/* Invalid response -- allow the user to cancel */
	if (!reply.validRecord) return (FALSE);

	/* Retrieve FSSpec from the reply */
	if (err == noErr)
	{
		AEKeyword theKeyword;
		DescType actualType;
		Size actualSize;

		/* Get a pointer to selected file */
		(void)AEGetNthPtr(
		        &reply.selection,
		        1,
		        typeFSS,
		        &theKeyword,
		        &actualType,
		        &savedGameSpec,
		        sizeof(FSSpec),
		        &actualSize);

		/* Dispose NavReplyRecord, resources and descriptors */
		(void)NavDisposeReply(&reply);
	}

	/* Dispose location info */
	AEDisposeDesc(&defaultLocation);

#ifdef MACH_O_CARBON

	/* Convert FSSpec to pathname and store it in variable savefile */
	(void)spec_to_path(&savedGameSpec, savefile, sizeof(savefile));

#else

	/* Convert FSSpec to pathname and store it in variable savefile */
	refnum_to_name(
	        savefile,
	        savedGameSpec.parID,
	        savedGameSpec.vRefNum,
	        (char *)savedGameSpec.name);

#endif

	/* Success */
	return (TRUE);
}


/*
 * Handle menu: "File" + "New"
 */
static void do_menu_file_new(void)
{
	/* Hack */
	HiliteMenu(0);

	/* Game is in progress */
	game_in_progress = 1;

	/* Flush input */
	Term_flush();

	/* Play a game */
	play_game(TRUE);

	/* Hack -- quit */
	quit(NULL);
}


/*
 * Handle menu: "File" + "Open" /  "Import"
 */
static void do_menu_file_open(bool all)
{
	/* Let the player to choose savefile */
	if (!select_savefile(all)) return;

	/* Hack */
	HiliteMenu(0);

	/* Game is in progress */
	game_in_progress = 1;

	/* Flush input */
	flush();

	/* Play a game */
	play_game(FALSE);

	/* Hack -- quit */
	quit(NULL);
}

#endif /* !SAVEFILE_SCREEN */


/*
 * Handle the "open_when_ready" flag
 */
static void handle_open_when_ready(void)
{
	/* Check the flag XXX XXX XXX make a function for this */
	if (open_when_ready && initialized && !game_in_progress)
	{
		/* Forget */
		open_when_ready = FALSE;

		/* Game is in progress */
		game_in_progress = 1;

		/* Wait for it */
		pause_line(23);

		/* Flush input */
		flush();

#ifdef SAVEFILE_SCREEN

		/* User double-clicked savefile; no savefile screen */
		no_begin_screen = TRUE;

#endif /* SAVEFILE_SCREEN */

		/* Play a game */
		play_game(FALSE);

		/* Quit */
		quit(NULL);
	}
}




/*
 * Menus
 *
 * The standard menus are:
 *
 *   Apple (128) =   { About, -, ... }
 *   File (129) =    { New,Open,Import,Close,Save,-,Score,Quit }
 *     (If SAVEFILE_SCREEN is defined, this becomes)
 *   File (129) =    { Close,Save,-,Score,Quit }
 *   Edit (130) =    { Cut, Copy, Paste, Clear }   (?)
 *   Font (131) =    { Bold, Extend, -, Monaco, ..., -, ... }
 *   Size (132) =    { ... }
 *   Window (133) =  { Angband, Term-1/Mirror, Term-2/Recall, Term-3/Choice,
 *                     Term-4, Term-5, Term-6, Term-7 }
 *   Special (134) = { Sound, Graphics, TileWidth, TileHeight, -,
 *                     Fiddle, Wizard }
 */

/* Apple menu */
#define MENU_APPLE	128
#define ITEM_ABOUT	1

/* File menu */
#define MENU_FILE	129
#ifndef SAVEFILE_SCREEN
# define ITEM_NEW	1
# define ITEM_OPEN	2
# define ITEM_IMPORT	3
# define ITEM_CLOSE	4
# define ITEM_SAVE	5
# ifdef HAS_SCORE_MENU
# define ITEM_SCORE 7
# define ITEM_QUIT	8
# else
# define ITEM_QUIT	7
# endif  /* HAS_SCORE_MENU */
#else /* !SAVEFILE_SCREEN - in-game savefile menu */
# define ITEM_CLOSE	1
# define ITEM_SAVE	2
# ifdef HAS_SCORE_MENU
# define ITEM_SCORE 4
# define ITEM_QUIT	5
# else
# define ITEM_QUIT	4
# endif  /* HAS_SCORE_MENU */
#endif /* !SAVEFILE_SCREEN */

/* Edit menu */
#define MENU_EDIT	130
#define ITEM_UNDO	1
#define ITEM_CUT	3
#define ITEM_COPY	4
#define ITEM_PASTE	5
#define ITEM_CLEAR	6

/* Font menu */
#define MENU_FONT	131
#define ITEM_BOLD	1
#define ITEM_WIDE	2

/* Size menu */
#define MENU_SIZE	132

/* Windows menu */
#define MENU_WINDOWS	133

/* Special menu */
#define MENU_SPECIAL	134
#define ITEM_SOUND	1
#define ITEM_GRAPH	2
# define SUBMENU_GRAPH	144
# define ITEM_NONE	1
# define ITEM_8X8	2
# define ITEM_16X16	3
# define ITEM_32X32	4
# define ITEM_BIGTILE 6
#define ITEM_TILEWIDTH 3
# define SUBMENU_TILEWIDTH 145
#define ITEM_TILEHEIGHT 4
# define SUBMENU_TILEHEIGHT 146
#define ITEM_FIDDLE	6
#define ITEM_WIZARD	7


/*
 * I HATE UNICODE!  We've never wanted it.  Some multi-national companies
 * made it up as their internationalisation "solution".  So I won't use
 * any such API's -- pelpel
 */
#define NSIZES 32
static byte menu_size_values[NSIZES];
static byte menu_tilewidth_values[NSIZES];
static byte menu_tileheight_values[NSIZES];

/*
 * Initialize the menus
 *
 * Fixed top level menus are now loaded all at once by GetNewMBar().
 * Although this simplifies the function a bit, we have to make sure
 * that resources have all the expected entries defined XXX XXX
 */
static void init_menubar(void)
{
	int i, n;

	Rect r;

	WindowPtr tmpw;

	MenuRef m;

#ifdef USE_NIB

	/* The new way - loading main menu using Interface Builder services */
	{
		IBNibRef nib;
		OSStatus err;

		/* Create a nib reference to the main nib file */
		err = CreateNibReference(CFSTR("main"), &nib);

		/* Fatal error - missing Main.nib */
		if (err != noErr) quit("Cannot find Main.nib in the bundle!");

		/* Unarchive the menu bar and make it ready to use */
		err = SetMenuBarFromNib(nib, CFSTR("MainMenu"));

		/* Fatal error - couldn't insert menu bar */
		if (err != noErr) quit("Cannot prepare menu bar!");

		/* Dispose of the nib reference because we don't need it any longer */
		DisposeNibReference(nib);
	}

#else /* USE_NIB */

	/* The old way - loading main menu from Resource Manager resource */
	{
		Handle mbar;

		/* Load menubar from resources */
		mbar = GetNewMBar(128);

		/* Whoops! */
		if (mbar == nil) quit("Cannot find menubar('MBAR') id 128!");

		/* Insert them into the current menu list */
		SetMenuBar(mbar);

		/* Free handle */
		DisposeHandle(mbar);
	}

#endif /* USE_NIB */


	/* Apple menu (id 128) - we don't have to do anything */

#ifndef USE_NIB

	/* File menu (id 129) - Aqua provides Quit menu for us */
	if (is_aqua)
	{
		/* Get a handle to the file menu */
		m = GetMenuHandle(MENU_FILE);

		/* Nuke the quit menu since Aqua does that for us */
		DeleteMenuItem(m, ITEM_QUIT);

#ifndef HAS_SCORE_MENU

		/* Hack - because the above leaves a separator as the last item */
		DeleteMenuItem(m, ITEM_QUIT - 1);

#endif /* !HAS_SCORE_MENU */
	}

#endif /* !USE_NIB */


	/* Edit menu (id 130) - we don't have to do anything */


	/*
	 * Font menu (id 131) - append names of mono-spaced fonts
	 * followed by all available ones
	 */
	m = GetMenuHandle(MENU_FONT);

	/* Fake window */
	r.left = r.right = r.top = r.bottom = 0;

	/* Make the fake window so that we can retrieve font info */
	(void)CreateNewWindow(
	        kDocumentWindowClass,
	        kWindowNoAttributes,
	        &r,
	        &tmpw);

	/* Activate the "fake" window */
	SetPort(GetWindowPort(tmpw));

	/* Default mode */
	TextMode(0);

	/* Default size */
	TextSize(12);

	/* Add the fonts to the menu */
	AppendResMenu(m, 'FONT');

	/* Size of menu */
	n = CountMenuItems(m);

	/* Scan the menu */
	for (i = n; i >= 4; i--)
	{
		Str255 tmpName;
		short fontNum;

		/* Acquire the font name */
		GetMenuItemText(m, i, tmpName);

		/* Acquire the font index */
		GetFNum(tmpName, &fontNum);

		/* Apply the font index */
		TextFont(fontNum);

		/* Remove non-mono-spaced fonts */
		if ((CharWidth('i') != CharWidth('W')) || (CharWidth('W') == 0))
		{
			/* Delete the menu item */
			DeleteMenuItem(m, i);
		}
	}

	/* Destroy the fake window */
	DisposeWindow(tmpw);

	/* Add a separator */
	AppendMenu(m, "\p-");

	/* Add the fonts to the menu */
	AppendResMenu(m, 'FONT');


#ifndef USE_NIB

	/* Size menu (id 132) */
	m = GetMenuHandle(MENU_SIZE);

	/* Add some sizes (stagger choices) */
	for (i = 8, n = 1; i <= 32; i += ((i / 16) + 1), n++)
	{
		Str15 buf;

		/* Textual size */
		strnfmt((char*)buf + 1, 15, "%d", i);
		buf[0] = strlen((char*)buf + 1);

		/* Add the item */
		AppendMenu(m, buf);

		/* Remember its value, for we can't be sure it's in ASCII */
		menu_size_values[n] = i;
	}

#endif /* !USE_NIB */


	/* Windows menu (id 133) */
	m = GetMenuHandle(MENU_WINDOWS);

	/* Default choices */
	for (i = 0; i < MAX_TERM_DATA; i++)
	{
		Str15 buf;

		/* Describe the item */
		strnfmt((char*)buf + 1, 15, "%.15s", angband_term_name[i]);
		buf[0] = strlen((char*)buf + 1);

		/* Add the item */
		AppendMenu(m, buf);

		/* Command-Key shortcuts */
		if (i < 8) SetItemCmd(m, i + 1, I2D(i));
	}


#ifndef USE_NIB

	/* Special menu (id 134) */
	m = GetMenuHandle(MENU_SPECIAL);

	/* Insert Graphics submenu (id 144) */
	{
		MenuHandle submenu;

		/* Get the submenu */
		submenu = GetMenu(SUBMENU_GRAPH);

		/* Insert it */
		SetMenuItemHierarchicalMenu(m, ITEM_GRAPH, submenu);
	}

	/* Insert TileWidth submenu (id 145) */
	{
		MenuHandle submenu;

		/* Get the submenu */
		submenu = GetMenu(SUBMENU_TILEWIDTH);

		/* Add some sizes */
		for (i = 4, n = 1; i <= 32; i++, n++)
		{
			Str15 buf;

			/* Textual size */
			strnfmt((char*)buf + 1, 15, "%d", i);
			buf[0] = strlen((char*)buf + 1);

			/* Append item */
			AppendMenu(submenu, buf);

			/* Remember its value, for we can't be sure it's in ASCII */
			menu_tilewidth_values[n] = i;
		}

		/* Insert it */
		SetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, submenu);
	}

	/* Insert TileHeight submenu (id 146) */
	{
		MenuHandle submenu;

		/* Get the submenu */
		submenu = GetMenu(SUBMENU_TILEHEIGHT);


		/* Add some sizes */
		for (i = 4, n = 1; i <= 32; i++, n++)
		{
			Str15 buf;

			/* Textual size */
			strnfmt((char*)buf + 1, 15, "%d", i);
			buf[0] = strlen((char*)buf + 1);

			/* Append item */
			AppendMenu(submenu, buf);

			/* Remember its value, for we can't be sure it's in ASCII */
			menu_tileheight_values[n] = i;
		}

		/* Insert it */
		SetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, submenu);
	}

#endif /* !USE_NIB */

	/* Update the menu bar */
	DrawMenuBar();
}


/*
 * Prepare the menus
 *
 * It is very important that the player not be allowed to "save" the game
 * unless the "inkey_flag" variable is set, indicating that the game is
 * waiting for a new command.  XXX XXX XXX
 */

static void setup_menus(void)
{
	int i, n;

	short value;

	Str255 s;

	MenuHandle m;

	term_data *td = NULL;


	/* Relevant "term_data" */
	for (i = 0; i < MAX_TERM_DATA; i++)
	{
		/* Unused */
		if (!data[i].t) continue;

		/* Notice the matching window */
		if (data[i].w == FrontWindow()) td = &data[i];
	}


	/* File menu */
	m = GetMenuHandle(MENU_FILE);

	/* Get menu size */
	n = CountMenuItems(m);

	/* Reset menu */
	for (i = 1; i <= n; i++)
	{
		/* Reset */
		DisableMenuItem(m, i);
		CheckMenuItem(m, i, FALSE);
	}

#ifndef SAVEFILE_SCREEN

	/* Enable "new"/"open..."/"import..." */
	if (initialized && !game_in_progress)
	{
		EnableMenuItem(m, ITEM_NEW);
		EnableMenuItem(m, ITEM_OPEN);
		EnableMenuItem(m, ITEM_IMPORT);
	}

#endif /* !SAVEFILE_SCREEN */

	/* Enable "close" */
	if (initialized)
	{
		EnableMenuItem(m, ITEM_CLOSE);
	}

	/* Enable "save" */
	if (initialized && character_generated && inkey_flag)
	{
		EnableMenuItem(m, ITEM_SAVE);
	}

#ifdef HAS_SCORE_MENU

	/* Enable "score" */
	if (initialized && character_generated && !character_icky)
	{
		EnableMenuItem(m, ITEM_SCORE);
	}

#endif /* HAS_SCORE_MENU */

	/* Enable "quit" */
	if (!is_aqua)
	{
		if (!initialized || !character_generated || inkey_flag)
		{
			EnableMenuItem(m, ITEM_QUIT);
		}
	}


	/* Edit menu */
	m = GetMenuHandle(MENU_EDIT);

	/* Get menu size */
	n = CountMenuItems(m);

	/* Reset menu */
	for (i = 1; i <= n; i++)
	{
		/* Reset */
		DisableMenuItem(m, i);
		CheckMenuItem(m, i, FALSE);
	}

	/* Enable "edit" options if "needed" */
	if (!td)
	{
		EnableMenuItem(m, ITEM_UNDO);
		EnableMenuItem(m, ITEM_CUT);
		EnableMenuItem(m, ITEM_COPY);
		EnableMenuItem(m, ITEM_PASTE);
		EnableMenuItem(m, ITEM_CLEAR);
	}


	/* Font menu */
	m = GetMenuHandle(MENU_FONT);

	/* Get menu size */
	n = CountMenuItems(m);

	/* Reset menu */
	for (i = 1; i <= n; i++)
	{
		/* Reset */
		DisableMenuItem(m, i);
		CheckMenuItem(m, i, FALSE);
	}

	/* Hack -- look cute XXX XXX */
	/* SetItemStyle(m, ITEM_BOLD, bold); */

	/* Hack -- look cute XXX XXX */
	/* SetItemStyle(m, ITEM_WIDE, extend); */

	/* Active window */
	if (initialized && td)
	{
		/* Enable "bold" */
		EnableMenuItem(m, ITEM_BOLD);

		/* Enable "extend" */
		EnableMenuItem(m, ITEM_WIDE);

		/* Check the appropriate "bold-ness" */
		if (td->font_face & bold) CheckMenuItem(m, ITEM_BOLD, TRUE);

		/* Check the appropriate "wide-ness" */
		if (td->font_face & extend) CheckMenuItem(m, ITEM_WIDE, TRUE);

		/* Analyze fonts */
		for (i = 4; i <= n; i++)
		{
			/* Enable it */
			EnableMenuItem(m, i);

			/* Analyze font */
			GetMenuItemText(m, i, s);
			GetFNum(s, &value);

			/* Check active font */
			if (td->font_id == value) CheckMenuItem(m, i, TRUE);
		}
	}


	/* Size menu */
	m = GetMenuHandle(MENU_SIZE);

	/* Get menu size */
	n = CountMenuItems(m);

	/* Reset menu */
	for (i = 1; i <= n; i++)
	{
		/* Reset */
		DisableMenuItem(m, i);
		CheckMenuItem(m, i, FALSE);
	}

	/* Active window */
	if (initialized && td)
	{
		/* Analyze sizes */
		for (i = 1; i <= n; i++)
		{
			/* Analyze size */
			value = menu_size_values[i];

			/* Enable the "real" sizes */
			if (RealFont(td->font_id, value)) EnableMenuItem(m, i);

			/* Check the current size */
			if (td->font_size == value) CheckMenuItem(m, i, TRUE);
		}
	}


	/* Windows menu */
	m = GetMenuHandle(MENU_WINDOWS);

	/* Get menu size */
	n = CountMenuItems(m);

	/* Check active windows */
	for (i = 1; i <= n; i++)
	{
		/* Check if needed */
		CheckMenuItem(m, i, data[i - 1].mapped);
	}


	/* Special menu */
	m = GetMenuHandle(MENU_SPECIAL);

	/* Get menu size */
	n = CountMenuItems(m);

	/* Reset menu */
	for (i = 1; i <= n; i++)
	{
		/* Reset */
		DisableMenuItem(m, i);
		CheckMenuItem(m, i, FALSE);
	}

	/* Item "arg_sound" */
	EnableMenuItem(m, ITEM_SOUND);
	CheckMenuItem(m, ITEM_SOUND, arg_sound);

	/* Item "Graphics" */
	EnableMenuItem(m, ITEM_GRAPH);
	{
		MenuRef submenu;

		/* Graphics submenu */
		(void)GetMenuItemHierarchicalMenu(m, ITEM_GRAPH, &submenu);

		/* Get menu size */
		n = CountMenuItems(submenu);

		/* Reset menu */
		for (i = 1; i <= n; i++)
		{
			/* Reset */
			DisableMenuItem(submenu, i);
			CheckMenuItem(submenu, i, FALSE);
		}

		/* Item "None" */
		EnableMenuItem(submenu, ITEM_NONE);
		CheckMenuItem(submenu, ITEM_NONE, (graf_mode == GRAF_MODE_NONE));

		/* Item "8x8" */
		EnableMenuItem(submenu, ITEM_8X8);
		CheckMenuItem(submenu, ITEM_8X8, (graf_mode == GRAF_MODE_8X8));

		/* Item "16x16" */
		EnableMenuItem(submenu, ITEM_16X16);
		CheckMenuItem(submenu, ITEM_16X16, (graf_mode == GRAF_MODE_16X16));

		/* Item "32x32" */
		/*EnableMenuItem(submenu, ITEM_32X32);
		CheckMenuItem(submenu, ITEM_32X32, (graf_mode == GRAF_MODE_32X32));*/

#ifdef USE_DOUBLE_TILES

		/* Item "Big tiles" */
		if (inkey_flag) EnableMenuItem(submenu, ITEM_BIGTILE);
		CheckMenuItem(submenu, ITEM_BIGTILE, use_bigtile);

#endif /* USE_DOUBLE_TILES */

	}

	/* Item "TileWidth" */
	EnableMenuItem(m, ITEM_TILEWIDTH);
	{
		MenuRef submenu;

		/* TileWidth submenu */
		(void)GetMenuItemHierarchicalMenu(m, ITEM_TILEWIDTH, &submenu);

		/* Get menu size */
		n = CountMenuItems(submenu);

		/* Reset menu */
		for (i = 1; i <= n; i++)
		{
			/* Reset */
			DisableMenuItem(submenu, i);
			CheckMenuItem(submenu, i, FALSE);
		}

		/* Active window */
		if (initialized && td)
		{
			/* Analyze sizes */
			for (i = 1; i <= n; i++)
			{
				/* Analyze size */
				value = menu_tilewidth_values[i];

				/* Enable */
				if (value >= td->font_wid) EnableMenuItem(submenu, i);

				/* Check the current size */
				if (td->tile_wid == value) CheckMenuItem(submenu, i, TRUE);
			}
		}
	}

	/* Item "TileHeight" */
	EnableMenuItem(m, ITEM_TILEHEIGHT);
	{
		MenuRef submenu;

		/* TileWidth submenu */
		(void)GetMenuItemHierarchicalMenu(m, ITEM_TILEHEIGHT, &submenu);

		/* Get menu size */
		n = CountMenuItems(submenu);

		/* Reset menu */
		for (i = 1; i <= n; i++)
		{
			/* Reset */
			DisableMenuItem(submenu, i);
			CheckMenuItem(submenu, i, FALSE);
		}

		/* Active window */
		if (initialized && td)
		{
			/* Analyze sizes */
			for (i = 1; i <= n; i++)
			{
				/* Analyze size */
				value = menu_tileheight_values[i];

				/* Enable */
				if (value >= td->font_hgt) EnableMenuItem(submenu, i);

				/* Check the current size */
				if (td->tile_hgt == value) CheckMenuItem(submenu, i, TRUE);
			}
		}
	}

	/* Item "arg_fiddle" */
	EnableMenuItem(m, ITEM_FIDDLE);
	CheckMenuItem(m, ITEM_FIDDLE, arg_fiddle);

	/* Item "arg_wizard" */
	EnableMenuItem(m, ITEM_WIZARD);
	CheckMenuItem(m, ITEM_WIZARD, arg_wizard);


	/* TileHeight menu */
	m = GetMenuHandle(SUBMENU_TILEHEIGHT);

	/* Get menu size */
	n = CountMenuItems(m);

	/* Reset menu */
	for (i = 1; i <= n; i++)
	{
		/* Reset */
		DisableMenuItem(m, i);
		CheckMenuItem(m, i, FALSE);
	}
}


/*
 * Process a menu selection (see above)
 *
 * Hack -- assume that invalid menu selections are disabled above,
 * which I have been informed may not be reliable.  XXX XXX XXX
 */
static void menu(long mc)
{
	int i;

	int menuid, selection;

	static unsigned char s[1000];

	short fid;

	term_data *td = NULL;

	WindowPtr old_win;


	/* Analyze the menu command */
	menuid = HiWord(mc);
	selection = LoWord(mc);


	/* Find the window */
	for (i = 0; i < MAX_TERM_DATA; i++)
	{
		/* Skip dead windows */
		if (!data[i].t) continue;

		/* Notice matches */
		if (data[i].w == FrontWindow()) td = &data[i];
	}


	/* Branch on the menu */
	switch (menuid)
	{
		/* Apple Menu */
	case MENU_APPLE:
		{
			/* About Angband... */
			if (selection == ITEM_ABOUT)
			{
				DialogPtr dialog;
				short item_hit;

				/* Get the about dialogue */
				dialog = GetNewDialog(128, 0, (WindowPtr) - 1);

				/* Move it to the middle of the screen */
				RepositionWindow(
				        GetDialogWindow(dialog),
				        NULL,
				        kWindowCenterOnMainScreen);

				/* Show the dialog */
				TransitionWindow(GetDialogWindow(dialog),
				                 kWindowZoomTransitionEffect,
				                 kWindowShowTransitionAction,
				                 NULL);

				/* Wait for user to click on it */
				ModalDialog(0, &item_hit);

				/* Free the dialogue */
				DisposeDialog(dialog);
				break;
			}

			break;
		}

		/* File Menu */
	case MENU_FILE:
		{
			switch (selection)
			{
#ifndef SAVEFILE_SCREEN

				/* New */
			case ITEM_NEW:
				{
					do_menu_file_new();
					break;
				}

				/* Open... */
			case ITEM_OPEN:
				{
					do_menu_file_open(FALSE);
					break;
				}

				/* Import... */
			case ITEM_IMPORT:
				{
					do_menu_file_open(TRUE);
					break;
				}

#endif /* !SAVEFILE_SCREEN */

				/* Close */
			case ITEM_CLOSE:
				{
					/* No window */
					if (!td) break;

					/* Not Mapped */
					td->mapped = FALSE;

					/* Not Mapped */
					td->t->mapped_flag = FALSE;

					/* Hide the window */
					TransitionWindow(td->w,
					                 kWindowZoomTransitionEffect,
					                 kWindowHideTransitionAction,
					                 NULL);

					break;
				}

				/* Save */
			case ITEM_SAVE:
				{
					/* Hack -- Forget messages */
					msg_flag = FALSE;

					/* Hack -- Save the game */
#ifndef ZANG_AUTO_SAVE
					do_cmd_save_game();
#else
					do_cmd_save_game(FALSE);
#endif /* !ZANG_AUTO_SAVE */

					break;
				}

#ifdef HAS_SCORE_MENU

				/* Show score */
			case ITEM_SCORE:
				{
					char buf[1024];

					/* Paranoia */
					if (!initialized || character_icky ||
					                !game_in_progress || !character_generated)
					{
						/* Can't happen but just in case */
						plog("You may not do that right now.");

						break;
					}

					/* Build the pathname of the score file */
					path_build(buf, sizeof(buf), ANGBAND_DIR_APEX,
					           "scores.raw");

					/* Hack - open the score file for reading */
					highscore_fd = fd_open(buf, O_RDONLY);

					/* Paranoia - No score file */
					if (highscore_fd < 0)
					{
						msg_print("Score file is not available.");

						break;
					}

					/* Mega-Hack - prevent various functions XXX XXX XXX */
					initialized = FALSE;

					/* Save screen */
					screen_save();

					/* Clear screen */
					Term_clear();

					/* Prepare scores */
					if (game_in_progress && character_generated)
					{
						predict_score();
					}

#if 0 /* I don't like this - pelpel */

					/* Mega-Hack - No current player XXX XXX XXX XXX */
					else
					{
						display_scores_aux(0, MAX_HISCORES, -1, NULL);
					}

#endif

					/* Close the high score file */
					(void)fd_close(highscore_fd);

					/* Forget the fd */
					highscore_fd = -1;

					/* Restore screen */
					screen_load();

					/* Hack - Flush it */
					Term_fresh();

					/* Mega-Hack - We are ready again */
					initialized = TRUE;

					/* Done */
					break;
				}

#endif /* HAS_SCORE_MENU */

				/* Quit (with save) */
			case ITEM_QUIT:
				{
					/* Save the game (if necessary) */
					if (game_in_progress && character_generated)
					{
						/* Hack -- Forget messages */
						msg_flag = FALSE;

						/* Save the game */
#ifndef ZANG_AUTO_SAVE
						do_cmd_save_game();
#else
						do_cmd_save_game(FALSE);
#endif /* !ZANG_AUTO_SAVE */
					}

					/* Quit */
					quit(NULL);
					break;
				}
			}
			break;
		}

		/* Edit menu */
	case MENU_EDIT:
		{
			/* Unused */
			break;
		}

		/* Font menu */
	case MENU_FONT:
		{
			/* Require a window */
			if (!td) break;

			/* Memorize old */
			old_win = active;

			/* Activate */
			activate(td->w);

			/* Toggle the "bold" setting */
			if (selection == ITEM_BOLD)
			{
				/* Toggle the setting */
				if (td->font_face & bold)
				{
					td->font_face &= ~bold;
				}
				else
				{
					td->font_face |= bold;
				}

				/* Hack - clear tile size info XXX XXX */
				td->tile_wid = td->tile_hgt = 0;

				/* Apply and Verify */
				term_data_check_font(td);
				term_data_check_size(td);

				/* Resize and Redraw */
				term_data_resize(td);
				term_data_redraw(td);

				break;
			}

			/* Toggle the "wide" setting */
			if (selection == ITEM_WIDE)
			{
				/* Toggle the setting */
				if (td->font_face & extend)
				{
					td->font_face &= ~extend;
				}
				else
				{
					td->font_face |= extend;
				}

				/* Hack - clear tile size info XXX XXX */
				td->tile_wid = td->tile_hgt = 0;

				/* Apply and Verify */
				term_data_check_font(td);
				term_data_check_size(td);

				/* Resize and Redraw */
				term_data_resize(td);
				term_data_redraw(td);

				break;
			}

			/* Get a new font name */
			GetMenuItemText(GetMenuHandle(MENU_FONT), selection, s);
			GetFNum(s, &fid);

			/* Save the new font id */
			td->font_id = fid;

			/* Current size is bad for new font */
			if (!RealFont(td->font_id, td->font_size))
			{
				/* Find similar size */
				for (i = 1; i <= 32; i++)
				{
					/* Adjust smaller */
					if (td->font_size - i >= 8)
					{
						if (RealFont(td->font_id, td->font_size - i))
						{
							td->font_size -= i;
							break;
						}
					}

					/* Adjust larger */
					if (td->font_size + i <= 128)
					{
						if (RealFont(td->font_id, td->font_size + i))
						{
							td->font_size += i;
							break;
						}
					}
				}
			}

			/* Hack - clear tile size info XXX XXX */
			td->tile_wid = td->tile_hgt = 0;

			/* Apply and Verify */
			term_data_check_font(td);
			term_data_check_size(td);

			/* Resize and Redraw */
			term_data_resize(td);
			term_data_redraw(td);

			/* Restore the window */
			activate(old_win);

			break;
		}

		/* Size menu */
	case MENU_SIZE:
		{
			if (!td) break;

			/* Save old */
			old_win = active;

			/* Activate */
			activate(td->w);

			td->font_size = menu_size_values[selection];

			/* Hack - clear tile size info XXX XXX */
			td->tile_wid = td->tile_hgt = 0;

			/* Apply and Verify */
			term_data_check_font(td);
			term_data_check_size(td);

			/* Resize and Redraw */
			term_data_resize(td);
			term_data_redraw(td);

			/* Restore */
			activate(old_win);

			break;
		}

		/* Window menu */
	case MENU_WINDOWS:
		{
			/* Parse */
			i = selection - 1;

			/* Check legality of choice */
			if ((i < 0) || (i >= MAX_TERM_DATA)) break;

			/* Obtain the window */
			td = &data[i];

			/* Mapped */
			td->mapped = TRUE;

			/* Link */
			term_data_link(i);

			/* Mapped (?) */
			td->t->mapped_flag = TRUE;

			/* Show the window */
			TransitionWindow(td->w,
			                 kWindowZoomTransitionEffect,
			                 kWindowShowTransitionAction,
			                 NULL);

			/* Bring to the front */
			SelectWindow(td->w);

			break;
		}

		/* Special menu */
	case MENU_SPECIAL:
		{
			switch (selection)
			{
			case ITEM_SOUND:
				{
					/* Toggle arg_sound */
					arg_sound = !arg_sound;

					/* React to changes */
					Term_xtra(TERM_XTRA_REACT, 0);

					break;
				}

			case ITEM_FIDDLE:
				{
					arg_fiddle = !arg_fiddle;

					break;
				}

			case ITEM_WIZARD:
				{
					arg_wizard = !arg_wizard;

					break;
				}
			}

			break;
		}

		/* Graphics submenu */
	case SUBMENU_GRAPH:
		{
			switch (selection)
			{
			case ITEM_NONE:
				{
					graf_mode_req = GRAF_MODE_NONE;

					break;
				}

			case ITEM_8X8:
				{
					graf_mode_req = GRAF_MODE_8X8;

					break;
				}

			case ITEM_16X16:
				{
					graf_mode_req = GRAF_MODE_16X16;

					break;
				}

			case ITEM_32X32:
				{
					graf_mode_req = GRAF_MODE_32X32;

					break;
				}

#ifdef USE_DOUBLE_TILES

			case ITEM_BIGTILE:
				{
					term *old = Term;
					term_data *td = &data[0];

					/* Toggle "use_bigtile" */
					use_bigtile = !use_bigtile;
					arg_bigtile = use_bigtile;

					/* Activate */
					Term_activate(td->t);

					/* Resize the term */
					Term_resize(td->cols, td->rows);

					/* Activate old */
					Term_activate(old);

					break;
				}

#endif /* USE_DOUBLE_TILES */

			}

			/* Hack -- Force redraw */
			Term_key_push(KTRL('R'));

			break;
		}

		/* TileWidth menu */
	case SUBMENU_TILEWIDTH:
		{
			if (!td) break;

			/* Save old */
			old_win = active;

			/* Activate */
			activate(td->w);

			/* Analyse value */
			td->tile_wid = menu_tilewidth_values[selection];

			/* Apply and Verify */
			term_data_check_size(td);

			/* Resize and Redraw */
			term_data_resize(td);
			term_data_redraw(td);

			/* Restore */
			activate(old_win);

			break;
		}

		/* TileHeight menu */
	case SUBMENU_TILEHEIGHT:
		{
			if (!td) break;

			/* Save old */
			old_win = active;

			/* Activate */
			activate(td->w);

			/* Analyse value */
			td->tile_hgt = menu_tileheight_values[selection];

			/* Apply and Verify */
			term_data_check_size(td);

			/* Resize and Redraw */
			term_data_resize(td);
			term_data_redraw(td);

			/* Restore */
			activate(old_win);

			break;
		}
	}


	/* Clean the menu */
	HiliteMenu(0);
}


/*
 * Check for extra required parameters -- From "Maarten Hazewinkel"
 */
static OSErr CheckRequiredAEParams(const AppleEvent *theAppleEvent)
{
	OSErr aeError;
	DescType returnedType;
	Size actualSize;

	aeError = AEGetAttributePtr(
	                  theAppleEvent, keyMissedKeywordAttr, typeWildCard,
	                  &returnedType, NULL, 0, &actualSize);

	if (aeError == errAEDescNotFound) return (noErr);

	if (aeError == noErr) return (errAEParamMissed);

	return (aeError);
}


/*
 * Apple Event Handler -- Open Application
 */
static OSErr AEH_Start(const AppleEvent *theAppleEvent, AppleEvent *reply,
                       SInt32 handlerRefCon)
{
	return (CheckRequiredAEParams(theAppleEvent));
}


/*
 * Apple Event Handler -- Quit Application
 */
static OSErr AEH_Quit(const AppleEvent *theAppleEvent, AppleEvent *reply,
                      SInt32 handlerRefCon)
{
	/* Quit later */
	quit_when_ready = TRUE;

	/* Check arguments */
	return (CheckRequiredAEParams(theAppleEvent));
}


/*
 * Apple Event Handler -- Print Documents
 */
static OSErr AEH_Print(const AppleEvent *theAppleEvent, AppleEvent *reply,
                       SInt32 handlerRefCon)
{
	return (errAEEventNotHandled);
}


/*
 * Apple Event Handler by Steve Linberg (slinberg@crocker.com).
 *
 * The old method of opening savefiles from the finder does not work
 * on the Power Macintosh, because CountAppFiles and GetAppFiles,
 * used to return information about the selected document files when
 * an application is launched, are part of the Segment Loader, which
 * is not present in the RISC OS due to the new memory architecture.
 *
 * The "correct" way to do this is with AppleEvents.  The following
 * code is modeled on the "Getting Files Selected from the Finder"
 * snippet from Think Reference 2.0.  (The prior sentence could read
 * "shamelessly swiped & hacked")
 */
static OSErr AEH_Open(const AppleEvent *theAppleEvent, AppleEvent* reply,
                      SInt32 handlerRefCon)
{
	FSSpec myFSS;
	AEDescList docList;
	OSErr err;
	Size actualSize;
	AEKeyword keywd;
	DescType returnedType;
	char msg[128];
	FInfo myFileInfo;

	/* Put the direct parameter (a descriptor list) into a docList */
	err = AEGetParamDesc(
	              theAppleEvent, keyDirectObject, typeAEList, &docList);
	if (err) return err;

	/*
	 * We ignore the validity check, because we trust the FInder, and we only
	 * allow one savefile to be opened, so we ignore the depth of the list.
	 */
	err = AEGetNthPtr(
	              &docList, 1L, typeFSS, &keywd, &returnedType,
	              (Ptr) & myFSS, sizeof(myFSS), &actualSize);
	if (err) return err;

	/* Only needed to check savefile type below */
	err = FSpGetFInfo(&myFSS, &myFileInfo);
	if (err)
	{
		strnfmt(msg, 128, "Argh!  FSpGetFInfo failed with code %d", err);
		mac_warning(msg);
		return err;
	}

	/* Ignore non 'SAVE' files */
	if (myFileInfo.fdType != 'SAVE') return noErr;

#ifdef MACH_O_CARBON

	/* Extract a file name */
	(void)spec_to_path(&myFSS, savefile, sizeof(savefile));

#else

	/* XXX XXX XXX Extract a file name */
	PathNameFromDirID(myFSS.parID, myFSS.vRefNum, (StringPtr)savefile);
	pstrcat((StringPtr)savefile, (StringPtr)&myFSS.name);

	/* Convert the string */
	ptocstr((StringPtr)savefile);

#endif /* MACH_O_CARBON */

	/* Delay actual open */
	open_when_ready = TRUE;

	/* Dispose */
	err = AEDisposeDesc(&docList);

	/* Success */
	return noErr;
}


/*
 * Handle quit_when_ready, by Peter Ammon,
 * slightly modified to check inkey_flag.
 */
static void quit_calmly(void)
{
	/* Quit immediately if game's not started */
	if (!game_in_progress || !character_generated) quit(NULL);

	/* Save the game and Quit (if it's safe) */
	if (inkey_flag)
	{
		/* Hack -- Forget messages */
		msg_flag = FALSE;

		/* Save the game */
#ifndef ZANG_AUTO_SAVE
		do_cmd_save_game();
#else
		do_cmd_save_game(FALSE);
#endif /* !ZANG_AUTO_SAVE */

		/* Quit */
		quit(NULL);
	}

	/* Wait until inkey_flag is set */
}


/*
 * Macintosh modifiers (event.modifier & ccc):
 *   cmdKey, optionKey, shiftKey, alphaLock, controlKey
 *
 *
 * Macintosh Keycodes (0-63 normal, 64-95 keypad, 96-127 extra):
 *
 * Return:36
 * Delete:51
 *
 * Period:65
 * Star:67
 * Plus:69
 * Clear:71
 * Slash:75
 * Enter:76
 * Minus:78
 * Equal:81
 * 0-7:82-89
 * 8-9:91-92
 *
 * backslash/vertical bar (Japanese keyboard):93
 *
 * F5: 96
 * F6: 97
 * F7: 98
 * F3:99
 * F8:100
 * F10:101
 * F11:103
 * F13:105
 * F14:107
 * F9:109
 * F12:111
 * F15:113
 * Help:114
 * Home:115
 * PgUp:116
 * Del:117
 * F4: 118
 * End:119
 * F2:120
 * PgDn:121
 * F1:122
 * Lt:123
 * Rt:124
 * Dn:125
 * Up:126
 */


/*
 * Check for Events, return TRUE if we process any
 *
 * Now it really waits for events if wait set to true, to prevent
 * undesirable monopoly of CPU. The side-effect is that you cannot do
 * while (CheckEvents(TRUE)); without discretion.
 */
static bool CheckEvents(bool wait)
{
	EventRecord event;

	WindowPtr w;

	Rect r;

	UInt32 sleep_ticks;

	int ch, ck;

	int mc, ms, mo, mx;

	int i;

	term_data *td = NULL;


	/*
	 * With the wait mode blocking for available event / timeout,
	 * the non-wait mode should actually call WaitNextEvent,
	 * because of those event draining loops. Or we had to
	 * implement yet another mode.
	 */

	/* Handles the quit_when_ready flag */
	if (quit_when_ready) quit_calmly();

	/* Blocking call to WaitNextEvent - should use MAX_INT XXX XXX */
	if (wait) sleep_ticks = 0x7FFFFFFFL;

	/* Non-blocking */
	else sleep_ticks = 0L;

	/* Get an event (or null)  */
	WaitNextEvent(everyEvent, &event, sleep_ticks, nil);

	/* Hack -- Nothing is ready yet */
	if (event.what == nullEvent) return (FALSE);


	/* Analyze the event */
	switch (event.what)
	{

#if 0

	case activateEvt:
		{
			w = (WindowPtr)event.message;

			activate(w);

			break;
		}

#endif

	case updateEvt:
		{
			/* Extract the window */
			w = (WindowPtr)event.message;

			/* Find the window */
			for (i = 0; i < MAX_TERM_DATA; i++)
			{
				/* Skip dead windows */
				if (!data[i].t) continue;

				/* Notice matches */
				if (data[i].w == w) td = &data[i];
			}

			/* Hack XXX XXX XXX */
			BeginUpdate(w);
			EndUpdate(w);

			/* Redraw the window */
			if (td) term_data_redraw(td);

			break;
		}

	case keyDown:
	case autoKey:
		{
			/* Extract some modifiers */
			mc = (event.modifiers & controlKey) ? TRUE : FALSE;
			ms = (event.modifiers & shiftKey) ? TRUE : FALSE;
			mo = (event.modifiers & optionKey) ? TRUE : FALSE;
			mx = (event.modifiers & cmdKey) ? TRUE : FALSE;

			/* Keypress: (only "valid" if ck < 96) */
			ch = (event.message & charCodeMask) & 255;

			/* Keycode: see table above */
			ck = ((event.message & keyCodeMask) >> 8) & 255;

			/* Command + "normal key" -> menu action */
			if (mx && (ck < 64))
			{
#ifdef MENU_SHORTCUTS
				/* Hack -- Prepare the menus */
				setup_menus();

				/* Run the Menu-Handler */
				menu(MenuKey(ch));

				/* Turn off the menus */
				HiliteMenu(0);

				/* Done */
				break;
#else
				/* Begin special trigger */
				Term_keypress(31);

				/* Send some modifier keys */
				if (mc) Term_keypress('C');
				if (ms) Term_keypress('S');
				if (mo) Term_keypress('O');
				if (mx) Term_keypress('X');

				/* Enqueue the keypress */
				Term_keypress(ch);

				/* Terminate the trigger */
				Term_keypress(13);
#endif
			}

			/* Hide the mouse pointer */
			ObscureCursor();

			/* Normal key -> simple keypress */
			if ((ck < 64) || (ck == 93))
			{
				/* Enqueue the keypress */
				Term_keypress(ch);
			}

			/* Keypad keys -> trigger plus simple keypress */
			else if (!mc && !ms && !mo && !mx && (ck < 96))
			{
				/* Hack -- "enter" is confused */
				if (ck == 76) ch = '\n';

				/* Begin special trigger */
				Term_keypress(31);

				/* Send the "keypad" modifier */
				Term_keypress('K');

				/* Send the "ascii" keypress */
				Term_keypress(ch);

				/* Terminate the trigger */
				Term_keypress(13);
			}

			/* Bizarre key -> encoded keypress */
			else if (ck <= 127)
			{
				/* Begin special trigger */
				Term_keypress(31);

				/* Send some modifier keys */
				if (mc) Term_keypress('C');
				if (ms) Term_keypress('S');
				if (mo) Term_keypress('O');
				if (mx) Term_keypress('X');

				/* Downshift and encode the keycode */
				Term_keypress(I2D((ck - 64) / 10));
				Term_keypress(I2D((ck - 64) % 10));

				/* Terminate the trigger */
				Term_keypress(13);
			}

			break;
		}

	case mouseDown:
		{
			int code;

			/* Analyze click location */
			code = FindWindow(event.where, &w);

			/* Find the window */
			for (i = 0; i < MAX_TERM_DATA; i++)
			{
				/* Skip dead windows */
				if (!data[i].t) continue;

				/* Notice matches */
				if (data[i].w == w) td = &data[i];
			}

			/* Analyze */
			switch (code)
			{
			case inMenuBar:
				{
					setup_menus();
					menu(MenuSelect(event.where));
					HiliteMenu(0);
					break;
				}

			case inDrag:
				{
					WindowPtr old_win;
					BitMap tBitMap;
					Rect pRect;

					r = GetQDGlobalsScreenBits(&tBitMap)->bounds;
					r.top += 20;  /* GetMBarHeight() XXX XXX XXX */
					InsetRect(&r, 4, 4);
					DragWindow(w, event.where, &r);

					/* Oops */
					if (!td) break;

					/* Save */
					old_win = active;

					/* Activate */
					activate(td->w);

					/* Analyze */
					GetWindowBounds(
					        (WindowRef)td->w,
					        kWindowContentRgn,
					        &pRect);
					td->r.left = pRect.left;
					td->r.top = pRect.top;

					/* Apply and Verify */
					term_data_check_size(td);

					/* Restore */
					activate(old_win);

					break;
				}

			case inGoAway:
				{
					/* Oops */
					if (!td) break;

					/* Track the go-away box */
					if (TrackGoAway(w, event.where))
					{
						/* Not Mapped */
						td->mapped = FALSE;

						/* Not Mapped */
						td->t->mapped_flag = FALSE;

						/* Hide the window */
						TransitionWindow(td->w,
						                 kWindowZoomTransitionEffect,
						                 kWindowHideTransitionAction,
						                 NULL);
					}

					break;
				}

			case inGrow:
				{
					int x, y;

					Rect nr;

					term *old = Term;

					/* Oops */
					if (!td) break;

#ifndef ALLOW_BIG_SCREEN

					/* Minimum and maximum sizes */
					r.left = 20 * td->tile_wid + td->size_ow1;
					r.right = 80 * td->tile_wid + td->size_ow1 + td->size_ow2 + 1;
					r.top = 1 * td->tile_hgt + td->size_oh1;
					r.bottom = 24 * td->tile_hgt + td->size_oh1 + td->size_oh2 + 1;

					/* Grow the rectangle */
					if (!ResizeWindow(w, event.where, &r, NULL)) break;
#else

					/* Grow the rectangle */
					if (!ResizeWindow(w, event.where, NULL, NULL)) break;

#endif /* !ALLOW_BIG_SCREEN */


					/* Obtain geometry of resized window */
					GetWindowBounds(w, kWindowContentRgn, &nr);

					/* Extract the new size in pixels */
					y = nr.bottom - nr.top - td->size_oh1 - td->size_oh2;
					x = nr.right - nr.left - td->size_ow1 - td->size_ow2;

					/* Extract a "close" approximation */
					td->rows = y / td->tile_hgt;
					td->cols = x / td->tile_wid;

					/* Apply and Verify */
					term_data_check_size(td);

					/* Activate */
					Term_activate(td->t);

					/* Hack -- Resize the term */
					Term_resize(td->cols, td->rows);

					/* Resize and Redraw */
					term_data_resize(td);
					term_data_redraw(td);

					/* Restore */
					Term_activate(old);

					break;
				}

			case inContent:
				{
					SelectWindow(w);

					break;
				}
			}

			break;
		}

		/* OS Event -- From "Maarten Hazewinkel" */
	case osEvt:
		{
			switch ((event.message >> 24) & 0x000000FF)
			{
			case suspendResumeMessage:

				/* Resuming: activate the front window */
				if (event.message & resumeFlag)
				{
					Cursor tempCursor;
					SetPort(GetWindowPort(FrontWindow()));
					SetCursor(GetQDGlobalsArrow(&tempCursor));
				}

				/* Suspend: deactivate the front window */
				else
				{
					/* Nothing */
				}

				break;
			}

			break;
		}

		/* From "Steve Linberg" and "Maarten Hazewinkel" */
	case kHighLevelEvent:
		{
			/* Process apple events */
			(void)AEProcessAppleEvent(&event);

			/* Handle "quit_when_ready" */
			if (quit_when_ready)
			{
#if 0 /* Doesn't work with Aqua well */
				/* Forget */
				quit_when_ready = FALSE;

				/* Do the menu key */
				menu(MenuKey('q'));
#endif
				/* Turn off the menus */
				HiliteMenu(0);
			}

			/* Handle "open_when_ready" */
			else if (open_when_ready)
			{
				handle_open_when_ready();
			}

			break;
		}

	}


	/* Something happened */
	return (TRUE);
}


/*** Some Hooks for various routines ***/


/*
 * Mega-Hack -- emergency lifeboat
 */
static void *lifeboat = NULL;


/*
 * Hook to "release" memory
 */
#ifdef NEW_ZVIRT_HOOKS /* [V] removed the unused 'size' argument. */
static void *hook_rnfree(void *v)
#else
static void *hook_rnfree(void *v, size_t size)
#endif /* NEW_ZVIRT_HOOKS */
{

#ifdef USE_MALLOC

	/* Alternative method */
	free(v);

#else

	/* Dispose */
	DisposePtr(v);

#endif

	/* Success */
	return (NULL);
}

/*
 * Hook to "allocate" memory
 */
static void *hook_ralloc(size_t size)
{

#ifdef USE_MALLOC

	/* Make a new pointer */
	return (malloc(size));

#else

	/* Make a new pointer */
	return (NewPtr(size));

#endif

}

/*
 * Hook to handle "out of memory" errors
 */
static void *hook_rpanic(size_t size)
{
	/* void *mem = NULL; */

	/* Free the lifeboat */
	if (lifeboat)
	{
		/* Free the lifeboat */
		DisposePtr(lifeboat);

		/* Forget the lifeboat */
		lifeboat = NULL;

		/* Mega-Hack -- Warning */
		mac_warning("Running out of Memory!\rAbort this process now!");

		/* Mega-Hack -- Never leave this function */
		while (TRUE) CheckEvents(TRUE);
	}

	/* Mega-Hack -- Crash */
	return (NULL);
}


/*
 * Hook to tell the user something important
 */
static void hook_plog(cptr str)
{
	/* Warning message */
	mac_warning(str);
}


/*
 * Hook to tell the user something, and then quit
 */
static void hook_quit(cptr str)
{
	/* Warning if needed */
	if (str) mac_warning(str);

#ifdef USE_ASYNC_SOUND

	/* Clean up sound support */
	cleanup_sound();

#endif /* USE_ASYNC_SOUND */

	/* Dispose of graphic tiles */
	if (frameP)
	{
		/* Unlock */
		BenSWUnlockFrame(frameP);

		/* Dispose of the GWorld */
		DisposeGWorld(frameP->framePort);

		/* Dispose of the memory */
		DisposePtr((Ptr)frameP);
	}

	/* Write a preference file */
	save_pref_file();

	/* All done */
	ExitToShell();
}


/*
 * Hook to tell the user something, and then crash
 */
static void hook_core(cptr str)
{
	/* XXX Use the debugger */
	/* DebugStr(str); */

	/* Warning */
	if (str) mac_warning(str);

	/* Warn, then save player */
	mac_warning("Fatal error.\rI will now attempt to save and quit.");

	/* Attempt to save */
	if (!save_player()) mac_warning("Warning -- save failed!");

	/* Quit */
	quit(NULL);
}



/*** Main program ***/


/*
 * Init some stuff
 *
 * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
 * "Macintosh Save Bug" by using "absolute" path names, since on
 * System 7 machines anyway, the "current working directory" often
 * "changes" due to background processes, invalidating any "relative"
 * path names.  Note that the Macintosh is limited to 255 character
 * path names, so be careful about deeply embedded directories...
 *
 * XXX XXX XXX Hack -- This function attempts to "fix" the nasty
 * "missing lib folder bug" by allowing the user to help find the
 * "lib" folder by hand if the "application folder" code fails...
 *
 *
 * The problem description above no longer applies, but I left it here,
 * modified for Carbon, to allow the game proceeds when a user doesn't
 * placed the Angband binary and the lib folder in the same place for
 * whatever reasons. -- pelpel
 */
static void init_stuff(void)
{
	Rect r;
	BitMap tBitMap;
	Rect screenRect;
	Point topleft;

	char path[1024];

	OSErr err = noErr;
	NavDialogOptions dialogOptions;
	FSSpec theFolderSpec;
	NavReplyRecord theReply;


	/* Fake rectangle */
	r.left = 0;
	r.top = 0;
	r.right = 344;
	r.bottom = 188;

	/* Center it */
	screenRect = GetQDGlobalsScreenBits(&tBitMap)->bounds;
	center_rect(&r, &screenRect);

	/* Extract corner */
	topleft.v = r.top;
	topleft.h = r.left;

	/* Default to the "lib" folder with the application */
#ifdef MACH_O_CARBON
	if (locate_lib(path, sizeof(path)) == NULL) quit(NULL);
#else
	refnum_to_name(path, app_dir, app_vol, (char*)("\plib:"));
#endif


	/* Check until done */
	while (1)
	{
		/* Prepare the paths */
		init_file_paths(path);

		/* Build the filename */
		path_build(path, 1024, ANGBAND_DIR_FILE, "news.txt");

		/* Attempt to open and close that file */
		if (0 == fd_close(fd_open(path, O_RDONLY))) break;

		/* Warning */
		plog_fmt("Unable to open the '%s' file.", path);

		/* Warning */
		plog("The Angband 'lib' folder is probably missing or misplaced.");

		/* Ask the user to choose the lib folder */
		err = NavGetDefaultDialogOptions(&dialogOptions);

		/* Paranoia */
		if (err != noErr) quit(NULL);

		/* Set default location option */
		dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;

		/* Clear preview option */
		dialogOptions.dialogOptionFlags &= ~(kNavAllowPreviews);

		/* Forbit selection of multiple files */
		dialogOptions.dialogOptionFlags &= ~(kNavAllowMultipleFiles);

		/* Display location */
		dialogOptions.location = topleft;

#if 0

		/* Load the message for the missing folder from the resource fork */
		/* GetIndString(dialogOptions.message, 128, 1); */

#else

		/* Set the message for the missing folder XXX XXX */
		strcpy(dialogOptions.message + 1, "Please select the \"lib\" folder");
		dialogOptions.message[0] = strlen(dialogOptions.message + 1);

#endif

		/* Wait for the user to choose a folder */
		err = NavChooseFolder(
		              nil, &theReply, &dialogOptions, nil, nil, nil);

		/* Assume the player doesn't want to go on */
		if ((err != noErr) || !theReply.validRecord) quit(NULL);

		/* Retrieve FSSpec from the reply */
		{
			AEKeyword theKeyword;
			DescType actualType;
			Size actualSize;

			/* Get a pointer to selected folder */
			err = AEGetNthPtr(
			              &(theReply.selection), 1, typeFSS, &theKeyword,
			              &actualType, &theFolderSpec, sizeof(FSSpec), &actualSize);

			/* Paranoia */
			if (err != noErr) quit(NULL);
		}

		/* Free navitagor reply */
		err = NavDisposeReply(&theReply);

		/* Paranoia */
		if (err != noErr) quit(NULL);

		/* Extract textual file name for given file */
#ifdef MACH_O_CARBON
		if (spec_to_path(&theFolderSpec, path, sizeof(path)) != noErr)
		{
			quit(NULL);
		}
#else /* MACH_O_CARBON */
refnum_to_name(
		path,
		theFolderSpec.parID,
		theFolderSpec.vRefNum,
		(char *)theFolderSpec.name);
#endif /* MACH_O_CARBON */
	}
}


/*
 * Macintosh Main loop
 */
int main(void)
{
	int i;
	long response;
	OSStatus err;
	EventRecord tempEvent;
	UInt32 numberOfMasters = 10;

	/* Get more Masters -- it is not recommended by Apple, should go away */
	MoreMasterPointers(numberOfMasters);

	/* Check for existence of Carbon */
	err = Gestalt(gestaltCarbonVersion, &response);

	if (err != noErr) quit("This program requires Carbon API");

	/* See if we are running on Aqua */
	err = Gestalt(gestaltMenuMgrAttr, &response);

	/* Cache the result */
	if ((err == noErr) &&
	                (response & gestaltMenuMgrAquaLayoutMask)) is_aqua = TRUE;

	/*
	 * Remember Mac OS version, in case we have to cope with version-specific
	 * problems
	 */
	(void)Gestalt(gestaltSystemVersion, &mac_os_version);


	/* Set up the Macintosh */
	InitCursor();

	/* Flush events */
	FlushEvents(everyEvent, 0);

	/* Flush events some more (?) */
	if (EventAvail(everyEvent, &tempEvent)) FlushEvents(everyEvent, 0);


	/* Install the start event hook (ignore error codes) */
	AEInstallEventHandler(
	        kCoreEventClass,
	        kAEOpenApplication,
	        NewAEEventHandlerUPP(AEH_Start),
	        0L,
	        FALSE);

	/* Install the quit event hook (ignore error codes) */
	AEInstallEventHandler(
	        kCoreEventClass,
	        kAEQuitApplication,
	        NewAEEventHandlerUPP(AEH_Quit),
	        0L,
	        FALSE);

	/* Install the print event hook (ignore error codes) */
	AEInstallEventHandler(
	        kCoreEventClass,
	        kAEPrintDocuments,
	        NewAEEventHandlerUPP(AEH_Print),
	        0L,
	        FALSE);

	/* Install the open event hook (ignore error codes) */
	AEInstallEventHandler(
	        kCoreEventClass,
	        kAEOpenDocuments,
	        NewAEEventHandlerUPP(AEH_Open),
	        0L,
	        FALSE);


#ifndef MACH_O_CARBON

	/* Find the current application */
	SetupAppDir();

#endif /* !MACH_O_CARBON */

	/* Mark ourself as the file creator */
	_fcreator = ANGBAND_CREATOR;

	/* Default to saving a "text" file */
	_ftype = 'TEXT';


	/* Hook in some "z-virt.c" hooks */
	rnfree_aux = hook_rnfree;
	ralloc_aux = hook_ralloc;
	rpanic_aux = hook_rpanic;

	/* Hooks in some "z-util.c" hooks */
	plog_aux = hook_plog;
	quit_aux = hook_quit;
	core_aux = hook_core;


	/* Initialize colors */
	for (i = 0; i < 256; i++)
	{
		u16b rv, gv, bv;

		/* Extract the R,G,B data */
		rv = angband_color_table[i][1];
		gv = angband_color_table[i][2];
		bv = angband_color_table[i][3];

		/* Save the actual color */
		color_info[i].red = (rv | (rv << 8));
		color_info[i].green = (gv | (gv << 8));
		color_info[i].blue = (bv | (bv << 8));
	}


	/* Show the "watch" cursor */
	SetCursor(*(GetCursor(watchCursor)));

	/* Prepare the menubar */
	init_menubar();

	/* Prepare the windows */
	init_windows();

	/* Hack -- process all events */
	while (CheckEvents(FALSE)) /* loop */;

	/* Reset the cursor */
	{
		Cursor tempCursor;

		SetCursor(GetQDGlobalsArrow(&tempCursor));
	}

	/* Mega-Hack -- Allocate a "lifeboat" */
	lifeboat = NewPtr(16384);

#ifdef USE_QT_SOUND

	/* Load sound effect resources */
	load_sounds();

#endif /* USE_QT_SOUND */

	/* Note the "system" */
	ANGBAND_SYS = "mac";

#ifdef PRIVATE_USER_PATH
	if (check_create_user_dir() == FALSE)
		quit("Cannot create directory " PRIVATE_USER_PATH);
#endif

	/* Initialize */
	init_stuff();

	/* Initialize */
	init_angband();


	/* Hack -- process all events */
	while (CheckEvents(FALSE)) /* loop */;


	/* We are now initialized */
	initialized = TRUE;


	/* Handle "open_when_ready" */
	handle_open_when_ready();

#ifndef SAVEFILE_SCREEN

	/* Prompt the user - You may have to change this for some variants */
	prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 15);

	/* Flush the prompt */
	Term_fresh();

	/* Hack -- Process Events Forever */
	while (TRUE) CheckEvents(TRUE);

#else

	/* Game is in progress */
	game_in_progress = 1;

	/* Wait for keypress */
	pause_line(23);

	/* flush input - Warning: without this, _system_ would hang */
	flush();

	/* Play the game - note the value of the argument */
	play_game(FALSE);

	/* Quit */
	quit(NULL);

	/* Since it's a int function */
	return (0);
#endif /* !SAVEFILE_SCREEN */
}

#endif /* MACINTOSH || MACH_O_CARBON */

