diff --git a/quakespasm/Quake/cd_bsd.c b/quakespasm/Quake/cd_bsd.c new file mode 100644 index 00000000..2911bf00 --- /dev/null +++ b/quakespasm/Quake/cd_bsd.c @@ -0,0 +1,512 @@ +/* + cd_bsd.c + + Copyright (C) 1996-1997 Id Software, Inc. + A few BSD bits taken from the darkplaces project for Hexen II: + Hammer of Thyrion (uHexen2) + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + + +#include "cd_unix.h" + +#ifdef __USE_BSD_CDROM__ + +#include "quakedef.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static byte remap[100]; +static byte playTrack; +static byte maxTrack; + +static int cdfile = -1; +/* default path to cdrom device. user can always do -cddev */ +#if !defined(__FreeBSD__) +static const char default_dev[] = _PATH_DEV "cd0"; +#else +static const char default_dev[] = _PATH_DEV "acd0"; +#endif +static const char *cd_dev = default_dev; + +static float old_cdvolume; +static qboolean hw_vol_works = true; +static struct ioc_vol orig_vol; /* original setting to be restored upon exit */ +static struct ioc_vol drv_vol; /* the volume setting we'll be using */ + + +#define IOCTL_FAILURE(__name) do { \ + int __err = errno; \ + Con_DPrintf("ioctl %s failed (%d: %s)\n", #__name, __err, strerror(__err)); \ +} while (0) + + +static void CDAudio_Eject(void) +{ + if (cdfile == -1 || !enabled) + return; + + ioctl(cdfile, CDIOCALLOW); + if (ioctl(cdfile, CDIOCEJECT) == -1) + IOCTL_FAILURE(CDIOCEJECT); +} + +static void CDAudio_CloseDoor(void) +{ + if (cdfile == -1 || !enabled) + return; + + ioctl(cdfile, CDIOCALLOW); + if (ioctl(cdfile, CDIOCCLOSE) == -1) + IOCTL_FAILURE(CDIOCCLOSE); +} + +static int CDAudio_GetAudioDiskInfo(void) +{ + struct ioc_toc_header tochdr; + + if (cdfile == -1) + return -1; + + cdValid = false; + + if (ioctl(cdfile, CDIOREADTOCHEADER, &tochdr) == -1) + { + IOCTL_FAILURE(CDIOREADTOCHEADER); + return -1; + } + + if (tochdr.starting_track < 1) + { + Con_DPrintf("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = tochdr.ending_track; + + return 0; +} + +int CDAudio_Play(byte track, qboolean looping) +{ + struct ioc_read_toc_entry entry; + struct cd_toc_entry toc_buffer; + struct ioc_play_track ti; + + if (cdfile == -1 || !enabled) + return -1; + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + return -1; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) + { + Con_DPrintf("CDAudio: Bad track number %u.\n", track); + return -1; + } + + /* don't try to play a non-audio track */ +# define CDROM_DATA_TRACK 4 + memset((char *)&toc_buffer, 0, sizeof(toc_buffer)); + entry.data_len = sizeof(toc_buffer); + entry.data = &toc_buffer; + entry.starting_track = track; + entry.address_format = CD_MSF_FORMAT; + if (ioctl(cdfile, CDIOREADTOCENTRYS, &entry) == -1) + { + IOCTL_FAILURE(CDIOREADTOCENTRYS); + return -1; + } + if (toc_buffer.control & CDROM_DATA_TRACK) + { + Con_Printf("CDAudio: track %i is not audio\n", track); + return -1; + } + + if (playing) + { + if (playTrack == track) + return 0; + CDAudio_Stop(); + } + + ti.start_track = track; + ti.end_track = track; + ti.start_index = 1; + ti.end_index = 99; + + if (ioctl(cdfile, CDIOCPLAYTRACKS, &ti) == -1) + { + IOCTL_FAILURE(CDIOCPLAYTRACKS); + return -1; + } + + if (ioctl(cdfile, CDIOCRESUME) == -1) + { + IOCTL_FAILURE(CDIOCRESUME); + return -1; + } + + playLooping = looping; + playTrack = track; + playing = true; + + if (bgmvolume.value == 0) /* don't bother advancing */ + CDAudio_Pause (); + + return 0; +} + +void CDAudio_Stop(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if (ioctl(cdfile, CDIOCSTOP) == -1) + { + IOCTL_FAILURE(CDIOCSTOP); + return; + } + ioctl(cdfile, CDIOCALLOW); + + wasPlaying = false; + playing = false; +} + +void CDAudio_Pause(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if (ioctl(cdfile, CDIOCPAUSE) == -1) + IOCTL_FAILURE(CDIOCPAUSE); + + wasPlaying = playing; + playing = false; +} + +void CDAudio_Resume(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + if (ioctl(cdfile, CDIOCRESUME) == -1) + IOCTL_FAILURE(CDIOCRESUME); + playing = true; +} + +static void CD_f (void) +{ + const char *command; + int ret, n; + + if (Cmd_Argc() < 2) + { + Con_Printf("commands:"); + Con_Printf("on, off, reset, remap, \n"); + Con_Printf("play, stop, loop, pause, resume\n"); + Con_Printf("eject, close, info\n"); + return; + } + + command = Cmd_Argv (1); + + if (Q_strcasecmp(command, "on") == 0) + { + enabled = true; + return; + } + + if (Q_strcasecmp(command, "off") == 0) + { + if (playing) + CDAudio_Stop(); + enabled = false; + return; + } + + if (Q_strcasecmp(command, "reset") == 0) + { + enabled = true; + if (playing) + CDAudio_Stop(); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo(); + return; + } + + if (Q_strcasecmp(command, "remap") == 0) + { + ret = Cmd_Argc() - 2; + if (ret <= 0) + { + for (n = 1; n < 100; n++) + if (remap[n] != n) + Con_Printf(" %u -> %u\n", n, remap[n]); + return; + } + for (n = 1; n <= ret; n++) + remap[n] = atoi(Cmd_Argv (n+1)); + return; + } + + if (Q_strcasecmp(command, "close") == 0) + { + CDAudio_CloseDoor(); + return; + } + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + { + Con_Printf("No CD in player.\n"); + return; + } + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); + return; + } + + if (Q_strcasecmp(command, "stop") == 0) + { + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "pause") == 0) + { + CDAudio_Pause(); + return; + } + + if (Q_strcasecmp(command, "resume") == 0) + { + CDAudio_Resume(); + return; + } + + if (Q_strcasecmp(command, "eject") == 0) + { + if (playing) + CDAudio_Stop(); + CDAudio_Eject(); + cdValid = false; + return; + } + + if (Q_strcasecmp(command, "info") == 0) + { + Con_Printf("%u tracks\n", maxTrack); + if (playing) + Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); + else if (wasPlaying) + Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); + Con_Printf("Volume is %f\n", bgmvolume.value); + return; + } +} + +static qboolean CD_GetVolume (struct ioc_vol *vol) +{ + if (ioctl(cdfile, CDIOCGETVOL, vol) == -1) + { + IOCTL_FAILURE(CDIOCGETVOL); + return false; + } + return true; +} + +static qboolean CD_SetVolume (struct ioc_vol *vol) +{ + if (ioctl(cdfile, CDIOCSETVOL, vol) == -1) + { + IOCTL_FAILURE(CDIOCSETVOL); + return false; + } + return true; +} + +static qboolean CDAudio_SetVolume (float value) +{ + if (cdfile == -1 || !enabled) + return false; + + old_cdvolume = value; + + if (value == 0.0f) + CDAudio_Pause (); + else + CDAudio_Resume(); + + if (!hw_vol_works) + { + return false; + } + else + { + drv_vol.vol[0] = drv_vol.vol[2] = + drv_vol.vol[1] = drv_vol.vol[3] = value * 255.0f; + return CD_SetVolume (&drv_vol); + } +} + +void CDAudio_Update(void) +{ + struct ioc_read_subchannel subchnl; + struct cd_sub_channel_info data; + static time_t lastchk; + + if (cdfile == -1 || !enabled) + return; + + if (old_cdvolume != bgmvolume.value) + CDAudio_SetVolume (bgmvolume.value); + + if (playing && lastchk < time(NULL)) + { + lastchk = time(NULL) + 2; /* two seconds between chks */ + + memset (&subchnl, 0, sizeof(subchnl)); + subchnl.data = &data; + subchnl.data_len = sizeof(data); + subchnl.address_format = CD_MSF_FORMAT; + subchnl.data_format = CD_CURRENT_POSITION; + subchnl.track = playTrack; + if (ioctl(cdfile, CDIOCREADSUBCHANNEL, &subchnl) == -1) + { + IOCTL_FAILURE(CDIOCREADSUBCHANNEL); + playing = false; + return; + } + if (data.header.audio_status != CD_AS_PLAY_IN_PROGRESS && + data.header.audio_status != CD_AS_PLAY_PAUSED) + { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + else + { + playTrack = data.what.position.track_number; + } + } +} + +int CDAudio_Init(void) +{ + int i; + + if (safemode || COM_CheckParm("-nocdaudio")) + return -1; + + if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) + cd_dev = com_argv[i + 1]; + + if ((cdfile = open(cd_dev, O_RDONLY | O_NONBLOCK)) == -1) + { + i = errno; + Con_Printf("CDAudio_Init: open of \"%s\" failed (%d: %s)\n", + cd_dev, i, strerror(i)); + cdfile = -1; + return -1; + } + + for (i = 0; i < 100; i++) + remap[i] = i; + initialized = true; + enabled = true; + old_cdvolume = bgmvolume.value; + + Con_Printf("CDAudio initialized (using BSD ioctls)\n"); + + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in drive\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f); + + hw_vol_works = CD_GetVolume (&orig_vol); + if (hw_vol_works) + hw_vol_works = CDAudio_SetVolume (bgmvolume.value); + + return 0; +} + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); + if (hw_vol_works) + CD_SetVolume (&orig_vol); + close(cdfile); + cdfile = -1; +} + +#endif /* __USE_BSD_CDROM__ */ + diff --git a/quakespasm/Quake/cd_linux.c b/quakespasm/Quake/cd_linux.c new file mode 100644 index 00000000..caad3be8 --- /dev/null +++ b/quakespasm/Quake/cd_linux.c @@ -0,0 +1,487 @@ +/* + cd_linux.c + + Copyright (C) 1996-1997 Id Software, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + + +#include "cd_unix.h" + +#ifdef __USE_LINUX_CDROM__ + +#include "quakedef.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = true; +static qboolean playLooping = false; +static byte remap[100]; +static byte playTrack; +static byte maxTrack; + +static int cdfile = -1; +static const char default_dev[] = _PATH_DEV "cdrom"; /* user can always do -cddev */ +static const char *cd_dev = default_dev; + +static float old_cdvolume; +static qboolean hw_vol_works = true; +static struct cdrom_volctrl orig_vol; /* original setting to be restored upon exit */ +static struct cdrom_volctrl drv_vol; /* the volume setting we'll be using */ + + +#define IOCTL_FAILURE(__name) do { \ + int __err = errno; \ + Con_DPrintf("ioctl %s failed (%d: %s)\n", #__name, __err, strerror(__err)); \ +} while (0) + + +static void CDAudio_Eject(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (ioctl(cdfile, CDROMEJECT) == -1) + IOCTL_FAILURE(CDROMEJECT); +} + +static void CDAudio_CloseDoor(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (ioctl(cdfile, CDROMCLOSETRAY) == -1) + IOCTL_FAILURE(CDROMCLOSETRAY); +} + +static int CDAudio_GetAudioDiskInfo(void) +{ + struct cdrom_tochdr tochdr; + + if (cdfile == -1) + return -1; + + cdValid = false; + + if (ioctl(cdfile, CDROMREADTOCHDR, &tochdr) == -1) + { + IOCTL_FAILURE(CDROMREADTOCHDR); + return -1; + } + + if (tochdr.cdth_trk0 < 1) + { + Con_DPrintf("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = tochdr.cdth_trk1; + + return 0; +} + +int CDAudio_Play(byte track, qboolean looping) +{ + struct cdrom_tocentry entry; + struct cdrom_ti ti; + + if (cdfile == -1 || !enabled) + return -1; + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + return -1; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) + { + Con_DPrintf("CDAudio: Bad track number %u.\n", track); + return -1; + } + + /* don't try to play a non-audio track */ + entry.cdte_track = track; + entry.cdte_format = CDROM_MSF; + if (ioctl(cdfile, CDROMREADTOCENTRY, &entry) == -1) + { + IOCTL_FAILURE(CDROMREADTOCENTRY); + return -1; + } + if (entry.cdte_ctrl == CDROM_DATA_TRACK) + { + Con_Printf("CDAudio: track %i is not audio\n", track); + return -1; + } + + if (playing) + { + if (playTrack == track) + return 0; + CDAudio_Stop(); + } + + ti.cdti_trk0 = track; + ti.cdti_trk1 = track; + ti.cdti_ind0 = 1; + ti.cdti_ind1 = 99; + + if (ioctl(cdfile, CDROMPLAYTRKIND, &ti) == -1) + { + IOCTL_FAILURE(CDROMPLAYTRKIND); + return -1; + } + + if (ioctl(cdfile, CDROMRESUME) == -1) + { + IOCTL_FAILURE(CDROMRESUME); + return -1; + } + + playLooping = looping; + playTrack = track; + playing = true; + + if (bgmvolume.value == 0) /* don't bother advancing */ + CDAudio_Pause (); + + return 0; +} + +void CDAudio_Stop(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if (ioctl(cdfile, CDROMSTOP) == -1) + IOCTL_FAILURE(CDROMSTOP); + + wasPlaying = false; + playing = false; +} + +void CDAudio_Pause(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!playing) + return; + + if (ioctl(cdfile, CDROMPAUSE) == -1) + IOCTL_FAILURE(CDROMPAUSE); + + wasPlaying = playing; + playing = false; +} + +void CDAudio_Resume(void) +{ + if (cdfile == -1 || !enabled) + return; + + if (!cdValid) + return; + + if (!wasPlaying) + return; + + if (ioctl(cdfile, CDROMRESUME) == -1) + IOCTL_FAILURE(CDROMRESUME); + playing = true; +} + +static void CD_f (void) +{ + const char *command; + int ret, n; + + if (Cmd_Argc() < 2) + { + Con_Printf("commands:"); + Con_Printf("on, off, reset, remap, \n"); + Con_Printf("play, stop, loop, pause, resume\n"); + Con_Printf("eject, close, info\n"); + return; + } + + command = Cmd_Argv (1); + + if (Q_strcasecmp(command, "on") == 0) + { + enabled = true; + return; + } + + if (Q_strcasecmp(command, "off") == 0) + { + if (playing) + CDAudio_Stop(); + enabled = false; + return; + } + + if (Q_strcasecmp(command, "reset") == 0) + { + enabled = true; + if (playing) + CDAudio_Stop(); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo(); + return; + } + + if (Q_strcasecmp(command, "remap") == 0) + { + ret = Cmd_Argc() - 2; + if (ret <= 0) + { + for (n = 1; n < 100; n++) + if (remap[n] != n) + Con_Printf(" %u -> %u\n", n, remap[n]); + return; + } + for (n = 1; n <= ret; n++) + remap[n] = atoi(Cmd_Argv (n+1)); + return; + } + + if (Q_strcasecmp(command, "close") == 0) + { + CDAudio_CloseDoor(); + return; + } + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + { + Con_Printf("No CD in player.\n"); + return; + } + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); + return; + } + + if (Q_strcasecmp(command, "stop") == 0) + { + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "pause") == 0) + { + CDAudio_Pause(); + return; + } + + if (Q_strcasecmp(command, "resume") == 0) + { + CDAudio_Resume(); + return; + } + + if (Q_strcasecmp(command, "eject") == 0) + { + if (playing) + CDAudio_Stop(); + CDAudio_Eject(); + cdValid = false; + return; + } + + if (Q_strcasecmp(command, "info") == 0) + { + Con_Printf("%u tracks\n", maxTrack); + if (playing) + Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); + else if (wasPlaying) + Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); + Con_Printf("Volume is %f\n", bgmvolume.value); + return; + } +} + +static qboolean CD_GetVolume (struct cdrom_volctrl *vol) +{ + if (ioctl(cdfile, CDROMVOLREAD, vol) == -1) + { + IOCTL_FAILURE(CDROMVOLREAD); + return false; + } + return true; +} + +static qboolean CD_SetVolume (struct cdrom_volctrl *vol) +{ + if (ioctl(cdfile, CDROMVOLCTRL, vol) == -1) + { + IOCTL_FAILURE(CDROMVOLCTRL); + return false; + } + return true; +} + +static qboolean CDAudio_SetVolume (float value) +{ + if (cdfile == -1 || !enabled) + return false; + + old_cdvolume = value; + + if (value == 0.0f) + CDAudio_Pause (); + else + CDAudio_Resume(); + + if (!hw_vol_works) + { + return false; + } + else + { + drv_vol.channel0 = drv_vol.channel2 = + drv_vol.channel1 = drv_vol.channel3 = value * 255.0f; + return CD_SetVolume (&drv_vol); + } +} + +void CDAudio_Update(void) +{ + struct cdrom_subchnl subchnl; + static time_t lastchk; + + if (cdfile == -1 || !enabled) + return; + + if (old_cdvolume != bgmvolume.value) + CDAudio_SetVolume (bgmvolume.value); + + if (playing && lastchk < time(NULL)) + { + lastchk = time(NULL) + 2; /* two seconds between chks */ + subchnl.cdsc_format = CDROM_MSF; + if (ioctl(cdfile, CDROMSUBCHNL, &subchnl) == -1) + { + IOCTL_FAILURE(CDROMSUBCHNL); + playing = false; + return; + } + if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY && + subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED) + { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + else + { + playTrack = subchnl.cdsc_trk; + } + } +} + +int CDAudio_Init(void) +{ + int i; + + if (safemode || COM_CheckParm("-nocdaudio")) + return -1; + + if ((i = COM_CheckParm("-cddev")) != 0 && i < com_argc - 1) + cd_dev = com_argv[i + 1]; + + if ((cdfile = open(cd_dev, O_RDONLY | O_NONBLOCK)) == -1) + { + i = errno; + Con_Printf("CDAudio_Init: open of \"%s\" failed (%d: %s)\n", + cd_dev, i, strerror(i)); + cdfile = -1; + return -1; + } + + for (i = 0; i < 100; i++) + remap[i] = i; + initialized = true; + enabled = true; + old_cdvolume = bgmvolume.value; + + Con_Printf("CDAudio initialized (using Linux ioctls)\n"); + + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in drive\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f); + + hw_vol_works = CD_GetVolume (&orig_vol); + if (hw_vol_works) + hw_vol_works = CDAudio_SetVolume (bgmvolume.value); + + return 0; +} + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); + if (hw_vol_works) + CD_SetVolume (&orig_vol); + close(cdfile); + cdfile = -1; +} + +#endif /* __USE_LINUX_CDROM__ */ + diff --git a/quakespasm/Quake/cd_unix.h b/quakespasm/Quake/cd_unix.h new file mode 100644 index 00000000..13eb551e --- /dev/null +++ b/quakespasm/Quake/cd_unix.h @@ -0,0 +1,49 @@ +/* + cd_unix.h + Unix include file to compile the correct cdaudio code + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + + +#ifndef __CD_UNIX_H +#define __CD_UNIX_H + +#undef __USE_BSD_CDROM__ +#undef __USE_LINUX_CDROM__ +#undef __USE_SDL_CDROM__ + +#if defined (WITH_SDLCD) +/* This means that the makefile is edited for USE_SDLCD */ +# define __USE_SDL_CDROM__ 1 +#elif defined (__linux) || defined (__linux__) +# define __USE_LINUX_CDROM__ 1 +#elif defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__NetBSD__) +# define __USE_BSD_CDROM__ 1 +#else /* elif defined (SDLQUAKE) */ +# define __USE_SDL_CDROM__ 1 +/* +#else +# error "no cdaudio module defined. edit cd_unix.h or your makefile.." +*/ +#endif + +#endif /* __CD_UNIX_H */ + diff --git a/quakespasm/Quake/cd_win.c b/quakespasm/Quake/cd_win.c new file mode 100644 index 00000000..2d0ab2b1 --- /dev/null +++ b/quakespasm/Quake/cd_win.c @@ -0,0 +1,619 @@ +/* + cd_win.c + Win32 cdaudio code + + Copyright (C) 1996-1997 Id Software, Inc. + Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All + rights reserved. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA +*/ + +#include "quakedef.h" +#include "winquake.h" +#include + +/* + * You just can't set the volume of CD playback via MCI : + * http://blogs.msdn.com/larryosterman/archive/2005/10/06/477874.aspx + * OTOH, using the aux APIs to control the CD audio volume is broken. + */ +#undef USE_AUX_API + + +static qboolean cdValid = false; +static qboolean playing = false; +static qboolean wasPlaying = false; +static qboolean initialized = false; +static qboolean enabled = false; +static qboolean playLooping = false; +static byte remap[100]; +static byte playTrack; +static byte maxTrack; + +static float old_cdvolume; +static UINT wDeviceID; +static DWORD end_pos; +#if defined(USE_AUX_API) +static UINT CD_ID; +static unsigned long CD_OrigVolume; +static void CD_SetVolume(unsigned long Volume); +#endif /* USE_AUX_API */ + + +static void CDAudio_Eject(void) +{ + DWORD dwReturn; + + dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, (DWORD_PTR)NULL); + if (dwReturn) + Con_DPrintf("MCI_SET_DOOR_OPEN failed (%u)\n", (unsigned int)dwReturn); +} + + +static void CDAudio_CloseDoor(void) +{ + DWORD dwReturn; + + dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, (DWORD_PTR)NULL); + if (dwReturn) + Con_DPrintf("MCI_SET_DOOR_CLOSED failed (%u)\n", (unsigned int)dwReturn); +} + + +static int CDAudio_GetAudioDiskInfo(void) +{ + DWORD dwReturn; + MCI_STATUS_PARMS mciStatusParms; + + cdValid = false; + + mciStatusParms.dwItem = MCI_STATUS_READY; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: drive ready test - get status failed\n"); + return -1; + } + if (!mciStatusParms.dwReturn) + { + Con_DPrintf("CDAudio: drive not ready\n"); + return -1; + } + + mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: get tracks - status failed\n"); + return -1; + } + if (mciStatusParms.dwReturn < 1) + { + Con_DPrintf("CDAudio: no music tracks\n"); + return -1; + } + + cdValid = true; + maxTrack = mciStatusParms.dwReturn; + + return 0; +} + + +int CDAudio_Play(byte track, qboolean looping) +{ + DWORD dwReturn; + MCI_PLAY_PARMS mciPlayParms; + MCI_STATUS_PARMS mciStatusParms; + + if (!enabled) + return -1; + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + return -1; + } + + track = remap[track]; + + if (track < 1 || track > maxTrack) + { + Con_DPrintf("CDAudio: Bad track number %u.\n", track); + return -1; + } + + // don't try to play a non-audio track + mciStatusParms.dwItem = MCI_CDA_STATUS_TYPE_TRACK; + mciStatusParms.dwTrack = track; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("MCI_STATUS failed (%u)\n", (unsigned int)dwReturn); + return -1; + } + if (mciStatusParms.dwReturn != MCI_CDA_TRACK_AUDIO) + { + Con_Printf("CDAudio: track %i is not audio\n", track); + return -1; + } + + // get the length of the track to be played + mciStatusParms.dwItem = MCI_STATUS_LENGTH; + mciStatusParms.dwTrack = track; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("MCI_STATUS failed (%u)\n", (unsigned int)dwReturn); + return -1; + } + + if (playing) + { + if (playTrack == track) + return 0; + CDAudio_Stop(); + } + + mciPlayParms.dwFrom = MCI_MAKE_TMSF(track, 0, 0, 0); + mciPlayParms.dwTo = (mciStatusParms.dwReturn << 8) | track; + end_pos = mciPlayParms.dwTo; + mciPlayParms.dwCallback = (DWORD_PTR)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM | MCI_TO, (DWORD_PTR)(LPVOID) &mciPlayParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: MCI_PLAY failed (%u)\n", (unsigned int)dwReturn); + return -1; + } + + playLooping = looping; + playTrack = track; + playing = true; + + if (bgmvolume.value == 0) /* don't bother advancing */ + CDAudio_Pause (); + + return 0; +} + + +void CDAudio_Stop(void) +{ + DWORD dwReturn; + + if (!enabled) + return; + + if (!playing) + return; + + dwReturn = mciSendCommand(wDeviceID, MCI_STOP, 0, (DWORD_PTR)NULL); + if (dwReturn) + Con_DPrintf("MCI_STOP failed (%u)", (unsigned int)dwReturn); + + wasPlaying = false; + playing = false; +} + + +void CDAudio_Pause(void) +{ + DWORD dwReturn; + MCI_GENERIC_PARMS mciGenericParms; + + if (!enabled) + return; + + if (!playing) + return; + + mciGenericParms.dwCallback = (DWORD_PTR)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PAUSE, 0, (DWORD_PTR)(LPVOID) &mciGenericParms); + if (dwReturn) + Con_DPrintf("MCI_PAUSE failed (%u)", (unsigned int)dwReturn); + + wasPlaying = playing; + playing = false; +} + + +void CDAudio_Resume(void) +{ + DWORD dwReturn; + MCI_STATUS_PARMS mciStatusParms; + MCI_PLAY_PARMS mciPlayParms; + + if (!enabled) + return; + if (!cdValid) + return; + if (!wasPlaying) + return; + +#if 0 +/* dwReturn = mciSendCommand(wDeviceID, MCI_RESUME, MCI_WAIT, NULL); */ + mciPlayParms.dwFrom = MCI_MAKE_TMSF(playTrack, 0, 0, 0); + mciPlayParms.dwTo = MCI_MAKE_TMSF(playTrack + 1, 0, 0, 0); + mciPlayParms.dwCallback = (DWORD_PTR)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_TO | MCI_NOTIFY, (DWORD_PTR)(LPVOID) &mciPlayParms); +#endif + mciStatusParms.dwItem = MCI_STATUS_POSITION; + dwReturn = mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR) (LPVOID) &mciStatusParms); + if (dwReturn) + { + Con_DPrintf("MCI_STATUS failed (%u)\n", (unsigned int)dwReturn); + return; + } + mciPlayParms.dwFrom = mciStatusParms.dwReturn; + mciPlayParms.dwTo = end_pos; /* set in CDAudio_Play() */ + mciPlayParms.dwCallback = (DWORD_PTR)mainwindow; + dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_FROM | MCI_TO | MCI_NOTIFY, (DWORD_PTR)(LPVOID) &mciPlayParms); + if (dwReturn) + { + Con_DPrintf("CDAudio: MCI_PLAY failed (%u)\n", (unsigned int)dwReturn); + return; + } + playing = true; +} + + +static void CD_f (void) +{ + const char *command; + int ret, n; + + if (Cmd_Argc() < 2) + { + Con_Printf("commands:"); + Con_Printf("on, off, reset, remap, \n"); + Con_Printf("play, stop, loop, pause, resume\n"); + Con_Printf("eject, close, info\n"); + return; + } + + command = Cmd_Argv (1); + + if (Q_strcasecmp(command, "on") == 0) + { + enabled = true; + return; + } + + if (Q_strcasecmp(command, "off") == 0) + { + if (playing) + CDAudio_Stop(); + enabled = false; + return; + } + + if (Q_strcasecmp(command, "reset") == 0) + { + enabled = true; + if (playing) + CDAudio_Stop(); + for (n = 0; n < 100; n++) + remap[n] = n; + CDAudio_GetAudioDiskInfo(); + return; + } + + if (Q_strcasecmp(command, "remap") == 0) + { + ret = Cmd_Argc() - 2; + if (ret <= 0) + { + for (n = 1; n < 100; n++) + if (remap[n] != n) + Con_Printf(" %u -> %u\n", n, remap[n]); + return; + } + for (n = 1; n <= ret; n++) + remap[n] = atoi(Cmd_Argv (n+1)); + return; + } + + if (Q_strcasecmp(command, "close") == 0) + { + CDAudio_CloseDoor(); + return; + } + + if (!cdValid) + { + CDAudio_GetAudioDiskInfo(); + if (!cdValid) + { + Con_Printf("No CD in player.\n"); + return; + } + } + + if (Q_strcasecmp(command, "play") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), false); + return; + } + + if (Q_strcasecmp(command, "loop") == 0) + { + CDAudio_Play((byte)atoi(Cmd_Argv (2)), true); + return; + } + + if (Q_strcasecmp(command, "stop") == 0) + { + CDAudio_Stop(); + return; + } + + if (Q_strcasecmp(command, "pause") == 0) + { + CDAudio_Pause(); + return; + } + + if (Q_strcasecmp(command, "resume") == 0) + { + CDAudio_Resume(); + return; + } + + if (Q_strcasecmp(command, "eject") == 0) + { + if (playing) + CDAudio_Stop(); + CDAudio_Eject(); + cdValid = false; + return; + } + + if (Q_strcasecmp(command, "info") == 0) + { + Con_Printf("%u tracks\n", maxTrack); + if (playing) + Con_Printf("Currently %s track %u\n", playLooping ? "looping" : "playing", playTrack); + else if (wasPlaying) + Con_Printf("Paused %s track %u\n", playLooping ? "looping" : "playing", playTrack); + Con_Printf("Volume is %f\n", bgmvolume.value); + return; + } +} + + +LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (lParam != wDeviceID) + return 1; + + switch (wParam) + { + case MCI_NOTIFY_SUCCESSFUL: + if (playing) + { + playing = false; + if (playLooping) + CDAudio_Play(playTrack, true); + } + break; + + case MCI_NOTIFY_ABORTED: + case MCI_NOTIFY_SUPERSEDED: + break; + + case MCI_NOTIFY_FAILURE: + Con_DPrintf("MCI_NOTIFY_FAILURE\n"); + CDAudio_Stop (); + cdValid = false; + break; + + default: + Con_DPrintf("Unexpected MM_MCINOTIFY type (%Iu)\n", wParam); + return 1; + } + + return 0; +} + + +static void CDAudio_SetVolume (float value) +{ + old_cdvolume = value; + + if (value == 0.0f) + CDAudio_Pause (); + else + CDAudio_Resume(); + +#if defined(USE_AUX_API) + CD_SetVolume (value * 0xffff); +#endif /* USE_AUX_API */ +} + +void CDAudio_Update(void) +{ + if (!enabled) + return; + + if (old_cdvolume != bgmvolume.value) + CDAudio_SetVolume (bgmvolume.value); +} + + +#if defined(USE_AUX_API) +static void CD_FindCDAux(void) +{ + UINT NumDevs, counter; + MMRESULT Result; + AUXCAPS Caps; + + CD_ID = -1; + if (!COM_CheckParm("-usecdvolume")) + return; + NumDevs = auxGetNumDevs(); + for (counter = 0; counter < NumDevs; counter++) + { + Result = auxGetDevCaps(counter,&Caps,sizeof(Caps)); + if (!Result) // valid + { + if (Caps.wTechnology == AUXCAPS_CDAUDIO) + { + CD_ID = counter; + auxGetVolume(CD_ID, &CD_OrigVolume); + return; + } + } + } +} + +static void CD_SetVolume(unsigned long Volume) +{ + if (CD_ID != -1) + auxSetVolume(CD_ID, (Volume<<16) + Volume); +} +#endif /* USE_AUX_API */ + +static const char *get_cddev_arg (const char *arg) +{ +/* arg should be like "D", "D:" or "D:\", make + * sure it is so. Also check if this is really + * a CDROM drive. */ + static char drive[4]; + if (!arg || ! *arg) + return NULL; + if (arg[1] != '\0') + { + if (arg[1] != ':') + return NULL; + if (arg[2] != '\0') + { + if (arg[2] != '\\' && + arg[2] != '/') + return NULL; + if (arg[3] != '\0') + return NULL; + } + } + if (*arg >= 'A' && *arg <= 'Z') + { + drive[0] = *arg; + drive[1] = ':'; + drive[2] = '\\'; + drive[3] = '\0'; + } + else if (*arg >= 'a' && *arg <= 'z') + { + /* make it uppercase */ + drive[0] = *arg - ('a' - 'A'); + drive[1] = ':'; + drive[2] = '\\'; + drive[3] = '\0'; + } + else + { + return NULL; + } + if (GetDriveType(drive) != DRIVE_CDROM) + { + Con_Printf("%c is not a CDROM drive\n", drive[0]); + return NULL; + } + drive[2] = '\0'; + return drive; +} + +int CDAudio_Init(void) +{ + DWORD dwReturn; + MCI_OPEN_PARMS mciOpenParms; + MCI_SET_PARMS mciSetParms; + const char *userdev = NULL; + int n; + + if (safemode || COM_CheckParm("-nocdaudio")) + return -1; + + if ((n = COM_CheckParm("-cddev")) != 0 && n < com_argc - 1) + { + userdev = get_cddev_arg(com_argv[n + 1]); + if (!userdev) + { + Con_Printf("Invalid argument to -cddev\n"); + return -1; + } + mciOpenParms.lpstrElementName = userdev; + } + + mciOpenParms.lpstrDeviceType = "cdaudio"; + dwReturn = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD_PTR) (LPVOID) &mciOpenParms); + if (!userdev) + userdev = "default cdrom"; + if (dwReturn) + { + Con_Printf("CDAudio_Init: MCI_OPEN failed for %s (%u)\n", + userdev, (unsigned int)dwReturn); + return -1; + } + wDeviceID = mciOpenParms.wDeviceID; + + // Set the time format to track/minute/second/frame (TMSF). + mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; + dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID) &mciSetParms); + if (dwReturn) + { + Con_Printf("MCI_SET_TIME_FORMAT failed (%u)\n", (unsigned int)dwReturn); + mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD_PTR)NULL); + return -1; + } + + for (n = 0; n < 100; n++) + remap[n] = n; + initialized = true; + enabled = true; + old_cdvolume = bgmvolume.value; + + if (CDAudio_GetAudioDiskInfo()) + { + Con_Printf("CDAudio_Init: No CD in player.\n"); + cdValid = false; + } + + Cmd_AddCommand ("cd", CD_f); + +#if defined(USE_AUX_API) + CD_FindCDAux(); +#endif /* USE_AUX_API */ + + Con_Printf("CD Audio Initialized\n"); + + return 0; +} + + +void CDAudio_Shutdown(void) +{ + if (!initialized) + return; + CDAudio_Stop(); + if (mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD_PTR)NULL)) + Con_DPrintf("CDAudio_Shutdown: MCI_CLOSE failed\n"); +} +