Upload 2.0.2
[physicsfs] / platform / macosx.c
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 <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>
19
20 /* Seems to get defined in some system header... */
21 #ifdef Free
22 #undef Free
23 #endif
24
25 #include "physfs_internal.h"
26
27
28 /* Wrap PHYSFS_Allocator in a CFAllocator... */
29 static CFAllocatorRef cfallocator = NULL;
30
31 CFStringRef cfallocDesc(const void *info)
32 {
33     return(CFStringCreateWithCString(cfallocator, "PhysicsFS",
34                                      kCFStringEncodingASCII));
35 } /* cfallocDesc */
36
37
38 static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
39 {
40     return allocator.Malloc(allocSize);
41 } /* cfallocMalloc */
42
43
44 static void cfallocFree(void *ptr, void *info)
45 {
46     allocator.Free(ptr);
47 } /* cfallocFree */
48
49
50 static void *cfallocRealloc(void *ptr, CFIndex newsize,
51                             CFOptionFlags hint, void *info)
52 {
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 */
57
58
59 int __PHYSFS_platformInit(void)
60 {
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 */
72
73
74 int __PHYSFS_platformDeinit(void)
75 {
76     CFRelease(cfallocator);
77     cfallocator = NULL;
78     return(1);  /* always succeed. */
79 } /* __PHYSFS_platformDeinit */
80
81
82 /* CD-ROM detection code... */
83
84 /*
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
87  */
88
89 static int darwinIsWholeMedia(io_service_t service)
90 {
91     int retval = 0;
92     CFTypeRef wholeMedia;
93
94     if (!IOObjectConformsTo(service, kIOMediaClass))
95         return(0);
96         
97     wholeMedia = IORegistryEntryCreateCFProperty(service,
98                                                  CFSTR(kIOMediaWholeKey),
99                                                  cfallocator, 0);
100     if (wholeMedia == NULL)
101         return(0);
102
103     retval = CFBooleanGetValue(wholeMedia);
104     CFRelease(wholeMedia);
105
106     return retval;
107 } /* darwinIsWholeMedia */
108
109
110 static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort)
111 {
112     int retval = 0;
113     CFMutableDictionaryRef matchingDict;
114     kern_return_t rc;
115     io_iterator_t iter;
116     io_service_t service;
117
118     if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL)
119         return(0);
120
121     rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter);
122     if ((rc != KERN_SUCCESS) || (!iter))
123         return(0);
124
125     service = IOIteratorNext(iter);
126     IOObjectRelease(iter);
127     if (!service)
128         return(0);
129
130     rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
131              kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
132     
133     if (!iter)
134         return(0);
135
136     if (rc != KERN_SUCCESS)
137     {
138         IOObjectRelease(iter);
139         return(0);
140     } /* if */
141
142     IOObjectRetain(service);  /* add an extra object reference... */
143
144     do
145     {
146         if (darwinIsWholeMedia(service))
147         {
148             if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
149                  (IOObjectConformsTo(service, kIODVDMediaClass)) )
150             {
151                 retval = 1;
152             } /* if */
153         } /* if */
154         IOObjectRelease(service);
155     } while ((service = IOIteratorNext(iter)) && (!retval));
156                 
157     IOObjectRelease(iter);
158     IOObjectRelease(service);
159
160     return(retval);
161 } /* darwinIsMountedDisc */
162
163
164 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
165 {
166     const char *devPrefix = "/dev/";
167     const int prefixLen = strlen(devPrefix);
168     mach_port_t masterPort = 0;
169     struct statfs *mntbufp;
170     int i, mounts;
171
172     if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
173         BAIL_MACRO(ERR_OS_ERROR, ) /*return void*/;
174
175     mounts = getmntinfo(&mntbufp, MNT_WAIT);  /* NOT THREAD SAFE! */
176     for (i = 0; i < mounts; i++)
177     {
178         char *dev = mntbufp[i].f_mntfromname;
179         char *mnt = mntbufp[i].f_mntonname;
180         if (strncmp(dev, devPrefix, prefixLen) != 0)  /* a virtual device? */
181             continue;
182
183         dev += prefixLen;
184         if (darwinIsMountedDisc(dev, masterPort))
185             cb(data, mnt);
186     } /* for */
187 } /* __PHYSFS_platformDetectAvailableCDs */
188
189
190 static char *convertCFString(CFStringRef cfstr)
191 {
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);
196
197     if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8))
198     {
199         /* shrink overallocated buffer if possible... */
200         CFIndex newlen = strlen(retval) + 1;
201         if (newlen < len)
202         {
203             void *ptr = allocator.Realloc(retval, newlen);
204             if (ptr != NULL)
205                 retval = (char *) ptr;
206         } /* if */
207     } /* if */
208
209     else  /* probably shouldn't fail, but just in case... */
210     {
211         allocator.Free(retval);
212         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
213     } /* else */
214
215     return(retval);
216 } /* convertCFString */
217
218
219 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
220 {
221     ProcessSerialNumber psn = { 0, kCurrentProcess };
222     FSRef fsref;
223     CFRange cfrange;
224     CFURLRef cfurl = NULL;
225     CFStringRef cfstr = NULL;
226     CFMutableStringRef cfmutstr = NULL;
227     char *retval = NULL;
228
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);
233     CFRelease(cfurl);
234     BAIL_IF_MACRO(cfstr == NULL, NULL, NULL);
235     cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr);
236     CFRelease(cfstr);
237     BAIL_IF_MACRO(cfmutstr == NULL, NULL, NULL);
238
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)
242     {
243         assert(0);  /* shouldn't ever hit this... */
244         CFRelease(cfmutstr);
245         return(NULL);
246     } /* if */
247
248     /* chop the "/exename" from the end of the path string... */
249     cfrange.length = CFStringGetLength(cfmutstr) - cfrange.location;
250     CFStringDelete(cfmutstr, cfrange);
251
252     /* If we're an Application Bundle, chop everything but the base. */
253     cfrange = CFStringFind(cfmutstr, CFSTR("/Contents/MacOS"),
254                            kCFCompareCaseInsensitive |
255                            kCFCompareBackwards |
256                            kCFCompareAnchored);
257
258     if (cfrange.location != kCFNotFound)
259         CFStringDelete(cfmutstr, cfrange);  /* chop that, too. */
260
261     retval = convertCFString(cfmutstr);
262     CFRelease(cfmutstr);
263
264     return(retval);  /* whew. */
265 } /* __PHYSFS_platformCalcBaseDir */
266
267
268 /* !!! FIXME */
269 #define osxerr(x) x
270
271 char *__PHYSFS_platformRealPath(const char *path)
272 {
273     /* The symlink and relative path resolving happens in FSPathMakeRef() */
274     FSRef fsref;
275     CFURLRef cfurl = NULL;
276     CFStringRef cfstr = NULL;
277     char *retval = NULL;
278     OSStatus rc = osxerr(FSPathMakeRef((UInt8 *) path, &fsref, NULL));
279     BAIL_IF_MACRO(rc != noErr, NULL, NULL);
280
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);
285     CFRelease(cfurl);
286     BAIL_IF_MACRO(cfstr == NULL, ERR_OUT_OF_MEMORY, NULL);
287     retval = convertCFString(cfstr);
288     CFRelease(cfstr);
289
290     return(retval);
291 } /* __PHYSFS_platformRealPath */
292
293
294 char *__PHYSFS_platformCurrentDir(void)
295 {
296     return(__PHYSFS_platformRealPath("."));  /* let CFURL sort it out. */
297 } /* __PHYSFS_platformCurrentDir */
298
299
300 /* Platform allocator uses default CFAllocator at PHYSFS_init() time. */
301
302 static CFAllocatorRef cfallocdef = NULL;
303
304 static int macosxAllocatorInit(void)
305 {
306     int retval = 0;
307     cfallocdef = CFAllocatorGetDefault();
308     retval = (cfallocdef != NULL);
309     if (retval)
310         CFRetain(cfallocdef);
311     return(retval);
312 } /* macosxAllocatorInit */
313
314
315 static void macosxAllocatorDeinit(void)
316 {
317     if (cfallocdef != NULL)
318     {
319         CFRelease(cfallocdef);
320         cfallocdef = NULL;
321     } /* if */
322 } /* macosxAllocatorDeinit */
323
324
325 static void *macosxAllocatorMalloc(PHYSFS_uint64 s)
326 {
327     BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
328     return(CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0));
329 } /* macosxAllocatorMalloc */
330
331
332 static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
333 {
334     BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
335     return(CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0));
336 } /* macosxAllocatorRealloc */
337
338
339 static void macosxAllocatorFree(void *ptr)
340 {
341     CFAllocatorDeallocate(cfallocdef, ptr);
342 } /* macosxAllocatorFree */
343
344
345 int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
346 {
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 */
354
355
356 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
357 {
358     return( (PHYSFS_uint64) ((size_t) MPCurrentTaskID()) );
359 } /* __PHYSFS_platformGetThreadID */
360
361
362 void *__PHYSFS_platformCreateMutex(void)
363 {
364     MPCriticalRegionID m = NULL;
365     if (osxerr(MPCreateCriticalRegion(&m)) != noErr)
366         return NULL;
367     return m;
368 } /* __PHYSFS_platformCreateMutex */
369
370
371 void __PHYSFS_platformDestroyMutex(void *mutex)
372 {
373     MPCriticalRegionID m = (MPCriticalRegionID) mutex;
374     MPDeleteCriticalRegion(m);
375 } /* __PHYSFS_platformDestroyMutex */
376
377
378 int __PHYSFS_platformGrabMutex(void *mutex)
379 {
380     MPCriticalRegionID m = (MPCriticalRegionID) mutex;
381     if (MPEnterCriticalRegion(m, kDurationForever) != noErr)
382         return(0);
383     return(1);
384 } /* __PHYSFS_platformGrabMutex */
385
386
387 void __PHYSFS_platformReleaseMutex(void *mutex)
388 {
389     MPCriticalRegionID m = (MPCriticalRegionID) mutex;
390     MPExitCriticalRegion(m);
391 } /* __PHYSFS_platformReleaseMutex */
392
393 #endif /* PHYSFS_PLATFORM_MACOSX */
394
395 /* end of macosx.c ... */
396