OSDN Git Service

Made beginGame() an instance method of AngbandAppDelegate rather than a class method...
[hengbandforosx/hengbandosx.git] / src / main-cocoa.m
index ea070f6..d6ea506 100644 (file)
@@ -23,7 +23,7 @@
 #if defined(MACH_O_COCOA)
 
 /* Mac headers */
-#include <Cocoa/Cocoa.h>
+#include <cocoa/AppDelegate.h>
 //#include <Carbon/Carbon.h> /* For keycodes */
 /* Hack - keycodes to enable compiling in macOS 10.14 */
 #define kVK_Return 0x24
@@ -41,6 +41,7 @@ static NSString * const AngbandTerminalRowsDefaultsKey = @"Rows";
 static NSString * const AngbandTerminalColumnsDefaultsKey = @"Columns";
 static NSString * const AngbandTerminalVisibleDefaultsKey = @"Visible";
 static NSString * const AngbandGraphicsDefaultsKey = @"GraphicsID";
+static NSString * const AngbandBigTileDefaultsKey = @"UseBigTiles";
 static NSString * const AngbandFrameRateDefaultsKey = @"FramesPerSecond";
 static NSString * const AngbandSoundDefaultsKey = @"AllowSound";
 static NSInteger const AngbandWindowMenuItemTagBase = 1000;
@@ -468,9 +469,8 @@ static int resize_pending_changes(struct PendingChanges* pc, int nrow)
 
 /*
  * Change the minimum size for the window associated with the context.
- * If termIdx is not negative, use it as the terminal index (that is useful
- * if self->terminal has not been set yet).  Otherwise, [self terminalIndex]
- * will be used as the index.
+ * termIdx is the index for the terminal:  pass it so this function can be
+ * used when self->terminal has not yet been set.
  */
 - (void)setMinimumWindowSize:(int)termIdx;
 
@@ -480,14 +480,6 @@ static int resize_pending_changes(struct PendingChanges* pc, int nrow)
 - (void)saveWindowVisibleToDefaults: (BOOL)windowVisible;
 - (BOOL)windowVisibleUsingDefaults;
 
-/* Class methods */
-
-/* Begins an Angband game. This is the entry point for starting off. */
-+ (void)beginGame;
-
-/* Ends an Angband game. */
-+ (void)endGame;
-
 /* Internal method */
 - (AngbandView *)activeView;
 
@@ -630,6 +622,10 @@ static Boolean game_in_progress = FALSE;
 static void wakeup_event_loop(void);
 static void hook_plog(const char *str);
 static void hook_quit(const char * str);
+static NSString* get_lib_directory(void);
+static NSString* get_doc_directory(void);
+static NSString* AngbandCorrectedDirectoryPath(NSString *originalPath);
+static void prepare_paths_and_directories(void);
 static void load_prefs(void);
 static void load_sounds(void);
 static void init_windows(void);
@@ -676,7 +672,7 @@ static bool initialized = FALSE;
 /* The NSView subclass that draws our Angband image */
 @interface AngbandView : NSView
 {
-    IBOutlet AngbandContext *angbandContext;
+    AngbandContext *angbandContext;
 }
 
 - (void)setAngbandContext:(AngbandContext *)context;
@@ -1096,7 +1092,7 @@ static int compare_advances(const void *ap, const void *bp)
                 * and rows since they could be changed */
         NSRect contentRect = [self->primaryWindow contentRectForFrameRect: [self->primaryWindow frame]];
 
-       [self setMinimumWindowSize:-1];
+       [self setMinimumWindowSize:[self terminalIndex]];
        NSSize size = self->primaryWindow.contentMinSize;
        BOOL windowNeedsResizing = NO;
        if (contentRect.size.width < size.width) {
@@ -1189,116 +1185,6 @@ static int compare_advances(const void *ap, const void *bp)
     [super dealloc];
 }
 
-
-
-#pragma mark -
-#pragma mark Directories and Paths Setup
-
-/**
- * Return the path for Angband's lib directory and bail if it isn't found. The
- * lib directory should be in the bundle's resources directory, since it's
- * copied when built.
- */
-+ (NSString *)libDirectoryPath
-{
-    NSString *bundleLibPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: AngbandDirectoryNameLib];
-    BOOL isDirectory = NO;
-    BOOL libExists = [[NSFileManager defaultManager] fileExistsAtPath: bundleLibPath isDirectory: &isDirectory];
-
-    if( !libExists || !isDirectory )
-    {
-        NSLog( @"[%@ %@]: can't find %@/ in bundle: isDirectory: %d libExists: %d", NSStringFromClass( [self class] ), NSStringFromSelector( _cmd ), AngbandDirectoryNameLib, isDirectory, libExists );
-
-       NSString *msg = NSLocalizedStringWithDefaultValue(
-           @"Error.MissingResources",
-           AngbandMessageCatalog,
-           [NSBundle mainBundle],
-           @"Missing Resources",
-           @"Alert text for missing resources");
-       NSString *info = NSLocalizedStringWithDefaultValue(
-           @"Error.MissingAngbandLib",
-           AngbandMessageCatalog,
-           [NSBundle mainBundle],
-           @"Hengband was unable to find required resources and must quit. Please report a bug on the Angband forums.",
-           @"Alert informative message for missing Angband lib/ folder");
-       NSString *quit_label = NSLocalizedStringWithDefaultValue(
-           @"Label.Quit", AngbandMessageCatalog, [NSBundle mainBundle],
-           @"Quit", @"Quit");
-       NSAlert *alert = [[NSAlert alloc] init];
-
-       /*
-        * Note that NSCriticalAlertStyle was deprecated in 10.10.  The
-        * replacement is NSAlertStyleCritical.
-        */
-       alert.alertStyle = NSCriticalAlertStyle;
-       alert.messageText = msg;
-       alert.informativeText = info;
-       [alert addButtonWithTitle:quit_label];
-       NSModalResponse result = [alert runModal];
-       [alert release];
-        exit( 0 );
-    }
-
-       return bundleLibPath;
-}
-
-/**
- * Return the path for the directory where Angband should look for its standard
- * user file tree.
- */
-+ (NSString *)angbandDocumentsPath
-{
-       NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
-
-#if defined(SAFE_DIRECTORY)
-       NSString *versionedDirectory = [NSString stringWithFormat: @"%@-%s", AngbandDirectoryNameBase, VERSION_STRING];
-       return [documents stringByAppendingPathComponent: versionedDirectory];
-#else
-       return [documents stringByAppendingPathComponent: AngbandDirectoryNameBase];
-#endif
-}
-
-/**
- * Adjust directory paths as needed to correct for any differences needed by
- * Angband. \c init_file_paths() currently requires that all paths provided have
- * a trailing slash and all other platforms honor this.
- *
- * \param originalPath The directory path to adjust.
- * \return A path suitable for Angband or nil if an error occurred.
- */
-static NSString *AngbandCorrectedDirectoryPath(NSString *originalPath)
-{
-       if ([originalPath length] == 0) {
-               return nil;
-       }
-
-       if (![originalPath hasSuffix: @"/"]) {
-               return [originalPath stringByAppendingString: @"/"];
-       }
-
-       return originalPath;
-}
-
-/**
- * Give Angband the base paths that should be used for the various directories
- * it needs. It will create any needed directories.
- */
-+ (void)prepareFilePathsAndDirectories
-{
-       char libpath[PATH_MAX + 1] = "\0";
-       NSString *libDirectoryPath = AngbandCorrectedDirectoryPath([self libDirectoryPath]);
-       [libDirectoryPath getFileSystemRepresentation: libpath maxLength: sizeof(libpath)];
-
-       char basepath[PATH_MAX + 1] = "\0";
-       NSString *angbandDocumentsPath = AngbandCorrectedDirectoryPath([self angbandDocumentsPath]);
-       [angbandDocumentsPath getFileSystemRepresentation: basepath maxLength: sizeof(basepath)];
-
-       init_file_paths(libpath, basepath);
-       create_needed_dirs();
-}
-
-#pragma mark -
-
 #if 0
 /* From the Linux mbstowcs(3) man page:
  *   If dest is NULL, n is ignored, and the conversion  proceeds  as  above,
@@ -1350,97 +1236,6 @@ static size_t Term_mbcs_cocoa(wchar_t *dest, const char *src, int n)
 }
 #endif
 
-/**
- * Entry point for initializing Angband
- */
-+ (void)beginGame
-{
-    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-    
-    /* Hooks in some "z-util.c" hooks */
-    plog_aux = hook_plog;
-    quit_aux = hook_quit;
-    
-    /* Initialize file paths */
-    [self prepareFilePathsAndDirectories];
-
-    /* Note the "system" */
-    ANGBAND_SYS = "coc";
-
-    /* Load preferences */
-    load_prefs();
-    
-    /* Load possible graphics modes */
-    init_graphics_modes();
-
-    /* Prepare the windows */
-    init_windows();
-    
-    /* Set up game event handlers */
-    /* init_display(); */
-    
-    /* Register the sound hook */
-    /* sound_hook = play_sound; */
-    
-    /* Initialise game */
-    init_angband();
-
-    /* Initialize some save file stuff */
-    player_egid = getegid();
-    
-    /* We are now initialized */
-    initialized = TRUE;
-    
-    /* Handle "open_when_ready" */
-    handle_open_when_ready();
-    
-    /* Handle pending events (most notably update) and flush input */
-    Term_flush();
-
-    /* Prompt the user. */
-    int message_row = (Term->hgt - 23) / 5 + 23;
-    Term_erase(0, message_row, 255);
-    put_str(
-#ifdef JP
-       "['ファイル' メニューから '新' または '開く' を選択します]",
-       message_row, (Term->wid - 57) / 2
-#else
-       "[Choose 'New' or 'Open' from the 'File' menu]",
-       message_row, (Term->wid - 45) / 2
-#endif
-    );
-    Term_fresh();
-
-    /*
-     * Play a game -- "new_game" is set by "new", "open" or the open document
-     * even handler as appropriate
-     */
-        
-    [pool drain];
-    
-    while (!game_in_progress) {
-        NSAutoreleasePool *splashScreenPool = [[NSAutoreleasePool alloc] init];
-        NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES];
-        if (event) [NSApp sendEvent:event];
-        [splashScreenPool drain];
-    }
-
-    Term_fresh();
-    play_game(new_game);
-
-    quit(NULL);
-}
-
-+ (void)endGame
-{    
-    /* Hack -- Forget messages */
-    msg_flag = FALSE;
-    
-    p_ptr->playing = FALSE;
-    p_ptr->leaving = TRUE;
-    quit_when_ready = TRUE;
-}
-
 - (void)addAngbandView:(AngbandView *)view
 {
     if (! [angbandViews containsObject:view])
@@ -1504,30 +1299,6 @@ static size_t Term_mbcs_cocoa(wchar_t *dest, const char *src, int n)
 }
 
 
-static NSMenuItem *superitem(NSMenuItem *self)
-{
-    NSMenu *supermenu = [[self menu] supermenu];
-    int index = [supermenu indexOfItemWithSubmenu:[self menu]];
-    if (index == -1) return nil;
-    else return [supermenu itemAtIndex:index];
-}
-
-
-- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
-{
-    int tag = [menuItem tag];
-    SEL sel = [menuItem action];
-    if (sel == @selector(setGraphicsMode:))
-    {
-        [menuItem setState: (tag == graf_mode_req)];
-        return YES;
-    }
-    else
-    {
-        return YES;
-    }
-}
-
 - (NSWindow *)makePrimaryWindow
 {
     if (! primaryWindow)
@@ -1686,9 +1457,6 @@ static NSMenuItem *superitem(NSMenuItem *self)
 {
     NSSize minsize;
 
-    if (termIdx < 0) {
-       termIdx = [self terminalIndex];
-    }
     if (termIdx == 0) {
        minsize.width = 80;
        minsize.height = 24;
@@ -2226,14 +1994,16 @@ static CGImageRef create_angband_image(NSString *path)
 
         /* Draw the source image flipped, since the view is flipped */
         CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, CGImageGetBitsPerComponent(decodedImage), CGImageGetBytesPerRow(decodedImage), CGImageGetColorSpace(decodedImage), contextBitmapInfo);
-        CGContextSetBlendMode(ctx, kCGBlendModeCopy);
-        CGContextTranslateCTM(ctx, 0.0, height);
-        CGContextScaleCTM(ctx, 1.0, -1.0);
-        CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), decodedImage);
-        result = CGBitmapContextCreateImage(ctx);
-
-        /* Done with these things */
-        CFRelease(ctx);
+       if (ctx) {
+           CGContextSetBlendMode(ctx, kCGBlendModeCopy);
+           CGContextTranslateCTM(ctx, 0.0, height);
+           CGContextScaleCTM(ctx, 1.0, -1.0);
+           CGContextDrawImage(
+               ctx, CGRectMake(0, 0, width, height), decodedImage);
+           result = CGBitmapContextCreateImage(ctx);
+           CFRelease(ctx);
+       }
+
         CGImageRelease(decodedImage);
     }
     return result;
@@ -2272,10 +2042,36 @@ static errr Term_xtra_cocoa_react(void)
             NSString *img_path = [NSString stringWithFormat:@"%s/%s", new_mode->path, new_mode->file];
             pict_image = create_angband_image(img_path);
 
-            /* If we failed to create the image, set the new desired mode to
-                        * NULL */
-            if (! pict_image)
+            /* If we failed to create the image, revert to ASCII. */
+            if (! pict_image) {
                 new_mode = NULL;
+               if (use_bigtile) {
+                   arg_bigtile = FALSE;
+               }
+               [[NSUserDefaults angbandDefaults]
+                   setInteger:GRAPHICS_NONE
+                   forKey:AngbandGraphicsDefaultsKey];
+               [[NSUserDefaults angbandDefaults] synchronize];
+
+               NSString *msg = NSLocalizedStringWithDefaultValue(
+                   @"Error.TileSetLoadFailed",
+                   AngbandMessageCatalog,
+                   [NSBundle mainBundle],
+                   @"Failed to Load Tile Set",
+                   @"Alert text for failed tile set load");
+               NSString *info = NSLocalizedStringWithDefaultValue(
+                   @"Error.TileSetRevertToASCII",
+                   AngbandMessageCatalog,
+                   [NSBundle mainBundle],
+                   @"Could not load the tile set.  Switched back to ASCII.",
+                   @"Alert informative message for failed tile set load");
+               NSAlert *alert = [[NSAlert alloc] init];
+
+               alert.messageText = msg;
+               alert.informativeText = info;
+               NSModalResponse result = [alert runModal];
+               [alert release];
+           }
         }
         
         /* Record what we did */
@@ -2301,12 +2097,20 @@ static errr Term_xtra_cocoa_react(void)
         }
         
         /* Reset visuals */
-        if (initialized && game_in_progress)
+        if (arg_bigtile == use_bigtile)
         {
             reset_visuals();
         }
     }
 
+    if (arg_bigtile != use_bigtile) {
+       /* Reset visuals */
+       reset_visuals();
+
+       Term_activate(angband_term[0]);
+       Term_resize(angband_term[0]->wid, angband_term[0]->hgt);
+    }
+
     [pool drain];
     
     /* Success */
@@ -2368,7 +2172,11 @@ static void query_before_text(
             */
            break;
        } else if (prc->cell_changes[i].change_type == CELL_CHANGE_NONE) {
-           /* It has not changed so inquire what it is. */
+           /*
+            * It has not changed (or using big tile mode and it is within
+            * a changed tile but is not the left cell for that tile) so
+            * inquire what it is.
+            */
            TERM_COLOR a[2];
            char c[2];
 
@@ -2381,6 +2189,17 @@ static void query_before_text(
                 */
                break;
            }
+           if (use_bigtile && i > 0) {
+               Term_what(i - 1, iy, a, c);
+               if (use_graphics && (a[0] & 0x80) && (c[0] & 0x80)) {
+                   /*
+                    * It is the right cell of a location rendered with a tile.
+                    * Do not want to modify its contents so the clipping and
+                    * rendering region can not be exteded.
+                    */
+                   break;
+               }
+           }
            /*
             * It is unchanged text.  A character from the changed region
             * may have extended into it so render it to clear that.
@@ -2613,6 +2432,7 @@ static void Term_xtra_cocoa_fresh(AngbandContext* angbandContext)
                    NSGraphicsContext *nsContext =
                        [NSGraphicsContext currentContext];
                    NSCompositingOperation op = nsContext.compositingOperation;
+                   int step = (use_bigtile) ? 2 : 1;
 
                    jx = ix;
                    while (jx <= prc->xmax &&
@@ -2622,6 +2442,7 @@ static void Term_xtra_cocoa_fresh(AngbandContext* angbandContext)
                            [angbandContext rectInImageForTileAtX:jx Y:iy];
                        NSRect sourceRect, terrainRect;
 
+                       destinationRect.size.width *= step;
                        sourceRect.origin.x = graf_width *
                            prc->cell_changes[jx].c.c;
                        sourceRect.origin.y = graf_height *
@@ -2665,7 +2486,7 @@ static void Term_xtra_cocoa_fresh(AngbandContext* angbandContext)
                                destinationRect,
                                NSCompositeCopy);
                        }
-                       ++jx;
+                       jx += step;
                    }
 
                    [nsContext setCompositingOperation:op];
@@ -2939,7 +2760,7 @@ static errr Term_xtra_cocoa(int n, int v)
     return result;
 }
 
-static errr Term_curs_cocoa(int x, int y)
+static errr Term_curs_cocoa(TERM_LEN x, TERM_LEN y)
 {
     AngbandContext *angbandContext = Term->data;
 
@@ -2960,7 +2781,7 @@ static errr Term_curs_cocoa(int x, int y)
  * the cursor points at a kanji character, irregardless of whether operating
  * in big tile mode.
  */
-static errr Term_bigcurs_cocoa(int x, int y)
+static errr Term_bigcurs_cocoa(TERM_LEN x, TERM_LEN y)
 {
     AngbandContext *angbandContext = Term->data;
 
@@ -2981,7 +2802,7 @@ static errr Term_bigcurs_cocoa(int x, int y)
  *
  * Erase "n" characters starting at (x,y)
  */
-static errr Term_wipe_cocoa(int x, int y, int n)
+static errr Term_wipe_cocoa(TERM_LEN x, TERM_LEN y, int n)
 {
     AngbandContext *angbandContext = Term->data;
     struct PendingCellChange *pc;
@@ -3022,9 +2843,9 @@ static errr Term_wipe_cocoa(int x, int y, int n)
     return (0);
 }
 
-static errr Term_pict_cocoa(int x, int y, int n, TERM_COLOR *ap,
-                            const char *cp, const TERM_COLOR *tap,
-                            const char *tcp)
+static errr Term_pict_cocoa(TERM_LEN x, TERM_LEN y, int n,
+                           TERM_COLOR *ap, concptr cp,
+                           const TERM_COLOR *tap, concptr tcp)
 {
     
     /* Paranoia: Bail if we don't have a current graphics mode */
@@ -3032,12 +2853,18 @@ static errr Term_pict_cocoa(int x, int y, int n, TERM_COLOR *ap,
     
     AngbandContext* angbandContext = Term->data;
     int any_change = 0;
+    int step = (use_bigtile) ? 2 : 1;
     struct PendingCellChange *pc;
 
     if (angbandContext->changes == 0) {
        /* Bail out; there was an earlier memory allocation failure. */
        return 1;
     }
+    /*
+     * In bigtile mode, it is sufficient that the bounds for the modified
+     * region only encompass the left cell for the region affected by the
+     * tile and that only that cell has to have the details of the changes.
+     */
     if (angbandContext->changes->rows[y] == 0) {
        angbandContext->changes->rows[y] =
            create_row_change(angbandContext->cols);
@@ -3056,12 +2883,12 @@ static errr Term_pict_cocoa(int x, int y, int n, TERM_COLOR *ap,
     if (angbandContext->changes->rows[y]->xmin > x) {
        angbandContext->changes->rows[y]->xmin = x;
     }
-    if (angbandContext->changes->rows[y]->xmax < x + n - 1) {
-       angbandContext->changes->rows[y]->xmax = x + n - 1;
+    if (angbandContext->changes->rows[y]->xmax < x + step * (n - 1)) {
+       angbandContext->changes->rows[y]->xmax = x + step * (n - 1);
     }
     for (pc = angbandContext->changes->rows[y]->cell_changes + x;
-        pc != angbandContext->changes->rows[y]->cell_changes + x + n;
-        ++pc) {
+        pc != angbandContext->changes->rows[y]->cell_changes + x + step * n;
+        pc += step) {
        TERM_COLOR a = *ap++;
        char c = *cp++;
        TERM_COLOR ta = *tap++;
@@ -3089,7 +2916,8 @@ static errr Term_pict_cocoa(int x, int y, int n, TERM_COLOR *ap,
  *
  * Draw several ("n") chars, with an attr, at a given location.
  */
-static errr Term_text_cocoa(int x, int y, int n, byte_hack a, concptr cp)
+static errr Term_text_cocoa(
+    TERM_LEN x, TERM_LEN y, int n, TERM_COLOR a, concptr cp)
 {
     AngbandContext* angbandContext = Term->data;
     struct PendingCellChange *pc;
@@ -3306,6 +3134,7 @@ static void load_prefs()
                               [NSNumber numberWithInt:60], AngbandFrameRateDefaultsKey,
                               [NSNumber numberWithBool:YES], AngbandSoundDefaultsKey,
                               [NSNumber numberWithInt:GRAPHICS_NONE], AngbandGraphicsDefaultsKey,
+                              [NSNumber numberWithBool:YES], AngbandBigTileDefaultsKey,
                               defaultTerms, AngbandTerminalsDefaultsKey,
                               nil];
     [defs registerDefaults:defaults];
@@ -3314,7 +3143,16 @@ static void load_prefs()
     
     /* Preferred graphics mode */
     graf_mode_req = [defs integerForKey:AngbandGraphicsDefaultsKey];
-    
+    if (graf_mode_req != GRAPHICS_NONE &&
+       get_graphics_mode(graf_mode_req)->grafID != GRAPHICS_NONE &&
+       [defs boolForKey:AngbandBigTileDefaultsKey] == YES) {
+       use_bigtile = TRUE;
+       arg_bigtile = TRUE;
+    } else {
+       use_bigtile = FALSE;
+       arg_bigtile = FALSE;
+    }
+
     /* Use sounds; set the Angband global */
     use_sound = ([defs boolForKey:AngbandSoundDefaultsKey] == YES) ? TRUE : FALSE;
     
@@ -3893,32 +3731,114 @@ static void hook_quit(const char * str)
 }
 
 /**
- * ------------------------------------------------------------------------
- * Main program
- * ------------------------------------------------------------------------ */
+ * Return the path for Angband's lib directory and bail if it isn't found. The
+ * lib directory should be in the bundle's resources directory, since it's
+ * copied when built.
+ */
+static NSString* get_lib_directory(void)
+{
+    NSString *bundleLibPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: AngbandDirectoryNameLib];
+    BOOL isDirectory = NO;
+    BOOL libExists = [[NSFileManager defaultManager] fileExistsAtPath: bundleLibPath isDirectory: &isDirectory];
+
+    if( !libExists || !isDirectory )
+    {
+       NSLog( @"Hengband: can't find %@/ in bundle: isDirectory: %d libExists: %d", AngbandDirectoryNameLib, isDirectory, libExists );
 
-@interface AngbandAppDelegate : NSObject {
-    IBOutlet NSMenu *terminalsMenu;
-    NSMenu *_graphicsMenu;
-    NSMenu *_commandMenu;
-    NSDictionary *_commandMenuTagMap;
+       NSString *msg = NSLocalizedStringWithDefaultValue(
+           @"Error.MissingResources",
+           AngbandMessageCatalog,
+           [NSBundle mainBundle],
+           @"Missing Resources",
+           @"Alert text for missing resources");
+       NSString *info = NSLocalizedStringWithDefaultValue(
+           @"Error.MissingAngbandLib",
+           AngbandMessageCatalog,
+           [NSBundle mainBundle],
+           @"Hengband was unable to find required resources and must quit. Please report a bug on the Angband forums.",
+           @"Alert informative message for missing Angband lib/ folder");
+       NSString *quit_label = NSLocalizedStringWithDefaultValue(
+           @"Label.Quit", AngbandMessageCatalog, [NSBundle mainBundle],
+           @"Quit", @"Quit");
+       NSAlert *alert = [[NSAlert alloc] init];
+
+       /*
+        * Note that NSCriticalAlertStyle was deprecated in 10.10.  The
+        * replacement is NSAlertStyleCritical.
+        */
+       alert.alertStyle = NSCriticalAlertStyle;
+       alert.messageText = msg;
+       alert.informativeText = info;
+       [alert addButtonWithTitle:quit_label];
+       NSModalResponse result = [alert runModal];
+       [alert release];
+       exit( 0 );
+    }
+
+    return bundleLibPath;
 }
 
-@property (nonatomic, retain) IBOutlet NSMenu *graphicsMenu;
-@property (nonatomic, retain) IBOutlet NSMenu *commandMenu;
-@property (nonatomic, retain) NSDictionary *commandMenuTagMap;
+/**
+ * Return the path for the directory where Angband should look for its standard
+ * user file tree.
+ */
+static NSString* get_doc_directory(void)
+{
+       NSString *documents = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
 
-- (IBAction)newGame:sender;
-- (IBAction)openGame:sender;
+#if defined(SAFE_DIRECTORY)
+       NSString *versionedDirectory = [NSString stringWithFormat: @"%@-%s", AngbandDirectoryNameBase, VERSION_STRING];
+       return [documents stringByAppendingPathComponent: versionedDirectory];
+#else
+       return [documents stringByAppendingPathComponent: AngbandDirectoryNameBase];
+#endif
+}
 
-- (IBAction)editFont:sender;
-- (IBAction)setGraphicsMode:(NSMenuItem *)sender;
-- (IBAction)toggleSound:(NSMenuItem *)sender;
+/**
+ * Adjust directory paths as needed to correct for any differences needed by
+ * Angband.  init_file_paths() currently requires that all paths provided have
+ * a trailing slash and all other platforms honor this.
+ *
+ * \param originalPath The directory path to adjust.
+ * \return A path suitable for Angband or nil if an error occurred.
+ */
+static NSString* AngbandCorrectedDirectoryPath(NSString *originalPath)
+{
+       if ([originalPath length] == 0) {
+               return nil;
+       }
 
-- (IBAction)setRefreshRate:(NSMenuItem *)menuItem;
-- (IBAction)selectWindow: (id)sender;
+       if (![originalPath hasSuffix: @"/"]) {
+               return [originalPath stringByAppendingString: @"/"];
+       }
 
-@end
+       return originalPath;
+}
+
+/**
+ * Give Angband the base paths that should be used for the various directories
+ * it needs. It will create any needed directories.
+ */
+static void prepare_paths_and_directories(void)
+{
+       char libpath[PATH_MAX + 1] = "\0";
+       NSString *libDirectoryPath =
+           AngbandCorrectedDirectoryPath(get_lib_directory());
+       [libDirectoryPath getFileSystemRepresentation: libpath maxLength: sizeof(libpath)];
+
+       char basepath[PATH_MAX + 1] = "\0";
+       NSString *angbandDocumentsPath =
+           AngbandCorrectedDirectoryPath(get_doc_directory());
+       [angbandDocumentsPath getFileSystemRepresentation: basepath maxLength: sizeof(basepath)];
+
+       init_file_paths(libpath, basepath);
+       create_needed_dirs();
+}
+
+/**
+ * ------------------------------------------------------------------------
+ * Main program
+ * ------------------------------------------------------------------------ */
 
 @implementation AngbandAppDelegate
 
@@ -4066,6 +3986,86 @@ static void hook_quit(const char * str)
 }
 
 /**
+ * Entry point for initializing Angband
+ */
+- (void)beginGame
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    /* Hooks in some "z-util.c" hooks */
+    plog_aux = hook_plog;
+    quit_aux = hook_quit;
+
+    /* Initialize file paths */
+    prepare_paths_and_directories();
+
+    /* Note the "system" */
+    ANGBAND_SYS = "coc";
+
+    /* Load possible graphics modes */
+    init_graphics_modes();
+
+    /* Load preferences */
+    load_prefs();
+
+    /* Prepare the windows */
+    init_windows();
+
+    /* Set up game event handlers */
+    /* init_display(); */
+
+    /* Register the sound hook */
+    /* sound_hook = play_sound; */
+
+    /* Initialise game */
+    init_angband();
+
+    /* Initialize some save file stuff */
+    player_egid = getegid();
+
+    /* We are now initialized */
+    initialized = TRUE;
+
+    /* Handle "open_when_ready" */
+    handle_open_when_ready();
+
+    /* Handle pending events (most notably update) and flush input */
+    Term_flush();
+
+    /* Prompt the user. */
+    int message_row = (Term->hgt - 23) / 5 + 23;
+    Term_erase(0, message_row, 255);
+    put_str(
+#ifdef JP
+       "['ファイル' メニューから '新' または '開く' を選択します]",
+       message_row, (Term->wid - 57) / 2
+#else
+       "[Choose 'New' or 'Open' from the 'File' menu]",
+       message_row, (Term->wid - 45) / 2
+#endif
+    );
+    Term_fresh();
+
+    [pool drain];
+
+    while (!game_in_progress) {
+        NSAutoreleasePool *splashScreenPool = [[NSAutoreleasePool alloc] init];
+        NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES];
+        if (event) [NSApp sendEvent:event];
+        [splashScreenPool drain];
+    }
+
+    /*
+     * Play a game -- "new_game" is set by "new", "open" or the open document
+     * even handler as appropriate
+     */
+    Term_fresh();
+    play_game(new_game);
+
+    quit(NULL);
+}
+
+/**
  * Implement NSObject's validateMenuItem() method to override enabling or
  * disabling a menu item.  Note that, as of 10.14, validateMenuItem() is
  * deprecated in NSObject - it will be removed at some point and  the
@@ -4112,7 +4112,8 @@ static void hook_quit(const char * str)
     {
         return ! game_in_progress;
     }
-    else if (sel == @selector(setRefreshRate:) && [superitem(menuItem) tag] == 150)
+    else if (sel == @selector(setRefreshRate:) &&
+            [[menuItem parentItem] tag] == 150)
     {
         NSInteger fps = [[NSUserDefaults standardUserDefaults] integerForKey:AngbandFrameRateDefaultsKey];
         [menuItem setState: ([menuItem tag] == fps)];
@@ -4151,7 +4152,7 @@ static void hook_quit(const char * str)
     [[NSUserDefaults angbandDefaults] setInteger:frames_per_second forKey:AngbandFrameRateDefaultsKey];
 }
 
-- (IBAction)selectWindow: (id)sender
+- (void)selectWindow: (id)sender
 {
     NSInteger subwindowNumber = [(NSMenuItem *)sender tag] - AngbandWindowMenuItemTagBase;
     AngbandContext *context = angband_term[subwindowNumber]->data;
@@ -4198,7 +4199,7 @@ static void hook_quit(const char * str)
     }
 }
 
-- (IBAction)setGraphicsMode:(NSMenuItem *)sender
+- (void)setGraphicsMode:(NSMenuItem *)sender
 {
     /* We stashed the graphics mode ID in the menu item's tag */
     graf_mode_req = [sender tag];
@@ -4207,8 +4208,23 @@ static void hook_quit(const char * str)
     [[NSUserDefaults angbandDefaults] setInteger:graf_mode_req forKey:AngbandGraphicsDefaultsKey];
     [[NSUserDefaults angbandDefaults] synchronize];
     
+    if (graf_mode_req == GRAPHICS_NONE ||
+       get_graphics_mode(graf_mode_req) == GRAPHICS_NONE) {
+       if (use_bigtile) {
+           arg_bigtile = FALSE;
+       }
+    } else if ([[NSUserDefaults angbandDefaults] boolForKey:AngbandBigTileDefaultsKey] == YES &&
+              ! use_bigtile) {
+       arg_bigtile = TRUE;
+    }
+
     if (game_in_progress)
     {
+       if (arg_bigtile != use_bigtile) {
+           Term_activate(angband_term[0]);
+           Term_resize(angband_term[0]->wid, angband_term[0]->hgt);
+       }
+
         /* Hack -- Force redraw */
         do_cmd_redraw();
         
@@ -4228,6 +4244,27 @@ static void hook_quit(const char * str)
                                      forKey:AngbandSoundDefaultsKey];
 }
 
+- (IBAction)toggleWideTiles:(NSMenuItem *) sender
+{
+    BOOL is_on = (sender.state == NSOnState);
+
+    /* Toggle the state and update the Angband globals and preferences. */
+    sender.state = (is_on) ? NSOffState : NSOnState;
+    [[NSUserDefaults angbandDefaults] setBool:(! is_on)
+                                     forKey:AngbandBigTileDefaultsKey];
+    [[NSUserDefaults angbandDefaults] synchronize];
+    if (graphics_are_enabled()) {
+       arg_bigtile = (is_on) ? FALSE : TRUE;
+       /* Mimics the logic in setGraphicsMode(). */
+       if (game_in_progress && arg_bigtile != use_bigtile) {
+           Term_activate(angband_term[0]);
+           Term_resize(angband_term[0]->wid, angband_term[0]->hgt);
+           do_cmd_redraw();
+           wakeup_event_loop();
+       }
+    }
+}
+
 /**
  *  Send a command to Angband via a menu item. This places the appropriate key
  * down events into the queue so that it seems like the user pressed them
@@ -4319,7 +4356,7 @@ static void hook_quit(const char * str)
 
 - (void)applicationDidFinishLaunching:sender
 {
-    [AngbandContext beginGame];
+    [self beginGame];
     
     /* Once beginGame finished, the game is over - that's how Angband works,
         * and we should quit */
@@ -4408,27 +4445,39 @@ static void hook_quit(const char * str)
 /**
  * Delegate method that gets called if we're asked to open a file.
  */
-- (BOOL)application:(NSApplication *)sender openFiles:(NSArray *)filenames
+- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames
 {
     /* Can't open a file once we've started */
-    if (game_in_progress) return NO;
-    
+    if (game_in_progress) {
+       [[NSApplication sharedApplication]
+           replyToOpenOrPrint:NSApplicationDelegateReplyFailure];
+       return;
+    }
+
     /* We can only open one file. Use the last one. */
     NSString *file = [filenames lastObject];
-    if (! file) return NO;
-    
+    if (! file) {
+       [[NSApplication sharedApplication]
+           replyToOpenOrPrint:NSApplicationDelegateReplyFailure];
+       return;
+    }
+
     /* Put it in savefile */
-    if (! [file getFileSystemRepresentation:savefile maxLength:sizeof savefile])
-               return NO;
-    
+    if (! [file getFileSystemRepresentation:savefile maxLength:sizeof savefile]) {
+       [[NSApplication sharedApplication]
+           replyToOpenOrPrint:NSApplicationDelegateReplyFailure];
+       return;
+    }
+
     game_in_progress = TRUE;
     new_game = FALSE;
 
     /* Wake us up in case this arrives while we're sitting at the Welcome
         * screen! */
     wakeup_event_loop();
-    
-    return YES;
+
+    [[NSApplication sharedApplication]
+       replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
 }
 
 @end