2 * Mac OS X support routines for PhysicsFS.
4 * Please see the file LICENSE.txt in the source's root directory.
6 * This file written by Ryan C. Gordon.
9 #define __PHYSICSFS_INTERNAL__
10 #include "physfs_platforms.h"
12 #ifdef PHYSFS_PLATFORM_MACOSX
14 #include <Carbon/Carbon.h>
15 #include <IOKit/storage/IOMedia.h>
16 #include <IOKit/storage/IOCDMedia.h>
17 #include <IOKit/storage/IODVDMedia.h>
18 #include <sys/mount.h>
20 /* Seems to get defined in some system header... */
25 #include "physfs_internal.h"
28 /* Wrap PHYSFS_Allocator in a CFAllocator... */
29 static CFAllocatorRef cfallocator = NULL;
31 CFStringRef cfallocDesc(const void *info)
33 return(CFStringCreateWithCString(cfallocator, "PhysicsFS",
34 kCFStringEncodingASCII));
38 static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
40 return allocator.Malloc(allocSize);
44 static void cfallocFree(void *ptr, void *info)
50 static void *cfallocRealloc(void *ptr, CFIndex newsize,
51 CFOptionFlags hint, void *info)
53 if ((ptr == NULL) || (newsize <= 0))
54 return NULL; /* ADC docs say you should always return NULL here. */
55 return allocator.Realloc(ptr, newsize);
56 } /* cfallocRealloc */
59 int __PHYSFS_platformInit(void)
61 /* set up a CFAllocator, so Carbon can use the physfs allocator, too. */
62 CFAllocatorContext ctx;
63 memset(&ctx, '\0', sizeof (ctx));
64 ctx.copyDescription = cfallocDesc;
65 ctx.allocate = cfallocMalloc;
66 ctx.reallocate = cfallocRealloc;
67 ctx.deallocate = cfallocFree;
68 cfallocator = CFAllocatorCreate(kCFAllocatorUseContext, &ctx);
69 BAIL_IF_MACRO(cfallocator == NULL, ERR_OUT_OF_MEMORY, 0);
70 return(1); /* success. */
71 } /* __PHYSFS_platformInit */
74 int __PHYSFS_platformDeinit(void)
76 CFRelease(cfallocator);
78 return(1); /* always succeed. */
79 } /* __PHYSFS_platformDeinit */
82 /* CD-ROM detection code... */
85 * Code based on sample from Apple Developer Connection:
86 * http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm
89 static int darwinIsWholeMedia(io_service_t service)
94 if (!IOObjectConformsTo(service, kIOMediaClass))
97 wholeMedia = IORegistryEntryCreateCFProperty(service,
98 CFSTR(kIOMediaWholeKey),
100 if (wholeMedia == NULL)
103 retval = CFBooleanGetValue(wholeMedia);
104 CFRelease(wholeMedia);
107 } /* darwinIsWholeMedia */
110 static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort)
113 CFMutableDictionaryRef matchingDict;
116 io_service_t service;
118 if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL)
121 rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter);
122 if ((rc != KERN_SUCCESS) || (!iter))
125 service = IOIteratorNext(iter);
126 IOObjectRelease(iter);
130 rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
131 kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
136 if (rc != KERN_SUCCESS)
138 IOObjectRelease(iter);
142 IOObjectRetain(service); /* add an extra object reference... */
146 if (darwinIsWholeMedia(service))
148 if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
149 (IOObjectConformsTo(service, kIODVDMediaClass)) )
154 IOObjectRelease(service);
155 } while ((service = IOIteratorNext(iter)) && (!retval));
157 IOObjectRelease(iter);
158 IOObjectRelease(service);
161 } /* darwinIsMountedDisc */
164 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
166 const char *devPrefix = "/dev/";
167 const int prefixLen = strlen(devPrefix);
168 mach_port_t masterPort = 0;
169 struct statfs *mntbufp;
172 if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
173 BAIL_MACRO(ERR_OS_ERROR, ) /*return void*/;
175 mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */
176 for (i = 0; i < mounts; i++)
178 char *dev = mntbufp[i].f_mntfromname;
179 char *mnt = mntbufp[i].f_mntonname;
180 if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */
184 if (darwinIsMountedDisc(dev, masterPort))
187 } /* __PHYSFS_platformDetectAvailableCDs */
190 static char *convertCFString(CFStringRef cfstr)
192 CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
193 kCFStringEncodingUTF8) + 1;
194 char *retval = (char *) allocator.Malloc(len);
195 BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
197 if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8))
199 /* shrink overallocated buffer if possible... */
200 CFIndex newlen = strlen(retval) + 1;
203 void *ptr = allocator.Realloc(retval, newlen);
205 retval = (char *) ptr;
209 else /* probably shouldn't fail, but just in case... */
211 allocator.Free(retval);
212 BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
216 } /* convertCFString */
219 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
221 ProcessSerialNumber psn = { 0, kCurrentProcess };
224 CFURLRef cfurl = NULL;
225 CFStringRef cfstr = NULL;
226 CFMutableStringRef cfmutstr = NULL;
229 BAIL_IF_MACRO(GetProcessBundleLocation(&psn, &fsref) != noErr, NULL, NULL);
230 cfurl = CFURLCreateFromFSRef(cfallocator, &fsref);
231 BAIL_IF_MACRO(cfurl == NULL, NULL, NULL);
232 cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
234 BAIL_IF_MACRO(cfstr == NULL, NULL, NULL);
235 cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr);
237 BAIL_IF_MACRO(cfmutstr == NULL, NULL, NULL);
239 /* Find last dirsep so we can chop the binary's filename from the path. */
240 cfrange = CFStringFind(cfmutstr, CFSTR("/"), kCFCompareBackwards);
241 if (cfrange.location == kCFNotFound)
243 assert(0); /* shouldn't ever hit this... */
248 /* chop the "/exename" from the end of the path string... */
249 cfrange.length = CFStringGetLength(cfmutstr) - cfrange.location;
250 CFStringDelete(cfmutstr, cfrange);
252 /* If we're an Application Bundle, chop everything but the base. */
253 cfrange = CFStringFind(cfmutstr, CFSTR("/Contents/MacOS"),
254 kCFCompareCaseInsensitive |
255 kCFCompareBackwards |
258 if (cfrange.location != kCFNotFound)
259 CFStringDelete(cfmutstr, cfrange); /* chop that, too. */
261 retval = convertCFString(cfmutstr);
264 return(retval); /* whew. */
265 } /* __PHYSFS_platformCalcBaseDir */
271 char *__PHYSFS_platformRealPath(const char *path)
273 /* The symlink and relative path resolving happens in FSPathMakeRef() */
275 CFURLRef cfurl = NULL;
276 CFStringRef cfstr = NULL;
278 OSStatus rc = osxerr(FSPathMakeRef((UInt8 *) path, &fsref, NULL));
279 BAIL_IF_MACRO(rc != noErr, NULL, NULL);
281 /* Now get it to spit out a full path. */
282 cfurl = CFURLCreateFromFSRef(cfallocator, &fsref);
283 BAIL_IF_MACRO(cfurl == NULL, ERR_OUT_OF_MEMORY, NULL);
284 cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
286 BAIL_IF_MACRO(cfstr == NULL, ERR_OUT_OF_MEMORY, NULL);
287 retval = convertCFString(cfstr);
291 } /* __PHYSFS_platformRealPath */
294 char *__PHYSFS_platformCurrentDir(void)
296 return(__PHYSFS_platformRealPath(".")); /* let CFURL sort it out. */
297 } /* __PHYSFS_platformCurrentDir */
300 /* Platform allocator uses default CFAllocator at PHYSFS_init() time. */
302 static CFAllocatorRef cfallocdef = NULL;
304 static int macosxAllocatorInit(void)
307 cfallocdef = CFAllocatorGetDefault();
308 retval = (cfallocdef != NULL);
310 CFRetain(cfallocdef);
312 } /* macosxAllocatorInit */
315 static void macosxAllocatorDeinit(void)
317 if (cfallocdef != NULL)
319 CFRelease(cfallocdef);
322 } /* macosxAllocatorDeinit */
325 static void *macosxAllocatorMalloc(PHYSFS_uint64 s)
327 BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
328 return(CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0));
329 } /* macosxAllocatorMalloc */
332 static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
334 BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
335 return(CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0));
336 } /* macosxAllocatorRealloc */
339 static void macosxAllocatorFree(void *ptr)
341 CFAllocatorDeallocate(cfallocdef, ptr);
342 } /* macosxAllocatorFree */
345 int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
347 allocator.Init = macosxAllocatorInit;
348 allocator.Deinit = macosxAllocatorDeinit;
349 allocator.Malloc = macosxAllocatorMalloc;
350 allocator.Realloc = macosxAllocatorRealloc;
351 allocator.Free = macosxAllocatorFree;
352 return(1); /* return non-zero: we're supplying custom allocator. */
353 } /* __PHYSFS_platformSetDefaultAllocator */
356 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
358 return( (PHYSFS_uint64) ((size_t) MPCurrentTaskID()) );
359 } /* __PHYSFS_platformGetThreadID */
362 void *__PHYSFS_platformCreateMutex(void)
364 MPCriticalRegionID m = NULL;
365 if (osxerr(MPCreateCriticalRegion(&m)) != noErr)
368 } /* __PHYSFS_platformCreateMutex */
371 void __PHYSFS_platformDestroyMutex(void *mutex)
373 MPCriticalRegionID m = (MPCriticalRegionID) mutex;
374 MPDeleteCriticalRegion(m);
375 } /* __PHYSFS_platformDestroyMutex */
378 int __PHYSFS_platformGrabMutex(void *mutex)
380 MPCriticalRegionID m = (MPCriticalRegionID) mutex;
381 if (MPEnterCriticalRegion(m, kDurationForever) != noErr)
384 } /* __PHYSFS_platformGrabMutex */
387 void __PHYSFS_platformReleaseMutex(void *mutex)
389 MPCriticalRegionID m = (MPCriticalRegionID) mutex;
390 MPExitCriticalRegion(m);
391 } /* __PHYSFS_platformReleaseMutex */
393 #endif /* PHYSFS_PLATFORM_MACOSX */
395 /* end of macosx.c ... */