summaryrefslogtreecommitdiff
path: root/macosx/Source
diff options
context:
space:
mode:
authorSND\MaddTheSane_cp <SND\MaddTheSane_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97>2014-07-20 05:09:43 +0000
committerSND\MaddTheSane_cp <SND\MaddTheSane_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97>2014-07-20 05:09:43 +0000
commitd6942932d64a02aa92b1e04e91f6126f33fdb05e (patch)
tree7cad698308e39abc2b0e1c71674c610ec3ce74dd /macosx/Source
parentb8d0d24d56dbc0ee64f4ec9a72ab917604d8109d (diff)
OS X: Move source files to their own folder.
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@90999 e17a0e51-4ae3-4d35-97c3-1a29b211df97
Diffstat (limited to 'macosx/Source')
-rw-r--r--macosx/Source/CheatController.h53
-rw-r--r--macosx/Source/CheatController.m324
-rw-r--r--macosx/Source/ConfigurationController.h57
-rw-r--r--macosx/Source/ConfigurationController.m291
-rw-r--r--macosx/Source/EmuThread.h40
-rw-r--r--macosx/Source/EmuThread.m396
-rw-r--r--macosx/Source/ExtendedKeys.h28
-rw-r--r--macosx/Source/HotkeyController.h25
-rw-r--r--macosx/Source/HotkeyController.m211
-rw-r--r--macosx/Source/LaunchArg.h25
-rw-r--r--macosx/Source/LaunchArg.m51
-rw-r--r--macosx/Source/MemBadgeView.h14
-rw-r--r--macosx/Source/MemBadgeView.m51
-rw-r--r--macosx/Source/OSXPlugLocalization.h21
-rw-r--r--macosx/Source/PcsxrCheatHandler.h14
-rw-r--r--macosx/Source/PcsxrCheatHandler.m36
-rw-r--r--macosx/Source/PcsxrController.h53
-rw-r--r--macosx/Source/PcsxrController.m974
-rw-r--r--macosx/Source/PcsxrDiscHandler.h14
-rw-r--r--macosx/Source/PcsxrDiscHandler.m58
-rw-r--r--macosx/Source/PcsxrFileHandle.h14
-rw-r--r--macosx/Source/PcsxrFreezeStateHandler.h14
-rw-r--r--macosx/Source/PcsxrFreezeStateHandler.m43
-rw-r--r--macosx/Source/PcsxrHexadecimalFormatter.h14
-rw-r--r--macosx/Source/PcsxrHexadecimalFormatter.m88
-rw-r--r--macosx/Source/PcsxrMemCardArray.h40
-rw-r--r--macosx/Source/PcsxrMemCardArray.m345
-rw-r--r--macosx/Source/PcsxrMemCardController.h30
-rw-r--r--macosx/Source/PcsxrMemCardController.m238
-rw-r--r--macosx/Source/PcsxrMemCardHandler.h15
-rw-r--r--macosx/Source/PcsxrMemCardHandler.m81
-rw-r--r--macosx/Source/PcsxrMemoryObject.h45
-rw-r--r--macosx/Source/PcsxrMemoryObject.m323
-rw-r--r--macosx/Source/PcsxrPlugin.h32
-rw-r--r--macosx/Source/PcsxrPlugin.m404
-rw-r--r--macosx/Source/PcsxrPluginHandler.h15
-rw-r--r--macosx/Source/PcsxrPluginHandler.m97
-rw-r--r--macosx/Source/Plugin.c227
-rw-r--r--macosx/Source/PluginController.h18
-rw-r--r--macosx/Source/PluginController.m83
-rw-r--r--macosx/Source/PluginList.h28
-rw-r--r--macosx/Source/PluginList.m318
-rw-r--r--macosx/Source/RecentItemsMenu.h22
-rw-r--r--macosx/Source/RecentItemsMenu.m114
-rw-r--r--macosx/Source/config.h32
-rw-r--r--macosx/Source/hotkeys.h15
-rw-r--r--macosx/Source/hotkeys.m141
-rw-r--r--macosx/Source/main.m311
48 files changed, 5883 insertions, 0 deletions
diff --git a/macosx/Source/CheatController.h b/macosx/Source/CheatController.h
new file mode 100644
index 00000000..c56f2a68
--- /dev/null
+++ b/macosx/Source/CheatController.h
@@ -0,0 +1,53 @@
+//
+// CheatController.h
+// Pcsxr
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PcsxrHexadecimalFormatter.h"
+#include "psxcommon.h"
+#include "cheat.h"
+
+@interface PcsxrCheatTempObject : NSObject <NSCopying>
+@property (readwrite) uint32_t cheatAddress;
+@property (readwrite) uint16_t cheatValue;
+
+- (instancetype)initWithAddress:(uint32_t)add value:(uint16_t)val;
+- (instancetype)initWithCheatCode:(CheatCode *)theCheat;
+@end
+
+@interface PcsxrCheatTemp : NSObject
+@property (readwrite, strong) NSMutableArray *cheatValues;
+@property (readwrite, strong) NSString *cheatName;
+@property (readwrite, getter = isEnabled) BOOL enabled;
+
+- (instancetype)initWithCheat:(Cheat *)theCheat;
+@end
+
+@interface CheatController : NSWindowController <NSWindowDelegate, NSTableViewDelegate>
+
+@property (weak) IBOutlet NSTableView *cheatView;
+@property (weak) IBOutlet NSWindow *editCheatWindow;
+@property (weak) IBOutlet NSTableView *editCheatView;
+@property (weak) IBOutlet PcsxrHexadecimalFormatter *addressFormatter;
+@property (weak) IBOutlet PcsxrHexadecimalFormatter *valueFormatter;
+
+@property (readwrite, strong) NSMutableArray *tempCheatCodes;
+@property (readwrite, strong) NSMutableArray *cheats;
+
+- (void)refresh;
+
+- (IBAction)saveCheats:(id)sender;
+- (IBAction)loadCheats:(id)sender;
+- (IBAction)clear:(id)sender;
+- (IBAction)editCheat:(id)sender;
+- (IBAction)addCheat:(id)sender;
+- (IBAction)applyCheats:(id)sender;
+- (IBAction)removeCheats:(id)sender;
+- (IBAction)changeCheat:(id)sender;
+
+- (IBAction)closeCheatEdit:(id)sender;
+- (IBAction)removeCheatValue:(id)sender;
+- (IBAction)addCheatValue:(id)sender;
+
+@end
diff --git a/macosx/Source/CheatController.m b/macosx/Source/CheatController.m
new file mode 100644
index 00000000..ebe73137
--- /dev/null
+++ b/macosx/Source/CheatController.m
@@ -0,0 +1,324 @@
+//
+// CheatController.m
+// Pcsxr
+//
+
+#import <Cocoa/Cocoa.h>
+#import "CheatController.h"
+#import "PcsxrCheatHandler.h"
+#import "PcsxrHexadecimalFormatter.h"
+
+#define kTempCheatCodesName @"tempCheatCodes"
+#define kCheatsName @"cheats"
+
+@implementation PcsxrCheatTempObject
+@synthesize cheatAddress, cheatValue;
+
+- (instancetype)init
+{
+ return [self initWithAddress:0x10000000 value:0];
+}
+
+- (instancetype)initWithAddress:(uint32_t)add value:(uint16_t)val
+{
+ if (self = [super init]) {
+ self.cheatAddress = add;
+ self.cheatValue = val;
+ }
+ return self;
+}
+
+- (instancetype)initWithCheatCode:(CheatCode *)theCheat
+{
+ return [self initWithAddress:theCheat->Addr value:theCheat->Val];
+}
+
+- (NSString*)description
+{
+ return [NSString stringWithFormat:@"%08x %04x", cheatAddress, cheatValue];
+}
+
+- (BOOL)isEqual:(id)object
+{
+ if ([object isKindOfClass:[PcsxrCheatTempObject class]]) {
+ if (cheatAddress != [object cheatAddress]) {
+ return NO;
+ } else if (cheatValue != [object cheatValue]) {
+ return NO;
+ } else
+ return YES;
+ } else
+ return NO;
+}
+
+- (NSUInteger)hash
+{
+ return cheatAddress ^ cheatValue;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+ return [[[self class] allocWithZone:zone] initWithAddress:cheatAddress value:cheatValue];
+}
+
+@end
+
+@implementation PcsxrCheatTemp
+@synthesize cheatName;
+@synthesize cheatValues;
+@synthesize enabled;
+
+- (instancetype)initWithCheat:(Cheat *)theCheat
+{
+ if (self = [super init]) {
+ self.cheatName = @(theCheat->Descr);
+ self.enabled = theCheat->Enabled ? YES : NO;
+ self.cheatValues = [NSMutableArray arrayWithCapacity:theCheat->n];
+ for (int i = 0; i < theCheat->n; i++) {
+ [cheatValues addObject:[[PcsxrCheatTempObject alloc] initWithCheatCode:&CheatCodes[i+theCheat->First]]];
+ }
+ }
+ return self;
+}
+
+- (NSUInteger)hash
+{
+ return [cheatName hash] ^ [cheatValues hash];
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"[%@%@]\n%@", enabled ? @"*" : @"", cheatName, [cheatValues componentsJoinedByString:@"\n"]];
+}
+
+@end
+
+@implementation CheatController
+@synthesize addressFormatter;
+@synthesize cheatView;
+@synthesize editCheatView;
+@synthesize editCheatWindow;
+@synthesize valueFormatter;
+
+- (NSString *)windowNibName
+{
+ return @"CheatWindow";
+}
+
+- (instancetype)init
+{
+ return self = [self initWithWindowNibName:@"CheatWindow"];
+}
+
+- (instancetype)initWithCoder:(NSCoder *)aDecoder
+{
+ if (self = [super initWithCoder:aDecoder]) {
+ self.tempCheatCodes = [NSMutableArray array];
+ }
+ return self;
+}
+
+- (instancetype)initWithWindow:(NSWindow *)window
+{
+ if (self = [super initWithWindow:window]) {
+ self.tempCheatCodes = [NSMutableArray array];
+ }
+ return self;
+}
+
+- (void)refreshNSCheatArray
+{
+ NSMutableArray *tmpArray = [[NSMutableArray alloc] initWithCapacity:NumCheats];
+ for (int i = 0; i < NumCheats; i++) {
+ PcsxrCheatTemp *tmpObj = [[PcsxrCheatTemp alloc] initWithCheat:&Cheats[i]];
+ [tmpArray addObject:tmpObj];
+ }
+ self.cheats = tmpArray;
+ [self setDocumentEdited:NO];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if ([keyPath isEqualToString:kCheatsName]) {
+ [self setDocumentEdited:YES];
+ }
+}
+
+- (void)refresh
+{
+ [cheatView reloadData];
+ [self refreshNSCheatArray];
+}
+
+- (void)awakeFromNib
+{
+ [valueFormatter setHexPadding:4];
+ [addressFormatter setHexPadding:8];
+ [self refreshNSCheatArray];
+ [self addObserver:self forKeyPath:kCheatsName options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
+}
+
+- (IBAction)loadCheats:(id)sender
+{
+ NSOpenPanel *openDlg = [NSOpenPanel openPanel];
+ [openDlg setAllowsMultipleSelection:NO];
+ [openDlg setAllowedFileTypes:[PcsxrCheatHandler supportedUTIs]];
+
+ if ([openDlg runModal] == NSFileHandlingPanelOKButton) {
+ NSURL *file = [openDlg URL];
+ LoadCheats([[file path] fileSystemRepresentation]);
+ [self refresh];
+ }
+}
+
+- (IBAction)saveCheats:(id)sender
+{
+ NSSavePanel *saveDlg = [NSSavePanel savePanel];
+ [saveDlg setAllowedFileTypes:[PcsxrCheatHandler supportedUTIs]];
+ [saveDlg setCanSelectHiddenExtension:YES];
+ [saveDlg setCanCreateDirectories:YES];
+ [saveDlg setPrompt:NSLocalizedString(@"Save Cheats", nil)];
+ if ([saveDlg runModal] == NSFileHandlingPanelOKButton) {
+ NSURL *url = [saveDlg URL];
+ NSString *saveString = [self.cheats componentsJoinedByString:@"\n"];
+ [saveString writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+ }
+}
+
+- (IBAction)clear:(id)sender
+{
+ self.cheats = [[NSMutableArray alloc] init];
+}
+
+- (IBAction)closeCheatEdit:(id)sender
+{
+ [NSApp endSheet:editCheatWindow returnCode:[sender tag] == 1 ? NSCancelButton : NSOKButton];
+}
+
+- (IBAction)changeCheat:(id)sender
+{
+ [self setDocumentEdited:YES];
+}
+
+- (IBAction)removeCheatValue:(id)sender
+{
+ if ([editCheatView selectedRow] < 0) {
+ NSBeep();
+ return;
+ }
+
+ NSIndexSet *toRemoveIndex = [editCheatView selectedRowIndexes];
+ [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kTempCheatCodesName];
+ [self.tempCheatCodes removeObjectsAtIndexes:toRemoveIndex];
+ [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kTempCheatCodesName];
+}
+
+- (IBAction)addCheatValue:(id)sender
+{
+ NSIndexSet *newSet = [NSIndexSet indexSetWithIndex:[self.tempCheatCodes count]];
+ [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kTempCheatCodesName];
+ [self.tempCheatCodes addObject:[[PcsxrCheatTempObject alloc] init]];
+ [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kTempCheatCodesName];
+}
+
+- (void)reloadCheats
+{
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSURL *tmpURL = [[manager URLForDirectory:NSItemReplacementDirectory inDomain:NSUserDomainMask appropriateForURL:[[NSBundle mainBundle] bundleURL] create:YES error:nil] URLByAppendingPathComponent:@"temp.cht" isDirectory:NO];
+ NSString *tmpStr = [self.cheats componentsJoinedByString:@"\n"];
+ [tmpStr writeToURL:tmpURL atomically:NO encoding:NSUTF8StringEncoding error:NULL];
+ LoadCheats([[tmpURL path] fileSystemRepresentation]);
+ [manager removeItemAtURL:tmpURL error:NULL];
+}
+
+- (IBAction)editCheat:(id)sender
+{
+ if ([cheatView selectedRow] < 0) {
+ NSBeep();
+ return;
+ }
+ NSMutableArray *tmpArray = [(self.cheats)[[cheatView selectedRow]] cheatValues];
+ NSMutableArray *newCheats = [[NSMutableArray alloc] initWithArray:tmpArray copyItems:YES];
+ self.tempCheatCodes = newCheats;
+ [[self window] beginSheet:editCheatWindow completionHandler:^(NSModalResponse returnCode) {
+ if (returnCode == NSOKButton) {
+ PcsxrCheatTemp *tmpCheat = (self.cheats)[[cheatView selectedRow]];
+ if (![tmpCheat.cheatValues isEqualToArray:self.tempCheatCodes]) {
+ tmpCheat.cheatValues = self.tempCheatCodes;
+ [self setDocumentEdited:YES];
+ }
+ }
+
+ [editCheatWindow orderOut:nil];
+ }];
+}
+
+- (IBAction)addCheat:(id)sender
+{
+ NSIndexSet *newSet = [NSIndexSet indexSetWithIndex:[self.cheats count]];
+ [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kCheatsName];
+ PcsxrCheatTemp *tmpCheat = [[PcsxrCheatTemp alloc] init];
+ tmpCheat.cheatName = NSLocalizedString(@"New Cheat", @"New Cheat Name" );
+ PcsxrCheatTempObject *tmpObj = [[PcsxrCheatTempObject alloc] initWithAddress:0x10000000 value:0];
+ NSMutableArray *tmpArray = [NSMutableArray arrayWithObject:tmpObj];
+ tmpCheat.cheatValues = tmpArray;
+ [self.cheats addObject:tmpCheat];
+ [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:newSet forKey:kCheatsName];
+ [self setDocumentEdited:YES];
+}
+
+- (IBAction)applyCheats:(id)sender
+{
+ [self reloadCheats];
+ [self setDocumentEdited:NO];
+}
+
+- (void)sheetDidDismiss:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
+{
+ switch (returnCode) {
+ case NSAlertDefaultReturn:
+ [self reloadCheats];
+ [self close];
+ break;
+
+ default:
+ [self refreshNSCheatArray];
+ [self close];
+ break;
+
+ case NSAlertOtherReturn:
+ break;
+ }
+}
+
+- (BOOL)windowShouldClose:(id)sender
+{
+ if (![sender isDocumentEdited] || ![[self window] isEqual:sender]) {
+ return YES;
+ } else {
+ NSBeginAlertSheet(NSLocalizedString(@"Unsaved Changes", @"Unsaved changes"),
+ NSLocalizedString(@"Save", @"Save"),
+ NSLocalizedString(@"Don't Save",@"Don't Save"),
+ NSLocalizedString(@"Cancel", @"Cancel"), [self window], self,
+ NULL, @selector(sheetDidDismiss:returnCode:contextInfo:), NULL,
+ NSLocalizedString(@"The cheat codes have not been applied. Unapplied cheats will not run nor be saved. Do you wish to save?", nil));
+
+ return NO;
+ }
+}
+
+- (IBAction)removeCheats:(id)sender
+{
+ if ([cheatView selectedRow] < 0) {
+ NSBeep();
+ return;
+ }
+
+ NSIndexSet *toRemoveIndex = [cheatView selectedRowIndexes];
+ [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kCheatsName];
+ [self.cheats removeObjectsAtIndexes:toRemoveIndex];
+ [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:toRemoveIndex forKey:kCheatsName];
+ [self setDocumentEdited:YES];
+}
+
+@end
diff --git a/macosx/Source/ConfigurationController.h b/macosx/Source/ConfigurationController.h
new file mode 100644
index 00000000..4406a7a2
--- /dev/null
+++ b/macosx/Source/ConfigurationController.h
@@ -0,0 +1,57 @@
+/* ConfigurationController */
+
+#import <Cocoa/Cocoa.h>
+#import "PluginController.h"
+#import "HotkeyController.h"
+#import "PluginList.h"
+
+extern NSString *const memChangeNotifier;
+extern NSString *const memCardChangeNumberKey;
+
+@class PcsxrMemCardController;
+
+@interface ConfigurationController : NSWindowController <NSWindowDelegate, NSTabViewDelegate>
+{
+ IBOutlet PluginController *cdromPlugin;
+ IBOutlet PluginController *graphicsPlugin;
+ IBOutlet PluginController *padPlugin;
+ IBOutlet PluginController *soundPlugin;
+ IBOutlet PluginController *netPlugin;
+ IBOutlet PluginController *sio1Plugin;
+
+ IBOutlet PcsxrMemCardController *memCardEdit;
+
+ // Hotkeys
+ IBOutlet HotkeyController *hkController;
+ IBOutlet NSTabViewItem *hkTab;
+}
+
+@property (weak) IBOutlet NSButtonCell *noXaAudioCell;
+@property (weak) IBOutlet NSButtonCell *sioIrqAlwaysCell;
+@property (weak) IBOutlet NSButtonCell *bwMdecCell;
+@property (weak) IBOutlet NSButtonCell *autoVTypeCell;
+@property (weak) IBOutlet NSPopUpButton *vTypePALCell;
+@property (weak) IBOutlet NSButtonCell *noCDAudioCell;
+@property (weak) IBOutlet NSButtonCell *usesHleCell;
+@property (weak) IBOutlet NSButtonCell *usesDynarecCell;
+@property (weak) IBOutlet NSButtonCell *consoleOutputCell;
+@property (weak) IBOutlet NSButtonCell *spuIrqAlwaysCell;
+@property (weak) IBOutlet NSButtonCell *rCountFixCell;
+@property (weak) IBOutlet NSButtonCell *vSyncWAFixCell;
+@property (weak) IBOutlet NSButtonCell *noFastBootCell;
+@property (weak) IBOutlet NSButtonCell *enableNetPlayCell;
+@property (weak) IBOutlet NSButtonCell *widescreen;
+
+- (IBAction)setCheckbox:(id)sender;
+- (IBAction)setCheckboxInverse:(id)sender;
+- (IBAction)setVideoType:(id)sender;
+
++ (void)setMemoryCard:(NSInteger)theCard toPath:(NSString *)theFile;
++ (void)setMemoryCard:(NSInteger)theCard toURL:(NSURL *)theURL;
+
+- (IBAction)mcdNewClicked:(id)sender;
+- (IBAction)mcdChangeClicked:(id)sender;
+
+- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem;
+
+@end
diff --git a/macosx/Source/ConfigurationController.m b/macosx/Source/ConfigurationController.m
new file mode 100644
index 00000000..aef8f7f4
--- /dev/null
+++ b/macosx/Source/ConfigurationController.m
@@ -0,0 +1,291 @@
+#import "ConfigurationController.h"
+#import "PcsxrController.h"
+#import "PluginList.h"
+#import "PcsxrPlugin.h"
+#import "PcsxrMemCardController.h"
+#import "PcsxrMemCardHandler.h"
+#include "psxcommon.h"
+#include "plugins.h"
+
+NSString *const memChangeNotifier = @"PcsxrMemoryCardDidChangeNotifier";
+NSString *const memCardChangeNumberKey = @"PcsxrMemoryCardThatChangedKey";
+
+@interface ConfigurationController ()
+@property (strong) NSMutableDictionary *checkBoxDefaults;
+- (NSString *)keyForSender:(id)sender;
+@end
+
+@implementation ConfigurationController
+@synthesize autoVTypeCell;
+@synthesize bwMdecCell;
+@synthesize checkBoxDefaults = _checkBoxDefaults;
+@synthesize consoleOutputCell;
+@synthesize enableNetPlayCell;
+@synthesize noCDAudioCell;
+@synthesize noFastBootCell;
+@synthesize noXaAudioCell;
+@synthesize rCountFixCell;
+@synthesize sioIrqAlwaysCell;
+@synthesize spuIrqAlwaysCell;
+@synthesize usesDynarecCell;
+@synthesize usesHleCell;
+@synthesize vSyncWAFixCell;
+@synthesize vTypePALCell;
+@synthesize widescreen;
+
++ (void)setMemoryCard:(NSInteger)theCard toURL:(NSURL *)theURL;
+{
+ if (theCard == 1) {
+ [[NSUserDefaults standardUserDefaults] setURL:theURL forKey:@"Mcd1"];
+ strlcpy(Config.Mcd1, [[theURL path] fileSystemRepresentation], MAXPATHLEN );
+ } else {
+ [[NSUserDefaults standardUserDefaults] setURL:theURL forKey:@"Mcd2"];
+ strlcpy(Config.Mcd2, [[theURL path] fileSystemRepresentation], MAXPATHLEN );
+ }
+
+ [[NSNotificationCenter defaultCenter] postNotificationName:memChangeNotifier object:nil userInfo:
+ @{memCardChangeNumberKey: @(theCard)}];
+}
+
++ (void)setMemoryCard:(NSInteger)theCard toPath:(NSString *)theFile
+{
+ [self setMemoryCard:theCard toURL:[NSURL fileURLWithPath:theFile isDirectory:NO]];
+}
+
+- (IBAction)setCheckbox:(id)sender
+{
+ if ([sender isKindOfClass:[NSMatrix class]]) {
+ sender = [sender selectedCell];
+ }
+
+ NSString *key = [self keyForSender:sender];
+ if (key) {
+ [[NSUserDefaults standardUserDefaults] setBool:[sender intValue] ? YES : NO forKey:key];
+ [PcsxrController setConfigFromDefaults];
+ }
+}
+
+- (IBAction)setCheckboxInverse:(id)sender
+{
+ if ([sender isKindOfClass:[NSMatrix class]]) {
+ sender = [sender selectedCell];
+ }
+
+ NSString *key = [self keyForSender:sender];
+ if (key) {
+ [[NSUserDefaults standardUserDefaults] setBool:[sender intValue] ? NO : YES forKey:key];
+ [PcsxrController setConfigFromDefaults];
+ }
+}
+
+- (IBAction)mcdChangeClicked:(id)sender
+{
+ NSInteger tag = [sender tag];
+ char *mcd;
+ NSOpenPanel *openDlg = [NSOpenPanel openPanel];
+ NSString *path;
+
+ if (tag == 1) {
+ mcd = Config.Mcd1;
+ } else {
+ mcd = Config.Mcd2;
+ }
+
+ path = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:mcd length:strlen(mcd)];
+
+ [openDlg setAllowedFileTypes:[PcsxrMemCardHandler supportedUTIs]];
+ [openDlg setDirectoryURL:[NSURL fileURLWithPath:[path stringByDeletingLastPathComponent] isDirectory:YES]];
+ [openDlg setNameFieldStringValue:[path lastPathComponent]];
+ [openDlg beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
+ if (result == NSFileHandlingPanelOKButton) {
+ NSURL *mcdURL = [openDlg URLs][0];
+
+ [ConfigurationController setMemoryCard:tag toURL:mcdURL];
+ }
+ }];
+}
+
+- (IBAction)mcdNewClicked:(id)sender
+{
+ NSInteger tag = [sender tag];
+ char *mcd;
+ NSSavePanel *openDlg = [NSSavePanel savePanel];
+ NSString *path;
+
+ if (tag == 1) {
+ mcd = Config.Mcd1;
+ } else {
+ mcd = Config.Mcd2;
+ }
+
+ path = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:mcd length:strlen(mcd)];
+
+ [openDlg setDirectoryURL:[NSURL fileURLWithPath:[path stringByDeletingLastPathComponent] isDirectory:YES]];
+ [openDlg setNameFieldStringValue:NSLocalizedString(@"New Memory Card.mcd", nil)];
+ [openDlg setAllowedFileTypes:[PcsxrMemCardHandler supportedUTIs]];
+
+ [openDlg beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
+ if (result == NSFileHandlingPanelOKButton) {
+ NSURL *mcdURL = [openDlg URL];
+ const char *fileSysRep;
+
+ if ([mcdURL respondsToSelector:@selector(fileSystemRepresentation)]) {
+ fileSysRep = [mcdURL fileSystemRepresentation];
+ } else {
+ fileSysRep = [[mcdURL path] fileSystemRepresentation];
+ }
+
+ //Workaround/kludge to make sure we create a memory card before posting a notification
+ strlcpy(mcd, fileSysRep, MAXPATHLEN);
+ CreateMcd(mcd);
+
+ [ConfigurationController setMemoryCard:tag toURL:mcdURL];
+ }
+ }];
+}
+
+- (IBAction)setVideoType:(id)sender
+{
+ NSInteger tag = [[sender selectedItem] tag];
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ if (3 == tag) {
+ [defaults setBool:YES forKey:@"AutoDetectVideoType"];
+ } else if (1 == tag || 2 == tag) {
+ [defaults setBool:NO forKey:@"AutoDetectVideoType"];
+ [defaults setBool:tag==2 forKey:@"VideoTypePAL"];
+ } else {
+ return;
+ }
+ [PcsxrController setConfigFromDefaults];
+
+ if ([sender pullsDown]) {
+ NSArray *items = [sender itemArray];
+ for (id object in items) {
+ [object setState:NSOffState];
+ }
+
+ [[sender selectedItem] setState:NSOnState];
+ }
+}
+
+- (void)windowWillClose:(NSNotification *)notification
+{
+ [memCardEdit stopMemoryAnimation];
+}
+
+- (void)windowDidBecomeMain:(NSNotification *)notification
+{
+ [memCardEdit beginMemoryAnimation];
+}
+
+- (void)awakeFromNib
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ [[self window] center];
+
+ // setup checkboxes
+ self.checkBoxDefaults = [[NSMutableDictionary alloc] init];
+
+ // check that the outlets are active before adding them
+ if (noXaAudioCell)
+ _checkBoxDefaults[@"NoXaAudio"] = noXaAudioCell;
+ if (enableNetPlayCell)
+ _checkBoxDefaults[@"NetPlay"] = enableNetPlayCell;
+ if (sioIrqAlwaysCell)
+ _checkBoxDefaults[@"SioIrqAlways"] = sioIrqAlwaysCell;
+ if (bwMdecCell)
+ _checkBoxDefaults[@"BlackAndWhiteMDECVideo"] = bwMdecCell;
+ if (autoVTypeCell)
+ _checkBoxDefaults[@"AutoDetectVideoType"] = autoVTypeCell;
+ if (vTypePALCell)
+ _checkBoxDefaults[@"VideoTypePAL"] = vTypePALCell;
+ if (noCDAudioCell)
+ _checkBoxDefaults[@"NoCDAudio"] = noCDAudioCell;
+ if (usesHleCell)
+ _checkBoxDefaults[@"UseHLE"] = usesHleCell;
+ if (usesDynarecCell)
+ _checkBoxDefaults[@"NoDynarec"] = usesDynarecCell;
+ if (consoleOutputCell)
+ _checkBoxDefaults[@"ConsoleOutput"] = consoleOutputCell;
+ if (spuIrqAlwaysCell)
+ _checkBoxDefaults[@"SpuIrqAlways"] = spuIrqAlwaysCell;
+ if (rCountFixCell)
+ _checkBoxDefaults[@"RootCounterFix"] = rCountFixCell;
+ if (vSyncWAFixCell)
+ _checkBoxDefaults[@"VideoSyncWAFix"] = vSyncWAFixCell;
+ if (noFastBootCell)
+ _checkBoxDefaults[@"NoFastBoot"] = noFastBootCell;
+ if (widescreen)
+ _checkBoxDefaults[@"Widescreen"] = widescreen;
+
+ // make the visuals match the defaults
+
+ for (NSString* key in _checkBoxDefaults) {
+ if ([defaults integerForKey:key]) {
+ [_checkBoxDefaults[key] setNextState];
+ }
+ }
+
+ // special cases
+ if (![PcsxrController biosAvailable]) {
+ // no bios means always use HLE
+ [usesHleCell setState:NSOnState];
+ [usesHleCell setEnabled:NO];
+ }
+
+
+ // setup labels
+
+ NSInteger tag = [defaults integerForKey:@"AutoDetectVideoType"];
+ if (tag)
+ tag = 3;
+ else {
+ tag = [defaults integerForKey:@"VideoTypePAL"]+1;
+ }
+ [vTypePALCell setAutoenablesItems:NO];
+ if ([vTypePALCell pullsDown]) {
+ [[vTypePALCell itemAtIndex:[vTypePALCell indexOfItemWithTag:tag]] setState:NSOnState];
+ } else {
+ [vTypePALCell selectItemAtIndex:[vTypePALCell indexOfItemWithTag:tag]];
+ }
+
+ // setup plugin lists
+ PluginList *list = [PluginList list];
+
+ [list refreshPlugins];
+ [graphicsPlugin setPluginsTo:[list pluginsForType:PSE_LT_GPU] withType: PSE_LT_GPU];
+ [soundPlugin setPluginsTo:[list pluginsForType:PSE_LT_SPU] withType: PSE_LT_SPU];
+ [padPlugin setPluginsTo:[list pluginsForType:PSE_LT_PAD] withType: PSE_LT_PAD];
+ [cdromPlugin setPluginsTo:[list pluginsForType:PSE_LT_CDR] withType: PSE_LT_CDR];
+ [netPlugin setPluginsTo:[list pluginsForType:PSE_LT_NET] withType: PSE_LT_NET];
+ [sio1Plugin setPluginsTo:[list pluginsForType:PSE_LT_SIO1] withType:PSE_LT_SIO1];
+
+ // Setup hotkey view
+ [hkController initialize];
+}
+
+- (NSString *)keyForSender:(id)sender
+{
+ for (NSString *key in [self.checkBoxDefaults keyEnumerator]) {
+ id object = (self.checkBoxDefaults)[key];
+ if ([object isEqual:sender])
+ return key;
+ }
+
+ return nil;
+}
+
+- (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
+{
+ NSWindow *window = [self window];
+ if(tabViewItem == hkTab) {
+ [window makeFirstResponder:(NSView*)hkController];
+ }
+ else if([window firstResponder] == (NSView*)hkController) {
+ [hkController resignFirstResponder];
+ }
+}
+
+@end
diff --git a/macosx/Source/EmuThread.h b/macosx/Source/EmuThread.h
new file mode 100644
index 00000000..affab393
--- /dev/null
+++ b/macosx/Source/EmuThread.h
@@ -0,0 +1,40 @@
+//
+// EmuThread.h
+// Pcsxr
+//
+// Created by Gil Pedersen on Sun Sep 21 2003.
+// Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#include <setjmp.h>
+
+@interface EmuThread : NSObject {
+ jmp_buf restartJmp;
+ BOOL wasPaused;
+}
+
+- (void)EmuThreadRun:(id)anObject;
+- (void)EmuThreadRunBios:(id)anObject;
+- (void)handleEvents;
+
++ (void)run;
++ (void)runBios;
++ (void)stop;
++ (BOOL)pause;
++ (BOOL)pauseSafe;
++ (void)pauseSafeWithBlock:(void (^)(BOOL))theBlock;
++ (void)resume;
++ (void)resetNow;
++ (void)reset;
+
++ (BOOL)isPaused;
++ (BOOL)active;
++ (BOOL)isRunBios;
+
++ (void)freezeAt:(NSString *)path which:(int)num;
++ (BOOL)defrostAt:(NSString *)path;
+
+@end
+
+extern EmuThread *emuThread;
diff --git a/macosx/Source/EmuThread.m b/macosx/Source/EmuThread.m
new file mode 100644
index 00000000..f4404c8e
--- /dev/null
+++ b/macosx/Source/EmuThread.m
@@ -0,0 +1,396 @@
+//
+// EmuThread.m
+// Pcsxr
+//
+// Created by Gil Pedersen on Sun Sep 21 2003.
+// Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import <ExceptionHandling/NSExceptionHandler.h>
+#import <Cocoa/Cocoa.h>
+#include <pthread.h>
+#include <setjmp.h>
+#import "EmuThread.h"
+#include "psxcommon.h"
+#include "plugins.h"
+#include "misc.h"
+
+EmuThread *emuThread = nil;
+static NSString *defrostPath = nil;
+static int safeEvent;
+static BOOL paused;
+static BOOL runbios;
+
+static pthread_cond_t eventCond;
+static pthread_mutex_t eventMutex;
+
+#define EMUEVENT_NONE 0
+#define EMUEVENT_PAUSE (1<<0)
+#define EMUEVENT_RESET (1<<1)
+#define EMUEVENT_STOP (1<<2)
+
+@implementation EmuThread
+
+- (void)setUpThread
+{
+ NSAssert(![[NSThread currentThread] isEqual:[NSThread mainThread]], @"This function should not be run on the main thread!");
+
+ [[NSThread currentThread] setName:@"PSX Emu Background thread"];
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(emuWindowDidClose:)
+ name:@"emuWindowDidClose" object:nil];
+
+ [center addObserver:self
+ selector:@selector(emuWindowWantPause:)
+ name:@"emuWindowWantPause" object:nil];
+
+ [center addObserver:self
+ selector:@selector(emuWindowWantResume:)
+ name:@"emuWindowWantResume" object:nil];
+
+ // we shouldn't change the priority, since we might depend on subthreads
+ //[NSThread setThreadPriority:1.0-((1.0-[NSThread threadPriority])/4.0)];
+}
+
+- (void)EmuThreadRun:(id)anObject
+{
+ [self setUpThread];
+
+ // Do processing here
+ if (OpenPlugins() == -1)
+ goto done;
+
+ setjmp(restartJmp);
+
+ int res = CheckCdrom();
+ if (res == -1) {
+ ClosePlugins();
+ SysMessage("%s", _("Could not check CD-ROM!\n"));
+ goto done;
+ }
+
+ // Auto-detect: region first, then rcnt reset
+ EmuReset();
+
+ LoadCdrom();
+
+ if (defrostPath) {
+ LoadState([defrostPath fileSystemRepresentation]);
+ defrostPath = nil;
+ }
+
+ psxCpu->Execute();
+
+done:
+ emuThread = nil;
+
+ return;
+}
+
+- (void)EmuThreadRunBios:(id)anObject
+{
+ [self setUpThread];
+
+ // Do processing here
+ if (OpenPlugins() == -1)
+ goto done;
+
+ EmuReset();
+
+ psxCpu->Execute();
+
+done:
+ emuThread = nil;
+
+ return;
+}
+
+- (void)dealloc
+{
+ // remove all registered observers
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)emuWindowDidClose:(NSNotification *)aNotification
+{
+ [EmuThread stop];
+}
+
+- (void)emuWindowWantPause:(NSNotification *)aNotification
+{
+ wasPaused = [EmuThread pause];
+}
+
+- (void)emuWindowWantResume:(NSNotification *)aNotification
+{
+ if (!wasPaused) {
+ [EmuThread resume];
+ }
+ wasPaused = NO;
+}
+
+/* called periodically from the emulation thread */
+- (void)handleEvents
+{
+ /* only do a trylock here, since we're not interested in blocking,
+ and we can just handle events next time round */
+ if (pthread_mutex_trylock(&eventMutex) == 0) {
+ while (safeEvent) {
+ if (safeEvent & EMUEVENT_STOP) {
+ /* signify that the emulation has stopped */
+ emuThread = nil;
+ paused = NO;
+
+ /* better unlock the mutex before killing ourself */
+ pthread_mutex_unlock(&eventMutex);
+
+ ClosePlugins();
+ SysClose();
+
+ //[[NSThread currentThread] autorelease];
+ [NSThread exit];
+ return;
+ }
+
+ if (safeEvent & EMUEVENT_RESET) {
+#if 0
+ /* signify that the emulation has stopped */
+ [emuThread autorelease];
+ emuThread = nil;
+
+ /* better unlock the mutex before killing ourself */
+ pthread_mutex_unlock(&eventMutex);
+
+ ClosePlugins();
+
+ // start a new emulation thread
+ [EmuThread run];
+
+ //[[NSThread currentThread] autorelease];
+ [NSThread exit];
+ return;
+#else
+ safeEvent &= ~EMUEVENT_RESET;
+ pthread_mutex_unlock(&eventMutex);
+
+ longjmp(restartJmp, 0);
+#endif
+ }
+
+ if (safeEvent & EMUEVENT_PAUSE) {
+ paused = 2;
+ /* wait until we're signalled */
+ pthread_cond_wait(&eventCond, &eventMutex);
+ }
+ }
+ pthread_mutex_unlock(&eventMutex);
+ }
+}
+
++ (void)run
+{
+ int err;
+
+ if (emuThread) {
+ [EmuThread resume];
+ return;
+ }
+
+ if (pthread_mutex_lock(&eventMutex) != 0) {
+ err = pthread_cond_init(&eventCond, NULL);
+ if (err) return;
+
+ err = pthread_mutex_init(&eventMutex, NULL);
+ if (err) return;
+
+ pthread_mutex_lock(&eventMutex);
+ }
+
+ safeEvent = EMUEVENT_NONE;
+ paused = NO;
+ runbios = NO;
+
+ if (SysInit() != 0) {
+ pthread_mutex_unlock(&eventMutex);
+ return;
+ }
+
+ emuThread = [[EmuThread alloc] init];
+
+ [NSThread detachNewThreadSelector:@selector(EmuThreadRun:)
+ toTarget:emuThread withObject:nil];
+
+ pthread_mutex_unlock(&eventMutex);
+}
+
++ (void)runBios
+{
+ int err;
+
+ if (emuThread) {
+ [EmuThread resume];
+ return;
+ }
+
+ if (pthread_mutex_lock(&eventMutex) != 0) {
+ err = pthread_cond_init(&eventCond, NULL);
+ if (err) return;
+
+ err = pthread_mutex_init(&eventMutex, NULL);
+ if (err) return;
+
+ pthread_mutex_lock(&eventMutex);
+ }
+
+ safeEvent = EMUEVENT_NONE;
+ paused = NO;
+ runbios = YES;
+
+ if (SysInit() != 0) {
+ pthread_mutex_unlock(&eventMutex);
+ return;
+ }
+
+ emuThread = [[EmuThread alloc] init];
+
+ [NSThread detachNewThreadSelector:@selector(EmuThreadRunBios:)
+ toTarget:emuThread withObject:nil];
+
+ pthread_mutex_unlock(&eventMutex);
+}
+
++ (void)stop
+{
+ pthread_mutex_lock(&eventMutex);
+ safeEvent = EMUEVENT_STOP;
+ pthread_mutex_unlock(&eventMutex);
+
+ // wake it if it's sleeping
+ pthread_cond_broadcast(&eventCond);
+}
+
++ (BOOL)pause
+{
+ if (paused || ![EmuThread active])
+ return YES;
+
+ pthread_mutex_lock(&eventMutex);
+ safeEvent |= EMUEVENT_PAUSE;
+ paused = 1;
+ pthread_mutex_unlock(&eventMutex);
+
+ pthread_cond_broadcast(&eventCond);
+
+ return NO;
+}
+
++ (BOOL)pauseSafe
+{
+ if ((paused == 2) || ![EmuThread active])
+ return YES;
+
+ [EmuThread pause];
+ while ([EmuThread isPaused] != 2)
+ [NSThread sleepUntilDate:[[NSDate date] dateByAddingTimeInterval:0.05]];
+
+ return NO;
+}
+
++ (void)pauseSafeWithBlock:(void (^)(BOOL))theBlock
+{
+ dispatch_async(dispatch_get_global_queue(0, 0), ^{
+ BOOL wasPaused = [self pauseSafe];
+ dispatch_async(dispatch_get_main_queue(), ^{theBlock(wasPaused);});
+ });
+}
+
++ (void)resume
+{
+ if (!paused || ![EmuThread active])
+ return;
+
+ pthread_mutex_lock(&eventMutex);
+
+ safeEvent &= ~EMUEVENT_PAUSE;
+ paused = NO;
+ pthread_mutex_unlock(&eventMutex);
+
+ pthread_cond_broadcast(&eventCond);
+}
+
++ (void)reset
+{
+ pthread_mutex_lock(&eventMutex);
+ safeEvent = EMUEVENT_RESET;
+ pthread_mutex_unlock(&eventMutex);
+
+ pthread_cond_broadcast(&eventCond);
+}
+
+// must only be called from within the emulation thread!!!
++ (void)resetNow
+{
+ /* signify that the emulation has stopped */
+ emuThread = nil;
+
+ ClosePlugins();
+
+ // start a new emulation thread
+ [EmuThread run];
+
+ //[[NSThread currentThread] autorelease];
+ [NSThread exit];
+ return;
+}
+
++ (BOOL)isPaused
+{
+ return paused;
+}
+
++ (BOOL)isRunBios
+{
+ return runbios;
+}
+
++ (BOOL)active
+{
+ return emuThread ? YES : NO;
+}
+
++ (void)freezeAt:(NSString *)path which:(int)num
+{
+ [self pauseSafeWithBlock:^(BOOL emuWasPaused) {
+ int tmpNum = num;
+ char Text[256];
+
+ GPU_freeze(2, (GPUFreeze_t *)&tmpNum);
+ int ret = SaveState([path fileSystemRepresentation]);
+
+ if (!emuWasPaused) {
+ [EmuThread resume];
+ }
+
+ if (ret == 0)
+ snprintf(Text, sizeof(Text), _("*PCSXR*: Saved State %d"), num);
+ else
+ snprintf(Text, sizeof(Text), _("*PCSXR*: Error Saving State %d"), num);
+ GPU_displayText(Text);
+ }];
+}
+
++ (BOOL)defrostAt:(NSString *)path
+{
+ const char *cPath = [path fileSystemRepresentation];
+ if (CheckState(cPath) != 0)
+ return NO;
+
+ defrostPath = path;
+ [EmuThread reset];
+
+ GPU_displayText(_("*PCSXR*: Loaded State"));
+ return YES;
+}
+
+@end
diff --git a/macosx/Source/ExtendedKeys.h b/macosx/Source/ExtendedKeys.h
new file mode 100644
index 00000000..b4990b20
--- /dev/null
+++ b/macosx/Source/ExtendedKeys.h
@@ -0,0 +1,28 @@
+
+#ifndef __EXTENDED_KEYS_H__
+#define __EXTENDED_KEYS_H__
+
+enum {
+ PSX_FREEZE_KEY = 0xFFBE/*XK_F1*/,
+ PSX_NEXT_FREEZE_SLOT_KEY = 0xFFBF/*XK_F2*/,
+ PSX_DEFROST_KEY = 0xFFC0/*XK_F3*/,
+ PSX_SHOW_FREEZE_PIC_KEY = 0xFFC1/*XK_F4*/,
+ PSX_SIO_ALWAYS_ON_KEY = 0xFFC2/*XK_F5*/,
+ PSX_BW_MDEC_KEY = 0xFFC3/*XK_F6*/,
+ PSX_XA_AUDIO_ON_KEY = 0xFFC4/*XK_F7*/,
+ PSX_SNAPSHOT_KEY = 0xFFC5/*XK_F8*/,
+ PSX_OPEN_SHELL_KEY = 0xFFC6/*XK_F9*/,
+ PSX_CLOSE_SHELL_KEY = 0xFFC7/*XK_F10*/,
+
+ PSX_STOP_KEY = 0xFF1B/*XK_Escape*/,
+
+ GPU_FULLSCREEN_KEY = 0x0100,
+ GPU_FPS_DISPLAY_KEY = 0xFFFF,/*XK_Delete*/
+
+ // Fake HotKeys
+ GPU_HOTKEYS = 0x020,
+ GPU_FAST_FORWARD,
+ GPU_FRAME_LIMIT
+};
+
+#endif //__EXTENDED_KEYS_H__
diff --git a/macosx/Source/HotkeyController.h b/macosx/Source/HotkeyController.h
new file mode 100644
index 00000000..d24a921c
--- /dev/null
+++ b/macosx/Source/HotkeyController.h
@@ -0,0 +1,25 @@
+/**
+ * HotkeyController
+ * Nicolas Pépin-Perreault - npepinpe - 2012
+ */
+
+#import <Cocoa/Cocoa.h>
+
+@interface HotkeyController : NSView
+
+@property (weak) IBOutlet NSTextField *FastForward;
+@property (weak) IBOutlet NSTextField *SaveState;
+@property (weak) IBOutlet NSTextField *LoadState;
+@property (weak) IBOutlet NSTextField *NextState;
+@property (weak) IBOutlet NSTextField *PrevState;
+@property (weak) IBOutlet NSTextField *FrameLimit;
+
+
+@property NSInteger configInput;
+
+- (void) initialize;
+- (BOOL) handleMouseDown:(NSEvent *)mouseEvent;
+- (IBAction) hotkeySet:(id)sender;
+- (void) hotkeyCancel;
+
+@end
diff --git a/macosx/Source/HotkeyController.m b/macosx/Source/HotkeyController.m
new file mode 100644
index 00000000..58ec0fdd
--- /dev/null
+++ b/macosx/Source/HotkeyController.m
@@ -0,0 +1,211 @@
+/**
+ * HotkeyController.m
+ * Pcsxr
+ *
+ * Created by Nicolas Pepin-Perreault on 12-12-10.
+ *
+ * Adapted from the Cocoa port of DeSMuMe
+ */
+
+#import "HotkeyController.h"
+
+#define INPUT_HOLD_TIME 0.1
+
+@interface HotkeyController ()
+@property (strong) NSButton *lastConfigButton;
+@property (strong) NSMutableDictionary *hotkeysList;
+@property (strong) NSDictionary *keyNameTable;
+@property (strong) NSMutableDictionary *hotkeyOutlets;
+@end
+
+@implementation HotkeyController
+
+@synthesize FastForward;
+@synthesize FrameLimit;
+@synthesize LoadState;
+@synthesize NextState;
+@synthesize PrevState;
+@synthesize SaveState;
+
+- (void)initialize
+{
+ self.lastConfigButton = nil;
+ self.configInput = 0;
+ self.hotkeysList = [[NSMutableDictionary alloc] initWithCapacity:16];
+ self.keyNameTable = [[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"KeyNames" ofType:@"plist"]];
+ self.hotkeyOutlets = [[NSMutableDictionary alloc] initWithCapacity:8];
+
+ [self mapOutletToIdentifier:FastForward forIdentifier:@"FastForward"];
+ [self mapOutletToIdentifier:SaveState forIdentifier:@"SaveState"];
+ [self mapOutletToIdentifier:LoadState forIdentifier:@"LoadState"];
+ [self mapOutletToIdentifier:NextState forIdentifier:@"NextState"];
+ [self mapOutletToIdentifier:PrevState forIdentifier:@"PrevState"];
+ [self mapOutletToIdentifier:FrameLimit forIdentifier:@"FrameLimit"];
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)mapOutletToIdentifier:(id)outlet forIdentifier:(NSString*)identifier1
+{
+ (self.hotkeyOutlets)[identifier1] = outlet;
+ [self setHotkeyDisplay:identifier1];
+}
+
+- (void)setHotkeyDisplay:(NSString*)keyIdent
+{
+ NSString *label = [self parseMappingDisplayString:keyIdent];
+ NSTextField *displayField = (self.hotkeyOutlets)[keyIdent];
+
+ if(displayField) {
+ [displayField setStringValue:label];
+ }
+}
+
+- (void)mouseDown:(NSEvent *)theEvent
+{
+ BOOL isHandled = [self handleMouseDown:theEvent];
+ if (!isHandled)
+ {
+ [super mouseDown:theEvent];
+ }
+}
+
+- (void)mouseDragged:(NSEvent *)theEvent
+{
+ [self mouseDown:theEvent];
+}
+
+- (void)rightMouseDown:(NSEvent *)theEvent
+{
+ BOOL isHandled = [self handleMouseDown:theEvent];
+ if (!isHandled)
+ {
+ [super rightMouseDown:theEvent];
+ }
+}
+
+- (void)rightMouseDragged:(NSEvent *)theEvent
+{
+ [self rightMouseDown:theEvent];
+}
+
+- (void)otherMouseDown:(NSEvent *)theEvent
+{
+ BOOL isHandled = [self handleMouseDown:theEvent];
+ if (!isHandled)
+ {
+ [super otherMouseDown:theEvent];
+ }
+}
+
+- (void)otherMouseDragged:(NSEvent *)theEvent
+{
+ [self otherMouseDown:theEvent];
+}
+
+- (BOOL) handleMouseDown:(NSEvent *)mouseEvent
+{
+ if (self.configInput != 0)
+ {
+ [self hotkeyCancel];
+ }
+
+ return YES;
+}
+
+- (void)keyDown:(NSEvent *)theEvent
+{
+ NSString *keyCode = [NSString stringWithFormat:@"%d", [theEvent keyCode]];
+ NSString *keyLabel = (NSString *) (self.keyNameTable)[keyCode];
+
+ if (self.configInput != 0)
+ {
+ // Save input
+ NSString *ident = [self.lastConfigButton identifier];
+ [self saveHotkey:ident device:@"NSEventKeyboard" deviceLabel:@"Keyboard" code:keyCode label:keyLabel];
+ [self setHotkeyDisplay:ident];
+ [self hotkeyCancel];
+ }
+}
+
+- (void)saveHotkey:(NSString*)keyIdent device:(NSString*)device deviceLabel:(NSString*)deviceLabel code:(NSString*)keyCode label:(NSString*)keyLabel
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults] ;
+ NSMutableDictionary *tempUserMappings = [NSMutableDictionary dictionaryWithDictionary:[defaults dictionaryForKey:@"HotkeyBindings"]];
+ [tempUserMappings setValue:@{@"device": device,
+ @"deviceName": deviceLabel,
+ @"keyCode": keyCode,
+ @"keyLabel": keyLabel} forKey:keyIdent];
+ [defaults setValue:tempUserMappings forKey:@"HotkeyBindings"];
+}
+
+- (NSString *) parseMappingDisplayString:(NSString *)keyString
+{
+ NSDictionary *userMappings = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"HotkeyBindings"];
+ NSDictionary *binding = (NSDictionary *)[userMappings valueForKey:keyString];
+
+ NSString *displayString = @"";
+ if(binding) {
+ NSString *deviceLabel = (NSString *)[binding valueForKey:@"deviceName"];
+ NSString *keyLabel = (NSString *)[binding valueForKey:@"keyLabel"];
+
+ displayString = [NSString stringWithString:deviceLabel];
+ displayString = [displayString stringByAppendingString:@": "];
+ displayString = [displayString stringByAppendingString:keyLabel];
+ }
+
+ return displayString;
+}
+
+- (IBAction) hotkeySet:(id)sender
+{
+ NSButton *theButton = (NSButton *)sender;
+
+ if (self.configInput && self.lastConfigButton != theButton)
+ {
+ [self.lastConfigButton setState:NSOffState];
+ }
+
+ if ([theButton state] == NSOnState)
+ {
+ self.lastConfigButton = theButton;
+ [self.hotkeysList removeAllObjects];
+ self.configInput = [theButton tag];
+ }
+ else
+ {
+ [self hotkeyCancel];
+ }
+
+}
+
+- (void) hotkeyCancel
+{
+ if (self.lastConfigButton != nil)
+ {
+ [self.lastConfigButton setState:NSOffState];
+ self.lastConfigButton = nil;
+ }
+
+ self.configInput = 0;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)becomeFirstResponder
+{
+ return YES;
+}
+
+- (BOOL)resignFirstResponder
+{
+ return YES;
+}
+
+@end
diff --git a/macosx/Source/LaunchArg.h b/macosx/Source/LaunchArg.h
new file mode 100644
index 00000000..2584123d
--- /dev/null
+++ b/macosx/Source/LaunchArg.h
@@ -0,0 +1,25 @@
+//
+// LaunchArg.h
+// Pcsxr
+//
+// Created by C.W. Betts on 7/8/13.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+typedef enum _LaunchArgOrder {
+ LaunchArgPreRun = 0,
+ LaunchArgRun = 200,
+ LaunchArgPostRun = 400
+}LaunchArgOrder;
+
+@interface LaunchArg : NSObject
+@property (readonly) unsigned launchOrder;
+@property (readonly, copy, nonatomic) dispatch_block_t theBlock;
+@property (readonly, strong) NSString *argument;
+
+- (id)initWithLaunchOrder:(unsigned)order block:(dispatch_block_t)block argument:(NSString*)arg;
+- (id)initWithLaunchOrder:(unsigned)order argument:(NSString*)arg block:(dispatch_block_t)block;
+- (void)addToDictionary:(NSMutableDictionary*)toAdd;
+@end
diff --git a/macosx/Source/LaunchArg.m b/macosx/Source/LaunchArg.m
new file mode 100644
index 00000000..dca47a7b
--- /dev/null
+++ b/macosx/Source/LaunchArg.m
@@ -0,0 +1,51 @@
+//
+// LaunchArg.m
+// Pcsxr
+//
+// Created by C.W. Betts on 7/8/13.
+//
+//
+
+#import "LaunchArg.h"
+
+@interface LaunchArg ()
+@property (readwrite) unsigned launchOrder;
+@property (readwrite, copy, nonatomic) dispatch_block_t theBlock;
+@property (readwrite, strong) NSString *argument;
+@end
+
+@implementation LaunchArg
+@synthesize argument = _argument;
+@synthesize launchOrder = _launchOrder;
+@synthesize theBlock = _theBlock;
+- (void)setTheBlock:(dispatch_block_t)theBlock
+{
+ _theBlock = [theBlock copy];
+}
+
+- (id)initWithLaunchOrder:(unsigned)order argument:(NSString*)arg block:(dispatch_block_t)block
+{
+ return [self initWithLaunchOrder:order block:block argument:arg];
+}
+
+- (id)initWithLaunchOrder:(unsigned)order block:(dispatch_block_t)block argument:(NSString*)arg
+{
+ if (self = [super init]) {
+ self.launchOrder = order;
+ self.theBlock = block;
+ self.argument = arg;
+ }
+ return self;
+}
+
+- (void)addToDictionary:(NSMutableDictionary*)toAdd
+{
+ toAdd[self.argument] = self;
+}
+
+- (NSString*)description
+{
+ return [NSString stringWithFormat:@"Arg: %@, order: %u, block addr: %p", _argument, _launchOrder, _theBlock];
+}
+
+@end
diff --git a/macosx/Source/MemBadgeView.h b/macosx/Source/MemBadgeView.h
new file mode 100644
index 00000000..b28353bd
--- /dev/null
+++ b/macosx/Source/MemBadgeView.h
@@ -0,0 +1,14 @@
+//
+// MemBadgeView.h
+// Pcsxr
+//
+// Created by C.W. Betts on 7/6/13.
+//
+//
+
+#import <Cocoa/Cocoa.h>
+
+//TODO: also include the memory count in the view as well.
+@interface MemBadgeView : NSView
+
+@end
diff --git a/macosx/Source/MemBadgeView.m b/macosx/Source/MemBadgeView.m
new file mode 100644
index 00000000..e15a6302
--- /dev/null
+++ b/macosx/Source/MemBadgeView.m
@@ -0,0 +1,51 @@
+//
+// MemBadgeView.m
+// Pcsxr
+//
+// Created by C.W. Betts on 7/6/13.
+//
+//
+
+#import "MemBadgeView.h"
+
+@implementation MemBadgeView
+
+//TODO: also include the memory count in the view as well.
+- (id)initWithFrame:(NSRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self) {
+ // Initialization code here.
+ }
+
+ return self;
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+ NSRect drawToRect = dirtyRect;
+ NSImage *tmpDraw = nil;
+ if (!NSEqualSizes(self.frame.size, dirtyRect.size)) {
+ drawToRect = (NSRect) {NSZeroPoint, self.frame.size};
+ tmpDraw = [[NSImage alloc] initWithSize:drawToRect.size];
+ [tmpDraw lockFocus];
+ }
+
+ [[NSColor whiteColor] set];
+ [[NSBezierPath bezierPathWithOvalInRect:drawToRect] fill];
+ [[NSColor redColor] set];
+ NSRect smallerRect = drawToRect;
+ smallerRect.origin.x += 2;
+ smallerRect.origin.y += 2;
+ smallerRect.size.height -= 4;
+ smallerRect.size.width -= 4;
+ [[NSBezierPath bezierPathWithOvalInRect:smallerRect] fill];
+
+ if (tmpDraw) {
+ [tmpDraw unlockFocus];
+
+ [tmpDraw drawInRect:dirtyRect fromRect:dirtyRect operation:NSCompositeSourceOver fraction:1.0];
+ }
+}
+
+@end
diff --git a/macosx/Source/OSXPlugLocalization.h b/macosx/Source/OSXPlugLocalization.h
new file mode 100644
index 00000000..134a3daa
--- /dev/null
+++ b/macosx/Source/OSXPlugLocalization.h
@@ -0,0 +1,21 @@
+//
+// OSXPlugLocalization.h
+// Pcsxr
+//
+// Created by C.W. Betts on 7/8/13.
+//
+//
+
+#ifndef Pcsxr_OSXPlugLocalization_h
+#define Pcsxr_OSXPlugLocalization_h
+
+#define PLUGLOCIMP(klass) \
+char* PLUGLOC(char *toloc) \
+{ \
+NSBundle *mainBundle = [NSBundle bundleForClass:klass]; \
+NSString *origString = @(toloc), *transString = nil; \
+transString = [mainBundle localizedStringForKey:origString value:@"" table:nil]; \
+return (char*)[transString UTF8String]; \
+}
+
+#endif
diff --git a/macosx/Source/PcsxrCheatHandler.h b/macosx/Source/PcsxrCheatHandler.h
new file mode 100644
index 00000000..1dc4fc8e
--- /dev/null
+++ b/macosx/Source/PcsxrCheatHandler.h
@@ -0,0 +1,14 @@
+//
+// PcsxrCheatHandler.h
+// Pcsxr
+//
+// Created by C.W. Betts on 8/1/13.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import "PcsxrFileHandle.h"
+
+@interface PcsxrCheatHandler : NSObject <PcsxrFileHandle>
+
+@end
diff --git a/macosx/Source/PcsxrCheatHandler.m b/macosx/Source/PcsxrCheatHandler.m
new file mode 100644
index 00000000..54f6cb5b
--- /dev/null
+++ b/macosx/Source/PcsxrCheatHandler.m
@@ -0,0 +1,36 @@
+//
+// PcsxrCheatHandler.m
+// Pcsxr
+//
+// Created by C.W. Betts on 8/1/13.
+//
+//
+
+#import "PcsxrCheatHandler.h"
+#import "CheatController.h"
+#import "PcsxrController.h"
+#include "psxcommon.h"
+#include "cheat.h"
+
+@implementation PcsxrCheatHandler
+
++ (NSArray *)supportedUTIs
+{
+ static NSArray *utisupport;
+ if (utisupport == nil) {
+ utisupport = @[@"com.codeplex.pcsxr.cheat"];
+ }
+ return utisupport;
+}
+
+- (BOOL)handleFile:(NSString *)theFile
+{
+ LoadCheats([theFile fileSystemRepresentation]);
+
+ if ([(PcsxrController*)[NSApp delegate] cheatController]) {
+ [[(PcsxrController*)[NSApp delegate] cheatController] refresh];
+ }
+ return YES;
+}
+
+@end
diff --git a/macosx/Source/PcsxrController.h b/macosx/Source/PcsxrController.h
new file mode 100644
index 00000000..b271ae55
--- /dev/null
+++ b/macosx/Source/PcsxrController.h
@@ -0,0 +1,53 @@
+/* PcsxrController */
+
+#import <Cocoa/Cocoa.h>
+#import "EmuThread.h"
+#import "PluginList.h"
+#import "RecentItemsMenu.h"
+
+@class ConfigurationController;
+@class CheatController;
+
+__private_extern void ShowHelpAndExit(FILE* output, int exitCode);
+extern BOOL wasFinderLaunch;
+
+@interface PcsxrController : NSObject <NSApplicationDelegate>
+{
+ ConfigurationController *preferencesController;
+ CheatController *cheatController;
+ PluginList *pluginList;
+
+ struct _PSXflags {
+ unsigned int sleepInBackground:1;
+ unsigned int wasPausedBeforeBGSwitch:1;
+ unsigned int endAtEmuClose:1;
+ unsigned int wasPausedBeforeDiscEject:1;
+ unsigned int reserved:28;
+ } PSXflags;
+}
+@property (weak) IBOutlet RecentItemsMenu *recentItems;
+@property (readonly) CheatController *cheatController;
+@property (readonly) BOOL endAtEmuClose;
+
+- (IBAction)ejectCD:(id)sender;
+- (IBAction)pause:(id)sender;
+- (IBAction)showCheatsWindow:(id)sender;
+- (IBAction)preferences:(id)sender;
+- (IBAction)reset:(id)sender;
+- (IBAction)runCD:(id)sender;
+- (IBAction)runIso:(id)sender;
+- (IBAction)runBios:(id)sender;
+- (IBAction)freeze:(id)sender;
+- (IBAction)defrost:(id)sender;
+- (IBAction)fullscreen:(id)sender;
+- (IBAction)pauseInBackground:(id)sender;
+- (void)runURL:(NSURL*)url;
+
++ (void)setConfigFromDefaults;
++ (void)setDefaultFromConfig:(NSString *)defaultKey;
++ (BOOL)biosAvailable;
++ (NSString*)saveStatePath:(int)slot;
++ (void)saveState:(int)num;
++ (void)loadState:(int)num;
+
+@end
diff --git a/macosx/Source/PcsxrController.m b/macosx/Source/PcsxrController.m
new file mode 100644
index 00000000..4bdce477
--- /dev/null
+++ b/macosx/Source/PcsxrController.m
@@ -0,0 +1,974 @@
+#import <Cocoa/Cocoa.h>
+#import "PcsxrController.h"
+#import "ConfigurationController.h"
+#import "CheatController.h"
+#import "EmuThread.h"
+#import "PcsxrMemCardHandler.h"
+#import "PcsxrPluginHandler.h"
+#import "PcsxrDiscHandler.h"
+#import "PcsxrFreezeStateHandler.h"
+#import "PcsxrCheatHandler.h"
+#import "LaunchArg.h"
+#include <DiskArbitration/DiskArbitration.h>
+#include <IOKit/storage/IOCDMedia.h>
+#include "psxcommon.h"
+#include "plugins.h"
+#include "misc.h"
+#include "cdrom.h"
+#include "ExtendedKeys.h"
+
+NSDictionary *prefStringKeys = nil;
+NSDictionary *prefByteKeys = nil;
+NSDictionary *prefURLKeys = nil;
+NSMutableArray *biosList = nil;
+NSString *saveStatePath = nil;
+BOOL wasFinderLaunch = NO;
+
+
+#define HELPSTR "\n" \
+"At least one of these must be passed:\n" \
+"\t--iso path launch with selected ISO\n" \
+"\t--cdrom launch with a CD-ROM\n" \
+"\t--bios launch into the BIOS\n" \
+"\n" \
+"Additional options:\n" \
+"\t--exitAtClose closes PCSX-R at when the emulation has ended\n" \
+"\t--mcd1 path sets the fist memory card to path\n" \
+"\t--mcd2 path sets the second memory card to path\n" \
+"\t--freeze path loads freeze state from path\n" \
+"\n" \
+"Help:\n" \
+"\t--help shows this message\n" \
+"\n" \
+
+
+void ShowHelpAndExit(FILE* output, int exitCode)
+{
+ fprintf(output, HELPSTR);
+ if (!NSApp) {
+ exit(exitCode);
+ } else {
+ [NSApp stop:nil];
+ }
+}
+
+@interface PcsxrController ()
+@property (readwrite) BOOL endAtEmuClose;
+@property BOOL sleepInBackground;
+@property BOOL wasPausedBeforeBGSwitch;
+@property BOOL wasPausedBeforeDiscEject;
+@property (strong) NSMutableArray *skipFiles;
+@property (strong) NSWindow *preferenceWindow;
+@property (strong) NSWindow *cheatWindow;
+@property (nonatomic) DASessionRef diskSession;
+@end
+
+@implementation PcsxrController
+@synthesize recentItems;
+@synthesize skipFiles;
+@synthesize cheatController;
+@synthesize cheatWindow;
+@synthesize preferenceWindow;
+
+- (BOOL)endAtEmuClose
+{
+ return PSXflags.endAtEmuClose;
+}
+
+- (void)setEndAtEmuClose:(BOOL)endAtEmuClose
+{
+ PSXflags.endAtEmuClose = endAtEmuClose;
+}
+
+- (BOOL)sleepInBackground
+{
+ return PSXflags.sleepInBackground;
+}
+
+- (void)setSleepInBackground:(BOOL)sleepInBackground
+{
+ PSXflags.sleepInBackground = sleepInBackground;
+}
+
+- (BOOL)wasPausedBeforeBGSwitch
+{
+ return PSXflags.wasPausedBeforeBGSwitch;
+}
+
+- (void)setWasPausedBeforeBGSwitch:(BOOL)wasPausedBeforeBGSwitch
+{
+ PSXflags.wasPausedBeforeBGSwitch = wasPausedBeforeBGSwitch;
+}
+
+- (BOOL)wasPausedBeforeDiscEject
+{
+ return PSXflags.wasPausedBeforeDiscEject;
+}
+
+-(void)setWasPausedBeforeDiscEject:(BOOL)wasPausedBeforeDiscEject
+{
+ PSXflags.wasPausedBeforeDiscEject = wasPausedBeforeDiscEject;
+}
+
+@synthesize diskSession = _diskSession;
+- (void)setDiskSession:(DASessionRef)diskSession
+{
+ if (diskSession == _diskSession) {
+ return;
+ }
+ if (_diskSession) {
+ CFRelease(_diskSession);
+ _diskSession = NULL;
+ }if (diskSession) {
+ _diskSession = diskSession;
+ CFRetain(diskSession);
+ }
+}
+
+static void PSXDiscAppearedCallback(DADiskRef disk, void *context)
+{
+ PcsxrController *theSelf = (__bridge PcsxrController*)context;
+ //sleep(3); //Is this needed?
+ SetCdOpenCaseTime(time(NULL) + 2);
+ LidInterrupt();
+
+ /* and open new cd */
+ if ([EmuThread active])
+ CDR_open();
+
+ if (!theSelf.wasPausedBeforeDiscEject) {
+ [EmuThread resume];
+ }
+
+ DASessionUnscheduleFromRunLoop(theSelf.diskSession, CFRunLoopGetMain(), kCFRunLoopCommonModes);
+ theSelf.diskSession = NULL;
+}
+
+- (IBAction)ejectCD:(id)sender
+{
+ self.wasPausedBeforeDiscEject = [EmuThread pauseSafe];
+
+ /* close connection to current cd */
+ if ([EmuThread active])
+ CDR_close();
+
+ // switch to another ISO if using internal image reader, otherwise eject the CD
+ if (UsingIso()) {
+ NSOpenPanel* openDlg = [NSOpenPanel openPanel];
+ [openDlg setAllowedFileTypes:[PcsxrDiscHandler supportedUTIs]];
+
+ if ([openDlg runModal] == NSFileHandlingPanelOKButton) {
+ NSArray* files = [openDlg URLs];
+ SetIsoFile([[files[0] path] fileSystemRepresentation]);
+ SetCdOpenCaseTime(time(NULL) + 2);
+ LidInterrupt();
+ }
+
+ if ([EmuThread active])
+ CDR_open();
+
+ if (!self.wasPausedBeforeDiscEject) {
+ [EmuThread resume];
+ }
+ } else {
+ NSMutableString *deviceName;
+ NSTask *ejectTask;
+ NSRange rdiskRange;
+ char *driveLetter = CDR_getDriveLetter();
+
+ if (driveLetter != NULL) {
+ deviceName = [NSMutableString stringWithString:[[NSFileManager defaultManager] stringWithFileSystemRepresentation:driveLetter length:strlen(driveLetter)]];
+
+ // delete the 'r' in 'rdisk'
+ rdiskRange = [deviceName rangeOfString:@"rdisk"];
+ if (rdiskRange.length != 0) {
+ rdiskRange.length = 1;
+ [deviceName deleteCharactersInRange:rdiskRange];
+ }
+ // execute hdiutil to eject the device
+ ejectTask = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/hdiutil" arguments:@[@"eject", deviceName]];
+ [ejectTask waitUntilExit];
+ }
+ DASessionRef tmpSession = DASessionCreate(kCFAllocatorDefault);
+ CFDictionaryRef match = CFBridgingRetain(@{(NSString*)kDADiskDescriptionMediaKindKey : @(kIOCDMediaClass),
+ (NSString*)kDADiskDescriptionMediaWholeKey : @YES});
+ DARegisterDiskAppearedCallback(tmpSession, match, PSXDiscAppearedCallback, (__bridge void*)self);
+ CFRelease(match);
+
+ DASessionScheduleWithRunLoop(tmpSession, CFRunLoopGetMain(), kCFRunLoopCommonModes);
+
+ self.diskSession = tmpSession;
+ CFRelease(tmpSession);
+ }
+}
+
+- (void)emuWindowDidClose:(NSNotification*)theNot
+{
+ if (self.diskSession) {
+ DASessionUnscheduleFromRunLoop(self.diskSession, CFRunLoopGetMain(), kCFRunLoopCommonModes);
+ self.diskSession = NULL;
+ }
+}
+
+- (IBAction)pause:(id)sender
+{
+ if ([EmuThread isPaused]) {
+ //[sender setState:NSOffState];
+ [EmuThread resume];
+ }
+ else {
+ //[sender setState:NSOnState];
+ [EmuThread pause];
+ }
+}
+
+- (IBAction)showCheatsWindow:(id)sender
+{
+ /* load the nib if it hasn't yet */
+ if (cheatWindow == nil) {
+ if (cheatController == nil) {
+ cheatController = [[CheatController alloc] initWithWindowNibName:@"CheatWindow"];
+ }
+ cheatWindow = [cheatController window];
+ }
+
+ /* show the window */
+ [cheatController showWindow:sender];
+}
+
+- (IBAction)preferences:(id)sender
+{
+ /* load the nib if it hasn't yet */
+ if (preferenceWindow == nil) {
+ if (preferencesController == nil) {
+ preferencesController = [[ConfigurationController alloc] initWithWindowNibName:@"Configuration"];
+ }
+ preferenceWindow = [preferencesController window];
+ }
+
+ /* show the window */
+ [preferencesController showWindow:sender];
+}
+
+- (IBAction)reset:(id)sender
+{
+ [EmuThread reset];
+}
+
+- (IBAction)runCD:(id)sender
+{
+ SetIsoFile(NULL);
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"NetPlay"]) {
+ [pluginList enableNetPlug];
+ } else {
+ [pluginList disableNetPlug];
+ }
+ [EmuThread run];
+}
+
+- (IBAction)runIso:(id)sender
+{
+ NSOpenPanel* openDlg = [NSOpenPanel openPanel];
+ [openDlg setAllowedFileTypes:[PcsxrDiscHandler supportedUTIs]];
+
+ if ([openDlg runModal] == NSFileHandlingPanelOKButton) {
+ NSURL *url = [openDlg URLs][0];
+ [recentItems addRecentItem:url];
+ [self runURL:url];
+ }
+}
+
+- (IBAction)runBios:(id)sender
+{
+ SetIsoFile(NULL);
+ [pluginList disableNetPlug];
+ [EmuThread runBios];
+}
+
+- (void)runURL:(NSURL*)url
+{
+ if ([EmuThread active] == YES) {
+ if (UsingIso()) {
+ SetIsoFile([[url path] fileSystemRepresentation]);
+ SetCdOpenCaseTime(time(NULL) + 2);
+ LidInterrupt();
+ } else {
+ NSBeep();
+ }
+ } else {
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"NetPlay"]) {
+ [pluginList enableNetPlug];
+ } else {
+ [pluginList disableNetPlug];
+ }
+ SetIsoFile([[url path] fileSystemRepresentation]);
+ [EmuThread run];
+ }
+}
+
+- (IBAction)freeze:(id)sender
+{
+ NSInteger num = [sender tag];
+ [PcsxrController saveState:(int)num];
+}
+
++ (void)saveState:(int)num
+{
+ [EmuThread freezeAt:[PcsxrController saveStatePath:num] which:num];
+}
+
+- (IBAction)defrost:(id)sender
+{
+ NSInteger num = [sender tag];
+ [PcsxrController loadState:(int)num];
+}
+
++ (void)loadState:(int)num
+{
+ [EmuThread defrostAt:[PcsxrController saveStatePath:num]];
+}
+
+- (IBAction)fullscreen:(id)sender
+{
+ GPU_keypressed(GPU_FULLSCREEN_KEY);
+}
+
+- (IBAction)pauseInBackground:(id)sender
+{
+ self.sleepInBackground = !self.sleepInBackground;
+ [[NSUserDefaults standardUserDefaults] setBool:self.sleepInBackground forKey:@"PauseInBackground"];
+}
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
+{
+ if ([menuItem action] == @selector(pause:)) {
+ [menuItem setState:([EmuThread isPaused] ? NSOnState : NSOffState)];
+ }
+
+ if ([menuItem action] == @selector(pause:) || [menuItem action] == @selector(fullscreen:))
+ return [EmuThread active];
+
+ if ([menuItem action] == @selector(reset:) || [menuItem action] == @selector(ejectCD:) ||
+ [menuItem action] == @selector(freeze:))
+ return [EmuThread active] && ![EmuThread isRunBios];
+
+ if ([menuItem action] == @selector(runCD:) || [menuItem action] == @selector(runIso:) ||
+ [menuItem action] == @selector(runBios:)) {
+ if (preferenceWindow != nil)
+ if ([preferenceWindow isVisible])
+ return NO;
+
+ if (cheatWindow != nil)
+ if ([cheatWindow isVisible])
+ return NO;
+
+ if ([menuItem action] == @selector(runBios:) && strcmp(Config.Bios, "HLE") == 0)
+ return NO;
+
+ return ![EmuThread active];
+ }
+
+ if ([menuItem action] == @selector(defrost:)) {
+ if (![EmuThread active] || [EmuThread isRunBios])
+ return NO;
+
+ NSString *path = [saveStatePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s-%3.3ld.pcsxrstate", CdromId, (long)[menuItem tag]]];
+ return (CheckState((char *)[path fileSystemRepresentation]) == 0);
+ }
+
+ if ([menuItem action] == @selector(preferences:))
+ return ![EmuThread active];
+
+ if ([menuItem action] == @selector(pauseInBackground:)) {
+ [menuItem setState:(self.sleepInBackground ? NSOnState : NSOffState)];
+ return YES;
+ }
+
+ return YES;
+}
+
+- (void)applicationWillResignActive:(NSNotification *)aNotification
+{
+ self.wasPausedBeforeBGSwitch = [EmuThread isPaused];
+
+ if (self.sleepInBackground) {
+ [EmuThread pause];
+ }
+}
+
+- (void)applicationDidBecomeActive:(NSNotification *)aNotification
+{
+ if (self.sleepInBackground && !self.wasPausedBeforeBGSwitch) {
+ [EmuThread resume];
+ }
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notification
+{
+ self.skipFiles = nil;
+}
+
+static void ParseErrorStr(NSString *errStr)
+{
+ NSLog(@"Parse error: %@", errStr);
+ NSRunCriticalAlertPanel(@"Parsing error", @"%@\n\nPlease check the command line options and try again.\n\nPCSXR will now quit.", nil, nil, nil, errStr);
+ ShowHelpAndExit(stderr, EXIT_FAILURE);
+}
+
+//DO NOT END THIS MACRO WITH A SIMICOLON! it will break the if-else if process
+#define HandleArg(arg, launchable, otherblock) \
+if ([[progArgs objectAtIndex:i] compare:arg options:NSCaseInsensitiveSearch] == NSOrderedSame) { \
+HandleArgBase(arg, launchable, otherblock)
+
+#define HandleArgElse(arg, launchable, otherblock) \
+else if ([[progArgs objectAtIndex:i] compare:arg options:NSCaseInsensitiveSearch] == NSOrderedSame) { \
+HandleArgBase(arg, launchable, otherblock)
+
+#define HandleArgBase(arg, launchable, otherblock) \
+if (isLaunchable && launchable) { \
+ParseErrorStr([NSString stringWithFormat:@"The options %@ and %@ are exclusive.", arg, runtimeStr]); \
+} \
+if(launchable) { \
+isLaunchable = YES; \
+runtimeStr = arg; \
+} \
+otherblock();\
+}
+
+#define kPCSXRArgumentCDROM @"--cdrom"
+#define kPCSXRArgumentBIOS @"--bios"
+#define kPCSXRArgumentISO @"--iso"
+#define kPCSXRArgumentMcd @"--mcd"
+#define kPCSXRArgumentMcd1 kPCSXRArgumentMcd @"1"
+#define kPCSXRArgumentMcd2 kPCSXRArgumentMcd @"2"
+#define kPCSXRArgumentFreeze @"--freeze"
+#define kPCSXRArgumentExitAtClose @"--exitAtClose"
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (void)awakeFromNib
+{
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self selector:@selector(emuWindowDidClose:)
+ name:@"emuWindowDidClose" object:nil];
+
+ pluginList = [[PluginList alloc] init];
+ if (![pluginList configured] /*!Config.Gpu[0] || !Config.Spu[0] || !Config.Pad1[0] || !Config.Cdr[0]*/) {
+ // configure plugins
+ [self preferences:nil];
+
+ NSRunCriticalAlertPanel(NSLocalizedString(@"Missing plugins!", nil),
+ NSLocalizedString(@"Pcsxr is missing one or more critical plugins. You will need to install these in order to play games.", nil),
+ nil, nil, nil);
+ }
+
+ if (![PcsxrController biosAvailable]) {
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSURL *supportURL = [manager URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
+ NSURL *biosURL = [[supportURL URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"Bios"];
+ NSInteger retVal = NSRunInformationalAlertPanel(NSLocalizedString(@"Missing BIOS!", nil),
+ NSLocalizedString(@"Pcsxr wasn't able to locate any Playstation BIOS ROM files. This means that it will run in BIOS simulation mode which is less stable and compatible than using a real Playstation BIOS.\nIf you have a BIOS available, please copy it to\n%@", nil),
+ NSLocalizedString(@"Okay", @"OK"), NSLocalizedString(@"Show Folder", @"Show Folder"), nil, [[biosURL path] stringByAbbreviatingWithTildeInPath]);
+ if (retVal == NSAlertAlternateReturn) {
+ [[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[biosURL]];
+ }
+ }
+
+ self.sleepInBackground = [[NSUserDefaults standardUserDefaults] boolForKey:@"PauseInBackground"];
+
+ NSArray *progArgs = [[NSProcessInfo processInfo] arguments];
+ if ([progArgs count] > 1 && !wasFinderLaunch) {
+ self.skipFiles = [NSMutableArray array];
+
+ BOOL isLaunchable = NO;
+ NSString *runtimeStr = nil;
+
+ __block short memcardHandled = 0;
+ __block BOOL hasParsedAnArgument = NO;
+ __block NSString *(^FileTestBlock)() = NULL;
+ __block NSMutableDictionary *argDict = [[NSMutableDictionary alloc] initWithCapacity:[progArgs count]];
+
+
+ NSMutableArray *unknownOptions = [NSMutableArray array];
+
+ dispatch_block_t cdromBlock = ^{
+ hasParsedAnArgument = YES;
+ LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgRun argument:kPCSXRArgumentCDROM block:^{
+ [self runCD:nil];
+ }];
+ [larg addToDictionary:argDict];
+ };
+
+ dispatch_block_t biosBlock = ^{
+ hasParsedAnArgument = YES;
+ LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgRun argument:kPCSXRArgumentBIOS block:^{
+ [self runBios:nil];
+ }];
+ [larg addToDictionary:argDict];
+ };
+
+ //This block/argument does not need to be sorted
+ dispatch_block_t emuCloseAtEnd = ^{
+ hasParsedAnArgument = YES;
+ LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgPreRun argument:kPCSXRArgumentExitAtClose block:^{
+ self.endAtEmuClose = YES;
+ }];
+ [larg addToDictionary:argDict];
+ };
+
+ dispatch_block_t isoBlock = ^{
+ hasParsedAnArgument = YES;
+ NSString *path = FileTestBlock();
+ LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgRun argument:kPCSXRArgumentISO block:^{
+ [self runURL:[NSURL fileURLWithPath:path isDirectory:NO]];
+ }];
+ [larg addToDictionary:argDict];
+ };
+
+ void (^mcdBlock)(int mcdNumber) = ^(int mcdnumber){
+ hasParsedAnArgument = YES;
+ if (memcardHandled & (1 << mcdnumber)) {
+ NSLog(@"Memory card %i has already been defined. The latest one passed will be used.", mcdnumber);
+ } else {
+ memcardHandled |= (1 << mcdnumber);
+ }
+
+ NSString *path = FileTestBlock();
+ NSString *mcdArg = [kPCSXRArgumentMcd stringByAppendingFormat:@"%i", mcdnumber];
+ LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgPreRun argument:mcdArg block:^{
+ LoadMcd(mcdnumber, (char*)[path fileSystemRepresentation]);
+ }];
+ [larg addToDictionary:argDict];
+ };
+
+ dispatch_block_t freezeBlock = ^{
+ hasParsedAnArgument = YES;
+ NSString *path = FileTestBlock();
+ LaunchArg *larg = [[LaunchArg alloc] initWithLaunchOrder:LaunchArgPostRun argument:kPCSXRArgumentFreeze block:^{
+ if (![EmuThread isRunBios]) {
+ //Make sure the emulator is running
+ sleep(5);
+ [EmuThread defrostAt:path];
+ }
+ }];
+ [larg addToDictionary:argDict];
+ };
+
+ BOOL hasFileTestBlock = NO;
+
+ for (__block int i = 1; i < [progArgs count]; i++) {
+ if (!hasFileTestBlock)
+ {
+ FileTestBlock = ^NSString *(){
+ if ([progArgs count] <= ++i) {
+ ParseErrorStr(@"Not enough arguments.");
+ }
+ NSString *path = [progArgs[i] stringByExpandingTildeInPath];
+ if (![[NSFileManager defaultManager] fileExistsAtPath:path])
+ {
+ ParseErrorStr([NSString stringWithFormat:@"The file \"%@\" does not exist.", path]);
+ return nil;
+ }
+ [skipFiles addObject:path];
+ return path;
+ };
+ hasFileTestBlock = YES;
+ }
+
+ //DO NOT END these MACROS WITH A SIMICOLON! It will break the if-else if process
+ HandleArg(kPCSXRArgumentISO, YES, isoBlock)
+ HandleArgElse(kPCSXRArgumentCDROM, YES, cdromBlock)
+ HandleArgElse(kPCSXRArgumentBIOS, YES, biosBlock)
+ HandleArgElse(kPCSXRArgumentExitAtClose, NO, emuCloseAtEnd)
+ HandleArgElse(kPCSXRArgumentMcd1, NO, ^{mcdBlock(1);})
+ HandleArgElse(kPCSXRArgumentMcd2, NO, ^{mcdBlock(2);})
+ HandleArgElse(kPCSXRArgumentFreeze, NO, freezeBlock)
+ else {
+ [unknownOptions addObject:progArgs[i]];
+ }
+ }
+#ifdef DEBUG
+ if ([unknownOptions count]) {
+ NSString *unknownString = [unknownOptions componentsJoinedByString:@" "];
+
+ NSLog(@"The following options weren't recognized by PCSX-R: %@. This may be due to extra arguments passed by the OS or debugger.", unknownString);
+ }
+#endif
+ unknownOptions = nil;
+ if (!isLaunchable && hasParsedAnArgument) {
+ NSMutableArray *mutProgArgs = [NSMutableArray arrayWithArray:progArgs];
+ NSString *appRawPath = mutProgArgs[0];
+ //Remove the app file path from the array
+ [mutProgArgs removeObjectAtIndex:0];
+ NSString *arg = [mutProgArgs componentsJoinedByString:@" "];
+ NSString *recognizedArgs = [[argDict allKeys] componentsJoinedByString:@" "];
+
+ NSString *tmpStr = [NSString stringWithFormat:@"A launch command wasn't found in the command line and one or more arguments that PCSX-R recognizes were: %@.\nThe following command line arguments were passed with the application launch file at %@: %@.\n\nThe valid launch commands are %@, %@, and %@.", recognizedArgs, appRawPath, arg, kPCSXRArgumentISO, kPCSXRArgumentCDROM, kPCSXRArgumentBIOS];
+ ParseErrorStr(tmpStr);
+ } else if (hasParsedAnArgument){
+ NSArray *argArray = [[argDict allValues] sortedArrayWithOptions:NSSortStable usingComparator:^NSComparisonResult(id obj1, id obj2) {
+ LaunchArg *LA1 = obj1;
+ LaunchArg *LA2 = obj2;
+ if (LA1.launchOrder > LA2.launchOrder) {
+ return NSOrderedDescending;
+ } else if (LA1.launchOrder < LA2.launchOrder) {
+ return NSOrderedAscending;
+ } else {
+ return NSOrderedSame;
+ }
+ }];
+ for (LaunchArg *arg in argArray) {
+ arg.theBlock();
+ }
+ }
+ }
+}
+
++ (void)setConfigFromDefaults
+{
+ const char *str;
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ /*
+ enumerator = [prefStringKeys keyEnumerator];
+ while ((key = [enumerator nextObject])) {
+ str = [[defaults stringForKey:key] fileSystemRepresentation];
+ char *dst = (char *)[[prefStringKeys objectForKey:key] pointerValue];
+ if (str != nil && dst != nil) strncpy(dst, str, 255);
+ }*/
+
+ for (NSString *key in prefByteKeys) {
+ u8 *dst = (u8 *)[prefByteKeys[key] pointerValue];
+ if (dst != NULL) *dst = [defaults boolForKey:key];
+ }
+
+ // special cases
+ //str = [[defaults stringForKey:@"PluginPAD"] fileSystemRepresentation];
+ //if (str != nil) strncpy(Config.Pad2, str, 255);
+
+ str = [[defaults stringForKey:@"Bios"] fileSystemRepresentation];
+ if (str) {
+ NSString *path = [defaults stringForKey:@"Bios"];
+ NSInteger index = [biosList indexOfObject:path];
+
+ if (-1 == index) {
+ [biosList insertObject:path atIndex:0];
+ } else if (0 < index) {
+ [biosList exchangeObjectAtIndex:index withObjectAtIndex:0];
+ }
+ }
+
+ {
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSURL *memoryURL = [[[manager URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL] URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"Memory Cards"];
+
+ str = [[[defaults URLForKey:@"Mcd1"] path] fileSystemRepresentation];
+ if (str) {
+ strlcpy(Config.Mcd1, str, MAXPATHLEN);
+ } else {
+ NSURL *url = [memoryURL URLByAppendingPathComponent:@"Mcd001.mcr"];
+ [defaults setURL:url forKey:@"Mcd1"];
+ str = [[url path] fileSystemRepresentation];
+ if (str != nil) strlcpy(Config.Mcd1, str, MAXPATHLEN);
+ }
+
+ str = [[[defaults URLForKey:@"Mcd2"] path] fileSystemRepresentation];
+ if (str) {
+ strlcpy(Config.Mcd2, str, MAXPATHLEN);
+ } else {
+ NSURL *url = [memoryURL URLByAppendingPathComponent:@"Mcd002.mcr"];
+ [defaults setURL:url forKey:@"Mcd2"];
+ str = [[url path] fileSystemRepresentation];
+ if (str != nil) strlcpy(Config.Mcd2, str, MAXPATHLEN);
+ }
+ }
+
+ if ([defaults boolForKey:@"UseHLE"] || 0 == [biosList count]) {
+ strcpy(Config.Bios, "HLE");
+ } else {
+ str = [(NSString *)biosList[0] fileSystemRepresentation];
+ if (str != nil) strlcpy(Config.Bios, str, MAXPATHLEN);
+ else strcpy(Config.Bios, "HLE");
+ }
+
+ str = [[defaults stringForKey:@"Net"] fileSystemRepresentation];
+ if (str) strlcpy(Config.Net, str, MAXPATHLEN);
+ else {
+ strcpy(Config.Net, "Disabled");
+ }
+}
+
++ (void)setDefaultFromConfig:(NSString *)defaultKey
+{
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+
+ char *str = (char *)[prefStringKeys[defaultKey] pointerValue];
+ if (str) {
+ NSString *tmpNSStr = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:str length:strlen(str)];
+ if (!tmpNSStr) {
+ tmpNSStr = @(str);
+ }
+
+ [defaults setObject:tmpNSStr forKey:defaultKey];
+ return;
+ }
+
+ str = (char *)[prefURLKeys[defaultKey] pointerValue];
+ if (str) {
+ NSString *tmpNSStr = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:str length:strlen(str)];
+ if (!tmpNSStr) {
+ tmpNSStr = @(str);
+ }
+ [defaults setURL:[NSURL fileURLWithPath:tmpNSStr isDirectory:NO] forKey:defaultKey];
+ return;
+ }
+
+ u8 *val = (u8 *)[prefByteKeys[defaultKey] pointerValue];
+ if (val) {
+ [defaults setInteger:*val forKey:defaultKey];
+ return;
+ }
+}
+
++ (BOOL)biosAvailable
+{
+ return ([biosList count] > 0);
+}
+
+// called when class is initialized
++ (void)initialize
+{
+ NSString *path;
+ const char *str;
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ NSDictionary *appDefaults = @{@"NoDynarec": @YES,
+ @"AutoDetectVideoType": @YES,
+ @"UseHLE": @NO,
+ @"PauseInBackground": @YES,
+ @"Widescreen": @NO,
+ @"NetPlay": @NO};
+
+ [defaults registerDefaults:appDefaults];
+
+ prefStringKeys = @{@"PluginGPU": [NSValue valueWithPointer:Config.Gpu],
+ @"PluginSPU": [NSValue valueWithPointer:Config.Spu],
+ @"PluginPAD": [NSValue valueWithPointer:Config.Pad1],
+ @"PluginCDR": [NSValue valueWithPointer:Config.Cdr],
+ @"PluginNET": [NSValue valueWithPointer:Config.Net],
+ @"PluginSIO1": [NSValue valueWithPointer:Config.Sio1]};
+
+ prefURLKeys = @{@"Mcd1": [NSValue valueWithPointer:Config.Mcd1],
+ @"Mcd2": [NSValue valueWithPointer:Config.Mcd2]};
+
+ prefByteKeys = @{@"NoXaAudio": [NSValue valueWithPointer:&Config.Xa],
+ @"SioIrqAlways": [NSValue valueWithPointer:&Config.SioIrq],
+ @"BlackAndWhiteMDECVideo": [NSValue valueWithPointer:&Config.Mdec],
+ @"AutoDetectVideoType": [NSValue valueWithPointer:&Config.PsxAuto],
+ @"VideoTypePAL": [NSValue valueWithPointer:&Config.PsxType],
+ @"NoCDAudio": [NSValue valueWithPointer:&Config.Cdda],
+ @"NoDynarec": [NSValue valueWithPointer:&Config.Cpu],
+ @"ConsoleOutput": [NSValue valueWithPointer:&Config.PsxOut],
+ @"SpuIrqAlways": [NSValue valueWithPointer:&Config.SpuIrq],
+ @"RootCounterFix": [NSValue valueWithPointer:&Config.RCntFix],
+ @"VideoSyncWAFix": [NSValue valueWithPointer:&Config.VSyncWA],
+ @"Widescreen": [NSValue valueWithPointer:&Config.Widescreen]};
+
+ // setup application support paths
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSURL *supportURL = [manager URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
+
+ if(supportURL != nil) {
+ NSURL *PcsxrAppSupport;
+ NSURL *MemCardPath;
+ NSURL *url;
+ BOOL dir;
+
+ PcsxrAppSupport = [supportURL URLByAppendingPathComponent:@"Pcsxr"];
+
+ // create them if needed
+ url = [PcsxrAppSupport URLByAppendingPathComponent:@"Bios"];
+ if (![url checkResourceIsReachableAndReturnError:NULL])
+ [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
+
+ MemCardPath = [PcsxrAppSupport URLByAppendingPathComponent:@"Memory Cards"];
+ url = MemCardPath;
+ if (![url checkResourceIsReachableAndReturnError:NULL])
+ [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
+
+ url = [PcsxrAppSupport URLByAppendingPathComponent:@"Patches"];
+ if (![url checkResourceIsReachableAndReturnError:NULL])
+ [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
+
+ url = [PcsxrAppSupport URLByAppendingPathComponent:@"PlugIns"];
+ if (![url checkResourceIsReachableAndReturnError:NULL])
+ [manager createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
+
+ saveStatePath = [[[PcsxrAppSupport URLByAppendingPathComponent:@"Save States"] path] copy];
+ if (![manager fileExistsAtPath:saveStatePath isDirectory:&dir])
+ [manager createDirectoryAtPath:saveStatePath withIntermediateDirectories:YES attributes:nil error:NULL];
+
+ url = [MemCardPath URLByAppendingPathComponent:@"Mcd001.mcr"];
+ str = [[url path] fileSystemRepresentation];
+ if (str != nil)
+ strlcpy(Config.Mcd1, str, MAXPATHLEN);
+
+ url = [MemCardPath URLByAppendingPathComponent:@"Mcd002.mcr"];
+ str = [[url path] fileSystemRepresentation];
+ if (str != nil)
+ strlcpy(Config.Mcd2, str, MAXPATHLEN);
+
+ url = [PcsxrAppSupport URLByAppendingPathComponent:@"Bios"];
+ str = [[url path] fileSystemRepresentation];
+ if (str != nil)
+ strlcpy(Config.BiosDir, str, MAXPATHLEN);
+
+ url = [PcsxrAppSupport URLByAppendingPathComponent:@"Patches"];
+ str = [[url path] fileSystemRepresentation];
+ if (str != nil) {
+ strlcpy(Config.PatchesDir, str, MAXPATHLEN);
+ }
+ } else {
+ strcpy(Config.BiosDir, "Bios/");
+ strcpy(Config.PatchesDir, "Patches/");
+
+ //NSString constants don't need to be retained/released. In fact, retain/releasing them does nothing.
+ saveStatePath = @"sstates";
+ }
+
+ // set plugin path
+ path = [[NSBundle mainBundle] builtInPlugInsPath];
+ str = [path fileSystemRepresentation];
+ if (str != nil)
+ strlcpy(Config.PluginsDir, str, MAXPATHLEN);
+
+ // locate a bios
+ biosList = [[NSMutableArray alloc] init];
+
+ NSString *biosDir = [manager stringWithFileSystemRepresentation:Config.BiosDir length:strlen(Config.BiosDir)];
+ NSArray *bioses = [manager contentsOfDirectoryAtPath:biosDir error:NULL];
+ if (bioses) {
+ for (NSString *file in bioses) {
+ NSDictionary *attrib = [manager attributesOfItemAtPath:[[biosDir stringByAppendingPathComponent:file] stringByResolvingSymlinksInPath] error:NULL];
+
+ if ([[attrib fileType] isEqualToString:NSFileTypeRegular]) {
+ unsigned long long size = [attrib fileSize];
+ if ((size % (256 * 1024)) == 0 && size > 0) {
+ [biosList addObject:file];
+ }
+ }
+ }
+ }
+
+ [PcsxrController setConfigFromDefaults];
+}
+
++ (NSString*)saveStatePath:(int)slot
+{
+ if(slot >= 0) {
+ return [saveStatePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%s-%3.3d.pcsxrstate", CdromId, slot]];
+ }
+
+ return saveStatePath;
+}
+
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
+{
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSWorkspace *workspace = [NSWorkspace sharedWorkspace];
+ if (skipFiles && [skipFiles count]) {
+ for (NSString *parsedFile in skipFiles) {
+ if ([filename isEqualToString:parsedFile]) {
+ return YES;
+ }
+ }
+ }
+
+ if (![fm fileExistsAtPath:filename]) {
+ NSLog(@"Nonexistant file %@ was passed to open.", filename );
+ return NO;
+ }
+
+ if ([[filename pathExtension] compare:@"bin" options:(NSCaseInsensitiveSearch | NSWidthInsensitiveSearch)]) {
+ NSDictionary *attrib = [fm attributesOfItemAtPath:filename error:NULL];
+ if ([[attrib fileType] isEqualToString:NSFileTypeRegular] && ([attrib fileSize] % (256 * 1024)) == 0 && [attrib fileSize] > 0 ) {
+ NSAlert *biosInfo = [NSAlert alertWithMessageText:NSLocalizedString(@"PlayStation BIOS File", @"PSX BIOS File") defaultButton:NSLocalizedString(@"BIOS_Copy", @"copy the BIOS over") alternateButton:NSLocalizedString(@"Cancel", @"Cancel") otherButton:NSLocalizedString(@"BIOS_Move", @"Move the bios over") informativeTextWithFormat:NSLocalizedString(@"The file \"%@\" seems to be a BIOS file. Do you want PCSX-R to copy it to the proper location?", @"Can we copy the BIOS?")];
+ biosInfo.alertStyle = NSInformationalAlertStyle;
+ switch ([biosInfo runModal]) {
+ case NSAlertFirstButtonReturn:
+ case NSAlertDefaultReturn:
+ {
+ NSError *theErr = nil;
+ NSURL *biosDirPath = [NSURL fileURLWithPath:[fm stringWithFileSystemRepresentation:Config.BiosDir length:strlen(Config.BiosDir)] isDirectory:YES];
+ NSURL *biosPath = [biosDirPath URLByAppendingPathComponent:[filename lastPathComponent]];
+ if ([biosPath checkResourceIsReachableAndReturnError:NULL]) {
+ NSAlert *alreadyThere = [NSAlert alertWithMessageText:NSLocalizedString(@"BIOS Already Exists", @"BIOS file already there.") defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"There already exists a BIOS file at \"%1$@\": not copying the file at \"%2$@\".\n\nIf you do want to use the BIOS file at \"%2$@\", delete the BIOS at \"%1$@\".", @"What to do"), [biosPath path], filename];
+ alreadyThere.alertStyle = NSCriticalAlertStyle;
+ [alreadyThere runModal];
+ return NO;
+ }
+ if (![fm copyItemAtURL:[NSURL fileURLWithPath:filename isDirectory:NO] toURL:biosPath error:&theErr]) {
+ [[NSAlert alertWithError:theErr] runModal];
+ return NO;
+ }
+ }
+ break;
+
+ case NSAlertThirdButtonReturn:
+ case NSAlertOtherReturn:
+ {
+ NSError *theErr = nil;
+ NSURL *biosDirPath = [NSURL fileURLWithPath:[fm stringWithFileSystemRepresentation:Config.BiosDir length:strlen(Config.BiosDir)] isDirectory:YES];
+ NSURL *biosPath = [biosDirPath URLByAppendingPathComponent:[filename lastPathComponent]];
+ if ([biosPath checkResourceIsReachableAndReturnError:NULL]) {
+ NSAlert *alreadyThere = [NSAlert alertWithMessageText:NSLocalizedString(@"BIOS Already Exists", @"BIOS file already there.") defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:NSLocalizedString(@"There already exists a BIOS file at \"%1$@\": not moving the file at \"%2$@\".\n\nIf you do want to use the BIOS file at \"%2$@\", delete the BIOS at \"%1$@\".", @"What to do"), [biosPath path], filename];
+ alreadyThere.alertStyle = NSCriticalAlertStyle;
+ [alreadyThere runModal];
+ return NO;
+ }
+ if (![fm moveItemAtURL:[NSURL fileURLWithPath:filename isDirectory:NO] toURL:biosPath error:&theErr]) {
+ [[NSAlert alertWithError:theErr] runModal];
+ return NO;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return YES;
+ }
+ }
+
+ NSError *err = nil;
+ NSString *utiFile = [workspace typeOfFile:filename error:&err];
+ if (err) {
+ NSRunAlertPanel(NSLocalizedString(@"Error opening file", nil), NSLocalizedString(@"Unable to open %@: %@", nil), nil, nil, nil, [filename lastPathComponent], err);
+ return NO;
+ }
+ static NSArray *handlers = nil;
+ if (handlers == nil) {
+ handlers = @[[PcsxrPluginHandler class], [PcsxrMemCardHandler class], [PcsxrFreezeStateHandler class], [PcsxrDiscHandler class], [PcsxrCheatHandler class]];
+ }
+ BOOL isHandled = NO;
+ for (Class fileHandler in handlers) {
+ NSObject<PcsxrFileHandle> *hand = [[fileHandler alloc] init];
+ BOOL canHandle = NO;
+ for (NSString *uti in [fileHandler supportedUTIs]) {
+ if ([workspace type:utiFile conformsToType:uti]) {
+ canHandle = YES;
+ break;
+ }
+ }
+ if (canHandle) {
+ isHandled = [hand handleFile:filename];
+ break;
+ }
+ }
+
+ return isHandled;
+}
+
+@end
diff --git a/macosx/Source/PcsxrDiscHandler.h b/macosx/Source/PcsxrDiscHandler.h
new file mode 100644
index 00000000..933d7799
--- /dev/null
+++ b/macosx/Source/PcsxrDiscHandler.h
@@ -0,0 +1,14 @@
+//
+// PcsxrDiscHandler.h
+// Pcsxr
+//
+// Created by Charles Betts on 12/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "PcsxrFileHandle.h"
+
+@interface PcsxrDiscHandler : NSObject <PcsxrFileHandle>
+
+@end
diff --git a/macosx/Source/PcsxrDiscHandler.m b/macosx/Source/PcsxrDiscHandler.m
new file mode 100644
index 00000000..996fdab3
--- /dev/null
+++ b/macosx/Source/PcsxrDiscHandler.m
@@ -0,0 +1,58 @@
+//
+// PcsxrDiscHandler.m
+// Pcsxr
+//
+// Created by Charles Betts on 12/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "PcsxrDiscHandler.h"
+#import "EmuThread.h"
+#include "psxcommon.h"
+#include "plugins.h"
+#include "cdrom.h"
+#import "RecentItemsMenu.h"
+#import "PcsxrController.h"
+
+@interface PcsxrDiscHandler ()
+@property (nonatomic, strong) NSURL *discURL;
+@property (weak) NSString *discPath;
+@end
+
+@implementation PcsxrDiscHandler
+@synthesize discURL = _discURL;
+- (NSURL*)discURL
+{
+ if (!_discURL) {
+ self.discURL = [NSURL fileURLWithPath:discPath];
+ }
+ return _discURL;
+}
+
+@synthesize discPath;
+
++ (NSArray *)supportedUTIs
+{
+ static NSArray *utisupport = nil;
+ if (utisupport == nil) {
+ utisupport = @[@"com.alcohol-soft.mdfdisc", @"com.goldenhawk.cdrwin-cuesheet",
+ @"com.apple.disk-image-ndif", @"public.iso-image", @"com.sony.psp.firmware",
+ @"com.codeplex.pcsxr.compressed-bin-image", @"com.coppertino.vox.cue",
+ @"com.apple.macbinary-​archive"];
+ }
+ return utisupport;
+}
+
+- (BOOL)handleFile:(NSString *)theFile
+{
+ self.discPath = theFile;
+ PcsxrController *appDelegate = [NSApp delegate];
+ if ([EmuThread active] == YES && !UsingIso()) {
+ return NO;
+ }
+ [appDelegate runURL:[self discURL]];
+ [[appDelegate recentItems] addRecentItem:[self discURL]];
+ return YES;
+}
+
+@end
diff --git a/macosx/Source/PcsxrFileHandle.h b/macosx/Source/PcsxrFileHandle.h
new file mode 100644
index 00000000..6c663fae
--- /dev/null
+++ b/macosx/Source/PcsxrFileHandle.h
@@ -0,0 +1,14 @@
+//
+// PcsxrFileHandle.h
+// Pcsxr
+//
+// Created by Charles Betts on 12/10/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol PcsxrFileHandle <NSObject>
++ (NSArray *)supportedUTIs;
+- (BOOL)handleFile:(NSString *)theFile;
+@end
diff --git a/macosx/Source/PcsxrFreezeStateHandler.h b/macosx/Source/PcsxrFreezeStateHandler.h
new file mode 100644
index 00000000..ca50f533
--- /dev/null
+++ b/macosx/Source/PcsxrFreezeStateHandler.h
@@ -0,0 +1,14 @@
+//
+// PcsxrFreezeStateHandler.h
+// Pcsxr
+//
+// Created by Charles Betts on 12/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#import "PcsxrFileHandle.h"
+
+@interface PcsxrFreezeStateHandler : NSObject <PcsxrFileHandle>
+
+@end
diff --git a/macosx/Source/PcsxrFreezeStateHandler.m b/macosx/Source/PcsxrFreezeStateHandler.m
new file mode 100644
index 00000000..b300fe02
--- /dev/null
+++ b/macosx/Source/PcsxrFreezeStateHandler.m
@@ -0,0 +1,43 @@
+//
+// PcsxrFreezeStateHandler.m
+// Pcsxr
+//
+// Created by Charles Betts on 12/11/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "PcsxrFreezeStateHandler.h"
+#import "EmuThread.h"
+#import "PluginList.h"
+#include "misc.h"
+
+@implementation PcsxrFreezeStateHandler
+
++ (NSArray *)supportedUTIs
+{
+ static NSArray *utisupport = nil;
+ if (utisupport == nil) {
+ utisupport = @[@"com.codeplex.pcsxr.freeze"];
+ }
+ return utisupport;
+}
+
+- (BOOL)handleFile:(NSString *)theFile
+{
+ if (CheckState([theFile fileSystemRepresentation]) != 0) {
+ return NO;
+ }
+ if (![EmuThread active]) {
+ PluginList *pluginList = [PluginList list];
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"NetPlay"]) {
+ [pluginList enableNetPlug];
+ } else {
+ [pluginList disableNetPlug];
+ }
+
+ [EmuThread run];
+ }
+ return [EmuThread defrostAt:theFile];
+}
+
+@end
diff --git a/macosx/Source/PcsxrHexadecimalFormatter.h b/macosx/Source/PcsxrHexadecimalFormatter.h
new file mode 100644
index 00000000..63a3da44
--- /dev/null
+++ b/macosx/Source/PcsxrHexadecimalFormatter.h
@@ -0,0 +1,14 @@
+//
+// PcsxrHexadecimalFormatter.h
+// Pcsxr
+//
+// Created by C.W. Betts on 8/17/13.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+@interface PcsxrHexadecimalFormatter : NSFormatter
+@property (nonatomic) char hexPadding;
+
+@end
diff --git a/macosx/Source/PcsxrHexadecimalFormatter.m b/macosx/Source/PcsxrHexadecimalFormatter.m
new file mode 100644
index 00000000..550a7cb1
--- /dev/null
+++ b/macosx/Source/PcsxrHexadecimalFormatter.m
@@ -0,0 +1,88 @@
+//
+// PcsxrHexadecimalFormatter.m
+// Pcsxr
+//
+// Created by C.W. Betts on 8/17/13.
+//
+//
+
+#import "PcsxrHexadecimalFormatter.h"
+
+@interface PcsxrHexadecimalFormatter ()
+@property (strong) NSString *hexFormatString;
+@end
+
+@implementation PcsxrHexadecimalFormatter
+@synthesize hexPadding;
+@synthesize hexFormatString;
+
+- (void)setHexPadding:(char)_hexPadding
+{
+ hexPadding = _hexPadding;
+ self.hexFormatString = [NSString stringWithFormat:@"0x%%0%ilx", hexPadding];
+}
+
+- (id)init
+{
+ if (self = [super init]) {
+#ifdef __LP64__
+ self.hexPadding = 16;
+#else
+ self.hexPadding = 8;
+#endif
+ }
+ return self;
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ if (self = [super initWithCoder:aDecoder]) {
+#ifdef __LP64__
+ self.hexPadding = 16;
+#else
+ self.hexPadding = 8;
+#endif
+ }
+ return self;
+}
+
+- (NSString *)stringForObjectValue:(id)obj
+{
+ if ([obj isKindOfClass:[NSNumber class]]) {
+ return [NSString stringWithFormat:self.hexFormatString, (long)[obj integerValue]];
+ } else return nil;
+}
+
+- (NSString *)editingStringForObjectValue:(id)obj
+{
+ if ([obj isKindOfClass:[NSNumber class]]) {
+ return [NSString stringWithFormat:@"%lx", (long)[obj integerValue]];
+ } else return nil;
+}
+
+- (BOOL)getObjectValue:(out id *)obj forString:(NSString *)string errorDescription:(out NSString **)error
+{
+ NSString *tmpstr = nil;
+ unsigned int tmpNum;
+ NSScanner *theScan = [[NSScanner alloc] initWithString:string];
+ if ([theScan scanHexInt:&tmpNum]) {
+ *obj = @(tmpNum);
+ return YES;
+ } else {
+ if ([string hasPrefix:@"0x"]) {
+ NSRange zeroXRange = [string rangeOfString:@"0x"];
+ tmpstr = [string stringByReplacingCharactersInRange:zeroXRange withString:@""];
+ }else {
+ tmpstr = string;
+ }
+ long tmpNum = 0;
+ if (sscanf([tmpstr UTF8String], "%lx", &tmpNum) == 1) {
+ *obj = @(tmpNum);
+ return YES;
+ } else {
+ return NO;
+ }
+ }
+}
+
+@end
diff --git a/macosx/Source/PcsxrMemCardArray.h b/macosx/Source/PcsxrMemCardArray.h
new file mode 100644
index 00000000..21b45f3c
--- /dev/null
+++ b/macosx/Source/PcsxrMemCardArray.h
@@ -0,0 +1,40 @@
+//
+// PcsxrMemCardArray.h
+// Pcsxr
+//
+// Created by C.W. Betts on 7/6/13.
+//
+//
+
+#import <Foundation/Foundation.h>
+#import "PcsxrMemoryObject.h"
+
+@interface PcsxrMemCardArray : NSObject
+
+- (id)initWithMemoryCardNumber:(int)carNum;
+
+- (void)deleteMemoryBlocksAtIndex:(int)slotnum;
+- (void)compactMemory;
+
+/**
+ * @fn freeBlocks
+ * @abstract Blocks that are free from any data
+ * @result free blocks
+ */
+- (int)freeBlocks;
+
+/**
+ * @fn availableBlocks
+ * @abstract Blocks that have been deleted
+ * @result free blocks
+ */
+- (int)availableBlocks;
+- (int)memorySizeAtIndex:(int)idx;
+- (BOOL)moveBlockAtIndex:(int)idx toMemoryCard:(PcsxrMemCardArray*)otherCard;
+- (int)indexOfFreeBlocksWithSize:(int)asize;
+
+@property (nonatomic, readonly, unsafe_unretained) NSArray *memoryArray;
+@property (nonatomic, readonly, unsafe_unretained) NSURL *memCardURL;
+@property (nonatomic, readonly) const char *memCardCPath;
+
+@end
diff --git a/macosx/Source/PcsxrMemCardArray.m b/macosx/Source/PcsxrMemCardArray.m
new file mode 100644
index 00000000..57de81ae
--- /dev/null
+++ b/macosx/Source/PcsxrMemCardArray.m
@@ -0,0 +1,345 @@
+//
+// PcsxrMemCardArray.m
+// Pcsxr
+//
+// Created by C.W. Betts on 7/6/13.
+//
+//
+
+#import "PcsxrMemCardArray.h"
+#import "ConfigurationController.h"
+#include "sio.h"
+
+#define MAX_MEMCARD_BLOCKS 15
+
+static inline void CopyMemcardData(char *from, char *to, int srci, int dsti, char *str)
+{
+ // header
+ memmove(to + (dsti + 1) * 128, from + (srci + 1) * 128, 128);
+ SaveMcd(str, to, (dsti + 1) * 128, 128);
+
+ // data
+ memmove(to + (dsti + 1) * 1024 * 8, from + (srci+1) * 1024 * 8, 1024 * 8);
+ SaveMcd(str, to, (dsti + 1) * 1024 * 8, 1024 * 8);
+}
+
+static inline char* BlankHeader()
+{
+ struct PSXMemHeader {
+ unsigned int allocState;
+ unsigned int fileSize;
+ unsigned short nextBlock;
+ char fileName[21];
+ unsigned char garbage[96];
+ unsigned char checksum;
+ };
+
+ static struct PSXMemHeader *toReturn = NULL;
+ if (!toReturn) {
+ toReturn = calloc(sizeof(struct PSXMemHeader), 1);
+
+ //FIXME: Which value is right?
+ toReturn->allocState = 0x000000a0;
+ //toReturn->allocState = 0xa0000000;
+ toReturn->nextBlock = 0xFFFF;
+ unsigned char *bytePtr = (unsigned char*)toReturn;
+ for (int i = 0; i < sizeof(struct PSXMemHeader) - sizeof(unsigned char); i++) {
+ toReturn->checksum ^= bytePtr[i];
+ }
+ }
+
+ return (char*)toReturn;
+}
+
+static inline void ClearMemcardData(char *to, int dsti, char *str)
+{
+ // header
+ char *header = BlankHeader();
+ memcpy(to + (dsti + 1) * 128, header, 128);
+ SaveMcd(str, to, (dsti + 1) * 128, 128);
+
+ // data
+ memset(to + (dsti + 1) * 1024 * 8, 0, 1024 * 8);
+ SaveMcd(str, to, (dsti + 1) * 1024 * 8, 1024 * 8);
+}
+
+@interface PcsxrMemCardArray ()
+@property (strong) NSArray *rawArray;
+@property (readonly) char* memDataPtr;
+@property int cardNumber;
+@end
+
+@implementation PcsxrMemCardArray
+@synthesize rawArray;
+@synthesize cardNumber;
+
+- (char*)memDataPtr
+{
+ if (cardNumber == 1) {
+ return Mcd1Data;
+ } else {
+ return Mcd2Data;
+ }
+}
+
+- (const char *)memCardCPath
+{
+ if (cardNumber == 1) {
+ return Config.Mcd1;
+ } else {
+ return Config.Mcd2;
+ }
+}
+
+- (id)initWithMemoryCardNumber:(int)carNum
+{
+ NSParameterAssert(carNum == 1 || carNum == 2);
+ if (self = [super init]) {
+ NSMutableArray *tmpMemArray = [[NSMutableArray alloc] initWithCapacity:MAX_MEMCARD_BLOCKS];
+ cardNumber = carNum;
+ int i = 0, x;
+ while (i < MAX_MEMCARD_BLOCKS) {
+ x = 1;
+ McdBlock memBlock;
+ GetMcdBlockInfo(carNum, i + 1, &memBlock);
+
+ if ([PcsxrMemoryObject memFlagsFromBlockFlags:memBlock.Flags] == memFlagFree) {
+ //Free space: ignore
+ i++;
+ continue;
+ }
+ do {
+ McdBlock tmpBlock;
+ GetMcdBlockInfo(carNum, i + x + 1, &tmpBlock);
+ if ((tmpBlock.Flags & 0x3) == 0x3) {
+ x++;
+ break;
+ } else if ((tmpBlock.Flags & 0x2) == 0x2) {
+ x++;
+ } else {
+ break;
+ }
+ } while (i + x - 1 < MAX_MEMCARD_BLOCKS);
+ @autoreleasepool {
+ PcsxrMemoryObject *obj = [[PcsxrMemoryObject alloc] initWithMcdBlock:&memBlock startingIndex:i size:x];
+ [tmpMemArray addObject:obj];
+ }
+ i += x;
+ }
+ self.rawArray = [[NSArray alloc] initWithArray:tmpMemArray];
+ }
+ return self;
+}
+
+- (int)indexOfFreeBlocksWithSize:(int)asize
+{
+ int foundcount = 0, i = 0;
+
+ McdBlock obj;
+ // search for empty (formatted) blocks first
+ while (i < MAX_MEMCARD_BLOCKS && foundcount < asize) {
+ GetMcdBlockInfo(cardNumber, 1 + i++, &obj);
+ //&Blocks[target_card][++i];
+ if ((obj.Flags & 0xFF) == 0xA0) { // if A0 but not A1
+ foundcount++;
+ } else if (foundcount >= 1) { // need to find n count consecutive blocks
+ foundcount = 0;
+ } else {
+ //i++;
+ }
+ //printf("formatstatus=%x\n", Info->Flags);
+ }
+
+ if (foundcount == asize)
+ return (i-foundcount);
+
+ // no free formatted slots, try to find a deleted one
+ foundcount = i = 0;
+ while (i < MAX_MEMCARD_BLOCKS && foundcount < asize) {
+ GetMcdBlockInfo(cardNumber, 1 + i++, &obj);
+ if ((obj.Flags & 0xF0) == 0xA0) { // A2 or A6 f.e.
+ foundcount++;
+ } else if (foundcount >= 1) { // need to find n count consecutive blocks
+ foundcount = 0;
+ } else {
+ //i++;
+ }
+ //printf("delstatus=%x\n", Info->Flags);
+ }
+
+ if (foundcount == asize)
+ return (i-foundcount);
+
+ return -1;
+}
+
+- (BOOL)moveBlockAtIndex:(int)idx toMemoryCard:(PcsxrMemCardArray*)otherCard
+{
+ if (idx == [rawArray count]) {
+#ifdef DEBUG
+ NSLog(@"Trying to get an object one more than the length of the raw array. Perhaps you were trying to \"move\" the free blocks. We don't want to do this.");
+#endif
+ return NO;
+ }
+ PcsxrMemoryObject *tmpObj = rawArray[idx];
+
+ int memSize = tmpObj.blockSize;
+
+ if ([otherCard availableBlocks] < memSize) {
+ NSLog(@"Failing because the other card does not have enough space!");
+ return NO;
+ }
+
+ int toCopy = [otherCard indexOfFreeBlocksWithSize:memSize];
+ if (toCopy == -1) {
+ NSLog(@"Not enough consecutive blocks. Compacting the other card.");
+ [otherCard compactMemory];
+ //Since we're accessing the mem card data directly (instead of via PcsxrMemoryObject objects) using the following calls, we don't need to reload the data.
+ toCopy = [otherCard indexOfFreeBlocksWithSize:memSize];
+ NSAssert(toCopy != -1, @"Compacting the card should have made space!");
+ }
+
+ int memIdx = tmpObj.startingIndex;
+ for (int i = 0; i < memSize; i++) {
+ CopyMemcardData([self memDataPtr], [otherCard memDataPtr], memIdx + i, toCopy + i, (char*)otherCard.memCardCPath);
+ }
+
+ return YES;
+}
+
+- (int)freeBlocks
+{
+ int memSize = MAX_MEMCARD_BLOCKS;
+ for (PcsxrMemoryObject *memObj in rawArray) {
+ memSize -= memObj.blockSize;
+ }
+ return memSize;
+}
+
+- (int)availableBlocks
+{
+ int memSize = MAX_MEMCARD_BLOCKS;
+ for (PcsxrMemoryObject *memObj in rawArray) {
+ if (memObj.flagNameIndex != memFlagDeleted) {
+ memSize -= memObj.blockSize;
+ }
+ }
+ return memSize;
+}
+
+- (NSArray*)memoryArray
+{
+ int freeSize = [self freeBlocks];
+
+ if (freeSize) {
+ McdBlock theBlock;
+ //Create a blank "block" that will be used to show the amount of free blocks
+ theBlock.Flags = 0xA0;
+ theBlock.IconCount = 0;
+ PcsxrMemoryObject *freeObj = [[PcsxrMemoryObject alloc] initWithMcdBlock:&theBlock startingIndex:MAX_MEMCARD_BLOCKS - 1 - freeSize size:freeSize];
+ return [rawArray arrayByAddingObject:freeObj];
+ } else
+ return rawArray;
+}
+
+- (NSURL*)memCardURL
+{
+ if (cardNumber == 1) {
+ return [[NSUserDefaults standardUserDefaults] URLForKey:@"Mcd1"];
+ } else {
+ return [[NSUserDefaults standardUserDefaults] URLForKey:@"Mcd2"];
+ }
+}
+
+- (int)memorySizeAtIndex:(int)idx
+{
+ if (idx == [rawArray count]) {
+#ifdef DEBUG
+ NSLog(@"Trying to get an object one more than the length of the raw array. Perhaps you were trying to \"count\" the free blocks?");
+#endif
+ return [self freeBlocks];
+ }
+
+ return [rawArray[idx] blockSize];
+}
+
+- (void)compactMemory
+{
+ int i = 0, x = 1;
+ while (i < MAX_MEMCARD_BLOCKS && x < MAX_MEMCARD_BLOCKS) {
+ x = i;
+ McdBlock baseBlock;
+ GetMcdBlockInfo(cardNumber, i+1, &baseBlock);
+ PCSXRMemFlags theFlags = [PcsxrMemoryObject memFlagsFromBlockFlags:baseBlock.Flags];
+
+ if (theFlags == memFlagDeleted || theFlags == memFlagFree) {
+ PCSXRMemFlags up1Flags = theFlags;
+ while ((up1Flags == memFlagDeleted || up1Flags == memFlagFree) && x < MAX_MEMCARD_BLOCKS) {
+ x++;
+ McdBlock up1Block;
+ GetMcdBlockInfo(cardNumber, x+1, &up1Block);
+ up1Flags = [PcsxrMemoryObject memFlagsFromBlockFlags:up1Block.Flags];
+ }
+ if (x >= MAX_MEMCARD_BLOCKS) {
+ break;
+ }
+
+ CopyMemcardData(self.memDataPtr, self.memDataPtr, x, i, (char*)[[self.memCardURL path] fileSystemRepresentation]);
+ ClearMemcardData(self.memDataPtr, x, (char*)self.memCardCPath);
+ }
+ i++;
+ }
+
+ while (i < MAX_MEMCARD_BLOCKS) {
+ ClearMemcardData(self.memDataPtr, i, (char*)self.memCardCPath);
+ i++;
+ }
+
+ LoadMcd(cardNumber, (char*)self.memCardCPath);
+}
+
+- (void)deleteMemoryBlocksAtIndex:(int)slotnum
+{
+ int xor = 0, i, j;
+ char *data, *ptr, *filename;
+ if (cardNumber == 1) {
+ filename = Config.Mcd1;
+ data = Mcd1Data;
+ } else {
+ filename = Config.Mcd2;
+ data = Mcd2Data;
+ }
+
+ if (slotnum == [rawArray count]) {
+#ifdef DEBUG
+ NSLog(@"Trying to get an object one more than the length of the raw array. Perhaps you were trying to \"delete\" the free blocks?");
+#endif
+ return;
+ }
+
+ PcsxrMemoryObject *theObj = rawArray[slotnum];
+
+ McdBlock flagBlock;
+
+ for(i = theObj.startingIndex + 1; i < (theObj.startingIndex + theObj.blockSize + 1); i++)
+ {
+ GetMcdBlockInfo(cardNumber, i, &flagBlock);
+ ptr = data + i * 128;
+
+ if ((flagBlock.Flags & 0xF0) == 0xA0) {
+ if ((flagBlock.Flags & 0xF) >= 1 &&
+ (flagBlock.Flags & 0xF) <= 3) { // deleted
+ *ptr = 0x50 | (flagBlock.Flags & 0xF);
+ } else return;
+ } else if ((flagBlock.Flags & 0xF0) == 0x50) { // used
+ *ptr = 0xA0 | (flagBlock.Flags & 0xF);
+ } else { continue; }
+
+ for (j = 0; j < 127; j++) xor ^= *ptr++;
+ *ptr = xor;
+
+ SaveMcd(filename, data, i * 128, 128);
+ }
+}
+
+@end
diff --git a/macosx/Source/PcsxrMemCardController.h b/macosx/Source/PcsxrMemCardController.h
new file mode 100644
index 00000000..d4a71088
--- /dev/null
+++ b/macosx/Source/PcsxrMemCardController.h
@@ -0,0 +1,30 @@
+//
+// PcsxrMemCardManager.h
+// Pcsxr
+//
+// Created by Charles Betts on 11/23/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+@class PcsxrMemCardArray;
+
+@interface PcsxrMemCardController : NSViewController
+{
+ IBOutlet NSCollectionView *memCard1view;
+ IBOutlet NSCollectionView *memCard2view;
+ IBOutlet NSTextField *memCard1Label;
+ IBOutlet NSTextField *memCard2Label;
+}
+@property (readonly, strong) PcsxrMemCardArray *memCard1Array;
+@property (readonly, strong) PcsxrMemCardArray *memCard2Array;
+
+- (IBAction)moveBlock:(id)sender;
+- (IBAction)formatCard:(id)sender;
+- (IBAction)deleteMemoryObject:(id)sender;
+- (void)loadMemoryCardInfoForCard:(int)theCard;
+
+- (void)beginMemoryAnimation;
+- (void)stopMemoryAnimation;
+
+@end
diff --git a/macosx/Source/PcsxrMemCardController.m b/macosx/Source/PcsxrMemCardController.m
new file mode 100644
index 00000000..7fb139ea
--- /dev/null
+++ b/macosx/Source/PcsxrMemCardController.m
@@ -0,0 +1,238 @@
+//
+// PcsxrMemCardManager.m
+// Pcsxr
+//
+// Created by Charles Betts on 11/23/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "PcsxrMemCardController.h"
+#import "PcsxrMemoryObject.h"
+#import "ConfigurationController.h"
+#import "PcsxrMemCardHandler.h"
+#import "PcsxrMemCardArray.h"
+#include "sio.h"
+
+#define MAX_MEMCARD_BLOCKS 15
+
+@interface PcsxrMemCardController ()
+@property (readwrite, strong) PcsxrMemCardArray *memCard1Array;
+@property (readwrite, strong) PcsxrMemCardArray *memCard2Array;
+@property (strong) NSTimer *imageAnimateTimer;
+@end
+
+@implementation PcsxrMemCardController
+@synthesize memCard1Array;
+@synthesize memCard2Array;
+
+- (void)stopMemoryAnimation
+{
+ [self.imageAnimateTimer invalidate];
+ self.imageAnimateTimer = nil;
+}
+
+- (void)beginMemoryAnimation
+{
+ if (!_imageAnimateTimer) {
+ self.imageAnimateTimer = [[NSTimer alloc] initWithFireDate:[NSDate date] interval:0.30 target:self selector:@selector(animateMemCards:) userInfo:nil repeats:YES];
+ [[NSRunLoop mainRunLoop] addTimer:self.imageAnimateTimer forMode:NSRunLoopCommonModes];
+ }
+}
+
+- (void)setupValues:(int)theCards
+{
+ NSParameterAssert(theCards < 4 && theCards > 0);
+ if (theCards == 3) {
+ LoadMcds(Config.Mcd1, Config.Mcd2);
+ } else {
+ LoadMcd(theCards, theCards == 1 ? Config.Mcd1 : Config.Mcd2);
+ }
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
+ NSString *fullPath = nil;
+ NSString *fileName = nil;
+
+ if (theCards & 1) {
+ fullPath = [[def URLForKey:@"Mcd1"] path];
+ fileName = [fm displayNameAtPath:fullPath];
+
+ [memCard1Label setStringValue:fileName];
+ [memCard1Label setToolTip:fullPath];
+
+ [self loadMemoryCardInfoForCard:1];
+ }
+
+ if (theCards & 2) {
+ fullPath = [[def URLForKey:@"Mcd2"] path];
+ fileName = [fm displayNameAtPath:fullPath];
+
+ [memCard2Label setStringValue:fileName];
+ [memCard2Label setToolTip:fullPath];
+
+ [self loadMemoryCardInfoForCard:2];
+ }
+}
+
+- (void)loadMemoryCardInfoForCard:(int)theCard
+{
+ PcsxrMemCardArray *newArray = [[PcsxrMemCardArray alloc] initWithMemoryCardNumber:theCard];
+
+ if (theCard == 1) {
+ [self setMemCard1Array:newArray];
+ } else {
+ [self setMemCard2Array:newArray];
+ }
+}
+
+- (void)memoryCardDidChangeNotification:(NSNotification *)aNote
+{
+ NSDictionary *dict = [aNote userInfo];
+ NSNumber *theNum = dict[memCardChangeNumberKey];
+ [self setupValues: theNum ? [theNum intValue] : 3];
+}
+
+- (void)awakeFromNib
+{
+ [super awakeFromNib];
+ [self setupValues:3];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(memoryCardDidChangeNotification:) name:memChangeNotifier object:nil];
+
+ [self beginMemoryAnimation];
+}
+
+- (void)animateMemCards:(NSTimer*)theTimer
+{
+ [[NSNotificationCenter defaultCenter] postNotificationName:memoryAnimateTimerKey object:self];
+}
+
+- (IBAction)moveBlock:(id)sender
+{
+ NSInteger memCardSelect = [sender tag];
+ NSCollectionView *cardView;
+ NSIndexSet *selection;
+ PcsxrMemCardArray *toCard, *fromCard;
+ int cardnum;
+ if (memCardSelect == 1) {
+ cardView = memCard2view;
+ toCard = memCard1Array;
+ fromCard = memCard2Array;
+ cardnum = 1;
+ } else {
+ cardView = memCard1view;
+ toCard = memCard2Array;
+ fromCard = memCard1Array;
+ cardnum = 2;
+ }
+ selection = [cardView selectionIndexes];
+ if (!selection || [selection count] != 1) {
+ NSBeep();
+ return;
+ }
+ NSInteger selectedIndex = [selection firstIndex];
+
+ int cardSize, freeConsBlocks, availBlocks;
+
+ if ([[fromCard memoryArray][selectedIndex] flagNameIndex] == memFlagFree) {
+ NSBeep();
+ return;
+ }
+
+ cardSize = [fromCard memorySizeAtIndex:(int)selectedIndex];
+ freeConsBlocks = [toCard indexOfFreeBlocksWithSize:cardSize];
+ availBlocks = [toCard availableBlocks];
+ if (freeConsBlocks == -1 && availBlocks >= cardSize) {
+ PcsxrMemoryObject *tmpmemobj = (fromCard.memoryArray)[selectedIndex];
+ NSInteger copyOK = NSRunInformationalAlertPanel(NSLocalizedString(@"Free Size", nil), NSLocalizedString(@"Memory card %i does not have enough free consecutive blocks.\n\nIn order to copy over \"%@ (%@),\" memory card %i must be compressed. Compressing memory cards will make deleted blocks unrecoverable.\n\nDo you want to continue?", nil), NSLocalizedString(@"Yes", nil), NSLocalizedString(@"No", nil), nil, cardnum, tmpmemobj.englishName, tmpmemobj.sjisName, cardnum);
+ if (copyOK != NSAlertDefaultReturn) {
+ return;
+ }
+ } else if (cardSize > availBlocks) {
+ NSRunCriticalAlertPanel(NSLocalizedString(@"No Free Space", nil), NSLocalizedString(@"Memory card %d doesn't have %d free consecutive blocks on it. Please remove some blocks on that card to continue", nil), nil, nil, nil, availBlocks, cardnum);
+ return;
+ }
+
+ [fromCard moveBlockAtIndex:(int)selectedIndex toMemoryCard:toCard];
+
+ if (cardnum == 1) {
+ LoadMcd(1, Config.Mcd1);
+ } else {
+ LoadMcd(2, Config.Mcd2);
+ }
+ [self loadMemoryCardInfoForCard:cardnum];
+}
+
+- (IBAction)formatCard:(id)sender
+{
+ NSInteger formatOkay = NSRunAlertPanel(NSLocalizedString(@"Format Card", nil), NSLocalizedString(@"Formatting a memory card will remove all data on it.\n\nThis cannot be undone.", nil), NSLocalizedString(@"Cancel", nil), NSLocalizedString(@"Format", nil), nil);
+ if (formatOkay == NSAlertAlternateReturn) {
+ NSInteger memCardSelect = [sender tag];
+ if (memCardSelect == 1) {
+ CreateMcd(Config.Mcd1);
+ LoadMcd(1, Config.Mcd1);
+ } else {
+ CreateMcd(Config.Mcd2);
+ LoadMcd(2, Config.Mcd2);
+ }
+ [self loadMemoryCardInfoForCard:(int)memCardSelect];
+ }
+}
+
+- (void)deleteMemoryBlocksAtIndex:(int)slotnum card:(int)cardNum
+{
+ PcsxrMemCardArray *cardArray;
+ if (cardNum == 1) {
+ cardArray = [self memCard1Array];
+ } else {
+ cardArray = [self memCard2Array];
+ }
+ [cardArray deleteMemoryBlocksAtIndex:slotnum];
+}
+
+- (IBAction)deleteMemoryObject:(id)sender
+{
+ PcsxrMemCardArray *curCard;
+ NSInteger memCardSelect = [sender tag];
+ NSIndexSet *selected;
+ if (memCardSelect == 1) {
+ curCard = memCard1Array;
+ selected = [memCard1view selectionIndexes];
+ } else {
+ curCard = memCard2Array;
+ selected = [memCard2view selectionIndexes];
+ }
+
+ if (!selected || [selected count] == 0) {
+ NSBeep();
+ return;
+ }
+
+ NSInteger selectedIndex = [selected firstIndex];
+
+ PcsxrMemoryObject *tmpObj = [curCard memoryArray][selectedIndex];
+
+ if (tmpObj.flagNameIndex == memFlagFree) {
+ NSBeep();
+ return;
+ }
+
+ NSInteger deleteOkay = NSRunAlertPanel(NSLocalizedString(@"Delete Block", @"The block will be deleted"), NSLocalizedString(@"Deleting a block will remove all saved data on that block.\n\nThis cannot be undone.", @"Delete block cannot be undone"), NSLocalizedString(@"Cancel", @"Cancel"), NSLocalizedString(@"Delete", nil), nil);
+ if (deleteOkay == NSAlertAlternateReturn) {
+ [self deleteMemoryBlocksAtIndex:(int)selectedIndex card:(int)memCardSelect];
+
+ if (memCardSelect == 1) {
+ LoadMcd(1, Config.Mcd1);
+ } else {
+ LoadMcd(2, Config.Mcd2);
+ }
+ [self loadMemoryCardInfoForCard:(int)memCardSelect];
+ }
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [self.imageAnimateTimer invalidate];
+}
+
+@end
diff --git a/macosx/Source/PcsxrMemCardHandler.h b/macosx/Source/PcsxrMemCardHandler.h
new file mode 100644
index 00000000..d87b73a7
--- /dev/null
+++ b/macosx/Source/PcsxrMemCardHandler.h
@@ -0,0 +1,15 @@
+//
+// PcsxrMemCardHandler.h
+// Pcsxr
+//
+// Created by Charles Betts on 12/10/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PcsxrFileHandle.h"
+
+@interface PcsxrMemCardHandler : NSWindowController <PcsxrFileHandle>
+@property (weak) IBOutlet NSTextField *cardPath;
+- (IBAction)setMemCard:(id)sender;
+@end
diff --git a/macosx/Source/PcsxrMemCardHandler.m b/macosx/Source/PcsxrMemCardHandler.m
new file mode 100644
index 00000000..654ade0e
--- /dev/null
+++ b/macosx/Source/PcsxrMemCardHandler.m
@@ -0,0 +1,81 @@
+//
+// PcsxrMemCardHandler.m
+// Pcsxr
+//
+// Created by Charles Betts on 12/10/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "PcsxrMemCardHandler.h"
+#import "ConfigurationController.h"
+#import "EmuThread.h"
+
+@interface PcsxrMemCardHandler ()
+@property NSInteger memChosen;
+@end
+
+@implementation PcsxrMemCardHandler
+@synthesize cardPath;
+@synthesize memChosen;
+
++ (NSArray *)supportedUTIs
+{
+ static NSArray *utisupport = nil;
+ if (utisupport == nil) {
+ utisupport = @[@"com.codeplex.pcsxr.memcard"];
+ }
+ return utisupport;
+}
+
+- (id)initWithWindow:(NSWindow *)window
+{
+ self = [super initWithWindow:window];
+ if (self) {
+ memChosen = 0;
+ }
+
+ return self;
+}
+
+- (id)init
+{
+ return self = [self initWithWindowNibName:@"PcsxrMemCard"];
+}
+
+- (void)windowDidLoad
+{
+ [super windowDidLoad];
+ // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
+}
+
+- (NSString*)windowNibName
+{
+ return @"PcsxrMemCard";
+}
+
+- (IBAction)setMemCard:(id)sender
+{
+ memChosen = [sender tag];
+
+ [NSApp stopModal];
+}
+
+- (BOOL)handleFile:(NSString *)theFile
+{
+ if ([EmuThread active]) {
+ return NO;
+ }
+
+ [cardPath setStringValue:[[NSFileManager defaultManager] displayNameAtPath:theFile]];
+
+ [NSApp runModalForWindow:[self window]];
+
+ [[self window] orderOut:nil];
+
+ if (memChosen != 0) {
+ [ConfigurationController setMemoryCard:memChosen toPath:theFile];
+ }
+ return YES;
+}
+
+@end
diff --git a/macosx/Source/PcsxrMemoryObject.h b/macosx/Source/PcsxrMemoryObject.h
new file mode 100644
index 00000000..5a7a3b37
--- /dev/null
+++ b/macosx/Source/PcsxrMemoryObject.h
@@ -0,0 +1,45 @@
+//
+// PcsxrMemoryObject.h
+// Pcsxr
+//
+// Created by Charles Betts on 11/23/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#include "sio.h"
+
+extern NSString *const memoryAnimateTimerKey;
+
+typedef NS_ENUM(char, PCSXRMemFlags) {
+ memFlagDeleted,
+ memFlagFree,
+ memFlagUsed,
+ memFlagLink,
+ memFlagEndLink
+};
+
+@interface PcsxrMemoryObject : NSObject
+
++ (NSArray *)imagesFromMcd:(McdBlock *)block;
++ (NSString*)memoryLabelFromFlag:(PCSXRMemFlags)flagNameIndex;
++ (NSImage *)blankImage;
++ (PCSXRMemFlags)memFlagsFromBlockFlags:(unsigned char)blockFlags;
+
+- (id)initWithMcdBlock:(McdBlock *)infoBlockc startingIndex:(uint8_t)startIdx size:(uint8_t)memSize;
+
+@property (readonly, strong) NSString *englishName;
+@property (readonly, strong) NSString *sjisName;
+@property (readonly, strong) NSString *memName;
+@property (readonly, strong) NSString *memID;
+@property (readonly) PCSXRMemFlags flagNameIndex;
+@property (readonly) uint8_t startingIndex;
+@property (readonly) uint8_t blockSize;
+
+@property (readonly, unsafe_unretained, nonatomic) NSImage *memImage;
+@property (readonly, nonatomic) unsigned memIconCount;
+@property (readonly, unsafe_unretained, nonatomic) NSString *flagName;
+@property (readonly, unsafe_unretained, nonatomic) NSAttributedString *attributedFlagName;
+@property (readonly, nonatomic) BOOL isBiggerThanOne;
+
+@end
diff --git a/macosx/Source/PcsxrMemoryObject.m b/macosx/Source/PcsxrMemoryObject.m
new file mode 100644
index 00000000..7d11b6f8
--- /dev/null
+++ b/macosx/Source/PcsxrMemoryObject.m
@@ -0,0 +1,323 @@
+//
+// PcsxrMemoryObject.m
+// Pcsxr
+//
+// Created by Charles Betts on 11/23/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PcsxrMemoryObject.h"
+
+NSString *const memoryAnimateTimerKey = @"PCSXR Memory Card Image Animate";
+
+@interface PcsxrMemoryObject ()
+@property (readwrite, strong) NSString *englishName;
+@property (readwrite, strong) NSString *sjisName;
+@property (readwrite, strong) NSString *memName;
+@property (readwrite, strong) NSString *memID;
+@property (readwrite) uint8_t startingIndex;
+@property (readwrite) uint8_t blockSize;
+
+@property (nonatomic) NSInteger memImageIndex;
+@property (strong) NSArray *memImages;
+@property (readwrite) PCSXRMemFlags flagNameIndex;
+@end
+
+@implementation PcsxrMemoryObject
+
++ (NSArray *)imagesFromMcd:(McdBlock *)block
+{
+ NSMutableArray *imagesArray = [[NSMutableArray alloc] initWithCapacity:block->IconCount];
+ for (int i = 0; i < block->IconCount; i++) {
+ NSImage *memImage;
+ {
+ NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:16 pixelsHigh:16 bitsPerSample:8 samplesPerPixel:3 hasAlpha:NO isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:0];
+
+ short *icon = block->Icon;
+
+ int x, y, c, v, r, g, b;
+ for (v = 0; v < 256; v++) {
+ x = (v % 16);
+ y = (v / 16);
+ c = icon[(i * 256) + v];
+ r = (c & 0x001f) << 3;
+ g = ((c & 0x03e0) >> 5) << 3;
+ b = ((c & 0x7c00) >> 10) << 3;
+ [imageRep setColor:[NSColor colorWithCalibratedRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1.0] atX:x y:y];
+ }
+ memImage = [[NSImage alloc] init];
+ [memImage addRepresentation:imageRep];
+ [memImage setSize:NSMakeSize(32, 32)];
+ }
+ [imagesArray addObject:memImage];
+ }
+ return [NSArray arrayWithArray:imagesArray];
+}
+
+static NSString *MemLabelDeleted;
+static NSString *MemLabelFree;
+static NSString *MemLabelUsed;
+static NSString *MemLabelLink;
+static NSString *MemLabelEndLink;
+
++ (void)initialize
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSBundle *mainBundle = [NSBundle mainBundle];
+ MemLabelDeleted = [[mainBundle localizedStringForKey:@"MemCard_Deleted" value:@"" table:nil] copy];
+ MemLabelFree = [[mainBundle localizedStringForKey:@"MemCard_Free" value:@"" table:nil] copy];
+ MemLabelUsed = [[mainBundle localizedStringForKey:@"MemCard_Used" value:@"" table:nil] copy];
+ MemLabelLink = [[mainBundle localizedStringForKey:@"MemCard_Link" value:@"" table:nil] copy];
+ MemLabelEndLink = [[mainBundle localizedStringForKey:@"MemCard_EndLink" value:@"" table:nil] copy];
+ });
+}
+
++ (NSString*)memoryLabelFromFlag:(PCSXRMemFlags)flagNameIndex
+{
+ switch (flagNameIndex) {
+ default:
+ case memFlagFree:
+ return MemLabelFree;
+ break;
+
+ case memFlagEndLink:
+ return MemLabelEndLink;
+ break;
+
+ case memFlagLink:
+ return MemLabelLink;
+ break;
+
+ case memFlagUsed:
+ return MemLabelUsed;
+ break;
+
+ case memFlagDeleted:
+ return MemLabelDeleted;
+ break;
+ }
+}
+
++ (NSImage *)blankImage
+{
+ static NSImage *imageBlank = nil;
+ if (imageBlank == nil) {
+ NSRect imageRect = NSMakeRect(0, 0, 32, 32);
+ imageBlank = [[NSImage alloc] initWithSize:imageRect.size];
+ [imageBlank lockFocus];
+ [[NSColor blackColor] set];
+ [NSBezierPath fillRect:imageRect];
+ [imageBlank unlockFocus];
+ }
+ return imageBlank;
+}
+
++ (PCSXRMemFlags)memFlagsFromBlockFlags:(unsigned char)blockFlags
+{
+ if ((blockFlags & 0xF0) == 0xA0) {
+ if ((blockFlags & 0xF) >= 1 && (blockFlags & 0xF) <= 3)
+ return memFlagDeleted;
+ else
+ return memFlagFree;
+ } else if ((blockFlags & 0xF0) == 0x50) {
+ if ((blockFlags & 0xF) == 0x1)
+ return memFlagUsed;
+ else if ((blockFlags & 0xF) == 0x2)
+ return memFlagLink;
+ else if ((blockFlags & 0xF) == 0x3)
+ return memFlagEndLink;
+ } else
+ return memFlagFree;
+
+ //Xcode complains unless we do this...
+ NSLog(@"Unknown flag %x", blockFlags);
+ return memFlagFree;
+}
+
+- (id)initWithMcdBlock:(McdBlock *)infoBlock startingIndex:(uint8_t)startIdx size:(uint8_t)memSize
+{
+ if (self = [super init]) {
+ self.startingIndex = startIdx;
+ self.blockSize = memSize;
+ self.flagNameIndex = [PcsxrMemoryObject memFlagsFromBlockFlags:infoBlock->Flags];
+ if (self.flagNameIndex == memFlagFree) {
+ self.memImages = @[];
+ self.memImageIndex = -1;
+ self.englishName = self.sjisName = @"Free block";
+ self.memID = self.memName = @"";
+ } else {
+ self.englishName = @(infoBlock->Title);
+ self.sjisName = [NSString stringWithCString:infoBlock->sTitle encoding:NSShiftJISStringEncoding];
+
+ if ([englishName isEqualToString:sjisName]) {
+#if 0
+ if (![englishName isEqualToString:@""])
+ NSLog(@"English name and sjis name are the same: %@. Replacing the sjis string with the English string.", englishName);
+#endif
+ self.sjisName = self.englishName;
+ }
+ @autoreleasepool {
+ self.memImages = [PcsxrMemoryObject imagesFromMcd:infoBlock];
+ }
+
+ if ([memImages count] == 0) {
+ self.memImageIndex = -1;
+ } else if ([memImages count] == 1) {
+ self.memImageIndex = 0;
+ } else {
+ self.memImageIndex = 0;
+ [[NSNotificationCenter defaultCenter] addObserverForName:memoryAnimateTimerKey object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
+ NSInteger index = memImageIndex;
+ if (++index >= [memImages count]) {
+ index = 0;
+ }
+ self.memImageIndex = index;
+ }];
+ }
+ self.memName = @(infoBlock->Name);
+ self.memID = @(infoBlock->ID);
+ }
+ }
+ return self;
+}
+
+#pragma mark - Property Synthesizers
+@synthesize englishName;
+@synthesize sjisName;
+@synthesize memImageIndex;
+- (void)setMemImageIndex:(NSInteger)theMemImageIndex
+{
+ [self willChangeValueForKey:@"memImage"];
+ memImageIndex = theMemImageIndex;
+ [self didChangeValueForKey:@"memImage"];
+}
+
+@synthesize memName;
+@synthesize memID;
+@synthesize memImages;
+@synthesize flagNameIndex;
+@synthesize blockSize;
+@synthesize startingIndex;
+
+#pragma mark Non-synthesized Properties
+- (unsigned)memIconCount
+{
+ return (unsigned)[memImages count];
+}
+
+- (NSImage*)memImage
+{
+ if (memImageIndex == -1) {
+ return [PcsxrMemoryObject blankImage];
+ }
+ return memImages[memImageIndex];
+}
+
+- (NSString*)flagName
+{
+ return [PcsxrMemoryObject memoryLabelFromFlag:flagNameIndex];
+}
+
+static inline void SetupAttrStr(NSMutableAttributedString *mutStr, NSColor *txtclr)
+{
+ NSRange wholeStrRange = NSMakeRange(0, mutStr.string.length);
+ [mutStr addAttribute:NSFontAttributeName value:[NSFont userFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]] range:wholeStrRange];
+ [mutStr addAttribute:NSForegroundColorAttributeName value:txtclr range:wholeStrRange];
+ [mutStr setAlignment:NSCenterTextAlignment range:wholeStrRange];
+}
+
+- (NSAttributedString*)attributedFlagName
+{
+ static NSAttributedString *attribMemLabelDeleted;
+ static NSAttributedString *attribMemLabelFree;
+ static NSAttributedString *attribMemLabelUsed;
+ static NSAttributedString *attribMemLabelLink;
+ static NSAttributedString *attribMemLabelEndLink;
+
+ static dispatch_once_t attrStrSetOnceToken;
+ dispatch_once(&attrStrSetOnceToken, ^{
+ NSMutableAttributedString *tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelFree];
+ SetupAttrStr(tmpStr, [NSColor greenColor]);
+ attribMemLabelFree = [tmpStr copy];
+
+#ifdef DEBUG
+ tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelEndLink];
+ SetupAttrStr(tmpStr, [NSColor blueColor]);
+ attribMemLabelEndLink = [tmpStr copy];
+
+ tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelLink];
+ SetupAttrStr(tmpStr, [NSColor blueColor]);
+ attribMemLabelLink = [tmpStr copy];
+
+ tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelUsed];
+ SetupAttrStr(tmpStr, [NSColor controlTextColor]);
+ attribMemLabelUsed = [tmpStr copy];
+#else
+ tmpStr = [[NSMutableAttributedString alloc] initWithString:@"Multi-save"];
+ SetupAttrStr(tmpStr, [NSColor blueColor]);
+ attribMemLabelEndLink = [tmpStr copy];
+
+ //tmpStr = [[NSMutableAttributedString alloc] initWithString:@"Multi-save"];
+ //SetupAttrStr(tmpStr, [NSColor blueColor]);
+ //attribMemLabelLink = [tmpStr copy];
+ //RELEASEOBJ(tmpStr);
+ attribMemLabelLink = attribMemLabelEndLink;
+
+ //display nothing
+ attribMemLabelUsed = [[NSAttributedString alloc] initWithString:@""];
+#endif
+
+ tmpStr = [[NSMutableAttributedString alloc] initWithString:MemLabelDeleted];
+ SetupAttrStr(tmpStr, [NSColor redColor]);
+ attribMemLabelDeleted = [tmpStr copy];
+ });
+
+ switch (flagNameIndex) {
+ default:
+ case memFlagFree:
+ return attribMemLabelFree;
+ break;
+
+ case memFlagEndLink:
+ return attribMemLabelEndLink;
+ break;
+
+ case memFlagLink:
+ return attribMemLabelLink;
+ break;
+
+ case memFlagUsed:
+ return attribMemLabelUsed;
+ break;
+
+ case memFlagDeleted:
+ return attribMemLabelDeleted;
+ break;
+ }
+}
+
+- (BOOL)isBiggerThanOne
+{
+ if (flagNameIndex == memFlagFree) {
+ //Always show the size of the free blocks
+ return YES;
+ } else {
+ return blockSize != 1;
+ }
+}
+
+#pragma mark -
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+}
+
+- (NSString *)description
+{
+ return [NSString stringWithFormat:@"%@ (%@): Name: %@ ID: %@, type: %@ start: %i size: %i", englishName, sjisName, memName, memID, self.flagName, startingIndex, blockSize];
+}
+
+@end
diff --git a/macosx/Source/PcsxrPlugin.h b/macosx/Source/PcsxrPlugin.h
new file mode 100644
index 00000000..f2a0f854
--- /dev/null
+++ b/macosx/Source/PcsxrPlugin.h
@@ -0,0 +1,32 @@
+//
+// PcsxrPlugin.h
+// Pcsxr
+//
+// Created by Gil Pedersen on Fri Oct 03 2003.
+// Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface PcsxrPlugin : NSObject
+@property (readonly, copy) NSString *path;
+@property (readonly, strong) NSString *name;
+@property (readonly) int type;
+
++ (NSString *)prefixForType:(int)type;
++ (NSString *)defaultKeyForType:(int)type;
++ (char **)configEntriesForType:(int)type;
++ (NSArray *)pluginsPaths;
+
+- (id)initWithPath:(NSString *)aPath;
+
+- (NSString *)displayVersion;
+- (BOOL)hasAboutAs:(int)type;
+- (BOOL)hasConfigureAs:(int)type;
+- (long)runAs:(int)aType;
+- (long)shutdownAs:(int)aType;
+- (void)aboutAs:(int)type;
+- (void)configureAs:(int)type;
+- (BOOL)verifyOK;
+
+@end
diff --git a/macosx/Source/PcsxrPlugin.m b/macosx/Source/PcsxrPlugin.m
new file mode 100644
index 00000000..f326c040
--- /dev/null
+++ b/macosx/Source/PcsxrPlugin.m
@@ -0,0 +1,404 @@
+//
+// PcsxrPlugin.m
+// Pcsxr
+//
+// Created by Gil Pedersen on Fri Oct 03 2003.
+// Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PcsxrPlugin.h"
+#include "psxcommon.h"
+#include "plugins.h"
+
+#define kPCSXRGetLibName "PSEgetLibName"
+#define kPCSXRGetLibVersion "PSEgetLibVersion"
+#define kPCSXRGetLibType "PSEgetLibType"
+
+@interface PcsxrPlugin ()
+@property (readwrite, copy) NSString *path;
+@property (readwrite, strong) NSString *name;
+@property (strong) NSDate *modDate;
+@property (strong) NSString *fullPlugPath;
+@property long version;
+@property (readwrite) int type;
+@property int active;
+@property void *pluginRef;
+@end
+
+@implementation PcsxrPlugin
+@synthesize pluginRef;
+@synthesize active;
+@synthesize fullPlugPath;
+@synthesize modDate;
+@synthesize path;
+@synthesize type;
+@synthesize version;
+
++ (NSString *)prefixForType:(int)aType
+{
+ switch (aType) {
+ case PSE_LT_GPU: return @"GPU"; break;
+ case PSE_LT_CDR: return @"CDR"; break;
+ case PSE_LT_SPU: return @"SPU"; break;
+ case PSE_LT_PAD: return @"PAD"; break;
+ case PSE_LT_NET: return @"NET"; break;
+ case PSE_LT_SIO1: return @"SIO1"; break;
+ }
+
+ return @"";
+}
+
++ (NSString *)defaultKeyForType:(int)aType
+{
+ //return @"Plugin" [PcsxrPlugin prefixForType:aType];
+ switch (aType) {
+ case PSE_LT_GPU:
+ case PSE_LT_CDR:
+ case PSE_LT_SPU:
+ case PSE_LT_PAD:
+ case PSE_LT_NET:
+ case PSE_LT_SIO1:
+ return [NSString stringWithFormat:@"Plugin%@", [self prefixForType:aType]];
+ break;
+ default:
+ return @"";
+ break;
+ }
+}
+
++ (char **)configEntriesForType:(int)aType
+{
+ static char *gpu[2] = {(char *)&Config.Gpu, NULL};
+ static char *cdr[2] = {(char *)&Config.Cdr, NULL};
+ static char *spu[2] = {(char *)&Config.Spu, NULL};
+ static char *pad[3] = {(char *)&Config.Pad1, (char *)&Config.Pad2, NULL};
+ static char *net[2] = {(char *)&Config.Net, NULL};
+ static char *sio1[2] = {(char *)&Config.Sio1, NULL};
+
+ switch (aType) {
+ case PSE_LT_GPU: return (char **)gpu;
+ case PSE_LT_CDR: return (char **)cdr;
+ case PSE_LT_SPU: return (char **)spu;
+ case PSE_LT_PAD: return (char **)pad;
+ case PSE_LT_NET: return (char **)net;
+ case PSE_LT_SIO1: return (char **)sio1;
+ }
+
+ return nil;
+}
+
++ (NSArray *)pluginsPaths
+{
+ static NSArray *returnArray = nil;
+ if (returnArray == nil)
+ {
+ NSURL *supportURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
+ NSURL *libraryURL = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
+ NSURL *localSupportURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSLocalDomainMask appropriateForURL:nil create:YES error:NULL];
+ NSURL *localLibraryURL = [[NSFileManager defaultManager] URLForDirectory:NSLibraryDirectory inDomain:NSLocalDomainMask appropriateForURL:nil create:YES error:NULL];
+
+ NSMutableArray *mutArray = [NSMutableArray arrayWithCapacity:5];
+
+ [mutArray addObject:[[NSFileManager defaultManager] stringWithFileSystemRepresentation:Config.PluginsDir length:strlen(Config.PluginsDir)]];
+ NSURL *url = [localLibraryURL URLByAppendingPathComponent:@"Playstation Emulator Plugins"];
+ if ([url checkResourceIsReachableAndReturnError:NULL])
+ [mutArray addObject:[url path]];
+ url = [[localSupportURL URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"PlugIns"];
+ if ([url checkResourceIsReachableAndReturnError:NULL])
+ [mutArray addObject:[url path]];
+ url = [libraryURL URLByAppendingPathComponent:@"Playstation Emulator Plugins"];
+ if ([url checkResourceIsReachableAndReturnError:NULL])
+ [mutArray addObject:[url path]];
+ url = [[supportURL URLByAppendingPathComponent:@"Pcsxr"] URLByAppendingPathComponent:@"PlugIns"];
+ if ([url checkResourceIsReachableAndReturnError:NULL])
+ [mutArray addObject:[url path]];
+ returnArray = [[NSArray alloc] initWithArray:mutArray];
+ }
+ return returnArray;
+}
+
+- (id)initWithPath:(NSString *)aPath
+{
+ if (!(self = [super init])) {
+ return nil;
+ }
+
+ PSEgetLibType PSE_getLibType = NULL;
+ PSEgetLibVersion PSE_getLibVersion = NULL;
+ PSEgetLibName PSE_getLibName = NULL;
+
+ NSFileManager *fm = [NSFileManager defaultManager];
+
+ pluginRef = NULL;
+ self.name = nil;
+ self.path = aPath;
+ NSString *goodPath = nil;
+ if ([aPath isAbsolutePath]) {
+ goodPath = aPath;
+ } else {
+ long tempVers = 0;
+ for (NSString *plugDir in [PcsxrPlugin pluginsPaths])
+ {
+ NSString *fullPath = [plugDir stringByAppendingPathComponent:path];
+ if ([fm fileExistsAtPath:fullPath]) {
+ void *tempHandle = SysLoadLibrary([fullPath fileSystemRepresentation]);
+ if (tempHandle != NULL)
+ {
+ PSEgetLibVersion tempLibVersion = SysLoadSym(tempHandle, kPCSXRGetLibVersion);
+ if (SysLibError() == NULL)
+ {
+ long tempVers2 = tempLibVersion();
+ if (tempVers < tempVers2 ){
+ goodPath = fullPath;
+ tempVers = tempVers2;
+ if (![plugDir isEqualToString:[fm stringWithFileSystemRepresentation:Config.PluginsDir length:strlen(Config.PluginsDir)]]) {
+ self.path = goodPath;
+ }
+ }
+ }
+ SysCloseLibrary(tempHandle);
+ } else {
+ SysLibError();
+ }
+ }
+ }
+ }
+
+ if (goodPath == nil) {
+ return nil;
+ }
+
+ pluginRef = SysLoadLibrary([goodPath fileSystemRepresentation]);
+ if (pluginRef == NULL) {
+ SysLibError();
+ return nil;
+ }
+
+ // TODO: add support for plugins with multiple functionalities???
+ PSE_getLibType = (PSEgetLibType) SysLoadSym(pluginRef, kPCSXRGetLibType);
+ if (SysLibError() != nil) {
+ if (([path rangeOfString: @"gpu" options:NSCaseInsensitiveSearch]).length != 0)
+ type = PSE_LT_GPU;
+ else if (([path rangeOfString: @"cdr" options:NSCaseInsensitiveSearch]).length != 0)
+ type = PSE_LT_CDR;
+ else if (([path rangeOfString: @"spu" options:NSCaseInsensitiveSearch]).length != 0)
+ type = PSE_LT_SPU;
+ else if (([path rangeOfString: @"pad" options:NSCaseInsensitiveSearch]).length != 0)
+ type = PSE_LT_PAD;
+ else if (([path rangeOfString: @"net" options:NSCaseInsensitiveSearch]).length != 0)
+ type = PSE_LT_NET;
+ else if (([path rangeOfString: @"sio1" options:NSCaseInsensitiveSearch]).length != 0)
+ type = PSE_LT_SIO1;
+ else {
+ return nil;
+ }
+ } else {
+ type = (int)PSE_getLibType();
+ if (type != PSE_LT_GPU && type != PSE_LT_CDR && type != PSE_LT_SPU && type != PSE_LT_PAD && type != PSE_LT_NET && type != PSE_LT_SIO1) {
+ return nil;
+ }
+ }
+
+ PSE_getLibName = (PSEgetLibName) SysLoadSym(pluginRef, kPCSXRGetLibName);
+ if (SysLibError() == nil) {
+ self.name = @(PSE_getLibName());
+ }
+
+ PSE_getLibVersion = (PSEgetLibVersion) SysLoadSym(pluginRef, kPCSXRGetLibVersion);
+ if (SysLibError() == nil) {
+ version = PSE_getLibVersion();
+ } else {
+ version = -1;
+ }
+
+ // save the current modification date
+ NSDictionary *fattrs = [fm attributesOfItemAtPath:[goodPath stringByResolvingSymlinksInPath] error:NULL];
+ self.modDate = [fattrs fileModificationDate];
+ self.fullPlugPath = goodPath;
+
+ active = 0;
+
+ return self;
+}
+
+- (void)dealloc
+{
+ int i;
+
+ // shutdown if we had previously been inited
+ for (i=0; i<32; i++) {
+ if (active & (1 << i)) {
+ [self shutdownAs:(1 << i)];
+ }
+ }
+
+ if (pluginRef) {
+ SysCloseLibrary(pluginRef);
+ pluginRef = NULL;
+ }
+}
+
+#define PluginSymbolName(type, theName) [[PcsxrPlugin prefixForType:type] stringByAppendingString:theName]
+
+- (void)runCommand:(id)arg
+{
+ @autoreleasepool {
+ NSString *funcName = arg[0];
+ long (*func)(void);
+
+ func = SysLoadSym(pluginRef, [funcName cStringUsingEncoding:NSASCIIStringEncoding]);
+ if (SysLibError() == NULL) {
+ func();
+ } else {
+ NSBeep();
+ }
+
+ return;
+ }
+}
+
+- (long)runAs:(int)aType
+{
+ long (*init)();
+ long (*initArg)(long arg);
+ long res = PSE_ERR_FATAL;
+
+ if ((active & aType) == aType) {
+ return 0;
+ }
+
+ init = initArg = SysLoadSym(pluginRef, [PluginSymbolName(aType, @"init")
+ cStringUsingEncoding:NSASCIIStringEncoding]);
+ if (SysLibError() == NULL) {
+ if (aType != PSE_LT_PAD) {
+ res = init();
+ } else {
+ res = initArg(1|2);
+ }
+ }
+
+ if (0 == res) {
+ active |= aType;
+ } else {
+ NSRunCriticalAlertPanel(NSLocalizedString(@"Plugin Initialization Failed!", nil),
+ NSLocalizedString(@"Pcsxr failed to initialize the selected %@ plugin (error=%i).\nThe plugin might not work with your system.", nil),
+ nil, nil, nil, [PcsxrPlugin prefixForType:aType], res);
+ return res;
+ }
+
+ //Prevent a memory leak.
+ long (*shutdown)(void);
+ shutdown = SysLoadSym(pluginRef, [PluginSymbolName(aType, @"shutdown")
+ cStringUsingEncoding:NSASCIIStringEncoding]);
+ if (SysLibError() == NULL) {
+ shutdown();
+ }
+
+ return res;
+}
+
+- (long)shutdownAs:(int)aType
+{
+#if 0
+ long (*shutdown)(void);
+
+ shutdown = SysLoadSym(pluginRef, [PluginSymbolName(aType, @"shutdown")
+ cStringUsingEncoding:NSASCIIStringEncoding]);
+ if (SysLibError() == nil) {
+ active &= ~aType;
+ return shutdown();
+ }
+
+ return PSE_ERR_FATAL;
+#else
+ active &= ~aType;
+ return PSE_ERR_SUCCESS;
+#endif
+}
+
+#define PluginSymbolNameConfigure(type) PluginSymbolName(type, @"configure")
+#define PluginSymbolNameAbout(type) PluginSymbolName(type, @"about")
+
+- (BOOL)hasAboutAs:(int)aType
+{
+ SysLoadSym(pluginRef, [PluginSymbolNameAbout(aType)
+ cStringUsingEncoding:NSASCIIStringEncoding]);
+
+ return (SysLibError() == NULL);
+}
+
+- (BOOL)hasConfigureAs:(int)aType
+{
+ SysLoadSym(pluginRef, [PluginSymbolNameConfigure(aType)
+ cStringUsingEncoding:NSASCIIStringEncoding]);
+
+ return (SysLibError() == NULL);
+}
+
+- (void)aboutAs:(int)aType
+{
+ NSArray *arg;
+
+ NSString *aboutSym = PluginSymbolNameAbout(aType);
+ arg = @[aboutSym, @0];
+
+ // detach a new thread
+ [NSThread detachNewThreadSelector:@selector(runCommand:) toTarget:self
+ withObject:arg];
+}
+
+- (void)configureAs:(int)aType
+{
+ NSArray *arg;
+
+ NSString *configSym = PluginSymbolNameConfigure(aType);
+ arg = @[configSym, @1];
+
+ // detach a new thread
+ [NSThread detachNewThreadSelector:@selector(runCommand:) toTarget:self
+ withObject:arg];
+}
+
+- (NSString *)displayVersion
+{
+ if (version == -1)
+ return @"";
+
+ return [NSString stringWithFormat:@"v%ld.%ld.%ld", version>>16,(version>>8)&0xff,version&0xff];
+}
+
+- (NSUInteger)hash
+{
+ return [path hash];
+}
+
+- (NSString *)description
+{
+ if (_name == nil)
+ return [path lastPathComponent];
+
+ return [NSString stringWithFormat:@"%@ %@ [%@]", self.name, [self displayVersion], [path lastPathComponent]];
+}
+
+- (NSString*)debugDescription
+{
+ if (_name == nil) {
+ return fullPlugPath;
+ }
+ return [NSString stringWithFormat:@"%@, %@ [%@]", self.name, [self displayVersion], fullPlugPath];
+}
+
+// the plugin will check if it's still valid and return the status
+- (BOOL)verifyOK
+{
+ // check that the file is still there with the same modification date
+ NSFileManager *dfm = [NSFileManager defaultManager];
+ if (![dfm fileExistsAtPath:fullPlugPath])
+ return NO;
+
+ NSDictionary *fattrs = [dfm attributesOfItemAtPath:[fullPlugPath stringByResolvingSymlinksInPath] error:NULL];
+ return [[fattrs fileModificationDate] isEqualToDate:modDate];
+}
+
+@end
diff --git a/macosx/Source/PcsxrPluginHandler.h b/macosx/Source/PcsxrPluginHandler.h
new file mode 100644
index 00000000..2855e410
--- /dev/null
+++ b/macosx/Source/PcsxrPluginHandler.h
@@ -0,0 +1,15 @@
+//
+// PcsxrPluginHandler.h
+// Pcsxr
+//
+// Created by Charles Betts on 12/10/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PcsxrFileHandle.h"
+
+@interface PcsxrPluginHandler : NSWindowController <PcsxrFileHandle>
+@property (weak) IBOutlet NSTextField *pluginName;
+- (IBAction)closeAddPluginSheet:(id)sender;
+@end
diff --git a/macosx/Source/PcsxrPluginHandler.m b/macosx/Source/PcsxrPluginHandler.m
new file mode 100644
index 00000000..6e99932b
--- /dev/null
+++ b/macosx/Source/PcsxrPluginHandler.m
@@ -0,0 +1,97 @@
+//
+// PcsxrPluginHandler.m
+// Pcsxr
+//
+// Created by Charles Betts on 12/10/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
+#import "PcsxrPluginHandler.h"
+#import "EmuThread.h"
+
+@interface PcsxrPluginHandler ()
+@property BOOL moveOK;
+@end
+
+@implementation PcsxrPluginHandler
+@synthesize pluginName;
+@synthesize moveOK;
+
++ (NSArray *)supportedUTIs
+{
+ static NSArray *utisupport = nil;
+ if (utisupport == nil) {
+ utisupport = @[@"com.codeplex.pcsxr.plugin"];
+ }
+ return utisupport;
+}
+
+- (id)initWithWindow:(NSWindow *)window
+{
+ self = [super initWithWindow:window];
+ if (self) {
+ moveOK = NO;
+ }
+
+ return self;
+}
+
+- (NSString*)windowNibName
+{
+ return @"AddPluginSheet";
+}
+
+- (id)init
+{
+ return self = [self initWithWindowNibName:@"AddPluginSheet"];
+}
+
+- (void)windowDidLoad
+{
+ [super windowDidLoad];
+
+ // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
+}
+
+- (IBAction)closeAddPluginSheet:(id)sender
+{
+ if ([[sender keyEquivalent] isEqualToString:@"\r"]) {
+ moveOK = YES;
+ } else {
+ moveOK = NO;
+ }
+ [NSApp stopModal];
+}
+
+- (BOOL)handleFile:(NSString *)theFile
+{
+ if ([EmuThread active]) {
+ return NO;
+ }
+
+ [pluginName setObjectValue:[theFile lastPathComponent]];
+
+ [NSApp runModalForWindow:[self window]];
+
+ [[self window] orderOut:nil];
+ if (moveOK == YES) {
+ NSURL *supportURL = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
+ NSURL *url = [[supportURL URLByAppendingPathComponent:@"Pcsxr" isDirectory:YES] URLByAppendingPathComponent:@"PlugIns" isDirectory:YES];
+
+ NSFileWrapper *wrapper = [[NSFileWrapper alloc] initWithPath:theFile];
+ NSString *dst = [[url URLByAppendingPathComponent:[wrapper filename]] path];
+ if ([wrapper writeToFile:dst atomically:NO updateFilenames:NO]) {
+ [[NSWorkspace sharedWorkspace] noteFileSystemChanged:[url path]];
+ NSRunInformationalAlertPanel(NSLocalizedString(@"Installation Succesfull", nil),
+ NSLocalizedString(@"The installation of the specified plugin was succesfull. In order to use it, please restart the application.", nil),
+ nil, nil, nil);
+ } else {
+ NSRunAlertPanel(NSLocalizedString(@"Installation Failed!", nil),
+ NSLocalizedString(@"The installation of the specified plugin failed. Please try again, or make a manual install.", nil),
+ nil, nil, nil);
+ }
+ }
+ return YES;
+}
+
+@end
diff --git a/macosx/Source/Plugin.c b/macosx/Source/Plugin.c
new file mode 100644
index 00000000..b9e7ff4a
--- /dev/null
+++ b/macosx/Source/Plugin.c
@@ -0,0 +1,227 @@
+/* Pcsxr - Pc Psx Emulator
+ * Copyright (C) 1999-2002 Pcsx Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include "psxcommon.h"
+#include "plugins.h"
+#include "spu.h"
+
+void OnFile_Exit();
+
+unsigned long gpuDisp;
+
+long SPU__open(void) {
+ return SPU_open();
+}
+
+int StatesC = 0;
+extern int UseGui;
+int ShowPic=0;
+
+void gpuShowPic() {
+}
+
+void PADhandleKey(int key) {
+ //TODO: get these set up properly!
+ GPU_keypressed(key);
+#ifdef ENABLE_SIO1API
+ SIO1_keypressed(key);
+#endif
+ if (Config.UseNet)
+ NET_keypressed(key);
+}
+
+long PAD1__open(void) {
+ return PAD1_open(&gpuDisp);
+}
+
+long PAD2__open(void) {
+ return PAD2_open(&gpuDisp);
+}
+
+void SignalExit(int sig) {
+ ClosePlugins();
+ OnFile_Exit();
+}
+
+#define PARSEPATH(dst, src) \
+ ptr = src + strlen(src); \
+ while (*ptr != '\\' && ptr != src) ptr--; \
+ if (ptr != src) { \
+ strcpy(dst, ptr+1); \
+ }
+
+int _OpenPlugins() {
+ long ret;
+
+ GPU_clearDynarec(clearDynarec);
+
+ ret = CDR_open();
+ if (ret < 0) { SysMessage("%s", _("Error Opening CDR Plugin")); return -1; }
+ ret = SPU_open();
+ if (ret < 0) { SysMessage("%s", _("Error Opening SPU Plugin")); return -1; }
+ SPU_registerCallback(SPUirq);
+ ret = GPU_open(&gpuDisp, "PCSXR", NULL);
+ if (ret < 0) { SysMessage("%s", _("Error Opening GPU Plugin")); return -1; }
+ ret = PAD1_open(&gpuDisp);
+ if (ret < 0) { SysMessage("%s", _("Error Opening PAD1 Plugin")); return -1; }
+ PAD1_registerVibration(GPU_visualVibration);
+ PAD1_registerCursor(GPU_cursor);
+ ret = PAD2_open(&gpuDisp);
+ if (ret < 0) { SysMessage("%s", _("Error Opening PAD2 Plugin")); return -1; }
+ PAD2_registerVibration(GPU_visualVibration);
+ PAD2_registerCursor(GPU_cursor);
+#ifdef ENABLE_SIO1API
+ ret = SIO1_open(&gpuDisp);
+ if (ret < 0) { SysMessage("%s", _("Error opening SIO1 plugin!")); return -1; }
+ SIO1_registerCallback(SIO1irq);
+#endif
+
+ if (Config.UseNet && !NetOpened) {
+ netInfo info;
+ char path[MAXPATHLEN];
+
+ strcpy(info.EmuName, "PCSXR " PACKAGE_VERSION);
+ strncpy(info.CdromID, CdromId, 9);
+ strncpy(info.CdromLabel, CdromLabel, 11);
+ info.psxMem = psxM;
+ info.GPU_showScreenPic = GPU_showScreenPic;
+ info.GPU_displayText = GPU_displayText;
+ info.GPU_showScreenPic = GPU_showScreenPic;
+ info.PAD_setSensitive = PAD1_setSensitive;
+ sprintf(path, "%s%s", Config.BiosDir, Config.Bios);
+ strcpy(info.BIOSpath, path);
+ strcpy(info.MCD1path, Config.Mcd1);
+ strcpy(info.MCD2path, Config.Mcd2);
+ sprintf(path, "%s%s", Config.PluginsDir, Config.Gpu);
+ strcpy(info.GPUpath, path);
+ sprintf(path, "%s%s", Config.PluginsDir, Config.Spu);
+ strcpy(info.SPUpath, path);
+ sprintf(path, "%s%s", Config.PluginsDir, Config.Cdr);
+ strcpy(info.CDRpath, path);
+ NET_setInfo(&info);
+
+ ret = NET_open(&gpuDisp);
+ if (ret < 0) {
+ if (ret == -2) {
+ // -2 is returned when something in the info
+ // changed and needs to be synced
+ char *ptr;
+
+ PARSEPATH(Config.Bios, info.BIOSpath);
+ PARSEPATH(Config.Gpu, info.GPUpath);
+ PARSEPATH(Config.Spu, info.SPUpath);
+ PARSEPATH(Config.Cdr, info.CDRpath);
+
+ strcpy(Config.Mcd1, info.MCD1path);
+ strcpy(Config.Mcd2, info.MCD2path);
+ return -2;
+ } else {
+ Config.UseNet = FALSE;
+ }
+ } else {
+ if (NET_queryPlayer() == 1) {
+ if (SendPcsxInfo() == -1) Config.UseNet = FALSE;
+ } else {
+ if (RecvPcsxInfo() == -1) Config.UseNet = FALSE;
+ }
+ }
+ NetOpened = TRUE;
+ } else if (Config.UseNet) {
+ NET_resume();
+ }
+
+ return 0;
+}
+
+int OpenPlugins() {
+ int ret;
+
+ while ((ret = _OpenPlugins()) == -2) {
+ ReleasePlugins();
+ LoadMcds(Config.Mcd1, Config.Mcd2);
+ if (LoadPlugins() == -1) return -1;
+ }
+ return ret;
+}
+
+void ClosePlugins() {
+ long ret;
+
+ //signal(SIGINT, SIG_DFL);
+ //signal(SIGPIPE, SIG_DFL);
+ ret = CDR_close();
+ if (ret < 0) { SysMessage("%s", _("Error Closing CDR Plugin")); return; }
+ ret = SPU_close();
+ if (ret < 0) { SysMessage("%s", _("Error Closing SPU Plugin")); return; }
+ ret = PAD1_close();
+ if (ret < 0) { SysMessage("%s", _("Error Closing PAD1 Plugin")); return; }
+ ret = PAD2_close();
+ if (ret < 0) { SysMessage("%s", _("Error Closing PAD2 Plugin")); return; }
+ ret = GPU_close();
+ if (ret < 0) { SysMessage("%s", _("Error Closing GPU Plugin")); return; }
+#ifdef ENABLE_SIO1API
+ ret = SIO1_close();
+ if (ret < 0) { SysMessage("%s", _("Error closing SIO1 plugin!")); return; }
+#endif
+
+ if (Config.UseNet) {
+ NET_pause();
+ }
+}
+
+void ResetPlugins() {
+ long ret;
+
+ CDR_shutdown();
+ GPU_shutdown();
+ SPU_shutdown();
+ PAD1_shutdown();
+ PAD2_shutdown();
+#ifdef ENABLE_SIO1API
+ SIO1_shutdown();
+#endif
+ if (Config.UseNet) NET_shutdown();
+
+ ret = CDR_init();
+ if (ret < 0) { SysMessage(_("CDRinit error: %d"), ret); return; }
+ ret = GPU_init();
+ if (ret < 0) { SysMessage(_("GPUinit error: %d"), ret); return; }
+ ret = SPU_init();
+ if (ret < 0) { SysMessage(_("SPUinit error: %d"), ret); return; }
+ ret = PAD1_init(1);
+ if (ret < 0) { SysMessage(_("PAD1init error: %d"), ret); return; }
+ ret = PAD2_init(2);
+ if (ret < 0) { SysMessage(_("PAD2init error: %d"), ret); return; }
+#ifdef ENABLE_SIO1API
+ ret = SIO1_init();
+ if (ret < 0) { SysMessage(_("SIO1init error: %d!"), ret); return; }
+#endif
+
+ if (Config.UseNet) {
+ ret = NET_init();
+ if (ret < 0) { SysMessage(_("NETinit error: %d"), ret); return; }
+ }
+
+ NetOpened = FALSE;
+}
diff --git a/macosx/Source/PluginController.h b/macosx/Source/PluginController.h
new file mode 100644
index 00000000..4519d98a
--- /dev/null
+++ b/macosx/Source/PluginController.h
@@ -0,0 +1,18 @@
+/* PluginController */
+
+#import <Cocoa/Cocoa.h>
+#import "PluginList.h"
+
+@interface PluginController : NSObject
+
+@property (weak) IBOutlet NSButton *aboutButton;
+@property (weak) IBOutlet NSButton *configureButton;
+@property (weak) IBOutlet NSPopUpButton *pluginMenu;
+
+- (IBAction)doAbout:(id)sender;
+- (IBAction)doConfigure:(id)sender;
+- (IBAction)selectPlugin:(id)sender;
+
+- (void)setPluginsTo:(NSArray *)list withType:(int)type;
+
+@end
diff --git a/macosx/Source/PluginController.m b/macosx/Source/PluginController.m
new file mode 100644
index 00000000..2d9b26c9
--- /dev/null
+++ b/macosx/Source/PluginController.m
@@ -0,0 +1,83 @@
+#import "PluginController.h"
+#import "PcsxrPlugin.h"
+#import "PcsxrController.h"
+
+@interface PluginController ()
+@property (strong) NSArray *plugins;
+@property (strong) NSString *defaultKey;
+@property int pluginType;
+@end
+
+@implementation PluginController
+@synthesize aboutButton;
+@synthesize configureButton;
+@synthesize pluginMenu;
+
+- (IBAction)doAbout:(id)sender
+{
+ PcsxrPlugin *plugin = (self.plugins)[[pluginMenu indexOfSelectedItem]];
+ [plugin aboutAs:self.pluginType];
+}
+
+- (IBAction)doConfigure:(id)sender
+{
+ PcsxrPlugin *plugin = (self.plugins)[[pluginMenu indexOfSelectedItem]];
+ [plugin configureAs:self.pluginType];
+}
+
+- (IBAction)selectPlugin:(id)sender
+{
+ if (sender == pluginMenu) {
+ NSInteger index = [pluginMenu indexOfSelectedItem];
+ if (index != -1) {
+ PcsxrPlugin *plugin = (self.plugins)[index];
+
+ if (![[PluginList list] setActivePlugin:plugin forType:self.pluginType]) {
+ /* plugin won't initialize */
+ }
+
+ // write selection to defaults
+ [[NSUserDefaults standardUserDefaults] setObject:[plugin path] forKey:self.defaultKey];
+
+ // set button states
+ [aboutButton setEnabled:[plugin hasAboutAs:self.pluginType]];
+ [configureButton setEnabled:[plugin hasConfigureAs:self.pluginType]];
+ } else {
+ // set button states
+ [aboutButton setEnabled:NO];
+ [configureButton setEnabled:NO];
+ }
+ }
+}
+
+// must be called before anything else
+- (void)setPluginsTo:(NSArray *)list withType:(int)type
+{
+ NSString *sel;
+
+ // remember the list
+ self.pluginType = type;
+ self.plugins = list;
+ self.defaultKey = [PcsxrPlugin defaultKeyForType:self.pluginType];
+
+ // clear the previous menu items
+ [pluginMenu removeAllItems];
+
+ // load the currently selected plugin
+ sel = [[NSUserDefaults standardUserDefaults] stringForKey:self.defaultKey];
+
+ // add the menu entries
+ for (PcsxrPlugin *plug in self.plugins) {
+ NSString *description = [plug description];
+ [pluginMenu addItemWithTitle:description];
+
+ // make sure the currently selected is set as such
+ if ([sel isEqualToString:[plug path]]) {
+ [pluginMenu selectItemWithTitle:description];
+ }
+ }
+
+ [self selectPlugin:pluginMenu];
+}
+
+@end
diff --git a/macosx/Source/PluginList.h b/macosx/Source/PluginList.h
new file mode 100644
index 00000000..f6e0de55
--- /dev/null
+++ b/macosx/Source/PluginList.h
@@ -0,0 +1,28 @@
+//
+// PluginList.h
+// Pcsxr
+//
+// Created by Gil Pedersen on Sun Sep 21 2003.
+// Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "PcsxrPlugin.h"
+
+//extern NSMutableArray *plugins;
+
+@interface PluginList : NSObject
+
++ (PluginList *)list;
+
+- (void)refreshPlugins;
+- (NSArray *)pluginsForType:(int)typeMask;
+- (BOOL)hasPluginAtPath:(NSString *)path;
+- (BOOL)configured;
+- (PcsxrPlugin *)activePluginForType:(int)type;
+- (BOOL)setActivePlugin:(PcsxrPlugin *)plugin forType:(int)type;
+
+- (void)disableNetPlug;
+- (void)enableNetPlug;
+
+@end
diff --git a/macosx/Source/PluginList.m b/macosx/Source/PluginList.m
new file mode 100644
index 00000000..6b86bd90
--- /dev/null
+++ b/macosx/Source/PluginList.m
@@ -0,0 +1,318 @@
+//
+// PluginList.m
+// Pcsxr
+//
+// Created by Gil Pedersen on Sun Sep 21 2003.
+// Copyright (c) 2003 __MyCompanyName__. All rights reserved.
+//
+
+#import "EmuThread.h"
+#import "PluginList.h"
+#import "PcsxrPlugin.h"
+#include "psxcommon.h"
+#include "plugins.h"
+
+static PluginList __weak *sPluginList = nil;
+const static int typeList[] = {PSE_LT_GPU, PSE_LT_SPU, PSE_LT_CDR, PSE_LT_PAD, PSE_LT_NET, PSE_LT_SIO1};
+
+@interface PluginList ()
+@property (strong) NSMutableArray *pluginList;
+@property BOOL missingPlugins;
+@property (strong) PcsxrPlugin *activeGpuPlugin;
+@property (strong) PcsxrPlugin *activeSpuPlugin;
+@property (strong) PcsxrPlugin *activeCdrPlugin;
+@property (strong) PcsxrPlugin *activePadPlugin;
+@property (strong) PcsxrPlugin *activeNetPlugin;
+@property (strong) PcsxrPlugin *activeSIO1Plugin;
+
+@end
+
+@implementation PluginList
+@synthesize missingPlugins;
+
++ (PluginList *)list
+{
+ return sPluginList;
+}
+
+- (id)init
+{
+ NSUInteger i;
+
+ if (!(self = [super init]))
+ {
+ return nil;
+ }
+
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
+ self.pluginList = [[NSMutableArray alloc] initWithCapacity:20];
+
+ self.activeGpuPlugin = self.activeSpuPlugin = self.activeCdrPlugin = self.activePadPlugin = self.activeNetPlugin = self.activeSIO1Plugin = nil;
+
+ missingPlugins = NO;
+ for (i = 0; i < sizeof(typeList) / sizeof(typeList[0]); i++) {
+ NSString *path = [defaults stringForKey:[PcsxrPlugin defaultKeyForType:typeList[i]]];
+ if (nil == path) {
+ missingPlugins = YES;
+ continue;
+ }
+ if ([path isEqualToString:@"Disabled"])
+ continue;
+
+ if (![self hasPluginAtPath:path]) {
+ @autoreleasepool {
+ PcsxrPlugin *plugin = [[PcsxrPlugin alloc] initWithPath:path];
+ if (plugin) {
+ [self.pluginList addObject:plugin];
+ if (![self setActivePlugin:plugin forType:typeList[i]])
+ missingPlugins = YES;
+ } else {
+ missingPlugins = YES;
+ }
+ }
+ }
+ }
+
+ if (missingPlugins) {
+ [self refreshPlugins];
+ }
+
+ sPluginList = self;
+
+ return self;
+}
+
+- (void)refreshPlugins
+{
+ NSDirectoryEnumerator *dirEnum;
+ NSString *pname;
+ NSUInteger i;
+
+ // verify that the ones that are in list still works
+ for (i=0; i < [self.pluginList count]; i++) {
+ if (![(self.pluginList)[i] verifyOK]) {
+ [self.pluginList removeObjectAtIndex:i]; i--;
+ }
+ }
+
+ for (NSString *plugDir in [PcsxrPlugin pluginsPaths])
+ {
+ // look for new ones in the plugin directory
+ dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:plugDir];
+
+ while ((pname = [dirEnum nextObject])) {
+ if ([[pname pathExtension] isEqualToString:@"psxplugin"] ||
+ [[pname pathExtension] isEqualToString:@"so"]) {
+ [dirEnum skipDescendents]; /* don't enumerate this
+ directory */
+
+ if (![self hasPluginAtPath:pname]) {
+ @autoreleasepool {
+ PcsxrPlugin *plugin = [[PcsxrPlugin alloc] initWithPath:pname];
+ if (plugin != nil) {
+ [self.pluginList addObject:plugin];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // check the we have the needed plugins
+ missingPlugins = NO;
+ for (i=0; i < 4 /*sizeof(*typeList)*/; i++) {
+ PcsxrPlugin *plugin = [self activePluginForType:typeList[i]];
+ if (nil == plugin) {
+ NSArray *list = [self pluginsForType:typeList[i]];
+ NSUInteger j;
+
+ for (j=0; j < [list count]; j++) {
+ if ([self setActivePlugin:list[j] forType:typeList[i]])
+ break;
+ }
+ if (j == [list count])
+ missingPlugins = YES;
+ }
+ }
+}
+
+- (NSArray *)pluginsForType:(int)typeMask
+{
+ NSMutableArray *types = [NSMutableArray array];
+
+ for (PcsxrPlugin *plugin in self.pluginList) {
+ if ([plugin type] & typeMask) {
+ [types addObject:plugin];
+ }
+ }
+
+ return types;
+}
+
+- (BOOL)hasPluginAtPath:(NSString *)path
+{
+ if (nil == path)
+ return NO;
+
+ for (PcsxrPlugin *plugin in self.pluginList) {
+ if ([[plugin path] isEqualToString:path])
+ return YES;
+ }
+
+ return NO;
+}
+
+// returns if all the required plugins are available
+- (BOOL)configured
+{
+ return !missingPlugins;
+}
+
+- (BOOL)doInitPlugins
+{
+ BOOL bad = NO;
+
+ if ([self.activeGpuPlugin runAs:PSE_LT_GPU] != 0)
+ bad = YES;
+ if ([self.activeSpuPlugin runAs:PSE_LT_SPU] != 0)
+ bad = YES;
+ if ([self.activeCdrPlugin runAs:PSE_LT_CDR] != 0)
+ bad = YES;
+ if ([self.activePadPlugin runAs:PSE_LT_PAD] != 0)
+ bad = YES;
+ if ([self.activeNetPlugin runAs:PSE_LT_NET] != 0)
+ bad = YES;
+ if ([self.activeSIO1Plugin runAs:PSE_LT_SIO1] != 0)
+ bad = YES;
+
+ return !bad;
+}
+
+- (PcsxrPlugin *)activePluginForType:(int)type
+{
+ switch (type) {
+ case PSE_LT_GPU: return self.activeGpuPlugin; break;
+ case PSE_LT_CDR: return self.activeCdrPlugin; break;
+ case PSE_LT_SPU: return self.activeSpuPlugin; break;
+ case PSE_LT_PAD: return self.activePadPlugin; break;
+ case PSE_LT_NET: return self.activeNetPlugin; break;
+ case PSE_LT_SIO1: return self.activeSIO1Plugin; break;
+ }
+
+ return nil;
+}
+
+- (BOOL)setActivePlugin:(PcsxrPlugin *)plugin forType:(int)type
+{
+ PcsxrPlugin *pluginPtr = nil;
+
+ switch (type) {
+ case PSE_LT_SIO1:
+ case PSE_LT_GPU:
+ case PSE_LT_CDR:
+ case PSE_LT_SPU:
+ case PSE_LT_PAD:
+ case PSE_LT_NET: pluginPtr = [self activePluginForType:type]; break;
+ default: return NO; break;
+ }
+ if (plugin == pluginPtr) {
+ return YES;
+ }
+
+ BOOL active = pluginPtr && [EmuThread active];
+ BOOL wasPaused = NO;
+ if (active) {
+ // TODO: temporary freeze?
+ wasPaused = [EmuThread pauseSafe];
+ ClosePlugins();
+ ReleasePlugins();
+ }
+
+ // stop the old plugin and start the new one
+ if (pluginPtr) {
+ [pluginPtr shutdownAs:type];
+ pluginPtr = nil;
+ }
+
+ if ([plugin runAs:type] != 0) {
+ plugin = nil;
+ }
+
+ switch (type) {
+ case PSE_LT_GPU:
+ self.activeGpuPlugin = plugin;
+ break;
+ case PSE_LT_CDR:
+ self.activeCdrPlugin = plugin;
+ break;
+ case PSE_LT_SPU:
+ self.activeSpuPlugin = plugin;
+ break;
+ case PSE_LT_PAD:
+ self.activePadPlugin = plugin;
+ break;
+ case PSE_LT_NET:
+ self.activeNetPlugin = plugin;
+ break;
+ case PSE_LT_SIO1:
+ self.activeSIO1Plugin = plugin;
+ break;
+ }
+
+ // write path to the correct config entry
+ const char *str;
+ if (plugin != nil) {
+ str = [[plugin path] fileSystemRepresentation];
+ if (str == NULL) {
+ str = "Invalid Plugin";
+ }
+ } else {
+ str = "Invalid Plugin";
+ }
+
+ char **dst = [PcsxrPlugin configEntriesForType:type];
+ while (*dst) {
+ strlcpy(*dst, str, MAXPATHLEN);
+ dst++;
+ }
+
+ if (active) {
+ LoadPlugins();
+ OpenPlugins();
+
+ if (!wasPaused) {
+ [EmuThread resume];
+ }
+ }
+
+ return plugin != nil;
+}
+
+- (void)disableNetPlug
+{
+ char **dst = [PcsxrPlugin configEntriesForType:PSE_LT_NET];
+ while (*dst) {
+ strcpy(*dst, "Disabled");
+ dst++;
+ }
+}
+
+- (void)enableNetPlug
+{
+ PcsxrPlugin *netPlug = [self activePluginForType:PSE_LT_NET];
+
+ const char *str = NULL;
+ if (netPlug) {
+ str = [[netPlug path] fileSystemRepresentation];
+ }
+ if (str) {
+ char **dst = [PcsxrPlugin configEntriesForType:PSE_LT_NET];
+ while (*dst) {
+ strlcpy(*dst, str, MAXPATHLEN);
+ dst++;
+ }
+ }
+
+}
+
+@end
diff --git a/macosx/Source/RecentItemsMenu.h b/macosx/Source/RecentItemsMenu.h
new file mode 100644
index 00000000..d5bb2e07
--- /dev/null
+++ b/macosx/Source/RecentItemsMenu.h
@@ -0,0 +1,22 @@
+//
+// RecentItemsMenu.h
+// Pcsxr
+//
+// Created by Nicolas Pepin-Perreault on 12-12-16.
+//
+//
+
+#import <Cocoa/Cocoa.h>
+
+@class PcsxrController;
+@interface RecentItemsMenu : NSMenu
+
+@property (weak) IBOutlet PcsxrController *pcsxr;
+
+- (IBAction)clearRecentDocuments:(id)sender;
+- (void)addRecentItem:(NSURL*)documentURL;
+- (NSMenuItem*)newMenuItem:(NSURL*)documentURL;
+- (void)openRecentItem:(NSMenuItem*)sender;
+- (void)addMenuItem:(NSMenuItem*)item;
+
+@end
diff --git a/macosx/Source/RecentItemsMenu.m b/macosx/Source/RecentItemsMenu.m
new file mode 100644
index 00000000..926bd270
--- /dev/null
+++ b/macosx/Source/RecentItemsMenu.m
@@ -0,0 +1,114 @@
+//
+// RecentItemsMenu.m
+// Pcsxr
+//
+// Created by Nicolas Pepin-Perreault on 12-12-16.
+//
+//
+
+#import "RecentItemsMenu.h"
+#import "PcsxrController.h"
+
+@implementation RecentItemsMenu
+
+@synthesize pcsxr;
+
+// Initialization
+- (void)awakeFromNib
+{
+ [self setAutoenablesItems:YES];
+
+ // Populate the menu
+ NSArray* recentDocuments = [[NSDocumentController sharedDocumentController] recentDocumentURLs];
+ NSInteger index = 0;
+ for (NSURL* url in recentDocuments) {
+ NSMenuItem *tempItem = [self newMenuItem:url];
+ [self addMenuItem:tempItem atIndex:index];
+ index++;
+ }
+}
+
+- (void)addRecentItem:(NSURL*)documentURL
+{
+ [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:documentURL];
+
+ NSMenuItem* item = [self findMenuItemByURL:documentURL];
+ if (item != nil) {
+ [self removeItem:item];
+ [self insertItem:item atIndex:0];
+ } else {
+ NSMenuItem *newitem = [self newMenuItem:documentURL];
+ [self addMenuItem:newitem];
+ }
+}
+
+- (void)addMenuItem:(NSMenuItem*)item
+{
+ [self addMenuItem:item atIndex:0];
+
+ // Prevent menu from overflowing; the -2 accounts for the "Clear..." and the separator items
+ NSInteger maxNumItems = [[NSDocumentController sharedDocumentController] maximumRecentDocumentCount];
+ if (([self numberOfItems] - 2) > maxNumItems) {
+ [self removeItemAtIndex:maxNumItems];
+ }
+}
+
+- (NSMenuItem*)findMenuItemByURL:(NSURL*)url
+{
+ for(NSMenuItem* item in [self itemArray]) {
+ if([[item representedObject] isEqual:url]) {
+ return item;
+ }
+ }
+
+ return nil;
+}
+
+- (void)addMenuItem:(NSMenuItem*)item atIndex:(NSInteger)index
+{
+ [self insertItem:item atIndex:index]; // insert at the top
+}
+
+- (NSMenuItem*)newMenuItem:(NSURL*)documentURL
+{
+ NSString *lastName = nil;
+ [documentURL getResourceValue:&lastName forKey:NSURLLocalizedNameKey error:NULL];
+ if (!lastName) {
+ lastName = [documentURL lastPathComponent];
+ }
+
+ NSMenuItem *newItem = [[NSMenuItem alloc] initWithTitle:lastName action:@selector(openRecentItem:) keyEquivalent:@""];
+ [newItem setRepresentedObject:documentURL];
+ [newItem setTarget:self];
+
+ return newItem;
+}
+
+- (void)openRecentItem:(NSMenuItem*)sender
+{
+ NSURL* url = [sender representedObject];
+ [self addRecentItem:url];
+ [pcsxr runURL:url];
+}
+
+- (IBAction)clearRecentDocuments:(id)sender
+{
+ [self removeDocumentItems];
+ [[NSDocumentController sharedDocumentController] clearRecentDocuments:sender];
+}
+
+// Document items are menu items with tag 0
+- (void)removeDocumentItems
+{
+ NSMutableArray *removeItemsArray = [[NSMutableArray alloc] initWithCapacity:10];
+ for (NSMenuItem* item in [self itemArray]) {
+ if([item tag] == 0) {
+ [removeItemsArray addObject:item];
+ }
+ }
+ for (NSMenuItem *item in removeItemsArray) {
+ [self removeItem:item];
+ }
+}
+
+@end
diff --git a/macosx/Source/config.h b/macosx/Source/config.h
new file mode 100644
index 00000000..3be13f38
--- /dev/null
+++ b/macosx/Source/config.h
@@ -0,0 +1,32 @@
+//
+// Copyright (c) 2008, Wei Mingzhi. All rights reserved.
+//
+// Use, redistribution and modification of this code is unrestricted as long as this
+// notice is preserved.
+//
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#ifndef MAXPATHLEN
+//match PATH_MAX in <sys/param.h>
+#define MAXPATHLEN 1024
+#endif
+
+#ifndef PACKAGE_VERSION
+#define PACKAGE_VERSION "1.9"
+#endif
+
+#ifndef PREFIX
+#define PREFIX "./"
+#endif
+
+#ifndef inline
+#ifdef _DEBUG
+#define inline /* */
+#else
+#define inline __inline__
+#endif
+#endif
+
+#endif
diff --git a/macosx/Source/hotkeys.h b/macosx/Source/hotkeys.h
new file mode 100644
index 00000000..592ea0b7
--- /dev/null
+++ b/macosx/Source/hotkeys.h
@@ -0,0 +1,15 @@
+//
+// hotkeys.h
+// Pcsxr
+//
+// Created by Nicolas Pepin-Perreault on 12-12-12.
+//
+//
+
+#ifndef Pcsxr_hotkeys_h
+#define Pcsxr_hotkeys_h
+
+void attachHotkeys();
+void detachHotkeys();
+
+#endif
diff --git a/macosx/Source/hotkeys.m b/macosx/Source/hotkeys.m
new file mode 100644
index 00000000..70e3ae15
--- /dev/null
+++ b/macosx/Source/hotkeys.m
@@ -0,0 +1,141 @@
+//
+// hotkeys.m
+// Pcsxr
+//
+// Created by Nicolas Pepin-Perreault on 12-12-12.
+//
+//
+
+#import <Cocoa/Cocoa.h>
+#import "hotkeys.h"
+#import "EmuThread.h"
+#include "plugins.h"
+#include "ExtendedKeys.h"
+#import "PcsxrController.h"
+
+#define HK_MAX_STATE 10
+static id monitor;
+static id gpuMonitor;
+static int currentState = 0;
+static NSMutableDictionary *hotkeys = nil;
+
+typedef NS_ENUM(int, PCSXR_HotKey) {
+ HK_FAST_FORWARD,
+ HK_SAVE_STATE,
+ HK_LOAD_STATE,
+ HK_NEXT_STATE,
+ HK_PREV_STATE,
+ HK_FRAME_LIMIT
+};
+
+void nextState() {
+ currentState++;
+ if(currentState == HK_MAX_STATE) {
+ currentState = 0;
+ }
+}
+
+void prevState() {
+ currentState--;
+ if(currentState < 0) {
+ currentState = HK_MAX_STATE-1;
+ }
+}
+
+BOOL handleHotkey(NSString* keyCode) {
+ if([EmuThread active]) { // Don't catch hotkeys if there is no emulation
+ NSNumber *ident = hotkeys[keyCode];
+
+ if(ident != nil) {
+ switch([ident intValue]) {
+ case HK_FAST_FORWARD:
+ // We ignore FastForward requests if the emulation is paused
+ if(![EmuThread isPaused]) {
+ GPU_keypressed(GPU_FAST_FORWARD);
+ }
+ break;
+
+ case HK_FRAME_LIMIT:
+ // Ignore FrameLimit requests if paused
+ if(![EmuThread isPaused]) {
+ GPU_keypressed(GPU_FRAME_LIMIT);
+ }
+ break;
+
+ case HK_SAVE_STATE:
+ [PcsxrController saveState:currentState];
+ break;
+
+ case HK_LOAD_STATE:
+ [PcsxrController loadState:currentState];
+ break;
+
+ case HK_NEXT_STATE:
+ nextState();
+ GPU_displayText((char*)[[NSString stringWithFormat:@"State Slot: %d", currentState] UTF8String]);
+ break;
+
+ case HK_PREV_STATE:
+ prevState();
+ GPU_displayText((char*)[[NSString stringWithFormat:@"State Slot: %d", currentState] UTF8String]);
+ break;
+
+ default:
+ NSLog(@"Invalid hotkey identifier %i.", [ident intValue]);
+ break;
+ }
+
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+void setupHotkey(PCSXR_HotKey hk, NSString *label, NSDictionary *binding) {
+ if(binding != nil)
+ hotkeys[binding[@"keyCode"]] = @(hk);
+}
+
+void setupHotkeys() {
+ NSDictionary *bindings = [[NSUserDefaults standardUserDefaults] objectForKey:@"HotkeyBindings"];
+ hotkeys = [[NSMutableDictionary alloc] initWithCapacity:[bindings count]];
+
+ setupHotkey(HK_FAST_FORWARD, @"FastForward", bindings[@"FastForward"]);
+ setupHotkey(HK_SAVE_STATE, @"SaveState", bindings[@"SaveState"]);
+ setupHotkey(HK_LOAD_STATE, @"LoadState", bindings[@"LoadState"]);
+ setupHotkey(HK_NEXT_STATE, @"NextState", bindings[@"NextState"]);
+ setupHotkey(HK_PREV_STATE, @"PrevState", bindings[@"PrevState"]);
+ setupHotkey(HK_FRAME_LIMIT, @"FrameLimit", bindings[@"FrameLimit"]);
+
+ currentState = 0;
+}
+
+void attachHotkeys() {
+ // Configurable hotkeys
+ NSEvent* (^handler)(NSEvent*) = ^(NSEvent *event) {
+ if(handleHotkey([NSString stringWithFormat:@"%d", [event keyCode]])) {
+ return (NSEvent*)nil; // handled
+ }
+
+ // Not handled
+ return event;
+ };
+ setupHotkeys();
+ monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyUpMask handler:handler];
+
+ // GPU key presses
+ NSEvent* (^gpuKeypress)(NSEvent*) = ^(NSEvent *event) {
+ GPU_keypressed([event keyCode]);
+ return (NSEvent*)nil;
+ };
+ gpuMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:(NSKeyUpMask | NSControlKeyMask) handler:gpuKeypress];
+}
+
+void detachHotkeys() {
+ hotkeys = nil;
+ [NSEvent removeMonitor:monitor];
+ [NSEvent removeMonitor:gpuMonitor];
+ monitor = nil;
+ gpuMonitor = nil;
+}
diff --git a/macosx/Source/main.m b/macosx/Source/main.m
new file mode 100644
index 00000000..33f74908
--- /dev/null
+++ b/macosx/Source/main.m
@@ -0,0 +1,311 @@
+//
+// main.m
+//
+// Created by Gil Pedersen on Fri Jun 06 2003.
+// Copyright (c) 2003 SoftWorkz. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "EmuThread.h"
+#import "PcsxrController.h"
+#import "ConfigurationController.h"
+#include <dlfcn.h>
+#include <unistd.h>
+#include "psxcommon.h"
+#include "sio.h"
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#import "hotkeys.h"
+
+#ifndef NSFoundationVersionNumber10_8_4
+#define NSFoundationVersionNumber10_8_4 945.18
+#endif
+
+static inline void RunOnMainThreadSync(dispatch_block_t block)
+{
+ if ([NSThread isMainThread]) {
+ block();
+ } else {
+ dispatch_sync(dispatch_get_main_queue(), block);
+ }
+}
+
+static BOOL sysInited = NO;
+//#define EMU_LOG
+static IOPMAssertionID powerAssertion = kIOPMNullAssertionID;
+
+void PADhandleKey(int key);
+
+static inline BOOL IsRootCwd()
+{
+ char buf[MAXPATHLEN];
+ char *cwd = getcwd(buf, sizeof(buf));
+ return (cwd && (strcmp(cwd, "/") == 0));
+}
+
+static inline BOOL IsTenPointNineOrLater()
+{
+ int curFoundNum = floor(NSFoundationVersionNumber), tenPointEightFoundNum = floor(NSFoundationVersionNumber10_8_4);
+ return curFoundNum > tenPointEightFoundNum;
+}
+
+static BOOL IsFinderLaunch(const int argc, const char **argv)
+{
+ BOOL isNewerOS = IsTenPointNineOrLater();
+ /* -psn_XXX is passed if we are launched from Finder in 10.8 and earlier */
+ if ( (!isNewerOS) && (argc >= 2) && (strncmp(argv[1], "-psn", 4) == 0) ) {
+ return YES;
+ } else if ((isNewerOS) && (argc == 1) && IsRootCwd()) {
+ /* we might still be launched from the Finder; on 10.9+, you might not
+ get the -psn command line anymore. Check version, if there's no
+ command line, and if our current working directory is "/". */
+ return YES;
+ }
+ return NO; /* not a Finder launch. */
+}
+
+int main(int argc, const char *argv[])
+{
+ if (argc >= 2 && IsFinderLaunch(argc, argv)) {
+ wasFinderLaunch = YES;
+ char parentdir[MAXPATHLEN];
+ char *c;
+
+ strlcpy(parentdir, argv[0], sizeof(parentdir));
+ c = (char*)parentdir;
+
+ while (*c != '\0') /* go to end */
+ c++;
+
+ while (*c != '/') /* back up to parent */
+ c--;
+
+ *c++ = '\0'; /* cut off last part (binary name) */
+
+ assert(chdir(parentdir) == 0); /* chdir to the binary app's parent */
+ assert(chdir("../../../") == 0); /* chdir to the .app's parent */
+ } else {
+ for (int i = 1; i < argc; i++) {
+ //All the other option will be handled in the app delegate's awakeFromNib
+ if (!strcasecmp("--help", argv[i])) {
+ fprintf(stdout, "%s\n", argv[0]);
+ ShowHelpAndExit(stdout, EXIT_SUCCESS);
+ }
+ }
+ }
+
+ strcpy(Config.BiosDir, "Bios/");
+ strcpy(Config.PatchesDir, "Patches/");
+
+ // Setup the X11 window
+ if (getenv("DISPLAY") == NULL)
+ setenv("DISPLAY", ":0.0", 0); // Default to first local display
+
+ return NSApplicationMain(argc, argv);
+}
+
+int SysInit()
+{
+ if (!sysInited) {
+#ifdef EMU_LOG
+#ifndef LOG_STDOUT
+ NSFileManager *manager = [NSFileManager defaultManager];
+ NSURL *supportURL = [manager URLForDirectory:NSLibraryDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
+ NSURL *logFolderURL = [supportURL URLByAppendingPathComponent:@"Logs/PCSXR"];
+ if (![logFolderURL checkResourceIsReachableAndReturnError:NULL])
+ [manager createDirectoryAtPath:[logFolderURL path] withIntermediateDirectories:YES attributes:nil error:NULL];
+ //We use the log extension so that OS X's console app can open it by default.
+ NSURL *logFileURL = [logFolderURL URLByAppendingPathComponent:@"PCSX-R emuLog.log"];
+
+ emuLog = fopen([[logFileURL path] fileSystemRepresentation], "wb");
+#else
+ emuLog = stdout;
+#endif
+ setvbuf(emuLog, NULL, _IONBF, 0);
+#endif
+
+ if (EmuInit() != 0)
+ return -1;
+
+ sysInited = YES;
+ }
+
+ if (LoadPlugins() == -1) {
+ return -1;
+ }
+
+ LoadMcds(Config.Mcd1, Config.Mcd2);
+
+ IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, CFSTR("PSX Emu Running"), &powerAssertion);
+ if (success != kIOReturnSuccess) {
+ NSLog(@"Unable to stop sleep, error code %d", success);
+ }
+
+ attachHotkeys();
+
+ return 0;
+}
+
+void SysReset()
+{
+ [EmuThread resetNow];
+ //EmuReset();
+}
+
+static void AddStringToLogList(NSString *themsg)
+{
+ static NSMutableString *theStr;
+ static dispatch_once_t onceToken;
+ NSRange newlineRange, fullLineRange;
+ dispatch_once(&onceToken, ^{
+ theStr = [[NSMutableString alloc] init];
+ });
+ [theStr appendString:themsg];
+ while ((newlineRange = [theStr rangeOfString:@"\n"]).location != NSNotFound) {
+ newlineRange = [theStr rangeOfComposedCharacterSequencesForRange:newlineRange];
+ NSString *tmpStr = [theStr substringToIndex:newlineRange.location];
+ if (tmpStr && ![tmpStr isEqualToString:@""]) {
+ NSLog(@"%@", tmpStr);
+ }
+ fullLineRange.location = 0;
+ fullLineRange.length = newlineRange.location + newlineRange.length;
+ fullLineRange = [theStr rangeOfComposedCharacterSequencesForRange:fullLineRange];
+ [theStr deleteCharactersInRange:fullLineRange];
+ }
+}
+
+void SysPrintf(const char *fmt, ...)
+{
+ va_list list;
+ NSString *msg;
+
+ va_start(list, fmt);
+ msg = [[NSString alloc] initWithFormat:@(fmt) arguments:list];
+ va_end(list);
+
+ RunOnMainThreadSync(^{
+ if (Config.PsxOut)
+ AddStringToLogList(msg);
+#ifdef EMU_LOG
+#ifndef LOG_STDOUT
+ if (emuLog)
+ fprintf(emuLog, "%s", [msg UTF8String]);
+#endif
+#endif
+ });
+}
+
+void SysMessage(const char *fmt, ...)
+{
+ va_list list;
+ va_start(list, fmt);
+ NSString *msg = [[NSString alloc] initWithFormat:@(fmt) arguments:list];
+ va_end(list);
+
+ NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey: msg};
+
+ RunOnMainThreadSync(^{
+ [NSApp presentError:[NSError errorWithDomain:@"Unknown Domain" code:-1 userInfo:userInfo]];
+ });
+}
+
+void *SysLoadLibrary(const char *lib)
+{
+ NSBundle *bundle = [[NSBundle alloc] initWithPath:[[NSFileManager defaultManager] stringWithFileSystemRepresentation:lib length:strlen(lib)]];
+ if (bundle != nil) {
+ return dlopen([[bundle executablePath] fileSystemRepresentation], RTLD_LAZY /*RTLD_NOW*/);
+ }
+ return dlopen(lib, RTLD_LAZY);
+}
+
+void *SysLoadSym(void *lib, const char *sym)
+{
+ return dlsym(lib, sym);
+}
+
+const char *SysLibError()
+{
+#ifdef DEBUG
+ const char *theErr = dlerror();
+ if (theErr) {
+ NSLog(@"dlerror(): %s.", theErr);
+ }
+ return theErr;
+#else
+ return dlerror();
+#endif
+}
+
+void SysCloseLibrary(void *lib) {
+ // We do not close libraries due to how Objective C code misbehaves if unloaded,
+ // particularly constant NSStrings.
+ //dlclose(lib);
+}
+
+// Called periodically from the emu thread
+void SysUpdate()
+{
+#if 0
+ PADhandleKey(PAD1_keypressed() & 0xffffffff);
+ PADhandleKey(PAD2_keypressed() & 0xffffffff);
+#else
+ PAD1_keypressed();
+ PAD2_keypressed();
+#endif
+ [emuThread handleEvents];
+}
+
+// Returns to the Gui
+void SysRunGui()
+{
+ if (powerAssertion != kIOPMNullAssertionID) {
+ IOPMAssertionRelease(powerAssertion);
+ powerAssertion = kIOPMNullAssertionID;
+ }
+}
+
+// Close mem and plugins
+void SysClose()
+{
+ EmuShutdown();
+ ReleasePlugins();
+
+ if (powerAssertion != kIOPMNullAssertionID) {
+ IOPMAssertionRelease(powerAssertion);
+ powerAssertion = kIOPMNullAssertionID;
+ }
+
+ if (emuLog != NULL) {
+ fclose(emuLog);
+ emuLog = NULL;
+ }
+
+ sysInited = NO;
+ detachHotkeys();
+
+ if (((PcsxrController *)[NSApp delegate]).endAtEmuClose) {
+ [NSApp stop:nil];
+ }
+
+ //Tell the memory card manager that the memory cards changed.
+ //The number three tells the mem card manager to update both cards 1 and 2.
+ [[NSNotificationCenter defaultCenter] postNotificationName:memChangeNotifier object:nil userInfo:@{memCardChangeNumberKey: @3}];
+
+ //Clear the log list
+ RunOnMainThreadSync(^{
+ if (Config.PsxOut)
+ AddStringToLogList(@"\n");
+ });
+}
+
+void OnFile_Exit()
+{
+ SysClose();
+ [NSApp stop:nil];
+}
+
+char* Pcsxr_locale_text(char* toloc)
+{
+ NSString *origString = @(toloc), *transString = nil;
+ transString = [[NSBundle mainBundle] localizedStringForKey:origString value:@"" table:nil];
+ return (char*)[transString UTF8String];
+}