macosx.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  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. #define __PHYSICSFS_INTERNAL__
  9. #include "physfs_platforms.h"
  10. #ifdef PHYSFS_PLATFORM_MACOSX
  11. #include <Carbon/Carbon.h>
  12. #include <IOKit/storage/IOMedia.h>
  13. #include <IOKit/storage/IOCDMedia.h>
  14. #include <IOKit/storage/IODVDMedia.h>
  15. #include <sys/mount.h>
  16. /* Seems to get defined in some system header... */
  17. #ifdef Free
  18. #undef Free
  19. #endif
  20. #include "physfs_internal.h"
  21. /* Wrap PHYSFS_Allocator in a CFAllocator... */
  22. static CFAllocatorRef cfallocator = NULL;
  23. CFStringRef cfallocDesc(const void *info)
  24. {
  25. return(CFStringCreateWithCString(cfallocator, "PhysicsFS",
  26. kCFStringEncodingASCII));
  27. } /* cfallocDesc */
  28. static void *cfallocMalloc(CFIndex allocSize, CFOptionFlags hint, void *info)
  29. {
  30. return allocator.Malloc(allocSize);
  31. } /* cfallocMalloc */
  32. static void cfallocFree(void *ptr, void *info)
  33. {
  34. allocator.Free(ptr);
  35. } /* cfallocFree */
  36. static void *cfallocRealloc(void *ptr, CFIndex newsize,
  37. CFOptionFlags hint, void *info)
  38. {
  39. if ((ptr == NULL) || (newsize <= 0))
  40. return NULL; /* ADC docs say you should always return NULL here. */
  41. return allocator.Realloc(ptr, newsize);
  42. } /* cfallocRealloc */
  43. int __PHYSFS_platformInit(void)
  44. {
  45. /* set up a CFAllocator, so Carbon can use the physfs allocator, too. */
  46. CFAllocatorContext ctx;
  47. memset(&ctx, '\0', sizeof (ctx));
  48. ctx.copyDescription = cfallocDesc;
  49. ctx.allocate = cfallocMalloc;
  50. ctx.reallocate = cfallocRealloc;
  51. ctx.deallocate = cfallocFree;
  52. cfallocator = CFAllocatorCreate(kCFAllocatorUseContext, &ctx);
  53. BAIL_IF_MACRO(cfallocator == NULL, ERR_OUT_OF_MEMORY, 0);
  54. return(1); /* success. */
  55. } /* __PHYSFS_platformInit */
  56. int __PHYSFS_platformDeinit(void)
  57. {
  58. CFRelease(cfallocator);
  59. cfallocator = NULL;
  60. return(1); /* always succeed. */
  61. } /* __PHYSFS_platformDeinit */
  62. /* CD-ROM detection code... */
  63. /*
  64. * Code based on sample from Apple Developer Connection:
  65. * http://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm
  66. */
  67. static int darwinIsWholeMedia(io_service_t service)
  68. {
  69. int retval = 0;
  70. CFTypeRef wholeMedia;
  71. if (!IOObjectConformsTo(service, kIOMediaClass))
  72. return(0);
  73. wholeMedia = IORegistryEntryCreateCFProperty(service,
  74. CFSTR(kIOMediaWholeKey),
  75. cfallocator, 0);
  76. if (wholeMedia == NULL)
  77. return(0);
  78. retval = CFBooleanGetValue(wholeMedia);
  79. CFRelease(wholeMedia);
  80. return retval;
  81. } /* darwinIsWholeMedia */
  82. static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort)
  83. {
  84. int retval = 0;
  85. CFMutableDictionaryRef matchingDict;
  86. kern_return_t rc;
  87. io_iterator_t iter;
  88. io_service_t service;
  89. if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL)
  90. return(0);
  91. rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter);
  92. if ((rc != KERN_SUCCESS) || (!iter))
  93. return(0);
  94. service = IOIteratorNext(iter);
  95. IOObjectRelease(iter);
  96. if (!service)
  97. return(0);
  98. rc = IORegistryEntryCreateIterator(service, kIOServicePlane,
  99. kIORegistryIterateRecursively | kIORegistryIterateParents, &iter);
  100. if (!iter)
  101. return(0);
  102. if (rc != KERN_SUCCESS)
  103. {
  104. IOObjectRelease(iter);
  105. return(0);
  106. } /* if */
  107. IOObjectRetain(service); /* add an extra object reference... */
  108. do
  109. {
  110. if (darwinIsWholeMedia(service))
  111. {
  112. if ( (IOObjectConformsTo(service, kIOCDMediaClass)) ||
  113. (IOObjectConformsTo(service, kIODVDMediaClass)) )
  114. {
  115. retval = 1;
  116. } /* if */
  117. } /* if */
  118. IOObjectRelease(service);
  119. } while ((service = IOIteratorNext(iter)) && (!retval));
  120. IOObjectRelease(iter);
  121. IOObjectRelease(service);
  122. return(retval);
  123. } /* darwinIsMountedDisc */
  124. void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
  125. {
  126. const char *devPrefix = "/dev/";
  127. const int prefixLen = strlen(devPrefix);
  128. mach_port_t masterPort = 0;
  129. struct statfs *mntbufp;
  130. int i, mounts;
  131. if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS)
  132. BAIL_MACRO(ERR_OS_ERROR, ) /*return void*/;
  133. mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */
  134. for (i = 0; i < mounts; i++)
  135. {
  136. char *dev = mntbufp[i].f_mntfromname;
  137. char *mnt = mntbufp[i].f_mntonname;
  138. if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */
  139. continue;
  140. dev += prefixLen;
  141. if (darwinIsMountedDisc(dev, masterPort))
  142. cb(data, mnt);
  143. } /* for */
  144. } /* __PHYSFS_platformDetectAvailableCDs */
  145. static char *convertCFString(CFStringRef cfstr)
  146. {
  147. CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr),
  148. kCFStringEncodingUTF8) + 1;
  149. char *retval = (char *) allocator.Malloc(len);
  150. BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
  151. if (CFStringGetCString(cfstr, retval, len, kCFStringEncodingUTF8))
  152. {
  153. /* shrink overallocated buffer if possible... */
  154. CFIndex newlen = strlen(retval) + 1;
  155. if (newlen < len)
  156. {
  157. void *ptr = allocator.Realloc(retval, newlen);
  158. if (ptr != NULL)
  159. retval = (char *) ptr;
  160. } /* if */
  161. } /* if */
  162. else /* probably shouldn't fail, but just in case... */
  163. {
  164. allocator.Free(retval);
  165. BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
  166. } /* else */
  167. return(retval);
  168. } /* convertCFString */
  169. char *__PHYSFS_platformCalcBaseDir(const char *argv0)
  170. {
  171. ProcessSerialNumber psn = { 0, kCurrentProcess };
  172. FSRef fsref;
  173. CFRange cfrange;
  174. CFURLRef cfurl = NULL;
  175. CFStringRef cfstr = NULL;
  176. CFMutableStringRef cfmutstr = NULL;
  177. char *retval = NULL;
  178. BAIL_IF_MACRO(GetProcessBundleLocation(&psn, &fsref) != noErr, NULL, NULL);
  179. cfurl = CFURLCreateFromFSRef(cfallocator, &fsref);
  180. BAIL_IF_MACRO(cfurl == NULL, NULL, NULL);
  181. cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
  182. CFRelease(cfurl);
  183. BAIL_IF_MACRO(cfstr == NULL, NULL, NULL);
  184. cfmutstr = CFStringCreateMutableCopy(cfallocator, 0, cfstr);
  185. CFRelease(cfstr);
  186. BAIL_IF_MACRO(cfmutstr == NULL, NULL, NULL);
  187. /* Find last dirsep so we can chop the binary's filename from the path. */
  188. cfrange = CFStringFind(cfmutstr, CFSTR("/"), kCFCompareBackwards);
  189. if (cfrange.location == kCFNotFound)
  190. {
  191. assert(0); /* shouldn't ever hit this... */
  192. CFRelease(cfmutstr);
  193. return(NULL);
  194. } /* if */
  195. /* chop the "/exename" from the end of the path string... */
  196. cfrange.length = CFStringGetLength(cfmutstr) - cfrange.location;
  197. CFStringDelete(cfmutstr, cfrange);
  198. /* If we're an Application Bundle, chop everything but the base. */
  199. cfrange = CFStringFind(cfmutstr, CFSTR("/Contents/MacOS"),
  200. kCFCompareCaseInsensitive |
  201. kCFCompareBackwards |
  202. kCFCompareAnchored);
  203. if (cfrange.location != kCFNotFound)
  204. CFStringDelete(cfmutstr, cfrange); /* chop that, too. */
  205. retval = convertCFString(cfmutstr);
  206. CFRelease(cfmutstr);
  207. return(retval); /* whew. */
  208. } /* __PHYSFS_platformCalcBaseDir */
  209. /* !!! FIXME */
  210. #define osxerr(x) x
  211. char *__PHYSFS_platformRealPath(const char *path)
  212. {
  213. /* The symlink and relative path resolving happens in FSPathMakeRef() */
  214. FSRef fsref;
  215. CFURLRef cfurl = NULL;
  216. CFStringRef cfstr = NULL;
  217. char *retval = NULL;
  218. OSStatus rc = osxerr(FSPathMakeRef((UInt8 *) path, &fsref, NULL));
  219. BAIL_IF_MACRO(rc != noErr, NULL, NULL);
  220. /* Now get it to spit out a full path. */
  221. cfurl = CFURLCreateFromFSRef(cfallocator, &fsref);
  222. BAIL_IF_MACRO(cfurl == NULL, ERR_OUT_OF_MEMORY, NULL);
  223. cfstr = CFURLCopyFileSystemPath(cfurl, kCFURLPOSIXPathStyle);
  224. CFRelease(cfurl);
  225. BAIL_IF_MACRO(cfstr == NULL, ERR_OUT_OF_MEMORY, NULL);
  226. retval = convertCFString(cfstr);
  227. CFRelease(cfstr);
  228. return(retval);
  229. } /* __PHYSFS_platformRealPath */
  230. char *__PHYSFS_platformCurrentDir(void)
  231. {
  232. return(__PHYSFS_platformRealPath(".")); /* let CFURL sort it out. */
  233. } /* __PHYSFS_platformCurrentDir */
  234. /* Platform allocator uses default CFAllocator at PHYSFS_init() time. */
  235. static CFAllocatorRef cfallocdef = NULL;
  236. static int macosxAllocatorInit(void)
  237. {
  238. int retval = 0;
  239. cfallocdef = CFAllocatorGetDefault();
  240. retval = (cfallocdef != NULL);
  241. if (retval)
  242. CFRetain(cfallocdef);
  243. return(retval);
  244. } /* macosxAllocatorInit */
  245. static void macosxAllocatorDeinit(void)
  246. {
  247. if (cfallocdef != NULL)
  248. {
  249. CFRelease(cfallocdef);
  250. cfallocdef = NULL;
  251. } /* if */
  252. } /* macosxAllocatorDeinit */
  253. static void *macosxAllocatorMalloc(PHYSFS_uint64 s)
  254. {
  255. BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
  256. return(CFAllocatorAllocate(cfallocdef, (CFIndex) s, 0));
  257. } /* macosxAllocatorMalloc */
  258. static void *macosxAllocatorRealloc(void *ptr, PHYSFS_uint64 s)
  259. {
  260. BAIL_IF_MACRO(__PHYSFS_ui64FitsAddressSpace(s), ERR_OUT_OF_MEMORY, NULL);
  261. return(CFAllocatorReallocate(cfallocdef, ptr, (CFIndex) s, 0));
  262. } /* macosxAllocatorRealloc */
  263. static void macosxAllocatorFree(void *ptr)
  264. {
  265. CFAllocatorDeallocate(cfallocdef, ptr);
  266. } /* macosxAllocatorFree */
  267. int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
  268. {
  269. allocator.Init = macosxAllocatorInit;
  270. allocator.Deinit = macosxAllocatorDeinit;
  271. allocator.Malloc = macosxAllocatorMalloc;
  272. allocator.Realloc = macosxAllocatorRealloc;
  273. allocator.Free = macosxAllocatorFree;
  274. return(1); /* return non-zero: we're supplying custom allocator. */
  275. } /* __PHYSFS_platformSetDefaultAllocator */
  276. PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
  277. {
  278. return( (PHYSFS_uint64) ((size_t) MPCurrentTaskID()) );
  279. } /* __PHYSFS_platformGetThreadID */
  280. void *__PHYSFS_platformCreateMutex(void)
  281. {
  282. MPCriticalRegionID m = NULL;
  283. if (osxerr(MPCreateCriticalRegion(&m)) != noErr)
  284. return NULL;
  285. return m;
  286. } /* __PHYSFS_platformCreateMutex */
  287. void __PHYSFS_platformDestroyMutex(void *mutex)
  288. {
  289. MPCriticalRegionID m = (MPCriticalRegionID) mutex;
  290. MPDeleteCriticalRegion(m);
  291. } /* __PHYSFS_platformDestroyMutex */
  292. int __PHYSFS_platformGrabMutex(void *mutex)
  293. {
  294. MPCriticalRegionID m = (MPCriticalRegionID) mutex;
  295. if (MPEnterCriticalRegion(m, kDurationForever) != noErr)
  296. return(0);
  297. return(1);
  298. } /* __PHYSFS_platformGrabMutex */
  299. void __PHYSFS_platformReleaseMutex(void *mutex)
  300. {
  301. MPCriticalRegionID m = (MPCriticalRegionID) mutex;
  302. MPExitCriticalRegion(m);
  303. } /* __PHYSFS_platformReleaseMutex */
  304. #endif /* PHYSFS_PLATFORM_MACOSX */
  305. /* end of macosx.c ... */