• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 */
LoadTracks(SDL_CD * cdrom)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 */
GetFileForOffset(SDL_CD * cdrom,int start,int length,int * outStartFrame,int * outStopFrame)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) */
CompletionProc(SDL_CD * cdrom)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 */
SDL_SYS_CDInit(void)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 */
SDL_SYS_CDQuit(void)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 */
SDL_SYS_CDName(int drive)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" */
SDL_SYS_CDOpen(int drive)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 */
SDL_SYS_CDGetTOC(SDL_CD * cdrom)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 */
SDL_SYS_CDStatus(SDL_CD * cdrom,int * position)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 */
SDL_SYS_CDPlay(SDL_CD * cdrom,int start,int length)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 */
SDL_SYS_CDPause(SDL_CD * cdrom)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 */
SDL_SYS_CDResume(SDL_CD * cdrom)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 */
SDL_SYS_CDStop(SDL_CD * cdrom)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) */
SDL_SYS_CDEject(SDL_CD * cdrom)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 */
SDL_SYS_CDClose(SDL_CD * cdrom)508 static void SDL_SYS_CDClose(SDL_CD *cdrom)
509 {
510     currentDrive = -1;
511     return;
512 }
513 
514 #endif /* SDL_CDROM_MACOSX */
515