| 1 | /* |
| 2 | * Copyright (c) 2010, Wei Mingzhi <whistler@openoffice.org>. |
| 3 | * All Rights Reserved. |
| 4 | * |
| 5 | * Based on: Cdrom for Psemu Pro like Emulators |
| 6 | * By: linuzappz <linuzappz@hotmail.com> |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License as published by |
| 10 | * the Free Software Foundation; either version 2 of the License, or |
| 11 | * (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU General Public License |
| 19 | * along with this program; if not, see <http://www.gnu.org/licenses>. |
| 20 | */ |
| 21 | |
| 22 | #include "cdr.h" |
| 23 | |
| 24 | #ifdef _MACOSX |
| 25 | |
| 26 | #include <IOKit/IOKitLib.h> |
| 27 | #include <IOKit/IOBSD.h> |
| 28 | #include <IOKit/storage/IOCDMedia.h> |
| 29 | #include <IOKit/storage/IODVDMedia.h> |
| 30 | #include <IOKit/storage/IOMedia.h> |
| 31 | #include <IOKit/storage/IOCDMediaBSDClient.h> |
| 32 | #include <CoreFoundation/CoreFoundation.h> |
| 33 | |
| 34 | int cdHandle = -1; |
| 35 | char cdDevice[4096] = ""; |
| 36 | |
| 37 | static int IsPsxDisc(const char *dev) { |
| 38 | int fd; |
| 39 | char buf[CD_FRAMESIZE_RAW]; |
| 40 | dk_cd_read_t r; |
| 41 | |
| 42 | fd = open(dev, O_RDONLY, 0); |
| 43 | if (fd < 0) return 0; |
| 44 | |
| 45 | memset(&r, 0, sizeof(r)); |
| 46 | |
| 47 | r.offset = msf_to_lba(0, 2, 4) * CD_FRAMESIZE_RAW; |
| 48 | r.sectorArea = 0xF8; |
| 49 | r.sectorType = kCDSectorTypeUnknown; |
| 50 | r.bufferLength = CD_FRAMESIZE_RAW; |
| 51 | r.buffer = buf; |
| 52 | |
| 53 | if (ioctl(fd, DKIOCCDREAD, &r) != kIOReturnSuccess) { |
| 54 | close(fd); |
| 55 | return 0; |
| 56 | } |
| 57 | |
| 58 | close(fd); |
| 59 | |
| 60 | if (strncmp(buf + 56, "Sony Computer Entertainment", 27) == 0) { |
| 61 | return 1; |
| 62 | } |
| 63 | |
| 64 | return 0; |
| 65 | } |
| 66 | |
| 67 | static void FindCdDevice(char *dev) { |
| 68 | io_object_t next_media; |
| 69 | kern_return_t kern_result; |
| 70 | io_iterator_t media_iterator; |
| 71 | CFMutableDictionaryRef classes_to_match; |
| 72 | const char *name, *cd = kIOCDMediaClass, *dvd = kIODVDMediaClass; |
| 73 | |
| 74 | dev[0] = '\0'; |
| 75 | name = cd; |
| 76 | |
| 77 | start: |
| 78 | classes_to_match = IOServiceMatching(name); |
| 79 | if (classes_to_match == NULL) goto end; |
| 80 | |
| 81 | CFDictionarySetValue(classes_to_match, CFSTR(kIOMediaEjectableKey), |
| 82 | kCFBooleanTrue); |
| 83 | |
| 84 | kern_result = IOServiceGetMatchingServices(kIOMasterPortDefault, |
| 85 | classes_to_match, &media_iterator); |
| 86 | |
| 87 | if (kern_result != KERN_SUCCESS) goto end; |
| 88 | |
| 89 | next_media = IOIteratorNext(media_iterator); |
| 90 | if (next_media != 0) { |
| 91 | char psz_buf[0x32]; |
| 92 | size_t dev_path_length; |
| 93 | CFTypeRef str_bsd_path; |
| 94 | |
| 95 | do { |
| 96 | str_bsd_path = IORegistryEntryCreateCFProperty(next_media, |
| 97 | CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); |
| 98 | |
| 99 | if (str_bsd_path == NULL) { |
| 100 | IOObjectRelease(next_media); |
| 101 | continue; |
| 102 | } |
| 103 | |
| 104 | strcpy(psz_buf, "/dev/r"); |
| 105 | dev_path_length = strlen(psz_buf); |
| 106 | |
| 107 | if (CFStringGetCString(str_bsd_path, (char *)&psz_buf + dev_path_length, |
| 108 | sizeof(psz_buf) - dev_path_length, kCFStringEncodingASCII)) |
| 109 | { |
| 110 | strcpy(dev, psz_buf); |
| 111 | |
| 112 | if (IsPsxDisc(dev)) { |
| 113 | CFRelease(str_bsd_path); |
| 114 | IOObjectRelease(next_media); |
| 115 | IOObjectRelease(media_iterator); |
| 116 | return; |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | CFRelease(str_bsd_path); |
| 121 | IOObjectRelease(next_media); |
| 122 | } while ((next_media = IOIteratorNext(media_iterator)) != 0); |
| 123 | } |
| 124 | |
| 125 | IOObjectRelease(media_iterator); |
| 126 | |
| 127 | end: |
| 128 | if (dev[0] == '\0') { |
| 129 | if (name == cd) { |
| 130 | name = dvd; // Is this really necessary or correct? Dunno... |
| 131 | goto start; |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | int OpenCdHandle(const char *dev) { |
| 137 | if (dev != NULL && dev[0] != '\0') strcpy(cdDevice, dev); |
| 138 | else if (cdDevice[0] == '\0') FindCdDevice(cdDevice); |
| 139 | |
| 140 | cdHandle = open(cdDevice, O_RDONLY, 0); |
| 141 | if (cdHandle < 0) return -1; |
| 142 | |
| 143 | if (CdrSpeed > 0) { |
| 144 | u_int16_t speed = kCDSpeedMin * CdrSpeed; |
| 145 | ioctl(cdHandle, DKIOCCDSETSPEED, &speed); |
| 146 | } |
| 147 | |
| 148 | return 0; |
| 149 | } |
| 150 | |
| 151 | void CloseCdHandle() { |
| 152 | if (cdHandle != -1) close(cdHandle); |
| 153 | cdHandle = -1; |
| 154 | } |
| 155 | |
| 156 | int IsCdHandleOpen() { |
| 157 | return 1; |
| 158 | } |
| 159 | |
| 160 | long GetTN(unsigned char *buffer) { |
| 161 | if (cdHandle < 0) return -1; |
| 162 | |
| 163 | // TODO |
| 164 | buffer[0] = 1; |
| 165 | buffer[1] = 1; |
| 166 | |
| 167 | return 0; |
| 168 | } |
| 169 | |
| 170 | long GetTD(unsigned char track, unsigned char *buffer) { |
| 171 | if (cdHandle < 0) return -1; |
| 172 | |
| 173 | // TODO |
| 174 | memset(buffer + 1, 0, 3); |
| 175 | return 0; |
| 176 | } |
| 177 | |
| 178 | long GetTE(unsigned char track, unsigned char *m, unsigned char *s, unsigned char *f) { |
| 179 | return -1; // TODO |
| 180 | } |
| 181 | |
| 182 | long ReadSector(crdata *cr) { |
| 183 | int lba; |
| 184 | dk_cd_read_t r; |
| 185 | |
| 186 | if (cdHandle < 0) return -1; |
| 187 | |
| 188 | lba = msf_to_lba(cr->msf.cdmsf_min0, cr->msf.cdmsf_sec0, cr->msf.cdmsf_frame0); |
| 189 | |
| 190 | memset(&r, 0, sizeof(r)); |
| 191 | |
| 192 | r.offset = lba * CD_FRAMESIZE_RAW; |
| 193 | r.sectorArea = 0xF8; |
| 194 | r.sectorType = kCDSectorTypeUnknown; |
| 195 | r.bufferLength = CD_FRAMESIZE_RAW; |
| 196 | r.buffer = cr->buf; |
| 197 | |
| 198 | if (ioctl(cdHandle, DKIOCCDREAD, &r) != kIOReturnSuccess) { |
| 199 | return -1; |
| 200 | } |
| 201 | |
| 202 | return 0; |
| 203 | } |
| 204 | |
| 205 | long PlayCDDA(unsigned char *sector) { |
| 206 | return 0; // TODO |
| 207 | } |
| 208 | |
| 209 | long StopCDDA() { |
| 210 | return 0; // TODO |
| 211 | } |
| 212 | |
| 213 | long GetStatus(int playing, struct CdrStat *stat) { |
| 214 | memset(stat, 0, sizeof(struct CdrStat)); |
| 215 | stat->Type = 0x01; |
| 216 | |
| 217 | // Close and reopen the CD handle. If opening failed, |
| 218 | // then there is no CD in drive. |
| 219 | // Note that this WILL be screwed if user inserted another |
| 220 | // removable device such as USB stick when tray is open. |
| 221 | // There may be a better way, but this should do the job. |
| 222 | if (cdHandle >= 0) { |
| 223 | close(cdHandle); |
| 224 | cdHandle = -1; |
| 225 | } |
| 226 | |
| 227 | cdHandle = open(cdDevice, O_RDONLY, 0); |
| 228 | if (cdHandle < 0) { |
| 229 | // No CD in drive |
| 230 | stat->Type = 0xff; |
| 231 | stat->Status |= 0x10; |
| 232 | } else { |
| 233 | if (CdrSpeed > 0) { |
| 234 | u_int16_t speed = kCDSpeedMin * CdrSpeed; |
| 235 | ioctl(cdHandle, DKIOCCDSETSPEED, &speed); |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | return 0; |
| 240 | } |
| 241 | |
| 242 | unsigned char *ReadSub(const unsigned char *time) { |
| 243 | return NULL; // TODO |
| 244 | } |
| 245 | |
| 246 | char *CDRgetDriveLetter(void) { |
| 247 | return cdDevice; |
| 248 | } |
| 249 | |
| 250 | #endif |