summaryrefslogtreecommitdiff
path: root/src/main-crb.c
diff options
context:
space:
mode:
authorBardur Arantsson <bardur@scientician.net>2010-01-08 20:28:34 +0100
committerBardur Arantsson <bardur@scientician.net>2010-01-08 23:46:06 +0100
commit6aa48afdd57d03314fdf4be6c9da911c32277c84 (patch)
tree2dc401f9aae2dc6736d2fc3811c8f8099d3eabe6 /src/main-crb.c
Import tome-2.3.5.
Diffstat (limited to 'src/main-crb.c')
-rw-r--r--src/main-crb.c6450
1 files changed, 6450 insertions, 0 deletions
diff --git a/src/main-crb.c b/src/main-crb.c
new file mode 100644
index 00000000..7c8d11b7
--- /dev/null
+++ b/src/main-crb.c
@@ -0,0 +1,6450 @@
+/* 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 */
+