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