SDL-1.2.14
[sdl_omap.git] / src / cdrom / macosx / SDL_syscdrom.c
CommitLineData
e14743d1 1/*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 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
30static FSRef** tracks;
31static FSVolumeRefNum* volumes;
32static CDstatus status;
33static int nextTrackFrame;
34static int nextTrackFramesRemaining;
35static int fakeCD;
36static int currentTrack;
37static int didReadTOC;
38static int cacheTOCNumTracks;
39static int currentDrive; /* Only allow 1 drive in use at a time */
40
41#pragma mark -- Prototypes --
42
43static const char *SDL_SYS_CDName (int drive);
44static int SDL_SYS_CDOpen (int drive);
45static int SDL_SYS_CDGetTOC (SDL_CD *cdrom);
46static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position);
47static int SDL_SYS_CDPlay (SDL_CD *cdrom, int start, int length);
48static int SDL_SYS_CDPause (SDL_CD *cdrom);
49static int SDL_SYS_CDResume (SDL_CD *cdrom);
50static int SDL_SYS_CDStop (SDL_CD *cdrom);
51static int SDL_SYS_CDEject (SDL_CD *cdrom);
52static void SDL_SYS_CDClose (SDL_CD *cdrom);
53
54#pragma mark -- Helper Functions --
55
56/* Read a list of tracks from the volume */
57static 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 */
78static 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) */
113static 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 */
157int 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 */
245void 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 */
264static 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" */
297static 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 */
311static 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 */
332static 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 */
348static 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 */
391static 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 */
413static 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 */
435static 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) */
462static 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 */
508static void SDL_SYS_CDClose(SDL_CD *cdrom)
509{
510 currentDrive = -1;
511 return;
512}
513
514#endif /* SDL_CDROM_MACOSX */