Files
dvdisaster/rs02-common.c
2020-08-20 14:17:58 +02:00

813 lines
25 KiB
C

/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2017 Carsten Gnoerlich.
*
* Email: carsten@dvdisaster.org -or- cgnoerlich@fsfe.org
* Project homepage: http://www.dvdisaster.org
*
* This file is part of dvdisaster.
*
* dvdisaster 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.
*
* dvdisaster 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 dvdisaster. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dvdisaster.h"
#include "rs02-includes.h"
#include "scsi-layer.h"
/***
*** Read and buffer CRC information from RS02 file
***/
CrcBuf *RS02GetCrcBuf(Image *image)
{ RS02CksumClosure *csc;
AlignedBuffer *ab = CreateAlignedBuffer(2048);
RS02Layout *lay;
CrcBuf *cb;
gint64 block_idx[256];
gint64 crc_sector;
gint64 s,i;
int crc_idx, crc_valid = FALSE;
csc = (RS02CksumClosure*)image->eccMethod->ckSumClosure;
if(csc->lay) g_free(csc->lay);
lay = csc->lay = RS02LayoutFromImage(image);
cb = CreateCrcBuf(image);
/* Initialize ecc block index pointers.
The first CRC set (of lay->ndata checksums) relates to
ecc block lay->firstCrcLayerIndex + 1. */
for(s=0, i=0; i<lay->ndata; s+=lay->sectorsPerLayer, i++)
block_idx[i] = s + lay->firstCrcLayerIndex + 1;
crc_idx = 512; /* force crc buffer reload */
crc_sector = lay->dataSectors+2; /* first crc data sector on medium */
/* Cycle through the ecc blocks and descramble CRC sums in
ascending sector numbers. */
for(s=0; s<lay->sectorsPerLayer; s++)
{ gint64 si = (s + lay->firstCrcLayerIndex + 1) % lay->sectorsPerLayer;
/* Wrap the block_idx[] ptrs at si == 0 */
if(!si)
{ gint64 bs;
for(bs=0, i=0; i<lay->ndata; bs+=lay->sectorsPerLayer, i++)
block_idx[i] = bs;
}
/* Go through all data sectors of current ecc block */
for(i=0; i<lay->ndata; i++)
{ gint64 bidx = block_idx[i];
if(bidx < lay->dataSectors) /* only data sectors have CRCs */
{
/* Refill crc cache if needed */
if(crc_idx >= 512)
{ crc_valid = ImageReadSectors(image, ab->buf, crc_sector++, 1);
crc_idx = 0;
}
/* Sort crc into appropriate place */
if(crc_valid)
{ cb->crcbuf[bidx] = ((guint32*)ab->buf)[crc_idx];
SetBit(cb->valid, bidx);
}
crc_idx++;
block_idx[i]++;
}
}
}
FreeAlignedBuffer(ab);
/* The ecc header records only the md5 sum of the data portion,
but not that of the whole image, so flag the md5 sums as missing. */
memcpy(cb->dataMD5sum, image->eccHeader->mediumSum, 16);
cb->md5State = MD5_BUILDING | MD5_DATA_COMPLETE;
return cb;
}
/***
*** Internal checksum handling.
***/
void RS02ResetCksums(Image *image)
{ RS02CksumClosure *csc = (RS02CksumClosure*)image->eccMethod->ckSumClosure;
MD5Init(&csc->md5ctxt);
MD5Init(&csc->dataCtxt);
MD5Init(&csc->crcCtxt);
MD5Init(&csc->eccCtxt);
MD5Init(&csc->metaCtxt);
}
void RS02UpdateCksums(Image *image, gint64 sector, unsigned char *buf)
{ RS02CksumClosure *csc = (RS02CksumClosure*)image->eccMethod->ckSumClosure;
MD5Update(&csc->md5ctxt, buf, 2048);
/* md5sum the data portion */
if(sector < csc->lay->dataSectors)
{ if(sector < csc->lay->dataSectors - 1)
MD5Update(&csc->dataCtxt, buf, 2048);
else MD5Update(&csc->dataCtxt, buf, image->eccHeader->inLast);
}
/* md5sum the crc portion */
if(sector >= csc->lay->dataSectors+2 && sector < csc->lay->protectedSectors)
MD5Update(&csc->crcCtxt, buf, 2048);
/* md5sum the ecc layers */
if(sector >= csc->lay->protectedSectors)
{ gint64 layer, n;
RS02SliceIndex(csc->lay, sector, &layer, &n);
if(layer != -1) /* not an ecc header? */
{ if(n < csc->lay->sectorsPerLayer-1) /* not at layer end? */
MD5Update(&csc->eccCtxt, buf, 2048);
else /* layer end; update meta md5 and skip to next layer */
{ guint8 sum[16];
MD5Update(&csc->eccCtxt, buf, 2048);
MD5Final(sum, &csc->eccCtxt);
MD5Update(&csc->metaCtxt, sum, 16);
MD5Init(&csc->eccCtxt);
}
}
/* maybe add ...else { check ecc header } */
}
}
int RS02FinalizeCksums(Image *image)
{ RS02CksumClosure *csc = (RS02CksumClosure*)image->eccMethod->ckSumClosure;
guint8 image_fp[16];
guint8 data_md5[16],crc_md5[16],meta_md5[16];
char ascii_digest[33];
int result = 0;
MD5Final(image_fp, &csc->md5ctxt);
MD5Final(data_md5, &csc->dataCtxt);
MD5Final(crc_md5, &csc->crcCtxt);
MD5Final(meta_md5, &csc->metaCtxt);
/* Data (payload) checksum */
if(memcmp(data_md5, image->eccHeader->mediumSum, 16))
{ AsciiDigest(ascii_digest, data_md5);
Verbose("BAD Data md5sum: %s\n", ascii_digest);
result |= DATA_MD5_BAD;
}
else Verbose("GOOD Data md5sum\n");
/* Crc layer checksum */
if(memcmp(crc_md5, image->eccHeader->crcSum, 16))
{ AsciiDigest(ascii_digest, crc_md5);
Verbose("BAD CRC md5sum: %s\n", ascii_digest);
result |= CRC_MD5_BAD;
}
else Verbose("GOOD CRC md5sum\n");
/* Ecc meta checksum */
if(memcmp(meta_md5, image->eccHeader->eccSum, 16))
{ AsciiDigest(ascii_digest, meta_md5);
Verbose("BAD ECC md5sum: %s\n", ascii_digest);
result |= ECC_MD5_BAD;
}
else Verbose("GOOD ECC md5sum\n");
return result;
}
/***
*** Read an image sector from the .iso file.
****
* Reading sectors beyond lay->protectedSectors always returns a zero padding sector.
*/
void RS02ReadSector(Image *image, 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 >= image->sectorSize)
{ CreateMissingSector(buf, s, NULL, 0, NULL);
return;
}
/* Read a real sector */
if(!LargeSeek(image->file, (gint64)(2048*s)))
Stop(_("Failed seeking to sector %lld in image: %s"),
s, strerror(errno));
n = LargeRead(image->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
***/
/*
* Calculate new layout for either a given medium size,
* or from default media sizes.
*/
RS02Layout *CalcRS02Layout(Image *image)
{ RS02Layout *lay = g_malloc0(sizeof(RS02Layout));
guint64 ecc_area;
int requested_roots = 0;
lay->eh = image->eccHeader;
/* 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(image->sectorSize < Closure->cdSize)
lay->mediumCapacity = Closure->cdSize; /* CDR */
else if(image->sectorSize < Closure->dvdSize1)
lay->mediumCapacity = Closure->dvdSize1; /* Single layered DVD */
else if(image->sectorSize < Closure->dvdSize2)
lay->mediumCapacity = Closure->dvdSize2; /* Double layered DVD */
else if(image->sectorSize < Closure->bdSize1)
lay->mediumCapacity = Closure->bdSize1; /* Single layered BD */
else if(image->sectorSize < Closure->bdSize2)
lay->mediumCapacity = Closure->bdSize2; /* Double layered BD */
else if(image->sectorSize < Closure->bdSize3)
lay->mediumCapacity = Closure->bdSize3; /* Triple layered BDXL */
else lay->mediumCapacity = Closure->bdSize4; /* Quadruple layered BDXL */
}
lay->dataSectors = image->sectorSize;
lay->firstEccHeader = lay->dataSectors;
lay->crcSectors = (sizeof(guint32)*lay->dataSectors+2047)/2048;
lay->protectedSectors = lay->dataSectors + 2 + lay->crcSectors; /* two sectors for header */
/* See if user wants to pick a certain redundancy */
#ifndef CLI
if(!Closure->guiMode && Closure->redundancy)
#else
if(Closure->redundancy)
#endif
{ int len = strlen(Closure->redundancy);
switch(Closure->redundancy[len-1])
{ case 'r': /* pick number of roots */
{ char buf[len+1];
memcpy(buf, Closure->redundancy, len);
buf[len] = '\0';
requested_roots = atoi(buf);
break;
}
case '%': /* pick redundancy directly */
{ char buf[len];
int percent;
memcpy(buf, Closure->redundancy, len-1);
buf[len-1] = '\0';
percent = atoi(buf);
for(requested_roots = 7; requested_roots < 171; requested_roots++)
{ double redundancy = ((double)requested_roots*100.0)/((double)(GF_FIELDMAX-requested_roots));
if(redundancy >= percent)
break;
}
if(requested_roots >170)
requested_roots = 0;
break;
}
}
}
/* 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;
if(lay->nroots < 0)
lay->nroots = 0;
/* 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->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;
}
/*
* Determine expected size of image.
*/
guint64 RS02ExpectedImageSize(Image *image)
{ EccHeader *eh = image->eccHeader;
RS02Layout *lay;
guint64 size;
if(!eh) return 0;
lay = RS02LayoutFromImage(image);
size = lay->eccSectors+lay->dataSectors;
g_free(lay);
return size;
}
/***
*** 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;
}
}
/*
* Calculate layout properties from a given image and ecc header.
*/
static void calc_headers(RS02Layout *lay)
{ gint64 first_repeat; /* first header which is interleaved with ecc sectors */
gint64 interleaved; /* number of ecc sectors after first header */
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->eccSectors = 2 + lay->crcSectors + lay->rsSectors + 2*lay->headers;
}
RS02Layout *RS02LayoutFromImage(Image *image)
{ RS02Layout *lay = g_malloc0(sizeof(RS02Layout));
EccHeader *eh = image->eccHeader;
guint64 expected_size=0;
guint64 expected_size2=0;
/* See if layout has already been cached in the image */
if(image->cachedLayout)
{ Verbose("RS02LayoutFromImage(): returning cached layout\n");
memcpy(lay, image->cachedLayout, sizeof(RS02Layout));
return lay;
}
/* Invariant values taken from Image/EccHeader */
lay->eh = image->eccHeader;
lay->dataSectors = uchar_to_gint64(eh->sectors);
lay->firstEccHeader = lay->dataSectors;
lay->crcSectors = (sizeof(guint32)*lay->dataSectors+2047)/2048;
lay->protectedSectors = lay->dataSectors + 2 + lay->crcSectors; /* two sectors for header */
lay->nroots = eh->eccBytes;
lay->ndata = eh->dataBytes;
lay->redundancy = ((double)lay->nroots*100.0)/(double)lay->ndata;
lay->sectorsPerLayer = (lay->protectedSectors + lay->ndata - 1) / lay->ndata;
lay->rsSectors = lay->nroots * lay->sectorsPerLayer;
lay->firstCrcLayerIndex = (2 + lay->dataSectors) % lay->sectorsPerLayer;
/* Calculate the header repeat value so that
we get about 20 to 40 copies of the header in the ecc section. */
lay->headerModulo = 32;
while(lay->rsSectors / lay->headerModulo > 40)
lay->headerModulo <<= 1;
/* Now assemble everything together and make sure it fits on the medium */
calc_headers(lay);
/* Due to a glitch in the layout calculation code when creating the image,
lay->headerModulo is ambigous in a very few cases.
See if this one is plausible, otherwise try the neighboring modulo. */
/* Size info is available since 0.79.5, allowing us to pick
an authoritative choice. */
if(eh->sectorsAddedByEcc > 0)
{ expected_size = lay->dataSectors + eh->sectorsAddedByEcc;
Verbose("Expected size calculated from ecc header: %lld\n",
(long long int)expected_size);
if(expected_size == lay->dataSectors+lay->eccSectors)
{ Verbose("--> confirmed layout variant 1\n");
}
else
{ RS02Layout *lay2=g_malloc0(sizeof(RS02Layout));
memcpy(lay2, lay, sizeof(RS02Layout));
lay2->headerModulo <<=1;
calc_headers(lay2);
if(expected_size == lay2->dataSectors+lay2->eccSectors)
{ Verbose("--> confirmed layout variant 2\n");
memcpy(lay, lay2, sizeof(RS02Layout));
g_free(lay2);
}
else
Verbose("--> FAIL: did not map to expected variants!\n");
}
goto finish;
}
else Verbose("Pre-0.79.5 RS02 header.\n");
/* Decide heuristically. */
if(image->type == IMAGE_FILE)
{ expected_size = image->file->size>>11;
Verbose("Expected size taken from image->file: %lld\n",
(long long int)expected_size);
}
if(image->type == IMAGE_MEDIUM)
{ expected_size = image->dh->readCapacity+1;
expected_size2 = image->dh->userAreaSize+1;
Verbose("Expected size taken from image->dh: %lld/%lld\n",
(long long int)expected_size, (long long int)expected_size2);
}
if( lay->eccSectors+lay->dataSectors != expected_size
&& lay->eccSectors+lay->dataSectors != expected_size2)
{ RS02Layout *lay2=g_malloc0(sizeof(RS02Layout));
int modulo_decided = FALSE;
memcpy(lay2, lay, sizeof(RS02Layout));
lay2->headerModulo <<=1;
calc_headers(lay2);
if( lay2->eccSectors+lay2->dataSectors == expected_size
|| lay2->eccSectors+lay2->dataSectors == expected_size2)
{ memcpy(lay, lay2, sizeof(RS02Layout));
Verbose("NOTE: header modulo glitch fixed\n");
}
else /* does not match either; probe at both modulos for headers */
{ AlignedBuffer *ab = CreateAlignedBuffer(4096);
gint64 s,remainder;
gint64 max_size;
/* We do not need to search further than to the predicted
end of both possible layouts. */
expected_size = lay->eccSectors+lay->dataSectors;
expected_size2 = lay2->eccSectors+lay2->dataSectors;
max_size = MAX(expected_size, expected_size2);
/* Position of first possible header
(lay->headerModulo is less than lay2->headerModulo) */
remainder=lay->protectedSectors%lay->headerModulo;
if(!remainder)
s=lay->protectedSectors;
else s=lay->protectedSectors-remainder+lay->headerModulo;
if(!(s%lay2->headerModulo)) /* we want the smaller modulos "between" the larger ones */
s += lay->headerModulo;
Verbose("Probing from %lld to %lld, modulos %lld, %lld\n",
(long long int)s, (long long int)max_size,
(long long int)lay->headerModulo, (long long int)lay2->headerModulo);
/* Probe headers at the smaller modulo. */
while(s <= max_size)
{
if(ImageReadSectors(image, ab->buf, s, 2) != 2)
{ Verbose("... sector %lld not present\n", (long long int)s);
}
else
{
#ifdef HAVE_BIG_ENDIAN
SwapEccHeaderBytes((EccHeader*)ab->buf);
#endif
if(!memcmp(eh, (EccHeader*)ab->buf, sizeof(EccHeader)))
{ Verbose("... sector %lld is a header -> choosing modulo %lld\n",
(long long int)s, (long long int)lay->headerModulo);
modulo_decided=TRUE;
break;
}
else /* rewriteable media might contain garbage behind the actual image */
{ if(s <= expected_size2)
{ Verbose("... sector %lld is NOT a header -> choosing modulo %lld\n",
(long long int)s, (long long int)lay2->headerModulo);
memcpy(lay, lay2, sizeof(RS02Layout));
modulo_decided=TRUE;
break;
}
}
}
s += lay2->headerModulo;
}
if(!modulo_decided) /* did not resolve; continue with first modulo */
{ Verbose("NOTE: possible UNRESOLVED header modulo glitch\n");
}
FreeAlignedBuffer(ab);
}
g_free(lay2); /* either lay has won or has been copied over with lay2 */
}
/* Generate debugging output in verbose mode */
finish:
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);
Verbose("medium capacity = n.a.\n");
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");
image->cachedLayout = g_malloc(sizeof(RS02Layout));
memcpy(image->cachedLayout, lay, sizeof(RS02Layout));
return lay;
}