platform_macosx.c 10 KB

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