DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_mscdex.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  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
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include <ctype.h>
00022 #include "regs.h"
00023 #include "callback.h"
00024 #include "dos_system.h"
00025 #include "dos_inc.h"
00026 #include "setup.h"
00027 #include "support.h"
00028 #include "bios_disk.h"
00029 #include "cpu.h"
00030 
00031 #include "cdrom.h"
00032 
00033 #define MSCDEX_LOG LOG(LOG_MISC,LOG_ERROR)
00034 //#define MSCDEX_LOG
00035 
00036 #define MSCDEX_VERSION_HIGH     2
00037 #define MSCDEX_VERSION_LOW      23
00038 #define MSCDEX_MAX_DRIVES       16
00039 
00040 // Error Codes
00041 #define MSCDEX_ERROR_INVALID_FUNCTION   1
00042 #define MSCDEX_ERROR_BAD_FORMAT                 11
00043 #define MSCDEX_ERROR_UNKNOWN_DRIVE              15
00044 #define MSCDEX_ERROR_DRIVE_NOT_READY    21
00045 
00046 // Request Status
00047 #define REQUEST_STATUS_DONE             0x0100
00048 #define REQUEST_STATUS_ERROR    0x8000
00049 
00050 // Use cdrom Interface
00051 int useCdromInterface   = CDROM_USE_SDL;
00052 int forceCD                             = -1;
00053 
00054 static Bitu MSCDEX_Strategy_Handler(void); 
00055 static Bitu MSCDEX_Interrupt_Handler(void);
00056 
00057 class DOS_DeviceHeader:public MemStruct {
00058 public:
00059         DOS_DeviceHeader(PhysPt ptr)                            { pt = ptr; };
00060         
00061         void    SetNextDeviceHeader     (RealPt ptr)    { sSave(sDeviceHeader,nextDeviceHeader,ptr);    };
00062         RealPt  GetNextDeviceHeader     (void)                  { return sGet(sDeviceHeader,nextDeviceHeader);  };
00063         void    SetAttribute            (Bit16u atr)    { sSave(sDeviceHeader,devAttributes,atr);               };
00064         void    SetDriveLetter          (Bit8u letter)  { sSave(sDeviceHeader,driveLetter,letter);              };
00065         void    SetNumSubUnits          (Bit8u num)             { sSave(sDeviceHeader,numSubUnits,num);                 };
00066         Bit8u   GetNumSubUnits          (void)                  { return sGet(sDeviceHeader,numSubUnits);               };
00067         void    SetName                         (char const* _name)     { MEM_BlockWrite(pt+offsetof(sDeviceHeader,name),_name,8); };
00068         void    SetInterrupt            (Bit16u ofs)    { sSave(sDeviceHeader,interrupt,ofs);                   };
00069         void    SetStrategy                     (Bit16u ofs)    { sSave(sDeviceHeader,strategy,ofs);                    };
00070 
00071 public:
00072         #ifdef _MSC_VER
00073         #pragma pack(1)
00074         #endif
00075         struct sDeviceHeader{
00076                 RealPt  nextDeviceHeader;
00077                 Bit16u  devAttributes;
00078                 Bit16u  strategy;
00079                 Bit16u  interrupt;
00080                 Bit8u   name[8];
00081                 Bit16u  wReserved;
00082                 Bit8u   driveLetter;
00083                 Bit8u   numSubUnits;
00084         } GCC_ATTRIBUTE(packed) TDeviceHeader;
00085         #ifdef _MSC_VER
00086         #pragma pack()
00087         #endif
00088 };
00089 
00090 class CMscdex {
00091 public:
00092         CMscdex         (void);
00093         ~CMscdex        (void);
00094 
00095         Bit16u          GetVersion                      (void)  { return (MSCDEX_VERSION_HIGH<<8)+MSCDEX_VERSION_LOW; };
00096         Bit16u          GetNumDrives            (void)  { return numDrives;                     };
00097         Bit16u          GetFirstDrive           (void)  { return dinfo[0].drive; };
00098         Bit8u           GetSubUnit                      (Bit16u _drive);
00099         bool            GetUPC                          (Bit8u subUnit, Bit8u& attr, char* upc);
00100 
00101         void            InitNewMedia            (Bit8u subUnit);
00102         bool            PlayAudioSector         (Bit8u subUnit, Bit32u start, Bit32u length);
00103         bool            PlayAudioMSF            (Bit8u subUnit, Bit32u start, Bit32u length);
00104         bool            StopAudio                       (Bit8u subUnit);
00105         bool            GetAudioStatus          (Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end);
00106 
00107         bool            GetSubChannelData       (Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs);
00108 
00109         int                     RemoveDrive                     (Bit16u _drive);
00110         int                     AddDrive                        (Bit16u _drive, char* physicalPath, Bit8u& subUnit);
00111         bool            HasDrive                        (Bit16u drive);
00112         void            ReplaceDrive            (CDROM_Interface* newCdrom, Bit8u subUnit);
00113         void            GetDrives                       (PhysPt data);
00114         void            GetDriverInfo           (PhysPt data);
00115         bool            GetVolumeName           (Bit8u subUnit, char* name);
00116         bool            GetCopyrightName        (Bit16u drive, PhysPt data);
00117         bool            GetAbstractName         (Bit16u drive, PhysPt data);
00118         bool            GetDocumentationName(Bit16u drive, PhysPt data);
00119         bool            GetDirectoryEntry       (Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error);
00120         bool            ReadVTOC                        (Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error);
00121         bool            ReadSectors                     (Bit16u drive, Bit32u sector, Bit16u num, PhysPt data);
00122         bool            ReadSectors                     (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data);
00123         bool            ReadSectorsMSF          (Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data);
00124         bool            SendDriverRequest       (Bit16u drive, PhysPt data);
00125         bool            IsValidDrive            (Bit16u drive);
00126         bool            GetCDInfo                       (Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut);
00127         Bit32u          GetVolumeSize           (Bit8u subUnit);
00128         bool            GetTrackInfo            (Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start);
00129         Bit16u          GetStatusWord           (Bit8u subUnit,Bit16u status);
00130         bool            GetCurrentPos           (Bit8u subUnit, TMSF& pos);
00131         Bit32u          GetDeviceStatus         (Bit8u subUnit);
00132         bool            GetMediaStatus          (Bit8u subUnit, Bit8u& status);
00133         bool            LoadUnloadMedia         (Bit8u subUnit, bool unload);
00134         bool            ResumeAudio                     (Bit8u subUnit);
00135         bool            GetMediaStatus          (Bit8u subUnit, bool& media, bool& changed, bool& trayOpen);
00136 
00137         PhysPt          GetDefaultBuffer        (void);
00138         PhysPt          GetTempBuffer           (void);
00139 
00140         void SaveState( std::ostream& stream );
00141         void LoadState( std::istream& stream );
00142 
00143         Bit16u          numDrives;
00144 
00145         typedef struct SDriveInfo {
00146                 Bit8u   drive;                  // drive letter in dosbox
00147                 Bit8u   physDrive;              // drive letter in system
00148                 bool    audioPlay;              // audio playing active
00149                 bool    audioPaused;    // audio playing paused
00150                 Bit32u  audioStart;             // StartLoc for resume
00151                 Bit32u  audioEnd;               // EndLoc for resume
00152                 bool    locked;                 // drive locked ?
00153                 bool    lastResult;             // last operation success ?
00154                 Bit32u  volumeSize;             // for media change
00155                 TCtrl   audioCtrl;              // audio channel control
00156         } TDriveInfo;
00157 
00158         Bit16u                          defaultBufSeg;
00159         TDriveInfo                      dinfo[MSCDEX_MAX_DRIVES];
00160         CDROM_Interface*                cdrom[MSCDEX_MAX_DRIVES];
00161         
00162 public:
00163         Bit16u          rootDriverHeaderSeg;
00164 
00165         bool            ChannelControl          (Bit8u subUnit, TCtrl ctrl);
00166         bool            GetChannelControl       (Bit8u subUnit, TCtrl& ctrl);
00167 };
00168 
00169 CMscdex::CMscdex(void) {
00170         numDrives                       = 0;
00171         rootDriverHeaderSeg     = 0;
00172         defaultBufSeg           = 0;
00173 
00174         memset(dinfo,0,sizeof(dinfo));
00175         for (Bit32u i=0; i<MSCDEX_MAX_DRIVES; i++) cdrom[i] = 0;
00176 }
00177 
00178 CMscdex::~CMscdex(void) {
00179         defaultBufSeg = 0;
00180         for (Bit16u i=0; i<GetNumDrives(); i++) {
00181                 delete (cdrom)[i];
00182                 cdrom[i] = 0;
00183         };
00184 }
00185 
00186 void CMscdex::GetDrives(PhysPt data)
00187 {
00188         for (Bit16u i=0; i<GetNumDrives(); i++) mem_writeb(data+i,dinfo[i].drive);
00189 }
00190 
00191 bool CMscdex::IsValidDrive(Bit16u _drive)
00192 {
00193         _drive &= 0xff; //Only lowerpart (Ultimate domain)
00194         for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return true;
00195         return false;
00196 }
00197 
00198 Bit8u CMscdex::GetSubUnit(Bit16u _drive)
00199 {
00200         _drive &= 0xff; //Only lowerpart (Ultimate domain)
00201         for (Bit16u i=0; i<GetNumDrives(); i++) if (dinfo[i].drive==_drive) return (Bit8u)i;
00202         return 0xff;
00203 }
00204 
00205 int CMscdex::RemoveDrive(Bit16u _drive)
00206 {
00207         Bit16u idx = MSCDEX_MAX_DRIVES;
00208         for (Bit16u i=0; i<GetNumDrives(); i++) {
00209                 if (dinfo[i].drive == _drive) {
00210                         idx = i;
00211                         break;
00212                 }
00213         }
00214 
00215         if (idx == MSCDEX_MAX_DRIVES || (idx!=0 && idx!=GetNumDrives()-1)) return 0;
00216         delete (cdrom)[idx];
00217         if (idx==0) {
00218                 for (Bit16u i=0; i<GetNumDrives(); i++) {
00219                         if (i == MSCDEX_MAX_DRIVES-1) {
00220                                 cdrom[i] = 0;
00221                                 memset(&dinfo[i],0,sizeof(TDriveInfo));
00222                         } else {
00223                                 dinfo[i] = dinfo[i+1];
00224                                 cdrom[i] = cdrom[i+1];
00225                         }
00226                 }
00227         } else {
00228                 cdrom[idx] = 0;
00229                 memset(&dinfo[idx],0,sizeof(TDriveInfo));
00230         }
00231         numDrives--;
00232 
00233         if (GetNumDrives() == 0) {
00234                 DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
00235                 Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
00236                 devHeader.SetStrategy(off+4);           // point to the RETF (To deactivate MSCDEX)
00237                 devHeader.SetInterrupt(off+4);          // point to the RETF (To deactivate MSCDEX)
00238                 devHeader.SetDriveLetter(0);
00239         } else if (idx==0) {
00240                 DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
00241                 devHeader.SetDriveLetter(GetFirstDrive()+1);
00242         }
00243         return 1;
00244 }
00245 
00246 int CMscdex::AddDrive(Bit16u _drive, char* physicalPath, Bit8u& subUnit)
00247 {
00248         subUnit = 0;
00249         if ((Bitu)GetNumDrives()+1>=MSCDEX_MAX_DRIVES) return 4;
00250         if (GetNumDrives()) {
00251                 // Error check, driveletter have to be in a row
00252                 if (dinfo[0].drive-1!=_drive && dinfo[numDrives-1].drive+1!=_drive) 
00253                         return 1;
00254         }
00255         // Set return type to ok
00256         int result = 0;
00257         // Get Mounttype and init needed cdrom interface
00258         switch (CDROM_GetMountType(physicalPath,forceCD)) {
00259         case 0x00: {    
00260                 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting physical cdrom: %s"  ,physicalPath);
00261 #if defined (WIN32)
00262                 // Check OS
00263                 OSVERSIONINFO osi;
00264                 osi.dwOSVersionInfoSize = sizeof(osi);
00265                 GetVersionEx(&osi);
00266                 if ((osi.dwPlatformId==VER_PLATFORM_WIN32_NT) && (osi.dwMajorVersion>4)) {
00267                         // only WIN NT/200/XP
00268                         if (useCdromInterface==CDROM_USE_IOCTL_DIO) {
00269 //                              cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DIO);
00270 //                              LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface.");
00271 //                              break;
00272                         }
00273                         if (useCdromInterface==CDROM_USE_IOCTL_DX) {
00274 //                              cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_DX);
00275 //                              LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (digital audio extraction).");
00276 //                              break;
00277                         }
00278                         if (useCdromInterface==CDROM_USE_IOCTL_MCI) {
00279 //                              cdrom[numDrives] = new CDROM_Interface_Ioctl(CDROM_Interface_Ioctl::CDIOCTL_CDA_MCI);
00280 //                              LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface (media control interface).");
00281 //                              break;
00282                         }
00283                 }
00284                 if (useCdromInterface==CDROM_USE_ASPI) {
00285                         // all Wins - ASPI
00286 //                      cdrom[numDrives] = new CDROM_Interface_Aspi();
00287 //                      LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: ASPI Interface.");
00288 //                      break;
00289                 }
00290 #endif
00291 #if defined (LINUX) || defined(OS2)
00292                 // Always use IOCTL in Linux or OS/2
00293                 cdrom[numDrives] = new CDROM_Interface_Ioctl();
00294                 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: IOCTL Interface.");
00295 #else
00296                 // Default case windows and other oses
00297                 cdrom[numDrives] = new CDROM_Interface_SDL();
00298                 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: SDL Interface.");
00299 #endif
00300                 } break;
00301         case 0x01:      // iso cdrom interface  
00302                 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting iso file as cdrom: %s", physicalPath);
00303                 cdrom[numDrives] = new CDROM_Interface_Image((Bit8u)numDrives);
00304                 break;
00305         case 0x02:      // fake cdrom interface (directories)
00306                 cdrom[numDrives] = new CDROM_Interface_Fake;
00307                 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: Mounting directory as cdrom: %s",physicalPath);
00308                 LOG(LOG_MISC,LOG_NORMAL)("MSCDEX: You wont have full MSCDEX support !");
00309                 result = 5;
00310                 break;
00311         default :       // weird result
00312                 return 6;
00313         };
00314 
00315         if (!cdrom[numDrives]->SetDevice(physicalPath,forceCD)) {
00316 //              delete cdrom[numDrives] ; mount seems to delete it
00317                 return 3;
00318         }
00319 
00320 
00321         if (rootDriverHeaderSeg==0) {
00322                 
00323                 Bit16u driverSize = sizeof(DOS_DeviceHeader::sDeviceHeader) + 10; // 10 = Bytes for 3 callbacks
00324                 
00325                 // Create Device Header
00326                 Bit16u seg = DOS_GetMemory(driverSize/16u+((driverSize%16u)>0u),"MSCDEX device header");
00327                 DOS_DeviceHeader devHeader(PhysMake(seg,0u));
00328                 devHeader.SetNextDeviceHeader   (0xFFFFFFFFul);
00329                 devHeader.SetAttribute(0xc800u);
00330                 devHeader.SetDriveLetter                (_drive+1u);
00331                 devHeader.SetNumSubUnits                (1u);
00332                 devHeader.SetName                               ("MSCD001 ");
00333 
00334                 //Link it in the device chain
00335                 Bit32u start = dos_infoblock.GetDeviceChain();
00336                 Bit16u segm  = (Bit16u)(start>>16ul);
00337                 Bit16u offm  = (Bit16u)(start&0xFFFFu);
00338                 while(start != 0xFFFFFFFFul) {
00339                         segm  = (Bit16u)(start>>16u);
00340                         offm  = (Bit16u)(start&0xFFFFu);
00341                         start = real_readd(segm,offm);
00342                 }
00343                 real_writed(segm,offm,(unsigned int)seg<<16u);
00344 
00345                 // Create Callback Strategy
00346                 Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
00347                 Bit16u call_strategy=(Bit16u)CALLBACK_Allocate();
00348                 CallBack_Handlers[call_strategy]=MSCDEX_Strategy_Handler;
00349                 real_writeb(seg,off+0,(Bit8u)0xFE);             //GRP 4
00350                 real_writeb(seg,off+1,(Bit8u)0x38);             //Extra Callback instruction
00351                 real_writew(seg,off+2,call_strategy);   //The immediate word
00352                 real_writeb(seg,off+4,(Bit8u)0xCB);             //A RETF Instruction
00353                 devHeader.SetStrategy(off);
00354                 
00355                 // Create Callback Interrupt
00356                 off += 5;
00357                 Bit16u call_interrupt=(Bit16u)CALLBACK_Allocate();
00358                 CallBack_Handlers[call_interrupt]=MSCDEX_Interrupt_Handler;
00359                 real_writeb(seg,off+0,(Bit8u)0xFE);             //GRP 4
00360                 real_writeb(seg,off+1,(Bit8u)0x38);             //Extra Callback instruction
00361                 real_writew(seg,off+2,call_interrupt);  //The immediate word
00362                 real_writeb(seg,off+4,(Bit8u)0xCB);             //A RETF Instruction
00363                 devHeader.SetInterrupt(off);
00364                 
00365                 rootDriverHeaderSeg = seg;
00366         
00367         } else if (GetNumDrives() == 0) {
00368                 DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
00369                 Bit16u off = sizeof(DOS_DeviceHeader::sDeviceHeader);
00370                 devHeader.SetDriveLetter(_drive+1);
00371                 devHeader.SetStrategy(off);
00372                 devHeader.SetInterrupt(off+5);
00373         }
00374 
00375         // Set drive
00376         DOS_DeviceHeader devHeader(PhysMake(rootDriverHeaderSeg,0));
00377         devHeader.SetNumSubUnits(devHeader.GetNumSubUnits()+1);
00378 
00379         if (dinfo[0].drive-1==_drive) {
00380                 CDROM_Interface *_cdrom = cdrom[numDrives];
00381                 CDROM_Interface_Image *_cdimg = CDROM_Interface_Image::images[numDrives];
00382                 for (Bit16u i=GetNumDrives(); i>0; i--) {
00383                         dinfo[i] = dinfo[i-1];
00384                         cdrom[i] = cdrom[i-1];
00385                         CDROM_Interface_Image::images[i] = CDROM_Interface_Image::images[i-1];
00386                 }
00387                 cdrom[0] = _cdrom;
00388                 CDROM_Interface_Image::images[0] = _cdimg;
00389                 dinfo[0].drive          = (Bit8u)_drive;
00390                 dinfo[0].physDrive      = (Bit8u)toupper(physicalPath[0]);
00391                 subUnit = 0;
00392         } else {
00393                 dinfo[numDrives].drive          = (Bit8u)_drive;
00394                 dinfo[numDrives].physDrive      = (Bit8u)toupper(physicalPath[0]);
00395                 subUnit = (Bit8u)numDrives;
00396         }
00397         numDrives++;
00398         // init channel control
00399         for (Bit8u chan=0;chan<4;chan++) {
00400                 dinfo[subUnit].audioCtrl.out[chan]=chan;
00401                 dinfo[subUnit].audioCtrl.vol[chan]=0xff;
00402         }
00403         // stop audio
00404         StopAudio(subUnit);
00405         return result;
00406 }
00407 
00408 bool CMscdex::HasDrive(Bit16u drive) {
00409         return (GetSubUnit(drive) != 0xff);
00410 }
00411 
00412 void CMscdex::ReplaceDrive(CDROM_Interface* newCdrom, Bit8u subUnit) {
00413         delete cdrom[subUnit];
00414         cdrom[subUnit] = newCdrom;
00415         StopAudio(subUnit);
00416 }
00417 
00418 PhysPt CMscdex::GetDefaultBuffer(void) {
00419         if (defaultBufSeg==0) {
00420                 Bit16u size = (2352*2+15)/16;
00421                 defaultBufSeg = DOS_GetMemory(size,"MSCDEX default buffer");
00422         };
00423         return PhysMake(defaultBufSeg,2352);
00424 }
00425 
00426 PhysPt CMscdex::GetTempBuffer(void) {
00427         if (defaultBufSeg==0) {
00428                 Bit16u size = (2352*2+15)/16;
00429                 defaultBufSeg = DOS_GetMemory(size,"MSCDEX temp buffer");
00430         };
00431         return PhysMake(defaultBufSeg,0);
00432 }
00433 
00434 void CMscdex::GetDriverInfo     (PhysPt data) {
00435         for (Bit16u i=0; i<GetNumDrives(); i++) {
00436                 mem_writeb(data  ,(Bit8u)i);    // subunit
00437                 mem_writed(data+1,RealMake(rootDriverHeaderSeg,0));
00438                 data+=5;
00439         };
00440 }
00441 
00442 bool CMscdex::GetCDInfo(Bit8u subUnit, Bit8u& tr1, Bit8u& tr2, TMSF& leadOut) {
00443         if (subUnit>=numDrives) return false;
00444         int tr1i,tr2i;
00445         // Assume Media change
00446         cdrom[subUnit]->InitNewMedia();
00447         dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTracks(tr1i,tr2i,leadOut);
00448         if (!dinfo[subUnit].lastResult) {
00449                 tr1 = tr2 = 0;
00450                 memset(&leadOut,0,sizeof(leadOut));
00451         } else {
00452                 tr1 = (Bit8u) tr1i;
00453                 tr2 = (Bit8u) tr2i;
00454         }
00455         return dinfo[subUnit].lastResult;
00456 }
00457 
00458 bool CMscdex::GetTrackInfo(Bit8u subUnit, Bit8u track, Bit8u& attr, TMSF& start) {
00459         if (subUnit>=numDrives) return false;
00460         dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioTrackInfo(track,start,attr);        
00461         if (!dinfo[subUnit].lastResult) {
00462                 attr = 0;
00463                 memset(&start,0,sizeof(start));
00464         }
00465         return dinfo[subUnit].lastResult;
00466 }
00467 
00468 bool CMscdex::PlayAudioSector(Bit8u subUnit, Bit32u sector, Bit32u length) {
00469         if (subUnit>=numDrives) return false;
00470         // If value from last stop is used, this is meant as a resume
00471         // better start using resume command
00472         if (dinfo[subUnit].audioPaused && (sector==dinfo[subUnit].audioStart) && (dinfo[subUnit].audioEnd!=0)) {
00473                 dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(true);
00474         } else 
00475                 dinfo[subUnit].lastResult = cdrom[subUnit]->PlayAudioSector(sector,length);
00476 
00477         if (dinfo[subUnit].lastResult) {
00478                 dinfo[subUnit].audioPlay        = true;
00479                 dinfo[subUnit].audioPaused      = false;
00480                 dinfo[subUnit].audioStart       = sector;
00481                 dinfo[subUnit].audioEnd         = length;
00482         }
00483         return dinfo[subUnit].lastResult;
00484 }
00485 
00486 bool CMscdex::PlayAudioMSF(Bit8u subUnit, Bit32u start, Bit32u length) {
00487         if (subUnit>=numDrives) return false;
00488         Bit8u min               = (Bit8u)(start>>16) & 0xFF;
00489         Bit8u sec               = (Bit8u)(start>> 8) & 0xFF;
00490         Bit8u fr                = (Bit8u)(start>> 0) & 0xFF;
00491         Bit32u sector   = min*60u*75u+sec*75u+fr - 150u;
00492         return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,sector,length);
00493 }
00494 
00495 bool CMscdex::GetSubChannelData(Bit8u subUnit, Bit8u& attr, Bit8u& track, Bit8u &index, TMSF& rel, TMSF& abs) {
00496         if (subUnit>=numDrives) return false;
00497         dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioSub(attr,track,index,rel,abs);
00498         if (!dinfo[subUnit].lastResult) {
00499                 attr = track = index = 0;
00500                 memset(&rel,0,sizeof(rel));
00501                 memset(&abs,0,sizeof(abs));
00502         }
00503         return dinfo[subUnit].lastResult;
00504 }
00505 
00506 bool CMscdex::GetAudioStatus(Bit8u subUnit, bool& playing, bool& pause, TMSF& start, TMSF& end) {
00507         if (subUnit>=numDrives) return false;
00508         dinfo[subUnit].lastResult = cdrom[subUnit]->GetAudioStatus(playing,pause);
00509         if (dinfo[subUnit].lastResult) {
00510                 // Start
00511                 Bit32u addr     = dinfo[subUnit].audioStart + 150;
00512                 start.fr        = (Bit8u)(addr%75);     addr/=75;
00513                 start.sec       = (Bit8u)(addr%60); 
00514                 start.min       = (Bit8u)(addr/60);
00515                 // End
00516                 addr            = dinfo[subUnit].audioEnd + 150;
00517                 end.fr          = (Bit8u)(addr%75);     addr/=75;
00518                 end.sec         = (Bit8u)(addr%60); 
00519                 end.min         = (Bit8u)(addr/60);
00520         } else {
00521                 playing         = false;
00522                 pause           = false;
00523                 memset(&start,0,sizeof(start));
00524                 memset(&end,0,sizeof(end));
00525         }
00526         
00527         return dinfo[subUnit].lastResult;
00528 }
00529 
00530 bool CMscdex::StopAudio(Bit8u subUnit) {
00531         if (subUnit>=numDrives) return false;
00532         if (dinfo[subUnit].audioPlay) {
00533                 // Check if audio is still playing....
00534                 TMSF start,end;
00535                 bool playing,pause;
00536                 if (GetAudioStatus(subUnit,playing,pause,start,end))
00537                         dinfo[subUnit].audioPlay = playing;
00538                 else
00539                         dinfo[subUnit].audioPlay = false;
00540         }
00541         if (dinfo[subUnit].audioPlay)
00542                 dinfo[subUnit].lastResult = cdrom[subUnit]->PauseAudio(false);
00543         else
00544                 dinfo[subUnit].lastResult = cdrom[subUnit]->StopAudio();
00545         
00546         if (dinfo[subUnit].lastResult) {
00547                 if (dinfo[subUnit].audioPlay) {
00548                         TMSF pos;
00549                         GetCurrentPos(subUnit,pos);
00550                         dinfo[subUnit].audioStart       = pos.min*60u*75u+pos.sec*75u+pos.fr - 150u;
00551                         dinfo[subUnit].audioPaused  = true;
00552                 } else {        
00553                         dinfo[subUnit].audioPaused  = false;
00554                         dinfo[subUnit].audioStart       = 0;
00555                         dinfo[subUnit].audioEnd         = 0;
00556                 }
00557                 dinfo[subUnit].audioPlay = false;
00558         }
00559         return dinfo[subUnit].lastResult;
00560 }
00561 
00562 bool CMscdex::ResumeAudio(Bit8u subUnit) {
00563         if (subUnit>=numDrives) return false;
00564         return dinfo[subUnit].lastResult = PlayAudioSector(subUnit,dinfo[subUnit].audioStart,dinfo[subUnit].audioEnd);
00565 }
00566 
00567 Bit32u CMscdex::GetVolumeSize(Bit8u subUnit) {
00568         if (subUnit>=numDrives) return false;
00569         Bit8u tr1,tr2;
00570         TMSF leadOut;
00571         dinfo[subUnit].lastResult = GetCDInfo(subUnit,tr1,tr2,leadOut);
00572         if (dinfo[subUnit].lastResult) return (leadOut.min*60u*75u)+(leadOut.sec*75u)+leadOut.fr;
00573         return 0;
00574 }
00575 
00576 bool CMscdex::ReadVTOC(Bit16u drive, Bit16u volume, PhysPt data, Bit16u& offset, Bit16u& error) {
00577         Bit8u subunit = GetSubUnit(drive);
00578 /*      if (subunit>=numDrives) {
00579                 error=MSCDEX_ERROR_UNKNOWN_DRIVE;
00580                 return false;
00581         } */
00582         if (!ReadSectors(subunit,false,16u+volume,1u,data)) {
00583                 error=MSCDEX_ERROR_DRIVE_NOT_READY;
00584                 return false;
00585         }
00586         char id[5];
00587         MEM_BlockRead(data + 1, id, 5);
00588         if (strncmp("CD001", id, 5)==0) offset = 0;
00589         else {
00590                 MEM_BlockRead(data + 9, id, 5);
00591                 if (strncmp("CDROM", id, 5)==0) offset = 8;
00592                 else {
00593                         error = MSCDEX_ERROR_BAD_FORMAT;
00594                         return false;
00595                 }
00596         }
00597         Bit8u type = mem_readb(data + offset);
00598         error = (type == 1) ? 1 : (type == 0xFF) ? 0xFF : 0;
00599         return true;
00600 }
00601 
00602 bool CMscdex::GetVolumeName(Bit8u subUnit, char* data) {        
00603         if (subUnit>=numDrives) return false;
00604         Bit16u drive = dinfo[subUnit].drive;
00605 
00606         Bit16u offset = 0, error;
00607         bool success = false;
00608         PhysPt ptoc = GetTempBuffer();
00609         success = ReadVTOC(drive,0x00,ptoc,offset,error);
00610         if (success) {
00611                 MEM_StrCopy(ptoc+offset+40,data,31);
00612                 data[31] = 0;
00613                 rtrim(data);
00614         };
00615 
00616         return success; 
00617 }
00618 
00619 bool CMscdex::GetCopyrightName(Bit16u drive, PhysPt data) {     
00620         Bit16u offset = 0, error;
00621         bool success = false;
00622         PhysPt ptoc = GetTempBuffer();
00623         success = ReadVTOC(drive,0x00,ptoc,offset,error);
00624         if (success) {
00625                 Bitu len;
00626                 for (len=0;len<37;len++) {
00627                         Bit8u c=mem_readb(ptoc+offset+702+len);
00628                         if (c==0 || c==0x20) break;
00629                 }
00630                 MEM_BlockCopy(data,ptoc+offset+702,len);
00631                 mem_writeb(data+len,0);
00632         };
00633         return success; 
00634 }
00635 
00636 bool CMscdex::GetAbstractName(Bit16u drive, PhysPt data) { 
00637         Bit16u offset = 0, error;
00638         bool success = false;
00639         PhysPt ptoc = GetTempBuffer();
00640         success = ReadVTOC(drive,0x00,ptoc,offset,error);
00641         if (success) {
00642                 Bitu len;
00643                 for (len=0;len<37;len++) {
00644                         Bit8u c=mem_readb(ptoc+offset+739+len);
00645                         if (c==0 || c==0x20) break;
00646                 }
00647                 MEM_BlockCopy(data,ptoc+offset+739,len);
00648                 mem_writeb(data+len,0);
00649         };
00650         return success; 
00651 }
00652 
00653 bool CMscdex::GetDocumentationName(Bit16u drive, PhysPt data) { 
00654         Bit16u offset = 0, error;
00655         bool success = false;
00656         PhysPt ptoc = GetTempBuffer();
00657         success = ReadVTOC(drive,0x00,ptoc,offset,error);
00658         if (success) {
00659                 Bitu len;
00660                 for (len=0;len<37;len++) {
00661                         Bit8u c=mem_readb(ptoc+offset+776+len);
00662                         if (c==0 || c==0x20) break;
00663                 }
00664                 MEM_BlockCopy(data,ptoc+offset+776,len);
00665                 mem_writeb(data+len,0);
00666         };
00667         return success; 
00668 }
00669 
00670 bool CMscdex::GetUPC(Bit8u subUnit, Bit8u& attr, char* upc)
00671 {
00672         if (subUnit>=numDrives) return false;
00673         return dinfo[subUnit].lastResult = cdrom[subUnit]->GetUPC(attr,&upc[0]);
00674 }
00675 
00676 bool CMscdex::ReadSectors(Bit8u subUnit, bool raw, Bit32u sector, Bit16u num, PhysPt data) {
00677         if (subUnit>=numDrives) return false;
00678         if ((int)(4u*num*2048u+5u) < CPU_Cycles) CPU_Cycles -= 4u*num*2048u;
00679         else CPU_Cycles = 5u;
00680         dinfo[subUnit].lastResult = cdrom[subUnit]->ReadSectors(data,raw,sector,num);
00681         return dinfo[subUnit].lastResult;
00682 }
00683 
00684 bool CMscdex::ReadSectorsMSF(Bit8u subUnit, bool raw, Bit32u start, Bit16u num, PhysPt data) {
00685         if (subUnit>=numDrives) return false;
00686         Bit8u min               = (Bit8u)(start>>16) & 0xFF;
00687         Bit8u sec               = (Bit8u)(start>> 8) & 0xFF;
00688         Bit8u fr                = (Bit8u)(start>> 0) & 0xFF;
00689         Bit32u sector   = min*60u*75u+sec*75u+fr - 150u;
00690         return ReadSectors(subUnit,raw,sector,num,data);
00691 }
00692 
00693 // Called from INT 2F
00694 bool CMscdex::ReadSectors(Bit16u drive, Bit32u sector, Bit16u num, PhysPt data) {
00695         return ReadSectors(GetSubUnit(drive),false,sector,num,data);
00696 }
00697 
00698 bool CMscdex::GetDirectoryEntry(Bit16u drive, bool copyFlag, PhysPt pathname, PhysPt buffer, Bit16u& error) {
00699         char    volumeID[6] = {0};
00700         char    searchName[256];
00701         char    entryName[256];
00702         bool    foundComplete = false;
00703         bool    foundName;
00704         bool    nextPart = true;
00705         char*   useName = 0;
00706         Bitu    entryLength,nameLength;
00707         // clear error
00708         error = 0;
00709         MEM_StrCopy(pathname+1,searchName,mem_readb(pathname));
00710         upcase(searchName);
00711         char* searchPos = searchName;
00712 
00713         //strip of tailing . (XCOM APOCALYPSE)
00714         size_t searchlen = strlen(searchName);
00715         if (searchlen > 1 && strcmp(searchName,".."))
00716                 if (searchName[searchlen-1] =='.')  searchName[searchlen-1] = 0;
00717 
00718         //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Find : %s",searchName);
00719         // read vtoc
00720         PhysPt defBuffer = GetDefaultBuffer();
00721         if (!ReadSectors(GetSubUnit(drive),false,16,1,defBuffer)) return false;
00722         MEM_StrCopy(defBuffer+1,volumeID,5); volumeID[5] = 0;
00723         Bit16u offset;
00724         if (strcmp("CD001",volumeID)==0) offset = 156;
00725         else {
00726                 MEM_StrCopy(defBuffer+9,volumeID,5);
00727                 if (strcmp("CDROM",volumeID)==0) offset = 180;
00728                 else E_Exit("MSCDEX: GetDirEntry: Not an ISO 9660 or High Sierra CD.");
00729         }
00730         // get directory position
00731         Bitu dirEntrySector     = mem_readd(defBuffer+offset+2);
00732         Bits dirSize            = mem_readd(defBuffer+offset+10);
00733         Bitu index;
00734         while (dirSize>0) {
00735                 index = 0;
00736                 if (!ReadSectors(GetSubUnit(drive),false,dirEntrySector,1,defBuffer)) return false;
00737                 // Get string part
00738                 foundName       = false;
00739                 if (nextPart) {
00740                         if (searchPos) { 
00741                                 useName = searchPos; 
00742                                 searchPos = strchr(searchPos,'\\'); 
00743                         }
00744                         if (searchPos) { *searchPos = 0; searchPos++; }
00745                         else foundComplete = true;
00746                 }
00747 
00748                 do {
00749                         entryLength = mem_readb(defBuffer+index);
00750                         if (entryLength==0) break;
00751                         nameLength  = mem_readb(defBuffer+index+32);
00752                         MEM_StrCopy(defBuffer+index+33,entryName,nameLength);
00753                         if (strcmp(entryName,useName)==0) {
00754                                 //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName);
00755                                 foundName = true;
00756                                 break;
00757                         }
00758                         /* Xcom Apocalipse searches for MUSIC. and expects to find MUSIC;1
00759                          * All Files on the CDROM are of the kind blah;1
00760                          */
00761                         char* longername = strchr(entryName,';');
00762                         if(longername) {
00763                                 *longername = 0;
00764                                 if (strcmp(entryName,useName)==0) {
00765                                         //LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Get DirEntry : Found : %s",useName);
00766                                         foundName = true;
00767                                         break;
00768                                 }
00769                         }
00770                         index += entryLength;
00771                 } while (index+33<=2048);
00772                 
00773                 if (foundName) {
00774                         if (foundComplete) {
00775                                 if (copyFlag) {
00776                                         LOG(LOG_MISC,LOG_WARN)("MSCDEX: GetDirEntry: Copyflag structure not entirely accurate maybe");
00777                                         Bit8u readBuf[256];
00778                                         Bit8u writeBuf[256];
00779                                         if (entryLength > 256)
00780                                                 return false;
00781                                         MEM_BlockRead( defBuffer+index, readBuf, entryLength );
00782                                         writeBuf[0] = readBuf[1];                                               // 00h  BYTE    length of XAR in Logical Block Numbers
00783                                         memcpy( &writeBuf[1], &readBuf[0x2], 4);                // 01h  DWORD   Logical Block Number of file start
00784                                         writeBuf[5] = 0;writeBuf[6] = 8;                                // 05h  WORD    size of disk in logical blocks
00785                                         memcpy( &writeBuf[7], &readBuf[0xa], 4);                // 07h  DWORD   file length in bytes
00786                                         memcpy( &writeBuf[0xb], &readBuf[0x12], 7);             // 0bh  DWORD   date and time
00787                                         writeBuf[0x12] = readBuf[0x19];                                 // 12h  BYTE    bit flags
00788                                         writeBuf[0x13] = readBuf[0x1a];                                 // 13h  BYTE    interleave size
00789                                         writeBuf[0x14] = readBuf[0x1b];                                 // 14h  BYTE    interleave skip factor
00790                                         memcpy( &writeBuf[0x15], &readBuf[0x1c], 2);    // 15h  WORD    volume set sequence number
00791                                         writeBuf[0x17] = readBuf[0x20];
00792                                         memcpy( &writeBuf[0x18], &readBuf[21], readBuf[0x20] <= 38 ? readBuf[0x20] : 38 );
00793                                         MEM_BlockWrite( buffer, writeBuf, 0x18 + 40 );
00794                                 } else {
00795                                         // Direct copy
00796                                         MEM_BlockCopy(buffer,defBuffer+index,entryLength);
00797                                 }
00798                                 error = 1;
00799                                 return true;
00800                         }
00801                         // change directory
00802                         dirEntrySector = mem_readd(defBuffer+index+2);
00803                         dirSize = mem_readd(defBuffer+index+10);
00804                         nextPart = true;
00805                 } else {
00806                         // continue search in next sector
00807                         dirSize -= 2048;
00808                         dirEntrySector++;
00809                         nextPart = false;
00810                 }
00811         }
00812         error = 2; // file not found
00813         return false; // not found
00814 }
00815 
00816 bool CMscdex::GetCurrentPos(Bit8u subUnit, TMSF& pos) {
00817         if (subUnit>=numDrives) return false;
00818         TMSF rel;
00819         Bit8u attr,track,index;
00820         dinfo[subUnit].lastResult = GetSubChannelData(subUnit, attr, track, index, rel, pos);
00821         if (!dinfo[subUnit].lastResult) memset(&pos,0,sizeof(pos));
00822         return dinfo[subUnit].lastResult;
00823 }
00824 
00825 bool CMscdex::GetMediaStatus(Bit8u subUnit, bool& media, bool& changed, bool& trayOpen) {
00826         if (subUnit>=numDrives) return false;
00827         dinfo[subUnit].lastResult = cdrom[subUnit]->GetMediaTrayStatus(media,changed,trayOpen);
00828         return dinfo[subUnit].lastResult;
00829 }
00830 
00831 Bit32u CMscdex::GetDeviceStatus(Bit8u subUnit) {
00832         if (subUnit>=numDrives) return false;
00833         bool media,changed,trayOpen;
00834 
00835         dinfo[subUnit].lastResult = GetMediaStatus(subUnit,media,changed,trayOpen);
00836         if (dinfo[subUnit].audioPlay) {
00837                 // Check if audio is still playing....
00838                 TMSF start,end;
00839                 bool playing,pause;
00840                 if (GetAudioStatus(subUnit,playing,pause,start,end))
00841                         dinfo[subUnit].audioPlay = playing;
00842                 else
00843                         dinfo[subUnit].audioPlay = false;
00844         }
00845 
00846         Bit32u status = ((trayOpen?1u:0u) << 0u)                                        |       // Drive is open ?
00847                                         ((dinfo[subUnit].locked?1u:0u) << 1u)           |       // Drive is locked ?
00848                                         (1u<<2u)                                                                        |       // raw + cooked sectors
00849                                         (1u<<4u)                                                                        |       // Can read sudio
00850                                         (1u<<8u)                                                                        |       // Can control audio
00851                                         (1u<<9u)                                                                        |       // Red book & HSG
00852                                         ((dinfo[subUnit].audioPlay?1u:0u) << 10u)       |       // Audio is playing ?
00853                                         ((media?0u:1u) << 11u);                                         // Drive is empty ?
00854         return status;
00855 }
00856 
00857 bool CMscdex::GetMediaStatus(Bit8u subUnit, Bit8u& status) {
00858         if (subUnit>=numDrives) return false;
00859 /*      bool media,changed,open,result;
00860         result = GetMediaStatus(subUnit,media,changed,open);
00861         status = changed ? 0xFF : 0x01;
00862         return result; */
00863         status = getSwapRequest() ? 0xFF : 0x01;
00864         return true;
00865 }
00866 
00867 bool CMscdex::LoadUnloadMedia(Bit8u subUnit, bool unload) {
00868         if (subUnit>=numDrives) return false;
00869         dinfo[subUnit].lastResult = cdrom[subUnit]->LoadUnloadMedia(unload);
00870         return dinfo[subUnit].lastResult;
00871 }
00872 
00873 bool CMscdex::SendDriverRequest(Bit16u drive, PhysPt data) {
00874         Bit8u subUnit = GetSubUnit(drive);
00875         if (subUnit>=numDrives) return false;
00876         // Get SubUnit
00877         mem_writeb(data+1,subUnit);
00878         // Call Strategy / Interrupt
00879         MSCDEX_Strategy_Handler();
00880         MSCDEX_Interrupt_Handler();
00881         return true;
00882 }
00883 
00884 Bit16u CMscdex::GetStatusWord(Bit8u subUnit,Bit16u status) {
00885         if (subUnit>=numDrives) return REQUEST_STATUS_ERROR | 0x02; // error : Drive not ready
00886 
00887         if (dinfo[subUnit].lastResult)  status |= REQUEST_STATUS_DONE;                          // ok
00888         else                                                    status |= REQUEST_STATUS_ERROR; 
00889 
00890         if (dinfo[subUnit].audioPlay) {
00891                 // Check if audio is still playing....
00892                 TMSF start,end;
00893                 bool playing,pause;
00894                 if (GetAudioStatus(subUnit,playing,pause,start,end)) {
00895                         dinfo[subUnit].audioPlay = playing;
00896                 } else
00897                         dinfo[subUnit].audioPlay = false;
00898 
00899                 status |= (dinfo[subUnit].audioPlay<<9);
00900         } 
00901         dinfo[subUnit].lastResult       = true;
00902         return status;
00903 }
00904 
00905 void CMscdex::InitNewMedia(Bit8u subUnit) {
00906         if (subUnit<numDrives) {
00907                 // Reopen new media
00908                 cdrom[subUnit]->InitNewMedia();
00909         }
00910 }
00911 
00912 bool CMscdex::ChannelControl(Bit8u subUnit, TCtrl ctrl) {
00913         if (subUnit>=numDrives) return false;
00914         // adjust strange channel mapping
00915         if (ctrl.out[0]>1) ctrl.out[0]=0;
00916         if (ctrl.out[1]>1) ctrl.out[1]=1;
00917         dinfo[subUnit].audioCtrl=ctrl;
00918         cdrom[subUnit]->ChannelControl(ctrl);
00919         return true;
00920 }
00921 
00922 bool CMscdex::GetChannelControl(Bit8u subUnit, TCtrl& ctrl) {
00923         if (subUnit>=numDrives) return false;
00924         ctrl=dinfo[subUnit].audioCtrl;
00925         return true;
00926 }
00927 
00928 static CMscdex* mscdex = 0;
00929 static PhysPt curReqheaderPtr = 0;
00930 
00931 bool GetMSCDEXDrive(unsigned char drive_letter,CDROM_Interface **_cdrom) {
00932         Bitu i;
00933 
00934         if (mscdex == NULL) {
00935                 if (_cdrom) *_cdrom = NULL;
00936                 return false;
00937         }
00938 
00939         for (i=0;i < MSCDEX_MAX_DRIVES;i++) {
00940                 if (mscdex->cdrom[i] == NULL) continue;
00941                 if (mscdex->dinfo[i].drive == drive_letter) {
00942                         if (_cdrom) *_cdrom = mscdex->cdrom[i];
00943                         return true;
00944                 }
00945         }
00946 
00947         return false;
00948 }
00949 
00950 static Bit16u MSCDEX_IOCTL_Input(PhysPt buffer,Bit8u drive_unit) {
00951         Bitu ioctl_fct = mem_readb(buffer);
00952         MSCDEX_LOG("MSCDEX: IOCTL INPUT Subfunction %02X",(int)ioctl_fct);
00953         switch (ioctl_fct) {
00954                 case 0x00 : /* Get Device Header address */
00955                                         mem_writed(buffer+1,RealMake(mscdex->rootDriverHeaderSeg,0));
00956                                         break;
00957                 case 0x01 :{/* Get current position */
00958                                         TMSF pos;
00959                                         mscdex->GetCurrentPos(drive_unit,pos);
00960                                         Bit8u addr_mode = mem_readb(buffer+1);
00961                                         if (addr_mode==0) {                     // HSG
00962                                                 Bit32u frames=MSF_TO_FRAMES(pos.min, pos.sec, pos.fr);
00963                                                 if (frames<150) MSCDEX_LOG("MSCDEX: Get position: invalid position %d:%d:%d", pos.min, pos.sec, pos.fr);
00964                                                 else frames-=150;
00965                                                 mem_writed(buffer+2,frames);
00966                                         } else if (addr_mode==1) {      // Red book
00967                                                 mem_writeb(buffer+2,pos.fr);
00968                                                 mem_writeb(buffer+3,pos.sec);
00969                                                 mem_writeb(buffer+4,pos.min);
00970                                                 mem_writeb(buffer+5,0x00);
00971                                         } else {
00972                                                 MSCDEX_LOG("MSCDEX: Get position: invalid address mode %x",addr_mode);
00973                                                 return 0x03;            // invalid function
00974                                         }
00975                                    }break;
00976                 case 0x04 : /* Audio Channel control */
00977                                         TCtrl ctrl;
00978                                         if (!mscdex->GetChannelControl(drive_unit,ctrl)) return 0x01;
00979                                         for (Bit8u chan=0;chan<4;chan++) {
00980                                                 mem_writeb(buffer+chan*2u+1u,ctrl.out[chan]);
00981                                                 mem_writeb(buffer+chan*2u+2u,ctrl.vol[chan]);
00982                                         }
00983                                         break;
00984                 case 0x06 : /* Get Device status */
00985                                         mem_writed(buffer+1,mscdex->GetDeviceStatus(drive_unit)); 
00986                                         break;
00987                 case 0x07 : /* Get sector size */
00988                                         if (mem_readb(buffer+1)==0) mem_writed(buffer+2,2048);
00989                                         else if (mem_readb(buffer+1)==1) mem_writed(buffer+2,2352);
00990                                         else return 0x03;               // invalid function
00991                                         break;
00992                 case 0x08 : /* Get size of current volume */
00993                                         mem_writed(buffer+1,mscdex->GetVolumeSize(drive_unit));
00994                                         break;
00995                 case 0x09 : /* Media change ? */
00996                                         Bit8u status;
00997                                         if (!mscdex->GetMediaStatus(drive_unit,status)) {
00998                                                 status = 0;             // state unknown
00999                                         }
01000                                         mem_writeb(buffer+1,status);
01001                                         break;
01002                 case 0x0A : /* Get Audio Disk info */   
01003                                         Bit8u tr1,tr2; TMSF leadOut;
01004                                         if (!mscdex->GetCDInfo(drive_unit,tr1,tr2,leadOut)) return 0x05;
01005                                         mem_writeb(buffer+1,tr1);
01006                                         mem_writeb(buffer+2,tr2);
01007                                         mem_writeb(buffer+3,leadOut.fr);
01008                                         mem_writeb(buffer+4,leadOut.sec);
01009                                         mem_writeb(buffer+5,leadOut.min);
01010                                         mem_writeb(buffer+6,0x00);
01011                                         break;
01012                 case 0x0B :{/* Audio Track Info */
01013                                         Bit8u attr; TMSF start;
01014                                         Bit8u track = mem_readb(buffer+1);
01015                                         mscdex->GetTrackInfo(drive_unit,track,attr,start);              
01016                                         mem_writeb(buffer+2,start.fr);
01017                                         mem_writeb(buffer+3,start.sec);
01018                                         mem_writeb(buffer+4,start.min);
01019                                         mem_writeb(buffer+5,0x00);
01020                                         mem_writeb(buffer+6,attr);
01021                                         break; };
01022                 case 0x0C :{/* Get Audio Sub Channel data */
01023                                         Bit8u attr,track,index; 
01024                                         TMSF abs,rel;
01025                                         mscdex->GetSubChannelData(drive_unit,attr,track,index,rel,abs);
01026                                         mem_writeb(buffer+1,attr);
01027                                         mem_writeb(buffer+2,track);
01028                                         mem_writeb(buffer+3,index);
01029                                         mem_writeb(buffer+4,rel.min);
01030                                         mem_writeb(buffer+5,rel.sec);
01031                                         mem_writeb(buffer+6,rel.fr);
01032                                         mem_writeb(buffer+7,0x00);
01033                                         mem_writeb(buffer+8,abs.min);
01034                                         mem_writeb(buffer+9,abs.sec);
01035                                         mem_writeb(buffer+10,abs.fr);
01036                                         break;
01037                                    };
01038                 case 0x0E :{ /* Get UPC */      
01039                                         Bit8u attr; char upc[8];
01040                                         mscdex->GetUPC(drive_unit,attr,&upc[0]);
01041                                         mem_writeb(buffer+1u,attr);
01042                                         for (unsigned int i=0; i<7; i++) mem_writeb(buffer+2u+i,(unsigned char)upc[i]);
01043                                         mem_writeb(buffer+9,0x00u);
01044                                         break;
01045                                    };
01046                 case 0x0F :{ /* Get Audio Status */     
01047                                         bool playing,pause;
01048                                         TMSF resStart,resEnd;
01049                                         mscdex->GetAudioStatus(drive_unit,playing,pause,resStart,resEnd);
01050                                         mem_writeb(buffer+1u,pause);
01051                                         mem_writeb(buffer+3u,resStart.min);
01052                                         mem_writeb(buffer+4u,resStart.sec);
01053                                         mem_writeb(buffer+5u,resStart.fr);
01054                                         mem_writeb(buffer+6u,0x00u);
01055                                         mem_writeb(buffer+7u,resEnd.min);
01056                                         mem_writeb(buffer+8u,resEnd.sec);
01057                                         mem_writeb(buffer+9u,resEnd.fr);
01058                                         mem_writeb(buffer+10u,0x00u);
01059                                         break;
01060                                    };
01061                 default :       LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL INPUT Subfunction %02X",(int)ioctl_fct);
01062                                         return 0x03;    // invalid function
01063         }
01064         return 0x00;    // success
01065 }
01066 
01067 static Bit16u MSCDEX_IOCTL_Optput(PhysPt buffer,Bit8u drive_unit) {
01068         Bitu ioctl_fct = mem_readb(buffer);
01069 //      MSCDEX_LOG("MSCDEX: IOCTL OUTPUT Subfunction %02X",ioctl_fct);
01070         switch (ioctl_fct) {
01071                 case 0x00 :     // Unload /eject media
01072                                         if (!mscdex->LoadUnloadMedia(drive_unit,true)) return 0x02;
01073                                         break;
01074                 case 0x03: //Audio Channel control
01075                                         TCtrl ctrl;
01076                                         for (Bit8u chan=0;chan<4;chan++) {
01077                                                 ctrl.out[chan]=mem_readb(buffer+chan*2u+1u);
01078                                                 ctrl.vol[chan]=mem_readb(buffer+chan*2u+2u);
01079                                         }
01080                                         if (!mscdex->ChannelControl(drive_unit,ctrl)) return 0x01;
01081                                         break;
01082                 case 0x01 : // (un)Lock door 
01083                                         // do nothing -> report as success
01084                                         break;
01085                 case 0x02 : // Reset Drive
01086                                         LOG(LOG_MISC,LOG_WARN)("cdromDrive reset");
01087                                         if (!mscdex->StopAudio(drive_unit))  return 0x02;
01088                                         break;
01089                 case 0x05 :     // load media
01090                                         if (!mscdex->LoadUnloadMedia(drive_unit,false)) return 0x02;
01091                                         break;
01092                 default :       LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported IOCTL OUTPUT Subfunction %02X",(int)ioctl_fct);
01093                                         return 0x03;    // invalid function
01094         }
01095         return 0x00;    // success
01096 }
01097 
01098 static Bitu MSCDEX_Strategy_Handler(void) {
01099         curReqheaderPtr = PhysMake(SegValue(es),reg_bx);
01100 //      MSCDEX_LOG("MSCDEX: Device Strategy Routine called, request header at %x",curReqheaderPtr);
01101         return CBRET_NONE;
01102 }
01103 
01104 static Bitu MSCDEX_Interrupt_Handler(void) {
01105         if (curReqheaderPtr==0) {
01106                 MSCDEX_LOG("MSCDEX: invalid call to interrupt handler");                                                
01107                 return CBRET_NONE;
01108         }
01109         Bit8u   subUnit         = mem_readb(curReqheaderPtr+1);
01110         Bit8u   funcNr          = mem_readb(curReqheaderPtr+2);
01111         Bit16u  errcode         = 0;
01112         PhysPt  buffer          = 0;
01113 
01114         MSCDEX_LOG("MSCDEX: Driver Function %02X",funcNr);
01115 
01116         if ((funcNr==0x03) || (funcNr==0x0c) || (funcNr==0x80) || (funcNr==0x82)) {
01117                 buffer = PhysMake(mem_readw(curReqheaderPtr+0x10),mem_readw(curReqheaderPtr+0x0E));
01118         }
01119 
01120         switch (funcNr) {
01121                 case 0x03       : {     /* IOCTL INPUT */
01122                                                 Bit16u error=MSCDEX_IOCTL_Input(buffer,subUnit);
01123                                                 if (error) errcode = error;
01124                                                 break;
01125                                           };
01126                 case 0x0C       : {     /* IOCTL OUTPUT */
01127                                                 Bit16u error=MSCDEX_IOCTL_Optput(buffer,subUnit);
01128                                                 if (error) errcode = error;
01129                                                 break;
01130                                           };
01131                 case 0x0D       :       // device open
01132                 case 0x0E       :       // device close - dont care :)
01133                                                 break;
01134                 case 0x80       :       // Read long
01135                 case 0x82       : { // Read long prefetch -> both the same here :)
01136                                                 Bit32u start = mem_readd(curReqheaderPtr+0x14);
01137                                                 Bit16u len       = mem_readw(curReqheaderPtr+0x12);
01138                                                 bool raw         = (mem_readb(curReqheaderPtr+0x18)==1);
01139                                                 if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG
01140                                                         mscdex->ReadSectors(subUnit,raw,start,len,buffer);
01141                                                 else 
01142                                                         mscdex->ReadSectorsMSF(subUnit,raw,start,len,buffer);
01143                                                 break;
01144                                           };
01145                 case 0x83       :       // Seek - dont care :)
01146                                                 break;
01147                 case 0x84       : {     /* Play Audio Sectors */
01148                                                 Bit32u start = mem_readd(curReqheaderPtr+0x0E);
01149                                                 Bit32u len       = mem_readd(curReqheaderPtr+0x12);
01150                                                 if (mem_readb(curReqheaderPtr+0x0D)==0x00) // HSG
01151                                                         mscdex->PlayAudioSector(subUnit,start,len);
01152                                                 else // RED BOOK
01153                                                         mscdex->PlayAudioMSF(subUnit,start,len);
01154                                                 break;
01155                                           };
01156                 case 0x85       :       /* Stop Audio */
01157                                                 mscdex->StopAudio(subUnit);
01158                                                 break;
01159                 case 0x88       :       /* Resume Audio */
01160                                                 mscdex->ResumeAudio(subUnit);
01161                                                 break;
01162                 default         :       LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unsupported Driver Request %02X",funcNr);
01163                                                 break;
01164         
01165         };
01166         
01167         // Set Statusword
01168         mem_writew(curReqheaderPtr+3,mscdex->GetStatusWord(subUnit,errcode));
01169         MSCDEX_LOG("MSCDEX: Status : %04X",mem_readw(curReqheaderPtr+3));                                               
01170         return CBRET_NONE;
01171 }
01172 
01173 static bool MSCDEX_Handler(void) {
01174         if(reg_ah == 0x11) {
01175                 if(reg_al == 0x00) { 
01176                         if (mscdex->rootDriverHeaderSeg==0) return false;
01177                         PhysPt check = PhysMake(SegValue(ss),reg_sp);
01178                         if(mem_readw(check+6) == 0xDADA) {
01179                                 //MSCDEX sets word on stack to ADAD if it DADA on entry.
01180                                 mem_writew(check+6,0xADAD);
01181                         }
01182                         reg_al = 0xff;
01183                         return true;
01184                 } else {
01185                         LOG(LOG_MISC,LOG_ERROR)("NETWORK REDIRECTOR USED!!!");
01186                         reg_ax = 0x49;//NETWERK SOFTWARE NOT INSTALLED
01187                         CALLBACK_SCF(true);
01188                         return true;
01189                 }
01190         }
01191 
01192         if (reg_ah!=0x15) return false;         // not handled here, continue chain
01193         if (mscdex->rootDriverHeaderSeg==0) return false;       // not handled if MSCDEX not installed
01194 
01195         PhysPt data = PhysMake(SegValue(es),reg_bx);
01196         MSCDEX_LOG("MSCDEX: INT 2F AX=%04X BX=%04X CX=%04X",reg_ax,reg_bx,reg_cx);
01197         switch (reg_ax) {
01198         
01199                 case 0x1500:    /* Install check */
01200                                                 reg_bx = mscdex->GetNumDrives();
01201                                                 if (reg_bx>0) reg_cx = mscdex->GetFirstDrive();
01202                                                 reg_al = 0xff;
01203                                                 return true;
01204                 case 0x1501:    /* Get cdrom driver info */
01205                                                 mscdex->GetDriverInfo(data);
01206                                                 return true;
01207                 case 0x1502:    /* Get Copyright filename */
01208                                                 if (mscdex->GetCopyrightName(reg_cx,data)) {
01209                                                         CALLBACK_SCF(false);
01210                                                 } else {
01211                                                         reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
01212                                                         CALLBACK_SCF(true);                                                     
01213                                                 };
01214                                                 return true;            
01215                 case 0x1503:    /* Get Abstract filename */
01216                                                 if (mscdex->GetAbstractName(reg_cx,data)) {
01217                                                         CALLBACK_SCF(false);
01218                                                 } else {
01219                                                         reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
01220                                                         CALLBACK_SCF(true);                                                     
01221                                                 };
01222                                                 return true;            
01223                 case 0x1504:    /* Get Documentation filename */
01224                                                 if (mscdex->GetDocumentationName(reg_cx,data)) {
01225                                                         CALLBACK_SCF(false);
01226                                                 } else {
01227                                                         reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
01228                                                         CALLBACK_SCF(true);                                                     
01229                                                 };
01230                                                 return true;            
01231                 case 0x1505: {  // read vtoc 
01232                                                 Bit16u offset = 0, error = 0;
01233                                                 if (mscdex->ReadVTOC(reg_cx,reg_dx,data,offset,error)) {
01234 //                                                      reg_ax = error; // return code
01235                                                         CALLBACK_SCF(false);
01236                                                 } else {
01237                                                         reg_ax = error;
01238                                                         CALLBACK_SCF(true);                                                     
01239                                                 };
01240                                          };
01241                                                 return true;
01242                 case 0x1508: {  // read sectors 
01243                                                 Bit32u sector = ((Bit32u)reg_si << 16u) + (Bit32u)reg_di;
01244                                                 if (mscdex->ReadSectors(reg_cx,sector,reg_dx,data)) {
01245                                                         reg_ax = 0;
01246                                                         CALLBACK_SCF(false);
01247                                                 } else {
01248                                                         // possibly: MSCDEX_ERROR_DRIVE_NOT_READY if sector is beyond total length
01249                                                         reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
01250                                                         CALLBACK_SCF(true);
01251                                                 };
01252                                                 return true;
01253                                          };
01254                 case 0x1509:    // write sectors - not supported 
01255                                                 reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
01256                                                 CALLBACK_SCF(true);
01257                                                 return true;
01258                 case 0x150B:    /* Valid CDROM drive ? */
01259                                                 reg_ax = (mscdex->IsValidDrive(reg_cx) ? 0x5ad8 : 0x0000);
01260                                                 reg_bx = 0xADAD;
01261                                                 return true;
01262                 case 0x150C:    /* Get MSCDEX Version */
01263                                                 reg_bx = mscdex->GetVersion();
01264                                                 return true;
01265                 case 0x150D:    /* Get drives */
01266                                                 mscdex->GetDrives(data);
01267                                                 return true;
01268                 case 0x150E:    /* Get/Set Volume Descriptor Preference */
01269                                                 if (mscdex->IsValidDrive(reg_cx)) {
01270                                                         if (reg_bx == 0) {
01271                                                                 // get preference
01272                                                                 reg_dx = 0x100; // preference?
01273                                                                 CALLBACK_SCF(false);
01274                                                         } else if (reg_bx == 1) {
01275                                                                 // set preference
01276                                                                 if (reg_dh == 1) {
01277                                                                         // valid
01278                                                                         CALLBACK_SCF(false);
01279                                                                 } else {
01280                                                                         reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
01281                                                                         CALLBACK_SCF(true);
01282                                                                 }
01283                                                         } else {
01284                                                                 reg_ax = MSCDEX_ERROR_INVALID_FUNCTION;
01285                                                                 CALLBACK_SCF(true);
01286                                                         }
01287                                                 } else {
01288                                                         reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
01289                                                         CALLBACK_SCF(true);
01290                                                 }
01291                                                 return true;
01292                 case 0x150F: {  // Get directory entry
01293                                                 Bit16u error;
01294                                                 bool success = mscdex->GetDirectoryEntry(reg_cl,reg_ch&1,data,PhysMake(reg_si,reg_di),error);
01295                                                 reg_ax = error;
01296                                                 CALLBACK_SCF(!success);
01297                                          };     return true;
01298                 case 0x1510:    /* Device driver request */
01299                                                 if (mscdex->SendDriverRequest(reg_cx,data)) {
01300                                                         CALLBACK_SCF(false);
01301                                                 } else {
01302                                                         reg_ax = MSCDEX_ERROR_UNKNOWN_DRIVE;
01303                                                         CALLBACK_SCF(true);
01304                                                 }
01305                                                 return true;
01306         };
01307         LOG(LOG_MISC,LOG_ERROR)("MSCDEX: Unknown call : %04X",reg_ax);
01308         return true;
01309 }
01310 
01311 class device_MSCDEX : public DOS_Device {
01312 public:
01313         device_MSCDEX() { SetName("MSCD001"); }
01314         bool Read (Bit8u * /*data*/,Bit16u * /*size*/) { return false;}
01315         bool Write(const Bit8u * /*data*/,Bit16u * /*size*/) { 
01316                 LOG(LOG_ALL,LOG_NORMAL)("Write to mscdex device");      
01317                 return false;
01318         }
01319         bool Seek(Bit32u * /*pos*/,Bit32u /*type*/){return false;}
01320         bool Close(){return false;}
01321         Bit16u GetInformation(void){return 0xc880;}
01322         bool ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
01323         bool WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode);
01324 private:
01325 //      Bit8u cache;
01326 };
01327 
01328 bool device_MSCDEX::ReadFromControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { 
01329         if (MSCDEX_IOCTL_Input(bufptr,0)==0) {
01330                 *retcode=size;
01331                 return true;
01332         }
01333         return false;
01334 }
01335 
01336 bool device_MSCDEX::WriteToControlChannel(PhysPt bufptr,Bit16u size,Bit16u * retcode) { 
01337         if (MSCDEX_IOCTL_Optput(bufptr,0)==0) {
01338                 *retcode=size;
01339                 return true;
01340         }
01341         return false;
01342 }
01343 
01344 int MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit)
01345 {
01346         int result = mscdex->AddDrive(driveLetter-'A',(char*)physicalPath,subUnit);
01347         return result;
01348 }
01349 
01350 int MSCDEX_RemoveDrive(char driveLetter)
01351 {
01352         if(!mscdex) return 0;
01353         return mscdex->RemoveDrive(driveLetter-'A');
01354 }
01355 
01356 bool MSCDEX_HasDrive(char driveLetter)
01357 {
01358         return mscdex->HasDrive(driveLetter-'A');
01359 }
01360 
01361 void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit)
01362 {
01363         mscdex->ReplaceDrive(cdrom, subUnit);
01364 }
01365 
01366 bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name)
01367 {
01368         return mscdex->GetVolumeName(subUnit,name);
01369 }
01370 
01371 bool MSCDEX_HasMediaChanged(Bit8u subUnit)
01372 {
01373         static TMSF leadOut[MSCDEX_MAX_DRIVES];
01374 
01375         TMSF leadnew;
01376         Bit8u tr1,tr2;
01377         if (mscdex->GetCDInfo(subUnit,tr1,tr2,leadnew)) {
01378                 bool changed = (leadOut[subUnit].min!=leadnew.min) || (leadOut[subUnit].sec!=leadnew.sec) || (leadOut[subUnit].fr!=leadnew.fr);
01379                 if (changed) {
01380                         leadOut[subUnit].min = leadnew.min;
01381                         leadOut[subUnit].sec = leadnew.sec;
01382                         leadOut[subUnit].fr      = leadnew.fr;
01383                         mscdex->InitNewMedia(subUnit);
01384                 }
01385                 return changed;
01386         };
01387         if (subUnit<MSCDEX_MAX_DRIVES) {
01388                 leadOut[subUnit].min = 0;
01389                 leadOut[subUnit].sec = 0;
01390                 leadOut[subUnit].fr      = 0;
01391         }
01392         return true;
01393 }
01394 
01395 void MSCDEX_SetCDInterface(int intNr, int numCD) {
01396         useCdromInterface = intNr;
01397         forceCD = numCD;
01398 }
01399 
01400 void MSCDEX_ShutDown(Section* /*sec*/) {
01401         if (mscdex != NULL) {
01402                 delete mscdex;
01403                 mscdex = NULL;
01404         }
01405 
01406         curReqheaderPtr = 0;
01407 }
01408 
01409 /* HACK: The IDE emulation is messily tied into calling MSCDEX.EXE!
01410  *       We cannot shut down the mscdex object when booting into a guest OS!
01411  *       Need to fix this, this is backwards! */
01412 void MSCDEX_DOS_ShutDown(Section* /*sec*/) {
01413         curReqheaderPtr = 0;
01414 }
01415 
01416 void MSCDEX_Startup(Section* sec) {
01417     (void)sec;//UNUSED
01418         if (mscdex == NULL) {
01419                 LOG(LOG_MISC,LOG_DEBUG)("Allocating MSCDEX.EXE emulation");
01420 
01421                 /* Register the mscdex device */
01422                 DOS_Device * newdev = new device_MSCDEX();
01423                 DOS_AddDevice(newdev);
01424                 curReqheaderPtr = 0;
01425                 /* Add Multiplexer */
01426                 DOS_AddMultiplexHandler(MSCDEX_Handler);
01427                 /* Create MSCDEX */
01428                 mscdex = new CMscdex;
01429         }
01430 }
01431 
01432 void MSCDEX_Init() {
01433         LOG(LOG_MISC,LOG_DEBUG)("Initializing MSCDEX.EXE emulation");
01434 
01435         AddExitFunction(AddExitFunctionFuncPair(MSCDEX_ShutDown));
01436 
01437         /* in any event that the DOS kernel is shutdown or abruptly wiped from memory */
01438         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(MSCDEX_ShutDown));
01439         AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(MSCDEX_DOS_ShutDown));
01440 }
01441