DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #ifndef __CDROM_INTERFACE__ 00021 #define __CDROM_INTERFACE__ 00022 00023 #define MAX_ASPI_CDROM 5 00024 #define assertm(exp, msg) assert(((void)msg, exp)) 00025 00026 #include <string.h> 00027 #include <string> 00028 #include <iostream> 00029 #include <memory> 00030 #include <vector> 00031 #include <fstream> 00032 #include <sstream> 00033 #include <algorithm> 00034 00035 #include "SDL.h" 00036 #include "SDL_thread.h" 00037 00038 #include "dosbox.h" 00039 #include "mem.h" 00040 #include "mixer.h" 00041 #include "../libs/decoders/SDL_sound.h" 00042 00043 #if defined(C_SDL2) /* SDL 1.x defines this, SDL 2.x does not */ 00044 00048 #define CD_FPS 75 00049 #define FRAMES_TO_MSF(f, M,S,F) { \ 00050 int value = f; \ 00051 *(F) = value%CD_FPS; \ 00052 value /= CD_FPS; \ 00053 *(S) = value%60; \ 00054 value /= 60; \ 00055 *(M) = value; \ 00056 } 00057 #define MSF_TO_FRAMES(M, S, F) ((M)*60*CD_FPS+(S)*CD_FPS+(F)) 00058 #endif /* C_SDL2 */ 00059 00060 #define RAW_SECTOR_SIZE 2352 00061 #define COOKED_SECTOR_SIZE 2048 00062 00063 // CDROM data and audio format constants 00064 #define BYTES_PER_RAW_REDBOOK_FRAME 2352u 00065 #define BYTES_PER_COOKED_REDBOOK_FRAME 2048u 00066 #define REDBOOK_FRAMES_PER_SECOND 75u 00067 #define REDBOOK_CHANNELS 2u 00068 #define REDBOOK_BPS 2u // bytes per sample 00069 #define REDBOOK_PCM_FRAMES_PER_SECOND 44100u // also CD Audio sampling rate 00070 #define REDBOOK_FRAME_PADDING 150u // The relationship between High Sierra sectors and Redbook 00071 // frames is described by the equation: 00072 // Sector = Minute * 60 * 75 + Second * 75 + Frame - 150 00073 #define MAX_REDBOOK_FRAMES 400000u // frames are Redbook's data unit 00074 #define MAX_REDBOOK_SECTOR 399999u // a sector is the index to a frame 00075 #define MAX_REDBOOK_TRACKS 99u // a CD can contain 99 playable tracks plus the remaining leadout 00076 #define MIN_REDBOOK_TRACKS 2u // One track plus the lead-out track 00077 #define REDBOOK_PCM_BYTES_PER_MS 176.4f // 44.1 frames/ms * 4 bytes/frame 00078 #define REDBOOK_PCM_BYTES_PER_MIN 10584000u // 44.1 frames/ms * 4 bytes/frame * 1000 ms/s * 60 s/min 00079 #define BYTES_PER_REDBOOK_PCM_FRAME 4u // 2 bytes/sample * 2 samples/frame 00080 #define MAX_REDBOOK_BYTES (MAX_REDBOOK_FRAMES * BYTES_PER_RAW_REDBOOK_FRAME) // length of a CDROM in bytes 00081 #define MAX_REDBOOK_DURATION_MS (99 * 60 * 1000) // 99 minute CDROM in milliseconds 00082 00083 enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI }; 00084 00088 typedef struct SMSF { 00090 unsigned char min; 00092 unsigned char sec; 00094 unsigned char fr; 00095 } TMSF; 00096 00098 typedef struct SCtrl { 00100 Bit8u out[4]; 00102 Bit8u vol[4]; 00103 } TCtrl; 00104 00105 template<typename T1, typename T2> 00106 inline constexpr T1 ceil_udivide(const T1 x, const T2 y) noexcept { 00107 static_assert(std::is_unsigned<T1>::value, "First parameter should be unsigned"); 00108 static_assert(std::is_unsigned<T2>::value, "Second parameter should be unsigned"); 00109 return (x != 0) ? 1 + ((x - 1) / y) : 0; 00110 } 00111 00112 inline TMSF frames_to_msf(uint32_t frames) 00113 { 00114 TMSF msf = {0, 0, 0}; 00115 msf.fr = frames % REDBOOK_FRAMES_PER_SECOND; 00116 frames /= REDBOOK_FRAMES_PER_SECOND; 00117 msf.sec = frames % 60; 00118 frames /= 60; 00119 msf.min = static_cast<uint8_t>(frames); 00120 return msf; 00121 } 00122 00123 extern int CDROM_GetMountType(const char* path, int forceCD); 00124 00128 class CDROM_Interface 00129 { 00130 public: 00131 // CDROM_Interface (void); 00132 virtual ~CDROM_Interface (void) {}; 00133 00135 virtual bool SetDevice (char* path, int forceCD) = 0; 00136 00138 virtual bool GetUPC (unsigned char& attr, char* upc) = 0; 00139 00141 virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut) = 0; 00142 00144 virtual bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr) = 0; 00145 00147 virtual bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos) = 0; 00148 00150 virtual bool GetAudioStatus (bool& playing, bool& pause) = 0; 00151 00153 virtual bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen) = 0; 00154 00156 virtual bool PlayAudioSector (unsigned long start,unsigned long len) = 0; 00157 00159 virtual bool PauseAudio (bool resume) = 0; 00160 00162 virtual bool StopAudio (void) = 0; 00163 00165 virtual void ChannelControl (TCtrl ctrl) = 0; 00166 00168 virtual bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num) = 0; 00169 00171 virtual bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num) = 0; 00172 00174 virtual bool LoadUnloadMedia (bool unload) = 0; 00175 00177 virtual void InitNewMedia (void) {}; 00178 }; 00179 00183 class CDROM_Interface_SDL : public CDROM_Interface 00184 { 00185 public: 00186 CDROM_Interface_SDL (void); 00187 virtual ~CDROM_Interface_SDL(void); 00188 00189 /* base C++ class overrides, no documentation needed */ 00190 virtual bool SetDevice (char* path, int forceCD); 00191 virtual bool GetUPC (unsigned char& attr, char* upc) { attr = 0; strcpy(upc,"UPC"); return true; }; 00192 virtual bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut); 00193 virtual bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr); 00194 virtual bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos); 00195 virtual bool GetAudioStatus (bool& playing, bool& pause); 00196 virtual bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen); 00197 virtual bool PlayAudioSector (unsigned long start,unsigned long len); 00198 virtual bool PauseAudio (bool resume); 00199 virtual bool StopAudio (void); 00200 virtual void ChannelControl (TCtrl ctrl) { (void)ctrl; return; }; 00201 virtual bool ReadSectors (PhysPt /*buffer*/, bool /*raw*/, unsigned long /*sector*/, unsigned long /*num*/) { return false; }; 00202 /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ 00203 virtual bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); 00204 virtual bool LoadUnloadMedia (bool unload); 00205 00206 private: 00208 bool Open (void); 00209 00211 void Close (void); 00212 00213 #if !defined(C_SDL2) 00214 00215 SDL_CD* cd = NULL; 00216 #endif 00217 int driveID = 0; 00218 Uint32 oldLeadOut = 0; 00219 }; 00220 00224 class CDROM_Interface_Fake : public CDROM_Interface 00225 { 00226 public: 00227 bool SetDevice (char* /*path*/, int /*forceCD*/) { return true; }; 00228 bool GetUPC (unsigned char& attr, char* upc) { attr = 0; strcpy(upc,"UPC"); return true; }; 00229 bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut); 00230 bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr); 00231 bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos); 00232 bool GetAudioStatus (bool& playing, bool& pause); 00233 bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen); 00234 bool PlayAudioSector (unsigned long /*start*/,unsigned long /*len*/) { return true; }; 00235 bool PauseAudio (bool /*resume*/) { return true; }; 00236 bool StopAudio (void) { return true; }; 00237 void ChannelControl (TCtrl ctrl) { (void)ctrl; return; }; 00238 bool ReadSectors (PhysPt /*buffer*/, bool /*raw*/, unsigned long /*sector*/, unsigned long /*num*/) { return true; }; 00239 /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ 00240 bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); 00241 00242 bool LoadUnloadMedia (bool /*unload*/) { return true; }; 00243 }; 00244 00248 class CDROM_Interface_Image : public CDROM_Interface 00249 { 00250 private: 00251 // Nested Class Definitions 00252 class TrackFile { 00253 protected: 00254 TrackFile(Bit16u _chunkSize) : chunkSize(_chunkSize) {} 00255 bool offsetInsideTrack(const uint32_t offset); 00256 uint32_t adjustOverRead(const uint32_t offset, 00257 const uint32_t requested_bytes); 00258 int length_redbook_bytes = -1; 00259 00260 public: 00261 virtual ~TrackFile() = default; 00262 virtual bool read(uint8_t *buffer, 00263 const uint32_t offset, 00264 const uint32_t requested_bytes) = 0; 00265 virtual bool seek(const uint32_t offset) = 0; 00266 virtual uint32_t decode(int16_t *buffer, const uint32_t desired_track_frames) = 0; 00267 virtual Bit16u getEndian() = 0; 00268 virtual Bit32u getRate() = 0; 00269 virtual Bit8u getChannels() = 0; 00270 virtual int getLength() = 0; 00271 const Bit16u chunkSize = 0; 00272 }; 00273 00275 class BinaryFile : public TrackFile { 00276 public: 00277 BinaryFile (const char *filename, bool &error); 00278 ~BinaryFile (); 00279 00280 BinaryFile () = delete; 00281 BinaryFile (const BinaryFile&) = delete; // prevent copying 00282 BinaryFile& operator= (const BinaryFile&) = delete; // prevent assignment 00283 00284 bool read(uint8_t *buffer, 00285 const uint32_t offset, 00286 const uint32_t requested_bytes); 00287 bool seek(const uint32_t offset); 00288 uint32_t decode(int16_t *buffer, const uint32_t desired_track_frames); 00289 Bit16u getEndian(); 00290 Bit32u getRate() { return 44100; } 00291 Bit8u getChannels() { return 2; } 00292 int getLength(); 00293 private: 00294 std::ifstream *file; 00295 }; 00296 00297 class AudioFile : public TrackFile { 00298 public: 00299 AudioFile (const char *filename, bool &error); 00300 ~AudioFile (); 00301 00302 AudioFile () = delete; 00303 AudioFile (const AudioFile&) = delete; // prevent copying 00304 AudioFile& operator= (const AudioFile&) = delete; // prevent assignment 00305 00306 bool read(uint8_t *buffer, 00307 const uint32_t offset, 00308 const uint32_t requested_bytes); 00309 bool seek(const uint32_t offset); 00310 uint32_t decode(int16_t *buffer, const uint32_t desired_track_frames); 00311 Bit16u getEndian(); 00312 Bit32u getRate(); 00313 Bit8u getChannels(); 00314 int getLength(); 00315 private: 00316 Sound_Sample *sample = nullptr; 00317 // ensure the first seek isn't cached by starting with an impossibly-large position 00318 uint32_t track_pos = (std::numeric_limits<uint32_t>::max)(); 00319 }; 00320 00321 public: 00322 // Nested struct definition 00323 struct Track { 00324 std::shared_ptr<TrackFile> file = nullptr; 00325 uint32_t start = 0; 00326 uint32_t length = 0; 00327 uint32_t skip = 0; 00328 uint16_t sectorSize = 0; 00329 uint8_t number = 0; 00330 uint8_t attr = 0; 00331 bool mode2 = false; 00332 }; 00334 CDROM_Interface_Image (Bit8u subUnit); 00335 virtual ~CDROM_Interface_Image (void); 00336 void InitNewMedia (void) {}; 00337 bool SetDevice (char *path, int forceCD); 00338 bool GetUPC (unsigned char& attr, char* upc); 00339 bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut); 00340 bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr); 00341 bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos); 00342 bool GetAudioStatus (bool& playing, bool& pause); 00343 bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen); 00344 bool PlayAudioSector (unsigned long start, unsigned long len); 00345 bool PauseAudio (bool resume); 00346 bool StopAudio (void); 00347 void ChannelControl (TCtrl ctrl); 00348 bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num); 00349 /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ 00350 bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); 00351 bool LoadUnloadMedia (bool unload); 00353 bool ReadSector (uint8_t *buffer, const bool raw, const uint32_t sector); 00355 bool HasDataTrack (void); 00361 static bool images_init; 00365 static CDROM_Interface_Image* images[26]; 00366 00367 private: 00368 static struct imagePlayer { 00369 // Objects, pointers, and then scalars; in descending size-order. 00370 MixerObject mixerChannel = {}; 00371 std::weak_ptr<TrackFile> trackFile = {}; 00372 SDL_mutex *mutex = nullptr; 00373 MixerChannel *channel = nullptr; 00374 CDROM_Interface_Image *cd = nullptr; 00375 void (MixerChannel::*addFrames) (Bitu, const Bit16s*) = nullptr; 00376 uint32_t playedTrackFrames = 0; 00377 uint32_t totalTrackFrames = 0; 00378 uint32_t startSector = 0; 00379 uint32_t totalRedbookFrames = 0; 00380 int16_t buffer[MIXER_BUFSIZE * REDBOOK_CHANNELS] = {0}; 00381 bool isPlaying = false; 00382 bool isPaused = false; 00383 bool ctrlUsed; 00384 TCtrl ctrlData; 00385 } player; 00386 00387 // Private utility functions 00388 bool LoadIsoFile(char *filename); 00389 bool CanReadPVD(TrackFile *file, 00390 const uint16_t sectorSize, 00391 const bool mode2); 00392 std::vector<Track>::iterator GetTrack(const uint32_t sector); 00393 static void CDAudioCallBack (Bitu desired_frames); 00394 00395 // Private functions for cue sheet processing 00396 bool LoadCueSheet(char *cuefile); 00397 bool GetRealFileName(std::string& filename, std::string& pathname); 00398 bool GetCueKeyword(std::string &keyword, std::istream &in); 00399 bool GetCueFrame(uint32_t &frames, std::istream &in); 00400 bool GetCueString(std::string &str, std::istream &in); 00401 bool AddTrack(Track &curr, 00402 uint32_t &shift, 00403 const int32_t prestart, 00404 uint32_t &totalPregap, 00405 uint32_t currPregap); 00406 // member variables 00407 std::vector<Track> tracks; 00408 std::vector<uint8_t> readBuffer; 00409 std::string mcn; 00410 static int refCount; 00411 }; 00412 00413 #if defined (WIN32) /* Win 32 */ 00414 00415 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 00416 00417 #include <windows.h> 00418 #include "wnaspi32.h" // Aspi stuff 00419 00420 class CDROM_Interface_Aspi : public CDROM_Interface 00421 { 00422 public: 00423 CDROM_Interface_Aspi (void); 00424 virtual ~CDROM_Interface_Aspi(void); 00425 00426 bool SetDevice (char* path, int forceCD); 00427 00428 bool GetUPC (unsigned char& attr, char* upc); 00429 00430 bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut); 00431 bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr); 00432 bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos); 00433 bool GetAudioStatus (bool& playing, bool& pause); 00434 bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen); 00435 00436 bool PlayAudioSector (unsigned long start,unsigned long len); 00437 bool PauseAudio (bool resume); 00438 bool StopAudio (void); 00439 void ChannelControl (TCtrl ctrl) { (void)ctrl; return; }; 00440 00441 bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num); 00442 /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ 00443 bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); 00444 00445 bool LoadUnloadMedia (bool unload); 00446 00447 private: 00448 DWORD GetTOC (LPTOC toc); 00449 HANDLE OpenIOCTLFile (char cLetter, BOOL bAsync); 00450 void GetIOCTLAdapter (HANDLE hF,int * iDA,int * iDT,int * iDL); 00451 bool ScanRegistryFindKey (HKEY& hKeyBase); 00452 bool ScanRegistry (HKEY& hKeyBase); 00453 BYTE GetHostAdapter (char* hardwareID); 00454 bool GetVendor (BYTE HA_num, BYTE SCSI_Id, BYTE SCSI_Lun, char* szBuffer); 00455 00456 // ASPI stuff 00457 BYTE haId; 00458 BYTE target; 00459 BYTE lun; 00460 char letter; 00461 00462 // Windows stuff 00463 HINSTANCE hASPI; 00464 HANDLE hEvent; // global event 00465 DWORD (*pGetASPI32SupportInfo) (void); // ptrs to aspi funcs 00466 DWORD (*pSendASPI32Command) (LPSRB); 00467 TMSF oldLeadOut; 00468 }; 00469 00470 class CDROM_Interface_Ioctl : public CDROM_Interface 00471 { 00472 public: 00473 enum cdioctl_cdatype { CDIOCTL_CDA_DIO, CDIOCTL_CDA_MCI, CDIOCTL_CDA_DX }; 00474 cdioctl_cdatype cdioctl_cda_selected; 00475 00476 CDROM_Interface_Ioctl (cdioctl_cdatype ioctl_cda); 00477 virtual ~CDROM_Interface_Ioctl(void); 00478 00479 bool SetDevice (char* path, int forceCD); 00480 00481 bool GetUPC (unsigned char& attr, char* upc); 00482 00483 bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut); 00484 bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr); 00485 bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos); 00486 bool GetAudioStatus (bool& playing, bool& pause); 00487 bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen); 00488 00489 bool PlayAudioSector (unsigned long start,unsigned long len); 00490 bool PauseAudio (bool resume); 00491 bool StopAudio (void); 00492 void ChannelControl (TCtrl ctrl); 00493 00494 bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector); 00495 bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num); 00496 /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ 00497 bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); 00498 00499 bool LoadUnloadMedia (bool unload); 00500 00501 void InitNewMedia (void) { Close(); Open(); }; 00502 private: 00503 00504 bool Open (void); 00505 void Close (void); 00506 00507 char pathname[32]; 00508 HANDLE hIOCTL; 00509 TMSF oldLeadOut; 00510 00511 00512 /* track start/length data */ 00513 bool track_start_valid; 00514 int track_start_first,track_start_last; 00515 int track_start[128]; 00516 00517 bool GetAudioTracksAll (void); 00518 00519 00520 /* mci audio cd interface */ 00521 bool use_mciplay; 00522 int mci_devid; 00523 00524 bool mci_CDioctl (UINT msg, DWORD flags, void *arg); 00525 bool mci_CDOpen (char drive); 00526 bool mci_CDClose (void); 00527 bool mci_CDPlay (int start, int length); 00528 bool mci_CDPause (void); 00529 bool mci_CDResume (void); 00530 bool mci_CDStop (void); 00531 int mci_CDStatus (void); 00532 bool mci_CDPosition (int *position); 00533 00534 00535 /* digital audio extraction cd interface */ 00536 static void dx_CDAudioCallBack(Bitu len); 00537 00538 bool use_dxplay; 00539 static struct dxPlayer { 00540 CDROM_Interface_Ioctl *cd; 00541 MixerChannel *channel; 00542 SDL_mutex *mutex; 00543 Bit8u buffer[8192]; 00544 int bufLen; 00545 int currFrame; 00546 int targetFrame; 00547 bool isPlaying; 00548 bool isPaused; 00549 bool ctrlUsed; 00550 TCtrl ctrlData; 00551 } player; 00552 00553 }; 00554 00555 #endif /* WIN 32 */ 00556 00557 #if defined (LINUX) || defined(OS2) 00558 00559 class CDROM_Interface_Ioctl : public CDROM_Interface_SDL 00560 { 00561 public: 00562 CDROM_Interface_Ioctl (void); 00563 00564 bool SetDevice (char* path, int forceCD); 00565 bool GetUPC (unsigned char& attr, char* upc); 00566 bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num); 00567 /* This is needed for IDE hack, who's buffer does not exist in DOS physical memory */ 00568 bool ReadSectorsHost (void* buffer, bool raw, unsigned long sector, unsigned long num); 00569 00570 private: 00571 char device_name[512]; 00572 }; 00573 00574 #endif /* LINUX */ 00575 00576 #endif /* __CDROM_INTERFACE__ */