chore: move *.c *.h to src/, build in build/
This commit is contained in:
421
src/rs01-common.c
Normal file
421
src/rs01-common.c
Normal file
@@ -0,0 +1,421 @@
|
||||
/* 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 "rs01-includes.h"
|
||||
|
||||
/***
|
||||
*** Recognize a RS01 error correction file
|
||||
***/
|
||||
|
||||
int RS01Recognize(LargeFile *ecc_file, EccHeader **eh)
|
||||
{ int n;
|
||||
|
||||
*eh = g_malloc(sizeof(EccHeader));
|
||||
|
||||
LargeSeek(ecc_file, 0);
|
||||
n = LargeRead(ecc_file, *eh, sizeof(EccHeader));
|
||||
|
||||
if(n != sizeof(EccHeader))
|
||||
{ g_free(*eh);
|
||||
return ECCFILE_INVALID;
|
||||
}
|
||||
|
||||
if(strncmp((char*)(*eh)->cookie, "*dvdisaster*", 12))
|
||||
{ g_free(*eh);
|
||||
return ECCFILE_DEFECTIVE_HEADER;
|
||||
}
|
||||
|
||||
if(!strncmp((char*)(*eh)->method, "RS01", 4))
|
||||
{
|
||||
#ifdef HAVE_BIG_ENDIAN
|
||||
SwapEccHeaderBytes(*eh);
|
||||
#endif
|
||||
return ECCFILE_PRESENT;
|
||||
}
|
||||
|
||||
g_free(*eh);
|
||||
return ECCFILE_WRONG_CODEC;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Read and buffer CRC information from RS01 file
|
||||
***/
|
||||
|
||||
CrcBuf *RS01GetCrcBuf(Image *image)
|
||||
{ LargeFile *file = image->eccFile;
|
||||
CrcBuf *cb;
|
||||
guint32 *buf;
|
||||
guint64 image_sectors_from_ecc, image_sectors_with_crc;
|
||||
guint64 crc_sectors,crc_remainder;
|
||||
guint64 i,j,sec_idx;
|
||||
|
||||
image_sectors_from_ecc = uchar_to_gint64(image->eccFileHeader->sectors);
|
||||
cb = CreateCrcBuf(image);
|
||||
buf = cb->crcbuf;
|
||||
|
||||
/* Seek to beginning of CRC sums */
|
||||
|
||||
if(!LargeSeek(file, (gint64)sizeof(EccHeader)))
|
||||
Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
|
||||
|
||||
/* Read crc sums. A sector of 2048 bytes contains 512 CRC sums. */
|
||||
|
||||
if(cb->allSectors < image_sectors_from_ecc)
|
||||
image_sectors_with_crc = cb->allSectors; // image is truncated
|
||||
else image_sectors_with_crc = image_sectors_from_ecc; // get all CRC sectors from ECC
|
||||
crc_sectors = image_sectors_with_crc / 512;
|
||||
sec_idx = 0;
|
||||
|
||||
for(i=0; i<crc_sectors; i++)
|
||||
{ if(LargeRead(file, buf, 2048) != 2048)
|
||||
Stop(_("Error reading CRC information: %s"),strerror(errno));
|
||||
buf += 512;
|
||||
|
||||
for(j=0; j<512; j++, sec_idx++)
|
||||
SetBit(cb->valid, sec_idx);
|
||||
}
|
||||
|
||||
crc_remainder = sizeof(guint32)*(image_sectors_with_crc % 512);
|
||||
if(crc_remainder)
|
||||
{ if(LargeRead(file, buf, crc_remainder) != crc_remainder)
|
||||
Stop(_("Error reading CRC information: %s"),strerror(errno));
|
||||
|
||||
for( ; sec_idx<image_sectors_with_crc; sec_idx++)
|
||||
SetBit(cb->valid, sec_idx);
|
||||
}
|
||||
|
||||
/* Copy the md5sum */
|
||||
|
||||
memcpy(cb->dataMD5sum, image->eccFileHeader->mediumSum, 16);
|
||||
memcpy(cb->imageMD5sum, image->eccFileHeader->mediumSum, 16);
|
||||
cb->md5State = MD5_COMPLETE;
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Internal checksum handling.
|
||||
***
|
||||
* Not overly complicated as we just have a global md5sum.
|
||||
*/
|
||||
|
||||
void RS01ResetCksums(Image *image)
|
||||
{ RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
|
||||
|
||||
MD5Init(&csc->md5ctxt);
|
||||
}
|
||||
|
||||
void RS01UpdateCksums(Image *image, gint64 sector, unsigned char *buf)
|
||||
{ RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
|
||||
|
||||
MD5Update(&csc->md5ctxt, buf, 2048);
|
||||
}
|
||||
|
||||
int RS01FinalizeCksums(Image *image)
|
||||
{ Method *self = image->eccFileMethod;
|
||||
RS01CksumClosure *csc = (RS01CksumClosure*)self->ckSumClosure;
|
||||
guint8 image_fp[16];
|
||||
int good_fp;
|
||||
|
||||
MD5Final(image_fp, &csc->md5ctxt);
|
||||
|
||||
good_fp = !(memcmp(image_fp, image->eccFileHeader->mediumSum ,16));
|
||||
|
||||
if(good_fp)
|
||||
return 0;
|
||||
else return DATA_MD5_BAD;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Read an image sector from the .iso file.
|
||||
***
|
||||
* Two special cases here:
|
||||
* - Missing sectors (beyond the range recorded in eh->sectors) will be padded with zeros,
|
||||
* since we need a multiple of ndata sectors for the parity generation.
|
||||
* - Missing sectors beyond the range recorded in ii->sectors, but before the real end
|
||||
* as defined above are treated as "dead sectors".
|
||||
*/
|
||||
|
||||
void RS01ReadSector(Image *image, unsigned char *buf, gint64 s)
|
||||
{ gint64 eh_sectors = uchar_to_gint64(image->eccFileHeader->sectors);
|
||||
|
||||
if(s >= image->sectorSize && s < eh_sectors)
|
||||
{
|
||||
CreateMissingSector(buf, s, NULL, 0, NULL); /* truncated image */
|
||||
}
|
||||
else if(s >= eh_sectors)
|
||||
{
|
||||
memset(buf, 0, 2048); /* zero padding for reads past the image */
|
||||
}
|
||||
else /* else normal read within the image */
|
||||
{ int n,expected;
|
||||
|
||||
if(!LargeSeek(image->file, (gint64)(2048*s)))
|
||||
Stop(_("Failed seeking to sector %lld in image: %s"),
|
||||
s, strerror(errno));
|
||||
|
||||
/* Prepare for short reads at the last image sector.
|
||||
Doesn't happen for CD and DVD media, but perhaps for future media? */
|
||||
|
||||
if(s < image->sectorSize-1) expected = 2048;
|
||||
else
|
||||
{ memset(buf, 0, 2048);
|
||||
expected = image->inLast;
|
||||
}
|
||||
|
||||
/* Finally, read the sector */
|
||||
|
||||
n = LargeRead(image->file, buf, expected);
|
||||
if(n != expected)
|
||||
Stop(_("Failed reading sector %lld in image: %s"),s,strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan the image for missing blocks.
|
||||
* If the ecc file is present, also compare the CRC sums.
|
||||
* If CREATE_CRC is requested, calculate the CRC sums.
|
||||
*
|
||||
* Actually this should be usable for all RS01 type ecc files.
|
||||
* But unless we have more than one codec, we'll label it as
|
||||
* as RS01 specific method.
|
||||
*/
|
||||
|
||||
#define CRCBUFSIZE (1024*256)
|
||||
|
||||
void RS01ScanImage(Method *method, Image* image, struct MD5Context *ecc_ctxt, int mode)
|
||||
{
|
||||
#ifndef CLI
|
||||
RS01Widgets *wl = NULL;
|
||||
#endif
|
||||
unsigned char buf[2048];
|
||||
guint32 *crcbuf = NULL;
|
||||
int unrecoverable_sectors = 0;
|
||||
int crcidx = 0;
|
||||
struct MD5Context image_md5;
|
||||
gint64 s, first_missing, last_missing;
|
||||
#ifndef CLI
|
||||
gint64 prev_missing = 0;
|
||||
gint64 prev_crc_errors = 0;
|
||||
#endif
|
||||
int last_percent,current_missing;
|
||||
char *msg;
|
||||
|
||||
/* Extract widget list from method */
|
||||
|
||||
#ifndef CLI
|
||||
if(method->widgetList)
|
||||
wl = (RS01Widgets*)method->widgetList;
|
||||
#endif
|
||||
|
||||
/* Position behind the ecc file header,
|
||||
initialize CRC buffer pointers */
|
||||
|
||||
if(image->eccFile)
|
||||
{ if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader)))
|
||||
Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
|
||||
|
||||
crcbuf = g_malloc(sizeof(guint32) * CRCBUFSIZE);
|
||||
crcidx = (mode & CREATE_CRC) ? 0 : CRCBUFSIZE;
|
||||
if(mode & CREATE_CRC)
|
||||
MD5Init(ecc_ctxt); /* md5sum of CRC portion of ecc file */
|
||||
}
|
||||
|
||||
/* Prepare for scanning the image and calculating its md5sum */
|
||||
|
||||
MD5Init(&image_md5); /* md5sum of image file itself */
|
||||
LargeSeek(image->file, 0); /* rewind image file */
|
||||
|
||||
if(mode & PRINT_MODE)
|
||||
msg = _("- testing sectors : %3d%%");
|
||||
else msg = _("Scanning image sectors: %3d%%");
|
||||
|
||||
last_percent = 0;
|
||||
image->sectorsMissing = 0;
|
||||
first_missing = last_missing = -1;
|
||||
|
||||
/* Go through all sectors and look for the "dead sector marker" */
|
||||
|
||||
for(s=0; s<image->sectorSize; s++)
|
||||
{ int n,percent,err;
|
||||
|
||||
/* Check for user interruption */
|
||||
|
||||
#ifndef CLI
|
||||
if(Closure->stopActions)
|
||||
{ image->sectorsMissing += image->sectorSize - s;
|
||||
if(crcbuf) g_free(crcbuf);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Read the next sector */
|
||||
|
||||
n = LargeRead(image->file, buf, 2048);
|
||||
if(n != 2048)
|
||||
{ if(s != image->sectorSize - 1 || n != image->inLast)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("premature end in image (only %d bytes): %s\n"),n,strerror(errno));
|
||||
}
|
||||
else /* Zero unused sectors for CRC generation */
|
||||
memset(buf+image->inLast, 0, 2048-image->inLast);
|
||||
}
|
||||
|
||||
/* Look for the dead sector marker */
|
||||
|
||||
err = CheckForMissingSector(buf, s, image->fpState == 2 ? image->imageFP : NULL,
|
||||
FINGERPRINT_SECTOR);
|
||||
if(err != SECTOR_PRESENT)
|
||||
{ current_missing = TRUE;
|
||||
ExplainMissingSector(buf, s, err, SOURCE_IMAGE, &unrecoverable_sectors);
|
||||
}
|
||||
else current_missing = FALSE;
|
||||
|
||||
if(current_missing)
|
||||
{ if(first_missing < 0) first_missing = s;
|
||||
last_missing = s;
|
||||
image->sectorsMissing++;
|
||||
}
|
||||
|
||||
/* Report dead sectors. Combine subsequent missing sectors into one report. */
|
||||
|
||||
if(mode & PRINT_MODE)
|
||||
if(!current_missing || s==image->sectorSize-1)
|
||||
{ if(first_missing>=0)
|
||||
{ if(first_missing == last_missing)
|
||||
PrintCLI(_("* missing sector : %lld\n"), first_missing);
|
||||
else PrintCLI(_("* missing sectors : %lld - %lld\n"), first_missing, last_missing);
|
||||
first_missing = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(image->eccFile) /* Do something with the CRC portion of the .ecc file */
|
||||
{
|
||||
/* If creation of the CRC32 is requested, do that. */
|
||||
|
||||
if(mode & CREATE_CRC)
|
||||
{ crcbuf[crcidx++] = Crc32(buf, 2048);
|
||||
|
||||
if(crcidx >= CRCBUFSIZE) /* write out CRC buffer contents */
|
||||
{ size_t size = CRCBUFSIZE*sizeof(guint32);
|
||||
|
||||
MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
|
||||
if(LargeWrite(image->eccFile, crcbuf, size) != size)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("Error writing CRC information: %s"),strerror(errno));
|
||||
}
|
||||
crcidx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* else do the CRC32 check. Missing sectors are skipped in the CRC report. */
|
||||
|
||||
else if(s < image->expectedSectors)
|
||||
{ guint32 crc = Crc32(buf, 2048);
|
||||
|
||||
/* If the CRC buf is exhausted, refill. */
|
||||
|
||||
if(crcidx >= CRCBUFSIZE)
|
||||
{ size_t remain = image->sectorSize-s;
|
||||
size_t size;
|
||||
|
||||
if(remain < CRCBUFSIZE)
|
||||
size = remain*sizeof(guint32);
|
||||
else size = CRCBUFSIZE*sizeof(guint32);
|
||||
|
||||
if(LargeRead(image->eccFile, crcbuf, size) != size)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("Error reading CRC information: %s"),strerror(errno));
|
||||
}
|
||||
crcidx = 0;
|
||||
}
|
||||
|
||||
if(crc != crcbuf[crcidx++] && !current_missing)
|
||||
{ PrintCLI(_("* CRC error, sector: %lld\n"), s);
|
||||
image->crcErrors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MD5Update(&image_md5, buf, n); /* update image md5sum */
|
||||
|
||||
#ifndef CLI
|
||||
if(Closure->guiMode && mode & PRINT_MODE)
|
||||
percent = (VERIFY_IMAGE_SEGMENTS*(s+1))/image->sectorSize;
|
||||
else
|
||||
#endif
|
||||
percent = (100*(s+1))/image->sectorSize;
|
||||
if(last_percent != percent)
|
||||
{ PrintProgress(msg,percent);
|
||||
|
||||
#ifndef CLI
|
||||
if(Closure->guiMode && mode & CREATE_CRC)
|
||||
SetProgress(wl->encPBar1, percent, 100);
|
||||
|
||||
if(Closure->guiMode && mode & PRINT_MODE)
|
||||
{ RS01AddVerifyValues(method, percent, image->sectorsMissing, image->crcErrors,
|
||||
image->sectorsMissing - prev_missing,
|
||||
image->crcErrors - prev_crc_errors);
|
||||
|
||||
prev_missing = image->sectorsMissing;
|
||||
prev_crc_errors = image->crcErrors;
|
||||
}
|
||||
#endif
|
||||
|
||||
last_percent = percent;
|
||||
}
|
||||
}
|
||||
|
||||
/*** Flush the rest of the CRC buffer */
|
||||
|
||||
if((mode & CREATE_CRC) && crcidx)
|
||||
{ size_t size = crcidx*sizeof(guint32);
|
||||
|
||||
MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
|
||||
if(LargeWrite(image->eccFile, crcbuf, size) != size)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("Error writing CRC information: %s"),strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/*** The image md5sum can only be calculated if all blocks have been successfully read. */
|
||||
|
||||
MD5Final(image->mediumSum, &image_md5);
|
||||
|
||||
LargeSeek(image->file, 0);
|
||||
if(crcbuf) g_free(crcbuf);
|
||||
}
|
||||
|
||||
/***
|
||||
*** Determine expected size of image
|
||||
***/
|
||||
|
||||
guint64 RS01ExpectedImageSize(Image *image)
|
||||
{ EccHeader *eh = image->eccFileHeader;
|
||||
|
||||
if(!eh) return 0;
|
||||
|
||||
return uchar_to_gint64(eh->sectors);
|
||||
}
|
||||
Reference in New Issue
Block a user