diff options
| author | SND\ckain_cp <SND\ckain_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2013-09-12 15:51:34 +0000 |
|---|---|---|
| committer | SND\ckain_cp <SND\ckain_cp@e17a0e51-4ae3-4d35-97c3-1a29b211df97> | 2013-09-12 15:51:34 +0000 |
| commit | 59132f7e4e49b3d0147c39632d3b3fa6cf8854c7 (patch) | |
| tree | fc80a43d71354f75ce4c49b711f21eeb0f7b9539 | |
| parent | 8bdccd853ab222efddf139bc53cd19cd96c79168 (diff) | |
| download | pcsxr-59132f7e4e49b3d0147c39632d3b3fa6cf8854c7.tar.gz | |
Experimental on-fly decoding of ECM file format. For now compiles also with --enable-ccdda
git-svn-id: https://pcsxr.svn.codeplex.com/svn/pcsxr@87140 e17a0e51-4ae3-4d35-97c3-1a29b211df97
| -rwxr-xr-x | libpcsxcore/cdriso.c | 207 | ||||
| -rw-r--r-- | libpcsxcore/ecm.h | 278 |
2 files changed, 485 insertions, 0 deletions
diff --git a/libpcsxcore/cdriso.c b/libpcsxcore/cdriso.c index 6548e787..1008c652 100755 --- a/libpcsxcore/cdriso.c +++ b/libpcsxcore/cdriso.c @@ -40,6 +40,8 @@ #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 @@ -1325,6 +1327,192 @@ 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 */ +static int cdread_ecm_decode(FILE *f, unsigned int base, void *dest, int sector) { + u32 output_edc = 0, b, writebytecount=0, sectorcount=0, num; + 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]); + + if (decoded_ecm_sectors && sector < decoded_ecm_sectors) { + //printf("ReadSector %i %i\n", sector, savedsectors); + return cdimg_read_func_o(decoded_ecm, base, dest, sector); + }/* else if (sector > len_ecm_savetable) { + SysPrintf("ECM: invalid sector requested\n"); + return -1; + }*/ + //printf("SeekSector %i %i %i\n", sector, pos->sector, lastsector); + + writebytecount = pos->sector * CD_FRAMESIZE_RAW; + sectorcount = pos->sector; + if (decoded_ecm_sectors) fseek(decoded_ecm, writebytecount, SEEK_SET); // rewind to last pos + fseek(f, base+pos->filepos, SEEK_SET); + while(sector >= sectorcount) { // decode ecm file until we are past wanted sector + int c = fgetc(f); + int bits = 5; + if(c == EOF) { goto error_in; } + type = c & 3; + num = (c >> 2) & 0x1F; + //printf("ECM1 file; count %x\n", c); + while(c & 0x80) { + c = fgetc(f); + //printf("ECM2 file; count %x\n", c); + if(c == EOF) { goto error_in; } + if( (bits > 31) || + ((uint32_t)(c & 0x7F)) >= (((uint32_t)0x80000000LU) >> (bits-1)) + ) { + //SysMessage(_("Corrupt ECM file; invalid sector count\n")); + goto error; + } + num |= ((uint32_t)(c & 0x7F)) << bits; + bits += 7; + } + if(num == 0xFFFFFFFF) { + // End indicator + break; + } + num++; + while(num) { + if (!processsectors && sectorcount >= (sector-1)) { // ensure that we read the sector we are supposed to + processsectors = TRUE; + //printf("Saving at %i\n", sectorcount); + } + /*printf("Type %i Num %i SeekSector %i ProcessedSectors %i(%i) Bytecount %i Pos %li Write %u\n", + type, num, sector, sectorcount, pos->sector, writebytecount, ftell(f), processsectors);*/ + switch(type) { + case 0: // META + b = num; + if(b > sizeof(sector_buffer)) { b = sizeof(sector_buffer); } + writebytecount += b; + if (!processsectors) { fseek(f, +b, SEEK_CUR); break; } // seek only + if(fread(sector_buffer, 1, b, f) != b) { + goto error_in; + } + //output_edc = edc_compute(output_edc, sector_buffer, b); + if(decoded_ecm_sectors && fwrite(sector_buffer, 1, b, decoded_ecm) != b) { // just seek or write also + goto error_out; + } + break; + case 1: //Mode 1 + b=1; + writebytecount += ECM_SECTOR_SIZE[type]; + if(fread(sector_buffer + 0x00C, 1, 0x003, f) != 0x003) { goto error_in; } + if(fread(sector_buffer + 0x010, 1, 0x800, f) != 0x800) { goto error_in; } + if (!processsectors) break; // seek only + reconstruct_sector(sector_buffer, 1); + //output_edc = edc_compute(output_edc, sector_buffer, ECM_SECTOR_SIZE[type]); + if(decoded_ecm_sectors && fwrite(sector_buffer, 1, ECM_SECTOR_SIZE[type], decoded_ecm) != ECM_SECTOR_SIZE[type]) { goto error_out; } + break; + case 2: //Mode 2 (XA), form 1 + b=1; + writebytecount += ECM_SECTOR_SIZE[type]; + if (!processsectors) { fseek(f, +0x804, SEEK_CUR); break; } // seek only + if(fread(sector_buffer + 0x014, 1, 0x804, f) != 0x804) { goto error_in; } + reconstruct_sector(sector_buffer, 2); + //output_edc = edc_compute(output_edc, sector_buffer + 0x10, ECM_SECTOR_SIZE[type]); + if(decoded_ecm_sectors && fwrite(sector_buffer + 0x10, 1, ECM_SECTOR_SIZE[type], decoded_ecm) != ECM_SECTOR_SIZE[type]) { goto error_out; } + break; + case 3: //Mode 2 (XA), form 2 + b=1; + writebytecount += ECM_SECTOR_SIZE[type]; + if (!processsectors) { fseek(f, +0x918, SEEK_CUR); break; } // seek only + if(fread(sector_buffer + 0x014, 1, 0x918, f) != 0x918) { goto error_in; } + reconstruct_sector(sector_buffer, 3); + //output_edc = edc_compute(output_edc, sector_buffer + 0x10, ECM_SECTOR_SIZE[type]); + if(decoded_ecm_sectors && fwrite(sector_buffer + 0x10, 1, ECM_SECTOR_SIZE[type], decoded_ecm) != ECM_SECTOR_SIZE[type]) { goto error_out; } + break; + } + sectorcount=((writebytecount/CD_FRAMESIZE_RAW) - 0); + num -= b; + } + if (sectorcount > 0 && ecm_savetable[sectorcount].filepos <= ECM_HEADER_SIZE ) { + ecm_savetable[sectorcount].filepos = ftell(f)-base; + ecm_savetable[sectorcount].sector = sectorcount; + //printf("Marked %i at pos %i\n", ecm_savetable[sectorcount].sector, ecm_savetable[sectorcount].filepos); + } + } + + if (decoded_ecm_sectors) { + fflush(decoded_ecm); + fseek(decoded_ecm, -1*CD_FRAMESIZE_RAW, SEEK_CUR); + num = fread(sector_buffer, 1, CD_FRAMESIZE_RAW, decoded_ecm); + decoded_ecm_sectors = MAX(decoded_ecm_sectors, sectorcount); + } else { + num = CD_FRAMESIZE_RAW; + } + + memcpy(dest, sector_buffer, CD_FRAMESIZE_RAW); + lastsector = sectorcount; + //printf("OK: Frame decoded %i %i\n", sectorcount-1, writebytecount); + return num; + +error_in: +error: +error_out: + //memset(dest, 0x0, CD_FRAMESIZE_RAW); + SysPrintf("Error decoding ECM image: WantedSector %i Type %i Sectors %i Pos %i(%li)\n", + sector, type, sectorcount, writebytecount, ftell(f)); + return -1; +} +#endif + +static int handleecm(const char *isoname) { +#ifdef ENABLE_CCDDA + // Rewind to start and check ECM header and filename suffix validity + fseek(cdHandle, 0, SEEK_SET); + if( + (fgetc(cdHandle) == 'E') && + (fgetc(cdHandle) == 'C') && + (fgetc(cdHandle) == 'M') && + (fgetc(cdHandle) == 0x00) && + (strncmp((isoname+strlen(isoname)-5), ".ecm", 4)) + ) { + SysPrintf(_("\nDetected ECM file with proper header and filename suffix.\n")); + + // Save real function used to read CD + cdimg_read_func_o = cdimg_read_func; + 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... + + // Full image decoded?? + if (decoded_ecm_sectors) { + len_decoded_ecm_buffer = len_ecm_savetable*CD_FRAMESIZE_RAW; + decoded_ecm_buffer = malloc(len_decoded_ecm_buffer); + if (decoded_ecm_buffer) { + //printf("Memory ok1 %u %p\n", len_decoded_ecm_buffer, decoded_ecm_buffer); + decoded_ecm = fmemopen(decoded_ecm_buffer, len_decoded_ecm_buffer, "w+b"); + decoded_ecm_sectors = 1; + } else { + SysMessage("Could not reserve memory for full ECM buffer. Only LUT will be used."); + decoded_ecm_sectors = 0; + } + } + + // Init ECC/EDC tables + eccedc_init(); + + // 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; +} + + static unsigned char * CALLBACK ISOgetBuffer_compr(void) { return compr_img->buff_raw[compr_img->sector_in_blk] + 12; } @@ -1422,6 +1610,10 @@ 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"); @@ -1448,6 +1640,12 @@ 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); @@ -1476,6 +1674,15 @@ long CALLBACK ISOinit(void) { static long CALLBACK ISOshutdown(void) { ISOclose(); + +#ifdef ENABLE_CCDDA + if (decoded_ecm != NULL) { + fclose(decoded_ecm); + free(decoded_ecm_buffer); + decoded_ecm_buffer = NULL; + decoded_ecm = NULL; + } +#endif return 0; } diff --git a/libpcsxcore/ecm.h b/libpcsxcore/ecm.h new file mode 100644 index 00000000..184382a7 --- /dev/null +++ b/libpcsxcore/ecm.h @@ -0,0 +1,278 @@ +//////////////////////////////////////////////////////////////////////////////// +// +#define TITLE "ecm - Encoder/decoder for Error Code Modeler format" +#define COPYR "Copyright (C) 2002-2011 Neill Corlett" +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// +//////////////////////////////////////////////////////////////////////////////// + +//#include "common.h" +//#include "banner.h" + +//////////////////////////////////////////////////////////////////////////////// +// +// Sector types +// +// Mode 1 +// ----------------------------------------------------- +// 0 1 2 3 4 5 6 7 8 9 A B C D E F +// 0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 01 +// 0010h [---DATA... +// ... +// 0800h ...DATA---] +// 0810h [---EDC---] 00 00 00 00 00 00 00 00 [---ECC... +// ... +// 0920h ...ECC---] +// ----------------------------------------------------- +// +// Mode 2 (XA), form 1 +// ----------------------------------------------------- +// 0 1 2 3 4 5 6 7 8 9 A B C D E F +// 0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 02 +// 0010h [--FLAGS--] [--FLAGS--] [---DATA... +// ... +// 0810h ...DATA---] [---EDC---] [---ECC... +// ... +// 0920h ...ECC---] +// ----------------------------------------------------- +// +// Mode 2 (XA), form 2 +// ----------------------------------------------------- +// 0 1 2 3 4 5 6 7 8 9 A B C D E F +// 0000h 00 FF FF FF FF FF FF FF FF FF FF 00 [-ADDR-] 02 +// 0010h [--FLAGS--] [--FLAGS--] [---DATA... +// ... +// 0920h ...DATA---] [---EDC---] +// ----------------------------------------------------- +// +// ADDR: Sector address, encoded as minutes:seconds:frames in BCD +// FLAGS: Used in Mode 2 (XA) sectors describing the type of sector; repeated +// twice for redundancy +// DATA: Area of the sector which contains the actual data itself +// EDC: Error Detection Code +// ECC: Error Correction Code +// + + +#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 +FILE* decoded_ecm = NULL; +void *decoded_ecm_buffer; + +int (*cdimg_read_func_o)(FILE *f, unsigned int base, void *dest, int sector); + +typedef struct ECMFILELUT { + s32 sector; + s32 filepos; +} ECMFILELUT; + +ECMFILELUT* ecm_savetable = NULL; + +static const size_t ECM_SECTOR_SIZE[4] = { + 1, + 2352, + 2336, + 2336 +}; + +//////////////////////////////////////////////////////////////////////////////// + +static uint32_t get32lsb(const uint8_t* src) { + return + (((uint32_t)(src[0])) << 0) | + (((uint32_t)(src[1])) << 8) | + (((uint32_t)(src[2])) << 16) | + (((uint32_t)(src[3])) << 24); +} + +static void put32lsb(uint8_t* dest, uint32_t value) { + dest[0] = (uint8_t)(value ); + dest[1] = (uint8_t)(value >> 8); + dest[2] = (uint8_t)(value >> 16); + dest[3] = (uint8_t)(value >> 24); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// LUTs used for computing ECC/EDC +// +static uint8_t ecc_f_lut[256]; +static uint8_t ecc_b_lut[256]; +static uint32_t edc_lut [256]; + +static void eccedc_init(void) { + size_t i; + for(i = 0; i < 256; i++) { + uint32_t edc = i; + size_t j = (i << 1) ^ (i & 0x80 ? 0x11D : 0); + ecc_f_lut[i] = j; + ecc_b_lut[i ^ j] = i; + for(j = 0; j < 8; j++) { + edc = (edc >> 1) ^ (edc & 1 ? 0xD8018001 : 0); + } + edc_lut[i] = edc; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Compute EDC for a block +// +static uint32_t edc_compute( + uint32_t edc, + const uint8_t* src, + size_t size +) { + for(; size; size--) { + edc = (edc >> 8) ^ edc_lut[(edc ^ (*src++)) & 0xFF]; + } + return edc; +} + +// +// Write ECC block (either P or Q) +// +static void ecc_writepq( + const uint8_t* address, + const uint8_t* data, + size_t major_count, + size_t minor_count, + size_t major_mult, + size_t minor_inc, + uint8_t* ecc +) { + size_t size = major_count * minor_count; + size_t major; + for(major = 0; major < major_count; major++) { + size_t index = (major >> 1) * major_mult + (major & 1); + uint8_t ecc_a = 0; + uint8_t ecc_b = 0; + size_t minor; + for(minor = 0; minor < minor_count; minor++) { + uint8_t temp; + if(index < 4) { + temp = address[index]; + } else { + temp = data[index - 4]; + } + index += minor_inc; + if(index >= size) { index -= size; } + ecc_a ^= temp; + ecc_b ^= temp; + ecc_a = ecc_f_lut[ecc_a]; + } + ecc_a = ecc_b_lut[ecc_f_lut[ecc_a] ^ ecc_b]; + ecc[major ] = (ecc_a ); + ecc[major + major_count] = (ecc_a ^ ecc_b); + } +} + +// +// Write ECC P and Q codes for a sector +// +static void ecc_writesector( + const uint8_t *address, + const uint8_t *data, + uint8_t *ecc +) { + ecc_writepq(address, data, 86, 24, 2, 86, ecc); // P + ecc_writepq(address, data, 52, 43, 86, 88, ecc + 0xAC); // Q +} + +//////////////////////////////////////////////////////////////////////////////// + +static const uint8_t zeroaddress[4] = {0, 0, 0, 0}; + +//////////////////////////////////////////////////////////////////////////////// +// +// Reconstruct a sector based on type +// +static void reconstruct_sector( + uint8_t* sector, // must point to a full 2352-byte sector + int8_t type +) { + // + // Sync + // + sector[0x000] = 0x00; + sector[0x001] = 0xFF; + sector[0x002] = 0xFF; + sector[0x003] = 0xFF; + sector[0x004] = 0xFF; + sector[0x005] = 0xFF; + sector[0x006] = 0xFF; + sector[0x007] = 0xFF; + sector[0x008] = 0xFF; + sector[0x009] = 0xFF; + sector[0x00A] = 0xFF; + sector[0x00B] = 0x00; + + switch(type) { + case 1: + // + // Mode + // + sector[0x00F] = 0x01; + // + // Reserved + // + sector[0x814] = 0x00; + sector[0x815] = 0x00; + sector[0x816] = 0x00; + sector[0x817] = 0x00; + sector[0x818] = 0x00; + sector[0x819] = 0x00; + sector[0x81A] = 0x00; + sector[0x81B] = 0x00; + break; + case 2: + case 3: + // + // Mode + // + sector[0x00F] = 0x02; + // + // Flags + // + sector[0x010] = sector[0x014]; + sector[0x011] = sector[0x015]; + sector[0x012] = sector[0x016]; + sector[0x013] = sector[0x017]; + break; + } + + // + // Compute EDC + // + switch(type) { + case 1: put32lsb(sector+0x810, edc_compute(0, sector , 0x810)); break; + case 2: put32lsb(sector+0x818, edc_compute(0, sector+0x10, 0x808)); break; + case 3: put32lsb(sector+0x92C, edc_compute(0, sector+0x10, 0x91C)); break; + } + + // + // Compute ECC + // + switch(type) { + case 1: ecc_writesector(sector+0xC , sector+0x10, sector+0x81C); break; + case 2: ecc_writesector(zeroaddress, sector+0x10, sector+0x81C); break; + } + + // + // Done + // +} |
