1 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
2 // rights reserved.
3 
4 #ifdef CD_AUDIO
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <stdlib.h>
8 #include <sys/ioctl.h>
9 #include <sys/file.h>
10 #include <sys/types.h>
11 #include <fcntl.h>
12 #include <string.h>
13 #include <time.h>
14 #include <errno.h>
15 
16 #include <linux/cdrom.h>
17 
18 #include "../client/client.h"
19 
20 static qboolean cdValid = false;
21 static qboolean	playing = false;
22 static qboolean	wasPlaying = false;
23 static qboolean	initialized = false;
24 static qboolean	enabled = true;
25 static qboolean playLooping = false;
26 static float	cdvolume;
27 static byte 	remap[100];
28 static byte		playTrack;
29 static byte		maxTrack;
30 
31 static int cdfile = -1;
32 
33 //static char cd_dev[64] = "/dev/cdrom";
34 
35 cvar_t	*cd_volume;
36 cvar_t *cd_nocd;
37 cvar_t *cd_dev;
38 
39 void CDAudio_Pause(void);
40 
CDAudio_Eject(void)41 static void CDAudio_Eject(void)
42 {
43 	if (cdfile == -1 || !enabled)
44 		return; // no cd init'd
45 
46 	if ( ioctl(cdfile, CDROMEJECT) == -1 )
47 		Com_DPrintf("ioctl cdromeject failed\n");
48 }
49 
50 
CDAudio_CloseDoor(void)51 static void CDAudio_CloseDoor(void)
52 {
53 	if (cdfile == -1 || !enabled)
54 		return; // no cd init'd
55 
56 	if ( ioctl(cdfile, CDROMCLOSETRAY) == -1 )
57 		Com_DPrintf("ioctl cdromclosetray failed\n");
58 }
59 
CDAudio_GetAudioDiskInfo(void)60 static int CDAudio_GetAudioDiskInfo(void)
61 {
62 	struct cdrom_tochdr tochdr;
63 
64 	cdValid = false;
65 
66 	if ( ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1 )
67     {
68       Com_DPrintf("ioctl cdromreadtochdr failed\n");
69 	  return -1;
70     }
71 
72 	if (tochdr.cdth_trk0 < 1)
73 	{
74 		Com_DPrintf("CDAudio: no music tracks\n");
75 		return -1;
76 	}
77 
78 	cdValid = true;
79 	maxTrack = tochdr.cdth_trk1;
80 
81 	return 0;
82 }
83 
84 
CDAudio_Play(int track,qboolean looping)85 void CDAudio_Play(int track, qboolean looping)
86 {
87 	struct cdrom_tocentry entry;
88 	struct cdrom_ti ti;
89 
90 	if (cdfile == -1 || !enabled)
91 		return;
92 
93 	if (!cdValid)
94 	{
95 		CDAudio_GetAudioDiskInfo();
96 		if (!cdValid)
97 			return;
98 	}
99 
100 	track = remap[track];
101 
102 	if (track < 1 || track > maxTrack)
103 	{
104 		Com_DPrintf("CDAudio: Bad track number %u.\n", track);
105 		return;
106 	}
107 
108 	// don't try to play a non-audio track
109 	entry.cdte_track = track;
110 	entry.cdte_format = CDROM_MSF;
111     if ( ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1 )
112 	{
113 		Com_DPrintf("ioctl cdromreadtocentry failed\n");
114 		return;
115 	}
116 	if (entry.cdte_ctrl == CDROM_DATA_TRACK)
117 	{
118 		Com_Printf("CDAudio: track %i is not audio\n", track);
119 		return;
120 	}
121 
122 	if (playing)
123 	{
124 		if (playTrack == track)
125 			return;
126 		CDAudio_Stop();
127 	}
128 
129 	ti.cdti_trk0 = track;
130 	ti.cdti_trk1 = track;
131 	ti.cdti_ind0 = 1;
132 	ti.cdti_ind1 = 99;
133 
134 	if ( ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1 )
135     {
136 		Com_DPrintf("ioctl cdromplaytrkind failed\n");
137 		return;
138     }
139 
140 	if ( ioctl(cdfile, CDROMRESUME) == -1 )
141 		Com_DPrintf("ioctl cdromresume failed\n");
142 
143 	playLooping = looping;
144 	playTrack = track;
145 	playing = true;
146 
147 	if (cd_volume->value == 0.0)
148 		CDAudio_Pause ();
149 }
150 
151 
CDAudio_Stop(void)152 void CDAudio_Stop(void)
153 {
154 	if (cdfile == -1 || !enabled)
155 		return;
156 
157 	if (!playing)
158 		return;
159 
160 	if ( ioctl(cdfile, CDROMSTOP) == -1 )
161 		Com_DPrintf("ioctl cdromstop failed (%d)\n", errno);
162 
163 	wasPlaying = false;
164 	playing = false;
165 }
166 
CDAudio_Pause(void)167 void CDAudio_Pause(void)
168 {
169 	if (cdfile == -1 || !enabled)
170 		return;
171 
172 	if (!playing)
173 		return;
174 
175 	if ( ioctl(cdfile, CDROMPAUSE) == -1 )
176 		Com_DPrintf("ioctl cdrompause failed\n");
177 
178 	wasPlaying = playing;
179 	playing = false;
180 }
181 
182 
CDAudio_Resume(void)183 void CDAudio_Resume(void)
184 {
185 	if (cdfile == -1 || !enabled)
186 		return;
187 
188 	if (!cdValid)
189 		return;
190 
191 	if (!wasPlaying)
192 		return;
193 
194 	if ( ioctl(cdfile, CDROMRESUME) == -1 )
195 		Com_DPrintf("ioctl cdromresume failed\n");
196 	playing = true;
197 }
198 
CD_f(void)199 static void CD_f (void)
200 {
201 	char	*command;
202 	int		ret;
203 	int		n;
204 
205 	if (Cmd_Argc() < 2)
206 		return;
207 
208 	command = Cmd_Argv (1);
209 
210 	if (Q_strcasecmp(command, "on") == 0)
211 	{
212 		enabled = true;
213 		return;
214 	}
215 
216 	if (Q_strcasecmp(command, "off") == 0)
217 	{
218 		if (playing)
219 			CDAudio_Stop();
220 		enabled = false;
221 		return;
222 	}
223 
224 	if (Q_strcasecmp(command, "reset") == 0)
225 	{
226 		enabled = true;
227 		if (playing)
228 			CDAudio_Stop();
229 		for (n = 0; n < 100; n++)
230 			remap[n] = n;
231 		CDAudio_GetAudioDiskInfo();
232 		return;
233 	}
234 
235 	if (Q_strcasecmp(command, "remap") == 0)
236 	{
237 		ret = Cmd_Argc() - 2;
238 		if (ret <= 0)
239 		{
240 			for (n = 1; n < 100; n++)
241 				if (remap[n] != n)
242 					Com_Printf("  %u -> %u\n", n, remap[n]);
243 			return;
244 		}
245 		for (n = 1; n <= ret; n++)
246 			remap[n] = atoi(Cmd_Argv (n+1));
247 		return;
248 	}
249 
250 	if (Q_strcasecmp(command, "close") == 0)
251 	{
252 		CDAudio_CloseDoor();
253 		return;
254 	}
255 
256 	if (!cdValid)
257 	{
258 		CDAudio_GetAudioDiskInfo();
259 		if (!cdValid)
260 		{
261 			Com_Printf("No CD in player.\n");
262 			return;
263 		}
264 	}
265 
266 	if (Q_strcasecmp(command, "play") == 0)
267 	{
268 		CDAudio_Play((byte)atoi(Cmd_Argv (2)), false);
269 		return;
270 	}
271 
272 	if (Q_strcasecmp(command, "loop") == 0)
273 	{
274 		CDAudio_Play((byte)atoi(Cmd_Argv (2)), true);
275 		return;
276 	}
277 
278 	if (Q_strcasecmp(command, "stop") == 0)
279 	{
280 		CDAudio_Stop();
281 		return;
282 	}
283 
284 	if (Q_strcasecmp(command, "pause") == 0)
285 	{
286 		CDAudio_Pause();
287 		return;
288 	}
289 
290 	if (Q_strcasecmp(command, "resume") == 0)
291 	{
292 		CDAudio_Resume();
293 		return;
294 	}
295 
296 	if (Q_strcasecmp(command, "eject") == 0)
297 	{
298 		if (playing)
299 			CDAudio_Stop();
300 		CDAudio_Eject();
301 		cdValid = false;
302 		return;
303 	}
304 
305 	if (Q_strcasecmp(command, "info") == 0)
306 	{
307 		Com_Printf("%u tracks\n", maxTrack);
308 		if (playing)
309 			Com_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
310 		else if (wasPlaying)
311 			Com_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack);
312 		Com_Printf("Volume is %f\n", cdvolume);
313 		return;
314 	}
315 }
316 
CDAudio_Update(void)317 void CDAudio_Update(void)
318 {
319 	struct cdrom_subchnl subchnl;
320 	static time_t lastchk;
321 
322 	if (cdfile == -1 || !enabled)
323 		return;
324 
325 	if (cd_volume && cd_volume->value != cdvolume)
326 	{
327 		if (cdvolume)
328 		{
329 			Cvar_SetValue ("cd_volume", 0.0);
330 			cdvolume = cd_volume->value;
331 			CDAudio_Pause ();
332 		}
333 		else
334 		{
335 			Cvar_SetValue ("cd_volume", 1.0);
336 			cdvolume = cd_volume->value;
337 			CDAudio_Resume ();
338 		}
339 	}
340 
341 	if (playing && lastchk < time(NULL)) {
342 		lastchk = time(NULL) + 2; //two seconds between chks
343 		subchnl.cdsc_format = CDROM_MSF;
344 		if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1 ) {
345 			Com_DPrintf("ioctl cdromsubchnl failed\n");
346 			playing = false;
347 			return;
348 		}
349 		if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
350 			subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) {
351 			playing = false;
352 			if (playLooping)
353 				CDAudio_Play(playTrack, true);
354 		}
355 	}
356 }
357 
CDAudio_Init(void)358 int CDAudio_Init(void)
359 {
360 	int i;
361 	cvar_t			*cv;
362 	extern uid_t saved_euid;
363 
364 	cv = Cvar_Get ("nocdaudio", "0", CVAR_NOSET);
365 	if (cv->value)
366 		return -1;
367 
368 	cd_nocd = Cvar_Get ("cd_nocd", "0", CVAR_ARCHIVE );
369 	if ( cd_nocd->value)
370 		return -1;
371 
372 	cd_volume = Cvar_Get ("cd_volume", "1", CVAR_ARCHIVE);
373 
374 	cd_dev = Cvar_Get("cd_dev", "/dev/cdrom", CVAR_ARCHIVE);
375 
376 	seteuid(saved_euid);
377 
378 	cdfile = open(cd_dev->string, O_RDONLY);
379 
380 	seteuid(getuid());
381 
382 	if (cdfile == -1) {
383 		Com_Printf("CDAudio_Init: open of \"%s\" failed (%i)\n", cd_dev->string, errno);
384 		cdfile = -1;
385 		return -1;
386 	}
387 
388 	for (i = 0; i < 100; i++)
389 		remap[i] = i;
390 	initialized = true;
391 	enabled = true;
392 
393 	if (CDAudio_GetAudioDiskInfo())
394 	{
395 		Com_Printf("CDAudio_Init: No CD in player.\n");
396 		cdValid = false;
397 	}
398 
399 	Cmd_AddCommand ("cd", CD_f);
400 
401 	Com_Printf("CD Audio Initialized\n");
402 
403 	return 0;
404 }
405 
CDAudio_Activate(qboolean active)406 void CDAudio_Activate (qboolean active)
407 {
408 	if (active)
409 		CDAudio_Resume ();
410 	else
411 		CDAudio_Pause ();
412 }
413 
CDAudio_Shutdown(void)414 void CDAudio_Shutdown(void)
415 {
416 	if (!initialized)
417 		return;
418 	CDAudio_Stop();
419 	close(cdfile);
420 	cdfile = -1;
421 }
422 #endif
423