1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
21 // rights reserved.
22 
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <sys/ioctl.h>
27 #include <sys/file.h>
28 #include <sys/types.h>
29 #include <fcntl.h>
30 #include <string.h>
31 #include <time.h>
32 #include <errno.h>
33 
34 #include <sys/cdio.h>
35 
36 #include "quakedef.h"
37 
38 static qboolean cdValid = false;
39 static qboolean	playing = false;
40 static qboolean	wasPlaying = false;
41 static qboolean	initialized = false;
42 static qboolean	enabled = true;
43 static qboolean playLooping = false;
44 static float	cdvolume;
45 static byte 	remap[100];
46 static byte		playTrack;
47 static byte		maxTrack;
48 
49 static int cdfile = -1;
50 static char cd_dev[64] = "/dev/cdrom";
51 
CDAudio_Eject(void)52 static void CDAudio_Eject(void)
53 {
54 	if (cdfile == -1 || !enabled)
55 		return; // no cd init'd
56 
57 	if ( ioctl(cdfile, CDIOCEJECT) == -1 )
58 		Con_DPrintf("ioctl CDIOCEJECT failed\n");
59 }
60 
61 
CDAudio_CloseDoor(void)62 static void CDAudio_CloseDoor(void)
63 {
64 	if (cdfile == -1 || !enabled)
65 		return; // no cd init'd
66 
67 	if ( ioctl(cdfile, CDIOCCLOSE) == -1 )
68 		Con_DPrintf("ioctl CDIOCCLOSE failed\n");
69 }
70 
CDAudio_GetAudioDiskInfo(void)71 static int CDAudio_GetAudioDiskInfo(void)
72 {
73 	struct ioc_toc_header tochdr;
74 
75 	cdValid = false;
76 
77 	if ( ioctl(cdfile, CDIOREADTOCHEADER, &tochdr) == -1 )
78     {
79       Con_DPrintf("ioctl CDIOREADTOCHEADER failed\n");
80 	  return -1;
81     }
82 
83 	if (tochdr.starting_track < 1)
84 	{
85 		Con_DPrintf("CDAudio: no music tracks\n");
86 		return -1;
87 	}
88 
89 	cdValid = true;
90 	maxTrack = tochdr.ending_track;
91 
92 	return 0;
93 }
94 
95 
CDAudio_Play(byte track,qboolean looping)96 void CDAudio_Play(byte track, qboolean looping)
97 {
98         struct ioc_read_toc_single_entry entry;
99         struct ioc_play_track ti;
100 
101 	if (cdfile == -1 || !enabled)
102 		return;
103 
104 	if (!cdValid)
105 	{
106 		CDAudio_GetAudioDiskInfo();
107 		if (!cdValid)
108 			return;
109 	}
110 
111 	track = remap[track];
112 
113 	if (track < 1 || track > maxTrack)
114 	{
115 		Con_DPrintf("CDAudio: Bad track number %u.\n", track);
116 		return;
117 	}
118 /*
119 	// don't try to play a non-audio track
120 	entry.cdte_track = track;
121 	entry.cdte_format = CDROM_MSF;
122     if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
123 	{
124 		Con_DPrintf("ioctl cdromreadtocentry failed\n");
125 		return;
126 	}
127 	if (entry.cdte_ctrl == CDROM_DATA_TRACK)
128 	{
129 		Con_Printf("CDAudio: track %i is not audio\n", track);
130 		return;
131 	}
132 */
133 	if (playing)
134 	{
135 		if (playTrack == track)
136 			return;
137 		CDAudio_Stop();
138 	}
139 
140         ti.start_track = track;
141         ti.end_track = track;
142         ti.start_index = 1;
143         ti.end_index = 99;
144 
145 	if ( ioctl(cdfile, CDIOCPLAYTRACKS, &ti) == -1 )
146     {
147 		Con_DPrintf("ioctl CDIOCPLAYTRACKS failed\n");
148 		return;
149     }
150 
151 	if ( ioctl(cdfile, CDIOCRESUME) == -1 )
152 		Con_DPrintf("ioctl CDIOCRESUME failed\n");
153 
154 	playLooping = looping;
155 	playTrack = track;
156 	playing = true;
157 
158 	if (cdvolume == 0.0)
159 		CDAudio_Pause ();
160 }
161 
162 
CDAudio_Stop(void)163 void CDAudio_Stop(void)
164 {
165 	if (cdfile == -1 || !enabled)
166 		return;
167 
168 	if (!playing)
169 		return;
170 
171 	if ( ioctl(cdfile, CDIOCSTOP) == -1 )
172 		Con_DPrintf("ioctl CDIOCSTOP failed (%d)\n", errno);
173 
174 	wasPlaying = false;
175 	playing = false;
176 }
177 
CDAudio_Pause(void)178 void CDAudio_Pause(void)
179 {
180 	if (cdfile == -1 || !enabled)
181 		return;
182 
183 	if (!playing)
184 		return;
185 
186 	if ( ioctl(cdfile, CDIOCPAUSE) == -1 )
187 		Con_DPrintf("ioctl CDIOCPAUSE failed\n");
188 
189 	wasPlaying = playing;
190 	playing = false;
191 }
192 
193 
CDAudio_Resume(void)194 void CDAudio_Resume(void)
195 {
196 	if (cdfile == -1 || !enabled)
197 		return;
198 
199 	if (!cdValid)
200 		return;
201 
202 	if (!wasPlaying)
203 		return;
204 
205 	if ( ioctl(cdfile, CDIOCRESUME) == -1 )
206 		Con_DPrintf("ioctl CDIOCRESUME failed\n");
207 	playing = true;
208 }
209 
CD_f(void)210 static void CD_f (void)
211 {
212 	char	*command;
213 	int		ret;
214 	int		n;
215 
216 	if (Cmd_Argc() < 2)
217 		return;
218 
219 	command = Cmd_Argv (1);
220 
221 	if (Q_strcasecmp(command, "on") == 0)
222 	{
223 		enabled = true;
224 		return;
225 	}
226 
227 	if (Q_strcasecmp(command, "off") == 0)
228 	{
229 		if (playing)
230 			CDAudio_Stop();
231 		enabled = false;
232 		return;
233 	}
234 
235 	if (Q_strcasecmp(command, "reset") == 0)
236 	{
237 		enabled = true;
238 		if (playing)
239 			CDAudio_Stop();
240 		for (n = 0; n < 100; n++)
241 			remap[n] = n;
242 		CDAudio_GetAudioDiskInfo();
243 		return;
244 	}
245 
246 	if (Q_strcasecmp(command, "remap") == 0)
247 	{
248 		ret = Cmd_Argc() - 2;
249 		if (ret <= 0)
250 		{
251 			for (n = 1; n < 100; n++)
252 				if (remap[n] != n)
253 					Con_Printf("  %u -> %u\n", n, remap[n]);
254 			return;
255 		}
256 		for (n = 1; n <= ret; n++)
257 			remap[n] = Q_atoi(Cmd_Argv (n+1));
258 		return;
259 	}
260 
261 	if (Q_strcasecmp(command, "close") == 0)
262 	{
263 		CDAudio_CloseDoor();
264 		return;
265 	}
266 
267 	if (!cdValid)
268 	{
269 		CDAudio_GetAudioDiskInfo();
270 		if (!cdValid)
271 		{
272 			Con_Printf("No CD in player.\n");
273 			return;
274 		}
275 	}
276 
277 	if (Q_strcasecmp(command, "play") == 0)
278 	{
279 		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), false);
280 		return;
281 	}
282 
283 	if (Q_strcasecmp(command, "loop") == 0)
284 	{
285 		CDAudio_Play((byte)Q_atoi(Cmd_Argv (2)), true);
286 		return;
287 	}
288 
289 	if (Q_strcasecmp(command, "stop") == 0)
290 	{
291 		CDAudio_Stop();
292 		return;
293 	}
294 
295 	if (Q_strcasecmp(command, "pause") == 0)
296 	{
297 		CDAudio_Pause();
298 		return;
299 	}
300 
301 	if (Q_strcasecmp(command, "resume") == 0)
302 	{
303 		CDAudio_Resume();
304 		return;
305 	}
306 
307 	if (Q_strcasecmp(command, "eject") == 0)
308 	{
309 		if (playing)
310 			CDAudio_Stop();
311 		CDAudio_Eject();
312 		cdValid = false;
313 		return;
314 	}
315 
316 	if (Q_strcasecmp(command, "info") == 0)
317 	{
318 		Con_Printf("%u tracks\n", maxTrack);
319 		if (playing)
320 			Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
321 		else if (wasPlaying)
322 			Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
323 		Con_Printf("Volume is %f\n", cdvolume);
324 		return;
325 	}
326 }
327 
CDAudio_Update(void)328 void CDAudio_Update(void)
329 {
330         struct ioc_read_subchannel subchnl;
331         struct cd_sub_channel_info data;
332 	static time_t lastchk;
333 
334 	if (!enabled)
335 		return;
336 
337 	if (bgmvolume.value != cdvolume)
338 	{
339 		if (cdvolume)
340 		{
341 			Cvar_SetValue ("bgmvolume", 0.0);
342 			cdvolume = bgmvolume.value;
343 			CDAudio_Pause ();
344 		}
345 		else
346 		{
347 			Cvar_SetValue ("bgmvolume", 1.0);
348 			cdvolume = bgmvolume.value;
349 			CDAudio_Resume ();
350 		}
351 	}
352 
353 	if (playing && lastchk < time(NULL)) {
354 		lastchk = time(NULL) + 2; //two seconds between chks
355                 subchnl.data = &data;
356                 subchnl.data_len = sizeof(data);
357                 subchnl.address_format = CD_MSF_FORMAT;
358                 subchnl.data_format = CD_CURRENT_POSITION;
359 		if (ioctl(cdfile, CDIOCREADSUBCHANNEL, (char*) &subchnl) == -1 ) {
360 			Con_DPrintf("ioctl CDIOCREADSUBCHANNEL failed\n");
361 			playing = false;
362 			return;
363 		}
364 		if (subchnl.data->header.audio_status != CD_AS_PLAY_IN_PROGRESS &&
365 			subchnl.data->header.audio_status != CD_AS_PLAY_PAUSED) {
366 			playing = false;
367 			if (playLooping)
368 				CDAudio_Play(playTrack, true);
369 		}
370 	}
371 }
372 
CDAudio_Init(void)373 int CDAudio_Init(void)
374 {
375 	int i;
376 
377 	if (cls.state == ca_dedicated)
378 		return -1;
379 
380 	if (COM_CheckParm("-nocdaudio"))
381 		return -1;
382 
383 	if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) {
384 		strncpy(cd_dev, com_argv[i + 1], sizeof(cd_dev));
385 		cd_dev[sizeof(cd_dev) - 1] = 0;
386 	}
387 
388 	if ((cdfile = open(cd_dev, O_RDONLY)) == -1) {
389 		Con_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev, errno);
390 		cdfile = -1;
391 		return -1;
392 	}
393 
394 	for (i = 0; i < 100; i++)
395 		remap[i] = i;
396 	initialized = true;
397 	enabled = true;
398 
399 	if (CDAudio_GetAudioDiskInfo())
400 	{
401 		Con_Printf("CDAudio_Init: No CD in player.\n");
402 		cdValid = false;
403 	}
404 
405 	Cmd_AddCommand ("cd", CD_f);
406 
407 	Con_Printf("CD Audio Initialized\n");
408 
409 	return 0;
410 }
411 
412 
CDAudio_Shutdown(void)413 void CDAudio_Shutdown(void)
414 {
415 	if (!initialized)
416 		return;
417 	CDAudio_Stop();
418 	close(cdfile);
419 	cdfile = -1;
420 }
421