X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libpcsxcore%2Fcdriso.c;h=549b62f7933a323567bf93f9044f6672193970a5;hb=9532b0f41b808d0155f8d26a4a81553a13593d58;hp=8171674a86d133a402e71d491c76cc0dffa18d82;hpb=4b353f8adc434d4168df88bca58e793becaa891b;p=pcsx_rearmed.git diff --git a/libpcsxcore/cdriso.c b/libpcsxcore/cdriso.c index 8171674a..549b62f7 100644 --- a/libpcsxcore/cdriso.c +++ b/libpcsxcore/cdriso.c @@ -43,6 +43,14 @@ #include #endif +#ifdef USE_LIBRETRO_VFS +#include +#undef fseeko +#undef ftello +#define ftello rftell +#define fseeko rfseek +#endif + #define OFF_T_MSB ((off_t)1 << (sizeof(off_t) * 8 - 1)) unsigned int cdrIsoMultidiskCount; @@ -352,11 +360,12 @@ static int parsetoc(const char *isofile) { } } // check if it's really a TOC named as a .cue - fgets(linebuf, sizeof(linebuf), fi); - token = strtok(linebuf, " "); - if (token && strncmp(token, "CD", 2) != 0 && strcmp(token, "CATALOG") != 0) { - fclose(fi); - return -1; + if (fgets(linebuf, sizeof(linebuf), fi) != NULL) { + token = strtok(linebuf, " "); + if (token && strncmp(token, "CD", 2) != 0 && strcmp(token, "CATALOG") != 0) { + fclose(fi); + return -1; + } } fseek(fi, 0, SEEK_SET); } @@ -477,7 +486,10 @@ static int parsecue(const char *isofile) { strncpy(cuename, isofile, sizeof(cuename)); cuename[MAXPATHLEN - 1] = '\0'; if (strlen(cuename) >= 4) { - strcpy(cuename + strlen(cuename) - 4, ".cue"); + // If 'isofile' is a '.cd' file, use it as a .cue file + // and don't try to search the additional .cue file + if (strncasecmp(cuename + strlen(cuename) - 4, ".cd", 3) != 0 ) + strcpy(cuename + strlen(cuename) - 4, ".cue"); } else { return -1; @@ -604,9 +616,9 @@ static int parsecue(const char *isofile) { file_len = ftell(ti[numtracks + 1].handle) / 2352; if (numtracks == 0 && strlen(isofile) >= 4 && - strcmp(isofile + strlen(isofile) - 4, ".cue") == 0) - { - // user selected .cue as image file, use it's data track instead + (strcmp(isofile + strlen(isofile) - 4, ".cue") == 0 || + strncasecmp(isofile + strlen(isofile) - 4, ".cd", 3) == 0)) { + // user selected .cue/.cdX as image file, use it's data track instead fclose(cdHandle); cdHandle = fopen(filepath, "rb"); } @@ -615,6 +627,10 @@ static int parsecue(const char *isofile) { fclose(fi); + // if there are no tracks detected, then it's not a cue file + if (!numtracks) + return -1; + return 0; } @@ -704,7 +720,8 @@ static int parsemds(const char *isofile) { memset(&ti, 0, sizeof(ti)); // check if it's a valid mds file - fread(&i, 1, sizeof(unsigned int), fi); + if (fread(&i, 1, sizeof(i), fi) != sizeof(i)) + goto fail_io; i = SWAP32(i); if (i != 0x4944454D) { // not an valid mds file @@ -714,19 +731,22 @@ static int parsemds(const char *isofile) { // get offset to session block fseek(fi, 0x50, SEEK_SET); - fread(&offset, 1, sizeof(unsigned int), fi); + if (fread(&offset, 1, sizeof(offset), fi) != sizeof(offset)) + goto fail_io; offset = SWAP32(offset); // get total number of tracks offset += 14; fseek(fi, offset, SEEK_SET); - fread(&s, 1, sizeof(unsigned short), fi); + if (fread(&s, 1, sizeof(s), fi) != sizeof(s)) + goto fail_io; s = SWAP16(s); numtracks = s; // get offset to track blocks fseek(fi, 4, SEEK_CUR); - fread(&offset, 1, sizeof(unsigned int), fi); + if (fread(&offset, 1, sizeof(offset), fi) != sizeof(offset)) + goto fail_io; offset = SWAP32(offset); // skip lead-in data @@ -755,32 +775,41 @@ static int parsemds(const char *isofile) { ti[i].start[1] = fgetc(fi); ti[i].start[2] = fgetc(fi); - fread(&extra_offset, 1, sizeof(unsigned int), fi); + if (fread(&extra_offset, 1, sizeof(extra_offset), fi) != sizeof(extra_offset)) + goto fail_io; extra_offset = SWAP32(extra_offset); // get track start offset (in .mdf) fseek(fi, offset + 0x28, SEEK_SET); - fread(&l, 1, sizeof(unsigned int), fi); + if (fread(&l, 1, sizeof(l), fi) != sizeof(l)) + goto fail_io; l = SWAP32(l); ti[i].start_offset = l; // get pregap fseek(fi, extra_offset, SEEK_SET); - fread(&l, 1, sizeof(unsigned int), fi); + if (fread(&l, 1, sizeof(l), fi) != sizeof(l)) + goto fail_io; l = SWAP32(l); if (l != 0 && i > 1) pregapOffset = msf2sec(ti[i].start); // get the track length - fread(&l, 1, sizeof(unsigned int), fi); + if (fread(&l, 1, sizeof(l), fi) != sizeof(l)) + goto fail_io; l = SWAP32(l); sec2msf(l, ti[i].length); offset += 0x50; } - fclose(fi); return 0; +fail_io: +#ifndef NDEBUG + SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__); +#endif + fclose(fi); + return -1; } static int handlepbp(const char *isofile) { @@ -831,7 +860,8 @@ static int handlepbp(const char *isofile) { } psisoimg_offs = pbp_hdr.psar_offs; - fread(psar_sig, 1, sizeof(psar_sig), cdHandle); + if (fread(psar_sig, 1, sizeof(psar_sig), cdHandle) != sizeof(psar_sig)) + goto fail_io; psar_sig[10] = 0; if (strcmp(psar_sig, "PSTITLEIMG") == 0) { // multidisk image? @@ -867,7 +897,8 @@ static int handlepbp(const char *isofile) { goto fail_io; } - fread(psar_sig, 1, sizeof(psar_sig), cdHandle); + if (fread(psar_sig, 1, sizeof(psar_sig), cdHandle) != sizeof(psar_sig)) + goto fail_io; psar_sig[10] = 0; } @@ -885,15 +916,18 @@ static int handlepbp(const char *isofile) { // first 3 entries are special fseek(cdHandle, sizeof(toc_entry), SEEK_CUR); - fread(&toc_entry, 1, sizeof(toc_entry), cdHandle); + if (fread(&toc_entry, 1, sizeof(toc_entry), cdHandle) != sizeof(toc_entry)) + goto fail_io; numtracks = btoi(toc_entry.index1[0]); - fread(&toc_entry, 1, sizeof(toc_entry), cdHandle); + if (fread(&toc_entry, 1, sizeof(toc_entry), cdHandle) != sizeof(toc_entry)) + goto fail_io; cd_length = btoi(toc_entry.index1[0]) * 60 * 75 + btoi(toc_entry.index1[1]) * 75 + btoi(toc_entry.index1[2]); for (i = 1; i <= numtracks; i++) { - fread(&toc_entry, 1, sizeof(toc_entry), cdHandle); + if (fread(&toc_entry, 1, sizeof(toc_entry), cdHandle) != sizeof(toc_entry)) + goto fail_io; ti[i].type = (toc_entry.type == 1) ? CDDA : DATA; @@ -951,7 +985,14 @@ static int handlepbp(const char *isofile) { fail_index: free(compr_img->index_table); compr_img->index_table = NULL; + goto done; + fail_io: +#ifndef NDEBUG + SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__); +#endif + +done: if (compr_img != NULL) { free(compr_img); compr_img = NULL; @@ -1050,7 +1091,10 @@ static int handlechd(const char *isofile) { goto fail_io; if(chd_open(isofile, CHD_OPEN_READ, NULL, &chd_img->chd) != CHDERR_NONE) - goto fail_io; + goto fail_io; + + if (Config.CHD_Precache && (chd_precache(chd_img->chd) != CHDERR_NONE)) + goto fail_io; chd_img->header = chd_get_header(chd_img->chd); @@ -1064,7 +1108,8 @@ static int handlechd(const char *isofile) { cddaBigEndian = TRUE; numtracks = 0; - int frame_offset = 150; + int frame_offset = 0; + int file_offset = 0; memset(ti, 0, sizeof(ti)); while (1) @@ -1089,18 +1134,20 @@ static int handlechd(const char *isofile) { else break; - ti[md.track].type = !strncmp(md.type, "AUDIO", 5) ? CDDA : DATA; + if(md.track == 1) + md.pregap = 150; + else + sec2msf(msf2sec(ti[md.track-1].length) + md.pregap, ti[md.track-1].length); - sec2msf(frame_offset + md.pregap, ti[md.track].start); - sec2msf(md.frames - md.pregap, ti[md.track].length); + ti[md.track].type = !strncmp(md.type, "AUDIO", 5) ? CDDA : DATA; - if (!strcmp(md.type, md.pgtype)) - frame_offset += md.pregap; + sec2msf(frame_offset + md.pregap, ti[md.track].start); + sec2msf(md.frames, ti[md.track].length); - ti[md.track].start_offset = frame_offset * CD_FRAMESIZE_RAW; + ti[md.track].start_offset = file_offset; - frame_offset += md.frames; - frame_offset += md.postgap; + frame_offset += md.pregap + md.frames + md.postgap; + file_offset += md.frames + md.postgap; numtracks++; } @@ -1163,6 +1210,199 @@ static int opensbifile(const char *isoname) { return LoadSBI(sbiname, s); } +#ifdef _WIN32 +static void readThreadStop() {} +static void readThreadStart() {} +#else +static pthread_t read_thread_id; + +static pthread_cond_t read_thread_msg_avail; +static pthread_cond_t read_thread_msg_done; +static pthread_mutex_t read_thread_msg_lock; + +static pthread_cond_t sectorbuffer_cond; +static pthread_mutex_t sectorbuffer_lock; + +static boolean read_thread_running = FALSE; +static int read_thread_sector_start = -1; +static int read_thread_sector_end = -1; + +typedef struct { + int sector; + long ret; + unsigned char data[CD_FRAMESIZE_RAW]; +} SectorBufferEntry; + +#define SECTOR_BUFFER_SIZE 4096 + +static SectorBufferEntry *sectorbuffer; +static size_t sectorbuffer_index; + +int (*sync_cdimg_read_func)(FILE *f, unsigned int base, void *dest, int sector); +unsigned char *(*sync_CDR_getBuffer)(void); + +static unsigned char * CALLBACK ISOgetBuffer_async(void); +static int cdread_async(FILE *f, unsigned int base, void *dest, int sector); + +static void *readThreadMain(void *param) { + int max_sector = -1; + int requested_sector_start = -1; + int requested_sector_end = -1; + int last_read_sector = -1; + int index = 0; + + int ra_sector = -1; + int max_ra = 128; + int initial_ra = 1; + int speedmult_ra = 4; + + int ra_count = 0; + int how_far_ahead = 0; + + unsigned char tmpdata[CD_FRAMESIZE_RAW]; + long ret; + + max_sector = msf2sec(ti[numtracks].start) + msf2sec(ti[numtracks].length); + + while(1) { + pthread_mutex_lock(&read_thread_msg_lock); + + // If we don't have readahead and we don't have a sector request, wait for one. + // If we still have readahead to go, don't block, just keep going. + // And if we ever have a sector request pending, acknowledge and reset it. + + if (!ra_count) { + if (read_thread_sector_start == -1 && read_thread_running) { + pthread_cond_wait(&read_thread_msg_avail, &read_thread_msg_lock); + } + } + + if (read_thread_sector_start != -1) { + requested_sector_start = read_thread_sector_start; + requested_sector_end = read_thread_sector_end; + read_thread_sector_start = -1; + read_thread_sector_end = -1; + pthread_cond_signal(&read_thread_msg_done); + } + + pthread_mutex_unlock(&read_thread_msg_lock); + + if (!read_thread_running) + break; + + // Readahead code, based on the implementation in mednafen psx's cdromif.cpp + if (requested_sector_start != -1) { + if (last_read_sector != -1 && last_read_sector == (requested_sector_start - 1)) { + how_far_ahead = ra_sector - requested_sector_end; + + if(how_far_ahead <= max_ra) + ra_count = (max_ra - how_far_ahead + 1 ? max_ra - how_far_ahead + 1 : speedmult_ra); + else + ra_count++; + } else if (requested_sector_end != last_read_sector) { + ra_sector = requested_sector_end; + ra_count = initial_ra; + } + + last_read_sector = requested_sector_end; + } + + index = ra_sector % SECTOR_BUFFER_SIZE; + + // check for end of CD + if (ra_count && ra_sector >= max_sector) { + ra_count = 0; + pthread_mutex_lock(§orbuffer_lock); + sectorbuffer[index].ret = -1; + sectorbuffer[index].sector = ra_sector; + pthread_cond_signal(§orbuffer_cond); + pthread_mutex_unlock(§orbuffer_lock); + } + + if (ra_count) { + pthread_mutex_lock(§orbuffer_lock); + if (sectorbuffer[index].sector != ra_sector) { + pthread_mutex_unlock(§orbuffer_lock); + + ret = sync_cdimg_read_func(cdHandle, 0, tmpdata, ra_sector); + + pthread_mutex_lock(§orbuffer_lock); + sectorbuffer[index].ret = ret; + sectorbuffer[index].sector = ra_sector; + memcpy(sectorbuffer[index].data, tmpdata, CD_FRAMESIZE_RAW); + } + pthread_cond_signal(§orbuffer_cond); + pthread_mutex_unlock(§orbuffer_lock); + + ra_sector++; + ra_count--; + } + } + + return NULL; +} + +static void readThreadStop() { + if (read_thread_running == TRUE) { + read_thread_running = FALSE; + pthread_cond_signal(&read_thread_msg_avail); + pthread_join(read_thread_id, NULL); + } + + pthread_cond_destroy(&read_thread_msg_done); + pthread_cond_destroy(&read_thread_msg_avail); + pthread_mutex_destroy(&read_thread_msg_lock); + + pthread_cond_destroy(§orbuffer_cond); + pthread_mutex_destroy(§orbuffer_lock); + + CDR_getBuffer = sync_CDR_getBuffer; + cdimg_read_func = sync_cdimg_read_func; + + free(sectorbuffer); + sectorbuffer = NULL; +} + +static void readThreadStart() { + SysPrintf("Starting async CD thread\n"); + + if (read_thread_running == TRUE) + return; + + read_thread_running = TRUE; + read_thread_sector_start = -1; + read_thread_sector_end = -1; + sectorbuffer_index = 0; + + sectorbuffer = calloc(SECTOR_BUFFER_SIZE, sizeof(SectorBufferEntry)); + if(!sectorbuffer) + goto error; + + sectorbuffer[0].sector = -1; // Otherwise we might think we've already fetched sector 0! + + sync_CDR_getBuffer = CDR_getBuffer; + CDR_getBuffer = ISOgetBuffer_async; + sync_cdimg_read_func = cdimg_read_func; + cdimg_read_func = cdread_async; + + if (pthread_cond_init(&read_thread_msg_avail, NULL) || + pthread_cond_init(&read_thread_msg_done, NULL) || + pthread_mutex_init(&read_thread_msg_lock, NULL) || + pthread_cond_init(§orbuffer_cond, NULL) || + pthread_mutex_init(§orbuffer_lock, NULL) || + pthread_create(&read_thread_id, NULL, readThreadMain, NULL)) + goto error; + + return; + + error: + SysPrintf("Error starting async CD thread\n"); + SysPrintf("Falling back to sync\n"); + + readThreadStop(); +} +#endif + static int cdread_normal(FILE *f, unsigned int base, void *dest, int sector) { fseek(f, base + sector * CD_FRAMESIZE_RAW, SEEK_SET); @@ -1175,10 +1415,18 @@ static int cdread_sub_mixed(FILE *f, unsigned int base, void *dest, int sector) fseek(f, base + sector * (CD_FRAMESIZE_RAW + SUB_FRAMESIZE), SEEK_SET); ret = fread(dest, 1, CD_FRAMESIZE_RAW, f); - fread(subbuffer, 1, SUB_FRAMESIZE, f); + if (fread(subbuffer, 1, SUB_FRAMESIZE, f) != SUB_FRAMESIZE) + goto fail_io; if (subChanRaw) DecodeRawSubData(); + goto done; + +fail_io: +#ifndef NDEBUG + SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__); +#endif +done: return ret; } @@ -1289,19 +1537,17 @@ static int cdread_chd(FILE *f, unsigned int base, void *dest, int sector) int hunk; if (base) - sector += base / CD_FRAMESIZE_RAW; + sector += base; hunk = sector / chd_img->sectors_per_hunk; chd_img->sector_in_hunk = sector % chd_img->sectors_per_hunk; - if (hunk == chd_img->current_hunk) - goto finish; - - chd_read(chd_img->chd, hunk, chd_img->buffer); - - chd_img->current_hunk = hunk; + if (hunk != chd_img->current_hunk) + { + chd_read(chd_img->chd, hunk, chd_img->buffer); + chd_img->current_hunk = hunk; + } -finish: if (dest != cdbuffer) // copy avoid HACK memcpy(dest, chd_img->buffer[chd_img->sector_in_hunk], CD_FRAMESIZE_RAW); @@ -1323,6 +1569,54 @@ static int cdread_2048(FILE *f, unsigned int base, void *dest, int sector) return ret; } +#ifndef _WIN32 + +static int cdread_async(FILE *f, unsigned int base, void *dest, int sector) { + boolean found = FALSE; + int i = sector % SECTOR_BUFFER_SIZE; + long ret; + + if (f != cdHandle || base != 0 || dest != cdbuffer) { + // Async reads are only supported for cdbuffer, so call the sync + // function directly. + return sync_cdimg_read_func(f, base, dest, sector); + } + + pthread_mutex_lock(&read_thread_msg_lock); + + // Only wait if we're not trying to read the next sector and + // sector_start is set (meaning the last request hasn't been + // processed yet) + while(read_thread_sector_start != -1 && read_thread_sector_end + 1 != sector) { + pthread_cond_wait(&read_thread_msg_done, &read_thread_msg_lock); + } + + if (read_thread_sector_start == -1) + read_thread_sector_start = sector; + + read_thread_sector_end = sector; + pthread_cond_signal(&read_thread_msg_avail); + pthread_mutex_unlock(&read_thread_msg_lock); + + do { + pthread_mutex_lock(§orbuffer_lock); + if (sectorbuffer[i].sector == sector) { + sectorbuffer_index = i; + ret = sectorbuffer[i].ret; + found = TRUE; + } + + if (!found) { + pthread_cond_wait(§orbuffer_cond, §orbuffer_lock); + } + pthread_mutex_unlock(§orbuffer_lock); + } while (!found); + + return ret; +} + +#endif + static unsigned char * CALLBACK ISOgetBuffer_compr(void) { return compr_img->buff_raw[compr_img->sector_in_blk] + 12; } @@ -1333,6 +1627,17 @@ static unsigned char * CALLBACK ISOgetBuffer_chd(void) { } #endif +#ifndef _WIN32 +static unsigned char * CALLBACK ISOgetBuffer_async(void) { + unsigned char *buffer; + pthread_mutex_lock(§orbuffer_lock); + buffer = sectorbuffer[sectorbuffer_index].data; + pthread_mutex_unlock(§orbuffer_lock); + return buffer + 12; +} + +#endif + static unsigned char * CALLBACK ISOgetBuffer(void) { return cdbuffer + 12; } @@ -1449,7 +1754,12 @@ static long CALLBACK ISOopen(void) { if (ftello(cdHandle) % 2048 == 0) { unsigned int modeTest = 0; fseek(cdHandle, 0, SEEK_SET); - fread(&modeTest, 4, 1, cdHandle); + if (!fread(&modeTest, sizeof(modeTest), 1, cdHandle)) { +#ifndef NDEBUG + SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__); +#endif + return -1; + } if (SWAP32(modeTest) != 0xffffff00) { strcat(image_str, "[2048]"); isMode1ISO = TRUE; @@ -1473,6 +1783,9 @@ static long CALLBACK ISOopen(void) { cdda_cur_sector = 0; cdda_file_offset = 0; + if (Config.AsyncCD) { + readThreadStart(); + } return 0; } @@ -1518,6 +1831,10 @@ static long CALLBACK ISOclose(void) { memset(cdbuffer, 0, sizeof(cdbuffer)); CDR_getBuffer = ISOgetBuffer; + if (Config.AsyncCD) { + readThreadStop(); + } + return 0; } @@ -1598,12 +1915,12 @@ static void DecodeRawSubData(void) { // read track // time: byte 0 - minute; byte 1 - second; byte 2 - frame // uses bcd format -static long CALLBACK ISOreadTrack(unsigned char *time) { +static boolean CALLBACK ISOreadTrack(unsigned char *time) { int sector = MSF2SECT(btoi(time[0]), btoi(time[1]), btoi(time[2])); long ret; if (cdHandle == NULL) { - return -1; + return 0; } if (pregapOffset) { @@ -1617,16 +1934,18 @@ static long CALLBACK ISOreadTrack(unsigned char *time) { ret = cdimg_read_func(cdHandle, 0, cdbuffer, sector); if (ret < 0) - return -1; + return 0; if (subHandle != NULL) { fseek(subHandle, sector * SUB_FRAMESIZE, SEEK_SET); - fread(subbuffer, 1, SUB_FRAMESIZE, subHandle); + if (fread(subbuffer, 1, SUB_FRAMESIZE, subHandle) != SUB_FRAMESIZE) + /* Faulty subchannel data shouldn't cause a read failure */ + return 0; if (subChanRaw) DecodeRawSubData(); } - return 0; + return 1; } // plays cdda audio