Home | History | Annotate | Download | only in macosx
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2006 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     OSStatus     err = noErr;
    267     HParamBlockRec  pb;
    268     GetVolParmsInfoBuffer   volParmsInfo;
    269 
    270     if (fakeCD)
    271         return "Fake CD-ROM Device";
    272 
    273     pb.ioParam.ioNamePtr = NULL;
    274     pb.ioParam.ioVRefNum = volumes[drive];
    275     pb.ioParam.ioBuffer = (Ptr)&volParmsInfo;
    276     pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo);
    277     err = PBHGetVolParmsSync(&pb);
    278 
    279     if (err != noErr) {
    280         SDL_SetError ("PBHGetVolParmsSync returned %d", err);
    281         return NULL;
    282     }
    283 
    284     return volParmsInfo.vMDeviceID;
    285 }
    286 
    287 /* Open the "device" */
    288 static int SDL_SYS_CDOpen (int drive)
    289 {
    290     /* Only allow 1 device to be open */
    291     if (currentDrive >= 0) {
    292         SDL_SetError ("Only one cdrom is supported");
    293         return -1;
    294     }
    295     else
    296         currentDrive = drive;
    297 
    298     return drive;
    299 }
    300 
    301 /* Get the table of contents */
    302 static int SDL_SYS_CDGetTOC (SDL_CD *cdrom)
    303 {
    304     if (fakeCD) {
    305         SDL_SetError (kErrorFakeDevice);
    306         return -1;
    307     }
    308 
    309     if (didReadTOC) {
    310         cdrom->numtracks = cacheTOCNumTracks;
    311         return 0;
    312     }
    313 
    314 
    315     ReadTOCData (volumes[cdrom->id], cdrom);
    316     didReadTOC = SDL_TRUE;
    317     cacheTOCNumTracks = cdrom->numtracks;
    318 
    319     return 0;
    320 }
    321 
    322 /* Get CD-ROM status */
    323 static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position)
    324 {
    325     if (position) {
    326         int trackFrame;
    327 
    328         Lock ();
    329         trackFrame = GetCurrentFrame ();
    330         Unlock ();
    331 
    332         *position = cdrom->track[currentTrack].offset + trackFrame;
    333     }
    334 
    335     return status;
    336 }
    337 
    338 /* Start playback */
    339 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
    340 {
    341     int startFrame, stopFrame;
    342     FSRef *ref;
    343 
    344     if (fakeCD) {
    345         SDL_SetError (kErrorFakeDevice);
    346         return -1;
    347     }
    348 
    349     Lock();
    350 
    351     if (LoadTracks (cdrom) < 0)
    352         return -2;
    353 
    354     if (PauseFile () < 0)
    355         return -3;
    356 
    357     if (ReleaseFile () < 0)
    358         return -4;
    359 
    360     ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame);
    361     if (ref == NULL) {
    362         SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length);
    363         return -5;
    364     }
    365 
    366     if (LoadFile (ref, startFrame, stopFrame) < 0)
    367         return -6;
    368 
    369     SetCompletionProc (CompletionProc, cdrom);
    370 
    371     if (PlayFile () < 0)
    372         return -7;
    373 
    374     status = CD_PLAYING;
    375 
    376     Unlock();
    377 
    378     return 0;
    379 }
    380 
    381 /* Pause playback */
    382 static int SDL_SYS_CDPause(SDL_CD *cdrom)
    383 {
    384     if (fakeCD) {
    385         SDL_SetError (kErrorFakeDevice);
    386         return -1;
    387     }
    388 
    389     Lock ();
    390 
    391     if (PauseFile () < 0) {
    392         Unlock ();
    393         return -2;
    394     }
    395 
    396     status = CD_PAUSED;
    397 
    398     Unlock ();
    399 
    400     return 0;
    401 }
    402 
    403 /* Resume playback */
    404 static int SDL_SYS_CDResume(SDL_CD *cdrom)
    405 {
    406     if (fakeCD) {
    407         SDL_SetError (kErrorFakeDevice);
    408         return -1;
    409     }
    410 
    411     Lock ();
    412 
    413     if (PlayFile () < 0) {
    414         Unlock ();
    415         return -2;
    416     }
    417 
    418     status = CD_PLAYING;
    419 
    420     Unlock ();
    421 
    422     return 0;
    423 }
    424 
    425 /* Stop playback */
    426 static int SDL_SYS_CDStop(SDL_CD *cdrom)
    427 {
    428     if (fakeCD) {
    429         SDL_SetError (kErrorFakeDevice);
    430         return -1;
    431     }
    432 
    433     Lock ();
    434 
    435     if (PauseFile () < 0) {
    436         Unlock ();
    437         return -2;
    438     }
    439 
    440     if (ReleaseFile () < 0) {
    441         Unlock ();
    442         return -3;
    443     }
    444 
    445     status = CD_STOPPED;
    446 
    447     Unlock ();
    448 
    449     return 0;
    450 }
    451 
    452 /* Eject the CD-ROM (Unmount the volume) */
    453 static int SDL_SYS_CDEject(SDL_CD *cdrom)
    454 {
    455     OSStatus err;
    456     pid_t dissenter;
    457 
    458     if (fakeCD) {
    459         SDL_SetError (kErrorFakeDevice);
    460         return -1;
    461     }
    462 
    463     Lock ();
    464 
    465     if (PauseFile () < 0) {
    466         Unlock ();
    467         return -2;
    468     }
    469 
    470     if (ReleaseFile () < 0) {
    471         Unlock ();
    472         return -3;
    473     }
    474 
    475     status = CD_STOPPED;
    476 
    477 	/* Eject the volume */
    478 	err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter);
    479 
    480 	if (err != noErr) {
    481         Unlock ();
    482 		SDL_SetError ("PBUnmountVol returned %d", err);
    483 		return -4;
    484 	}
    485 
    486     status = CD_TRAYEMPTY;
    487 
    488     /* Invalidate volume and track info */
    489     volumes[cdrom->id] = 0;
    490     free (tracks[cdrom->id]);
    491     tracks[cdrom->id] = NULL;
    492 
    493     Unlock ();
    494 
    495     return 0;
    496 }
    497 
    498 /* Close the CD-ROM */
    499 static void SDL_SYS_CDClose(SDL_CD *cdrom)
    500 {
    501     currentDrive = -1;
    502     return;
    503 }
    504 
    505 #endif /* SDL_CDROM_MACOSX */
    506