CDRISO: support for common archive formats (7z, rar, zip, etc) via libarchive. Streams requested sectors on-fly and eventually buffers whole image in-memory. In some optimal cases reading compressed data without any noticable delays, but however usually with long decompression delays.

git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@88838 e17a0e51-4ae3-4d35-97c3-1a29b211df97
This commit is contained in:
SND\ckain_cp 2014-02-04 13:26:02 +00:00
parent 10b7afe174
commit a1d083bf2b
3 changed files with 196 additions and 33 deletions

View File

@ -58,6 +58,7 @@ AC_CHECK_LIB(socket, socket, [LIBS="$LIBS -lsocket"], [])
AC_CHECK_LIB(nsl, gethostbyname, [LIBS="$LIBS -lnsl"], [])
AC_CHECK_LIB(umem, umem_alloc, [LIBS="$LIBS -lumem"], [])
AC_CHECK_LIB(rt, shm_open, [LIBS="$LIBS -lrt"], [AC_DEFINE([NO_RT_SHM], [1], [rt_shm])])
AC_CHECK_LIB(archive, archive_read_open_filename)
AM_CONDITIONAL(SOUND_OSS, false)
AM_CONDITIONAL(SOUND_SDL, false)

View File

@ -61,6 +61,7 @@ static boolean subChanRaw = FALSE;
static boolean subChanMissing = FALSE;
static boolean multifile = FALSE;
static boolean isMode1ISO = FALSE; // TODO: use sector size/mode info from CUE also?
static unsigned char cdbuffer[CD_FRAMESIZE_RAW];
static unsigned char subbuffer[SUB_FRAMESIZE];
@ -656,7 +657,7 @@ static int parsecue(const char *isofile) {
s32 accurate_len;
// TODO: if 2048 frame length -> recalculate file_len?
ti[numtracks].type = DATA;
if (handleecm(filepath, &accurate_len) == 0) {// detect if ECM & get accurate length
if (handleecm(filepath, cdHandle, &accurate_len) == 0) {// detect if ECM & get accurate length
file_len = accurate_len;
}
} else {
@ -1496,14 +1497,14 @@ error_out:
return -1;
}
int handleecm(const char *isoname, s32* accurate_length) {
int handleecm(const char *isoname, FILE* cdh, s32* accurate_length) {
// Rewind to start and check ECM header and filename suffix validity
fseek(cdHandle, 0, SEEK_SET);
fseek(cdh, 0, SEEK_SET);
if(
(fgetc(cdHandle) == 'E') &&
(fgetc(cdHandle) == 'C') &&
(fgetc(cdHandle) == 'M') &&
(fgetc(cdHandle) == 0x00) &&
(fgetc(cdh) == 'E') &&
(fgetc(cdh) == 'C') &&
(fgetc(cdh) == 'M') &&
(fgetc(cdh) == 0x00) &&
(strncmp((isoname+strlen(isoname)-5), ".ecm", 4))
) {
// Function used to read CD normally
@ -1527,18 +1528,17 @@ int handleecm(const char *isoname, s32* accurate_length) {
// 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);
// Reserve maximum known sector ammount for LUT (80MIN CD)
len_ecm_savetable = 75*80*60; //2*(accurate_length/CD_FRAMESIZE_RAW);
// Index 0 always points to beginning of ECM data
ecm_savetable = calloc(len_ecm_savetable, sizeof(ECMFILELUT));
ecm_savetable = calloc(len_ecm_savetable, sizeof(ECMFILELUT)); // calloc returns nulled data
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);
len_ecm_savetable = 0; // indicates to cdread_ecm_decode that no lut has been built yet
cdread_ecm_decode(cdh, 0U, tbuf1, INT_MAX); // builds LUT completely
if (accurate_length)*accurate_length = len_ecm_savetable;
}
@ -1565,6 +1565,133 @@ int handleecm(const char *isoname, s32* accurate_length) {
return -1;
}
int (*cdimg_read_func_archive)(FILE *f, unsigned int base, void *dest, int sector) = NULL;
#ifdef HAVE_LIBARCHIVE
#include <archive.h>
#include <archive_entry.h>
struct archive *a = NULL;
u32 len_uncompressed_buffer = 0;
void *cdimage_buffer_mem = NULL;
FILE* cdimage_buffer = NULL; //cdHandle to store file
int aropen(FILE* fparchive, const char* _fn) {
s32 r;
u64 length = 0, length_peek;
boolean use_temp_file = FALSE; // TODO make a config param
static struct archive_entry *ae=NULL;
struct archive_entry *ae_peek;
if (a == NULL && cdimage_buffer == NULL) {
// We open file twice. First to peek sizes. This nastyness due used interface.
a = archive_read_new();
r = archive_read_support_compression_all(a);
r = archive_read_support_format_all(a);
//r = archive_read_support_filter_all(a);
//r = archive_read_support_format_raw(a);
//r = archive_read_open_FILE(a, archive);
archive_read_open_filename(a, _fn, 75*CD_FRAMESIZE_RAW);
if (r != ARCHIVE_OK) {
SysPrintf("Archive open failed (%i).\n", r);
archive_read_free(a);
a = NULL;
return -1;
}
// Get the biggest file in archive
while ((r=archive_read_next_header(a, &ae_peek)) == ARCHIVE_OK) {
length_peek = archive_entry_size(ae_peek);
//printf("Entry canditate %s %i\n", archive_entry_pathname(ae_peek), length_peek);
length = MAX(length_peek, length);
ae = (ae == NULL ? ae_peek : ae);
}
archive_read_free(a);
if (ae == NULL) {
SysPrintf("Archive entry read failed (%i).\n", r);
a = NULL;
return -1;
}
//Now really open the file
a = archive_read_new();
r = archive_read_support_compression_all(a);
r = archive_read_support_format_all(a);
archive_read_open_filename(a, _fn, 75*CD_FRAMESIZE_RAW);
while ((r=archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
length_peek = archive_entry_size(ae);
if (length_peek == length) {
//ae = ae_peek;
SysPrintf(" -- Selected entry %s %i", archive_entry_pathname(ae), length);
break;
}
}
len_uncompressed_buffer = length?length:700*1024*1024;
}
if (use_temp_file && (cdimage_buffer == NULL || cdHandle != cdimage_buffer)) {
cdimage_buffer = fopen("/tmp/pcsxr.tmp.bin", "w+b");
}
else if (!use_temp_file && (cdimage_buffer == NULL || cdHandle != cdimage_buffer)) {
if (cdimage_buffer_mem == NULL && ((cdimage_buffer_mem = malloc(len_uncompressed_buffer)) == NULL)) {
SysMessage("Could not reserve enough memory for full image buffer.\n");
exit(3);
}
//printf("Memory ok2 %u %p\n", len_uncompressed_buffer, cdimage_buffer_mem);
cdimage_buffer = fmemopen(cdimage_buffer_mem, len_uncompressed_buffer, "w+b");
} else {
}
if (cdHandle != cdimage_buffer) {
fclose(cdHandle); // opened thru archive so this not needed anymore
cdHandle = cdimage_buffer;
}
return 0;
}
static int cdread_archive(FILE *f, unsigned int base, void *dest, int sector)
{
s32 r;
size_t size;
size_t readsize;
static off_t offset = 0; // w/o read always or static/ftell
const void *buff;
// If not pointing to archive file but CDDA file or some other track
if(f != cdHandle) {
return cdimg_read_func_archive(f, base, dest, sector);
}
// Jump if already completely read
if (a != NULL /*&& (ecm_file_detected || sector*CD_FRAMESIZE_RAW <= len_uncompressed_buffer)*/) {
readsize = (sector+1) * CD_FRAMESIZE_RAW;
for (fseek(cdimage_buffer, offset, SEEK_SET); offset < readsize;) {
r = archive_read_data_block(a, &buff, &size, &offset);
offset += size;
SysPrintf("ReadArchive seek:%u(%u) cur:%u(%u)\r", sector, readsize/1024, offset/CD_FRAMESIZE_RAW, offset/1024);
fwrite(buff, size, 1, cdimage_buffer);
if (r != ARCHIVE_OK) {
//SysPrintf("End of archive.\n");
archive_read_free(a);
a = NULL;
readsize = offset;
fflush(cdimage_buffer);
fseek(cdimage_buffer, 0, SEEK_SET);
}
}
} else {
//SysPrintf("ReadSectorArchSector: %u(%u)\n", sector, sector*CD_FRAMESIZE_RAW);
}
// TODO what causes req sector to be greater than CD size?
r = cdimg_read_func_archive(cdimage_buffer, base, dest, sector);
return r;
}
#else
int aropen(FILE* fparchive, const char* _fn) {return -1;}
static int cdread_archive(FILE *f, unsigned int base, void *dest, int sector) {return -1;}
#endif
static unsigned char * CALLBACK ISOgetBuffer_compr(void) {
return compr_img->buff_raw[compr_img->sector_in_blk] + 12;
}
@ -1587,8 +1714,6 @@ static void PrintTracks(void) {
// This function is invoked by the front-end when opening an ISO
// file for playback
static long CALLBACK ISOopen(void) {
boolean isMode1ISO = FALSE;
if (cdHandle != NULL) {
return 0; // it's already open
}
@ -1632,6 +1757,29 @@ static long CALLBACK ISOopen(void) {
CDR_getBuffer = ISOgetBuffer_compr;
cdimg_read_func = cdread_compressed;
}
else if ((handleecm(GetIsoFile(), cdHandle, NULL) == 0)) {
SysPrintf("[+ecm]");
}
#ifdef HAVE_LIBARCHIVE
else if (aropen(cdHandle, GetIsoFile()) == 0) {
cdimg_read_func = cdread_archive;
SysPrintf("[+archive]");
if (!ecm_file_detected) {
#ifndef ENABLE_ECM_FULL
//Detect ECM inside archive
cdimg_read_func_archive = cdread_normal;
cdread_archive(cdHandle, 0, cdbuffer, 16);
if (handleecm("test.ecm", cdimage_buffer, NULL) != -1) {
cdimg_read_func_archive = cdread_ecm_decode;
cdimg_read_func = cdread_archive;
SysPrintf("[+ecm]");
}
#endif
} else {
SysPrintf("[+ecm]");
}
}
#endif
if (!subChanMixed && opensubfile(GetIsoFile()) == 0) {
SysPrintf("[+sub]");
@ -1640,32 +1788,31 @@ 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;
fseek(cdHandle, 0, SEEK_SET);
fread(&modeTest, 4, 1, cdHandle);
if (SWAP32(modeTest) != 0xffffff00) {
SysPrintf("[2048]");
isMode1ISO = TRUE;
if (!ecm_file_detected) {
// guess whether it is mode1/2048
fseek(cdHandle, 0, SEEK_END);
if (ftell(cdHandle) % 2048 == 0) {
unsigned int modeTest = 0;
fseek(cdHandle, 0, SEEK_SET);
fread(&modeTest, 4, 1, cdHandle);
if (SWAP32(modeTest) != 0xffffff00) {
SysPrintf("[2048]");
isMode1ISO = TRUE;
}
}
fseek(cdHandle, 0, SEEK_SET);
}
fseek(cdHandle, 0, SEEK_SET);
SysPrintf(".\n");
PrintTracks();
if (subChanMixed)
if (subChanMixed && (cdimg_read_func == cdread_normal))
cdimg_read_func = cdread_sub_mixed;
else if (isMode1ISO)
else if (isMode1ISO && (cdimg_read_func == cdread_normal))
cdimg_read_func = cdread_2048;
else if (isMode1ISO && (cdimg_read_func_archive == cdread_normal))
cdimg_read_func_archive = cdread_2048;
// make sure we have another handle open for cdda
if (numtracks > 1 && ti[1].handle == NULL) {
@ -1681,6 +1828,7 @@ static long CALLBACK ISOclose(void) {
if (cdHandle != NULL) {
fclose(cdHandle);
cdHandle = NULL;
//cdimage_buffer = NULL;
}
if (subHandle != NULL) {
fclose(subHandle);
@ -1737,6 +1885,19 @@ static long CALLBACK ISOshutdown(void) {
}
ecm_file_detected = FALSE;
#ifdef HAVE_LIBARCHIVE
if (cdimage_buffer != NULL) {
//fclose(cdimage_buffer);
free(cdimage_buffer_mem);
cdimage_buffer_mem = NULL;
cdimage_buffer = NULL;
if (a) {
archive_read_free(a);
a = NULL;
}
}
#endif
return 0;
}

View File

@ -25,7 +25,8 @@
extern "C" {
#endif
int handleecm(const char *isoname, s32* accurate_length);
int handleecm(const char *isoname, FILE* cdh, s32* accurate_length);
int aropen(FILE* fparchive, const char* _fn);
void cdrIsoInit(void);
int cdrIsoActive(void);