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_MACOS |
25 | |
26 | /* MacOS functions for system-level CD-ROM audio control */ |
27 | |
28 | #include <Devices.h> |
29 | #include <Files.h> |
30 | #include <LowMem.h> /* Use entry table macros, not functions in InterfaceLib */ |
31 | |
32 | #include "SDL_cdrom.h" |
33 | #include "../SDL_syscdrom.h" |
34 | #include "SDL_syscdrom_c.h" |
35 | |
36 | /* Added by Matt Slot */ |
37 | #if !defined(LMGetUnitTableEntryCount) |
38 | #define LMGetUnitTableEntryCount() *(short *)0x01D2 |
39 | #endif |
40 | |
41 | /* The maximum number of CD-ROM drives we'll detect */ |
42 | #define MAX_DRIVES 26 |
43 | |
44 | /* A list of available CD-ROM drives */ |
45 | static long SDL_cdversion = 0; |
46 | static struct { |
47 | short dRefNum; |
48 | short driveNum; |
49 | long frames; |
50 | char name[256]; |
51 | Boolean hasAudio; |
52 | } SDL_cdlist[MAX_DRIVES]; |
53 | static StringPtr gDriverName = "\p.AppleCD"; |
54 | |
55 | /* The system-dependent CD control functions */ |
56 | static const char *SDL_SYS_CDName(int drive); |
57 | static int SDL_SYS_CDOpen(int drive); |
58 | static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); |
59 | static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); |
60 | static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); |
61 | static int SDL_SYS_CDPause(SDL_CD *cdrom); |
62 | static int SDL_SYS_CDResume(SDL_CD *cdrom); |
63 | static int SDL_SYS_CDStop(SDL_CD *cdrom); |
64 | static int SDL_SYS_CDEject(SDL_CD *cdrom); |
65 | static void SDL_SYS_CDClose(SDL_CD *cdrom); |
66 | |
67 | static short SDL_SYS_ShortToBCD(short value) |
68 | { |
69 | return((value % 10) + (value / 10) * 0x10); /* Convert value to BCD */ |
70 | } |
71 | |
72 | static short SDL_SYS_BCDToShort(short value) |
73 | { |
74 | return((value % 0x10) + (value / 0x10) * 10); /* Convert value from BCD */ |
75 | } |
76 | |
77 | int SDL_SYS_CDInit(void) |
78 | { |
79 | SInt16 dRefNum = 0; |
80 | SInt16 first, last; |
81 | |
82 | SDL_numcds = 0; |
83 | |
84 | /* Check that the software is available */ |
85 | if (Gestalt(kGestaltAudioCDSelector, &SDL_cdversion) || |
86 | !SDL_cdversion) return(0); |
87 | |
88 | /* Fill in our driver capabilities */ |
89 | SDL_CDcaps.Name = SDL_SYS_CDName; |
90 | SDL_CDcaps.Open = SDL_SYS_CDOpen; |
91 | SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; |
92 | SDL_CDcaps.Status = SDL_SYS_CDStatus; |
93 | SDL_CDcaps.Play = SDL_SYS_CDPlay; |
94 | SDL_CDcaps.Pause = SDL_SYS_CDPause; |
95 | SDL_CDcaps.Resume = SDL_SYS_CDResume; |
96 | SDL_CDcaps.Stop = SDL_SYS_CDStop; |
97 | SDL_CDcaps.Eject = SDL_SYS_CDEject; |
98 | SDL_CDcaps.Close = SDL_SYS_CDClose; |
99 | |
100 | /* Walk the list, count each AudioCD driver, and save the refnums */ |
101 | first = -1; |
102 | last = 0 - LMGetUnitTableEntryCount(); |
103 | for(dRefNum = first; dRefNum >= last; dRefNum--) { |
104 | Str255 driverName; |
105 | StringPtr namePtr; |
106 | DCtlHandle deviceEntry; |
107 | |
108 | deviceEntry = GetDCtlEntry(dRefNum); |
109 | if (! deviceEntry) continue; |
110 | |
111 | /* Is this an .AppleCD ? */ |
112 | namePtr = (*deviceEntry)->dCtlFlags & (1L << dRAMBased) ? |
113 | ((StringPtr) ((DCtlPtr) deviceEntry)->dCtlDriver + 18) : |
114 | ((StringPtr) (*deviceEntry)->dCtlDriver + 18); |
115 | BlockMoveData(namePtr, driverName, namePtr[0]+1); |
116 | if (driverName[0] > gDriverName[0]) driverName[0] = gDriverName[0]; |
117 | if (! EqualString(driverName, gDriverName, false, false)) continue; |
118 | |
119 | /* Record the basic info for each drive */ |
120 | SDL_cdlist[SDL_numcds].dRefNum = dRefNum; |
121 | BlockMoveData(namePtr + 1, SDL_cdlist[SDL_numcds].name, namePtr[0]); |
122 | SDL_cdlist[SDL_numcds].name[namePtr[0]] = 0; |
123 | SDL_cdlist[SDL_numcds].hasAudio = false; |
124 | SDL_numcds++; |
125 | } |
126 | return(0); |
127 | } |
128 | |
129 | static const char *SDL_SYS_CDName(int drive) |
130 | { |
131 | return(SDL_cdlist[drive].name); |
132 | } |
133 | |
134 | static int get_drivenum(int drive) |
135 | { |
136 | QHdr *driveQ = GetDrvQHdr(); |
137 | DrvQEl *driveElem; |
138 | |
139 | /* Update the drive number */ |
140 | SDL_cdlist[drive].driveNum = 0; |
141 | if ( driveQ->qTail ) { |
142 | driveQ->qTail->qLink = 0; |
143 | } |
144 | for ( driveElem=(DrvQEl *)driveQ->qHead; driveElem; |
145 | driveElem = (DrvQEl *)driveElem->qLink ) { |
146 | if ( driveElem->dQRefNum == SDL_cdlist[drive].dRefNum ) { |
147 | SDL_cdlist[drive].driveNum = driveElem->dQDrive; |
148 | break; |
149 | } |
150 | } |
151 | return(SDL_cdlist[drive].driveNum); |
152 | } |
153 | |
154 | static int SDL_SYS_CDOpen(int drive) |
155 | { |
156 | return(drive); |
157 | } |
158 | |
159 | static int SDL_SYS_CDGetTOC(SDL_CD *cdrom) |
160 | { |
161 | CDCntrlParam cdpb; |
162 | CDTrackData tracks[SDL_MAX_TRACKS]; |
163 | long i, leadout; |
164 | |
165 | /* Get the number of tracks on the CD by examining the TOC */ |
166 | SDL_memset(&cdpb, 0, sizeof(cdpb)); |
167 | cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; |
168 | cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; |
169 | cdpb.csCode = kReadTOC; |
170 | cdpb.csParam.words[0] = kGetTrackRange; |
171 | if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { |
172 | SDL_SetError("PBControlSync() failed"); |
173 | return(-1); |
174 | } |
175 | |
176 | cdrom->numtracks = |
177 | SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) - |
178 | SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1; |
179 | if ( cdrom->numtracks > SDL_MAX_TRACKS ) |
180 | cdrom->numtracks = SDL_MAX_TRACKS; |
181 | cdrom->status = CD_STOPPED; |
182 | cdrom->cur_track = 0; /* Apparently these are set elsewhere */ |
183 | cdrom->cur_frame = 0; /* Apparently these are set elsewhere */ |
184 | |
185 | |
186 | /* Get the lead out area of the CD by examining the TOC */ |
187 | SDL_memset(&cdpb, 0, sizeof(cdpb)); |
188 | cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; |
189 | cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; |
190 | cdpb.csCode = kReadTOC; |
191 | cdpb.csParam.words[0] = kGetLeadOutArea; |
192 | if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { |
193 | SDL_SetError("PBControlSync() failed"); |
194 | return(-1); |
195 | } |
196 | |
197 | leadout = MSF_TO_FRAMES( |
198 | SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]), |
199 | SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]), |
200 | SDL_SYS_BCDToShort(cdpb.csParam.bytes[2])); |
201 | |
202 | /* Get an array of track locations by examining the TOC */ |
203 | SDL_memset(tracks, 0, sizeof(tracks)); |
204 | SDL_memset(&cdpb, 0, sizeof(cdpb)); |
205 | cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; |
206 | cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; |
207 | cdpb.csCode = kReadTOC; |
208 | cdpb.csParam.words[0] = kGetTrackEntries; /* Type of Query */ |
209 | * ((long *) (cdpb.csParam.words+1)) = (long) tracks; |
210 | cdpb.csParam.words[3] = cdrom->numtracks * sizeof(tracks[0]); |
211 | * ((char *) (cdpb.csParam.words+4)) = 1; /* First track */ |
212 | if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { |
213 | SDL_SetError("PBControlSync() failed"); |
214 | return(-1); |
215 | } |
216 | |
217 | /* Read all the track TOC entries */ |
218 | SDL_cdlist[cdrom->id].hasAudio = false; |
219 | for ( i=0; i<cdrom->numtracks; ++i ) |
220 | { |
221 | cdrom->track[i].id = i+1; |
222 | if (tracks[i].entry.control & kDataTrackMask) |
223 | cdrom->track[i].type = SDL_DATA_TRACK; |
224 | else |
225 | { |
226 | cdrom->track[i].type = SDL_AUDIO_TRACK; |
227 | SDL_cdlist[SDL_numcds].hasAudio = true; |
228 | } |
229 | |
230 | cdrom->track[i].offset = MSF_TO_FRAMES( |
231 | SDL_SYS_BCDToShort(tracks[i].entry.min), |
232 | SDL_SYS_BCDToShort(tracks[i].entry.min), |
233 | SDL_SYS_BCDToShort(tracks[i].entry.frame)); |
234 | cdrom->track[i].length = MSF_TO_FRAMES( |
235 | SDL_SYS_BCDToShort(tracks[i+1].entry.min), |
236 | SDL_SYS_BCDToShort(tracks[i+1].entry.min), |
237 | SDL_SYS_BCDToShort(tracks[i+1].entry.frame)) - |
238 | cdrom->track[i].offset; |
239 | } |
240 | |
241 | /* Apparently SDL wants a fake last entry */ |
242 | cdrom->track[i].offset = leadout; |
243 | cdrom->track[i].length = 0; |
244 | |
245 | return(0); |
246 | } |
247 | |
248 | /* Get CD-ROM status */ |
249 | static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position) |
250 | { |
251 | CDCntrlParam cdpb; |
252 | CDstatus status = CD_ERROR; |
253 | Boolean spinning = false; |
254 | |
255 | if (position) *position = 0; |
256 | |
257 | /* Get the number of tracks on the CD by examining the TOC */ |
258 | if ( ! get_drivenum(cdrom->id) ) { |
259 | return(CD_TRAYEMPTY); |
260 | } |
261 | SDL_memset(&cdpb, 0, sizeof(cdpb)); |
262 | cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; |
263 | cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; |
264 | cdpb.csCode = kReadTOC; |
265 | cdpb.csParam.words[0] = kGetTrackRange; |
266 | if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { |
267 | SDL_SetError("PBControlSync() failed"); |
268 | return(CD_ERROR); |
269 | } |
270 | |
271 | cdrom->numtracks = |
272 | SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) - |
273 | SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1; |
274 | if ( cdrom->numtracks > SDL_MAX_TRACKS ) |
275 | cdrom->numtracks = SDL_MAX_TRACKS; |
276 | cdrom->cur_track = 0; /* Apparently these are set elsewhere */ |
277 | cdrom->cur_frame = 0; /* Apparently these are set elsewhere */ |
278 | |
279 | |
280 | if (1 || SDL_cdlist[cdrom->id].hasAudio) { |
281 | /* Get the current playback status */ |
282 | SDL_memset(&cdpb, 0, sizeof(cdpb)); |
283 | cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; |
284 | cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; |
285 | cdpb.csCode = kAudioStatus; |
286 | if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) { |
287 | SDL_SetError("PBControlSync() failed"); |
288 | return(-1); |
289 | } |
290 | |
291 | switch(cdpb.csParam.cd.status) { |
292 | case kStatusPlaying: |
293 | status = CD_PLAYING; |
294 | spinning = true; |
295 | break; |
296 | case kStatusPaused: |
297 | status = CD_PAUSED; |
298 | spinning = true; |
299 | break; |
300 | case kStatusMuted: |
301 | status = CD_PLAYING; /* What should I do here? */ |
302 | spinning = true; |
303 | break; |
304 | case kStatusDone: |
305 | status = CD_STOPPED; |
306 | spinning = true; |
307 | break; |
308 | case kStatusStopped: |
309 | status = CD_STOPPED; |
310 | spinning = false; |
311 | break; |
312 | case kStatusError: |
313 | default: |
314 | status = CD_ERROR; |
315 | spinning = false; |
316 | break; |
317 | } |
318 | |
319 | if (spinning && position) *position = MSF_TO_FRAMES( |
320 | SDL_SYS_BCDToShort(cdpb.csParam.cd.minute), |
321 | SDL_SYS_BCDToShort(cdpb.csParam.cd.second), |
322 | SDL_SYS_BCDToShort(cdpb.csParam.cd.frame)); |
323 | } |
324 | else |
325 | status = CD_ERROR; /* What should I do here? */ |
326 | |
327 | return(status); |
328 | } |
329 | |
330 | /* Start play */ |
331 | static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) |
332 | { |
333 | CDCntrlParam cdpb; |
334 | |
335 | /* Pause the current audio playback to avoid audible artifacts */ |
336 | if ( SDL_SYS_CDPause(cdrom) < 0 ) { |
337 | return(-1); |
338 | } |
339 | |
340 | /* Specify the AudioCD playback mode */ |
341 | SDL_memset(&cdpb, 0, sizeof(cdpb)); |
342 | cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum; |
343 | cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum; |
344 | cdpb.csCode = kSetPlayMode; |
345 | cdpb.csParam.bytes[0] = false; /* Repeat? */ |
346 | cdpb.csParam.bytes[1] = kPlayModeSequential; /* Play mode */ |
347 |