misc/physfs/src/platform_macosx.c
branchphysfslayer
changeset 7768 13e2037ebc79
equal deleted inserted replaced
7767:d1ea9b3f543e 7768:13e2037ebc79
       
     1 /*
       
     2  * Mac OS X support routines for PhysicsFS.
       
     3  *
       
     4  * Please see the file LICENSE.txt in the source's root directory.
       
     5  *
       
     6  *  This file written by Ryan C. Gordon.
       
     7  */
       
     8 
       
     9 #define __PHYSICSFS_INTERNAL__
       
    10 #include "physfs_platforms.h"
       
    11 
       
    12 #ifdef PHYSFS_PLATFORM_MACOSX
       
    13 
       
    14 #include <CoreFoundation/CoreFoundation.h>
       
    15 
       
    16 #if !defined(PHYSFS_NO_CDROM_SUPPORT)
       
    17 #include <Carbon/Carbon.h>  /* !!! FIXME */
       
    18 #include <IOKit/storage/IOMedia.h>
       
    19 #include <IOKit/storage/IOCDMedia.h>
       
    20 #include <IOKit/storage/IODVDMedia.h>
       
    21 #include <sys/mount.h>
       
    22 #endif
       
    23 
       
    24 /* Seems to get defined in some system header... */
       
    25 #ifdef Free
       
    26 #undef Free
       
    27 #endif
       
    28 
       
    29 #include "physfs_internal.h"
       
    30 
       
    31 
       
    32 /* Wrap PHYSFS_Allocator in a CFAllocator... */
       
    33 static CFAllocatorRef cfallocator = NULL;
       
    34 
       
    35 static CFStringRef cfallocDesc(const void *info)
       
    36 {
       
    37     return CFStringCreateWithCString(cfallocator, "PhysicsFS",
       
    38                                      kCFStringEncodingASCII);
       
    39 } /* cfallocDesc */
       
    40 
       
    41 
       
    42 static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
       
    43 {
       
    44     return allocator.Malloc(allocSize);
       
    45 } /* cfallocMalloc */
       
    46 
       
    47 
       
    48 static void cfallocFree(void *ptr, void *info)
       
    49 {
       
    50     allocator.Free(ptr);
       
    51 } /* cfallocFree */
       
    52 
       
    53 
       
    54 static void *cfallocRealloc(void *ptr, CFIndex newsize,
       
    55                             CFOptionFlags hint, void *info)
       
    56 {
       
    57     if ((ptr == NULL) || (newsize <= 0))
       
    58         return NULL;  /* ADC docs say you should always return NULL here. */
       
    59     return allocator.Realloc(ptr, newsize);
       
    60 } /* cfallocRealloc */
       
    61 
       
    62 
       
    63 int __PHYSFS_platformInit(void)
       
    64 {
       
    65     /* set up a CFAllocator, so Carbon can use the physfs allocator, too. */
       
    66     CFAllocatorContext ctx;
       
    67     memset(&ctx, '\0', sizeof (ctx));
       
    68     ctx.copyDescription = cfallocDesc;
       
    69     ctx.allocate = cfallocMalloc;
       
    70     ctx.reallocate = cfallocRealloc;
       
    71     ctx.deallocate = cfallocFree;
       
    72     cfallocator = CFAllocatorCreate(kCFAllocatorUseContext, &ctx);
       
    73     BAIL_IF_MACRO(!cfallocator, PHYSFS_ERR_OUT_OF_MEMORY, 0);
       
    74     return 1;  /* success. */
       
    75 } /* __PHYSFS_platformInit */
       
    76 
       
    77 
       
    78 int __PHYSFS_platformDeinit(void)
       
    79 {
       
    80     CFRelease(cfallocator);
       
    81     cfallocator = NULL;
       
    82     return 1;  /* always succeed. */
       
    83 } /* __PHYSFS_platformDeinit */
       
    84 
       
    85 
       
    86 
       
    87 /* CD-ROM detection code... */
       
    88 
       
    89 /*
       
    90  * Code based on sample from Apple Developer Connection:
       
    91  *  http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm
       
    92  */
       
    93 
       
    94 #if !defined(PHYSFS_NO_CDROM_SUPPORT)
       
    95 
       
    96 static int darwinIsWholeMedia(io_service_t service)
       
    97 {
       
    98     int retval = 0;
       
    99     CFTypeRef wholeMedia;
       
   100 
       
   101     if (!IOObjectConformsTo(service, kIOMediaClass))
       
   102         return 0;
       
   103         
       
   104     wholeMedia = IORegistryEntryCreateCFProperty(service,
       
   105                                                  CFSTR(kIOMediaWholeKey),
       
   106                                                  cfallocator, 0);
       
   107     if (wholeMedia == NULL)
       
   108         return 0;
       
   109 
       
   110     retval = CFBooleanGetValue(wholeMedia);
       
   111     CFRelease(wholeMedia);
       
   112 
       
   113     return retval;
       
   114 } /* darwinIsWholeMedia */
       
   115 
       
   116 
       
   117 static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort)
       
   118 {
       
   119     int retval = 0;
       
   120     CFMutableDictionaryRef matchingDict;
       
   121     kern_return_t rc;
       
   122     io_iterator_t iter;
       
   123     io_service_t service;
       
   124 
       
   125     if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL)
       
   126         return 0;
       
   127 
       
   128     rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter);
       
   129     if ((rc != KERN_SUCCESS) || (!iter))
       
   130         return 0;
       
   131 
       
   132     service = IOIteratorNext(iter);
       
   133     IOObjectRelease(iter);
       
   134     if (!service)
       
   135         return 0;
       
   136 
       
   137     rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
       
   138              kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
       
   139     
       
   140     if (!iter)
       
   141         return 0;
       
   142 
       
   143     if (rc != KERN_SUCCESS)
       
   144     {
       
   145         IOObjectRelease(iter);
       
   146         return 0;
       
   147     } /* if */
       
   148 
       
   149     IOObjectRetain(service);  /* add an extra object reference... */
       
   150 
       
   151     do
       
   152     {
       
   153         if (darwinIsWholeMedia(service))
       
   154         {
       
   155             if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
       
   156                  (IOObjectConformsTo(service, kIODVDMediaClass)) )
       
   157             {
       
   158                 retval = 1;
       
   159             } /* if */
       
   160         } /* if */
       
   161         IOObjectRelease(service);
       
   162     } while ((service = IOIteratorNext(iter)) && (!retval));
       
   163                 
       
   164     IOObjectRelease(iter);
       
   165     IOObjectRelease(service);
       
   166 
       
   167     return retval;
       
   168 } /* darwinIsMountedDisc */
       
   169 
       
   170 #endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
       
   171 
       
   172 
       
   173 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
       
   174 {
       
   175 #if !defined(PHYSFS_NO_CDROM_SUPPORT)
       
   176     const char *devPrefix = "/dev/";
       
   177     const int prefixLen = strlen(devPrefix);
       
   178     mach_port_t masterPort = 0;
       
   179     struct statfs *mntbufp;
       
   180     int i, mounts;
       
   181 
       
   182     if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
       
   183         BAIL_MACRO(PHYSFS_ERR_OS_ERROR, ) /*return void*/;
       
   184 
       
   185     mounts = getmntinfo(&mntbufp, MNT_WAIT);  /* NOT THREAD SAFE! */
       
   186     for (i = 0; i < mounts; i++)
       
   187     {
       
   188         char *dev = mntbufp[i].f_mntfromname;
       
   189         char *mnt = mntbufp[i].f_mntonname;
       
   190         if (strncmp(dev, devPrefix, prefixLen) != 0)  /* a virtual device? */
       
   191             continue;
       
   192 
       
   193         dev += prefixLen;
       
   194         if (darwinIsMountedDisc(dev, masterPort))
       
   195             cb(data, mnt);
       
   196     } /* for */
       
   197 #endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */
       
   198 } /* __PHYSFS_platformDetectAvailableCDs */
       
   199 
       
   200 
       
   201 static char *convertCFString(CFStringRef cfstr)
       
   202 {
       
   203     CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
       
   204                                                     kCFStringEncodingUTF8) + 1;
       
   205     char *retval = (char *) allocator.Malloc(len);
       
   206     BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   207 
       
   208     if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8))
       
   209     {
       
   210         /* shrink overallocated buffer if possible... */
       
   211         CFIndex newlen = strlen(retval) + 1;
       
   212         if (newlen < len)
       
   213         {
       
   214             void *ptr = allocator.Realloc(retval, newlen);
       
   215             if (ptr != NULL)
       
   216                 retval = (char *) ptr;
       
   217         } /* if */
       
   218     } /* if */
       
   219 
       
   220     else  /* probably shouldn't fail, but just in case... */
       
   221     {
       
   222         allocator.Free(retval);
       
   223         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   224     } /* else */
       
   225 
       
   226     return retval;
       
   227 } /* convertCFString */
       
   228 
       
   229 
       
   230 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
       
   231 {
       
   232     CFURLRef cfurl = NULL;
       
   233     CFStringRef cfstr = NULL;
       
   234     CFMutableStringRef cfmutstr = NULL;
       
   235     char *retval = NULL;
       
   236 
       
   237     cfurl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
       
   238     BAIL_IF_MACRO(cfurl == NULL, PHYSFS_ERR_OS_ERROR, NULL);
       
   239     cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
       
   240     CFRelease(cfurl);
       
   241     BAIL_IF_MACRO(!cfstr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   242     cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr);
       
   243     CFRelease(cfstr);
       
   244     BAIL_IF_MACRO(!cfmutstr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   245     CFStringAppendCString(cfmutstr, "/", kCFStringEncodingUTF8);
       
   246     retval = convertCFString(cfmutstr);
       
   247     CFRelease(cfmutstr);
       
   248 
       
   249     return retval;  /* whew. */
       
   250 } /* __PHYSFS_platformCalcBaseDir */
       
   251 
       
   252 
       
   253 char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
       
   254 {
       
   255     /* !!! FIXME: there's a real API to determine this */
       
   256     const char *userdir = __PHYSFS_getUserDir();
       
   257     const char *append = "Library/Application Support/";
       
   258     const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 2;
       
   259     char *retval = allocator.Malloc(len);
       
   260     BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   261     snprintf(retval, len, "%s%s%s/", userdir, append, app);
       
   262     return retval;
       
   263 } /* __PHYSFS_platformCalcPrefDir */
       
   264 
       
   265 
       
   266 /* Platform allocator uses default CFAllocator at PHYSFS_init() time. */
       
   267 
       
   268 static CFAllocatorRef cfallocdef = NULL;
       
   269 
       
   270 static int macosxAllocatorInit(void)
       
   271 {
       
   272     int retval = 0;
       
   273     cfallocdef = CFAllocatorGetDefault();
       
   274     retval = (cfallocdef != NULL);
       
   275     if (retval)
       
   276         CFRetain(cfallocdef);
       
   277     return retval;
       
   278 } /* macosxAllocatorInit */
       
   279 
       
   280 
       
   281 static void macosxAllocatorDeinit(void)
       
   282 {
       
   283     if (cfallocdef != NULL)
       
   284     {
       
   285         CFRelease(cfallocdef);
       
   286         cfallocdef = NULL;
       
   287     } /* if */
       
   288 } /* macosxAllocatorDeinit */
       
   289 
       
   290 
       
   291 static void *macosxAllocatorMalloc(PHYSFS_uint64 s)
       
   292 {
       
   293     if (!__PHYSFS_ui64FitsAddressSpace(s))
       
   294         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   295     return CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0);
       
   296 } /* macosxAllocatorMalloc */
       
   297 
       
   298 
       
   299 static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
       
   300 {
       
   301     if (!__PHYSFS_ui64FitsAddressSpace(s))
       
   302         BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
       
   303     return CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0);
       
   304 } /* macosxAllocatorRealloc */
       
   305 
       
   306 
       
   307 static void macosxAllocatorFree(void *ptr)
       
   308 {
       
   309     CFAllocatorDeallocate(cfallocdef, ptr);
       
   310 } /* macosxAllocatorFree */
       
   311 
       
   312 
       
   313 int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
       
   314 {
       
   315     allocator.Init = macosxAllocatorInit;
       
   316     allocator.Deinit = macosxAllocatorDeinit;
       
   317     allocator.Malloc = macosxAllocatorMalloc;
       
   318     allocator.Realloc = macosxAllocatorRealloc;
       
   319     allocator.Free = macosxAllocatorFree;
       
   320     return 1;  /* return non-zero: we're supplying custom allocator. */
       
   321 } /* __PHYSFS_platformSetDefaultAllocator */
       
   322 
       
   323 #endif /* PHYSFS_PLATFORM_MACOSX */
       
   324 
       
   325 /* end of macosx.c ... */
       
   326