ECM file support: mature enough so it can be now integrated with other platforms aswell.

git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@87188 e17a0e51-4ae3-4d35-97c3-1a29b211df97
This commit is contained in:
SND\ckain_cp 2013-09-16 09:54:43 +00:00
parent de50d760fd
commit 35447c717d
4 changed files with 92 additions and 56 deletions

View File

@ -691,12 +691,8 @@ static gchar *Open_Iso_Proc() {
gtk_file_filter_add_pattern(psxfilter, "*.CUE");
gtk_file_filter_add_pattern(psxfilter, "*.PBP");
gtk_file_filter_add_pattern(psxfilter, "*.CBN");
#ifdef ENABLE_CCDDA
gtk_file_filter_add_pattern(psxfilter, "*.ecm");
gtk_file_filter_set_name(psxfilter, _("PSX Image Files (*.bin, *.img, *.mdf, *.iso, *.ecm, *.cue, *.pbp, *.cbn)"));
#else
gtk_file_filter_set_name(psxfilter, _("PSX Image Files (*.bin, *.img, *.mdf, *.iso, *.cue, *.pbp, *.cbn)"));
#endif
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (chooser), psxfilter);
allfilter = gtk_file_filter_new();

View File

@ -24,6 +24,7 @@
#include "cdrom.h"
#include "cdriso.h"
#include "ppf.h"
#include "ecm.h"
#ifdef _WIN32
#include <process.h>
@ -40,8 +41,6 @@
#include "libavutil/mathematics.h"
#include "libavformat/avformat.h"
#include "ecm.h"
#define INBUF_SIZE 4096
#define AUDIO_INBUF_SIZE INBUF_SIZE*4
#define AUDIO_REFILL_THRESH 4096
@ -639,6 +638,7 @@ static int parsecue(const char *isofile) {
int seconds = get_compressed_cdda_track_length(filepath) + 0;
const boolean lazy_decode = TRUE; // TODO: config param
// TODO: get frame length for compressed audio as well
ti[numtracks].len_decoded_buffer = 44100 * (16/8) * 2 * seconds;
strcpy(ti[numtracks].filepath, filepath);
file_len = ti[numtracks].len_decoded_buffer/CD_FRAMESIZE_RAW;
@ -651,11 +651,14 @@ static int parsecue(const char *isofile) {
}
}
}
else if (sscanf(linebuf, " TRACK %u MODE%u/%u", &t, &mode, &sector_size) == 3)
// TODO: here detect if ECM and calculate real length
// TODO: also if 2048 frame length -> recalculate?
else if (sscanf(linebuf, " TRACK %u MODE%u/%u", &t, &mode, &sector_size) == 3) {
// TODO: if 2048 frame length -> recalculate file_len?
ti[numtracks].type = DATA;
else {
s32 accurate_len;
if (handleecm(filepath, &accurate_len) == 0) {// detect if ECM & get accurate length
file_len = accurate_len;
}
} else {
SysPrintf(".cue: failed to parse TRACK\n");
ti[numtracks].type = numtracks == 1 ? DATA : CDDA;
}
@ -1330,17 +1333,15 @@ static int cdread_2048(FILE *f, unsigned int base, void *dest, int sector)
return ret;
}
#ifdef ENABLE_CCDDA // TODO: experimental... test reliability & possibly move these functions to ecm.h
/* Adapted from ecm.c:unecmify() (C) Neill Corlett */
//TODO: move this func to ecm.h
static int cdread_ecm_decode(FILE *f, unsigned int base, void *dest, int sector) {
u32 output_edc = 0, b, writebytecount=0, sectorcount=0, num;
u32 output_edc=0, b, writebytecount=0, num;
s32 sectorcount=0;
s8 type; // mode type 0 (META) or 1, 2 or 3 for CDROM type
u8 sector_buffer[CD_FRAMESIZE_RAW];
boolean processsectors = (boolean)decoded_ecm_sectors; // this flag tells if to decode all sectors or just skip to wanted sector
ECMFILELUT* pos = &(ecm_savetable[sector-0]); // get sector from LUT which points to wanted sector or to beginning
// if suitable sector was not found from LUT use last sector if less than wanted sector
if (pos->filepos <= ECM_HEADER_SIZE && sector > lastsector) pos=&(ecm_savetable[lastsector]);
ECMFILELUT* pos = &(ecm_savetable[0]); // points always to beginning of ECM DATA
// If not pointing to ECM file but CDDA file or some other track
if(f != cdHandle) {
@ -1357,7 +1358,13 @@ static int cdread_ecm_decode(FILE *f, unsigned int base, void *dest, int sector)
SysPrintf("ECM: invalid sector requested\n");
return -1;
}*/
//printf("SeekSector %i %i %i %i\n", sector, pos->sector, lastsector, base);
//printf("SeekSector %i %i %i %i\n", sector, pos->sector, prevsector, base);
if (sector <= len_ecm_savetable) {
pos = &(ecm_savetable[sector-0]); // get sector from LUT which points to wanted sector or to beginning
// if suitable sector was not found from LUT use last sector if less than wanted sector
if (pos->filepos <= ECM_HEADER_SIZE && sector > prevsector) pos=&(ecm_savetable[prevsector]);
}
writebytecount = pos->sector * CD_FRAMESIZE_RAW;
sectorcount = pos->sector;
@ -1385,6 +1392,8 @@ static int cdread_ecm_decode(FILE *f, unsigned int base, void *dest, int sector)
}
if(num == 0xFFFFFFFF) {
// End indicator
len_decoded_ecm_buffer = writebytecount;
len_ecm_savetable = len_decoded_ecm_buffer/CD_FRAMESIZE_RAW;
break;
}
num++;
@ -1458,7 +1467,7 @@ static int cdread_ecm_decode(FILE *f, unsigned int base, void *dest, int sector)
}
memcpy(dest, sector_buffer, CD_FRAMESIZE_RAW);
lastsector = sectorcount;
prevsector = sectorcount;
//printf("OK: Frame decoded %i %i\n", sectorcount-1, writebytecount);
return num;
@ -1470,10 +1479,8 @@ error_out:
sector, type, base, sectorcount, pos->sector, writebytecount, ftell(f));
return -1;
}
#endif
static int handleecm(const char *isoname) {
#ifdef ENABLE_CCDDA
int handleecm(const char *isoname, s32* accurate_length) {
// Rewind to start and check ECM header and filename suffix validity
fseek(cdHandle, 0, SEEK_SET);
if(
@ -1483,18 +1490,44 @@ static int handleecm(const char *isoname) {
(fgetc(cdHandle) == 0x00) &&
(strncmp((isoname+strlen(isoname)-5), ".ecm", 4))
) {
SysPrintf(_("\nDetected ECM file with proper header and filename suffix.\n"));
// Function used to read CD normally
// TODO: detect if 2048 and use it
cdimg_read_func_o = cdread_normal;
// Save real function used to read CD
cdimg_read_func_o = cdimg_read_func;
// Function used to decode ECM data
cdimg_read_func = cdread_ecm_decode;
// Based in file length use LUT of variable size
fseek(cdHandle, 0, SEEK_END);
//len_ecm_savetable = savedsectors ? len_ecm_savetable : (ftell(cdHandle)/CD_FRAMESIZE_RAW)/100;
len_ecm_savetable = 2*(ftell(cdHandle)/CD_FRAMESIZE_RAW); // todo: optimize size...
// Last accessed sector
prevsector = 0;
// Full image decoded??
// Already analyzed during this session, use cached results
if (ecm_file_detected) {
if (accurate_length) *accurate_length = len_ecm_savetable;
return 0;
}
SysPrintf(_("\nDetected ECM file with proper header and filename suffix.\n"));
// Init ECC/EDC tables
eccedc_init();
// Default: use LUT of double the ECM image size
fseek(cdHandle, 0, SEEK_END);
len_ecm_savetable = 2*(ftell(cdHandle)/CD_FRAMESIZE_RAW);
// Index 0 always points to beginning of ECM data
ecm_savetable = calloc(len_ecm_savetable, sizeof(ECMFILELUT));
ecm_savetable[0].filepos = ECM_HEADER_SIZE;
if (accurate_length || decoded_ecm_sectors) {
u8 tbuf1[CD_FRAMESIZE_RAW];
len_ecm_savetable = 0;
cdread_ecm_decode(cdHandle, 0U, tbuf1, INT_MAX);
if (accurate_length)*accurate_length = len_ecm_savetable;
}
// Full image decoded? Needs fmemopen()
#ifdef ENABLE_ECM_FULL
if (decoded_ecm_sectors) {
len_decoded_ecm_buffer = len_ecm_savetable*CD_FRAMESIZE_RAW;
decoded_ecm_buffer = malloc(len_decoded_ecm_buffer);
@ -1507,23 +1540,15 @@ static int handleecm(const char *isoname) {
decoded_ecm_sectors = 0;
}
}
#endif
// Init ECC/EDC tables
eccedc_init();
ecm_file_detected = TRUE;
// Last accessed sector
lastsector = 0;
// Index 0 always points to beginning of ECM data
ecm_savetable = calloc(len_ecm_savetable, sizeof(ECMFILELUT));
ecm_savetable[0].filepos = ECM_HEADER_SIZE;
return 0;
}
#endif
return 1;
return -1;
}
static unsigned char * CALLBACK ISOgetBuffer_compr(void) {
return compr_img->buff_raw[compr_img->sector_in_blk] + 12;
}
@ -1599,7 +1624,12 @@ static long CALLBACK ISOopen(void) {
SysPrintf("[+sbi]");
}
if ((handleecm(GetIsoFile(), NULL) == 0)) {
SysPrintf("[+ecm]");
}
// guess whether it is mode1/2048
// TODO: use sector size/mode info from CUE?
fseek(cdHandle, 0, SEEK_END);
if (ftell(cdHandle) % 2048 == 0) {
unsigned int modeTest = 0;
@ -1621,10 +1651,6 @@ static long CALLBACK ISOopen(void) {
else if (isMode1ISO)
cdimg_read_func = cdread_2048;
if (handleecm(GetIsoFile()) == 0) {
SysPrintf("[+ecm]");
}
// make sure we have another handle open for cdda
if (numtracks > 1 && ti[1].handle == NULL) {
ti[1].handle = fopen(GetIsoFile(), "rb");
@ -1651,12 +1677,6 @@ static long CALLBACK ISOclose(void) {
compr_img = NULL;
}
// ECM LUT
#ifdef ENABLE_CCDDA
free(ecm_savetable);
ecm_savetable = NULL;
#endif
for (i = 1; i <= numtracks; i++) {
if (ti[i].handle != NULL) {
fclose(ti[i].handle);
@ -1679,6 +1699,9 @@ static long CALLBACK ISOclose(void) {
long CALLBACK ISOinit(void) {
assert(cdHandle == NULL);
assert(subHandle == NULL);
assert(ecm_file_detected == FALSE);
assert(decoded_ecm_buffer == NULL);
assert(decoded_ecm == NULL);
return 0; // do nothing
}
@ -1686,14 +1709,18 @@ long CALLBACK ISOinit(void) {
static long CALLBACK ISOshutdown(void) {
ISOclose();
#ifdef ENABLE_CCDDA
// ECM LUT
free(ecm_savetable);
ecm_savetable = NULL;
if (decoded_ecm != NULL) {
fclose(decoded_ecm);
free(decoded_ecm_buffer);
decoded_ecm_buffer = NULL;
decoded_ecm = NULL;
}
#endif
ecm_file_detected = FALSE;
return 0;
}
@ -1919,5 +1946,5 @@ void cdrIsoInit(void) {
}
int cdrIsoActive(void) {
return (cdHandle != NULL);
return (cdHandle != NULL || ecm_savetable != NULL || decoded_ecm != NULL);
}

View File

@ -25,6 +25,7 @@
extern "C" {
#endif
int handleecm(const char *isoname, s32* accurate_length);
void cdrIsoInit(void);
int cdrIsoActive(void);

View File

@ -68,12 +68,24 @@
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define ECM_HEADER_SIZE 4
u32 len_decoded_ecm_buffer, len_ecm_savetable, lastsector;
u32 decoded_ecm_sectors = 0; // setting this to 1 makes whole ECM to be decoded in-memory
u32 len_decoded_ecm_buffer=0; // same as decoded ECM file length or 2x size
u32 len_ecm_savetable=0; // same as sector count of decoded ECM file or 2x count
#ifdef ENABLE_ECM_FULL //setting this makes whole ECM to be decoded in-memory meaning buffer could eat up to 700 MB of memory
u32 decoded_ecm_sectors=1; // initially sector 1 is always decoded
#else
u32 decoded_ecm_sectors=0; // disabled
#endif
boolean ecm_file_detected = FALSE;
u32 prevsector;
FILE* decoded_ecm = NULL;
void *decoded_ecm_buffer;
int (*cdimg_read_func_o)(FILE *f, unsigned int base, void *dest, int sector);
// Function that is used to read CD normally
int (*cdimg_read_func_o)(FILE *f, unsigned int base, void *dest, int sector) = NULL;
typedef struct ECMFILELUT {
s32 sector;