1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
27extern	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
97struct playAudioRequest
98{
99	char	addressingMode;
100	int		startLocation;
101	int		sectors;
102};
103
104struct readRequest
105{
106	char	mediaDescriptor;
107	short	bufferOffset;
108	short	bufferSegment;
109	short	length;
110	short	startSector;
111	int		volumeID;
112};
113
114struct writeRequest
115{
116	char	mediaDescriptor;
117	short	bufferOffset;
118	short	bufferSegment;
119	short	length;
120	short	startSector;
121	int		volumeID;
122};
123
124struct 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
140struct 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
153struct deviceStatus_s
154{
155	char	code;
156	int		status;
157};
158
159struct mediaChange_s
160{
161	char	code;
162	char	status;
163};
164
165struct audioDiskInfo_s
166{
167	char	code;
168	char	lowTrack;
169	char	highTrack;
170	int		leadOutStart;
171};
172
173struct audioTrackInfo_s
174{
175	char	code;
176	char	track;
177	int		start;
178	char	control;
179};
180
181struct audioStatus_s
182{
183	char	code;
184	short	status;
185	int		PRstartLocation;
186	int		PRendLocation;
187};
188
189struct reset_s
190{
191	char	code;
192};
193
194union 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
209typedef struct
210{
211	int			start;
212	int			length;
213	qboolean	isData;
214} track_info;
215
216typedef struct
217{
218	qboolean	valid;
219	int			leadOutAddress;
220	track_info	track[MAXIMUM_TRACKS];
221	byte		lowTrack;
222	byte		highTrack;
223} cd_info;
224
225static struct cd_request	*cdRequest;
226static union readInfo_u		*readInfo;
227static cd_info				cd;
228
229static qboolean	playing = false;
230static qboolean	wasPlaying = false;
231static qboolean	mediaCheck = false;
232static qboolean	initialized = false;
233static qboolean	enabled = true;
234static qboolean playLooping = false;
235static short	cdRequestSegment;
236static short	cdRequestOffset;
237static short	readInfoSegment;
238static short	readInfoOffset;
239static byte 	remap[256];
240static byte		cdrom;
241static byte		playTrack;
242static byte		cdvolume;
243
244
245static 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
258static 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
282static 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
306static 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
343static 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
393static 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
421static 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
449void 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
495void 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
570void 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
591void CDAudio_Pause(void)
592{
593	CDAudio_Stop();
594}
595
596
597void 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
623static 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
739void 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
801int 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
881void CDAudio_Shutdown(void)
882{
883	if (!initialized)
884		return;
885	CDAudio_Stop();
886}
887