301 lines
7.4 KiB
C
301 lines
7.4 KiB
C
/* dvdisaster: Additional error correction for optical media.
|
|
* Copyright (C) 2004-2010 Carsten Gnoerlich.
|
|
* Project home page: http://www.dvdisaster.com
|
|
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA,
|
|
* or direct your browser at http://www.gnu.org.
|
|
*/
|
|
|
|
#include "dvdisaster.h"
|
|
#include "udf.h"
|
|
|
|
#include "rs03-includes.h"
|
|
|
|
/***
|
|
*** Recognize a RS03 error correction file
|
|
***/
|
|
|
|
int RS03RecognizeFile(Method *self, LargeFile *ecc_file)
|
|
{ EccHeader eh;
|
|
int n;
|
|
|
|
LargeSeek(ecc_file, 0);
|
|
n = LargeRead(ecc_file, &eh, sizeof(EccHeader));
|
|
|
|
if(n != sizeof(EccHeader))
|
|
return FALSE;
|
|
|
|
if(strncmp((char*)eh.cookie, "*dvdisaster*", 12))
|
|
return FALSE;
|
|
|
|
if(!strncmp((char*)eh.method, "RS03", 4))
|
|
{
|
|
if(self->lastEh) g_free(self->lastEh);
|
|
self->lastEh = g_malloc(sizeof(EccHeader));
|
|
memcpy(self->lastEh, &eh, sizeof(EccHeader));
|
|
|
|
#ifdef HAVE_BIG_ENDIAN
|
|
SwapEccHeaderBytes(self->lastEh);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***
|
|
*** Recognize RS03 error correction data in the image
|
|
***/
|
|
|
|
#if 0
|
|
static int read_fingerprint(LargeFile *file, unsigned char *fingerprint, gint64 sector)
|
|
{ struct MD5Context md5ctxt;
|
|
unsigned char buf[2048];
|
|
int n;
|
|
|
|
if(!LargeSeek(file, 2048LL*sector))
|
|
return FALSE;
|
|
|
|
n = LargeRead(file, buf, 2048);
|
|
|
|
if(n != 2048) return FALSE;
|
|
|
|
if(CheckForMissingSector(buf, sector, NULL, 0) != SECTOR_PRESENT)
|
|
return FALSE;
|
|
|
|
MD5Init(&md5ctxt);
|
|
MD5Update(&md5ctxt, buf, 2048);
|
|
MD5Final(fingerprint, &md5ctxt);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
EccHeader* ValidHeader(unsigned char *buf, gint64 hdr_pos)
|
|
{ EccHeader *eh = (EccHeader*)buf;
|
|
guint32 recorded_crc, real_crc;
|
|
// unsigned char fingerprint[16];
|
|
|
|
/* Medium read error in ecc header? */
|
|
|
|
if( (CheckForMissingSector(buf, hdr_pos, NULL, 0) != SECTOR_PRESENT)
|
|
|| (CheckForMissingSector(buf+2048, hdr_pos+1, NULL, 0) != SECTOR_PRESENT))
|
|
return NULL;
|
|
|
|
/* See if the magic cookie is there */
|
|
|
|
if( strncmp((char*)eh->cookie, "*dvdisaster*", 12)
|
|
|| strncmp((char*)eh->method, "RS03", 4)) // FIXME
|
|
return NULL;
|
|
|
|
/* Examine the checksum */
|
|
|
|
recorded_crc = eh->selfCRC;
|
|
|
|
#ifdef HAVE_BIG_ENDIAN
|
|
eh->selfCRC = 0x47504c00;
|
|
#else
|
|
eh->selfCRC = 0x4c5047;
|
|
#endif
|
|
real_crc = Crc32((unsigned char*)eh, 4096);
|
|
|
|
if(real_crc != recorded_crc)
|
|
return NULL;
|
|
|
|
/* Check the fingerprint */
|
|
|
|
eh = g_malloc(sizeof(EccHeader));
|
|
memcpy(eh, buf, sizeof(EccHeader));
|
|
#ifdef HAVE_BIG_ENDIAN
|
|
SwapEccHeaderBytes(eh);
|
|
#endif
|
|
eh->selfCRC = recorded_crc;
|
|
|
|
#if 0
|
|
status = read_fingerprint(file, fingerprint, eh->fpSector);
|
|
|
|
if(!status) /* be optimistic if fingerprint sector is unreadable */
|
|
return eh;
|
|
|
|
if(!memcmp(fingerprint, eh->mediumFP, 16)) /* good fingerprint */
|
|
{ printf("RS03 header found\n");
|
|
return eh;
|
|
}
|
|
g_free(eh);
|
|
#endif
|
|
|
|
return eh;
|
|
}
|
|
|
|
EccHeader* FindRS03HeaderInImage(LargeFile *file)
|
|
{ EccHeader *eh = NULL;
|
|
IsoInfo *ii;
|
|
gint64 hdr_pos;
|
|
unsigned char buf[4096];
|
|
|
|
Verbose("FindRS03HeaderInImage(%s)\n", file->path);
|
|
|
|
/*** Try to find the header behind the ISO image */
|
|
|
|
ii = ExamineUDF(NULL, file);
|
|
if(!ii) Verbose(" . NO ISO structures found!\n");
|
|
|
|
if(ii)
|
|
{ hdr_pos = ii->volumeSize;
|
|
if(LargeSeek(file, 2048*hdr_pos))
|
|
{ int n = LargeRead(file, buf, sizeof(EccHeader));
|
|
|
|
if(n == sizeof(EccHeader))
|
|
{ eh = ValidHeader(buf, hdr_pos);
|
|
if(eh)
|
|
{ Verbose("FindRS03HeaderInImage(): Header found at pos +0\n");
|
|
return eh;
|
|
}
|
|
}
|
|
}
|
|
|
|
hdr_pos = ii->volumeSize - 150;
|
|
if(LargeSeek(file, 2048*hdr_pos))
|
|
{ int n = LargeRead(file, buf, sizeof(EccHeader));
|
|
|
|
if(n == sizeof(EccHeader))
|
|
{ eh = ValidHeader(buf, hdr_pos);
|
|
if(eh)
|
|
{ Verbose("FindRS03HeaderInImage(): Header found at pos -150\n");
|
|
return eh;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedef struct
|
|
{ gint64 bidx[256];
|
|
char *layer[256];
|
|
} recognize_context;
|
|
|
|
static void free_recognize_context(recognize_context *rc)
|
|
{ int i;
|
|
|
|
for(i=0; i<255; i++)
|
|
if(rc->layer[i])
|
|
g_free(rc->layer[i]);
|
|
|
|
g_free(rc);
|
|
}
|
|
|
|
int RS03RecognizeImage(Method *self, LargeFile *ecc_file)
|
|
{ recognize_context *rc = g_malloc0(sizeof(recognize_context));
|
|
EccHeader *eh;
|
|
gint64 file_size;
|
|
gint64 layer_size;
|
|
int ecc_block,ndata,nroots;
|
|
int i;
|
|
|
|
/* Easy shot: Locate the ecc header in the image */
|
|
|
|
eh = FindRS03HeaderInImage(ecc_file);
|
|
|
|
if(eh)
|
|
{ if(self->lastEh) g_free(self->lastEh);
|
|
self->lastEh = eh;
|
|
return TRUE;
|
|
}
|
|
|
|
/* No exhaustive search unless explicitly okayed by user */
|
|
|
|
if(!Closure->examineRS03)
|
|
return FALSE;
|
|
|
|
/* Ugly case. Experimentally try the RS-Code. */
|
|
|
|
Verbose("RS03RecognizeImage(): No EH\n");
|
|
|
|
if(!LargeStat(Closure->imageName, &file_size))
|
|
return FALSE;
|
|
|
|
file_size /= 2048;
|
|
|
|
if(Closure->debugMode && Closure->mediumSize)
|
|
layer_size = Closure->mediumSize/GF_FIELDMAX;
|
|
else
|
|
{ if(file_size < CDR_SIZE) layer_size = CDR_SIZE/GF_FIELDMAX;
|
|
else if(file_size < DVD_SL_SIZE) layer_size = DVD_SL_SIZE/GF_FIELDMAX;
|
|
else if(file_size < DVD_DL_SIZE) layer_size = DVD_DL_SIZE/GF_FIELDMAX;
|
|
else if(file_size < BD_SL_SIZE) layer_size = BD_SL_SIZE/GF_FIELDMAX;
|
|
else layer_size = BD_DL_SIZE/GF_FIELDMAX;
|
|
}
|
|
|
|
Verbose(".. trying layer size %lld\n", layer_size);
|
|
|
|
for(i=0; i<255; i++)
|
|
{ rc->bidx[i] = i*layer_size;
|
|
rc->layer[i] = malloc(2048);
|
|
}
|
|
|
|
/* Now try all ecc blocks */
|
|
|
|
for(ecc_block=0; ecc_block<layer_size; ecc_block++)
|
|
{ Verbose("Assembling ecc block %d\n", ecc_block);
|
|
|
|
/* Assemble the ecc block */
|
|
|
|
for(i=0; i<255; i++)
|
|
{ gint64 sector = rc->bidx[i]++;
|
|
int n;
|
|
|
|
if(!LargeSeek(ecc_file, (gint64)(2048*sector)))
|
|
Stop(_("Failed seeking to sector %lld in image: %s"),
|
|
sector, strerror(errno));
|
|
|
|
n = LargeRead(ecc_file, rc->layer[i], 2048);
|
|
if(n != 2048)
|
|
Stop(_("Failed reading sector %lld in image: %s"),sector,strerror(errno));
|
|
}
|
|
|
|
/* Experimentally apply the RS code */
|
|
|
|
for(ndata=255-8; ndata >=85; ndata--)
|
|
{ CrcBlock *cb = (CrcBlock*)rc->layer[ndata];
|
|
|
|
/* Do the real decode here */
|
|
|
|
|
|
/* See if we have decoded a CRC block */
|
|
|
|
if( !memcmp(cb->cookie, "*dvdisaster*", 12)
|
|
||!memcmp(cb->method, "RS03", 4))
|
|
{
|
|
nroots = 255-ndata-1;
|
|
Verbose(".. Success: rediscovered format with %d roots\n", nroots);
|
|
|
|
if(self->lastEh) g_free(self->lastEh);
|
|
self->lastEh = g_malloc(sizeof(EccHeader));
|
|
ReconstructRS03Header(self->lastEh, cb);
|
|
//FIXME: endianess okay?
|
|
free_recognize_context(rc);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
free_recognize_context(rc);
|
|
return FALSE;
|
|
}
|
|
|