• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <dpmi.h>
24 #include "quakedef.h"
25 #include "dosisms.h"
26 
27 extern	cvar_t	bgmvolume;
28 
29 #define ADDRESS_MODE_HSG		0
30 #define ADDRESS_MODE_RED_BOOK	1
31 
32 #define STATUS_ERROR_BIT	0x8000
33 #define STATUS_BUSY_BIT		0x0200
34 #define STATUS_DONE_BIT		0x0100
35 #define STATUS_ERROR_MASK	0x00ff
36 
37 #define ERROR_WRITE_PROTECT		0
38 #define ERROR_UNKNOWN_UNIT		1
39 #define ERROR_DRIVE_NOT_READY	2
40 #define ERROR_UNKNOWN_COMMAND	3
41 #define ERROR_CRC_ERROR			4
42 #define ERROR_BAD_REQUEST_LEN	5
43 #define ERROR_SEEK_ERROR		6
44 #define ERROR_UNKNOWN_MEDIA		7
45 #define ERROR_SECTOR_NOT_FOUND	8
46 #define ERROR_OUT_OF_PAPER		9
47 #define ERROR_WRITE_FAULT		10
48 #define ERROR_READ_FAULT		11
49 #define ERROR_GENERAL_FAILURE	12
50 #define ERROR_RESERVED_13		13
51 #define ERROR_RESERVED_14		14
52 #define ERROR_BAD_DISK_CHANGE	15
53 
54 #define COMMAND_READ			3
55 #define COMMAND_WRITE			12
56 #define COMMAND_PLAY_AUDIO		132
57 #define COMMAND_STOP_AUDIO		133
58 #define COMMAND_RESUME_AUDIO	136
59 
60 #define READ_REQUEST_AUDIO_CHANNEL_INFO		4
61 #define READ_REQUEST_DEVICE_STATUS			6
62 #define READ_REQUEST_MEDIA_CHANGE			9
63 #define READ_REQUEST_AUDIO_DISK_INFO		10
64 #define READ_REQUEST_AUDIO_TRACK_INFO		11
65 #define READ_REQUEST_AUDIO_STATUS			15
66 
67 #define WRITE_REQUEST_EJECT					0
68 #define WRITE_REQUEST_RESET					2
69 #define WRITE_REQUEST_AUDIO_CHANNEL_INFO	3
70 
71 #define STATUS_DOOR_OPEN					0x00000001
72 #define STATUS_DOOR_UNLOCKED				0x00000002
73 #define STATUS_RAW_SUPPORT					0x00000004
74 #define STATUS_READ_WRITE					0x00000008
75 #define STATUS_AUDIO_SUPPORT				0x00000010
76 #define STATUS_INTERLEAVE_SUPPORT			0x00000020
77 #define STATUS_BIT_6_RESERVED				0x00000040
78 #define STATUS_PREFETCH_SUPPORT				0x00000080
79 #define STATUS_AUDIO_MANIPLUATION_SUPPORT	0x00000100
80 #define STATUS_RED_BOOK_ADDRESS_SUPPORT		0x00000200
81 
82 #define MEDIA_NOT_CHANGED		1
83 #define MEDIA_STATUS_UNKNOWN	0
84 #define MEDIA_CHANGED			-1
85 
86 #define AUDIO_CONTROL_MASK				0xd0
87 #define AUDIO_CONTROL_DATA_TRACK		0x40
88 #define AUDIO_CONTROL_AUDIO_2_TRACK		0x00
89 #define AUDIO_CONTROL_AUDIO_2P_TRACK	0x10
90 #define AUDIO_CONTROL_AUDIO_4_TRACK		0x80
91 #define AUDIO_CONTROL_AUDIO_4P_TRACK	0x90
92 
93 #define AUDIO_STATUS_PAUSED				0x0001
94 
95 #pragma pack(1)
96 
97 struct playAudioRequest
98 {
99 	char	addressingMode;
100 	int		startLocation;
101 	int		sectors;
102 };
103 
104 struct readRequest
105 {
106 	char	mediaDescriptor;
107 	short	bufferOffset;
108 	short	bufferSegment;
109 	short	length;
110 	short	startSector;
111 	int		volumeID;
112 };
113 
114 struct writeRequest
115 {
116 	char	mediaDescriptor;
117 	short	bufferOffset;
118 	short	bufferSegment;
119 	short	length;
120 	short	startSector;
121 	int		volumeID;
122 };
123 
124 struct cd_request
125 {
126 	char	headerLength;
127 	char	unit;
128 	char	command;
129 	short	status;
130 	char	reserved[8];
131 	union
132 	{
133 		struct	playAudioRequest	playAudio;
134 		struct	readRequest			read;
135 		struct	writeRequest		write;
136 	} x;
137 };
138 
139 
140 struct audioChannelInfo_s
141 {
142 	char	code;
143 	char	channel0input;
144 	char	channel0volume;
145 	char	channel1input;
146 	char	channel1volume;
147 	char	channel2input;
148 	char	channel2volume;
149 	char	channel3input;
150 	char	channel3volume;
151 };
152 
153 struct deviceStatus_s
154 {
155 	char	code;
156 	int		status;
157 };
158 
159 struct mediaChange_s
160 {
161 	char	code;
162 	char	status;
163 };
164 
165 struct audioDiskInfo_s
166 {
167 	char	code;
168 	char	lowTrack;
169 	char	highTrack;
170 	int		leadOutStart;
171 };
172 
173 struct audioTrackInfo_s
174 {
175 	char	code;
176 	char	track;
177 	int		start;
178 	char	control;
179 };
180 
181 struct audioStatus_s
182 {
183 	char	code;
184 	short	status;
185 	int		PRstartLocation;
186 	int		PRendLocation;
187 };
188 
189 struct reset_s
190 {
191 	char	code;
192 };
193 
194 union readInfo_u
195 {
196 	struct audioChannelInfo_s	audioChannelInfo;
197 	struct deviceStatus_s		deviceStatus;
198 	struct mediaChange_s		mediaChange;
199 	struct audioDiskInfo_s		audioDiskInfo;
200 	struct audioTrackInfo_s		audioTrackInfo;
201 	struct audioStatus_s		audioStatus;
202 	struct reset_s				reset;
203 };
204 
205 #pragma pack()
206 
207 #define MAXIMUM_TRACKS			100
208 
209 typedef struct
210 {
211 	int			start;
212 	int			length;
213 	qboolean	isData;
214 } track_info;
215 
216 typedef struct
217 {
218 	qboolean	valid;
219 	int			leadOutAddress;
220 	track_info	track[MAXIMUM_TRACKS];
221 	byte		lowTrack;
222 	byte		highTrack;
223 } cd_info;
224 
225 static struct cd_request	*cdRequest;
226 static union readInfo_u		*readInfo;
227 static cd_info				cd;
228 
229 static qboolean	playing = false;
230 static qboolean	wasPlaying = false;
231 static qboolean	mediaCheck = false;
232 static qboolean	initialized = false;
233 static qboolean	enabled = true;
234 static qboolean playLooping = false;
235 static short	cdRequestSegment;
236 static short	cdRequestOffset;
237 static short	readInfoSegment;
238 static short	readInfoOffset;
239 static byte 	remap[256];
240 static byte		cdrom;
241 static byte		playTrack;
242 static byte		cdvolume;
243 
244 
RedBookToSector(int rb)245 static int RedBookToSector(int rb)
246 {
247 	byte	minute;
248 	byte	second;
249 	byte	frame;
250 
251 	minute = (rb >> 16) & 0xff;
252 	second = (rb >> 8) & 0xff;
253 	frame = rb & 0xff;
254 	return minute * 60 * 75 + second * 75 + frame;
255 }
256 
257 
CDAudio_Reset(void)258 static void CDAudio_Reset(void)
259 {
260 	cdRequest->headerLength = 13;
261 	cdRequest->unit = 0;
262 	cdRequest->command = COMMAND_WRITE;
263 	cdRequest->status = 0;
264 
265 	cdRequest->x.write.mediaDescriptor = 0;
266 	cdRequest->x.write.bufferOffset = readInfoOffset;
267 	cdRequest->x.write.bufferSegment = readInfoSegment;
268 	cdRequest->x.write.length = sizeof(struct reset_s);
269 	cdRequest->x.write.startSector = 0;
270 	cdRequest->x.write.volumeID = 0;
271 
272 	readInfo->reset.code = WRITE_REQUEST_RESET;
273 
274 	regs.x.ax = 0x1510;
275 	regs.x.cx = cdrom;
276 	regs.x.es = cdRequestSegment;
277 	regs.x.bx = cdRequestOffset;
278 	dos_int86 (0x2f);
279 }
280 
281 
CDAudio_Eject(void)282 static void CDAudio_Eject(void)
283 {
284 	cdRequest->headerLength = 13;
285 	cdRequest->unit = 0;
286 	cdRequest->command = COMMAND_WRITE;
287 	cdRequest->status = 0;
288 
289 	cdRequest->x.write.mediaDescriptor = 0;
290 	cdRequest->x.write.bufferOffset = readInfoOffset;
291 	cdRequest->x.write.bufferSegment = readInfoSegment;
292 	cdRequest->x.write.length = sizeof(struct reset_s);
293 	cdRequest->x.write.startSector = 0;
294 	cdRequest->x.write.volumeID = 0;
295 
296 	readInfo->reset.code = WRITE_REQUEST_EJECT;
297 
298 	regs.x.ax = 0x1510;
299 	regs.x.cx = cdrom;
300 	regs.x.es = cdRequestSegment;
301 	regs.x.bx = cdRequestOffset;
302 	dos_int86 (0x2f);
303 }
304 
305 
CDAudio_GetAudioTrackInfo(byte track,int * start)306 static int CDAudio_GetAudioTrackInfo(byte track, int *start)
307 {
308 	byte	control;
309 
310 	cdRequest->headerLength = 13;
311 	cdRequest->unit = 0;
312 	cdRequest->command = COMMAND_READ;
313 	cdRequest->status = 0;
314 
315 	cdRequest->x.read.mediaDescriptor = 0;
316 	cdRequest->x.read.bufferOffset = readInfoOffset;
317 	cdRequest->x.read.bufferSegment = readInfoSegment;
318 	cdRequest->x.read.length = sizeof(struct audioTrackInfo_s);
319 	cdRequest->x.read.startSector = 0;
320 	cdRequest->x.read.volumeID = 0;
321 
322 	readInfo->audioTrackInfo.code = READ_REQUEST_AUDIO_TRACK_INFO;
323 	readInfo->audioTrackInfo.track = track;
324 
325 	regs.x.ax = 0x1510;
326 	regs.x.cx = cdrom;
327 	regs.x.es = cdRequestSegment;
328 	regs.x.bx = cdRequestOffset;
329 	dos_int86 (0x2f);
330 
331 	if (cdRequest->status & STATUS_ERROR_BIT)
332 	{
333 		Con_DPrintf("CDAudio_GetAudioTrackInfo %04x\n", cdRequest->status & 	0xffff);
334 		return -1;
335 	}
336 
337 	*start = readInfo->audioTrackInfo.start;
338 	control = readInfo->audioTrackInfo.control & AUDIO_CONTROL_MASK;
339 	return (control & AUDIO_CONTROL_DATA_TRACK);
340 }
341 
342 
CDAudio_GetAudioDiskInfo(void)343 static int CDAudio_GetAudioDiskInfo(void)
344 {
345 	int n;
346 
347 	cdRequest->headerLength = 13;
348 	cdRequest->unit = 0;
349 	cdRequest->command = COMMAND_READ;
350 	cdRequest->status = 0;
351 
352 	cdRequest->x.read.mediaDescriptor = 0;
353 	cdRequest->x.read.bufferOffset = readInfoOffset;
354 	cdRequest->x.read.bufferSegment = readInfoSegment;
355 	cdRequest->x.read.length = sizeof(struct audioDiskInfo_s);
356 	cdRequest->x.read.startSector = 0;
357 	cdRequest->x.read.volumeID = 0;
358 
359 	readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_DISK_INFO;
360 
361 	regs.x.ax = 0x1510;
362 	regs.x.cx = cdrom;
363 	regs.x.es = cdRequestSegment;
364 	regs.x.bx = cdRequestOffset;
365 	dos_int86 (0x2f);
366 
367 	if (cdRequest->status & STATUS_ERROR_BIT)
368 	{
369 		Con_DPrintf("CDAudio_GetAudioDiskInfo %04x\n", cdRequest->status & 	0xffff);
370 		return -1;
371 	}
372 
373 	cd.valid = true;
374 	cd.lowTrack = readInfo->audioDiskInfo.lowTrack;
375 	cd.highTrack = readInfo->audioDiskInfo.highTrack;
376 	cd.leadOutAddress = readInfo->audioDiskInfo.leadOutStart;
377 
378 	for (n = cd.lowTrack; n <= cd.highTrack; n++)
379 	{
380 		cd.track[n].isData = CDAudio_GetAudioTrackInfo (n, &cd.track[n].start);
381 		if (n > cd.lowTrack)
382 		{
383 			cd.track[n-1].length = RedBookToSector(cd.track[n].start) - RedBookToSector(cd.track[n-1].start);
384 			if (n == cd.highTrack)
385 				cd.track[n].length = RedBookToSector(cd.leadOutAddress) - RedBookToSector(cd.track[n].start);
386 		}
387 	}
388 
389 	return 0;
390 }
391 
392 
CDAudio_GetAudioStatus(void)393 static int CDAudio_GetAudioStatus(void)
394 {
395 	cdRequest->headerLength = 13;
396 	cdRequest->unit = 0;
397 	cdRequest->command = COMMAND_READ;
398 	cdRequest->status = 0;
399 
400 	cdRequest->x.read.mediaDescriptor = 0;
401 	cdRequest->x.read.bufferOffset = readInfoOffset;
402 	cdRequest->x.read.bufferSegment = readInfoSegment;
403 	cdRequest->x.read.length = sizeof(struct audioStatus_s);
404 	cdRequest->x.read.startSector = 0;
405 	cdRequest->x.read.volumeID = 0;
406 
407 	readInfo->audioDiskInfo.code = READ_REQUEST_AUDIO_STATUS;
408 
409 	regs.x.ax = 0x1510;
410 	regs.x.cx = cdrom;
411 	regs.x.es = cdRequestSegment;
412 	regs.x.bx = cdRequestOffset;
413 	dos_int86 (0x2f);
414 
415 	if (cdRequest->status & STATUS_ERROR_BIT)
416 		return -1;
417 	return 0;
418 }
419 
420 
CDAudio_MediaChange(void)421 static int CDAudio_MediaChange(void)
422 {
423 	cdRequest->headerLength = 13;
424 	cdRequest->unit = 0;
425 	cdRequest->command = COMMAND_READ;
426 	cdRequest->status = 0;
427 
428 	cdRequest->x.read.mediaDescriptor = 0;
429 	cdRequest->x.read.bufferOffset = readInfoOffset;
430 	cdRequest->x.read.bufferSegment = readInfoSegment;
431 	cdRequest->x.read.length = sizeof(struct mediaChange_s);
432 	cdRequest->x.read.startSector = 0;
433 	cdRequest->x.read.volumeID = 0;
434 
435 	readInfo->mediaChange.code = READ_REQUEST_MEDIA_CHANGE;
436 
437 	regs.x.ax = 0x1510;
438 	regs.x.cx = cdrom;
439 	regs.x.es = cdRequestSegment;
440 	regs.x.bx = cdRequestOffset;
441 	dos_int86 (0x2f);
442 
443 	return readInfo->mediaChange.status;
444 }
445 
446 
447 // we set the volume to 0 first and then to the desired volume
448 // some cd-rom drivers seem to need it done this way
CDAudio_SetVolume(byte volume)449 void CDAudio_SetVolume (byte volume)
450 {
451 	if (!initialized || !enabled)
452 		return;
453 
454 	cdRequest->headerLength = 13;
455 	cdRequest->unit = 0;
456 	cdRequest->command = COMMAND_WRITE;
457 	cdRequest->status = 0;
458 
459 	cdRequest->x.read.mediaDescriptor = 0;
460 	cdRequest->x.read.bufferOffset = readInfoOffset;
461 	cdRequest->x.read.bufferSegment = readInfoSegment;
462 	cdRequest->x.read.length = sizeof(struct audioChannelInfo_s);
463 	cdRequest->x.read.startSector = 0;
464 	cdRequest->x.read.volumeID = 0;
465 
466 	readInfo->audioChannelInfo.code = WRITE_REQUEST_AUDIO_CHANNEL_INFO;
467 	readInfo->audioChannelInfo.channel0input = 0;
468 	readInfo->audioChannelInfo.channel0volume = 0;
469 	readInfo->audioChannelInfo.channel1input = 1;
470 	readInfo->audioChannelInfo.channel1volume = 0;
471 	readInfo->audioChannelInfo.channel2input = 2;
472 	readInfo->audioChannelInfo.channel2volume = 0;
473 	readInfo->audioChannelInfo.channel3input = 3;
474 	readInfo->audioChannelInfo.channel3volume = 0;
475 
476 	regs.x.ax = 0x1510;
477 	regs.x.cx = cdrom;
478 	regs.x.es = cdRequestSegment;
479 	regs.x.bx = cdRequestOffset;
480 	dos_int86 (0x2f);
481 
482 	readInfo->audioChannelInfo.channel0volume = volume;
483 	readInfo->audioChannelInfo.channel1volume = volume;
484 
485 	regs.x.ax = 0x1510;
486 	regs.x.cx = cdrom;
487 	regs.x.es = cdRequestSegment;
488 	regs.x.bx = cdRequestOffset;
489 	dos_int86 (0x2f);
490 
491 	cdvolume = volume;
492 }
493 
494 
CDAudio_Play(byte track,qboolean looping)495 void CDAudio_Play(byte track, qboolean looping)
496 {
497 	int		volume;
498 
499 	if (!initialized || !enabled)
500 		return;
501 
502 	if (!cd.valid)
503 		return;
504 
505 	track = remap[track];
506 
507 	if (playing)
508 	{
509 		if (playTrack == track)
510 			return;
511 		CDAudio_Stop();
512 	}
513 
514 	playLooping = looping;
515 
516 	if (track < cd.lowTrack || track > cd.highTrack)
517 	{
518 		Con_DPrintf("CDAudio_Play: Bad track number %u.\n", track);
519 		return;
520 	}
521 
522 	playTrack = track;
523 
524 	if (cd.track[track].isData)
525 	{
526 		Con_DPrintf("CDAudio_Play: Can not play data.\n");
527 		return;
528 	}
529 
530 	volume = (int)(bgmvolume.value * 255.0);
531 	if (volume < 0)
532 	{
533 		Cvar_SetValue ("bgmvolume", 0.0);
534 		volume = 0;
535 	}
536 	else if (volume > 255)
537 	{
538 		Cvar_SetValue ("bgmvolume", 1.0);
539 		volume = 255;
540 	}
541 	CDAudio_SetVolume (volume);
542 
543 	cdRequest->headerLength = 13;
544 	cdRequest->unit = 0;
545 	cdRequest->command = COMMAND_PLAY_AUDIO;
546 	cdRequest->status = 0;
547 
548 	cdRequest->x.playAudio.addressingMode = ADDRESS_MODE_RED_BOOK;
549 	cdRequest->x.playAudio.startLocation = cd.track[track].start;
550 	cdRequest->x.playAudio.sectors = cd.track[track].length;
551 
552 	regs.x.ax = 0x1510;
553 	regs.x.cx = cdrom;
554 	regs.x.es = cdRequestSegment;
555 	regs.x.bx = cdRequestOffset;
556 	dos_int86 (0x2f);
557 
558 	if (cdRequest->status & STATUS_ERROR_BIT)
559 	{
560 		Con_DPrintf("CDAudio_Play: track %u failed\n", track);
561 		cd.valid = false;
562 		playing = false;
563 		return;
564 	}
565 
566 	playing = true;
567 }
568 
569 
CDAudio_Stop(void)570 void CDAudio_Stop(void)
571 {
572 	if (!initialized || !enabled)
573 		return;
574 
575 	cdRequest->headerLength = 13;
576 	cdRequest->unit = 0;
577 	cdRequest->command = COMMAND_STOP_AUDIO;
578 	cdRequest->status = 0;
579 
580 	regs.x.ax = 0x1510;
581 	regs.x.cx = cdrom;
582 	regs.x.es = cdRequestSegment;
583 	regs.x.bx = cdRequestOffset;
584 	dos_int86 (0x2f);
585 
586 	wasPlaying = playing;
587 	playing = false;
588 }
589 
590 
CDAudio_Pause(void)591 void CDAudio_Pause(void)
592 {
593 	CDAudio_Stop();
594 }
595 
596 
CDAudio_Resume(void)597 void CDAudio_Resume(void)
598 {
599 	if (!initialized || !enabled)
600 		return;
601 
602 	if (!cd.valid)
603 		return;
604 
605 	if (!wasPlaying)
606 		return;
607 
608 	cdRequest->headerLength = 13;
609 	cdRequest->unit = 0;
610 	cdRequest->command = COMMAND_RESUME_AUDIO;
611 	cdRequest->status = 0;
612 
613 	regs.x.ax = 0x1510;
614 	regs.x.cx = cdrom;
615 	regs.x.es = cdRequestSegment;
616 	regs.x.bx = cdRequestOffset;
617 	dos_int86 (0x2f);
618 
619 	playing = true;
620 }
621 
622 
CD_f(void)623 static void CD_f (void)
624 {
625 	char	*command;
626 	int		ret;
627 	int		n;
628 	int		startAddress;
629 
630 	if (Cmd_Argc() < 2)
631 		return;
632 
633 	command = Cmd_Argv (1);
634 
635 	if (Q_strcasecmp(command, "on") == 0)
636 	{
637 		enabled = true;
638 		return;
639 	}
640 
641 	if (Q_strcasecmp(command, "off") == 0)
642 	{
643 		if (playing)
644 			CDAudio_Stop();
645 		enabled = false;
646 		return;
647 	}
648 
649 	if (Q_strcasecmp(command, "reset") == 0)
650 	{
651 		enabled = true;
652 		if (playing)
653 			CDAudio_Stop();
654 		for (n = 0; n < 256; n++)
655 			remap[n] = n;
656 		CDAudio_Reset();
657 		CDAudio_GetAudioDiskInfo();
658 		return;
659 	}
660 
661 	if (Q_strcasecmp(command, "remap") == 0)
662 	{
663 		ret = Cmd_Argc() - 2;
664 		if (ret <= 0)
665 		{
666 			for (n = 1; n < 256; n++)
667 				if (remap[n] != n)
668 					Con_Printf("  %u -> %u\n", n, remap[n]);
669 			return;
670 		}
671 		for (n = 1; n <= ret; n++)
672 			remap[n] = Q_atoi(Cmd_Argv (n+1));
673 		return;
674 	}
675 
676 	if (!cd.valid)
677 	{
678 		Con_Printf("No CD in player.\n");
679 		return;
680 	}
681 
682 	if (Q_strcasecmp(command, "play") == 0)
683 	{
684 		CDAudio_Play(Q_atoi(Cmd_Argv (2)), false);
685 		return;
686 	}
687 
688 	if (Q_strcasecmp(command, "loop") == 0)
689 	{
690 		CDAudio_Play(Q_atoi(Cmd_Argv (2)), true);
691 		return;
692 	}
693 
694 	if (Q_strcasecmp(command, "stop") == 0)
695 	{
696 		CDAudio_Stop();
697 		return;
698 	}
699 
700 	if (Q_strcasecmp(command, "pause") == 0)
701 	{
702 		CDAudio_Pause();
703 		return;
704 	}
705 
706 	if (Q_strcasecmp(command, "resume") == 0)
707 	{
708 		CDAudio_Resume();
709 		return;
710 	}
711 
712 	if (Q_strcasecmp(command, "eject") == 0)
713 	{
714 		if (playing)
715 			CDAudio_Stop();
716 		CDAudio_Eject();
717 		cd.valid = false;
718 		return;
719 	}
720 
721 	if (Q_strcasecmp(command, "info") == 0)
722 	{
723 		Con_Printf("%u tracks\n", cd.highTrack - cd.lowTrack + 1);
724 		for (n = cd.lowTrack; n <= cd.highTrack; n++)
725 		{
726 			ret = CDAudio_GetAudioTrackInfo (n, &startAddress);
727 			Con_Printf("Track %2u: %s at %2u:%02u\n", n, ret ? "data " : "music", (startAddress >> 16) & 0xff, (startAddress >> 8) & 0xff);
728 		}
729 		if (playing)
730 			Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack);
731 		Con_Printf("Volume is %u\n", cdvolume);
732 		CDAudio_MediaChange();
733 		Con_Printf("Status %04x\n", cdRequest->status & 0xffff);
734 		return;
735 	}
736 }
737 
738 
CDAudio_Update(void)739 void CDAudio_Update(void)
740 {
741 	int		ret;
742 	int		newVolume;
743 	static	double lastUpdate;
744 
745 	if (!initialized || !enabled)
746 		return;
747 
748 	if ((realtime - lastUpdate) < 0.25)
749 		return;
750 	lastUpdate = realtime;
751 
752 	if (mediaCheck)
753 	{
754 		static	double lastCheck;
755 
756 		if ((realtime - lastCheck) < 5.0)
757 			return;
758 		lastCheck = realtime;
759 
760 		ret = CDAudio_MediaChange();
761 		if (ret == MEDIA_CHANGED)
762 		{
763 			Con_DPrintf("CDAudio: media changed\n");
764 			playing = false;
765 			wasPlaying = false;
766 			cd.valid = false;
767 			CDAudio_GetAudioDiskInfo();
768 			return;
769 		}
770 	}
771 
772 	newVolume = (int)(bgmvolume.value * 255.0);
773 	if (newVolume != cdvolume)
774 	{
775 		if (newVolume < 0)
776 		{
777 			Cvar_SetValue ("bgmvolume", 0.0);
778 			newVolume = 0;
779 		}
780 		else if (newVolume > 255)
781 		{
782 			Cvar_SetValue ("bgmvolume", 1.0);
783 			newVolume = 255;
784 		}
785 		CDAudio_SetVolume (newVolume);
786 	}
787 
788 	if (playing)
789 	{
790 		CDAudio_GetAudioStatus();
791 		if ((cdRequest->status & STATUS_BUSY_BIT) == 0)
792 		{
793 			playing = false;
794 			if (playLooping)
795 				CDAudio_Play(playTrack, true);
796 		}
797 	}
798 }
799 
800 
CDAudio_Init(void)801 int CDAudio_Init(void)
802 {
803 	char	*memory;
804 	int		n;
805 
806 	if (cls.state == ca_dedicated)
807 		return -1;
808 
809 	if (COM_CheckParm("-nocdaudio"))
810 		return -1;
811 
812 	if (COM_CheckParm("-cdmediacheck"))
813 		mediaCheck = true;
814 
815 	regs.x.ax = 0x1500;
816 	regs.x.bx = 0;
817 	dos_int86 (0x2f);
818 	if (regs.x.bx == 0)
819 	{
820 		Con_NotifyBox (
821 			"MSCDEX not loaded, music is\n"
822 			"disabled.  Use \"-nocdaudio\" if you\n"
823 			"wish to avoid this message in the\n"
824 			"future.  See README.TXT for help.\n"
825 			);
826 		return -1;
827 	}
828 	if (regs.x.bx > 1)
829 		Con_DPrintf("CDAudio_Init: First CD-ROM drive will be used\n");
830 	cdrom = regs.x.cx;
831 
832 	regs.x.ax = 0x150c;
833 	regs.x.bx = 0;
834 	dos_int86 (0x2f);
835 	if (regs.x.bx == 0)
836 	{
837 		Con_NotifyBox (
838 			"MSCDEX version 2.00 or later\n"
839 			"required for music. See README.TXT\n"
840 			"for help.\n"
841 			);
842 		Con_DPrintf("CDAudio_Init: MSCDEX version 2.00 or later required.\n");
843 		return -1;
844 	}
845 
846 	memory = dos_getmemory(sizeof(struct cd_request
847 ) + sizeof(union readInfo_u));
848 	if (memory == NULL)
849 	{
850 		Con_DPrintf("CDAudio_Init: Unable to allocate low memory.\n");
851 		return -1;
852 	}
853 
854 	cdRequest = (struct cd_request *)memory;
855 	cdRequestSegment = ptr2real(cdRequest) >> 4;
856 	cdRequestOffset = ptr2real(cdRequest) & 0xf;
857 
858 	readInfo = (union readInfo_u *)(memory + sizeof(struct cd_request));
859 	readInfoSegment = ptr2real(readInfo) >> 4;
860 	readInfoOffset = ptr2real(readInfo) & 0xf;
861 
862 	for (n = 0; n < 256; n++)
863 		remap[n] = n;
864 	initialized = true;
865 
866 	CDAudio_SetVolume (255);
867 	if (CDAudio_GetAudioDiskInfo())
868 	{
869 		Con_Printf("CDAudio_Init: No CD in player.\n");
870 		enabled = false;
871 	}
872 
873 	Cmd_AddCommand ("cd", CD_f);
874 
875 	Con_Printf("CD Audio Initialized\n");
876 
877 	return 0;
878 }
879 
880 
CDAudio_Shutdown(void)881 void CDAudio_Shutdown(void)
882 {
883 	if (!initialized)
884 		return;
885 	CDAudio_Stop();
886 }
887