DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/dos/drive_iso.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 <cctype>
00021 #include <cstring>
00022 #include "cdrom.h"
00023 #include "dosbox.h"
00024 #include "dos_system.h"
00025 #include "support.h"
00026 #include "drives.h"
00027 
00028 #define FLAGS1  ((iso) ? de.fileFlags : de.timeZone)
00029 #define FLAGS2  ((iso) ? de->fileFlags : de->timeZone)
00030 
00031 using namespace std;
00032 
00033 class isoFile : public DOS_File {
00034 public:
00035         isoFile(isoDrive *drive, const char *name, FileStat_Block *stat, Bit32u offset);
00036         bool Read(Bit8u *data, Bit16u *size);
00037         bool Write(const Bit8u *data, Bit16u *size);
00038         bool Seek(Bit32u *pos, Bit32u type);
00039         bool Close();
00040         Bit16u GetInformation(void);
00041         Bit32u GetSeekPos(void);
00042 private:
00043         isoDrive *drive;
00044         Bit8u buffer[ISO_FRAMESIZE];
00045         int cachedSector;
00046         Bit32u fileBegin;
00047         Bit32u filePos;
00048         Bit32u fileEnd;
00049 //      Bit16u info;
00050 };
00051 
00052 isoFile::isoFile(isoDrive *drive, const char *name, FileStat_Block *stat, Bit32u offset) {
00053         this->drive = drive;
00054         time = stat->time;
00055         date = stat->date;
00056         attr = stat->attr;
00057         fileBegin = offset;
00058         filePos = fileBegin;
00059         fileEnd = fileBegin + stat->size;
00060         cachedSector = -1;
00061         open = true;
00062         this->name = NULL;
00063         SetName(name);
00064 }
00065 
00066 bool isoFile::Read(Bit8u *data, Bit16u *size) {
00067         if (filePos + *size > fileEnd)
00068                 *size = (Bit16u)(fileEnd - filePos);
00069         
00070         Bit16u nowSize = 0;
00071         int sector = (int)(filePos / ISO_FRAMESIZE);
00072         Bit16u sectorPos = (Bit16u)(filePos % ISO_FRAMESIZE);
00073         
00074         if (sector != cachedSector) {
00075                 if (drive->readSector(buffer, (unsigned int)sector)) cachedSector = sector;
00076                 else { *size = 0; cachedSector = -1; }
00077         }
00078         while (nowSize < *size) {
00079                 Bit16u remSector = ISO_FRAMESIZE - sectorPos;
00080                 Bit16u remSize = *size - nowSize;
00081                 if(remSector < remSize) {
00082                         memcpy(&data[nowSize], &buffer[sectorPos], remSector);
00083                         nowSize += remSector;
00084                         sectorPos = 0;
00085                         sector++;
00086                         cachedSector++;
00087                         if (!drive->readSector(buffer, (unsigned int)sector)) {
00088                                 *size = nowSize;
00089                                 cachedSector = -1;
00090                         }
00091                 } else {
00092                         memcpy(&data[nowSize], &buffer[sectorPos], remSize);
00093                         nowSize += remSize;
00094                 }
00095                         
00096         }
00097         
00098         *size = nowSize;
00099         filePos += *size;
00100         return true;
00101 }
00102 
00103 bool isoFile::Write(const Bit8u* /*data*/, Bit16u* /*size*/) {
00104         return false;
00105 }
00106 
00107 bool isoFile::Seek(Bit32u *pos, Bit32u type) {
00108         switch (type) {
00109                 case DOS_SEEK_SET:
00110                         filePos = fileBegin + *pos;
00111                         break;
00112                 case DOS_SEEK_CUR:
00113                         filePos += *pos;
00114                         break;
00115                 case DOS_SEEK_END:
00116                         filePos = fileEnd + *pos;
00117                         break;
00118                 default:
00119                         return false;
00120         }
00121         if (filePos > fileEnd || filePos < fileBegin)
00122                 filePos = fileEnd;
00123         
00124         *pos = filePos - fileBegin;
00125         return true;
00126 }
00127 
00128 bool isoFile::Close() {
00129         if (refCtr == 1) open = false;
00130         return true;
00131 }
00132 
00133 Bit16u isoFile::GetInformation(void) {
00134         return 0x40;            // read-only drive
00135 }
00136 
00137 Bit32u isoFile::GetSeekPos() {
00138         return filePos - fileBegin;
00139 }
00140 
00141 
00142 int  MSCDEX_RemoveDrive(char driveLetter);
00143 int  MSCDEX_AddDrive(char driveLetter, const char* physicalPath, Bit8u& subUnit);
00144 void MSCDEX_ReplaceDrive(CDROM_Interface* cdrom, Bit8u subUnit);
00145 bool MSCDEX_HasDrive(char driveLetter);
00146 bool MSCDEX_GetVolumeName(Bit8u subUnit, char* name);
00147 
00148 bool CDROM_Interface_Image::images_init = false;
00149 
00150 isoDrive::isoDrive(char driveLetter, const char *fileName, Bit8u mediaid, int &error) {
00151         size_t i;
00152 
00153     if (!CDROM_Interface_Image::images_init) {
00154         CDROM_Interface_Image::images_init = true;
00155         for (i=0;i < 26;i++)
00156             CDROM_Interface_Image::images[i] = NULL;
00157     }
00158 
00159         subUnit = 0;
00160         nextFreeDirIterator = 0;
00161         memset(dirIterators, 0, sizeof(dirIterators));
00162         memset(sectorHashEntries, 0, sizeof(sectorHashEntries));
00163         memset(&rootEntry, 0, sizeof(isoDirEntry));
00164         
00165         safe_strncpy(this->fileName, fileName, CROSS_LEN);
00166         error = UpdateMscdex(driveLetter, fileName, subUnit);
00167 
00168         if (!error) {
00169                 if (loadImage()) {
00170                         strcpy(info, "isoDrive ");
00171                         strcat(info, fileName);
00172                         this->driveLetter = driveLetter;
00173                         this->mediaid = mediaid;
00174                         char buffer[32] = { 0 };
00175                         if (!MSCDEX_GetVolumeName(subUnit, buffer)) strcpy(buffer, "");
00176                         Set_Label(buffer,discLabel,true);
00177 
00178                 } else if (CDROM_Interface_Image::images[subUnit]->HasDataTrack() == false) { //Audio only cdrom
00179                         strcpy(info, "isoDrive ");
00180                         strcat(info, fileName);
00181                         this->driveLetter = driveLetter;
00182                         this->mediaid = mediaid;
00183                         char buffer[32] = { 0 };
00184                         strcpy(buffer, "Audio_CD");
00185                         Set_Label(buffer,discLabel,true);
00186                 } else error = 6; //Corrupt image
00187         }
00188 }
00189 
00190 isoDrive::~isoDrive() { }
00191 
00192 int isoDrive::UpdateMscdex(char driveLetter, const char* path, Bit8u& subUnit) {
00193         if (MSCDEX_HasDrive(driveLetter)) {
00194                 CDROM_Interface_Image* oldCdrom = CDROM_Interface_Image::images[subUnit];
00195                 CDROM_Interface* cdrom = new CDROM_Interface_Image(subUnit);
00196                 char pathCopy[CROSS_LEN];
00197                 safe_strncpy(pathCopy, path, CROSS_LEN);
00198                 if (!cdrom->SetDevice(pathCopy, 0)) {
00199                         CDROM_Interface_Image::images[subUnit] = oldCdrom;
00200                         delete cdrom;
00201                         return 3;
00202                 }
00203                 MSCDEX_ReplaceDrive(cdrom, subUnit);
00204                 return 0;
00205         } else {
00206                 return MSCDEX_AddDrive(driveLetter, path, subUnit);
00207         }
00208 }
00209 
00210 void isoDrive::Activate(void) {
00211         UpdateMscdex(driveLetter, fileName, subUnit);
00212 }
00213 
00214 bool isoDrive::FileOpen(DOS_File **file, const char *name, Bit32u flags) {
00215         if ((flags & 0x0f) == OPEN_WRITE) {
00216                 DOS_SetError(DOSERR_ACCESS_DENIED);
00217                 return false;
00218         }
00219         
00220         isoDirEntry de;
00221         bool success = lookup(&de, name) && !IS_DIR(FLAGS1);
00222 
00223         if (success) {
00224                 FileStat_Block file_stat;
00225                 file_stat.size = DATA_LENGTH(de);
00226                 file_stat.attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY;
00227                 file_stat.date = DOS_PackDate(1900 + de.dateYear, de.dateMonth, de.dateDay);
00228                 file_stat.time = DOS_PackTime(de.timeHour, de.timeMin, de.timeSec);
00229                 *file = new isoFile(this, name, &file_stat, EXTENT_LOCATION(de) * ISO_FRAMESIZE);
00230                 (*file)->flags = flags;
00231         }
00232         return success;
00233 }
00234 
00235 bool isoDrive::FileCreate(DOS_File** /*file*/, const char* /*name*/, Bit16u /*attributes*/) {
00236         DOS_SetError(DOSERR_ACCESS_DENIED);
00237         return false;
00238 }
00239 
00240 bool isoDrive::FileUnlink(const char* /*name*/) {
00241         DOS_SetError(DOSERR_ACCESS_DENIED);
00242         return false;
00243 }
00244 
00245 bool isoDrive::RemoveDir(const char* /*dir*/) {
00246         DOS_SetError(DOSERR_ACCESS_DENIED);
00247         return false;
00248 }
00249 
00250 bool isoDrive::MakeDir(const char* /*dir*/) {
00251         DOS_SetError(DOSERR_ACCESS_DENIED);
00252         return false;
00253 }
00254 
00255 bool isoDrive::TestDir(const char *dir) {
00256         isoDirEntry de; 
00257         return (lookup(&de, dir) && IS_DIR(FLAGS1));
00258 }
00259 
00260 bool isoDrive::FindFirst(const char *dir, DOS_DTA &dta, bool fcb_findfirst) {
00261         isoDirEntry de;
00262         if (!lookup(&de, dir)) {
00263                 DOS_SetError(DOSERR_PATH_NOT_FOUND);
00264                 return false;
00265         }
00266         
00267         // get a directory iterator and save its id in the dta
00268         int dirIterator = GetDirIterator(&de);
00269         bool isRoot = (*dir == 0);
00270         dirIterators[dirIterator].root = isRoot;
00271         dta.SetDirID((Bit16u)dirIterator);
00272 
00273         Bit8u attr;
00274         char pattern[ISO_MAXPATHNAME];
00275         dta.GetSearchParams(attr, pattern);
00276    
00277         if (attr == DOS_ATTR_VOLUME) {
00278                 dta.SetResult(discLabel, 0, 0, 0, DOS_ATTR_VOLUME);
00279                 return true;
00280         } else if ((attr & DOS_ATTR_VOLUME) && isRoot && !fcb_findfirst) {
00281                 if (WildFileCmp(discLabel,pattern)) {
00282                         // Get Volume Label (DOS_ATTR_VOLUME) and only in basedir and if it matches the searchstring
00283                         dta.SetResult(discLabel, 0, 0, 0, DOS_ATTR_VOLUME);
00284                         return true;
00285                 }
00286         }
00287 
00288         return FindNext(dta);
00289 }
00290 
00291 bool isoDrive::FindNext(DOS_DTA &dta) {
00292         Bit8u attr;
00293         char pattern[DOS_NAMELENGTH_ASCII];
00294         dta.GetSearchParams(attr, pattern);
00295         
00296         int dirIterator = dta.GetDirID();
00297         bool isRoot = dirIterators[dirIterator].root;
00298         
00299         isoDirEntry de;
00300         while (GetNextDirEntry(dirIterator, &de)) {
00301                 Bit8u findAttr = 0;
00302                 if (IS_DIR(FLAGS1)) findAttr |= DOS_ATTR_DIRECTORY;
00303                 else findAttr |= DOS_ATTR_ARCHIVE;
00304                 if (IS_HIDDEN(FLAGS1)) findAttr |= DOS_ATTR_HIDDEN;
00305 
00306                 if (!IS_ASSOC(FLAGS1) && !(isRoot && de.ident[0]=='.') && WildFileCmp((char*)de.ident, pattern)
00307                         && !(~attr & findAttr & (DOS_ATTR_DIRECTORY | DOS_ATTR_HIDDEN | DOS_ATTR_SYSTEM))) {
00308                         
00309                         /* file is okay, setup everything to be copied in DTA Block */
00310                         char findName[DOS_NAMELENGTH_ASCII];            
00311                         findName[0] = 0;
00312                         if(strlen((char*)de.ident) < DOS_NAMELENGTH_ASCII) {
00313                                 strcpy(findName, (char*)de.ident);
00314                                 upcase(findName);
00315                         }
00316                         Bit32u findSize = DATA_LENGTH(de);
00317                         Bit16u findDate = DOS_PackDate(1900 + de.dateYear, de.dateMonth, de.dateDay);
00318                         Bit16u findTime = DOS_PackTime(de.timeHour, de.timeMin, de.timeSec);
00319                         dta.SetResult(findName, findSize, findDate, findTime, findAttr);
00320                         return true;
00321                 }
00322         }
00323         // after searching the directory, free the iterator
00324         FreeDirIterator(dirIterator);
00325         
00326         DOS_SetError(DOSERR_NO_MORE_FILES);
00327         return false;
00328 }
00329 
00330 bool isoDrive::Rename(const char* /*oldname*/, const char* /*newname*/) {
00331         DOS_SetError(DOSERR_ACCESS_DENIED);
00332         return false;
00333 }
00334 
00335 bool isoDrive::GetFileAttr(const char *name, Bit16u *attr) {
00336         *attr = 0;
00337         isoDirEntry de;
00338         bool success = lookup(&de, name);
00339         if (success) {
00340                 *attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY;
00341                 if (IS_HIDDEN(FLAGS1)) *attr |= DOS_ATTR_HIDDEN;
00342                 if (IS_DIR(FLAGS1)) *attr |= DOS_ATTR_DIRECTORY;
00343         }
00344         return success;
00345 }
00346 
00347 bool isoDrive::AllocationInfo(Bit16u *bytes_sector, Bit8u *sectors_cluster, Bit16u *total_clusters, Bit16u *free_clusters) {
00348         *bytes_sector = 2048;
00349         *sectors_cluster = 1; // cluster size for cdroms ?
00350         *total_clusters = 65535;
00351         *free_clusters = 0;
00352         return true;
00353 }
00354 
00355 bool isoDrive::FileExists(const char *name) {
00356         isoDirEntry de;
00357         return (lookup(&de, name) && !IS_DIR(FLAGS1));
00358 }
00359 
00360 bool isoDrive::FileStat(const char *name, FileStat_Block *const stat_block) {
00361         isoDirEntry de;
00362         bool success = lookup(&de, name);
00363         
00364         if (success) {
00365                 stat_block->date = DOS_PackDate(1900 + de.dateYear, de.dateMonth, de.dateDay);
00366                 stat_block->time = DOS_PackTime(de.timeHour, de.timeMin, de.timeSec);
00367                 stat_block->size = DATA_LENGTH(de);
00368                 stat_block->attr = DOS_ATTR_ARCHIVE | DOS_ATTR_READ_ONLY;
00369                 if (IS_DIR(FLAGS1)) stat_block->attr |= DOS_ATTR_DIRECTORY;
00370         }
00371         
00372         return success;
00373 }
00374 
00375 Bit8u isoDrive::GetMediaByte(void) {
00376         return mediaid;
00377 }
00378 
00379 bool isoDrive::isRemote(void) {
00380         return true;
00381 }
00382 
00383 bool isoDrive::isRemovable(void) {
00384         return true;
00385 }
00386 
00387 Bits isoDrive::UnMount(void) {
00388         if(MSCDEX_RemoveDrive(driveLetter)) {
00389                 delete this;
00390                 return 0;
00391         }
00392         return 2;
00393 }
00394 
00395 int isoDrive::GetDirIterator(const isoDirEntry* de) {
00396         int dirIterator = nextFreeDirIterator;
00397         
00398         // get start and end sector of the directory entry (pad end sector if necessary)
00399         dirIterators[dirIterator].currentSector = EXTENT_LOCATION(*de);
00400         dirIterators[dirIterator].endSector =
00401                 EXTENT_LOCATION(*de) + DATA_LENGTH(*de) / ISO_FRAMESIZE - 1;
00402         if (DATA_LENGTH(*de) % ISO_FRAMESIZE != 0)
00403                 dirIterators[dirIterator].endSector++;
00404         
00405         // reset position and mark as valid
00406         dirIterators[dirIterator].pos = 0;
00407         dirIterators[dirIterator].valid = true;
00408 
00409         // advance to next directory iterator (wrap around if necessary)
00410         nextFreeDirIterator = (nextFreeDirIterator + 1) % MAX_OPENDIRS;
00411         
00412         return dirIterator;
00413 }
00414 
00415 bool isoDrive::GetNextDirEntry(const int dirIteratorHandle, isoDirEntry* de) {
00416         bool result = false;
00417         Bit8u* buffer = NULL;
00418         DirIterator& dirIterator = dirIterators[dirIteratorHandle];
00419         
00420         // check if the directory entry is valid
00421         if (dirIterator.valid && ReadCachedSector(&buffer, dirIterator.currentSector)) {
00422                 // check if the next sector has to be read
00423                 if ((dirIterator.pos >= ISO_FRAMESIZE)
00424                  || (buffer[dirIterator.pos] == 0)
00425                  || (dirIterator.pos + buffer[dirIterator.pos] > ISO_FRAMESIZE)) {
00426                         
00427                         // check if there is another sector available
00428                         if (dirIterator.currentSector < dirIterator.endSector) {
00429                                 dirIterator.pos = 0;
00430                                 dirIterator.currentSector++;
00431                                 if (!ReadCachedSector(&buffer, dirIterator.currentSector)) {
00432                                         return false;
00433                                 }
00434                         } else {
00435                                 return false;
00436                         }
00437                  }
00438                  // read sector and advance sector pointer
00439                  int length = readDirEntry(de, &buffer[dirIterator.pos]);
00440                  result = length >= 0;
00441          if (length > 0) dirIterator.pos += (unsigned int)length;
00442         }
00443         return result;
00444 }
00445 
00446 void isoDrive::FreeDirIterator(const int dirIterator) {
00447         dirIterators[dirIterator].valid = false;
00448         
00449         // if this was the last aquired iterator decrement nextFreeIterator
00450         if ((dirIterator + 1) % MAX_OPENDIRS == nextFreeDirIterator) {
00451                 if (nextFreeDirIterator>0) {
00452                         nextFreeDirIterator--;
00453                 } else {
00454                         nextFreeDirIterator = MAX_OPENDIRS-1;
00455                 }
00456         }
00457 }
00458 
00459 bool isoDrive::ReadCachedSector(Bit8u** buffer, const Bit32u sector) {
00460         // get hash table entry
00461         unsigned int pos = sector % ISO_MAX_HASH_TABLE_SIZE;
00462         SectorHashEntry& he = sectorHashEntries[pos];
00463         
00464         // check if the entry is valid and contains the correct sector
00465         if (!he.valid || he.sector != sector) {
00466                 if (!CDROM_Interface_Image::images[subUnit]->ReadSector(he.data, false, sector)) {
00467                         return false;
00468                 }
00469                 he.valid = true;
00470                 he.sector = sector;
00471         }
00472         
00473         *buffer = he.data;
00474         return true;
00475 }
00476 
00477 inline bool isoDrive :: readSector(Bit8u *buffer, Bit32u sector) {
00478         return CDROM_Interface_Image::images[subUnit]->ReadSector(buffer, false, sector);
00479 }
00480 
00481 int isoDrive :: readDirEntry(isoDirEntry *de, Bit8u *data) {    
00482         // copy data into isoDirEntry struct, data[0] = length of DirEntry
00483 //      if (data[0] > sizeof(isoDirEntry)) return -1;//check disabled as isoDirentry is currently 258 bytes large. So it always fits
00484         memcpy(de, data, data[0]);//Perharps care about a zero at the end.
00485         
00486         // xa not supported
00487         if (de->extAttrLength != 0) return -1;
00488         // interleaved mode not supported
00489         if (de->fileUnitSize != 0 || de->interleaveGapSize != 0) return -1;
00490         
00491         // modify file identifier for use with dosbox
00492         if ((de->length < 33 + de->fileIdentLength)) return -1;
00493         if (IS_DIR(FLAGS2)) {
00494                 if (de->fileIdentLength == 1 && de->ident[0] == 0) strcpy((char*)de->ident, ".");
00495                 else if (de->fileIdentLength == 1 && de->ident[0] == 1) strcpy((char*)de->ident, "..");
00496                 else {
00497                         if (de->fileIdentLength > 200) return -1;
00498                         de->ident[de->fileIdentLength] = 0;
00499                 }
00500         } else {
00501                 if (de->fileIdentLength > 200) return -1;
00502                 de->ident[de->fileIdentLength] = 0;     
00503                 // remove any file version identifiers as there are some cdroms that don't have them
00504                 strreplace((char*)de->ident, ';', 0);   
00505                 // if file has no extension remove the trailing dot
00506                 size_t tmp = strlen((char*)de->ident);
00507                 if (tmp > 0) {
00508                         if (de->ident[tmp - 1] == '.') de->ident[tmp - 1] = 0;
00509                 }
00510         }
00511         char* dotpos = strchr((char*)de->ident, '.');
00512         if (dotpos!=NULL) {
00513                 if (strlen(dotpos)>4) dotpos[4]=0;
00514                 if (dotpos-(char*)de->ident>8) {
00515                         strcpy((char*)(&de->ident[8]),dotpos);
00516                 }
00517         } else if (strlen((char*)de->ident)>8) de->ident[8]=0;
00518         return de->length;
00519 }
00520 
00521 bool isoDrive :: loadImage() {
00522         Bit8u pvd[COOKED_SECTOR_SIZE];
00523         dataCD = false;
00524         readSector(pvd, ISO_FIRST_VD);
00525         if (pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) iso = true;
00526         else if (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1) iso = false;
00527         else return false;
00528         Bit16u offset = iso ? 156 : 180;
00529         if (readDirEntry(&this->rootEntry, &pvd[offset])>0) {
00530                 dataCD = true;
00531                 return true;
00532         }
00533         return false;
00534 }
00535 
00536 bool isoDrive :: lookup(isoDirEntry *de, const char *path) {
00537         if (!dataCD) return false;
00538         *de = this->rootEntry;
00539         if (!strcmp(path, "")) return true;
00540         
00541         char isoPath[ISO_MAXPATHNAME];
00542         safe_strncpy(isoPath, path, ISO_MAXPATHNAME);
00543         strreplace(isoPath, '\\', '/');
00544         
00545         // iterate over all path elements (name), and search each of them in the current de
00546         for(char* name = strtok(isoPath, "/"); NULL != name; name = strtok(NULL, "/")) {
00547 
00548                 bool found = false;     
00549                 // current entry must be a directory, abort otherwise
00550                 if (IS_DIR(FLAGS2)) {
00551                         
00552                         // remove the trailing dot if present
00553                         size_t nameLength = strlen(name);
00554                         if (nameLength > 0) {
00555                                 if (name[nameLength - 1] == '.') name[nameLength - 1] = 0;
00556                         }
00557                         
00558                         // look for the current path element
00559                         int dirIterator = GetDirIterator(de);
00560                         while (!found && GetNextDirEntry(dirIterator, de)) {
00561                                 if (!IS_ASSOC(FLAGS2) && (0 == strncasecmp((char*) de->ident, name, ISO_MAX_FILENAME_LENGTH))) {
00562                                         found = true;
00563                                 }
00564                         }
00565                         FreeDirIterator(dirIterator);
00566                 }
00567                 if (!found) return false;
00568         }
00569         return true;
00570 }
00571 
00572 void IDE_ATAPI_MediaChangeNotify(unsigned char drive_index);
00573 
00574 void isoDrive :: MediaChange() {
00575         IDE_ATAPI_MediaChangeNotify(toupper(driveLetter) - 'A'); /* ewwww */
00576 }
00577