1 /*
2  * Copyright (C) 1997-2001 Id Software, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * 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 along with
16  * this program; if not, write to the Free Software Foundation, Inc., 59
17  * 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 #if defined(CDAUDIO)
24 
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <sys/ioctl.h>
29 #include <sys/file.h>
30 #include <sys/types.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <linux/cdrom.h>
36 
37 #include "../client/client.h"
38 
39 static qboolean	cdValid = false;
40 static qboolean	playing = false;
41 static qboolean	wasPlaying = false;
42 static qboolean	initialized = false;
43 static qboolean	enabled = true;
44 static qboolean	playLooping = false;
45 static float	cdvolume;
46 static byte	remap[100];
47 static byte	playTrack;
48 static byte	maxTrack;
49 
50 static int	cdfile = -1;
51 
52 /* static char cd_dev[64] = "/dev/cdrom"; */
53 
54 cvar_t         *cd_volume;
55 cvar_t         *cd_nocd;
56 cvar_t         *cd_dev;
57 
58 void		CDAudio_Pause(void);
59 
60 static void
CDAudio_Eject(void)61 CDAudio_Eject(void)
62 {
63 	if (cdfile == -1 || !enabled)
64 		return;		/* no cd init'd */
65 	if (ioctl(cdfile, CDROMEJECT) == -1)
66 		Com_DPrintf("ioctl cdromeject failed\n");
67 }
68 
69 
70 static void
CDAudio_CloseDoor(void)71 CDAudio_CloseDoor(void)
72 {
73 	if (cdfile == -1 || !enabled)
74 		return;		/* no cd init'd */
75 
76 	if (ioctl(cdfile, CDROMCLOSETRAY) == -1)
77 		Com_DPrintf("ioctl cdromclosetray failed\n");
78 }
79 
80 static int
CDAudio_GetAudioDiskInfo(void)81 CDAudio_GetAudioDiskInfo(void)
82 {
83 	struct cdrom_tochdr tochdr;
84 
85 	cdValid = false;
86 	if (ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1) {
87 		Com_DPrintf("ioctl cdromreadtochdr failed\n");
88 		return -1;
89 	}
90 	if (tochdr.cdth_trk0 < 1) {
91 		Com_DPrintf("CDAudio: no music tracks\n");
92 		return -1;
93 	}
94 	cdValid = true;
95 	maxTrack = tochdr.cdth_trk1;
96 
97 	return 0;
98 }
99 
100 
101 void
CDAudio_Play(int track,qboolean looping)102 CDAudio_Play(int track, qboolean looping)
103 {
104 	struct cdrom_tocentry entry;
105 	struct cdrom_ti	ti;
106 
107 	if (cdfile == -1 || !enabled)
108 		return;
109 
110 	if (!cdValid) {
111 		CDAudio_GetAudioDiskInfo();
112 		if (!cdValid)
113 			return;
114 	}
115 	track = remap[track];
116 
117 	if (track < 1 || track > maxTrack) {
118 		Com_DPrintf("CDAudio: Bad track number %u.\n", track);
119 		return;
120 	}
121 	/* don't try to play a non-audio track */
122 	entry.cdte_track = track;
123 	entry.cdte_format = CDROM_LBA;
124 	if (ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1) {
125 		Com_DPrintf("ioctl cdromreadtocentry failed\n");
126 		return;
127 	}
128 	if (entry.cdte_ctrl == CDROM_DATA_TRACK) {
129 		Com_Printf("CDAudio: track %i is not audio\n", track);
130 		return;
131 	}
132 	if (playing) {
133 		if (playTrack == track)
134 			return;
135 		CDAudio_Stop();
136 	}
137 	ti.cdti_trk0 = track;
138 	ti.cdti_trk1 = track;
139 	ti.cdti_ind0 = 0;
140 	ti.cdti_ind1 = 0;
141 
142 	if (ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1) {
143 		Com_DPrintf("ioctl cdromplaytrkind failed\n");
144 		return;
145 	}
146 	if (ioctl(cdfile, CDROMRESUME) == -1)
147 		Com_DPrintf("ioctl cdromresume failed\n");
148 
149 	playLooping = looping;
150 	playTrack = track;
151 	playing = true;
152 
153 	if (cd_volume->value == 0.0)
154 		CDAudio_Pause();
155 }
156 
157 void
CDAudio_RandomPlay(void)158 CDAudio_RandomPlay(void)
159 {
160 	int		track, i = 0, free_tracks = 0, remap_track;
161 	float		f;
162 	byte           *track_bools;
163 	struct cdrom_tocentry entry;
164 	struct cdrom_ti	ti;
165 
166 	if (cdfile == -1 || !enabled)
167 		return;
168 
169 	track_bools = (byte *) Q_malloc(maxTrack * sizeof(byte));
170 
171 	if (track_bools == 0)
172 		return;
173 
174 	/* create array of available audio tracknumbers */
175 
176 	for (; i < maxTrack; i++) {
177 		entry.cdte_track = remap[i];
178 		entry.cdte_format = CDROM_LBA;
179 		if (ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1) {
180 			track_bools[i] = 0;
181 		} else
182 			track_bools[i] = (entry.cdte_ctrl != CDROM_DATA_TRACK);
183 
184 		free_tracks += track_bools[i];
185 	}
186 
187 	if (!free_tracks) {
188 		Com_DPrintf("CDAudio_RandomPlay: Unable to find and play a random audio track, insert an audio cd please");
189 		goto free_end;
190 	}
191 	/* choose random audio track */
192 	do {
193 		do {
194 			f = ((float)rand()) / ((float)RAND_MAX + 1.0);
195 			track = (int)(maxTrack * f);
196 		}
197 		while (!track_bools[track]);
198 
199 		remap_track = remap[track];
200 
201 		if (playing) {
202 			if (playTrack == remap_track) {
203 				goto free_end;
204 			}
205 			CDAudio_Stop();
206 		}
207 		ti.cdti_trk0 = remap_track;
208 		ti.cdti_trk1 = remap_track;
209 		ti.cdti_ind0 = 0;
210 		ti.cdti_ind1 = 0;
211 
212 		if (ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1) {
213 			track_bools[track] = 0;
214 			free_tracks--;
215 		} else {
216 			playLooping = true;
217 			playTrack = remap_track;
218 			playing = true;
219 			break;
220 		}
221 	}
222 	while (free_tracks > 0);
223 
224 free_end:
225 	Q_free((void *)track_bools);
226 }
227 
228 void
CDAudio_Stop(void)229 CDAudio_Stop(void)
230 {
231 	if (cdfile == -1 || !enabled)
232 		return;
233 
234 	if (!playing)
235 		return;
236 
237 	if (ioctl(cdfile, CDROMSTOP) == -1)
238 		Com_DPrintf("ioctl cdromstop failed (%d)\n", errno);
239 
240 	wasPlaying = false;
241 	playing = false;
242 }
243 
244 void
CDAudio_Pause(void)245 CDAudio_Pause(void)
246 {
247 	if (cdfile == -1 || !enabled)
248 		return;
249 
250 	if (!playing)
251 		return;
252 	if (ioctl(cdfile, CDROMPAUSE) == -1)
253 		Com_DPrintf("ioctl cdrompause failed\n");
254 
255 	wasPlaying = playing;
256 	playing = false;
257 }
258 
259 
260 void
CDAudio_Resume(void)261 CDAudio_Resume(void)
262 {
263 	if (cdfile == -1 || !enabled)
264 		return;
265 
266 	if (!cdValid)
267 		return;
268 
269 	if (!wasPlaying)
270 		return;
271 
272 	if (ioctl(cdfile, CDROMRESUME) == -1)
273 		Com_DPrintf("ioctl cdromresume failed\n");
274 	playing = true;
275 }
276 
277 static void
CD_f(void)278 CD_f(void)
279 {
280 	char           *command;
281 	int		ret;
282 	int		n;
283 
284 	if (Cmd_Argc() < 2)
285 		return;
286 
287 	command = Cmd_Argv(1);
288 
289 	if (Q_strcasecmp(command, "on") == 0) {
290 		enabled = true;
291 		return;
292 	}
293 	if (Q_strcasecmp(command, "off") == 0) {
294 		if (playing)
295 			CDAudio_Stop();
296 		enabled = false;
297 		return;
298 	}
299 	if (Q_strcasecmp(command, "reset") == 0) {
300 		enabled = true;
301 		if (playing)
302 			CDAudio_Stop();
303 		for (n = 0; n < 100; n++)
304 			remap[n] = n;
305 		CDAudio_GetAudioDiskInfo();
306 		return;
307 	}
308 	if (Q_strcasecmp(command, "remap") == 0) {
309 		ret = Cmd_Argc() - 2;
310 		if (ret <= 0) {
311 			for (n = 1; n < 100; n++)
312 				if (remap[n] != n)
313 					Com_Printf("  %u -> %u\n", n, remap[n]);
314 			return;
315 		}
316 		for (n = 1; n <= ret; n++)
317 			remap[n] = atoi(Cmd_Argv(n + 1));
318 		return;
319 	}
320 	if (Q_strcasecmp(command, "close") == 0) {
321 		CDAudio_CloseDoor();
322 		return;
323 	}
324 	if (!cdValid) {
325 		CDAudio_GetAudioDiskInfo();
326 		if (!cdValid) {
327 			Com_Printf("No CD in player.\n");
328 			return;
329 		}
330 	}
331 	if (Q_strcasecmp(command, "play") == 0) {
332 		CDAudio_Play((byte) atoi(Cmd_Argv(2)), false);
333 		return;
334 	}
335 	if (Q_strcasecmp(command, "loop") == 0) {
336 		CDAudio_Play((byte) atoi(Cmd_Argv(2)), true);
337 		return;
338 	}
339 	if (Q_strcasecmp(command, "stop") == 0) {
340 		CDAudio_Stop();
341 		return;
342 	}
343 	if (Q_strcasecmp(command, "pause") == 0) {
344 		CDAudio_Pause();
345 		return;
346 	}
347 	if (Q_strcasecmp(command, "resume") == 0) {
348 		CDAudio_Resume();
349 		return;
350 	}
351 	if (Q_strcasecmp(command, "eject") == 0) {
352 		if (playing)
353 			CDAudio_Stop();
354 		CDAudio_Eject();
355 		cdValid = false;
356 		return;
357 	}
358 	if (!Q_strcasecmp(command, "random")) {
359 		CDAudio_RandomPlay();
360 		return;
361 	}
362 	if (Q_strcasecmp(command, "info") == 0) {
363 		Com_Printf("%u tracks\n", maxTrack);
364 		if (playing)
365 			Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
366 		else if (wasPlaying)
367 			Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
368 		Com_Printf("Volume is %f\n", cdvolume);
369 		return;
370 	}
371 }
372 
373 void
CDAudio_Update(void)374 CDAudio_Update(void)
375 {
376 	struct cdrom_subchnl subchnl;
377 	static time_t	lastchk;
378 
379 	if (cdfile == -1 || !enabled)
380 		return;
381 
382 	if (cd_volume && cd_volume->value != cdvolume) {
383 		if (cdvolume) {
384 			Cvar_SetValue("cd_volume", 0.0);
385 			cdvolume = cd_volume->value;
386 			CDAudio_Pause();
387 		} else {
388 			Cvar_SetValue("cd_volume", 1.0);
389 			cdvolume = cd_volume->value;
390 			CDAudio_Resume();
391 		}
392 	}
393 	if (playing && lastchk < time(NULL)) {
394 		lastchk = time(NULL) + 2;	/* two seconds between chks */
395 		subchnl.cdsc_format = CDROM_MSF;
396 		if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1) {
397 			Com_DPrintf("ioctl cdromsubchnl failed\n");
398 			playing = false;
399 			return;
400 		}
401 		if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
402 		    subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
403 			playing = false;
404 			if (playLooping)
405 				CDAudio_Play(playTrack, true);
406 		}
407 	}
408 }
409 
410 int
CDAudio_Init(void)411 CDAudio_Init(void)
412 {
413 	int		i;
414 	cvar_t         *cv;
415 	extern uid_t	saved_euid;
416 
417 	if (initialized)
418 		return 0;
419 
420 	cv = Cvar_Get("nocdaudio", "0", CVAR_NOSET);
421 	if (cv->value)
422 		return -1;
423 
424 	cd_nocd = Cvar_Get("cd_nocd", "0", CVAR_ARCHIVE);
425 	if (cd_nocd->value)
426 		return -1;
427 
428 	cd_volume = Cvar_Get("cd_volume", "1", CVAR_ARCHIVE);
429 
430 	cd_dev = Cvar_Get("cd_dev", "/dev/cdrom", CVAR_ARCHIVE);
431 
432 	seteuid(saved_euid);
433 
434 	cdfile = open(cd_dev->string, O_RDONLY | O_NONBLOCK | O_EXCL);
435 
436 	seteuid(getuid());
437 
438 	if (cdfile == -1) {
439 		Com_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev->string, errno);
440 
441 		cdfile = -1;
442 		return -1;
443 	}
444 	for (i = 0; i < 100; i++)
445 		remap[i] = i;
446 	initialized = true;
447 	enabled = true;
448 
449 	if (CDAudio_GetAudioDiskInfo()) {
450 		Com_Printf("CDAudio_Init: No CD in player.\n");
451 		cdValid = false;
452 	}
453 	Cmd_AddCommand("cd", CD_f);
454 
455 	Com_Printf("CD Audio Initialized\n");
456 
457 	return 0;
458 }
459 
460 void
CDAudio_Activate(qboolean active)461 CDAudio_Activate(qboolean active)
462 {
463 	if (active)
464 		CDAudio_Resume();
465 	else
466 		CDAudio_Pause();
467 }
468 
469 void
CDAudio_Shutdown(void)470 CDAudio_Shutdown(void)
471 {
472 	if (!initialized)
473 		return;
474 	CDAudio_Stop();
475 	close(cdfile);
476 	cdfile = -1;
477 	initialized = false;
478 }
479 
480 #endif /* CD_AUDIO */
481 
482