Home | History | Annotate | Download | only in macosx
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 #ifdef SDL_CDROM_MACOSX
     25 
     26 #include "SDL_syscdrom_c.h"
     27 
     28 #pragma mark -- Globals --
     29 
     30 static FSRef**         tracks;
     31 static FSVolumeRefNum* volumes;
     32 static CDstatus        status;
     33 static int             nextTrackFrame;
     34 static int             nextTrackFramesRemaining;
     35 static int             fakeCD;
     36 static int             currentTrack;
     37 static int             didReadTOC;
     38 static int             cacheTOCNumTracks;
     39 static int             currentDrive; /* Only allow 1 drive in use at a time */
     40 
     41 #pragma mark -- Prototypes --
     42 
     43 static const char *SDL_SYS_CDName   (int drive);
     44 static int         SDL_SYS_CDOpen   (int drive);
     45 static int         SDL_SYS_CDGetTOC (SDL_CD *cdrom);
     46 static CDstatus    SDL_SYS_CDStatus (SDL_CD *cdrom, int *position);
     47 static int         SDL_SYS_CDPlay   (SDL_CD *cdrom, int start, int length);
     48 static int         SDL_SYS_CDPause  (SDL_CD *cdrom);
     49 static int         SDL_SYS_CDResume (SDL_CD *cdrom);
     50 static int         SDL_SYS_CDStop   (SDL_CD *cdrom);
     51 static int         SDL_SYS_CDEject  (SDL_CD *cdrom);
     52 static void        SDL_SYS_CDClose  (SDL_CD *cdrom);
     53 
     54 #pragma mark -- Helper Functions --
     55 
     56 /* Read a list of tracks from the volume */
     57 static int LoadTracks (SDL_CD *cdrom)
     58 {
     59     /* Check if tracks are already loaded */
     60     if  ( tracks[cdrom->id] != NULL )
     61         return 0;
     62 
     63     /* Allocate memory for tracks */
     64     tracks[cdrom->id] = (FSRef*) SDL_calloc (1, sizeof(**tracks) * cdrom->numtracks);
     65     if (tracks[cdrom->id] == NULL) {
     66         SDL_OutOfMemory ();
     67         return -1;
     68     }
     69 
     70     /* Load tracks */
     71     if (ListTrackFiles (volumes[cdrom->id], tracks[cdrom->id], cdrom->numtracks) < 0)
     72         return -1;
     73 
     74     return 0;
     75 }
     76 
     77 /* Find a file for a given start frame and length */
     78 static FSRef* GetFileForOffset (SDL_CD *cdrom, int start, int length,  int *outStartFrame, int *outStopFrame)
     79 {
     80     int i;
     81 
     82     for (i = 0; i < cdrom->numtracks; i++) {
     83 
     84         if (cdrom->track[i].offset <= start &&
     85             start < (cdrom->track[i].offset + cdrom->track[i].length))
     86             break;
     87     }
     88 
     89     if (i == cdrom->numtracks)
     90         return NULL;
     91 
     92     currentTrack = i;
     93 
     94     *outStartFrame = start - cdrom->track[i].offset;
     95 
     96     if ((*outStartFrame + length) < cdrom->track[i].length) {
     97         *outStopFrame = *outStartFrame + length;
     98         length = 0;
     99         nextTrackFrame = -1;
    100         nextTrackFramesRemaining = -1;
    101     }
    102     else {
    103         *outStopFrame = -1;
    104         length -= cdrom->track[i].length - *outStartFrame;
    105         nextTrackFrame = cdrom->track[i+1].offset;
    106         nextTrackFramesRemaining = length;
    107     }
    108 
    109     return &tracks[cdrom->id][i];
    110 }
    111 
    112 /* Setup another file for playback, or stop playback (called from another thread) */
    113 static void CompletionProc (SDL_CD *cdrom)
    114 {
    115 
    116     Lock ();
    117 
    118     if (nextTrackFrame > 0 && nextTrackFramesRemaining > 0) {
    119 
    120         /* Load the next file to play */
    121         int startFrame, stopFrame;
    122         FSRef *file;
    123 
    124         PauseFile ();
    125         ReleaseFile ();
    126 
    127         file = GetFileForOffset (cdrom, nextTrackFrame,
    128             nextTrackFramesRemaining, &startFrame, &stopFrame);
    129 
    130         if (file == NULL) {
    131             status = CD_STOPPED;
    132             Unlock ();
    133             return;
    134         }
    135 
    136         LoadFile (file, startFrame, stopFrame);
    137 
    138         SetCompletionProc (CompletionProc, cdrom);
    139 
    140         PlayFile ();
    141     }
    142     else {
    143 
    144         /* Release the current file */
    145         PauseFile ();
    146         ReleaseFile ();
    147         status = CD_STOPPED;
    148     }
    149 
    150     Unlock ();
    151 }
    152 
    153 
    154 #pragma mark -- Driver Functions --
    155 
    156 /* Initialize */
    157 int SDL_SYS_CDInit (void)
    158 {
    159     /* Initialize globals */
    160     volumes = NULL;
    161     tracks  = NULL;
    162     status  = CD_STOPPED;
    163     nextTrackFrame = -1;
    164     nextTrackFramesRemaining = -1;
    165     fakeCD  = SDL_FALSE;
    166     currentTrack = -1;
    167     didReadTOC = SDL_FALSE;
    168     cacheTOCNumTracks = -1;
    169     currentDrive = -1;
    170 
    171     /* Fill in function pointers */
    172     SDL_CDcaps.Name   = SDL_SYS_CDName;
    173     SDL_CDcaps.Open   = SDL_SYS_CDOpen;
    174     SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
    175     SDL_CDcaps.Status = SDL_SYS_CDStatus;
    176     SDL_CDcaps.Play   = SDL_SYS_CDPlay;
    177     SDL_CDcaps.Pause  = SDL_SYS_CDPause;
    178     SDL_CDcaps.Resume = SDL_SYS_CDResume;
    179     SDL_CDcaps.Stop   = SDL_SYS_CDStop;
    180     SDL_CDcaps.Eject  = SDL_SYS_CDEject;
    181     SDL_CDcaps.Close  = SDL_SYS_CDClose;
    182 
    183     /*
    184         Read the list of "drives"
    185 
    186         This is currently a hack that infers drives from
    187         mounted audio CD volumes, rather than
    188         actual CD-ROM devices - which means it may not
    189         act as expected sometimes.
    190     */
    191 
    192     /* Find out how many cd volumes are mounted */
    193     SDL_numcds = DetectAudioCDVolumes (NULL, 0);
    194 
    195     /*
    196         If there are no volumes, fake a cd device
    197         so tray empty can be reported.
    198     */
    199     if (SDL_numcds == 0) {
    200 
    201         fakeCD = SDL_TRUE;
    202         SDL_numcds = 1;
    203         status = CD_TRAYEMPTY;
    204 
    205         return 0;
    206     }
    207 
    208     /* Allocate space for volumes */
    209     volumes = (FSVolumeRefNum*) SDL_calloc (1, sizeof(*volumes) * SDL_numcds);
    210     if (volumes == NULL) {
    211         SDL_OutOfMemory ();
    212         return -1;
    213     }
    214 
    215     /* Allocate space for tracks */
    216     tracks = (FSRef**) SDL_calloc (1, sizeof(*tracks) * (SDL_numcds + 1));
    217     if (tracks == NULL) {
    218         SDL_OutOfMemory ();
    219         return -1;
    220     }
    221 
    222     /* Mark the end of the tracks array */
    223     tracks[ SDL_numcds ] = (FSRef*)-1;
    224 
    225     /*
    226         Redetect, now save all volumes for later
    227         Update SDL_numcds just in case it changed
    228     */
    229     {
    230         int numVolumes = SDL_numcds;
    231 
    232         SDL_numcds = DetectAudioCDVolumes (volumes, numVolumes);
    233 
    234         /* If more cds suddenly show up, ignore them */
    235         if (SDL_numcds > numVolumes) {
    236             SDL_SetError ("Some CD's were added but they will be ignored");
    237             SDL_numcds = numVolumes;
    238         }
    239     }
    240 
    241     return 0;
    242 }
    243 
    244 /* Shutdown and cleanup */
    245 void SDL_SYS_CDQuit(void)
    246 {
    247     ReleaseFile();
    248 
    249     if (volumes != NULL)
    250         free (volumes);
    251 
    252     if (tracks != NULL) {
    253 
    254         FSRef **ptr;
    255         for (ptr = tracks; *ptr != (FSRef*)-1; ptr++)
    256             if (*ptr != NULL)
    257                 free (*ptr);
    258 
    259         free (tracks);
    260     }
    261 }
    262 
    263 /* Get the Unix disk name of the volume */
    264 static const char *SDL_SYS_CDName (int drive)
    265 {
    266     /*
    267      * !!! FIXME: PBHGetVolParmsSync() is gone in 10.6,
    268      * !!! FIXME:  replaced with FSGetVolumeParms(), which
    269      * !!! FIXME:  isn't available before 10.5.  :/
    270      */
    271     return "Mac OS X CD-ROM Device";
    272 
    273 #if 0
    274     OSStatus     err = noErr;
    275     HParamBlockRec  pb;
    276     GetVolParmsInfoBuffer   volParmsInfo;
    277 
    278     if (fakeCD)
    279         return "Fake CD-ROM Device";
    280 
    281     pb.ioParam.ioNamePtr = NULL;
    282     pb.ioParam.ioVRefNum = volumes[drive];
    283     pb.ioParam.ioBuffer = (Ptr)&volParmsInfo;
    284     pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo);
    285     err = PBHGetVolParmsSync(&pb);
    286 
    287     if (err != noErr) {
    288         SDL_SetError ("PBHGetVolParmsSync returned %d", err);
    289         return NULL;
    290     }
    291 
    292     return volParmsInfo.vMDeviceID;
    293 #endif
    294 }
    295 
    296 /* Open the "device" */
    297 static int SDL_SYS_CDOpen (int drive)
    298 {
    299     /* Only allow 1 device to be open */
    300     if (currentDrive >= 0) {
    301         SDL_SetError ("Only one cdrom is supported");
    302         return -1;
    303     }
    304     else
    305         currentDrive = drive;
    306 
    307     return drive;
    308 }
    309 
    310 /* Get the table of contents */
    311 static int SDL_SYS_CDGetTOC (SDL_CD *cdrom)
    312 {
    313     if (fakeCD) {
    314         SDL_SetError (kErrorFakeDevice);
    315         return -1;
    316     }
    317 
    318     if (didReadTOC) {
    319         cdrom->numtracks = cacheTOCNumTracks;
    320         return 0;
    321     }
    322 
    323 
    324     ReadTOCData (volumes[cdrom->id], cdrom);
    325     didReadTOC = SDL_TRUE;
    326     cacheTOCNumTracks = cdrom->numtracks;
    327 
    328     return 0;
    329 }
    330 
    331 /* Get CD-ROM status */
    332 static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position)
    333 {
    334     if (position) {
    335         int trackFrame;
    336 
    337         Lock ();
    338         trackFrame = GetCurrentFrame ();
    339         Unlock ();
    340 
    341         *position = cdrom->track[currentTrack].offset + trackFrame;
    342     }
    343 
    344     return status;
    345 }
    346 
    347 /* Start playback */
    348 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
    349 {
    350     int startFrame, stopFrame;
    351     FSRef *ref;
    352 
    353     if (fakeCD) {
    354         SDL_SetError (kErrorFakeDevice);
    355         return -1;
    356     }
    357 
    358     Lock();
    359 
    360     if (LoadTracks (cdrom) < 0)
    361         return -2;
    362 
    363     if (PauseFile () < 0)
    364         return -3;
    365 
    366     if (ReleaseFile () < 0)
    367         return -4;
    368 
    369     ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame);
    370     if (ref == NULL) {
    371         SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length);
    372         return -5;
    373     }
    374 
    375     if (LoadFile (ref, startFrame, stopFrame) < 0)
    376         return -6;
    377 
    378     SetCompletionProc (CompletionProc, cdrom);
    379 
    380     if (PlayFile () < 0)
    381         return -7;
    382 
    383     status = CD_PLAYING;
    384 
    385     Unlock();
    386 
    387     return 0;
    388 }
    389 
    390 /* Pause playback */
    391 static int SDL_SYS_CDPause(SDL_CD *cdrom)
    392 {
    393     if (fakeCD) {
    394         SDL_SetError (kErrorFakeDevice);
    395         return -1;
    396     }
    397 
    398     Lock ();
    399 
    400     if (PauseFile () < 0) {
    401         Unlock ();
    402         return -2;
    403     }
    404 
    405     status = CD_PAUSED;
    406 
    407     Unlock ();
    408 
    409     return 0;
    410 }
    411 
    412 /* Resume playback */
    413 static int SDL_SYS_CDResume(SDL_CD *cdrom)
    414 {
    415     if (fakeCD) {
    416         SDL_SetError (kErrorFakeDevice);
    417         return -1;
    418     }
    419 
    420     Lock ();
    421 
    422     if (PlayFile () < 0) {
    423         Unlock ();
    424         return -2;
    425     }
    426 
    427     status = CD_PLAYING;
    428 
    429     Unlock ();
    430 
    431     return 0;
    432 }
    433 
    434 /* Stop playback */
    435 static int SDL_SYS_CDStop(SDL_CD *cdrom)
    436 {
    437     if (fakeCD) {
    438         SDL_SetError (kErrorFakeDevice);
    439         return -1;
    440     }
    441 
    442     Lock ();
    443 
    444     if (PauseFile () < 0) {
    445         Unlock ();
    446         return -2;
    447     }
    448 
    449     if (ReleaseFile () < 0) {
    450         Unlock ();
    451         return -3;
    452     }
    453 
    454     status = CD_STOPPED;
    455 
    456     Unlock ();
    457 
    458     return 0;
    459 }
    460 
    461 /* Eject the CD-ROM (Unmount the volume) */
    462 static int SDL_SYS_CDEject(SDL_CD *cdrom)
    463 {
    464     OSStatus err;
    465     pid_t dissenter;
    466 
    467     if (fakeCD) {
    468         SDL_SetError (kErrorFakeDevice);
    469         return -1;
    470     }
    471 
    472     Lock ();
    473 
    474     if (PauseFile () < 0) {
    475         Unlock ();
    476         return -2;
    477     }
    478 
    479     if (ReleaseFile () < 0) {
    480         Unlock ();
    481         return -3;
    482     }
    483 
    484     status = CD_STOPPED;
    485 
    486 	/* Eject the volume */
    487 	err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter);
    488 
    489 	if (err != noErr) {
    490         Unlock ();
    491 		SDL_SetError ("PBUnmountVol returned %d", err);
    492 		return -4;
    493 	}
    494 
    495     status = CD_TRAYEMPTY;
    496 
    497     /* Invalidate volume and track info */
    498     volumes[cdrom->id] = 0;
    499     free (tracks[cdrom->id]);
    500     tracks[cdrom->id] = NULL;
    501 
    502     Unlock ();
    503 
    504     return 0;
    505 }
    506 
    507 /* Close the CD-ROM */
    508 static void SDL_SYS_CDClose(SDL_CD *cdrom)
    509 {
    510     currentDrive = -1;
    511     return;
    512 }
    513 
    514 #endif /* SDL_CDROM_MACOSX */
    515