// // PcsxrPlugin.m // Pcsxr // // Created by Gil Pedersen on Fri Oct 03 2003. // Copyright (c) 2003 __MyCompanyName__. All rights reserved. // #import #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, copy) NSString *name; @property (strong) NSDate *modDate; @property (copy) 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 { 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; } - (instancetype)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] #define PluginSymbolNameConfigure(type) PluginSymbolName(type, @"configure") #define PluginSymbolNameAbout(type) PluginSymbolName(type, @"about") - (void)runCommandNamed:(NSString*)arg { long (*func)(void); func = SysLoadSym(pluginRef, [arg cStringUsingEncoding:NSASCIIStringEncoding]); if (SysLibError() == NULL) { func(); } else { NSBeep(); } } - (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 } - (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 { NSString *aboutSym = PluginSymbolNameAbout(aType); //NSArray *arg = @[aboutSym, @0]; // detach a new thread dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self runCommandNamed:aboutSym]; }); } - (void)configureAs:(int)aType { NSString *configSym = PluginSymbolNameConfigure(aType); //NSArray *arg = @[configSym, @1]; // detach a new thread dispatch_async(dispatch_get_global_queue(0, 0), ^{ [self runCommandNamed:configSym]; }); } - (NSString *)displayVersion { if (version == -1) return @""; return [NSString stringWithFormat:@"v%ld.%ld.%ld", version>>16,(version>>8)&0xff,version&0xff]; } - (NSUInteger)hash { return [fullPlugPath 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