353 lines
12 KiB
C
353 lines
12 KiB
C
/* dvdisaster: Additional error correction for optical media.
|
|
* Copyright (C) 2004-2009 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 "rs02-includes.h"
|
|
|
|
/***
|
|
*** Read an image sector from the .iso file.
|
|
****
|
|
* Reading sectors beyond lay->protectedSectors always returns a zero padding sector.
|
|
*/
|
|
|
|
void RS02ReadSector(ImageInfo *ii, RS02Layout *lay, unsigned char *buf, gint64 s)
|
|
{ int n;
|
|
|
|
/* Padding sector for ecc calculation */
|
|
|
|
if(s >= lay->protectedSectors)
|
|
{ memset(buf, 0, 2048);
|
|
return;
|
|
}
|
|
|
|
/* There is a circular dependence between the first EccHeader
|
|
and the error correction because of the eccSum.
|
|
Simply return a null sector instead. */
|
|
|
|
if( s == lay->firstEccHeader
|
|
|| s == lay->firstEccHeader + 1)
|
|
{ memset(buf, 0, 2048);
|
|
return;
|
|
}
|
|
|
|
/* Reading beyond the image returns dead sectors */
|
|
|
|
if(s >= ii->sectors)
|
|
{ CreateMissingSector(buf, s, NULL, 0, NULL);
|
|
return;
|
|
}
|
|
|
|
/* Read a real sector */
|
|
|
|
if(!LargeSeek(ii->file, (gint64)(2048*s)))
|
|
Stop(_("Failed seeking to sector %lld in image: %s"),
|
|
s, strerror(errno));
|
|
|
|
n = LargeRead(ii->file, buf, 2048);
|
|
if(n != 2048)
|
|
Stop(_("Failed reading sector %lld in image: %s"),s,strerror(errno));
|
|
}
|
|
|
|
/***
|
|
*** Calculate position of n-th Ecc sector of the given slice in the image.
|
|
***
|
|
* Deprecated; use RS02SectorIndex() instead.
|
|
*/
|
|
|
|
gint64 RS02EccSectorIndex(RS02Layout *lay, gint64 slice, gint64 n)
|
|
{ gint64 ecc_idx;
|
|
gint64 fr,base;
|
|
gint64 s,nh;
|
|
|
|
/* Index of ecc sectors if numbering were continuos and starting from 0 */
|
|
|
|
ecc_idx = slice*lay->sectorsPerLayer + n;
|
|
|
|
/* Index of first Ecc header which is interleaved with ecc sectors */
|
|
|
|
fr = (lay->protectedSectors + lay->headerModulo - 1) / lay->headerModulo;
|
|
fr *= lay->headerModulo;
|
|
|
|
/* Number of ecc sectors before first interleaved Ecc header */
|
|
|
|
base = fr - lay->protectedSectors;
|
|
|
|
if(ecc_idx < base)
|
|
return lay->protectedSectors + ecc_idx;
|
|
|
|
/* Calculate index of ecc sectors with interleaved headers taken into account */
|
|
|
|
s = fr+2; /* first available ecc sector behind first interleaved header */
|
|
ecc_idx -= base; /* number of ecc sectors behind first interleaved header */
|
|
nh = ecc_idx/(lay->headerModulo-2); /* number of interleaved headers crossed */
|
|
s += ecc_idx; /* add ecc sector index */
|
|
s += 2*nh; /* add two for each interleaved header crossed */
|
|
return s;
|
|
}
|
|
|
|
/***
|
|
*** Calculate position of n-th sector of the given slice in the image.
|
|
***/
|
|
|
|
gint64 RS02SectorIndex(RS02Layout *lay, gint64 slice, gint64 n)
|
|
{ gint64 ecc_idx;
|
|
gint64 fr,base;
|
|
gint64 s,nh;
|
|
|
|
/* Easy case: Sector is a data or crc sector */
|
|
|
|
if(slice < lay->ndata)
|
|
{ gint64 sector = slice*lay->sectorsPerLayer + n;
|
|
|
|
if(sector < lay->protectedSectors)
|
|
return sector;
|
|
else return -1; /* padding sector (invalid)! */
|
|
}
|
|
|
|
/* else calculate position of ecc sector */
|
|
|
|
slice -= lay->ndata;
|
|
|
|
/* Index of ecc sectors if numbering were continuos and starting from 0 */
|
|
|
|
ecc_idx = slice*lay->sectorsPerLayer + n;
|
|
|
|
/* Index of first Ecc header which is interleaved with ecc sectors */
|
|
|
|
fr = (lay->protectedSectors + lay->headerModulo - 1) / lay->headerModulo;
|
|
fr *= lay->headerModulo;
|
|
|
|
/* Number of ecc sectors before first interleaved Ecc header */
|
|
|
|
base = fr - lay->protectedSectors;
|
|
|
|
if(ecc_idx < base)
|
|
return lay->protectedSectors + ecc_idx;
|
|
|
|
/* Calculate index of ecc sectors with interleaved headers taken into account */
|
|
|
|
s = fr+2; /* first available ecc sector behind first interleaved header */
|
|
ecc_idx -= base; /* number of ecc sectors behind first interleaved header */
|
|
nh = ecc_idx/(lay->headerModulo-2); /* number of interleaved headers crossed */
|
|
s += ecc_idx; /* add ecc sector index */
|
|
s += 2*nh; /* add two for each interleaved header crossed */
|
|
return s;
|
|
}
|
|
|
|
/***
|
|
*** Calculate position of given sector within its Ecc slice.
|
|
***
|
|
* E.g. if s = RS02SectorIndex(lay, slice, n)
|
|
* then RS02SliceIndex(lay, s, &slice, &n)
|
|
* returns the slice and n values for s.
|
|
*/
|
|
|
|
void RS02SliceIndex(RS02Layout *lay, gint64 sector, gint64 *slice, gint64 *n)
|
|
{ gint64 remainder;
|
|
gint64 first_repeat;
|
|
gint64 base;
|
|
|
|
/* Sector comes from data or crc section */
|
|
|
|
if(sector < lay->protectedSectors)
|
|
{ *slice = sector / lay->sectorsPerLayer;
|
|
*n = sector % lay->sectorsPerLayer;
|
|
return;
|
|
}
|
|
|
|
/* Position of first ecc header repeat */
|
|
|
|
first_repeat = (lay->protectedSectors + lay->headerModulo - 1) / lay->headerModulo;
|
|
first_repeat *= lay->headerModulo;
|
|
|
|
/* Querying a header returns -1 for the slice
|
|
and the header repeat number in n */
|
|
|
|
remainder = sector % lay->headerModulo;
|
|
if(remainder < 2)
|
|
{
|
|
*slice = -1;
|
|
*n = (sector-first_repeat) / lay->headerModulo;
|
|
return;
|
|
}
|
|
|
|
/* Sector is an ecc sector and lies before first interleaved Ecc header */
|
|
|
|
if(sector < first_repeat)
|
|
{ *slice = lay->ndata + (sector-lay->protectedSectors)/lay->sectorsPerLayer;
|
|
*n = (sector - lay->protectedSectors) % lay->sectorsPerLayer;
|
|
return;
|
|
}
|
|
|
|
/* Sector is an ecc sector and lies behind the first interleaved Ecc header */
|
|
|
|
base = first_repeat - lay->protectedSectors; /* ecc sectors before first repeat */
|
|
|
|
sector -= first_repeat;
|
|
sector -= 2; /* subtract first repeated header */
|
|
sector -= 2*(sector/(lay->headerModulo-0)); /* and other crossed repeats */
|
|
sector += base; /* add sectors before first repeat */
|
|
|
|
*slice = lay->ndata + sector / lay->sectorsPerLayer;
|
|
*n = sector % lay->sectorsPerLayer;
|
|
}
|
|
|
|
/***
|
|
*** Calculation of the image layout
|
|
***/
|
|
|
|
RS02Layout *CalcRS02Layout(gint64 data_sectors, int requested_roots)
|
|
{ RS02Layout *lay = g_malloc0(sizeof(RS02Layout));
|
|
guint64 ecc_area;
|
|
|
|
/* If no medium size is given by the user,
|
|
pick the smallest possible among CDR, single layer DVD and two layer DVD. */
|
|
|
|
if(Closure->mediumSize)
|
|
lay->mediumCapacity = Closure->mediumSize;
|
|
else
|
|
{ if(data_sectors < Closure->cdSize)
|
|
lay->mediumCapacity = Closure->cdSize; /* CDR */
|
|
else if(data_sectors < Closure->dvdSize1)
|
|
lay->mediumCapacity = Closure->dvdSize1; /* Single layered DVD */
|
|
else if(data_sectors < Closure->dvdSize2)
|
|
lay->mediumCapacity = Closure->dvdSize2; /* Double layered DVD */
|
|
else if(data_sectors < Closure->bdSize1)
|
|
lay->mediumCapacity = Closure->bdSize1; /* Single layered BD */
|
|
else lay->mediumCapacity = Closure->bdSize2; /* Double layered BD */
|
|
}
|
|
|
|
lay->dataSectors = data_sectors;
|
|
lay->firstEccHeader = lay->dataSectors;
|
|
lay->crcSectors = (sizeof(guint32)*lay->dataSectors+2047)/2048;
|
|
lay->protectedSectors = lay->dataSectors + 2 + lay->crcSectors; /* two sectors for header */
|
|
|
|
/* Calculate starting value for the redundancy */
|
|
|
|
if(requested_roots > 0)
|
|
lay->nroots = requested_roots;
|
|
else
|
|
{ lay->rsSectors = lay->mediumCapacity - lay->protectedSectors; /* just to start */
|
|
lay->nroots = (GF_FIELDMAX*lay->rsSectors) / lay->mediumCapacity; /* iteration below */
|
|
}
|
|
|
|
if(lay->nroots > 170) /* Cap redundancy to 200% */
|
|
lay->nroots = 170;
|
|
|
|
/* Calculate the header repeat value so that
|
|
we get about 20 to 40 copies of the header in the ecc section. */
|
|
|
|
lay->headerModulo = 32;
|
|
lay->ndata = GF_FIELDMAX-lay->nroots;
|
|
ecc_area = lay->nroots * ((lay->protectedSectors + lay->ndata - 1) / lay->ndata);
|
|
|
|
while(ecc_area / lay->headerModulo > 40)
|
|
lay->headerModulo <<= 1;
|
|
|
|
/* Now assemble everything together and make sure it fits on the medium */
|
|
|
|
while(lay->nroots > 7)
|
|
{ gint64 first_repeat; /* first header which is interleaved with ecc sectors */
|
|
gint64 interleaved; /* number of ecc sectors after first header */
|
|
|
|
lay->ndata = GF_FIELDMAX-lay->nroots;
|
|
lay->rsSectors = lay->nroots * ((lay->protectedSectors + lay->ndata - 1) / lay->ndata);
|
|
first_repeat = (lay->protectedSectors + lay->headerModulo - 1) / lay->headerModulo;
|
|
first_repeat *= lay->headerModulo;
|
|
|
|
interleaved = lay->rsSectors + lay->protectedSectors - first_repeat;
|
|
lay->headers = interleaved / (lay->headerModulo-2) + 1;
|
|
|
|
//lay->headers = 1 + (lay->rsSectors + lay->protectedSectors - first_repeat) / lay->headerModulo;
|
|
|
|
lay->eccSectors = 2 + lay->crcSectors + lay->rsSectors + 2*lay->headers;
|
|
lay->sectorsPerLayer = (lay->protectedSectors + lay->ndata - 1) / lay->ndata;
|
|
|
|
lay->firstCrcLayerIndex = (2 + lay->dataSectors) % lay->sectorsPerLayer;
|
|
|
|
if(requested_roots > 0)
|
|
break;
|
|
|
|
if(lay->eccSectors + lay->dataSectors <= lay->mediumCapacity)
|
|
break;
|
|
|
|
lay->nroots--;
|
|
}
|
|
|
|
lay->redundancy = ((double)lay->nroots*100.0)/(double)lay->ndata;
|
|
|
|
|
|
Verbose("Calculated layout for RS02 image:\n");
|
|
|
|
Verbose("data sectors = %lld\n", lay->dataSectors);
|
|
Verbose("crc sectors = %lld\n", lay->crcSectors);
|
|
Verbose("protected sectors = %lld (incl. 2 hdr sectors)\n", lay->protectedSectors);
|
|
Verbose("reed solomon secs = %lld (%d roots, %d data)\n", lay->rsSectors,lay->nroots,lay->ndata);
|
|
Verbose("header repeats = %lld (using modulo %lld)\n", lay->headers, lay->headerModulo);
|
|
Verbose("added sectors = %lld\n", lay->eccSectors);
|
|
Verbose("total image size = %lld\n", lay->eccSectors+lay->dataSectors);
|
|
if(requested_roots > 0)
|
|
Verbose("medium capacity = n.a.\n");
|
|
else Verbose("medium capacity = %lld\n", lay->mediumCapacity);
|
|
|
|
Verbose("\nInterleaving layout:\n");
|
|
Verbose("%lld sectors per ecc layer\n",lay->sectorsPerLayer);
|
|
Verbose("first layer sector with CRC data %lld (sector# %lld)\n",
|
|
lay->firstCrcLayerIndex, lay->dataSectors+2);
|
|
Verbose("\n");
|
|
|
|
return lay;
|
|
}
|
|
|
|
/***
|
|
*** Write the RS02 headers into the image.
|
|
***/
|
|
|
|
void WriteRS02Headers(LargeFile *file, RS02Layout *lay, EccHeader *eh)
|
|
{ guint64 hpos;
|
|
// guint64 end = lay->eccSectors+lay->dataSectors-2;
|
|
guint64 end = lay->eccSectors+lay->dataSectors;
|
|
int n;
|
|
|
|
if(!LargeSeek(file, 2048*lay->firstEccHeader))
|
|
Stop(_("Failed seeking to ecc header at %lld: %s\n"), lay->firstEccHeader, strerror(errno));
|
|
|
|
n = LargeWrite(file, eh, sizeof(EccHeader));
|
|
if(n != sizeof(EccHeader))
|
|
Stop(_("Failed writing ecc header at %lld: %s\n"), lay->firstEccHeader, strerror(errno));
|
|
|
|
hpos = (lay->protectedSectors + lay->headerModulo - 1) / lay->headerModulo;
|
|
hpos *= lay->headerModulo;
|
|
|
|
while(hpos < end)
|
|
{
|
|
if(!LargeSeek(file, 2048*hpos))
|
|
Stop(_("Failed seeking to ecc header at %lld: %s\n"), hpos, strerror(errno));
|
|
|
|
n = LargeWrite(file, eh, sizeof(EccHeader));
|
|
if(n != sizeof(EccHeader))
|
|
Stop(_("Failed writing ecc header at %lld: %s\n"), hpos, strerror(errno));
|
|
|
|
hpos += lay->headerModulo;
|
|
}
|
|
}
|