/*! Sets up to read from place specified by source_name and driver_id. This should be called before using any other routine, except cdio_init. This will call cdio_init, if that hasn't been done previously. NULL is returned on error. */ CdIo * cdio_open (const char *orig_source_name, driver_id_t driver_id) { char *source_name; if (CdIo_last_driver == -1) cdio_init(); if (NULL == orig_source_name || strlen(orig_source_name)==0) source_name = cdio_get_default_device(NULL); else source_name = strdup(orig_source_name); retry: switch (driver_id) { case DRIVER_UNKNOWN: { CdIo *cdio=scan_for_driver(CDIO_MIN_DRIVER, CDIO_MAX_DRIVER, source_name); if (cdio != NULL && cdio_is_device(source_name, cdio->driver_id)) { driver_id = cdio->driver_id; } else { struct stat buf; if (0 != stat(source_name, &buf)) { return NULL; } if (S_ISREG(buf.st_mode)) { /* FIXME: check to see if is a text file. If so, then set SOURCE_CUE. */ int i=strlen(source_name)-strlen("bin"); if (i > 0 && ( (source_name)[i] =='n' || (source_name)[i] =='N' ) && ( (source_name)[i+1] =='r' || (source_name)[i+1] =='R' ) && ( (source_name)[i+2] =='g' || (source_name)[i+2] =='G' ) ) driver_id = DRIVER_NRG; else if (i > 0 && ( (source_name)[i] =='c' || (source_name)[i] =='C') && ( (source_name)[i+1] =='u' || (source_name)[i+1] =='U') && ( (source_name)[i+2] =='e' || (source_name)[i+2] =='E') ) driver_id = DRIVER_BINCUE; else driver_id = DRIVER_BINCUE; } else { cdio_destroy(cdio); return NULL; } } cdio_destroy(cdio); goto retry; } case DRIVER_DEVICE: { /* Scan for a driver. */ CdIo *ret = cdio_open_cd(source_name); free(source_name); return ret; } break; case DRIVER_BSDI: case DRIVER_FREEBSD: case DRIVER_LINUX: case DRIVER_SOLARIS: case DRIVER_WIN32: case DRIVER_OSX: case DRIVER_NRG: case DRIVER_BINCUE: if ((*CdIo_all_drivers[driver_id].have_driver)()) { CdIo *ret = (*CdIo_all_drivers[driver_id].driver_open)(source_name); if (ret) ret->driver_id = driver_id; return ret; } } free(source_name); return NULL; }
CDRFile *cdrfile_open(const char *path) { CDRFile *ret = (CDRFile *)calloc(1, sizeof(CDRFile)); struct stat stat_buf; if(path == NULL || stat(path, &stat_buf) || !S_ISREG(stat_buf.st_mode)) { CdIo *p_cdio; char **devices; char **parseit; cdio_init(); GetFileBase("cdrom"); devices = cdio_get_devices(DRIVER_DEVICE); parseit = devices; if(parseit) { MDFN_printf(_("Connected physical devices:\n")); MDFN_indent(1); while(*parseit) { MDFN_printf("%s\n", *parseit); parseit++; } MDFN_indent(-1); } if(!parseit || parseit == devices) { MDFN_PrintError(_("No CDROM drives detected(or no disc present).")); if(devices) cdio_free_device_list(devices); free(ret); return(NULL); } if(devices) cdio_free_device_list(devices); p_cdio = cdio_open_cd(path); //, DRIVER_UNKNOWN); //NULL, DRIVER_UNKNOWN); if(!p_cdio) { free(ret); return(NULL); } ret->p_cdio = p_cdio; ret->FirstTrack = cdio_get_first_track_num(ret->p_cdio); ret->NumTracks = cdio_get_num_tracks(ret->p_cdio); ret->total_sectors = cdio_stat_size(ret->p_cdio); if(ret->FirstTrack > 99) { MDFN_PrintError(_("Invalid first track: %d\n"), ret->FirstTrack); free(ret); cdio_destroy(p_cdio); return(NULL); } if(ret->NumTracks > 100) { MDFN_PrintError(_("Invalid track count: %d\n"), ret->NumTracks); free(ret); cdio_destroy(p_cdio); return(NULL); } for(track_t track = ret->FirstTrack; track < (ret->FirstTrack + ret->NumTracks); track++) { memset(&ret->Tracks[track], 0, sizeof(CDRFILE_TRACK_INFO)); ret->Tracks[track].sectors = cdio_get_track_sec_count(ret->p_cdio, track); ret->Tracks[track].LSN = cdio_get_track_lsn(ret->p_cdio, track); ret->Tracks[track].Format = cdio_get_track_format(ret->p_cdio, track); } return(ret); } FILE *fp = fopen(path, "rb"); bool IsTOC = FALSE; // Assign opposite maximum values so our tests will work! int FirstTrack = 99; int LastTrack = 0; if(!fp) { MDFN_PrintError(_("Error opening CUE sheet/TOC \"%s\": %m\n"), path, errno); free(ret); return(NULL); } GetFileBase(path); char linebuf[512]; int32 active_track = -1; int32 AutoTrackInc = 1; // For TOC CDRFILE_TRACK_INFO TmpTrack; memset(&TmpTrack, 0, sizeof(TmpTrack)); while(fgets(linebuf, 512, fp) > 0) { char cmdbuf[512], raw_args[512], args[4][512]; int argcount = 0; raw_args[0] = 0; cmdbuf[0] = 0; args[0][0] = args[1][0] = args[2][0] = args[3][0] = 0; if(!strncasecmp(linebuf, "CD_ROM", 6) || !strncasecmp(linebuf, "CD_DA", 5) || !strncasecmp(linebuf, "CD_ROM_XA", 9)) { IsTOC = TRUE; puts("TOC file detected."); } if(IsTOC) { char *ss_loc = strstr(linebuf, "//"); if(ss_loc) { ss_loc[0] = '\n'; // For consistency! ss_loc[1] = 0; } } trio_sscanf(linebuf, "%s %[^\r\n]", cmdbuf, raw_args); if(!strcasecmp(cmdbuf, "CD_ROM") || !strcasecmp(cmdbuf, "CD_DA")) IsTOC = TRUE; UnQuotify(UnQuotify(UnQuotify(UnQuotify(raw_args, args[0]), args[1]), args[2]), args[3]); if(args[0][0]) { argcount++; if(args[1][0]) { argcount++; if(args[2][0]) { argcount++; if(args[3][0]) { argcount++; } } } } if(IsTOC) { if(!strcasecmp(cmdbuf, "TRACK")) { if(active_track >= 0) { memcpy(&ret->Tracks[active_track], &TmpTrack, sizeof(TmpTrack)); memset(&TmpTrack, 0, sizeof(TmpTrack)); active_track = -1; } if(AutoTrackInc > 99) { MDFN_printf(_("Invalid track number: %d\n"), AutoTrackInc); free(ret); return(NULL); } active_track = AutoTrackInc++; if(active_track < FirstTrack) FirstTrack = active_track; if(active_track > LastTrack) LastTrack = active_track; if(!strcasecmp(args[0], "AUDIO")) { TmpTrack.Format = TRACK_FORMAT_AUDIO; TmpTrack.RawAudioMSBFirst = TRUE; // Silly cdrdao... } else if(!strcasecmp(args[0], "MODE1")) { TmpTrack.Format = TRACK_FORMAT_DATA; TmpTrack.IsData2352 = 0; } else if(!strcasecmp(args[0], "MODE1_RAW")) { TmpTrack.Format = TRACK_FORMAT_DATA; TmpTrack.IsData2352 = 1; } if(!strcasecmp(args[1], "RW")) { TmpTrack.SubchannelMode = CDRF_SUBM_RW; MDFN_printf(_("\"RW\" format subchannel data not supported, only \"RW_RAW\" is!\n")); free(ret); return(0); } else if(!strcasecmp(args[1], "RW_RAW")) TmpTrack.SubchannelMode = CDRF_SUBM_RW_RAW; } // end to TRACK else if(!strcasecmp(cmdbuf, "SILENCE")) { } else if(!strcasecmp(cmdbuf, "ZERO")) { } else if(!strcasecmp(cmdbuf, "FILE") || !strcasecmp(cmdbuf, "AUDIOFILE")) { const char *binoffset = NULL; const char *msfoffset = NULL; const char *length = NULL; if(args[1][0] == '#') { binoffset = args[1] + 1; msfoffset = args[2]; length = args[3]; } else { msfoffset = args[1]; length = args[2]; } //printf("%s, %s, %s, %s\n", args[0], binoffset, msfoffset, length); if(!ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, msfoffset, length)) { free(ret); return(0); } } else if(!strcasecmp(cmdbuf, "DATAFILE")) { const char *binoffset = NULL; const char *length = NULL; if(args[1][0] == '#') { binoffset = args[1] + 1; length = args[2]; } else length = args[1]; if(!ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, NULL, length)) { free(ret); return(0); } } else if(!strcasecmp(cmdbuf, "INDEX")) { } else if(!strcasecmp(cmdbuf, "PREGAP")) { if(active_track < 0) { MDFN_printf(_("Command %s is outside of a TRACK definition!\n"), cmdbuf); free(ret); return(NULL); } int m,s,f; trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f); TmpTrack.pregap = (m * 60 + s) * 75 + f; } // end to PREGAP else if(!strcasecmp(cmdbuf, "START")) { if(active_track < 0) { MDFN_printf(_("Command %s is outside of a TRACK definition!\n"), cmdbuf); free(ret); return(NULL); } int m,s,f; trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f); TmpTrack.pregap = (m * 60 + s) * 75 + f; } } /*********** END TOC HANDLING ************/ else // now for CUE sheet handling { if(!strcasecmp(cmdbuf, "FILE")) { if(active_track >= 0) { memcpy(&ret->Tracks[active_track], &TmpTrack, sizeof(TmpTrack)); memset(&TmpTrack, 0, sizeof(TmpTrack)); active_track = -1; } std::string efn = MDFN_MakeFName(MDFNMKF_AUX, 0, args[0]); if(NULL == (TmpTrack.fp = fopen(efn.c_str(), "rb"))) { MDFN_printf(_("Could not open referenced file \"%s\": %m\n"), efn.c_str(), errno); free(ret); return(0); } TmpTrack.FirstFileInstance = 1; if(!strcasecmp(args[1], "BINARY")) { //TmpTrack.Format = TRACK_FORMAT_DATA; //struct stat stat_buf; //fstat(fileno(TmpTrack.fp), &stat_buf); //TmpTrack.sectors = stat_buf.st_size; // / 2048; } else if(!strcasecmp(args[1], "OGG") || !strcasecmp(args[1], "VORBIS") || !strcasecmp(args[1], "WAVE") || !strcasecmp(args[1], "WAV") || !strcasecmp(args[1], "PCM") || !strcasecmp(args[1], "MPC") || !strcasecmp(args[1], "MP+")) { TmpTrack.ovfile = (OggVorbis_File *) calloc(1, sizeof(OggVorbis_File)); if((TmpTrack.sf = sf_open_fd(fileno(TmpTrack.fp), SFM_READ, &TmpTrack.sfinfo, 0))) { free(TmpTrack.ovfile); TmpTrack.ovfile = NULL; } else if(!lseek(fileno(TmpTrack.fp), 0, SEEK_SET) && !ov_open(TmpTrack.fp, TmpTrack.ovfile, NULL, 0)) { //TmpTrack.Format = TRACK_FORMAT_AUDIO; //TmpTrack.sectors = ov_pcm_total(&TmpTrack.ovfile, -1) / 588; } else { free(TmpTrack.ovfile); TmpTrack.ovfile = NULL; fseek(TmpTrack.fp, 0, SEEK_SET); TmpTrack.MPCReaderFile = (mpc_reader_file *)calloc(1, sizeof(mpc_reader_file)); TmpTrack.MPCStreamInfo = (mpc_streaminfo *)calloc(1, sizeof(mpc_streaminfo)); TmpTrack.MPCDecoder = (mpc_decoder *)calloc(1, sizeof(mpc_decoder)); TmpTrack.MPCBuffer = (MPC_SAMPLE_FORMAT *)calloc(MPC_DECODER_BUFFER_LENGTH, sizeof(MPC_SAMPLE_FORMAT)); mpc_streaminfo_init(TmpTrack.MPCStreamInfo); mpc_reader_setup_file_reader(TmpTrack.MPCReaderFile, TmpTrack.fp); if(mpc_streaminfo_read(TmpTrack.MPCStreamInfo, &TmpTrack.MPCReaderFile->reader) != ERROR_CODE_OK) { MDFN_printf(_("Unsupported audio track file format: %s\n"), args[0]); free(TmpTrack.MPCReaderFile); free(TmpTrack.MPCStreamInfo); free(TmpTrack.MPCDecoder); free(TmpTrack.MPCBuffer); free(ret); return(0); } mpc_decoder_setup(TmpTrack.MPCDecoder, &TmpTrack.MPCReaderFile->reader); if(!mpc_decoder_initialize(TmpTrack.MPCDecoder, TmpTrack.MPCStreamInfo)) { MDFN_printf(_("Error initializing MusePack decoder: %s!\n"), args[0]); free(TmpTrack.MPCReaderFile); free(TmpTrack.MPCStreamInfo); free(TmpTrack.MPCDecoder); free(TmpTrack.MPCBuffer); free(ret); return(0); } } } else { MDFN_printf(_("Unsupported track format: %s\n"), args[1]); free(ret); return(0); } } else if(!strcasecmp(cmdbuf, "TRACK")) { if(active_track >= 0) { memcpy(&ret->Tracks[active_track], &TmpTrack, sizeof(TmpTrack)); TmpTrack.FirstFileInstance = 0; TmpTrack.pregap = 0; } active_track = atoi(args[0]); if(active_track < FirstTrack) FirstTrack = active_track; if(active_track > LastTrack) LastTrack = active_track; if(!strcasecmp(args[1], "AUDIO")) TmpTrack.Format = TRACK_FORMAT_AUDIO; else if(!strcasecmp(args[1], "MODE1/2048")) { TmpTrack.Format = TRACK_FORMAT_DATA; TmpTrack.IsData2352 = 0; } else if(!strcasecmp(args[1], "MODE1/2352")) { TmpTrack.Format = TRACK_FORMAT_DATA; TmpTrack.IsData2352 = 1; } TmpTrack.sectors = GetSectorCount(&TmpTrack); if(active_track < 0 || active_track > 99) { MDFN_printf(_("Invalid track number: %d\n"), active_track); return(0); } } else if(!strcasecmp(cmdbuf, "INDEX")) { if(active_track >= 0 && (!strcasecmp(args[0], "01") || !strcasecmp(args[0], "1"))) { int m,s,f; trio_sscanf(args[1], "%d:%d:%d", &m, &s, &f); TmpTrack.index = (m * 60 + s) * 75 + f; } } else if(!strcasecmp(cmdbuf, "PREGAP")) { if(active_track >= 0) { int m,s,f; trio_sscanf(args[0], "%d:%d:%d", &m, &s, &f); TmpTrack.pregap = (m * 60 + s) * 75 + f; } } } // end of CUE sheet handling } // end of fgets() loop if(ferror(fp)) { if(IsTOC) MDFN_printf(_("Error reading TOC file: %m\n"), errno); else MDFN_printf(_("Error reading CUE sheet: %m\n"), errno); return(0); } if(active_track >= 0) memcpy(&ret->Tracks[active_track], &TmpTrack, sizeof(TmpTrack)); if(FirstTrack > LastTrack) { MDFN_printf(_("No tracks found!\n")); return(0); } ret->FirstTrack = FirstTrack; ret->NumTracks = 1 + LastTrack - FirstTrack; lsn_t RunningLSN = 0; lsn_t LastIndex = 0; long FileOffset = 0; for(int x = ret->FirstTrack; x < (ret->FirstTrack + ret->NumTracks); x++) { if(IsTOC) { RunningLSN += ret->Tracks[x].pregap; ret->Tracks[x].LSN = RunningLSN; RunningLSN += ret->Tracks[x].sectors; } else // else handle CUE sheet... { if(ret->Tracks[x].FirstFileInstance) { LastIndex = 0; FileOffset = 0; } RunningLSN += ret->Tracks[x].pregap; ret->Tracks[x].LSN = RunningLSN; // Make sure this is set before the call to GetSectorCount() for the last track sector count fix. ret->Tracks[x].FileOffset = FileOffset; if((x + 1) >= (ret->FirstTrack + ret->NumTracks)) { if(!(ret->Tracks[x].FirstFileInstance)) { // This will fix the last sector count for CUE+BIN ret->Tracks[x].sectors = GetSectorCount(&ret->Tracks[x]); } } else if(ret->Tracks[x+1].FirstFileInstance) { //RunningLSN += ret->Tracks[x].sectors; } else { // Fix the sector count if we're CUE+BIN ret->Tracks[x].sectors = ret->Tracks[x + 1].index - ret->Tracks[x].index; } //printf("Poo: %d %d\n", x, ret->Tracks[x].sectors); RunningLSN += ret->Tracks[x].sectors; //printf("%d, %ld %d %d %d %d\n", x, FileOffset, ret->Tracks[x].index, ret->Tracks[x].pregap, ret->Tracks[x].sectors, ret->Tracks[x].LSN); if(ret->Tracks[x].Format == TRACK_FORMAT_AUDIO || TmpTrack.IsData2352) FileOffset += ret->Tracks[x].sectors * 2352; else FileOffset += ret->Tracks[x].sectors * 2048; } // end to cue sheet handling } // end to track loop LEC_Eval = MDFN_GetSettingB("cdrom.lec_eval"); if(LEC_Eval) { Init_LEC_Correct(); } MDFN_printf(_("Raw rip data correction using L-EC: %s\n\n"), LEC_Eval ? _("Enabled") : _("Disabled")); ret->total_sectors = RunningLSN; // Running LBA? Running LSN? arghafsdf...LSNBAN!#!$ -_- return(ret); }