int audiorip_get_track_addresses(int fd, struct track_address* const addresses, int const num_tracks, int verbose) { for (int i = 1; i <= num_tracks; ++i) { struct cdrom_tocentry current_track = { .cdte_track = i, .cdte_format = CDROM_MSF }; if (audiorip_cdrom_read_toc_entry(fd, ¤t_track) < 0) { fprintf(stderr, "Failed to read ToC entry for track: %d\n", i); audiorip_spindown_and_close(fd); return -1; } /** * In order to ascertain the length of the current track, we need * to know the address of the next track in the list. Should the * current track be the last one, we simply request the address * of the Leadout track. */ struct cdrom_tocentry next_track = { .cdte_track = i == num_tracks ? CDROM_LEADOUT : i + 1, .cdte_format = CDROM_MSF }; if (audiorip_cdrom_read_toc_entry(fd, &next_track) < 0) { if (next_track.cdte_track == CDROM_LEADOUT) { fprintf(stderr, "Failed to read ToC entry for leadout track\n"); } else { fprintf(stderr, "Failed to read ToC entry for track: %d\n", i + 1); } audiorip_spindown_and_close(fd); return -1; } addresses[i - 1].start = msf_to_frames(current_track.cdte_addr); addresses[i - 1].end = msf_to_frames(next_track.cdte_addr); addresses[i - 1].cdrom_addr = current_track.cdte_addr; if (verbose) { fprintf(stdout, "Track %d is %d frames long\n", i, addresses[i - 1].end - addresses[i - 1].start); } } return 0; } unsigned char const* audiorip_rip_track(int fd, struct track_address const address, int verbose) { int const readframes = address.end - address.start; unsigned char* buffer = malloc(readframes * CD_FRAMESIZE_RAW); unsigned char interim_buffer[CD_FRAMES * CD_FRAMESIZE_RAW]; struct cdrom_read_audio read_audio = { .addr = address.cdrom_addr, .addr_format = CDROM_MSF, .nframes = CD_FRAMES, .buf = &interim_buffer[0] }; if (verbose) { fprintf(stdout, "Reading track from %d to %d\n", address.start, address.end); } for (int chunk = 0; chunk < readframes; chunk += read_audio.nframes) { if ((CD_FRAMES + chunk) > readframes) { read_audio.nframes = readframes - chunk; } if (audiorip_cdrom_read_audio(fd, &read_audio) < 0) { fprintf(stderr, "Failed to read chunk\n"); fprintf(stderr, "%s\n", strerror(errno)); audiorip_spindown_and_close(fd); return NULL; } memcpy(buffer + (chunk * CD_FRAMESIZE_RAW), interim_buffer, read_audio.nframes * CD_FRAMESIZE_RAW); if (verbose) { fprintf(stdout, "progress %d/%d\n", chunk, readframes); } read_audio.addr.msf.frame += read_audio.nframes; if (read_audio.addr.msf.frame >= CD_FRAMES) { read_audio.addr.msf.second += read_audio.addr.msf.frame / CD_FRAMES; read_audio.addr.msf.frame = read_audio.addr.msf.frame % CD_FRAMES; if (read_audio.addr.msf.second >= 60) { read_audio.addr.msf.minute += read_audio.addr.msf.second / 60; read_audio.addr.msf.second = read_audio.addr.msf.second % 60; } } } return buffer; } int audiorip_rip_track_to_file(int fd, struct track_address const address, char const* filename, int verbose) { FILE* out = fopen(filename, "wb"); if (strcmp(strrchr(filename, '.'), ".wav") == 0) { write_wav_header(address, out); } unsigned char const* track_data = audiorip_rip_track(fd, address, verbose); if (track_data == NULL) { fclose(out); return -1; } fwrite((void const*)track_data, (size_t)CD_FRAMESIZE_RAW, (size_t)(address.end - address.start), out); fclose(out); audiorip_free_track(track_data); return 0; }
chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo) { FILE *infile; int i, trknum, cuemode = 0; static char token[128]; if (strstr(tocfname,".gdi")) { return chdcd_parse_gdi(tocfname, outtoc, outinfo); } if (strstr(tocfname,".cue")) { cuemode = 1; } infile = fopen(tocfname, "rt"); if (infile == (FILE *)NULL) { return CHDERR_FILE_NOT_FOUND; } /* clear structures */ memset(outtoc, 0, sizeof(cdrom_toc)); memset(outinfo, 0, sizeof(chdcd_track_input_info)); trknum = -1; while (!feof(infile)) { /* get the next line */ fgets(linebuffer, 511, infile); /* if EOF didn't hit, keep going */ if (!feof(infile)) { i = 0; TOKENIZE if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE"))) { int f; /* for bin/cue, this is where you increment the track # */ if (cuemode) { /* make sure we have a size for the current track before moving on */ if (trknum > -1) { chdcd_tracksize_helper(trknum, outtoc, outinfo); } trknum++; } /* found the data file for a track */ TOKENIZE /* keep the filename */ strncpy(&outinfo->fname[trknum][0], token, strlen(token)); /* get either the offset or the length */ TOKENIZE if (!strcmp(token, "SWAP")) { TOKENIZE outinfo->swap[trknum] = 1; } else { outinfo->swap[trknum] = 0; } if (token[0] == '#') { /* it's a decimal offset, use it */ f = strtoul(&token[1], NULL, 10); } else if (isdigit((UINT8)token[0])) { /* convert the time to an offset */ f = msf_to_frames( token ); f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize); } else { f = 0; } outinfo->offset[trknum] = f; TOKENIZE if (isdigit((UINT8)token[0])) { // this could be the length or an offset from the previous field. f = msf_to_frames( token ); TOKENIZE if (isdigit((UINT8)token[0])) { // it was an offset. f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize); outinfo->offset[trknum] += f; // this is the length. f = msf_to_frames( token ); } } else if( trknum == 0 && outinfo->offset[trknum] != 0 )
chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo) { FILE *infile; int i, trknum; static char token[128]; infile = fopen(tocfname, "rt"); if (infile == (FILE *)NULL) { return CHDERR_FILE_NOT_FOUND; } /* clear structures */ memset(outtoc, 0, sizeof(cdrom_toc)); memset(outinfo, 0, sizeof(chdcd_track_input_info)); trknum = -1; while (!feof(infile)) { /* get the next line */ fgets(linebuffer, 511, infile); /* if EOF didn't hit, keep going */ if (!feof(infile)) { i = 0; TOKENIZE if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE"))) { int f; /* found the data file for a track */ TOKENIZE /* keep the filename */ strncpy(&outinfo->fname[trknum][0], token, strlen(token)); /* get either the offset or the length */ TOKENIZE if (!strcmp(token, "SWAP")) { TOKENIZE outinfo->swap[trknum] = 1; } else { outinfo->swap[trknum] = 0; } if (token[0] == '#') { /* it's a decimal offset, use it */ f = strtoul(&token[1], NULL, 10); } else if (isdigit(token[0])) { /* convert the time to an offset */ f = msf_to_frames( token ); f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize); } else { f = 0; } outinfo->offset[trknum] = f; TOKENIZE if (isdigit(token[0])) { // this could be the length or an offset from the previous field. f = msf_to_frames( token ); TOKENIZE if (isdigit(token[0])) { // it was an offset. f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize); outinfo->offset[trknum] += f; // this is the length. f = msf_to_frames( token ); } } else if( trknum == 0 && outinfo->offset[trknum] != 0 ) { /* the 1st track might have a length with no offset */ f = outinfo->offset[trknum] / (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize); outinfo->offset[trknum] = 0; } else { /* guesstimate the track length */ UINT64 tlen; printf("Warning: Estimating length of track %d. If this is not the final or only track\n on the disc, the estimate may be wrong.\n", trknum+1); tlen = get_file_size(outinfo->fname[trknum]) - outinfo->offset[trknum]; tlen /= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize); f = tlen; } outtoc->tracks[trknum].frames = f; }