1 /*
2 Copyright (C) 1997-2001 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 #if defined(__FreeBSD__)
35   #include <sys/cdio.h>
36 #else
37   #include <linux/cdrom.h>
38 #endif
39 
40 #include "../client/client.h"
41 
42 static qboolean cdValid = false;
43 static qboolean	playing = false;
44 static qboolean	wasPlaying = false;
45 static qboolean	initialized = false;
46 static qboolean	enabled = true;
47 static qboolean playLooping = false;
48 static float	cdvolume;
49 static byte 	remap[100];
50 static byte		playTrack;
51 static byte		maxTrack;
52 
53 static int cdfile = -1;
54 
55 //static char cd_dev[64] = "/dev/cdrom";
56 
57 cvar_t	*cd_volume;
58 cvar_t *cd_nocd;
59 cvar_t *cd_dev;
60 
61 void CDAudio_Pause(void);
62 
CDAudio_Eject(void)63 static void CDAudio_Eject(void)
64 {
65 	if (cdfile == -1 || !enabled)
66 		return; // no cd init'd
67 
68 #if defined(__FreeBSD__)
69 	if ( ioctl(cdfile, CDIOCEJECT) == -1 )
70 		Com_DPrintf("ioctl cdioeject failed\n");
71 #else
72 	if ( ioctl(cdfile, CDROMEJECT) == -1 )
73 		Com_DPrintf("ioctl cdromeject failed\n");
74 #endif
75 }
76 
77 
CDAudio_CloseDoor(void)78 static void CDAudio_CloseDoor(void)
79 {
80 	if (cdfile == -1 || !enabled)
81 		return; // no cd init'd
82 
83 #if defined(__FreeBSD__)
84 	if ( ioctl(cdfile, CDIOCCLOSE) == -1 )
85 		Com_DPrintf("ioctl cdiocclose failed\n");
86 #else
87 	if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 )
88 		Com_DPrintf("ioctl cdromclosetray failed\n");
89 #endif
90 }
91 
CDAudio_GetAudioDiskInfo(void)92 static int CDAudio_GetAudioDiskInfo(void)
93 {
94 #if defined(__FreeBSD__)
95 	struct ioc_toc_header tochdr;
96 #endif
97 #ifdef __linux__
98 	struct cdrom_tochdr tochdr;
99 #endif
100 
101 	cdValid = false;
102 
103 #if defined(__FreeBSD__)
104 	if ( ioctl(cdfile, CDIOREADTOCHEADER, &tochdr) == -1 )
105     {
106       Com_DPrintf("ioctl cdioreadtocheader failed\n");
107 #endif
108 #ifdef __linux__
109 	if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 )
110     {
111       Com_DPrintf("ioctl cdromreadtochdr failed\n");
112 #endif
113 	  return -1;
114     }
115 
116 #if defined(__FreeBSD__)
117 	if (tochdr.starting_track < 1)
118 #endif
119 #ifdef __linux__
120 	if (tochdr.cdth_trk0 < 1)
121 #endif
122 	{
123 		Com_DPrintf("CDAudio: no music tracks\n");
124 		return -1;
125 	}
126 
127 	cdValid = true;
128 #if defined(__FreeBSD__)
129 	maxTrack = tochdr.ending_track;
130 #endif
131 #ifdef __linux__
132 	maxTrack = tochdr.cdth_trk1;
133 #endif
134 
135 	return 0;
136 }
137 
138 
139 void CDAudio_Play(int track, qboolean looping)
140 {
141 #if defined(__FreeBSD__)
142 	struct ioc_read_toc_entry entry;
143 	struct cd_toc_entry toc_buffer;
144 	struct ioc_play_track ti;
145 #endif
146 #if defined(__linux__)
147 	struct cdrom_tocentry entry;
148 	struct cdrom_ti ti;
149 #endif
150 
151 	if (cdfile == -1 || !enabled)
152 		return;
153 
154 	if (!cdValid)
155 	{
156 		CDAudio_GetAudioDiskInfo();
157 		if (!cdValid)
158 			return;
159 	}
160 
161 	track = remap[track];
162 
163 	if (track < 1 || track > maxTrack)
164 	{
165 		Com_DPrintf("CDAudio: Bad track number %u.\n", track);
166 		return;
167 	}
168 
169 #if defined(__FreeBSD__)
170 	#define CDROM_DATA_TRACK 4
171 	bzero((char *)&toc_buffer, sizeof(toc_buffer));
172 	entry.data_len = sizeof(toc_buffer);
173 	entry.data = &toc_buffer;
174 	// don't try to play a non-audio track
175 	entry.starting_track = track;
176 	entry.address_format = CD_MSF_FORMAT;
177     if ( ioctl(cdfile, CDIOREADTOCENTRYS, &entry) == -1 )
178 	{
179 		Com_DPrintf("ioctl cdromreadtocentry failed\n");
180 		return;
181 	}
182 	if (toc_buffer.control == CDROM_DATA_TRACK)
183 #endif
184 #if defined(__linux__)
185 	// don't try to play a non-audio track
186 	entry.cdte_track = track;
187 	entry.cdte_format = CDROM_LBA;
188     if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
189 	{
190 		Com_DPrintf("ioctl cdromreadtocentry failed\n");
191 		return;
192 	}
193 	if (entry.cdte_ctrl == CDROM_DATA_TRACK)
194 #endif
195 	{
196 		Com_Printf("CDAudio: track %i is not audio\n", track);
197 		return;
198 	}
199 
200 
201 	if (playing)
202 	{
203 		if (playTrack == track)
204 			return;
205 		CDAudio_Stop();
206 	}
207 
208 #if defined(__FreeBSD__)
209 	ti.start_track = track;
210 	ti.end_track = track;
211 	ti.start_index = 1;
212 	ti.end_index = 99;
213 #endif
214 #if defined(__linux__)
215 	ti.cdti_trk0 = track;
216 	ti.cdti_trk1 = track;
217 	ti.cdti_ind0 = 0;
218 	ti.cdti_ind1 = 0;
219 #endif
220 
221 #if defined(__FreeBSD__)
222 	if ( ioctl(cdfile, CDIOCPLAYTRACKS, &ti) == -1 )
223 #endif
224 #if defined(__linux__)
225 	if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 )
226 #endif
227 	{
228 		Com_DPrintf("ioctl cdromplaytrkind failed\n");
229 		return;
230 	}
231 
232 #if defined(__FreeBSD__)
233 	if ( ioctl(cdfile, CDIOCRESUME) == -1 )
234 #endif
235 #if defined(__linux__)
236 	if ( ioctl(cdfile, CDROMRESUME) == -1 )
237 #endif
238 		Com_DPrintf("ioctl cdromresume failed\n");
239 
240 	playLooping = looping;
241 	playTrack = track;
242 	playing = true;
243 
244 	if (cd_volume->value == 0.0)
245 		CDAudio_Pause ();
246 }
247 
248 void CDAudio_RandomPlay(void)
249 {
250   int track, i = 0, free_tracks = 0, remap_track;
251   float f;
252   byte* track_bools;
253 #if defined(__FreeBSD__)
254   struct ioc_read_toc_entry entry;
255   struct cd_toc_entry toc_buffer;
256   struct ioc_play_track ti;
257 #endif
258 #if defined(__linux__)
259   struct cdrom_tocentry entry;
260   struct cdrom_ti ti;
261 #endif
262 
263   if (cdfile == -1 || !enabled)
264     return;
265 
266   track_bools = (byte*)malloc(maxTrack * sizeof(byte));
267 
268   if (track_bools == 0)
269     return;
270 
271   //create array of available audio tracknumbers
272 
273   for (; i < maxTrack; i++)
274     {
275 #if defined(__FreeBSD__)
276   #define CDROM_DATA_TRACK 4
277   bzero((char *)&toc_buffer, sizeof(toc_buffer));
278   entry.data_len = sizeof(toc_buffer);
279   entry.data = &toc_buffer;
280 
281 	entry.starting_track = remap[i];
282 	entry.address_format = CD_LBA_FORMAT;
283 	if ( ioctl(cdfile, CDIOREADTOCENTRYS, &entry) == -1 )
284 	{
285 	  track_bools[i] = 0;
286 	}
287 	else
288 	  track_bools[i] = (entry.data->control != CDROM_DATA_TRACK);
289 #endif
290 #if defined(__linux__)
291 	entry.cdte_track = remap[i];
292 	entry.cdte_format = CDROM_LBA;
293 	if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
294 	{
295 	  track_bools[i] = 0;
296 	}
297 	else
298 	  track_bools[i] = (entry.cdte_ctrl != CDROM_DATA_TRACK);
299 #endif
300 
301 	free_tracks += track_bools[i];
302     }
303 
304   if (!free_tracks)
305     {
306       Com_DPrintf("CDAudio_RandomPlay: Unable to find and play a random audio track, insert an audio cd please");
307       goto free_end;
308     }
309 
310   //choose random audio track
311   do
312     {
313       do
314 	{
315 	  f = ((float)rand()) / ((float)RAND_MAX + 1.0);
316 	  track = (int)(maxTrack  * f);
317 	}
318       while(!track_bools[track]);
319 
320       remap_track = remap[track];
321 
322       if (playing)
323 	{
324 	  if (playTrack == remap_track)
325 	    {
326 	      goto free_end;
327 	    }
328 	  CDAudio_Stop();
329 	}
330 
331 #if defined(__FreeBSD__)
332       #define CDROMPLAYTRKIND 0x5304
333 
334       ti.start_track = remap_track;
335       ti.end_track = remap_track;
336       ti.start_index = 0;
337       ti.end_index = 0;
338 #endif
339 #if defined(__linux__)
340       ti.cdti_trk0 = remap_track;
341       ti.cdti_trk1 = remap_track;
342       ti.cdti_ind0 = 0;
343       ti.cdti_ind1 = 0;
344 #endif
345 
346       if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 )
347 	{
348 	  track_bools[track] = 0;
349 	  free_tracks--;
350 	}
351       else
352 	{
353 	  playLooping = true;
354 	  playTrack = remap_track;
355 	  playing = true;
356 	  break;
357 	}
358     }
359   while (free_tracks > 0);
360 
361  free_end:
362     free((void*)track_bools);
363 }
364 
365 void CDAudio_Stop(void)
366 {
367 	if (cdfile == -1 || !enabled)
368 		return;
369 
370 	if (!playing)
371 		return;
372 
373 #if defined(__FreeBSD__)
374 	if ( ioctl(cdfile, CDIOCSTOP) == -1 )
375 		Com_DPrintf("ioctl cdiocstop failed (%d)\n", errno);
376 #endif
377 #if defined(__linux__)
378 	if ( ioctl(cdfile, CDROMSTOP) == -1 )
379 		Com_DPrintf("ioctl cdromstop failed (%d)\n", errno);
380 #endif
381 
382 	wasPlaying = false;
383 	playing = false;
384 }
385 
386 void CDAudio_Pause(void)
387 {
388 	if (cdfile == -1 || !enabled)
389 		return;
390 
391 	if (!playing)
392 		return;
393 
394 #if defined(__FreeBSD__)
395 	if ( ioctl(cdfile, CDIOCPAUSE) == -1 )
396 		Com_DPrintf("ioctl cdiocpause failed\n");
397 #endif
398 #if defined(__linux__)
399 	if ( ioctl(cdfile, CDROMPAUSE) == -1 )
400 		Com_DPrintf("ioctl cdrompause failed\n");
401 #endif
402 
403 	wasPlaying = playing;
404 	playing = false;
405 }
406 
407 
408 void CDAudio_Resume(void)
409 {
410 	if (cdfile == -1 || !enabled)
411 		return;
412 
413 	if (!cdValid)
414 		return;
415 
416 	if (!wasPlaying)
417 		return;
418 
419 #if defined(__FreeBSD__)
420 	if ( ioctl(cdfile, CDIOCRESUME) == -1 )
421 		Com_DPrintf("ioctl cdiocresume failed\n");
422 #endif
423 #if defined(__linux__)
424 	if ( ioctl(cdfile, CDROMRESUME) == -1 )
425 		Com_DPrintf("ioctl cdromresume failed\n");
426 #endif
427 	playing = true;
428 }
429 
430 static void CD_f (void)
431 {
432 	char	*command;
433 	int		ret;
434 	int		n;
435 
436 	if (Cmd_Argc() < 2)
437 		return;
438 
439 	command = Cmd_Argv (1);
440 
441 	if (Q_strcasecmp(command, "on") == 0)
442 	{
443 		enabled = true;
444 		return;
445 	}
446 
447 	if (Q_strcasecmp(command, "off") == 0)
448 	{
449 		if (playing)
450 			CDAudio_Stop();
451 		enabled = false;
452 		return;
453 	}
454 
455 	if (Q_strcasecmp(command, "reset") == 0)
456 	{
457 		enabled = true;
458 		if (playing)
459 			CDAudio_Stop();
460 		for (n = 0; n < 100; n++)
461 			remap[n] = n;
462 		CDAudio_GetAudioDiskInfo();
463 		return;
464 	}
465 
466 	if (Q_strcasecmp(command, "remap") == 0)
467 	{
468 		ret = Cmd_Argc() - 2;
469 		if (ret <= 0)
470 		{
471 			for (n = 1; n < 100; n++)
472 				if (remap[n] != n)
473 					Com_Printf("  %u -> %u\n", n, remap[n]);
474 			return;
475 		}
476 		for (n = 1; n <= ret; n++)
477 			remap[n] = atoi(Cmd_Argv (n+1));
478 		return;
479 	}
480 
481 	if (Q_strcasecmp(command, "close") == 0)
482 	{
483 		CDAudio_CloseDoor();
484 		return;
485 	}
486 
487 	if (!cdValid)
488 	{
489 		CDAudio_GetAudioDiskInfo();
490 		if (!cdValid)
491 		{
492 			Com_Printf("No CD in player.\n");
493 			return;
494 		}
495 	}
496 
497 	if (Q_strcasecmp(command, "play") == 0)
498 	{
499 		CDAudio_Play((byte)atoi(Cmd_Argv (2)), false);
500 		return;
501 	}
502 
503 	if (Q_strcasecmp(command, "loop") == 0)
504 	{
505 		CDAudio_Play((byte)atoi(Cmd_Argv (2)), true);
506 		return;
507 	}
508 
509 	if (Q_strcasecmp(command, "stop") == 0)
510 	{
511 		CDAudio_Stop();
512 		return;
513 	}
514 
515 	if (Q_strcasecmp(command, "pause") == 0)
516 	{
517 		CDAudio_Pause();
518 		return;
519 	}
520 
521 	if (Q_strcasecmp(command, "resume") == 0)
522 	{
523 		CDAudio_Resume();
524 		return;
525 	}
526 
527 	if (Q_strcasecmp(command, "eject") == 0)
528 	{
529 		if (playing)
530 			CDAudio_Stop();
531 		CDAudio_Eject();
532 		cdValid = false;
533 		return;
534 	}
535 
536 	if (Q_strcasecmp(command, "info") == 0)
537 	{
538 		Com_Printf("%u tracks\n", maxTrack);
539 		if (playing)
540 			Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
541 		else if (wasPlaying)
542 			Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
543 		Com_Printf("Volume is %f\n", cdvolume);
544 		return;
545 	}
546 }
547 
548 void CDAudio_Update(void)
549 {
550 #if defined(__FreeBSD__)
551 	struct ioc_read_subchannel subchnl;
552 	struct cd_sub_channel_info data;
553 #endif
554 #if defined(__linux__)
555 	struct cdrom_subchnl subchnl;
556 #endif
557 	static time_t lastchk;
558 
559 	if (cdfile == -1 || !enabled)
560 		return;
561 
562 	if (cd_volume && cd_volume->value != cdvolume)
563 	{
564 		if (cdvolume)
565 		{
566 			Cvar_SetValue ("cd_volume", 0.0);
567 			cdvolume = cd_volume->value;
568 			CDAudio_Pause ();
569 		}
570 		else
571 		{
572 			Cvar_SetValue ("cd_volume", 1.0);
573 			cdvolume = cd_volume->value;
574 			CDAudio_Resume ();
575 		}
576 	}
577 
578 	if (playing && lastchk < time(NULL)) {
579 		lastchk = time(NULL) + 2; //two seconds between chks
580 #if defined(__FreeBSD__)
581 		subchnl.address_format = CD_MSF_FORMAT;
582 		subchnl.data_format = CD_CURRENT_POSITION;
583 		subchnl.data_len = sizeof(data);
584 		subchnl.track = playTrack;
585 		subchnl.data = &data;
586 		if (ioctl(cdfile, CDIOCREADSUBCHANNEL, &subchnl) == -1 ) {
587 			Com_DPrintf("ioctl cdiocreadsubchannel failed\n");
588 			playing = false;
589 			return;
590 		}
591 		if (subchnl.data->header.audio_status != CD_AS_PLAY_IN_PROGRESS &&
592 			subchnl.data->header.audio_status != CD_AS_PLAY_PAUSED) {
593 			playing = false;
594 			if (playLooping)
595 				CDAudio_Play(playTrack, true);
596 		}
597 #endif
598 #if defined(__linux__)
599 		subchnl.cdsc_format = CDROM_MSF;
600 		if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) {
601 			Com_DPrintf("ioctl cdromsubchnl failed\n");
602 			playing = false;
603 			return;
604 		}
605 		if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
606 			subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
607 			playing = false;
608 			if (playLooping)
609 				CDAudio_Play(playTrack, true);
610 		}
611 #endif
612 	}
613 }
614 
615 int CDAudio_Init(void)
616 {
617 	int i;
618 	cvar_t			*cv;
619 	extern uid_t saved_euid;
620 
621 	if (initialized)
622 		return 0;
623 
624 	cv = Cvar_Get ("nocdaudio", "0", CVAR_NOSET);
625 	if (cv->value)
626 		return -1;
627 
628 	cd_nocd = Cvar_Get ("cd_nocd", "0", CVAR_ARCHIVE );
629 	if ( cd_nocd->value)
630 		return -1;
631 
632 	cd_volume = Cvar_Get ("cd_volume", "1", CVAR_ARCHIVE);
633 
634 	cd_dev = Cvar_Get("cd_dev", "/dev/cdrom", CVAR_ARCHIVE);
635 
636 	seteuid(saved_euid);
637 
638 	cdfile = open(cd_dev->string, O_RDONLY | O_NONBLOCK | O_EXCL);
639 
640 	seteuid(getuid());
641 
642 	if (cdfile == -1) {
643 	  Com_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev->string, errno);
644 
645 	  cdfile = -1;
646 	  return -1;
647 	}
648 
649 	for (i = 0; i < 100; i++)
650 		remap[i] = i;
651 	initialized = true;
652 	enabled = true;
653 
654 	if (CDAudio_GetAudioDiskInfo())
655 	{
656 		Com_Printf("CDAudio_Init: No CD in player.\n");
657 		cdValid = false;
658 	}
659 
660 	Cmd_AddCommand ("cd", CD_f);
661 
662 	Com_Printf("CD Audio Initialized\n");
663 
664 	return 0;
665 }
666 
667 void CDAudio_Activate (qboolean active)
668 {
669 	if (active)
670 		CDAudio_Resume ();
671 	else
672 		CDAudio_Pause ();
673 }
674 
675 void CDAudio_Shutdown(void)
676 {
677 	if (!initialized)
678 		return;
679 	CDAudio_Stop();
680 	close(cdfile);
681 	cdfile = -1;
682 	initialized = false;
683 }
684