Issue reported by Dagar and Pr3tty F1y, and confirmed as a bug by ripsaw8080. Thank you! This fixes the GoG release of Betrayal at Krondor which (either due to CD mastering issues or a faulty rip), requests playback of a given track at the tail end of the prior track. In debugging and performing this fix, many debug messages were improved as well as making some small small code adjustments, such as using iterators to point to individual tracks (track->attribute) instead of using the tracks array (tracks[track -1].attribute).
435 lines
15 KiB
C++
435 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2002-2019 The DOSBox Team
|
|
*
|
|
* 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 the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
#ifndef __CDROM_INTERFACE__
|
|
#define __CDROM_INTERFACE__
|
|
|
|
#define MAX_ASPI_CDROM 5
|
|
|
|
#include <string.h>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <SDL.h>
|
|
#include <SDL_thread.h>
|
|
|
|
#include "dosbox.h"
|
|
#include "mem.h"
|
|
#include "mixer.h"
|
|
#include "../libs/decoders/SDL_sound.h"
|
|
|
|
// CDROM data and audio format constants
|
|
#define BYTES_PER_RAW_REDBOOK_FRAME 2352
|
|
#define BYTES_PER_COOKED_REDBOOK_FRAME 2048
|
|
#define REDBOOK_FRAMES_PER_SECOND 75
|
|
#define MAX_REDBOOK_FRAMES 400000 // frames are Redbook's data unit
|
|
#define MAX_REDBOOK_SECTOR 399999 // a sector is the index to a frame
|
|
#define MAX_REDBOOK_TRACKS 99
|
|
#define MIN_REDBOOK_TRACKS 2 // One track plus the lead-out track
|
|
#define REDBOOK_PCM_BYTES_PER_MS 176.4f // 44.1 frames/ms * 4 bytes/frame
|
|
#define BYTES_PER_REDBOOK_PCM_FRAME 4 // 2 bytes/sample * 2 samples/frame
|
|
|
|
enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI };
|
|
|
|
typedef struct SMSF {
|
|
unsigned char min;
|
|
unsigned char sec;
|
|
unsigned char fr;
|
|
} TMSF;
|
|
|
|
typedef struct SCtrl {
|
|
Bit8u out[4]; // output channel mapping
|
|
Bit8u vol[4]; // channel volume (0 to 255)
|
|
} TCtrl;
|
|
|
|
// Conversion function from frames to Minutes/Second/Frames
|
|
//
|
|
template<typename T>
|
|
inline void frames_to_msf(int frames, T *m, T *s, T *f) {
|
|
*f = frames % REDBOOK_FRAMES_PER_SECOND;
|
|
frames /= REDBOOK_FRAMES_PER_SECOND;
|
|
*s = frames % 60;
|
|
frames /= 60;
|
|
*m = frames;
|
|
}
|
|
|
|
// Conversion function from Minutes/Second/Frames to frames
|
|
//
|
|
inline int msf_to_frames(int m, int s, int f) {
|
|
return m * 60 * REDBOOK_FRAMES_PER_SECOND + s * REDBOOK_FRAMES_PER_SECOND + f;
|
|
}
|
|
|
|
extern int CDROM_GetMountType(char* path, int force);
|
|
|
|
class CDROM_Interface
|
|
{
|
|
public:
|
|
virtual ~CDROM_Interface (void) {};
|
|
virtual bool SetDevice (char* path, int forceCD) = 0;
|
|
virtual bool GetUPC (unsigned char& attr, char* upc) = 0;
|
|
virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut) = 0;
|
|
virtual bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr) = 0;
|
|
virtual bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos) = 0;
|
|
virtual bool GetAudioStatus (bool& playing, bool& pause) = 0;
|
|
virtual bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen) = 0;
|
|
virtual bool PlayAudioSector (unsigned long start,unsigned long len) = 0;
|
|
virtual bool PauseAudio (bool resume) = 0;
|
|
virtual bool StopAudio (void) = 0;
|
|
virtual void ChannelControl (TCtrl ctrl) = 0;
|
|
virtual bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num) = 0;
|
|
virtual bool LoadUnloadMedia (bool unload) = 0;
|
|
virtual void InitNewMedia (void) {};
|
|
};
|
|
|
|
class CDROM_Interface_SDL : public CDROM_Interface
|
|
{
|
|
public:
|
|
CDROM_Interface_SDL (void);
|
|
virtual ~CDROM_Interface_SDL (void);
|
|
virtual bool SetDevice (char* path, int forceCD);
|
|
virtual bool GetUPC (unsigned char& attr, char* upc) { attr = 0; strcpy(upc,"UPC"); return true; };
|
|
virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
|
|
virtual bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
|
|
virtual bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
|
|
virtual bool GetAudioStatus (bool& playing, bool& pause);
|
|
virtual bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
|
|
virtual bool PlayAudioSector (unsigned long start,unsigned long len);
|
|
virtual bool PauseAudio (bool resume);
|
|
virtual bool StopAudio (void);
|
|
virtual void ChannelControl (TCtrl ctrl) { (void)ctrl; // unused but part of the API
|
|
return; };
|
|
virtual bool ReadSectors (PhysPt /*buffer*/, bool /*raw*/, unsigned long /*sector*/, unsigned long /*num*/) { return false; };
|
|
virtual bool LoadUnloadMedia (bool unload);
|
|
|
|
private:
|
|
bool Open (void);
|
|
void Close (void);
|
|
|
|
SDL_CD* cd;
|
|
int driveID;
|
|
Uint32 oldLeadOut;
|
|
};
|
|
|
|
class CDROM_Interface_Fake : public CDROM_Interface
|
|
{
|
|
public:
|
|
bool SetDevice (char* /*path*/, int /*forceCD*/) { return true; };
|
|
bool GetUPC (unsigned char& attr, char* upc) { attr = 0; strcpy(upc,"UPC"); return true; };
|
|
bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
|
|
bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
|
|
bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
|
|
bool GetAudioStatus (bool& playing, bool& pause);
|
|
bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
|
|
bool PlayAudioSector (unsigned long /*start*/,unsigned long /*len*/) { return true; };
|
|
bool PauseAudio (bool /*resume*/) { return true; };
|
|
bool StopAudio (void) { return true; };
|
|
void ChannelControl (TCtrl ctrl) { (void)ctrl; // unused by part of the API
|
|
return; };
|
|
bool ReadSectors (PhysPt /*buffer*/, bool /*raw*/, unsigned long /*sector*/, unsigned long /*num*/) { return true; };
|
|
bool LoadUnloadMedia (bool /*unload*/) { return true; };
|
|
};
|
|
|
|
class CDROM_Interface_Image : public CDROM_Interface
|
|
{
|
|
private:
|
|
// Nested Class Definitions
|
|
class TrackFile {
|
|
protected:
|
|
TrackFile(Bit16u _chunkSize) : chunkSize(_chunkSize) {}
|
|
public:
|
|
virtual bool read(Bit8u *buffer, int seek, int count) = 0;
|
|
virtual bool seek(Bit32u offset) = 0;
|
|
virtual Bit32u decode(Bit16s *buffer, Bit32u desired_track_frames) = 0;
|
|
virtual Bit16u getEndian() = 0;
|
|
virtual Bit32u getRate() = 0;
|
|
virtual Bit8u getChannels() = 0;
|
|
virtual int getLength() = 0;
|
|
virtual ~TrackFile() {}
|
|
const Bit16u chunkSize;
|
|
};
|
|
class BinaryFile : public TrackFile {
|
|
public:
|
|
BinaryFile (const char *filename, bool &error);
|
|
bool read(Bit8u *buffer, int seek, int count);
|
|
bool seek(Bit32u offset);
|
|
Bit32u decode(Bit16s *buffer, Bit32u desired_track_frames);
|
|
Bit16u getEndian();
|
|
Bit32u getRate() { return 44100; }
|
|
Bit8u getChannels() { return 2; }
|
|
int getLength();
|
|
~BinaryFile();
|
|
private:
|
|
std::ifstream *file;
|
|
BinaryFile();
|
|
};
|
|
|
|
class AudioFile : public TrackFile {
|
|
public:
|
|
AudioFile (const char *filename, bool &error);
|
|
bool read(Bit8u *buffer, int seek, int count) {
|
|
(void)buffer; // unused but part of the API
|
|
(void)seek; // ...
|
|
(void)count; // ...
|
|
return false; }
|
|
bool seek(Bit32u offset);
|
|
Bit32u decode(Bit16s *buffer, Bit32u desired_track_frames);
|
|
Bit16u getEndian();
|
|
Bit32u getRate();
|
|
Bit8u getChannels();
|
|
int getLength();
|
|
~AudioFile();
|
|
private:
|
|
Sound_Sample *sample;
|
|
AudioFile();
|
|
};
|
|
|
|
public:
|
|
// Nested struct definition
|
|
struct Track {
|
|
TrackFile *file;
|
|
int number;
|
|
int attr;
|
|
int start;
|
|
int length;
|
|
int skip;
|
|
int sectorSize;
|
|
bool mode2;
|
|
};
|
|
CDROM_Interface_Image (Bit8u _subUnit);
|
|
virtual ~CDROM_Interface_Image (void);
|
|
void InitNewMedia (void);
|
|
bool SetDevice (char* path, int forceCD);
|
|
bool GetUPC (unsigned char& attr, char* upc);
|
|
bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
|
|
bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
|
|
bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
|
|
bool GetAudioStatus (bool& playing, bool& pause);
|
|
bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
|
|
bool PlayAudioSector (unsigned long start,unsigned long len);
|
|
bool PauseAudio (bool resume);
|
|
bool StopAudio (void);
|
|
void ChannelControl (TCtrl ctrl);
|
|
bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
|
|
bool LoadUnloadMedia (bool unload);
|
|
bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector);
|
|
bool HasDataTrack (void);
|
|
static CDROM_Interface_Image* images[26];
|
|
|
|
private:
|
|
static struct imagePlayer {
|
|
Bit16s buffer[MIXER_BUFSIZE * 2]; // 2 channels (max)
|
|
SDL_mutex *mutex;
|
|
TrackFile *trackFile;
|
|
MixerChannel *channel;
|
|
CDROM_Interface_Image *cd;
|
|
void (MixerChannel::*addFrames) (Bitu, const Bit16s*);
|
|
Bit32u startSector;
|
|
Bit32u totalRedbookFrames;
|
|
Bit32u playedTrackFrames;
|
|
Bit32u totalTrackFrames;
|
|
bool isPlaying;
|
|
bool isPaused;
|
|
} player;
|
|
|
|
// Private utility functions
|
|
void ClearTracks();
|
|
bool LoadIsoFile(char *filename);
|
|
bool CanReadPVD(TrackFile *file, int sectorSize, bool mode2);
|
|
std::vector<Track>::iterator GetTrack(int sector);
|
|
static void CDAudioCallBack (Bitu desired_frames);
|
|
|
|
// Private functions for cue sheet processing
|
|
bool LoadCueSheet(char *cuefile);
|
|
bool GetRealFileName(std::string& filename, std::string& pathname);
|
|
bool GetCueKeyword(std::string &keyword, std::istream &in);
|
|
bool GetCueFrame(int &frames, std::istream &in);
|
|
bool GetCueString(std::string &str, std::istream &in);
|
|
bool AddTrack(Track &curr, int &shift, int prestart, int &totalPregap, int currPregap);
|
|
|
|
// member variables
|
|
std::vector<Track> tracks;
|
|
std::string mcn;
|
|
static int refCount;
|
|
Bit8u subUnit;
|
|
};
|
|
|
|
#if defined (WIN32) /* Win 32 */
|
|
|
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
|
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX // Don't clobber std::max and std::min
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include "wnaspi32.h" // Aspi stuff
|
|
|
|
class CDROM_Interface_Aspi : public CDROM_Interface
|
|
{
|
|
public:
|
|
CDROM_Interface_Aspi (void);
|
|
virtual ~CDROM_Interface_Aspi(void);
|
|
|
|
bool SetDevice (char* path, int forceCD);
|
|
|
|
bool GetUPC (unsigned char& attr, char* upc);
|
|
|
|
bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
|
|
bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
|
|
bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
|
|
bool GetAudioStatus (bool& playing, bool& pause);
|
|
bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
|
|
|
|
bool PlayAudioSector (unsigned long start,unsigned long len);
|
|
bool PauseAudio (bool resume);
|
|
bool StopAudio (void);
|
|
void ChannelControl (TCtrl ctrl) { return; };
|
|
|
|
bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
|
|
|
|
bool LoadUnloadMedia (bool unload);
|
|
|
|
private:
|
|
DWORD GetTOC (LPTOC toc);
|
|
HANDLE OpenIOCTLFile (char cLetter, BOOL bAsync);
|
|
void GetIOCTLAdapter (HANDLE hF,int * iDA,int * iDT,int * iDL);
|
|
bool ScanRegistryFindKey (HKEY& hKeyBase);
|
|
bool ScanRegistry (HKEY& hKeyBase);
|
|
BYTE GetHostAdapter (char* hardwareID);
|
|
bool GetVendor (BYTE HA_num, BYTE SCSI_Id, BYTE SCSI_Lun, char* szBuffer);
|
|
|
|
// ASPI stuff
|
|
BYTE haId;
|
|
BYTE target;
|
|
BYTE lun;
|
|
char letter;
|
|
|
|
// Windows stuff
|
|
HINSTANCE hASPI;
|
|
HANDLE hEvent; // global event
|
|
DWORD (*pGetASPI32SupportInfo) (void); // ptrs to aspi funcs
|
|
DWORD (*pSendASPI32Command) (LPSRB);
|
|
TMSF oldLeadOut;
|
|
};
|
|
|
|
class CDROM_Interface_Ioctl : public CDROM_Interface
|
|
{
|
|
public:
|
|
enum cdioctl_cdatype { CDIOCTL_CDA_DIO, CDIOCTL_CDA_MCI, CDIOCTL_CDA_DX };
|
|
cdioctl_cdatype cdioctl_cda_selected;
|
|
|
|
CDROM_Interface_Ioctl (CDROM_Interface_Ioctl::cdioctl_cdatype ioctl_cda);
|
|
virtual ~CDROM_Interface_Ioctl(void);
|
|
|
|
bool SetDevice (char* path, int forceCD);
|
|
|
|
bool GetUPC (unsigned char& attr, char* upc);
|
|
|
|
bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
|
|
bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
|
|
bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
|
|
bool GetAudioStatus (bool& playing, bool& pause);
|
|
bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
|
|
|
|
bool PlayAudioSector (unsigned long start,unsigned long len);
|
|
bool PauseAudio (bool resume);
|
|
bool StopAudio (void);
|
|
void ChannelControl (TCtrl ctrl);
|
|
|
|
bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector);
|
|
bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
|
|
|
|
bool LoadUnloadMedia (bool unload);
|
|
|
|
void InitNewMedia (void) { Close(); Open(); };
|
|
private:
|
|
|
|
bool Open (void);
|
|
void Close (void);
|
|
|
|
char pathname[32];
|
|
HANDLE hIOCTL;
|
|
TMSF oldLeadOut;
|
|
|
|
|
|
/* track start/length data */
|
|
bool track_start_valid;
|
|
int track_start_first,track_start_last;
|
|
int track_start[128];
|
|
|
|
bool GetAudioTracksAll (void);
|
|
|
|
|
|
/* mci audio cd interface */
|
|
bool use_mciplay;
|
|
int mci_devid;
|
|
|
|
bool mci_CDioctl (UINT msg, DWORD flags, void *arg);
|
|
bool mci_CDOpen (char drive);
|
|
bool mci_CDClose (void);
|
|
bool mci_CDPlay (int start, int length);
|
|
bool mci_CDPause (void);
|
|
bool mci_CDResume (void);
|
|
bool mci_CDStop (void);
|
|
int mci_CDStatus (void);
|
|
bool mci_CDPosition (int *position);
|
|
|
|
|
|
/* digital audio extraction cd interface */
|
|
static void dx_CDAudioCallBack(Bitu len);
|
|
|
|
bool use_dxplay;
|
|
static struct dxPlayer {
|
|
CDROM_Interface_Ioctl *cd;
|
|
MixerChannel *channel;
|
|
SDL_mutex *mutex;
|
|
Bit8u buffer[8192];
|
|
int bufLen;
|
|
int currFrame;
|
|
int targetFrame;
|
|
bool isPlaying;
|
|
bool isPaused;
|
|
bool ctrlUsed;
|
|
TCtrl ctrlData;
|
|
} player;
|
|
|
|
};
|
|
|
|
#endif /* WIN 32 */
|
|
|
|
#if defined (LINUX) || defined(OS2)
|
|
|
|
class CDROM_Interface_Ioctl : public CDROM_Interface_SDL
|
|
{
|
|
public:
|
|
CDROM_Interface_Ioctl (void);
|
|
|
|
bool SetDevice (char* path, int forceCD);
|
|
bool GetUPC (unsigned char& attr, char* upc);
|
|
bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
|
|
|
|
private:
|
|
char device_name[512];
|
|
};
|
|
|
|
#endif /* LINUX */
|
|
|
|
#endif /* __CDROM_INTERFACE__ */
|