ample/MA2ME/AppDelegate.m

380 lines
11 KiB
Mathematica
Raw Normal View History

2020-08-16 16:53:17 +00:00
//
// AppDelegate.m
// MA2ME
//
// Created by Kelvin Sherlock on 8/16/2020.
// Copyright © 2020 Kelvin Sherlock. All rights reserved.
//
#import "AppDelegate.h"
2020-08-20 00:26:44 +00:00
#import "SlotViewController.h"
2020-08-21 22:38:02 +00:00
#import "MediaViewController.h"
2020-08-16 20:23:51 +00:00
2020-08-16 16:53:17 +00:00
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
2020-08-20 00:26:44 +00:00
@property (strong) IBOutlet SlotViewController *slotController;
2020-08-21 22:38:02 +00:00
@property (strong) IBOutlet MediaViewController *mediaController;
2020-08-20 00:26:44 +00:00
2020-08-16 20:23:51 +00:00
2020-08-20 00:26:44 +00:00
@property (weak) IBOutlet NSView *modelView;
@property (weak) IBOutlet NSView *slotView;
2020-08-21 22:38:02 +00:00
@property (weak) IBOutlet NSView *mediaView;
2020-08-16 20:23:51 +00:00
/* kvo */
@property NSString *commandLine;
2020-08-26 03:22:08 +00:00
@property NSArray *args;
2020-08-16 20:23:51 +00:00
@property NSString *mameROM;
@property BOOL mameWindow;
@property BOOL mameNoThrottle;
@property BOOL mameDebug;
@property BOOL mameSquarePixels;
@property NSArray *browserItems;
2020-08-16 16:53:17 +00:00
@end
2020-08-29 18:40:34 +00:00
@implementation AppDelegate {
NSWindowController *_prefs;
}
2020-08-16 16:53:17 +00:00
2020-08-16 20:23:51 +00:00
static NSString *kMyContext = @"kMyContext";
2020-08-16 16:53:17 +00:00
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
2020-08-16 20:23:51 +00:00
2020-08-29 18:40:34 +00:00
NSBundle *bundle = [NSBundle mainBundle];
NSString *path;
NSDictionary *dict;
path = [bundle pathForResource: @"Defaults" ofType: @"plist"];
dict = [NSDictionary dictionaryWithContentsOfFile: path];
if (dict)
{
[[NSUserDefaults standardUserDefaults] registerDefaults: dict];
}
2020-08-16 20:23:51 +00:00
/* My Copy of XCode/Interface Builder barfs on NSBrowser. */
2020-08-29 18:40:34 +00:00
path = [bundle pathForResource: @"models" ofType: @"plist"];
2020-08-16 20:23:51 +00:00
_browserItems = [NSArray arrayWithContentsOfFile: path];
NSView *view = [_window contentView];
2020-08-20 00:26:44 +00:00
NSRect frame;
NSBrowser *browser = nil;
frame = [_modelView frame];
browser = [[NSBrowser alloc] initWithFrame: frame];
2020-08-16 20:23:51 +00:00
[browser setMaxVisibleColumns: 2];
2020-08-26 02:23:08 +00:00
//[browser setTakesTitleFromPreviousColumn: YES];
//[browser setTitled: NO];
2020-08-20 00:26:44 +00:00
[browser setAllowsEmptySelection: NO];
2020-08-16 20:23:51 +00:00
[browser setDelegate: self];
[browser setAction: @selector(modelClick:)];
2020-08-20 00:26:44 +00:00
[view addSubview: browser];
2020-08-26 02:23:08 +00:00
//[browser setTitled: YES]; // NSBrowser title bug.
2020-08-20 00:26:44 +00:00
#if 0
frame = [_slotView frame];
browser = [[NSBrowser alloc] initWithFrame: frame];
2020-08-16 20:23:51 +00:00
2020-08-20 00:26:44 +00:00
[browser setMaxVisibleColumns: 2];
[browser setTakesTitleFromPreviousColumn: YES];
[browser setTitled: NO];
[browser setDelegate: _slotDelegate];
//[browser setAction: @selector(modelClick:)];
2020-08-16 20:23:51 +00:00
[view addSubview: browser];
[browser setTitled: YES]; // NSBrowser title bug.
2020-08-20 00:26:44 +00:00
[_slotDelegate setBrowser: browser];
#endif
[_slotView addSubview: [_slotController view]];
2020-08-21 22:38:02 +00:00
[_mediaView addSubview: [_mediaController view]];
2020-08-20 00:26:44 +00:00
2020-08-16 20:23:51 +00:00
[self addObserver: self forKeyPath: @"mameROM" options:0 context: (__bridge void * _Nullable)(kMyContext)];
[self addObserver: self forKeyPath: @"mameWindow" options:0 context: (__bridge void * _Nullable)(kMyContext)];
[self addObserver: self forKeyPath: @"mameSquarePixels" options:0 context: (__bridge void * _Nullable)(kMyContext)];
[self addObserver: self forKeyPath: @"mameDebug" options:0 context: (__bridge void * _Nullable)(kMyContext)];
[self addObserver: self forKeyPath: @"mameNoThrottle" options:0 context: (__bridge void * _Nullable)(kMyContext)];
2020-08-21 22:38:02 +00:00
[_slotController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)];
2020-08-26 00:13:37 +00:00
[_mediaController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)];
2020-08-25 04:35:40 +00:00
[_mediaController bind: @"media" toObject: _slotController withKeyPath: @"media" options: 0];
2020-08-16 20:23:51 +00:00
[self buildCommandLine];
2020-08-16 16:53:17 +00:00
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
2020-08-17 01:22:45 +00:00
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
return YES;
}
2020-08-16 16:53:17 +00:00
2020-08-16 20:23:51 +00:00
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
2020-08-21 22:38:02 +00:00
if (context == (__bridge void *)kMyContext) {
2020-08-16 20:23:51 +00:00
[self buildCommandLine];
} else {
[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
}
}
static NSString * JoinArguments(NSArray *argv) {
static NSCharacterSet *safe = nil;
static NSCharacterSet *unsafe = nil;
if (!safe) {
NSString *str =
@"%+-./:=_"
@"0123456789"
@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
;
safe = [NSCharacterSet characterSetWithCharactersInString: str];
unsafe = [safe invertedSet];
}
NSMutableString *rv = [NSMutableString new];
2020-08-26 03:22:08 +00:00
//unsigned ix = 0;
[rv appendString: @"mame"];
for (NSString *s in argv) {
2020-08-26 03:22:08 +00:00
[rv appendString: @" "];
NSUInteger l = [s length];
if (!l) {
[rv appendString: @"''"];
continue;
}
if (!CFStringFindCharacterFromSet((CFStringRef)s, (CFCharacterSetRef)unsafe, CFRangeMake(0, l), 0, NULL)) {
[rv appendString: s];
continue;
}
unichar *buffer = malloc(sizeof(unichar) * l);
[s getCharacters: buffer range: NSMakeRange(0, l)];
[rv appendString: @"'"];
for (NSUInteger i = 0; i < l; ++i) {
unichar c = buffer[i];
switch (c) {
case '\'':
[rv appendString: @"\\'"];
break;
case '\\':
[rv appendString: @"\\\\"];
break;
case 0x7f:
[rv appendString: @"\\177"];
break;
default: {
NSString *cc;
if (c < 0x20) {
cc = [NSString stringWithFormat: @"\\%o", c];
} else {
cc = [NSString stringWithCharacters: &c length: 1];
}
[rv appendString: cc];
break;
}
}
}
[rv appendString: @"'"];
free(buffer);
}
return rv;
}
2020-08-16 20:23:51 +00:00
-(void)buildCommandLine {
if (!_mameROM) {
[self setCommandLine: @""];
return;
}
NSMutableArray *argv = [NSMutableArray new];
2020-08-26 03:22:08 +00:00
//[argv addObject: @"mame"];
2020-08-16 20:23:51 +00:00
[argv addObject: _mameROM];
2020-08-17 01:22:45 +00:00
if (_mameDebug) [argv addObject: @"-debug"];
if (_mameWindow) [argv addObject: @"-window"];
// -nounevenstretch -video soft
2020-08-21 22:38:02 +00:00
[argv addObject: @"-skip_gameinfo"];
2020-08-17 01:22:45 +00:00
2020-08-21 22:38:02 +00:00
if (_mameWindow && _mameSquarePixels) {
NSSize screen = [_slotController resolution];
NSString *res = [NSString stringWithFormat: @"%ux%u", (unsigned)screen.width, (unsigned)screen.height];
2020-08-26 02:23:08 +00:00
NSString *aspect = [NSString stringWithFormat: @"%u:%u", (unsigned)screen.width, (unsigned)screen.height];
2020-08-21 22:38:02 +00:00
2020-08-20 00:26:44 +00:00
[argv addObject: @"-nomax"];
2020-08-21 22:38:02 +00:00
[argv addObject: @"-nounevenstretch"];
2020-08-17 01:22:45 +00:00
if ([_mameROM hasPrefix: @"apple2gs"]) {
[argv addObject: @"-resolution"];
2020-08-21 22:38:02 +00:00
[argv addObject: res]; // @"704x462"];
2020-08-17 01:22:45 +00:00
[argv addObject: @"-video"];
[argv addObject: @"soft"];
[argv addObject: @"-aspect"];
2020-08-26 02:23:08 +00:00
[argv addObject: aspect];
2020-08-17 01:22:45 +00:00
} else {
[argv addObject: @"-resolution"];
2020-08-21 22:38:02 +00:00
[argv addObject: res]; // @"560x384"];
2020-08-17 01:22:45 +00:00
}
2020-08-16 20:23:51 +00:00
}
2020-08-26 03:22:08 +00:00
// -speed n
// -scale n
2020-08-22 19:55:16 +00:00
2020-08-26 03:22:08 +00:00
NSArray *tmp;
tmp = [_slotController args];
if ([tmp count]) {
[argv addObjectsFromArray: tmp];
2020-08-21 22:38:02 +00:00
}
2020-08-26 03:22:08 +00:00
tmp = [_mediaController args];
if ([tmp count]) {
[argv addObjectsFromArray: tmp];
2020-08-26 00:13:37 +00:00
}
2020-08-17 01:22:45 +00:00
if (_mameNoThrottle) [argv addObject: @"-nothrottle"];
[self setCommandLine: JoinArguments(argv)]; //[argv componentsJoinedByString:@" "]];
2020-08-26 03:22:08 +00:00
[self setArgs: argv];
2020-08-16 20:23:51 +00:00
}
-(IBAction)modelClick:(id)sender {
NSDictionary *item = [self itemForBrowser: sender];
2020-08-26 02:23:08 +00:00
NSString *model = [item objectForKey: @"value"];
2020-08-20 00:26:44 +00:00
[self setMameROM: model];
2020-08-20 04:00:56 +00:00
2020-08-16 20:23:51 +00:00
// [self buildCommandLine];
2020-08-20 00:26:44 +00:00
2020-08-20 04:00:56 +00:00
[_slotController setModel: model];
2020-08-16 20:23:51 +00:00
}
#pragma mark NSBrowser
-(NSDictionary *)itemForBrowser: (NSBrowser *)browser {
NSIndexPath *path = [browser selectionIndexPath];
NSArray *a = _browserItems;
NSDictionary *item = nil;
NSUInteger l = [path length];
for (NSUInteger i = 0; i < l; ++i) {
NSUInteger ix = [path indexAtPosition: i];
if (ix > [a count]) return nil;
item = [a objectAtIndex: ix];
2020-08-26 02:23:08 +00:00
a = [item objectForKey: @"children"];
2020-08-16 20:23:51 +00:00
}
return item;
}
-(NSArray *)itemsForBrowser: (NSBrowser *)browser column: (NSInteger) column {
NSArray *a = _browserItems;
for (unsigned i = 0; i < column; ++i) {
NSInteger ix = [browser selectedRowInColumn: i];
if (ix < 0) return 0;
NSDictionary *item = [a objectAtIndex: ix];
2020-08-26 02:23:08 +00:00
a = [item objectForKey: @"children"];
2020-08-16 20:23:51 +00:00
if (!a) return 0;
}
return a;
}
- (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(NSInteger)row column:(NSInteger)column {
NSArray *a = [self itemsForBrowser: sender column: column];
if (!a || row >= [a count]) return;
NSDictionary *item = [a objectAtIndex: row];
NSBrowserCell *bc = (NSBrowserCell *)cell;
2020-08-26 02:23:08 +00:00
[bc setStringValue: [item objectForKey: @"description"]];
[bc setLeaf: ![item objectForKey: @"children"]];
2020-08-16 20:23:51 +00:00
}
- (NSString *)browser:(NSBrowser *)sender titleOfColumn:(NSInteger)column {
return column == 0 ? @"Model" : @"Submodel";
}
2020-08-20 00:26:44 +00:00
#if 0
2020-08-16 20:23:51 +00:00
- (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item {
return nil;
}
2020-08-20 00:26:44 +00:00
-(id)rootItemForBrowser:(NSBrowser *)browser {
return _browserItems;
}
#endif
2020-08-16 20:23:51 +00:00
- (NSInteger)browser:(NSBrowser *)sender numberOfRowsInColumn:(NSInteger)column {
NSArray *a = [self itemsForBrowser: sender column: column];
return [a count];
}
2020-08-26 03:22:08 +00:00
#pragma mark - IBActions
- (IBAction)launchAction:(id)sender {
if (![_args count]) return;
2020-08-29 18:40:34 +00:00
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *path = [defaults stringForKey: @"MamePath"];
if (![path length]) path = @"/usr/local/bin/mame";
2020-08-26 03:22:08 +00:00
NSError *error = nil;
2020-08-29 18:40:34 +00:00
NSURL *url = [NSURL fileURLWithPath: path];
2020-08-26 03:22:08 +00:00
NSTask *task = [NSTask launchedTaskWithExecutableURL: url
arguments: _args
error: &error
terminationHandler: ^(NSTask *t){
}];
if (error) NSLog(@"launchAction: %@", error);
}
2020-08-29 18:40:34 +00:00
- (IBAction)displayPreferences:(id)sender {
if (!_prefs) {
_prefs = [[NSWindowController alloc] initWithWindowNibName: @"Preferences"];
}
[_prefs showWindow: sender];
}
2020-08-16 20:23:51 +00:00
2020-08-20 00:26:44 +00:00
2020-08-16 16:53:17 +00:00
@end