DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/cdrom_image.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 #include <cctype>
00020 #include <cmath>
00021 #include <cstdio>
00022 #include <fstream>
00023 #include <iostream>
00024 #include <limits>
00025 #include <sstream>
00026 #include <vector>
00027 #include <sys/stat.h>
00028 #include "cdrom.h"
00029 #include "drives.h"
00030 #include "support.h"
00031 #include "control.h"
00032 #include "setup.h"
00033 
00034 #if !defined(WIN32)
00035 #include <libgen.h>
00036 #else
00037 #include <string.h>
00038 #endif
00039 
00040 using namespace std;
00041 
00042 #define MAX_LINE_LENGTH 512
00043 #define MAX_FILENAME_LENGTH 256
00044 
00045 CDROM_Interface_Image::BinaryFile::BinaryFile(const char *filename, bool &error)
00046 {
00047         file = new ifstream(filename, ios::in | ios::binary);
00048         error = (file == NULL) || (file->fail());
00049 }
00050 
00051 CDROM_Interface_Image::BinaryFile::~BinaryFile()
00052 {
00053         delete file;
00054 }
00055 
00056 bool CDROM_Interface_Image::BinaryFile::read(Bit8u *buffer, int seek, int count)
00057 {
00058         file->seekg(seek, ios::beg);
00059         file->read((char*)buffer, count);
00060         return !(file->fail());
00061 }
00062 
00063 int CDROM_Interface_Image::BinaryFile::getLength()
00064 {
00065         file->seekg(0, ios::end);
00066         int length = (int)file->tellg();
00067         if (file->fail()) return -1;
00068         return length;
00069 }
00070 
00071 // initialize static members
00072 int CDROM_Interface_Image::refCount = 0;
00073 CDROM_Interface_Image* CDROM_Interface_Image::images[26] = {NULL};
00074 CDROM_Interface_Image::imagePlayer CDROM_Interface_Image::player = {
00075         NULL, NULL, NULL, {0}, 0, 0, 0, false, false, false, {0} };
00076 
00077         
00078 CDROM_Interface_Image::CDROM_Interface_Image(Bit8u subUnit)
00079 {
00080         images[subUnit] = this;
00081         if (refCount == 0) {
00082                 player.mutex = SDL_CreateMutex();
00083                 if (player.channel == NULL)
00084                         player.channel = MIXER_AddChannel(&CDAudioCallBack, 44100, "CDAUDIO");
00085                 player.channel->Enable(true);
00086         }
00087         refCount++;
00088 }
00089 
00090 CDROM_Interface_Image::~CDROM_Interface_Image()
00091 {
00092         refCount--;
00093         if (player.cd == this) player.cd = NULL;
00094         ClearTracks();
00095         if (refCount == 0) {
00096                 SDL_DestroyMutex(player.mutex);
00097                 if (player.channel) {
00098                         player.channel->Enable(false);
00099                         MIXER_DelChannel(player.channel);
00100                         player.channel = NULL;
00101                 }
00102         }
00103 }
00104 
00105 void CDROM_Interface_Image::InitNewMedia()
00106 {
00107 }
00108 
00109 bool CDROM_Interface_Image::SetDevice(char* path, int forceCD)
00110 {
00111     (void)forceCD;//UNUSED
00112     (void)path;//UNUSED
00113         if (LoadCueSheet(path)) return true;
00114         if (LoadIsoFile(path)) return true;
00115         
00116         // print error message on dosbox console
00117         /*
00118         char buf[MAX_LINE_LENGTH];
00119         snprintf(buf, MAX_LINE_LENGTH, "Could not load image file: %s\n", path);
00120         Bit16u size = (Bit16u)strlen(buf);
00121         DOS_WriteFile(STDOUT, (Bit8u*)buf, &size);
00122         */
00123         LOG_MSG("Could not load image file: %s", path);
00124         return false;
00125 }
00126 
00127 bool CDROM_Interface_Image::GetUPC(unsigned char& attr, char* upc)
00128 {
00129         attr = 0;
00130         strcpy(upc, this->mcn.c_str());
00131         return true;
00132 }
00133 
00134 bool CDROM_Interface_Image::GetAudioTracks(int& stTrack, int& end, TMSF& leadOut)
00135 {
00136         stTrack = 1;
00137         end = (int)(tracks.size() - 1u);
00138         FRAMES_TO_MSF((unsigned int)tracks[tracks.size() - 1u].start + 150u, &leadOut.min, &leadOut.sec, &leadOut.fr);
00139         return true;
00140 }
00141 
00142 bool CDROM_Interface_Image::GetAudioTrackInfo(int track, TMSF& start, unsigned char& attr)
00143 {
00144         if (track < 1 || track > (int)tracks.size()) return false;
00145         FRAMES_TO_MSF((unsigned int)tracks[(unsigned int)track - 1u].start + 150u, &start.min, &start.sec, &start.fr);
00146         attr = tracks[(unsigned int)track - 1u].attr;
00147         return true;
00148 }
00149 
00150 bool CDROM_Interface_Image::GetAudioSub(unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos)
00151 {
00152         int cur_track = GetTrack(player.currFrame);
00153         if (cur_track < 1) return false;
00154         track = (unsigned char)cur_track;
00155         attr = tracks[track - 1u].attr;
00156         index = 1;
00157         FRAMES_TO_MSF((unsigned int)player.currFrame + 150u, &absPos.min, &absPos.sec, &absPos.fr);
00158         FRAMES_TO_MSF((unsigned int)player.currFrame - (unsigned int)tracks[track - 1u].start + 150u, &relPos.min, &relPos.sec, &relPos.fr);
00159         return true;
00160 }
00161 
00162 bool CDROM_Interface_Image::GetAudioStatus(bool& playing, bool& pause)
00163 {
00164         playing = player.isPlaying;
00165         pause = player.isPaused;
00166         return true;
00167 }
00168 
00169 bool CDROM_Interface_Image::GetMediaTrayStatus(bool& mediaPresent, bool& mediaChanged, bool& trayOpen)
00170 {
00171         mediaPresent = true;
00172         mediaChanged = false;
00173         trayOpen = false;
00174         return true;
00175 }
00176 
00177 bool CDROM_Interface_Image::PlayAudioSector(unsigned long start,unsigned long len)
00178 {
00179         // We might want to do some more checks. E.g valid start and length
00180         SDL_mutexP(player.mutex);
00181         player.cd = this;
00182         player.currFrame = start;
00183         player.targetFrame = start + len;
00184         int track = GetTrack(start) - 1;
00185         if(track >= 0 && tracks[(unsigned int)track].attr == 0x40) {
00186                 LOG(LOG_MISC,LOG_WARN)("Game tries to play the data track. Not doing this");
00187                 player.isPlaying = false;
00188                 //Unclear wether return false should be here. 
00189                 //specs say that this function returns at once and games should check the status wether the audio is actually playing
00190                 //Real drives either fail or succeed as well
00191         } else player.isPlaying = true;
00192         player.isPaused = false;
00193         SDL_mutexV(player.mutex);
00194         return true;
00195 }
00196 
00197 bool CDROM_Interface_Image::PauseAudio(bool resume)
00198 {
00199         player.isPaused = !resume;
00200         return true;
00201 }
00202 
00203 bool CDROM_Interface_Image::StopAudio(void)
00204 {
00205         player.isPlaying = false;
00206         player.isPaused = false;
00207         return true;
00208 }
00209 
00210 void CDROM_Interface_Image::ChannelControl(TCtrl ctrl)
00211 {
00212         player.ctrlUsed = (ctrl.out[0]!=0 || ctrl.out[1]!=1 || ctrl.vol[0]<0xfe || ctrl.vol[1]<0xfe);
00213         player.ctrlData = ctrl;
00214 }
00215 
00216 bool CDROM_Interface_Image::ReadSectors(PhysPt buffer, bool raw, unsigned long sector, unsigned long num)
00217 {
00218         unsigned int sectorSize = raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE;
00219         Bitu buflen = num * sectorSize;
00220         Bit8u* buf = new Bit8u[buflen];
00221         
00222         bool success = true; //Gobliiins reads 0 sectors
00223         for(unsigned long i = 0; i < num; i++) {
00224                 success = ReadSector(&buf[i * sectorSize], raw, sector + i);
00225                 if (!success) break;
00226         }
00227 
00228         MEM_BlockWrite(buffer, buf, buflen);
00229         delete[] buf;
00230 
00231         return success;
00232 }
00233 
00234 bool CDROM_Interface_Image::ReadSectorsHost(void *buffer, bool raw, unsigned long sector, unsigned long num)
00235 {
00236         unsigned int sectorSize = raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE;
00237         bool success = true; //Gobliiins reads 0 sectors
00238         for(unsigned long i = 0; i < num; i++) {
00239                 success = ReadSector((Bit8u*)buffer + (i * sectorSize), raw, sector + i);
00240                 if (!success) break;
00241         }
00242 
00243         return success;
00244 }
00245 
00246 bool CDROM_Interface_Image::LoadUnloadMedia(bool unload)
00247 {
00248     (void)unload;//UNUSED
00249         return true;
00250 }
00251 
00252 int CDROM_Interface_Image::GetTrack(int sector)
00253 {
00254         vector<Track>::iterator i = tracks.begin();
00255         vector<Track>::iterator end = tracks.end() - 1;
00256         
00257         while(i != end) {
00258                 Track &curr = *i;
00259                 Track &next = *(i + 1);
00260                 if (curr.start <= sector && sector < next.start) return curr.number;
00261                 i++;
00262         }
00263         return -1;
00264 }
00265 
00266 bool CDROM_Interface_Image::ReadSector(Bit8u *buffer, bool raw, unsigned long sector)
00267 {
00268         int track = GetTrack(sector) - 1;
00269         if (track < 0) return false;
00270 
00271         if (tracks[(unsigned int)track].sectorSize != RAW_SECTOR_SIZE && raw) return false;
00272 
00273         /* we must reject non-raw reads against CD audio sectors.
00274          * not just for correctness, but also to avoid a weird bug in MSCDEX.EXE
00275          * that reads the non-data sectors one-by-one looking for a volume label
00276          * that doesn't exist on pure CD audio emulated images */
00277         if (tracks[(unsigned int)track].sectorSize == RAW_SECTOR_SIZE && !raw) {
00278                 if (((unsigned char)tracks[(unsigned int)track].attr&0x40u) == 0x00u) {
00279                         LOG_MSG("Rejecting cooked read from raw audio CD sector\n");
00280                         return false;
00281                 }
00282         }
00283 
00284         unsigned int length = (raw ? RAW_SECTOR_SIZE : COOKED_SECTOR_SIZE);
00285 
00286         if (sector >= (unsigned long)(tracks[(unsigned int)track].start + tracks[(unsigned int)track].length)) {
00287                 memset(buffer, 0, length);
00288                 return true;
00289         }
00290 
00291         unsigned long seek = (unsigned long)tracks[(unsigned int)track].skip +
00292         (sector - (unsigned long)tracks[(unsigned int)track].start) * (unsigned long)tracks[(unsigned int)track].sectorSize;
00293         if (tracks[(unsigned int)track].sectorSize == RAW_SECTOR_SIZE && !tracks[(unsigned int)track].mode2 && !raw) seek += 16ul;
00294         if (tracks[(unsigned int)track].mode2 && !raw) seek += 24ul;
00295 
00296         return tracks[(unsigned int)track].file->read(buffer, seek, (int)length);
00297 }
00298 
00299 void CDROM_Interface_Image::CDAudioCallBack(Bitu len)
00300 {
00301         len *= 4u;       // 16 bit, stereo
00302         if (!len) return;
00303         if (!player.isPlaying || player.isPaused) {
00304                 player.channel->AddSilence();
00305                 return;
00306         }
00307         
00308         SDL_mutexP(player.mutex);
00309         while (player.bufLen < (Bits)len) {
00310                 bool success;
00311                 if (player.targetFrame > player.currFrame)
00312                         success = player.cd->ReadSector(&player.buffer[player.bufLen], true, (unsigned long)player.currFrame);
00313                 else success = false;
00314                 
00315                 if (success) {
00316                         player.currFrame++;
00317                         player.bufLen += RAW_SECTOR_SIZE;
00318                 } else {
00319                         memset(&player.buffer[player.bufLen], 0, (size_t)(len - (Bitu)player.bufLen));
00320                         player.bufLen = len;
00321                         player.isPlaying = false;
00322                 }
00323         }
00324         SDL_mutexV(player.mutex);
00325         if (player.ctrlUsed) {
00326                 Bit16s sample0,sample1;
00327                 Bit16s * samples=(Bit16s *)&player.buffer;
00328                 for (Bitu pos=0;pos<len/4;pos++) {
00329 #if defined(WORDS_BIGENDIAN)
00330                         sample0=(Bit16s)host_readw((HostPt)&samples[pos*2+player.ctrlData.out[0]]);
00331                         sample1=(Bit16s)host_readw((HostPt)&samples[pos*2+player.ctrlData.out[1]]);
00332 #else
00333                         sample0=samples[pos*2+player.ctrlData.out[0]];
00334                         sample1=samples[pos*2+player.ctrlData.out[1]];
00335 #endif
00336                         samples[pos*2+0]=(Bit16s)(sample0*player.ctrlData.vol[0]/255.0);
00337                         samples[pos*2+1]=(Bit16s)(sample1*player.ctrlData.vol[1]/255.0);
00338                 }
00339 #if defined(WORDS_BIGENDIAN)
00340                 player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
00341         } else  player.channel->AddSamples_s16_nonnative(len/4,(Bit16s *)player.buffer);
00342 #else
00343         }
00344         player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
00345 #endif
00346         memmove(player.buffer, &player.buffer[len], (size_t)((Bitu)player.bufLen - len));
00347         player.bufLen -= (int)len;
00348 }
00349 
00350 bool CDROM_Interface_Image::LoadIsoFile(char* filename)
00351 {
00352         tracks.clear();
00353         
00354         // data track
00355         Track track = {0, 0, 0, 0, 0, 0, false, NULL};
00356         bool error;
00357         track.file = new BinaryFile(filename, error);
00358         if (error) {
00359                 delete track.file;
00360                 return false;
00361         }
00362         track.number = 1;
00363         track.attr = 0x40;//data
00364         
00365         // try to detect iso type
00366         if (CanReadPVD(track.file, COOKED_SECTOR_SIZE, false)) {
00367                 track.sectorSize = COOKED_SECTOR_SIZE;
00368                 track.mode2 = false;
00369         } else if (CanReadPVD(track.file, RAW_SECTOR_SIZE, false)) {
00370                 track.sectorSize = RAW_SECTOR_SIZE;
00371                 track.mode2 = false;            
00372         } else if (CanReadPVD(track.file, 2336, true)) {
00373                 track.sectorSize = 2336;
00374                 track.mode2 = true;             
00375         } else if (CanReadPVD(track.file, RAW_SECTOR_SIZE, true)) {
00376                 track.sectorSize = RAW_SECTOR_SIZE;
00377                 track.mode2 = true;             
00378         } else return false;
00379         
00380         track.length = track.file->getLength() / track.sectorSize;
00381         tracks.push_back(track);
00382         
00383         // leadout track
00384         track.number = 2;
00385         track.attr = 0;
00386         track.start = track.length;
00387         track.length = 0;
00388         track.file = NULL;
00389         tracks.push_back(track);
00390 
00391         return true;
00392 }
00393 
00394 bool CDROM_Interface_Image::CanReadPVD(TrackFile *file, int sectorSize, bool mode2)
00395 {
00396         Bit8u pvd[COOKED_SECTOR_SIZE];
00397         int seek = 16 * sectorSize;     // first vd is located at sector 16
00398         if (sectorSize == RAW_SECTOR_SIZE && !mode2) seek += 16;
00399         if (mode2) seek += 24;
00400         file->read(pvd, seek, COOKED_SECTOR_SIZE);
00401         // pvd[0] = descriptor type, pvd[1..5] = standard identifier, pvd[6] = iso version (+8 for High Sierra)
00402         return ((pvd[0] == 1 && !strncmp((char*)(&pvd[1]), "CD001", 5) && pvd[6] == 1) ||
00403                         (pvd[8] == 1 && !strncmp((char*)(&pvd[9]), "CDROM", 5) && pvd[14] == 1));
00404 }
00405 
00406 #if defined(WIN32)
00407 static string dirname(char * file) {
00408         char * sep = strrchr(file, '\\');
00409         if (sep == NULL)
00410                 sep = strrchr(file, '/');
00411         if (sep == NULL)
00412                 return "";
00413         else {
00414                 int len = (int)(sep - file);
00415                 char tmp[MAX_FILENAME_LENGTH];
00416                 safe_strncpy(tmp, file, len+1);
00417                 return tmp;
00418         }
00419 }
00420 #endif
00421 
00422 bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
00423 {
00424         Track track = {0, 0, 0, 0, 0, 0, false, NULL};
00425         tracks.clear();
00426         int shift = 0;
00427         int currPregap = 0;
00428         int totalPregap = 0;
00429         int prestart = 0;
00430         bool success;
00431         bool canAddTrack = false;
00432         char tmp[MAX_FILENAME_LENGTH];  // dirname can change its argument
00433         safe_strncpy(tmp, cuefile, MAX_FILENAME_LENGTH);
00434         string pathname(dirname(tmp));
00435         ifstream in;
00436         in.open(cuefile, ios::in);
00437         if (in.fail()) return false;
00438         
00439         while(!in.eof()) {
00440                 // get next line
00441                 char buf[MAX_LINE_LENGTH];
00442                 in.getline(buf, MAX_LINE_LENGTH);
00443                 if (in.fail() && !in.eof()) return false;  // probably a binary file
00444                 istringstream line(buf);
00445                 
00446                 string command;
00447                 GetCueKeyword(command, line);
00448                 
00449                 if (command == "TRACK") {
00450                         if (canAddTrack) success = AddTrack(track, shift, prestart, totalPregap, currPregap);
00451                         else success = true;
00452                         
00453                         track.start = 0;
00454                         track.skip = 0;
00455                         currPregap = 0;
00456                         prestart = 0;
00457         
00458                         line >> track.number;
00459                         string type;
00460                         GetCueKeyword(type, line);
00461                         
00462                         if (type == "AUDIO") {
00463                                 track.sectorSize = RAW_SECTOR_SIZE;
00464                                 track.attr = 0;
00465                                 track.mode2 = false;
00466                         } else if (type == "MODE1/2048") {
00467                                 track.sectorSize = COOKED_SECTOR_SIZE;
00468                                 track.attr = 0x40;
00469                                 track.mode2 = false;
00470                         } else if (type == "MODE1/2352") {
00471                                 track.sectorSize = RAW_SECTOR_SIZE;
00472                                 track.attr = 0x40;
00473                                 track.mode2 = false;
00474                         } else if (type == "MODE2/2336") {
00475                                 track.sectorSize = 2336;
00476                                 track.attr = 0x40;
00477                                 track.mode2 = true;
00478                         } else if (type == "MODE2/2352") {
00479                                 track.sectorSize = RAW_SECTOR_SIZE;
00480                                 track.attr = 0x40;
00481                                 track.mode2 = true;
00482                         } else success = false;
00483                         
00484                         canAddTrack = true;
00485                 }
00486                 else if (command == "INDEX") {
00487                         int index;
00488                         line >> index;
00489                         int frame;
00490                         success = GetCueFrame(frame, line);
00491                         
00492                         if (index == 1) track.start = frame;
00493                         else if (index == 0) prestart = frame;
00494                         // ignore other indices
00495                 }
00496                 else if (command == "FILE") {
00497                         if (canAddTrack) success = AddTrack(track, shift, prestart, totalPregap, currPregap);
00498                         else success = true;
00499                         canAddTrack = false;
00500                         
00501                         string filename;
00502                         GetCueString(filename, line);
00503                         GetRealFileName(filename, pathname);
00504                         string type;
00505                         GetCueKeyword(type, line);
00506 
00507                         track.file = NULL;
00508                         bool error = true;
00509                         if (type == "BINARY") {
00510                                 track.file = new BinaryFile(filename.c_str(), error);
00511                         }
00512                         if (error) {
00513                                 delete track.file;
00514                                 success = false;
00515                         }
00516                 }
00517                 else if (command == "PREGAP") success = GetCueFrame(currPregap, line);
00518                 else if (command == "CATALOG") success = GetCueString(mcn, line);
00519                 // ignored commands
00520                 else if (command == "CDTEXTFILE" || command == "FLAGS" || command == "ISRC"
00521                         || command == "PERFORMER" || command == "POSTGAP" || command == "REM"
00522                         || command == "SONGWRITER" || command == "TITLE" || command == "") success = true;
00523                 // failure
00524                 else success = false;
00525 
00526                 if (!success) return false;
00527         }
00528         // add last track
00529         if (!AddTrack(track, shift, prestart, totalPregap, currPregap)) return false;
00530         
00531         // add leadout track
00532         track.number++;
00533         track.attr = 0;//sync with load iso
00534         track.start = 0;
00535         track.length = 0;
00536         track.file = NULL;
00537         if(!AddTrack(track, shift, 0, totalPregap, 0)) return false;
00538 
00539         return true;
00540 }
00541 
00542 bool CDROM_Interface_Image::AddTrack(Track &curr, int &shift, int prestart, int &totalPregap, int currPregap)
00543 {
00544         // frames between index 0(prestart) and 1(curr.start) must be skipped
00545         int skip;
00546         if (prestart > 0) {
00547                 if (prestart > curr.start) return false;
00548                 skip = curr.start - prestart;
00549         } else skip = 0;
00550         
00551         // first track (track number must be 1)
00552         if (tracks.empty()) {
00553                 if (curr.number != 1) return false;
00554                 curr.skip = skip * curr.sectorSize;
00555                 curr.start += currPregap;
00556                 totalPregap = currPregap;
00557                 tracks.push_back(curr);
00558                 return true;
00559         }
00560         
00561         Track &prev = *(tracks.end() - 1);
00562         
00563         // current track consumes data from the same file as the previous
00564         if (prev.file == curr.file) {
00565                 curr.start += shift;
00566                 prev.length = curr.start + totalPregap - prev.start - skip;
00567                 curr.skip += prev.skip + prev.length * prev.sectorSize + skip * curr.sectorSize;                
00568                 totalPregap += currPregap;
00569                 curr.start += totalPregap;
00570         // current track uses a different file as the previous track
00571         } else {
00572                 int tmp = prev.file->getLength() - prev.skip;
00573                 prev.length = tmp / prev.sectorSize;
00574                 if (tmp % prev.sectorSize != 0) prev.length++; // padding
00575                 
00576                 curr.start += prev.start + prev.length + currPregap;
00577                 curr.skip = skip * curr.sectorSize;
00578                 shift += prev.start + prev.length;
00579                 totalPregap = currPregap;
00580         }
00581         
00582         // error checks
00583         if (curr.number <= 1) return false;
00584         if (prev.number + 1 != curr.number) return false;
00585         if (curr.start < prev.start + prev.length) return false;
00586         if (curr.length < 0) return false;
00587         
00588         tracks.push_back(curr);
00589         return true;
00590 }
00591 
00592 bool CDROM_Interface_Image::HasDataTrack(void)
00593 {
00594         //Data track has attribute 0x40
00595         for(track_it it = tracks.begin(); it != tracks.end(); it++) {
00596                 if ((*it).attr == 0x40) return true;
00597         }
00598         return false;
00599 }
00600 
00601 
00602 bool CDROM_Interface_Image::GetRealFileName(string &filename, string &pathname)
00603 {
00604         // check if file exists
00605         struct stat test;
00606         if (stat(filename.c_str(), &test) == 0) return true;
00607         
00608         // check if file with path relative to cue file exists
00609         string tmpstr(pathname + "/" + filename);
00610         if (stat(tmpstr.c_str(), &test) == 0) {
00611                 filename = tmpstr;
00612                 return true;
00613         }
00614         // finally check if file is in a dosbox local drive
00615         char fullname[CROSS_LEN];
00616         char tmp[CROSS_LEN];
00617         safe_strncpy(tmp, filename.c_str(), CROSS_LEN);
00618         Bit8u drive;
00619         if (!DOS_MakeName(tmp, fullname, &drive)) return false;
00620         
00621         localDrive *ldp = dynamic_cast<localDrive*>(Drives[drive]);
00622         if (ldp) {
00623                 ldp->GetSystemFilename(tmp, fullname);
00624                 if (stat(tmp, &test) == 0) {
00625                         filename = tmp;
00626                         return true;
00627                 }
00628         }
00629 #if defined (WIN32) || defined(OS2)
00630         //Nothing
00631 #else
00632         //Consider the possibility that the filename has a windows directory seperator (inside the CUE file) 
00633         //which is common for some commercial rereleases of DOS games using DOSBox
00634 
00635         string copy = filename;
00636         size_t l = copy.size();
00637         for (size_t i = 0; i < l;i++) {
00638                 if(copy[i] == '\\') copy[i] = '/';
00639         }
00640 
00641         if (stat(copy.c_str(), &test) == 0) {
00642                 filename = copy;
00643                 return true;
00644         }
00645 
00646         tmpstr = pathname + "/" + copy;
00647         if (stat(tmpstr.c_str(), &test) == 0) {
00648                 filename = tmpstr;
00649                 return true;
00650         }
00651 
00652 #endif
00653         return false;
00654 }
00655 
00656 bool CDROM_Interface_Image::GetCueKeyword(string &keyword, istream &in)
00657 {
00658         in >> keyword;
00659         for(Bitu i = 0; i < keyword.size(); i++) keyword[i] = toupper(keyword[i]);
00660         
00661         return true;
00662 }
00663 
00664 bool CDROM_Interface_Image::GetCueFrame(int &frames, istream &in)
00665 {
00666         string msf;
00667         in >> msf;
00668         unsigned int min, sec, fr;
00669         bool success = sscanf(msf.c_str(), "%u:%u:%u", &min, &sec, &fr) == 3;
00670         frames = (int)MSF_TO_FRAMES(min, sec, fr);
00671         
00672         return success;
00673 }
00674 
00675 bool CDROM_Interface_Image::GetCueString(string &str, istream &in)
00676 {
00677         int pos = (int)in.tellg();
00678         in >> str;
00679         if (str[0] == '\"') {
00680                 if (str[str.size() - 1] == '\"') {
00681                         str.assign(str, 1, str.size() - 2);
00682                 } else {
00683                         in.seekg(pos, ios::beg);
00684                         char buffer[MAX_FILENAME_LENGTH];
00685                         in.getline(buffer, MAX_FILENAME_LENGTH, '\"');  // skip
00686                         in.getline(buffer, MAX_FILENAME_LENGTH, '\"');
00687                         str = buffer;
00688                 }
00689         }
00690         return true;
00691 }
00692 
00693 void CDROM_Interface_Image::ClearTracks()
00694 {
00695         vector<Track>::iterator i = tracks.begin();
00696         vector<Track>::iterator end = tracks.end();
00697 
00698         TrackFile* last = NULL; 
00699         while(i != end) {
00700                 Track &curr = *i;
00701                 if (curr.file != last) {
00702                         delete curr.file;
00703                         last = curr.file;
00704                 }
00705                 i++;
00706         }
00707         tracks.clear();
00708 }
00709 
00710 void CDROM_Image_Destroy(Section*) {
00711 }
00712