Home | History | Annotate | Download | only in WinQuake
      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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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 
    591 void CDAudio_Pause(void)
    592 {
    593 	CDAudio_Stop();
    594 }
    595 
    596 
    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 
    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 
    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 
    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 
    881 void CDAudio_Shutdown(void)
    882 {
    883 	if (!initialized)
    884 		return;
    885 	CDAudio_Stop();
    886 }
    887