chore: move *.c *.h to src/, build in build/

This commit is contained in:
Stéphane Lesimple
2020-09-02 20:42:43 +02:00
parent 5b82ec64bc
commit 898f2fcfb6
96 changed files with 7047 additions and 6857 deletions

67
src/bitmap.c Normal file
View File

@@ -0,0 +1,67 @@
/* 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"
/***
*** A simple bitmap structure
***/
/*
* Allocate the bitmap
*/
Bitmap* CreateBitmap0(int size)
{ Bitmap *bm = g_malloc(sizeof(Bitmap));
bm->size = size;
bm->words = (size>>5)+1;
bm->bitmap = g_malloc0(bm->words*sizeof(guint32));
return bm;
}
/*
* Free it
*/
void FreeBitmap(Bitmap *bm)
{ if(bm->bitmap)
g_free(bm->bitmap);
g_free(bm);
}
/*
* Count the '1' bits in the bitmap
*/
gint32 CountBits(Bitmap *bm)
{ gint32 i;
gint32 sum = 0;
for(i=0; i<bm->size; i++)
if(GetBit(bm, i))
sum++;
return sum;
}

28
src/build.c Normal file
View File

@@ -0,0 +1,28 @@
/* 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 "build.h"
/* build is incremented at each make;
make recompile of dependent file as fast as possible */
const char *const buildCount = BUILD;

82
src/cacheprobe.c Normal file
View File

@@ -0,0 +1,82 @@
/* 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"
#if defined(SYS_LINUX)
int ProbeCacheLineSize()
{ int cl_size = 0;
cl_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
if(!cl_size)
cl_size = sysconf(_SC_LEVEL2_CACHE_LINESIZE);
if(cl_size < 16)
cl_size = 64;
return cl_size;
}
#elif defined(SYS_FREEBSD) || defined(SYS_KFREEBSD)
#include <sys/param.h>
int ProbeCacheLineSize()
{ int cl_size = CACHE_LINE_SIZE;
/* Doing this at compile time may backfire,
but let's just hope for the best. */
if(cl_size < 16)
cl_size = 64;
return cl_size;
}
#elif defined(SYS_NETBSD)
#include <sys/param.h>
int ProbeCacheLineSize()
{ int cl_size = CACHE_LINE_SIZE;
/* Doing this at compile time may backfire,
but let's just hope for the best. */
if(cl_size < 16)
cl_size = 64;
return cl_size;
}
#else /* SYS_UNKNOWN and others. */
int ProbeCacheLineSize()
{
return 64;
}
#endif
/* TODO MINGW: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-cache_descriptor */

715
src/closure.c Normal file
View File

@@ -0,0 +1,715 @@
/* 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"
#if 0
#define Verbose g_printf
#else
#define Verbose(format, ...)
#endif
#ifdef SYS_MINGW
#include <windows.h>
/* safety margin in case we're getting UTF path names */
#define WIN_MAX_PATH (4*MAX_PATH)
/*
* Find the place of our executable
* (Windows only)
*/
static char* get_exe_path()
{ char path[WIN_MAX_PATH];
int n = GetModuleFileNameA(NULL, path, WIN_MAX_PATH);
if(n>0 && n<WIN_MAX_PATH-1)
{ char *backslash = strrchr(path, '\\');
if(backslash) *backslash=0;
return g_strdup(path);
}
return g_strdup(".");
}
#endif
/***
*** Locate the binary and documentation directory
***/
static void get_base_dirs()
{
/*** Unless completely disabled through a configure option, the
source directory is supposed to hold the most recent files,
so try this first (never under Windows). */
#if defined(WITH_EMBEDDED_SRC_PATH_YES) && !defined(SYS_MINGW)
if(DirStat(SRCDIR))
{ Closure->binDir = g_strdup(SRCDIR);
Closure->docDir = g_strdup_printf("%s/documentation",SRCDIR);
Verbose("Using paths from SRCDIR = %s\n", SRCDIR);
goto find_dotfile;
}
#endif /* WITH_EMBEDDED_SRC_PATH_YES */
/*** Otherwise try the installation directory.
On Unices this is a hardcoded directory. */
#ifndef SYS_MINGW
if(DirStat(BINDIR))
Closure->binDir = g_strdup(BINDIR);
if(DirStat(DOCDIR))
Closure->docDir = g_strdup(DOCDIR);
Verbose("Using hardcoded BINDIR = %s, DOCDIR = %s\n", BINDIR, DOCDIR);
#else
Closure->binDir = get_exe_path();
/* We'll just put the 2 PDF in the same dir, portable mode */
Closure->docDir = g_strdup(Closure->binDir);
Verbose("Using path from get_exe_path() = %s\n", Closure->binDir);
#endif
/*** The location of the dotfile depends on the operating system.
Under Unix the users home directory is used. */
#if defined(WITH_EMBEDDED_SRC_PATH_YES) && !defined(SYS_MINGW)
find_dotfile:
#endif /* WITH_EMBEDDED_SRC_PATH_YES */
#ifndef SYS_MINGW
Closure->homeDir = g_strdup(g_getenv("HOME"));
#else
Closure->homeDir = g_strdup(Closure->binDir); /* portable mode */
#endif
if(!Closure->dotFile) /* may have been set by the --resource-file option */
#ifndef SYS_MINGW
Closure->dotFile = g_strdup_printf("%s/.dvdisaster", Closure->homeDir);
#else
/* Windows doesn't really love dotfiles */
Closure->dotFile = g_strdup_printf("%s/dvdisaster.cfg", Closure->homeDir);
#endif
Verbose("\nUsing file locations:\n"
"- Homedir: %s\n"
"- Bin dir: %s\n"
"- Doc dir: %s\n"
"- dotfile: %s\n\n",
Closure->homeDir,
Closure->binDir,
Closure->docDir,
Closure->dotFile);
}
/***
*** Set/get color values
***/
/*
* Update color string for the <span color="#f00baa">...</span> string
*/
#ifndef CLI
void UpdateMarkup(char **string, GdkColor *color)
{ int hexval;
hexval = (color->red << 8) & 0xff0000;
hexval |= color->green & 0xff00;
hexval |= (color->blue >> 8) & 0xff;
if(*string) g_free(*string);
*string = g_strdup_printf("color=\"#%06x\"", hexval);
}
/*
* Default color values
*/
void DefaultColors()
{
Closure->redText->red = 0xffff;
Closure->redText->green = 0;
Closure->redText->blue = 0;
Closure->greenText->red = 0;
Closure->greenText->green = 0x8000;
Closure->greenText->blue = 0;
Closure->barColor->red = 0xffff;
Closure->barColor->green = 0;
Closure->barColor->blue = 0;
Closure->logColor->red = 0xffff;
Closure->logColor->green = 0;
Closure->logColor->blue = 0xffff;
Closure->curveColor->red = 0;
Closure->curveColor->green = 0;
Closure->curveColor->blue = 0xffff;
Closure->redSector->red = 0xffff;
Closure->redSector->green = 0;
Closure->redSector->blue = 0;
Closure->yellowSector->red = 0xffff;
Closure->yellowSector->green = 0xc000;
Closure->yellowSector->blue = 0;
Closure->greenSector->red = 0;
Closure->greenSector->green = 0xdb00;
Closure->greenSector->blue = 0;
Closure->darkSector->red = 0;
Closure->darkSector->green = 0x8000;
Closure->darkSector->blue = 0;
Closure->blueSector->red = 0;
Closure->blueSector->green = 0;
Closure->blueSector->blue = 0xffff;
Closure->whiteSector->red = 0xffff;
Closure->whiteSector->green = 0xffff;
Closure->whiteSector->blue = 0xffff;
UpdateMarkup(&Closure->redMarkup, Closure->redText);
UpdateMarkup(&Closure->greenMarkup, Closure->greenText);
}
static void save_colors(FILE *dotfile, char *symbol, GdkColor *color)
{ char *blanks=" ";
char *pad;
int len=strlen(symbol);
if(len>19) pad=blanks+19;
else pad=blanks+len;
fprintf(dotfile, "%s:%s%02x%02x%02x\n", symbol, pad,
color->red>>8, color->green>>8, color->blue>>8);
}
static void get_color(GdkColor *color, char *value)
{ unsigned int hex = strtol(value, NULL, 16);
color->red = (hex>>8)&0xff00;
color->green = hex&0xff00;
color->blue = (hex<<8)&0xff00;
}
#endif
/***
*** Save and restore user settings to/from the .dvdisaster file
***/
#define MAX_LINE_LEN 512
void ReadDotfile()
{ FILE *dotfile;
char line[MAX_LINE_LEN];
dotfile = portable_fopen(Closure->dotFile, "rb");
if(!dotfile)
return;
while(TRUE)
{ int n;
char symbol[41];
char *value;
/* Get first MAX_LINE_LEN bytes of line, discard the rest */
line[MAX_LINE_LEN-1] = 1;
if (!fgets(line, MAX_LINE_LEN, dotfile)) break;
if(!line[MAX_LINE_LEN-1]) /* line longer than buffer */
while(!feof(dotfile) && fgetc(dotfile) != '\n')
;
/* Trivially reject the line */
if(feof(dotfile)) break;
if(*line == '#') continue;
if(!sscanf(line, "%40[0-9a-zA-Z-]%n", symbol, &n)) continue;
if(line[n] != ':') continue;
/* Separate line contents into symbol: value pair */
value = line+n+1;
while(*value && *value == ' ')
value++;
if(!*value) continue;
n = strlen(value);
if(value[n-1] == '\n')
value[n-1] = 0;
/* Parse the symbols which are recognized in this version */
if(!strcmp(symbol, "last-device")) { if(Closure->device) g_free(Closure->device);
Closure->device = g_strdup(value); continue; }
if(!strcmp(symbol, "last-image")) { g_free(Closure->imageName);
if(!strcmp(value, "none"))
Closure->imageName = g_strdup("");
else Closure->imageName = g_strdup(value);
continue;
}
if(!strcmp(symbol, "last-ecc")) { g_free(Closure->eccName);
if(!strcmp(value, "none"))
Closure->eccName = g_strdup("");
else Closure->eccName = g_strdup(value);
continue;
}
if(!strcmp(symbol, "adaptive-read")) { Closure->adaptiveRead = atoi(value); continue; }
if(!strcmp(symbol, "auto-suffix")) { Closure->autoSuffix = atoi(value); continue; }
if(!strcmp(symbol, "bd-size1")) { Closure->bdSize1 = Closure->savedBDSize1 = atoll(value); continue; }
if(!strcmp(symbol, "bd-size2")) { Closure->bdSize2 = Closure->savedBDSize2 = atoll(value); continue; }
if(!strcmp(symbol, "bd-size3")) { Closure->bdSize3 = Closure->savedBDSize3 = atoll(value); continue; }
if(!strcmp(symbol, "bd-size4")) { Closure->bdSize4 = Closure->savedBDSize4 = atoll(value); continue; }
if(!strcmp(symbol, "cache-size")) { Closure->cacheMiB = atoi(value); continue; }
if(!strcmp(symbol, "cd-size")) { Closure->cdSize = Closure->savedCDSize = atoll(value); continue; }
if(!strcmp(symbol, "codec-threads")) { Closure->codecThreads = atoi(value); continue; }
if(!strcmp(symbol, "confirm-deletion")){ Closure->confirmDeletion = atoi(value); continue; }
if(!strcmp(symbol, "dao")) { Closure->noTruncate = atoi(value); continue; }
if(!strcmp(symbol, "defective-dump")) { Closure->defectiveDump = atoi(value); continue; }
if(!strcmp(symbol, "defective-dir")) { if(Closure->dDumpDir) g_free(Closure->dDumpDir);
Closure->dDumpDir = g_strdup(value); continue; }
if(!strcmp(symbol, "defective-prefix")){ if(Closure->dDumpPrefix) g_free(Closure->dDumpPrefix);
Closure->dDumpPrefix = g_strdup(value); continue; }
if(!strcmp(symbol, "dotfile-version")) { Closure->dotFileVersion = atoi(value); continue; }
if(!strcmp(symbol, "dvd-size1")) { Closure->dvdSize1 = Closure->savedDVDSize1 = atoll(value); continue; }
if(!strcmp(symbol, "dvd-size2")) { Closure->dvdSize2 = Closure->savedDVDSize2 = atoll(value); continue; }
if(!strcmp(symbol, "ecc-target")) { Closure->eccTarget = atoi(value); continue; }
if(!strcmp(symbol, "eject")) { Closure->eject = atoi(value); continue; }
if(!strcmp(symbol, "encoding-algorithm")) { Closure->encodingAlgorithm = atoi(value); continue; }
if(!strcmp(symbol, "encoding-io-strategy")) { Closure->encodingIOStrategy = atoi(value); continue; }
if(!strcmp(symbol, "examine-rs02")) { Closure->examineRS02 = atoi(value); continue; }
if(!strcmp(symbol, "examine-rs03")) { Closure->examineRS03 = atoi(value); continue; }
if(!strcmp(symbol, "fill-unreadable")) { Closure->fillUnreadable = atoi(value); continue; }
if(!strcmp(symbol, "ignore-fatal-sense")) { Closure->ignoreFatalSense = atoi(value); continue; }
if(!strcmp(symbol, "ignore-iso-size")) { Closure->ignoreIsoSize = atoi(value); continue; }
if(!strcmp(symbol, "internal-attempts")) { Closure->internalAttempts = atoi(value); continue; }
if(!strcmp(symbol, "jump")) { Closure->sectorSkip = atoi(value); continue; }
if(!strcmp(symbol, "log-file-enabled")){ Closure->logFileEnabled = atoi(value); continue; }
if(!strcmp(symbol, "log-file")) { if(Closure->logFile) g_free(Closure->logFile);
Closure->logFile = g_strdup(value); continue; }
if(!strcmp(symbol, "medium-size")) { Closure->mediumSize = atoll(value); continue; }
if(!strcmp(symbol, "method-name")) { if(Closure->methodName) g_free(Closure->methodName);
Closure->methodName = g_strdup(value); continue; }
if(!strcmp(symbol, "max-read-attempts")) { Closure->maxReadAttempts = atoi(value); continue; }
if(!strcmp(symbol, "min-read-attempts")) { Closure->minReadAttempts = atoi(value); continue; }
if(!strcmp(symbol, "old-missing-sector-marker")) { Closure->dsmVersion = !atoi(value); continue; }
if(!strcmp(symbol, "pdf-viewer")) { g_free(Closure->viewer);
Closure->viewer = g_strdup(value); continue; }
if(!strcmp(symbol, "prefetch-sectors")){ Closure->prefetchSectors = atoi(value); continue; }
if(!strcmp(symbol, "raw-mode")) { Closure->rawMode = atoi(value); continue; }
if(!strcmp(symbol, "read-and-create")) { Closure->readAndCreate = atoi(value); continue; }
if(!strcmp(symbol, "read-medium")) { Closure->readingPasses = atoi(value); continue; }
if(!strcmp(symbol, "read-raw")) { Closure->readRaw = atoi(value); continue; }
if(!strcmp(symbol, "redundancy")) { if(Closure->redundancy) g_free(Closure->redundancy);
Closure->redundancy = g_strdup(value); continue; }
if(!strcmp(symbol, "reverse-cancel-ok")) { Closure->reverseCancelOK = atoi(value); continue; }
if(!strcmp(symbol, "spinup-delay")) { Closure->spinupDelay = atoi(value); continue; }
if(!strcmp(symbol, "unlink")) { Closure->unlinkImage = atoi(value); continue; }
if(!strcmp(symbol, "verbose")) { Closure->verbose = atoi(value); continue; }
if(!strcmp(symbol, "welcome-msg")) { Closure->welcomeMessage = atoi(value); continue; }
#ifndef CLI
if(!strcmp(symbol, "positive-text")) { get_color(Closure->greenText, value);
UpdateMarkup(&Closure->greenMarkup, Closure->greenText);
continue;
}
if(!strcmp(symbol, "negative-text")) { get_color(Closure->redText, value);
UpdateMarkup(&Closure->redMarkup, Closure->redText);
continue;
}
if(!strcmp(symbol, "bar-color")) { get_color(Closure->barColor, value); continue; }
if(!strcmp(symbol, "log-color")) { get_color(Closure->logColor, value); continue; }
if(!strcmp(symbol, "curve-color")) { get_color(Closure->curveColor, value); continue; }
if(!strcmp(symbol, "defective-sector")){ get_color(Closure->redSector, value); continue; }
if(!strcmp(symbol, "bad-checksum-sector")){ get_color(Closure->yellowSector, value); continue; }
if(!strcmp(symbol, "good-sector")) { get_color(Closure->greenSector, value); continue; }
if(!strcmp(symbol, "ignored-sector")) { get_color(Closure->blueSector, value); continue; }
if(!strcmp(symbol, "highlit-sector")) { get_color(Closure->whiteSector, value); continue; }
if(!strcmp(symbol, "present-sector")) { get_color(Closure->darkSector, value); continue; }
#endif
}
if(fclose(dotfile))
g_printf("Error closing configuration file %s: %s\n",
Closure->dotFile, strerror(errno));
}
static void update_dotfile()
{ const char *no_dot_files;
FILE *dotfile;
/*** If the environment $NO_DOT_FILES is set,
do not alter the dotfile. */
no_dot_files = g_getenv("NO_DOT_FILES");
if(no_dot_files && atoi(no_dot_files))
return;
/*** Otherwise, save our session */
dotfile = portable_fopen(Closure->dotFile, "wb");
if(!dotfile)
{ g_printf("Could not open configuration file %s: %s\n",
Closure->dotFile, strerror(errno));
return;
}
g_fprintf(dotfile,
_("# dvdisaster-%s configuration file\n"
"# This is an automatically generated file\n"
"# which will be overwritten each time dvdisaster is run.\n\n"),
VERSION);
g_fprintf(dotfile, "last-device: %s\n", Closure->device);
g_fprintf(dotfile, "last-image: %s\n", Closure->imageName);
g_fprintf(dotfile, "last-ecc: %s\n\n", Closure->eccName);
g_fprintf(dotfile, "adaptive-read: %d\n", Closure->adaptiveRead);
g_fprintf(dotfile, "auto-suffix: %d\n", Closure->autoSuffix);
g_fprintf(dotfile, "bd-size1: %lld\n", (long long int)Closure->bdSize1);
g_fprintf(dotfile, "bd-size2: %lld\n", (long long int)Closure->bdSize2);
g_fprintf(dotfile, "bd-size3: %lld\n", (long long int)Closure->bdSize3);
g_fprintf(dotfile, "bd-size4: %lld\n", (long long int)Closure->bdSize4);
g_fprintf(dotfile, "cache-size: %d\n", Closure->cacheMiB);
g_fprintf(dotfile, "cd-size: %lld\n", (long long int)Closure->cdSize);
g_fprintf(dotfile, "codec-threads: %d\n", Closure->codecThreads);
g_fprintf(dotfile, "confirm-deletion: %d\n", Closure->confirmDeletion);
g_fprintf(dotfile, "dao: %d\n", Closure->noTruncate);
g_fprintf(dotfile, "defective-dump: %d\n", Closure->defectiveDump);
g_fprintf(dotfile, "defective-dir: %s\n", Closure->dDumpDir);
g_fprintf(dotfile, "defective-prefix: %s\n", Closure->dDumpPrefix);
g_fprintf(dotfile, "dotfile-version: %d\n", Closure->dotFileVersion);
g_fprintf(dotfile, "dvd-size1: %lld\n", (long long int)Closure->dvdSize1);
g_fprintf(dotfile, "dvd-size2: %lld\n", (long long int)Closure->dvdSize2);
g_fprintf(dotfile, "ecc-target: %d\n", Closure->eccTarget);
g_fprintf(dotfile, "eject: %d\n", Closure->eject);
g_fprintf(dotfile, "encoding-algorithm:%d\n", Closure->encodingAlgorithm);
g_fprintf(dotfile, "encoding-io-strategy:%d\n", Closure->encodingIOStrategy);
g_fprintf(dotfile, "examine-rs02: %d\n", Closure->examineRS02);
g_fprintf(dotfile, "examine-rs03: %d\n", Closure->examineRS03);
g_fprintf(dotfile, "fill-unreadable: %d\n", Closure->fillUnreadable);
g_fprintf(dotfile, "ignore-fatal-sense: %d\n", Closure->ignoreFatalSense);
g_fprintf(dotfile, "ignore-iso-size: %d\n", Closure->ignoreIsoSize);
g_fprintf(dotfile, "internal-attempts: %d\n", Closure->internalAttempts);
g_fprintf(dotfile, "jump: %d\n", Closure->sectorSkip);
g_fprintf(dotfile, "log-file-enabled: %d\n", Closure->logFileEnabled);
g_fprintf(dotfile, "log-file: %s\n", Closure->logFile);
g_fprintf(dotfile, "medium-size: %lld\n", (long long int)Closure->mediumSize);
g_fprintf(dotfile, "method-name: %s\n", Closure->methodName);
g_fprintf(dotfile, "max-read-attempts: %d\n", Closure->maxReadAttempts);
g_fprintf(dotfile, "min-read-attempts: %d\n", Closure->minReadAttempts);
g_fprintf(dotfile, "old-missing-sector-marker: %d\n", !Closure->dsmVersion);
g_fprintf(dotfile, "pdf-viewer: %s\n", Closure->viewer);
g_fprintf(dotfile, "prefetch-sectors: %d\n", Closure->prefetchSectors);
g_fprintf(dotfile, "raw-mode: %d\n", Closure->rawMode);
g_fprintf(dotfile, "read-and-create: %d\n", Closure->readAndCreate);
g_fprintf(dotfile, "read-medium: %d\n", Closure->readingPasses);
g_fprintf(dotfile, "read-raw: %d\n", Closure->readRaw);
if(Closure->redundancy)
g_fprintf(dotfile, "redundancy: %s\n", Closure->redundancy);
g_fprintf(dotfile, "reverse-cancel-ok: %d\n", Closure->reverseCancelOK);
g_fprintf(dotfile, "spinup-delay: %d\n", Closure->spinupDelay);
g_fprintf(dotfile, "unlink: %d\n", Closure->unlinkImage);
g_fprintf(dotfile, "verbose: %d\n", Closure->verbose);
g_fprintf(dotfile, "welcome-msg: %d\n\n", Closure->welcomeMessage);
#ifndef CLI
save_colors(dotfile, "positive-text", Closure->greenText);
save_colors(dotfile, "negative-text", Closure->redText);
save_colors(dotfile, "bar-color", Closure->barColor);
save_colors(dotfile, "log-color", Closure->logColor);
save_colors(dotfile, "curve-color", Closure->curveColor);
save_colors(dotfile, "defective-sector", Closure->redSector);
save_colors(dotfile, "bad-checksum-sector",Closure->yellowSector);
save_colors(dotfile, "good-sector", Closure->greenSector);
save_colors(dotfile, "ignored-sector", Closure->blueSector);
save_colors(dotfile, "highlit-sector", Closure->whiteSector);
save_colors(dotfile, "present-sector", Closure->darkSector);
#endif
if(fclose(dotfile))
g_printf("Error closing configuration file %s: %s\n",
Closure->dotFile, strerror(errno));
}
/***
*** Allocate and initialize our global variables
***/
GlobalClosure *Closure;
int exitCode = EXIT_SUCCESS;
void InitClosure()
{ int v1,v2,v3,dots=0;
char *v,version[strlen(VERSION)+1];
Closure = g_malloc0(sizeof(GlobalClosure));
/* Extract the version string */
#if defined(HAVE_UNSTABLE_RELEASE) && defined(PATCHLEVEL)
Closure->cookedVersion = g_strdup_printf("%s (unstable-unofficial patchlevel %d)", VERSION, PATCHLEVEL);
Closure->releaseFlags = MFLAG_DEVEL;
#elif defined(HAVE_UNSTABLE_RELEASE)
Closure->cookedVersion = g_strdup_printf("%s (unstable)", VERSION);
Closure->releaseFlags = MFLAG_DEVEL;
#else
Closure->cookedVersion = g_strdup(VERSION);
#endif
/* Generate a more comprehensive version string */
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
#ifdef HAVE_64BIT
#define BITNESS_STRING " 64bit"
#else
#define BITNESS_STRING " 32bit"
#endif
#else
#define BITNESS_STRING ""
#endif
Closure->versionString = g_strdup_printf("dvdisaster %s build %s, %s%s",
Closure->cookedVersion, buildCount, SYS_NAME, BITNESS_STRING);
/* Replace the dot with a locale-resistant separator */
strcpy(version,VERSION);
for(v=version; *v; v++)
if(*v=='.')
{ *v='x';
dots++;
}
if(dots == 2)
{ v1 = v2 = v3 = 0;
sscanf(version,"%dx%dx%d",&v1,&v2,&v3);
}
else
{ g_printf("Error: malformed version number %s\n",VERSION);
exit(EXIT_FAILURE);
}
Closure->version = 10000*v1 + 100*v2 + v3;
/* Get home and system directories */
get_base_dirs();
/* Fill in other closure defaults */
Closure->deviceNames = g_ptr_array_new();
Closure->deviceNodes = g_ptr_array_new();
Closure->viewer = g_strdup("xdg-open");
Closure->browser = g_strdup("xdg-open");
Closure->methodList = g_ptr_array_new();
Closure->methodName = g_strdup("RS01");
Closure->dDumpDir = g_strdup(Closure->homeDir);
Closure->cacheMiB = 32;
Closure->prefetchSectors = 128;
Closure->codecThreads = 1;
Closure->eccTarget = 1;
Closure->encodingAlgorithm = ENCODING_ALG_DEFAULT;
Closure->minReadAttempts = 1;
Closure->maxReadAttempts = 1;
Closure->rawMode = 0x20;
Closure->internalAttempts = -1;
Closure->sectorSkip = 16;
Closure->spinupDelay = 5;
Closure->fillUnreadable = -1;
Closure->welcomeMessage = 1;
Closure->useSCSIDriver = DRIVER_SG;
Closure->dsmVersion = 1;
Closure->noBdrDefectManagement = FALSE;
Closure->ignoreRS03header = FALSE;
/* default sizes for typical CD and DVD media */
Closure->cdSize = Closure->savedCDSize = CDR_SIZE;
Closure->dvdSize1 = Closure->savedDVDSize1 = DVD_SL_SIZE;
Closure->dvdSize2 = Closure->savedDVDSize2 = DVD_DL_SIZE;
Closure->bdSize1 = Closure->savedBDSize1 = BD_SL_SIZE;
Closure->bdSize2 = Closure->savedBDSize2 = BD_DL_SIZE;
Closure->bdSize3 = Closure->savedBDSize3 = BDXL_TL_SIZE;
Closure->bdSize4 = Closure->savedBDSize4 = BDXL_QL_SIZE;
#ifndef CLI
Closure->logString = g_string_sized_new(1024);
Closure->logLock = g_malloc0(sizeof(GMutex));
g_mutex_init(Closure->logLock);
Closure->background = g_malloc0(sizeof(GdkColor));
Closure->foreground = g_malloc0(sizeof(GdkColor));
Closure->grid = g_malloc0(sizeof(GdkColor));
Closure->redText = g_malloc0(sizeof(GdkColor));
Closure->greenText = g_malloc0(sizeof(GdkColor));
Closure->barColor = g_malloc0(sizeof(GdkColor));
Closure->logColor = g_malloc0(sizeof(GdkColor));
Closure->curveColor = g_malloc0(sizeof(GdkColor));
Closure->redSector = g_malloc0(sizeof(GdkColor));
Closure->yellowSector= g_malloc0(sizeof(GdkColor));
Closure->greenSector = g_malloc0(sizeof(GdkColor));
Closure->blueSector = g_malloc0(sizeof(GdkColor));
Closure->whiteSector = g_malloc0(sizeof(GdkColor));
Closure->darkSector = g_malloc0(sizeof(GdkColor));
DefaultColors();
#endif
memset(Closure->bs, '\b', 255);
memset(Closure->sp, ' ', 255);
DefaultLogFile();
}
/*
* Add some localized file name defaults.
* Can't do this in InitClosure() as the locale has not been
* initialized when it is being called.
*/
void LocalizedFileDefaults()
{
/* Storing the files in the cwd appears to be a sane default. */
Closure->imageName = g_strdup(_("medium.iso"));
Closure->eccName = g_strdup(_("medium.ecc"));
Closure->dDumpPrefix = g_strdup(_("sector-"));
}
/*
* Clean up properly
*/
#define cond_free(x) if(x) g_free(x)
/* Doing a simple g_ptr_array_free(a, TRUE)
would confuse our memory leak checker */
void cond_free_ptr_array(GPtrArray *a)
{ unsigned int i;
if(!a) return;
for(i=0; i<a->len; i++)
g_free(g_ptr_array_index(a,i));
g_ptr_array_free(a, FALSE);
}
void FreeClosure()
{
#ifndef CLI
if(Closure->guiMode)
#endif
/* in CLI-only mode, always update dotfile */
update_dotfile();
cond_free(Closure->cookedVersion);
cond_free(Closure->versionString);
cond_free(Closure->device);
cond_free_ptr_array(Closure->deviceNames);
cond_free_ptr_array(Closure->deviceNodes);
cond_free(Closure->imageName);
cond_free(Closure->eccName);
cond_free(Closure->redundancy);
CallMethodDestructors();
cond_free_ptr_array(Closure->methodList);
cond_free(Closure->methodName);
cond_free(Closure->homeDir);
cond_free(Closure->dotFile);
cond_free(Closure->logFile);
cond_free(Closure->binDir);
cond_free(Closure->docDir);
cond_free(Closure->viewer);
cond_free(Closure->browser);
cond_free(Closure->errorTitle);
cond_free(Closure->simulateCD);
cond_free(Closure->dDumpDir);
cond_free(Closure->dDumpPrefix);
#ifndef CLI
if(Closure->prefsContext)
FreePreferences(Closure->prefsContext);
if(Closure->rawEditorContext)
FreeRawEditorContext(Closure->rawEditorContext);
if(Closure->logString)
g_string_free(Closure->logString, TRUE);
if(Closure->logLock)
{ g_mutex_clear(Closure->logLock);
g_free(Closure->logLock);
}
if(Closure->drawGC)
g_object_unref(Closure->drawGC);
cond_free(Closure->background);
cond_free(Closure->foreground);
cond_free(Closure->grid);
cond_free(Closure->redText);
cond_free(Closure->greenText);
cond_free(Closure->barColor);
cond_free(Closure->logColor);
cond_free(Closure->curveColor);
cond_free(Closure->redSector);
cond_free(Closure->yellowSector);
cond_free(Closure->greenSector);
cond_free(Closure->blueSector);
cond_free(Closure->whiteSector);
cond_free(Closure->darkSector);
cond_free(Closure->redMarkup);
cond_free(Closure->greenMarkup);
cond_free(Closure->invisibleDash);
if(Closure->readLinearCurve)
FreeCurve(Closure->readLinearCurve);
if(Closure->readLinearSpiral)
FreeSpiral(Closure->readLinearSpiral);
if(Closure->readAdaptiveSpiral)
FreeSpiral(Closure->readAdaptiveSpiral);
if(Closure->readAdaptiveSubtitle)
g_free(Closure->readAdaptiveSubtitle);
if(Closure->readAdaptiveErrorMsg)
g_free(Closure->readAdaptiveErrorMsg);
#endif
g_free(Closure);
}

247
src/crc32.c Normal file
View File

@@ -0,0 +1,247 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2017 Carsten Gnoerlich.
* CRC32 code based upon public domain code by Ross Williams (see notes below)
*
* 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"
/***
*** Crc32 used in the dvdisaster error correction data
***/
/*****************************************************************/
/* */
/* CRC LOOKUP TABLE */
/* ================ */
/* The following CRC lookup table was generated automagically */
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
/* Program V1.0 using the following model parameters: */
/* */
/* Width : 4 bytes. */
/* Poly : 0x04C11DB7L */
/* Reverse : TRUE. */
/* */
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
/* see the document titled "A Painless Guide to CRC Error */
/* Detection Algorithms" by Ross Williams */
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
/* */
/*****************************************************************/
static guint32 crctable[256] =
{
0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
};
/*
* The table-based CRC32 algorithm
*
* Note that endianess does not matter for the internal calculations,
* but the final CRC sum will be returned in little endian format
* so that comparing against the sums in the ecc file does not need
* to be endian-aware.
*/
guint32 Crc32(unsigned char *data, int len)
{ guint32 crc = ~0;
while(len--)
crc = crctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
#ifdef HAVE_BIG_ENDIAN
crc = SwapBytes32(crc);
#endif
return crc;
}
/***
*** EDC checksum used in CDROM sectors
***/
/*****************************************************************/
/* */
/* CRC LOOKUP TABLE */
/* ================ */
/* The following CRC lookup table was generated automagically */
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
/* Program V1.0 using the following model parameters: */
/* */
/* Width : 4 bytes. */
/* Poly : 0x8001801BL */
/* Reverse : TRUE. */
/* */
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
/* see the document titled "A Painless Guide to CRC Error */
/* Detection Algorithms" by Ross Williams */
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
/* */
/*****************************************************************/
unsigned long edctable[256] =
{
0x00000000L, 0x90910101L, 0x91210201L, 0x01B00300L,
0x92410401L, 0x02D00500L, 0x03600600L, 0x93F10701L,
0x94810801L, 0x04100900L, 0x05A00A00L, 0x95310B01L,
0x06C00C00L, 0x96510D01L, 0x97E10E01L, 0x07700F00L,
0x99011001L, 0x09901100L, 0x08201200L, 0x98B11301L,
0x0B401400L, 0x9BD11501L, 0x9A611601L, 0x0AF01700L,
0x0D801800L, 0x9D111901L, 0x9CA11A01L, 0x0C301B00L,
0x9FC11C01L, 0x0F501D00L, 0x0EE01E00L, 0x9E711F01L,
0x82012001L, 0x12902100L, 0x13202200L, 0x83B12301L,
0x10402400L, 0x80D12501L, 0x81612601L, 0x11F02700L,
0x16802800L, 0x86112901L, 0x87A12A01L, 0x17302B00L,
0x84C12C01L, 0x14502D00L, 0x15E02E00L, 0x85712F01L,
0x1B003000L, 0x8B913101L, 0x8A213201L, 0x1AB03300L,
0x89413401L, 0x19D03500L, 0x18603600L, 0x88F13701L,
0x8F813801L, 0x1F103900L, 0x1EA03A00L, 0x8E313B01L,
0x1DC03C00L, 0x8D513D01L, 0x8CE13E01L, 0x1C703F00L,
0xB4014001L, 0x24904100L, 0x25204200L, 0xB5B14301L,
0x26404400L, 0xB6D14501L, 0xB7614601L, 0x27F04700L,
0x20804800L, 0xB0114901L, 0xB1A14A01L, 0x21304B00L,
0xB2C14C01L, 0x22504D00L, 0x23E04E00L, 0xB3714F01L,
0x2D005000L, 0xBD915101L, 0xBC215201L, 0x2CB05300L,
0xBF415401L, 0x2FD05500L, 0x2E605600L, 0xBEF15701L,
0xB9815801L, 0x29105900L, 0x28A05A00L, 0xB8315B01L,
0x2BC05C00L, 0xBB515D01L, 0xBAE15E01L, 0x2A705F00L,
0x36006000L, 0xA6916101L, 0xA7216201L, 0x37B06300L,
0xA4416401L, 0x34D06500L, 0x35606600L, 0xA5F16701L,
0xA2816801L, 0x32106900L, 0x33A06A00L, 0xA3316B01L,
0x30C06C00L, 0xA0516D01L, 0xA1E16E01L, 0x31706F00L,
0xAF017001L, 0x3F907100L, 0x3E207200L, 0xAEB17301L,
0x3D407400L, 0xADD17501L, 0xAC617601L, 0x3CF07700L,
0x3B807800L, 0xAB117901L, 0xAAA17A01L, 0x3A307B00L,
0xA9C17C01L, 0x39507D00L, 0x38E07E00L, 0xA8717F01L,
0xD8018001L, 0x48908100L, 0x49208200L, 0xD9B18301L,
0x4A408400L, 0xDAD18501L, 0xDB618601L, 0x4BF08700L,
0x4C808800L, 0xDC118901L, 0xDDA18A01L, 0x4D308B00L,
0xDEC18C01L, 0x4E508D00L, 0x4FE08E00L, 0xDF718F01L,
0x41009000L, 0xD1919101L, 0xD0219201L, 0x40B09300L,
0xD3419401L, 0x43D09500L, 0x42609600L, 0xD2F19701L,
0xD5819801L, 0x45109900L, 0x44A09A00L, 0xD4319B01L,
0x47C09C00L, 0xD7519D01L, 0xD6E19E01L, 0x46709F00L,
0x5A00A000L, 0xCA91A101L, 0xCB21A201L, 0x5BB0A300L,
0xC841A401L, 0x58D0A500L, 0x5960A600L, 0xC9F1A701L,
0xCE81A801L, 0x5E10A900L, 0x5FA0AA00L, 0xCF31AB01L,
0x5CC0AC00L, 0xCC51AD01L, 0xCDE1AE01L, 0x5D70AF00L,
0xC301B001L, 0x5390B100L, 0x5220B200L, 0xC2B1B301L,
0x5140B400L, 0xC1D1B501L, 0xC061B601L, 0x50F0B700L,
0x5780B800L, 0xC711B901L, 0xC6A1BA01L, 0x5630BB00L,
0xC5C1BC01L, 0x5550BD00L, 0x54E0BE00L, 0xC471BF01L,
0x6C00C000L, 0xFC91C101L, 0xFD21C201L, 0x6DB0C300L,
0xFE41C401L, 0x6ED0C500L, 0x6F60C600L, 0xFFF1C701L,
0xF881C801L, 0x6810C900L, 0x69A0CA00L, 0xF931CB01L,
0x6AC0CC00L, 0xFA51CD01L, 0xFBE1CE01L, 0x6B70CF00L,
0xF501D001L, 0x6590D100L, 0x6420D200L, 0xF4B1D301L,
0x6740D400L, 0xF7D1D501L, 0xF661D601L, 0x66F0D700L,
0x6180D800L, 0xF111D901L, 0xF0A1DA01L, 0x6030DB00L,
0xF3C1DC01L, 0x6350DD00L, 0x62E0DE00L, 0xF271DF01L,
0xEE01E001L, 0x7E90E100L, 0x7F20E200L, 0xEFB1E301L,
0x7C40E400L, 0xECD1E501L, 0xED61E601L, 0x7DF0E700L,
0x7A80E800L, 0xEA11E901L, 0xEBA1EA01L, 0x7B30EB00L,
0xE8C1EC01L, 0x7850ED00L, 0x79E0EE00L, 0xE971EF01L,
0x7700F000L, 0xE791F101L, 0xE621F201L, 0x76B0F300L,
0xE541F401L, 0x75D0F500L, 0x7460F600L, 0xE4F1F701L,
0xE381F801L, 0x7310F900L, 0x72A0FA00L, 0xE231FB01L,
0x71C0FC00L, 0xE151FD01L, 0xE0E1FE01L, 0x7070FF00L
};
/*
* CDROM EDC calculation
*/
guint32 EDCCrc32(unsigned char *data, int len)
{ guint32 crc = 0;
while(len--)
crc = edctable[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
#ifdef HAVE_BIG_ENDIAN
crc = SwapBytes32(crc);
#endif
return crc;
}

262
src/crcbuf.c Normal file
View File

@@ -0,0 +1,262 @@
/* 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 "scsi-layer.h"
#include "rs02-includes.h"
/***
*** Create a CRC buffer ready for accumulating CRC and MD5 sums
***/
CrcBuf *CreateCrcBuf(Image *image)
{ CrcBuf *cb = g_malloc0(sizeof(CrcBuf));
/* MD5 sum related data */
cb->md5Ctxt = g_malloc(sizeof(struct MD5Context));
MD5Init(cb->md5Ctxt);
cb->md5State = MD5_BUILDING;
cb->lastSector = 0;
memset(cb->dataMD5sum, 0, 16);
memset(cb->imageMD5sum, 0, 16);
/* image identification */
if(image->type == IMAGE_FILE)
cb->imageName = g_strdup(image->file->path);
/* For augmented images we need both the pure data size and the full size
in order to compute MD5 sums for both. Depending on what the user wants
to do with the image (create an ecc file or re-augment the image) we'll
need the first or the last version.
There's one catch, though: If the augmented image is accompanied with
an ecc file, we must treat the image as raw data; e.g. do not set
the data size to the portion protected by the augmented ecc.
Otherwise, the CRC sums from the ecc file which do also protect
the augmented ecc part will become unreachable. */
if(image->eccHeader && !image->eccFileHeader) /* augmented image, and no ecc file? */
{ cb->dataSectors = uchar_to_gint64(image->eccHeader->sectors);
if(image->type == IMAGE_FILE)
cb->allSectors = image->sectorSize;
else cb->allSectors = image->dh->sectors;
}
else
{ if(image->type == IMAGE_FILE)
cb->dataSectors = cb->allSectors = image->sectorSize;
else cb->dataSectors = cb->allSectors = image->dh->sectors;
}
/* Note: The following statement is not correct for RS03.
It does not hurt since RS03 will set the correct value when
the CrcBuf is created via RS03GetCrcBuf(), and in all other
cases it does currently not matter. Especially, CRC sums
created during an image read are not used when subsequently
creating new ecc data. */
cb->coveredSectors = cb->dataSectors;
/* Extract the fingerprint */
if(image->fpState == FP_PRESENT)
{ memcpy(cb->mediumFP, image->imageFP, 16);
cb->fpValid = TRUE;
}
cb->fpSector = image->fpSector;
/* CRC sum array */
cb->crcbuf = g_malloc(cb->allSectors * sizeof(guint32));
cb->crcSize = cb->allSectors;
cb->valid = CreateBitmap0(cb->allSectors);
image->crcCache = cb;
return cb;
}
/*
* Add a 2048 byte block into the checksum buffer
*/
int AddSectorToCrcBuffer(CrcBuf *cb, int mode, guint64 idx, unsigned char *buf, int buf_size)
{ guint32 crc;
if(idx < 0 || idx >= cb->crcSize)
return CRC_OUTSIDE_BOUND;
/* Update the CRC sums */
if( (mode & CRCBUF_UPDATE_CRC)
|| ((mode & CRCBUF_UPDATE_CRC_AFTER_DATA) && idx >= cb->coveredSectors))
{ crc = Crc32(buf, 2048); /* should be buf_size, but remains at 2048 for backwards compatibility. */
cb->crcbuf[idx] = crc; /* does not harm except that the last sector is padded with the contents */
SetBit(cb->valid, idx); /* of the previous sector when reading an image file whole size is not */
} /* a multiple of 2048 */
/* Update the MD5 sums */
if(!(mode & CRCBUF_UPDATE_MD5))
return CRC_GOOD;
if(cb->lastSector != idx) /* sector out of order -> md5sum dead */
{ cb->md5State = MD5_INVALID;
return CRC_BAD;
}
cb->lastSector++;
if(idx <= cb->allSectors-1)
MD5Update(cb->md5Ctxt, buf, buf_size);
if(idx == cb->dataSectors-1)
{ MD5Context *dataCtxt = alloca(sizeof(MD5Context));
memcpy(dataCtxt, cb->md5Ctxt, sizeof(MD5Context));
MD5Final(cb->dataMD5sum, dataCtxt);
cb->md5State |= MD5_DATA_COMPLETE;
}
if(idx == cb->allSectors-1)
{ MD5Final(cb->imageMD5sum, cb->md5Ctxt);
cb->md5State |= MD5_IMAGE_COMPLETE;
cb->md5State &= ~MD5_BUILDING;
}
return CRC_GOOD;
}
/*
* Test a 2048 byte block against the checksum in the buffer
*/
int CheckAgainstCrcBuffer(CrcBuf *cb, gint64 idx, unsigned char *buf)
{ guint32 crc;
if(idx < 0 || idx >= cb->crcSize)
return CRC_OUTSIDE_BOUND;
crc = Crc32(buf, 2048);
if(!GetBit(cb->valid, idx))
return CRC_UNKNOWN;
if(crc == cb->crcbuf[idx])
return CRC_GOOD;
return CRC_BAD;
}
/*
* Make sure that the current image and ecc file match the
* cached CRC and md5 informaton
*/
int CrcBufValid(CrcBuf *crcbuf, Image *image, EccHeader *eh)
{
if(!crcbuf)
{ Verbose("CrcBufValid: crcbuf==NULL\n");
return FALSE;
}
/* if still in building state we do not have all CRC sums */
if(crcbuf->md5State & MD5_BUILDING)
{ Verbose("CrcBufValid: NO, still building\n");
return FALSE;
}
/* presence of one MD5 sum suffices (data md5sum may not be present
under some circumstances) */
if((!(crcbuf->md5State & MD5_COMPLETE)))
{ Verbose("CrcBufValid: NOT complete\n");
return FALSE;
}
Verbose("CrcBufValid: buffer VALID\n");
return TRUE;
}
/***
*** Clean up
***/
void FreeCrcBuf(CrcBuf *cb)
{
if(!cb)
{ Verbose("FreeCrcBuf - nothing to do\n");
return;
}
g_free(cb->crcbuf);
FreeBitmap(cb->valid);
if(cb->imageName)
g_free(cb->imageName);
if(cb->md5Ctxt)
g_free(cb->md5Ctxt);
g_free(cb);
Verbose("FreeCrcBuf - buffer cleared\n");
}
/***
*** Debugging output
***/
void PrintCrcBuf(CrcBuf *cb)
{ char digest[33];
guint64 i,missing=0;
if(!Closure->verbose)
return;
PrintLog("CrcBuf contents, image path %s:\n", cb->imageName ? cb->imageName : "none (medium)" );
PrintLog(" crcSize: %lld, dataSectors: %lld, coveredSectors: %lld, allSectors: %lld\n",
cb->crcSize, cb->dataSectors, cb->coveredSectors, cb->allSectors);
PrintLog(" md5State:");
if(cb->md5State)
{ if(cb->md5State & MD5_BUILDING) PrintLog(" building");
if(cb->md5State & MD5_DATA_COMPLETE) PrintLog(" data_complete");
if(cb->md5State & MD5_IMAGE_COMPLETE) PrintLog(" image_complete");
}
else PrintLog(" invalid");
PrintLog("\n");
if(cb->md5State & MD5_COMPLETE)
{ AsciiDigest(digest, cb->dataMD5sum);
PrintLog(" data: %s\n", digest);
AsciiDigest(digest, cb->imageMD5sum);
PrintLog(" full: %s\n", digest);
}
AsciiDigest(digest, cb->mediumFP);
if(cb->fpValid)
PrintLog(" fp sector: %d; %s\n", cb->fpSector, digest);
else PrintLog(" fp sector: %d; invalid\n", cb->fpSector);
for(i=0; i<cb->crcSize; i++)
if(!GetBit(cb->valid, i))
{ missing++;
}
PrintLog(" missing crcs: %lld\n", missing);
}

379
src/curve.c Normal file
View File

@@ -0,0 +1,379 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
/***
*** Constructors and destructors
***/
/*
* Initialize the curve
*/
Curve* CreateCurve(GtkWidget *widget, char *left_label, char *left_format, int n_values, int bottom_format)
{ Curve *curve = g_malloc0(sizeof(Curve));
curve->widget = widget;
curve->layout = gtk_widget_create_pango_layout(widget, NULL);
curve->leftLabel = g_strdup(left_label);
curve->leftFormat = g_strdup(left_format);
curve->bottomFormat = bottom_format;
curve->fvalue = g_malloc0(sizeof(gdouble)*(n_values+1));
curve->ivalue = g_malloc0(sizeof(gint)*(n_values+1));
curve->lvalue = g_malloc0(sizeof(gint)*(n_values+1));
curve->lastValueIdx = n_values;
curve->maxX = 1;
curve->maxY = 1;
curve->logMaxY = 1;
if(bottom_format != CURVE_PERCENT)
curve->margin = 2;
return curve;
}
/*
* Get rid of it
*/
void FreeCurve(Curve *curve)
{
g_object_unref(curve->layout);
g_free(curve->leftLabel);
if(curve->leftLogLabel)
g_free(curve->leftLogLabel);
g_free(curve->leftFormat);
g_free(curve->fvalue);
g_free(curve->ivalue);
g_free(curve->lvalue);
g_free(curve);
}
/*
* Reset the values
*/
void ZeroCurve(Curve *curve)
{ int i;
if(curve)
for(i=0; i<=curve->lastValueIdx; i++)
{ curve->fvalue[i] = -1.0;
curve->ivalue[i] = 0;
curve->lvalue[i] = 0;
}
}
/***
*** Auxiliary functions
***/
/*
* Calculate pixel coords from curve values
*/
int CurveX(Curve *curve, gdouble x)
{ gdouble width = (curve->rightX - curve->leftX - curve->margin);
return 1 + curve->leftX + ((gdouble)x * width) / 1000.0;
}
int CurveLX(Curve *curve, gdouble x)
{ gdouble width = (curve->rightX - curve->leftX - curve->margin);
return 1 + curve->leftX + (x * width) / (gdouble)curve->maxX;
}
int CurveY(Curve *curve, gdouble y)
{ gdouble hfact;
hfact = (gdouble)(curve->bottomY - curve->topY)
/ (gdouble)curve->maxY;
return curve->bottomY - y * hfact;
}
int CurveLogY(Curve *curve, gdouble y) /* not really a log */
{ gdouble hfact;
if(y<1) return curve->bottomLY;
hfact = (gdouble)(curve->bottomLY - curve->topLY);
if(y==1) return curve->bottomLY - ((log(2)/log(curve->logMaxY)) * hfact)/2;
else return curve->bottomLY - (log(y)/log(curve->logMaxY)) * hfact;
}
/***
*** Calculate the curve geometry
***/
void UpdateCurveGeometry(Curve *curve, char *largest_left_label, int right_padding)
{ GtkAllocation *a = &curve->widget->allocation;
int w,h;
/* Top and bottom margins */
SetText(curve->layout, curve->leftLabel, &w, &h);
curve->topY = h + 10;
SetText(curve->layout, "0123456789", &w, &h);
curve->bottomY = a->height - h - 10;
/* Left and right margins */
SetText(curve->layout, largest_left_label, &w, &h);
curve->leftX = 5 + 6 + 3 + w;
curve->rightX = a->width - right_padding;
/* Add space for the lograithmic curve */
if(curve->enable & DRAW_LCURVE)
{ int height = curve->bottomY - curve->topY;
curve->bottomLY = curve->bottomY;
curve->bottomY -= height/4;
curve->topLY = curve->bottomY + h + 15;
}
}
/***
*** Redraw the coordinate axes
***/
void RedrawAxes(Curve *curve)
{ GdkDrawable *d = curve->widget->window;
int i,w,h,x,y;
int yg=0;
int step;
int bottom_y;
/* Draw and label the left coordinate axis */
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
gdk_draw_line(d, Closure->drawGC,
curve->leftX, curve->topY, curve->leftX, curve->bottomY);
if(curve->enable & DRAW_LCURVE)
{ gdk_draw_line(d, Closure->drawGC,
curve->leftX, curve->topLY, curve->leftX, curve->bottomLY);
}
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->curveColor);
SetText(curve->layout, curve->leftLabel, &w, &h);
x = curve->leftX - w/2;
if(x < 5) x = 5;
gdk_draw_layout(d, Closure->drawGC,
x, curve->topY - h - 5, curve->layout);
/* Draw and label the grid lines for the log curve */
if(curve->enable & DRAW_LCURVE)
{ int val;
char buf[16];
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->logColor);
SetText(curve->layout, curve->leftLogLabel, &w, &h);
x = curve->leftX - w/2;
if(x < 5) x = 5;
gdk_draw_layout(d, Closure->drawGC,
x, curve->topLY - h - 5, curve->layout);
for(val=400; val>3; val/=2)
{ y = CurveLogY(curve, val);
sprintf(buf,"%d",val);
SetText(curve->layout, buf, &w, &h);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->logColor);
gdk_draw_layout(d, Closure->drawGC, curve->leftX-9-w, y-h/2, curve->layout);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
gdk_draw_line(d, Closure->drawGC, curve->leftX-6, y, curve->leftX, y);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->grid);
gdk_draw_line(d, Closure->drawGC, curve->leftX, y, curve->rightX, y);
val /=2;
y = CurveLogY(curve, val);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
gdk_draw_line(d, Closure->drawGC, curve->leftX-3, y, curve->leftX, y);
if(curve->bottomLY-curve->topLY > 8*h)
{ sprintf(buf,"%d",val);
SetText(curve->layout, buf, &w, &h);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->logColor);
gdk_draw_layout(d, Closure->drawGC, curve->leftX-9-w, y-h/2, curve->layout);
}
}
}
/* Draw and label the grid lines for the normal curve */
if(curve->maxY < 20) step = 4;
else step = 10;
for(i=0; i<=curve->maxY; i+=step)
{ char buf[4];
g_snprintf(buf, 4, curve->leftFormat, i);
SetText(curve->layout, buf, &w, &h);
y = yg = CurveY(curve, i);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->curveColor);
gdk_draw_layout(d, Closure->drawGC, curve->leftX-9-w, y-h/2, curve->layout);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
gdk_draw_line(d, Closure->drawGC, curve->leftX-6, y, curve->leftX, y);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->grid);
gdk_draw_line(d, Closure->drawGC, curve->leftX, y, curve->rightX, y);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
y = CurveY(curve, i+step/2);
if(y >= curve->topY)
gdk_draw_line(d, Closure->drawGC, curve->leftX-3, y, curve->leftX, y);
}
/* Draw the right coordinate axis */
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
gdk_draw_line(d, Closure->drawGC,
curve->rightX, curve->topY, curve->rightX, curve->bottomY);
if(curve->enable & DRAW_LCURVE)
gdk_draw_line(d, Closure->drawGC,
curve->rightX, curve->topLY, curve->rightX, curve->bottomLY);
/* Draw and label the bottom coordinate axis */
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
gdk_draw_line(d, Closure->drawGC,
curve->leftX, curve->bottomY, curve->rightX, curve->bottomY);
if(curve->enable & DRAW_LCURVE)
{ gdk_draw_line(d, Closure->drawGC,
curve->leftX, curve->bottomLY, curve->rightX, curve->bottomLY);
bottom_y = curve->bottomLY;
}
else bottom_y = curve->bottomY;
if(curve->maxX <= 100) step = 20; /* <100M */
else if(curve->maxX < 1000) step = 100; /* 100M ... 1000M */
else if(curve->maxX < 8000) step = 1024; /* 1G .. 8G */
else if(curve->maxX < 15000) step = 2560; /* 8G .. 15G */
else if(curve->maxX < 25000) step = 5120; /* 15G .. 25G */
else step = 10240;
for(i=0; i<=curve->maxX; i+=step)
{ char buf[10];
switch(curve->bottomFormat)
{ case CURVE_PERCENT:
g_snprintf(buf, 10, "%d%%", i);
break;
case CURVE_MEGABYTES:
if(step <= 100)
g_snprintf(buf, 10, "%dM",i);
else g_snprintf(buf, 10, "%3.1fG",(gdouble)i/1024.0);
break;
}
SetText(curve->layout, buf, &w, &h);
x = CurveLX(curve,i)-1;
gdk_draw_line(d, Closure->drawGC, x, bottom_y+6, x, bottom_y);
gdk_draw_layout(d, Closure->drawGC, x-w/2, bottom_y+8, curve->layout);
if(i && x < curve->rightX)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->grid);
gdk_draw_line(d, Closure->drawGC, x, curve->bottomY-1, x, yg);
if(curve->enable & DRAW_LCURVE)
gdk_draw_line(d, Closure->drawGC, x, curve->bottomLY-1, x, curve->topLY);
}
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
x = CurveLX(curve,i+step/2)-1;
if(x < curve->rightX)
gdk_draw_line(d, Closure->drawGC, x, bottom_y+3, x, bottom_y);
}
}
/*
* Redraw the curve
*/
void RedrawCurve(Curve *curve, int last)
{ int i,x0,x1,fy0,fy1;
x0 = CurveX(curve, 0);
fy0 = CurveY(curve, curve->fvalue[0]);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->curveColor);
/* Draw the curve */
for(i=1; i<=last; i++)
{ x1 = CurveX(curve, i);
if(curve->enable & DRAW_ICURVE)
{ int iy = CurveY(curve, curve->ivalue[i]);
if(curve->ivalue[i] > 0)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->barColor);
gdk_draw_rectangle(curve->widget->window,
Closure->drawGC, TRUE,
x0, iy, x0==x1 ? 1 : x1-x0, curve->bottomY-iy);
}
}
if(curve->enable & DRAW_LCURVE)
{ int iy = CurveLogY(curve, curve->lvalue[i]);
if(curve->lvalue[i] > 0)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->logColor);
gdk_draw_rectangle(curve->widget->window,
Closure->drawGC, TRUE,
x0, iy, x0==x1 ? 1 : x1-x0, curve->bottomLY-iy);
}
}
if(curve->enable & DRAW_FCURVE && curve->fvalue[i] >= 0)
{ fy1 = CurveY(curve, curve->fvalue[i]);
if(x0 < x1)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->curveColor);
gdk_draw_line(curve->widget->window, Closure->drawGC, x0, fy0, x1, fy1);
fy0 = fy1;
}
}
x0 = x1;
}
}

1368
src/debug.c Normal file

File diff suppressed because it is too large Load Diff

437
src/ds-marker.c Normal file
View File

@@ -0,0 +1,437 @@
/* 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"
#define DSM_VERSION "1.00"
#define PSM_VERSION "1.00"
/***
*** Create an unique marker for missing sectors
***/
static void write_missing_sector(unsigned char *out, guint64 sector,
unsigned char *fingerprint, guint64 fingerprint_sector,
char *volume_label, char *simulation_hint)
{ char *buf = (char*)out;
char *end_marker;
int end_length;
/* Bytefill requested? */
if(Closure->fillUnreadable >= 0)
{ memset(out, Closure->fillUnreadable, 2048);
return;
}
/* historic words ... ;-) */
memset(buf, 0, 2048);
g_sprintf(buf,
"dvdisaster dead sector marker\n"
"This sector could not be read from the image.\n"
"Its contents have been substituted by the dvdisaster read routine.\n");
end_marker = "dvdisaster dead sector end marker\n";
end_length = strlen(end_marker);
memcpy(buf+2046-end_length, end_marker, end_length);
/* May we use the new marker features? */
if(!Closure->dsmVersion)
return;
/* Yes, add the missing sector attributes */
g_sprintf(buf+0x100,"Dead sector marker version");
g_sprintf(buf+0x120,"%s",DSM_VERSION);
g_sprintf(buf+0x140,"Dead sector number");
g_sprintf(buf+0x160,"%lld", (long long)sector);
g_sprintf(buf+0x180,"Medium fingerprint");
if(fingerprint) memcpy(buf+0x1a0, fingerprint, 16);
else memcpy(buf+0x1b0, "none", 4);
g_sprintf(buf+0x1c0,"Medium fingerprint sector");
g_sprintf(buf+0x1e0,"%lld", (long long)fingerprint_sector);
g_sprintf(buf+0x200,"Volume label (if any)");
g_sprintf(buf+0x220,"%s", volume_label ? volume_label : "none");
if(simulation_hint)
{ g_sprintf(buf+0x240,"Simulation hint");
g_sprintf(buf+0x260,"%s", simulation_hint);
}
}
void CreateDebuggingSector(unsigned char *out, guint64 sector,
unsigned char *fingerprint, guint64 fingerprint_sector,
char *volume_label, char *simulation_hint)
{ write_missing_sector(out, sector, fingerprint, fingerprint_sector, volume_label, simulation_hint);
}
void CreateMissingSector(unsigned char *out, guint64 sector,
unsigned char *fingerprint, guint64 fingerprint_sector,
char *volume_label)
{ write_missing_sector(out, sector, fingerprint, fingerprint_sector, volume_label, NULL);
}
/***
*** Create an unique padding sector
***/
void CreatePaddingSector(unsigned char *out, guint64 sector,
unsigned char *fingerprint, guint64 fingerprint_sector)
{ char *buf = (char*)out;
char *end_marker;
int end_length;
memset(buf, 0, 2048);
g_sprintf(buf,
"dvdisaster padding sector "
"This is a padding sector needed for augmenting the image "
"with error correction data.");
end_marker = "dvdisaster padding sector end marker";
end_length = strlen(end_marker);
memcpy(buf+2047-end_length, end_marker, end_length);
g_sprintf(buf+0x100,"Padding sector marker version");
g_sprintf(buf+0x120,"%s",DSM_VERSION);
g_sprintf(buf+0x140,"Padding sector number");
g_sprintf(buf+0x160,"%lld", (long long)sector);
g_sprintf(buf+0x180,"Medium fingerprint");
if(fingerprint) memcpy(buf+0x1a0, fingerprint, 16);
else memcpy(buf+0x1b0, "none", 4);
g_sprintf(buf+0x1c0,"Medium fingerprint sector");
g_sprintf(buf+0x1e0,"%lld", (long long)fingerprint_sector);
}
/***
*** Helper functions
***/
static int get_recorded_number(unsigned char *buf, guint64 *number)
{
if(!strcmp((char*)buf+0x140, "Dead sector number"))
{ *number = strtoll((char*)buf+0x160, NULL, 10);
return TRUE;
}
*number = 0;
return FALSE;
}
static char *get_volume_label(unsigned char *buf)
{
if(!strcmp((char*)buf+0x200, "Volume label (if any)"))
{ if(!strcmp((char*)buf+0x220, "none"))
return NULL;
else return g_strdup((char*)buf+0x220);
}
return NULL;
}
/*
* Used for simulating specific errors
*/
char *GetSimulationHint(unsigned char *buf)
{
if(!strcmp((char*)buf+0x240, "Simulation hint"))
return g_strdup((char*)buf+0x260);
return NULL;
}
/***
*** Check whether this is a missing sector
***/
int CheckForMissingSector(unsigned char *buf, guint64 sector,
unsigned char *fingerprint, guint64 fingerprint_sector)
{ static char pattern[2048];
static char last_pattern = 0;
guint64 recorded_number;
char *sim_hint;
/* Bytefill used as missing sector marker? */
if(Closure->fillUnreadable >= 0)
{ if(Closure->fillUnreadable != last_pattern) /* cache the pattern */
memset(pattern, Closure->fillUnreadable, 2048);
if(memcmp(buf, pattern, 2048))
return SECTOR_PRESENT;
else return SECTOR_MISSING;
}
/* See if it is our dead sector marker */
if(strncmp((char*)buf,
"dvdisaster dead sector marker\n"
"This sector could not be read from the image.\n"
"Its contents have been substituted by the dvdisaster read routine.\n",
143)
|| strncmp((char*)buf+2046-34, "dvdisaster dead sector end marker\n", 34))
return SECTOR_PRESENT;
/* New style missing sector marker? */
if(strcmp((char*)buf+0x100,"Dead sector marker version"))
return SECTOR_MISSING;
/*** Evaluate new style sector marker */
/* Look for hints on simulated images */
sim_hint = GetSimulationHint(buf);
if(sim_hint)
{ g_free(sim_hint);
return SECTOR_WITH_SIMULATION_HINT;
}
/* Verify sector number */
if(get_recorded_number(buf, &recorded_number))
if(recorded_number != sector)
return SECTOR_MISSING_DISPLACED;
/* Verify medium fingerprint. If the dead sector was fingerprinted with
a different sector, ignore the test. Retrieving the right fingerprint
sector is too expensive. */
if(fingerprint
&& !strcmp((char*)buf+0x1c0, "Medium fingerprint sector")
&& memcmp((char*)buf+0x1b0, "none", 4))
{ gint64 fps_recorded = strtoll((char*)buf+0x1e0, NULL, 10);
if(fps_recorded == fingerprint_sector)
{ if(!strcmp((char*)buf+0x180, "Medium fingerprint"))
if(memcmp((char*)buf+0x1a0, (char*)fingerprint, 16))
return SECTOR_MISSING_WRONG_FP;
}
}
return SECTOR_MISSING;
}
int CheckForMissingSectors(unsigned char *buf, guint64 sector,
unsigned char *fingerprint, guint64 fingerprint_sector,
int n_sectors, guint64 *first_defect)
{ int i,result;
for(i=0; i<n_sectors; i++)
{ result = CheckForMissingSector(buf, sector, fingerprint, fingerprint_sector);
if(result != SECTOR_PRESENT)
{ *first_defect = sector;
return result;
}
buf += 2048;
sector++;
}
return SECTOR_PRESENT;
}
/***
*** Dialogue for indicating problem with the missing sector
***/
#ifndef CLI
static void insert_buttons(GtkDialog *dialog)
{
gtk_dialog_add_buttons(dialog,
_utf("Stop reporting these errors"), 1,
_utf("Continue reporting"), 0, NULL);
}
#endif
void ExplainMissingSector(unsigned char *buf, guint64 sector, int error, int source_type, int *number)
{
#ifndef CLI
int answer;
#endif
guint64 recorded_number;
char *vol_label, *label_msg;
if(Closure->noMissingWarnings)
return;
/* Missing sectors should be reported in the following cases:
- In an image, normal missing sectors are to be expected.
Only displayced sectors and sectors with wrong fingerprint should be reported.
- In a medium, all kinds of missing sectors constitute a problem and must be reported.
- Within an ecc file, no missing sectors should appear although these are at least
harmless for RS03-type ecc files. Report them all.
*/
if(source_type == SOURCE_IMAGE && error != SECTOR_MISSING_DISPLACED && error != SECTOR_MISSING_WRONG_FP)
return;
/* In CLI mode, only report the first unrecoverable sector unless verbose is given. */
#ifndef CLI
if(!Closure->guiMode && !Closure->verbose && *number > 0)
#else
if(!Closure->verbose && *number > 0)
#endif
{ if(*number == 1)
PrintLog(_("* ... more unrecoverable sectors found ...\n"
"* further messages are suppressed unless the -v option is given.\n"));
(*number)++;
return;
}
(*number)++;
/* Get some meta data from the dsm */
get_recorded_number(buf, &recorded_number);
vol_label = get_volume_label(buf);
if(vol_label)
{
#ifndef CLI
if(Closure->guiMode)
label_msg = g_strdup_printf(_("\n\nThe label of the original (defective) medium was:\n%s\n\n"), vol_label);
else
#endif
label_msg = g_strdup_printf(_("\n* \n* The label of the original (defective) medium was:\n* \n* %s\n* "), vol_label);
g_free(vol_label);
}
else label_msg = g_strdup("\n");
/* Error was found in an image */
if(source_type == SOURCE_IMAGE)
{ switch(error)
{ case SECTOR_MISSING_DISPLACED:
{ char *msg = _("Unrecoverable sector found!\n\n"
"Sector %lld is marked unreadable and annotated to be\n"
"in a different location (%lld).\n\n"
"The image was probably mastered from defective content.\n"
"For example it might contain one or more files which came\n"
"from a damaged medium which was NOT fully recovered.\n"
"This means that some files may have been silently corrupted.%s\n"
"Since the image was already created defective it can not be\n"
"repaired by dvdisaster. Also it will not be possible to create\n"
"error correction data for it. Sorry for the bad news.\n");
#ifndef CLI
if(!Closure->guiMode)
#endif
PrintLogWithAsterisks(msg,sector, recorded_number, label_msg);
#ifndef CLI
else
{ answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
sector, recorded_number, label_msg);
if(answer) Closure->noMissingWarnings = TRUE;
}
#endif
}
break;
case SECTOR_MISSING_WRONG_FP:
{ char *msg = _("Unrecoverable sector found!\n\n"
"Sector %lld is marked unreadable and seems to come\n"
"from a different medium.\n\n"
"The image was probably mastered from defective content.\n"
"For example it might contain one or more files which came\n"
"from a damaged medium which was NOT fully recovered.\n"
"This means that some files may have been silently corrupted.%s\n"
"Since the image was already created defective it can not be\n"
"repaired by dvdisaster. Also it will not be possible to create\n"
"error correction data for it. Sorry for the bad news.\n");
#ifndef CLI
if(!Closure->guiMode)
#endif
PrintLogWithAsterisks(msg,sector, label_msg);
#ifndef CLI
else
{ answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
sector, label_msg);
if(answer) Closure->noMissingWarnings = TRUE;
}
#endif
}
break;
}
}
/* Error was found while reading a medium */
if(source_type == SOURCE_MEDIUM)
{ char *msg = _("Unrecoverable sector found!\n\n"
"Sector %lld is marked unreadable on the medium.\n\n"
"The medium was probably mastered from defective content.\n"
"For example it might contain one or more files which came\n"
"from a damaged medium which was NOT fully recovered.\n"
"This means that some files may have been silently corrupted.\n"
"Since the medium was already created defective it can not be\n"
"repaired by dvdisaster. Also it will not be possible to create\n"
"error correction data for it. Sorry for the bad news.\n");
#ifndef CLI
if(!Closure->guiMode)
#endif
PrintLogWithAsterisks(msg, sector);
#ifndef CLI
else
{ answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
sector);
if(answer) Closure->noMissingWarnings = TRUE;
}
#endif
}
/* Error was found while reading an ecc file */
if(source_type == SOURCE_ECCFILE)
{ char *msg = _("Unrecoverable sector found!\n\n"
"Sector %lld is marked unreadable in the ecc file.\n\n"
"The ecc file was probably taken from a medium which\n"
"was NOT fully recovered. That means that some sectors\n"
"in the ecc file are missing and its error correction\n"
"capacity will be reduced.\n");
#ifndef CLI
if(!Closure->guiMode)
#endif
PrintLogWithAsterisks(msg, sector);
#ifndef CLI
else
{ answer = ModalDialog(GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, insert_buttons, msg,
sector);
if(answer) Closure->noMissingWarnings = TRUE;
}
#endif
}
g_free(label_msg);
}

1053
src/dvdisaster.c Normal file

File diff suppressed because it is too large Load Diff

1493
src/dvdisaster.h Normal file

File diff suppressed because it is too large Load Diff

104
src/ecc-rs01.c Normal file
View File

@@ -0,0 +1,104 @@
/* 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"
/***
*** Method registration
***/
static void destroy(Method*);
void register_rs01(void)
{ Method *method = g_malloc0(sizeof(Method));
method->ckSumClosure = g_malloc0(sizeof(RS01CksumClosure));
/*** Standard infomation and methods */
strncpy(method->name, "RS01", 4);
method->menuEntry = g_strdup(_("Error correction file (RS01)"));
method->description = g_strdup(_("Classic Reed-Solomon method based on polynomial arithmetic"));
method->create = RS01Create;
method->fix = RS01Fix;
method->verify = RS01Verify;
/*** Linkage to rs01-common.c */
method->recognizeEccFile = RS01Recognize;
method->getCrcBuf = RS01GetCrcBuf;
method->resetCksums = RS01ResetCksums;
method->updateCksums = RS01UpdateCksums;
method->finalizeCksums = RS01FinalizeCksums;
method->expectedImageSize = RS01ExpectedImageSize;
#ifndef CLI
/*** Linkage to rs01-window.c */
method->createCreateWindow = CreateRS01EWindow;
method->createFixWindow = CreateRS01FWindow;
method->resetCreateWindow = ResetRS01EncodeWindow;
method->resetFixWindow = ResetRS01FixWindow;
method->createPrefsPage = CreateRS01PrefsPage;
method->resetPrefsPage = ResetRS01PrefsPage;
/*** Linkage to rs01-verify.c */
method->createVerifyWindow = CreateRS01VerifyWindow;
method->resetVerifyWindow = ResetRS01VerifyWindow;
#endif
/*** Register ourself */
method->destroy = destroy;
RegisterMethod(method);
}
static void destroy(Method *method)
{
#ifndef CLI
RS01Widgets *wl = (RS01Widgets*)method->widgetList;
#endif
g_free(method->ckSumClosure);
#ifndef CLI
if(wl)
{ if(wl->fixCurve) FreeCurve(wl->fixCurve);
if(wl->cmpSpiral)
FreeSpiral(wl->cmpSpiral);
if(wl->cmpLayout)
g_object_unref(wl->cmpLayout);
g_free(wl);
}
#endif
}

108
src/ecc-rs02.c Normal file
View File

@@ -0,0 +1,108 @@
/* 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"
/***
*** Method registration
***/
static void destroy(Method*);
void register_rs02(void)
{ Method *method = g_malloc0(sizeof(Method));
method->ckSumClosure = g_malloc0(sizeof(RS02CksumClosure));
/*** Standard infomation and methods */
strncpy(method->name, "RS02", 4);
method->menuEntry = g_strdup(_("Augmented image (RS02)"));
method->description = g_strdup(_("Reed-Solomon method with improved tolerance for defective ecc data"));
method->create = RS02Create;
method->fix = RS02Fix;
method->verify = RS02Verify;
/*** Linkage to rs02-common.c */
method->recognizeEccImage = RS02Recognize;
method->getCrcBuf = RS02GetCrcBuf;
method->resetCksums = RS02ResetCksums;
method->updateCksums = RS02UpdateCksums;
method->finalizeCksums = RS02FinalizeCksums;
method->expectedImageSize = RS02ExpectedImageSize;
#ifndef CLI
/*** Linkage to rs02-window.c */
method->createCreateWindow = CreateRS02EncWindow;
method->createFixWindow = CreateRS02FixWindow;
method->resetCreateWindow = ResetRS02EncWindow;
method->resetFixWindow = ResetRS02FixWindow;
method->createPrefsPage = CreateRS02PrefsPage;
method->resetPrefsPage = ResetRS02PrefsPage;
method->readPreferences = ReadRS02Preferences;
/*** Linkage to rs02-verify.c */
method->createVerifyWindow = CreateRS02VerifyWindow;
method->resetVerifyWindow = ResetRS02VerifyWindow;
#endif
/*** Register ourself */
method->destroy = destroy;
RegisterMethod(method);
}
static void destroy(Method *method)
{
#ifndef CLI
RS02Widgets *wl = (RS02Widgets*)method->widgetList;
#endif
RS02CksumClosure *csc = (RS02CksumClosure*)method->ckSumClosure;
if(csc->lay)
g_free(csc->lay);
g_free(method->ckSumClosure);
#ifndef CLI
if(wl)
{ if(wl->fixCurve) FreeCurve(wl->fixCurve);
if(wl->cmpSpiral)
FreeSpiral(wl->cmpSpiral);
if(wl->cmpLayout)
g_object_unref(wl->cmpLayout);
g_free(wl);
}
#endif
}

109
src/ecc-rs03.c Normal file
View File

@@ -0,0 +1,109 @@
/* 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 "rs03-includes.h"
/***
*** Method registration
***/
static void destroy(Method*);
void register_rs03(void)
{ Method *method = g_malloc0(sizeof(Method));
method->ckSumClosure = g_malloc0(sizeof(RS03CksumClosure));
/*** Standard infomation and methods */
strncpy(method->name, "RS03", 4);
method->menuEntry = g_strdup(_("Multithreaded RS codec (RS03)"));
method->description = g_strdup(_("Multithreaded Reed-Solomon codec for error correction files and augmented images"));
method->create = RS03Create;
method->fix = RS03Fix;
method->verify = RS03Verify;
/*** Linkage to rs03-common.c */
method->expectedImageSize = RS03ExpectedImageSize;
method->getCrcBuf = RS03GetCrcBuf;
/*** Linkage to rs03-recognize.c */
method->recognizeEccFile = RS03RecognizeFile;
method->recognizeEccImage = RS03RecognizeImage;
#ifndef CLI
/*** Linkage to rs03-window.c */
method->createCreateWindow = CreateRS03EncWindow;
method->createFixWindow = CreateRS03FixWindow;
method->resetCreateWindow = ResetRS03EncWindow;
method->resetFixWindow = ResetRS03FixWindow;
method->createPrefsPage = CreateRS03PrefsPage;
method->resetPrefsPage = ResetRS03PrefsPage;
method->readPreferences = ReadRS03Preferences;
/*** Linkage to rs03-verify.c */
method->createVerifyWindow = CreateRS03VerifyWindow;
method->resetVerifyWindow = ResetRS03VerifyWindow;
#endif
/*** Register ourself */
method->destroy = destroy;
RegisterMethod(method);
}
static void destroy(Method *method)
{
#ifndef CLI
RS03Widgets *wl = (RS03Widgets*)method->widgetList;
#endif
RS03CksumClosure *csc = (RS03CksumClosure*)method->ckSumClosure;
if(csc->lay)
g_free(csc->lay);
g_free(method->ckSumClosure);
#ifndef CLI
if(wl)
{ if(wl->fixCurve) FreeCurve(wl->fixCurve);
if(wl->cmpSpiral)
FreeSpiral(wl->cmpSpiral);
if(wl->cmpLayout)
g_object_unref(wl->cmpLayout);
g_free(wl);
}
#endif
}

181
src/endian.c Normal file
View File

@@ -0,0 +1,181 @@
/* 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"
/***
*** Conversion between little and big endian words.
*** Only suitable for non performance critical code.
*/
/*
* Swap bytes in a 32 bit word to convert between little and big endian.
*/
guint32 SwapBytes32(guint32 in)
{
return
((in & 0xff000000) >> 24)
| ((in & 0x00ff0000) >> 8)
| ((in & 0x0000ff00) << 8)
| ((in & 0x000000ff) << 24);
}
guint64 SwapBytes64(guint64 in)
{
return
((in & 0xff00000000000000ull) >> 56)
| ((in & 0x00ff000000000000ull) >> 40)
| ((in & 0x0000ff0000000000ull) >> 24)
| ((in & 0x000000ff00000000ull) >> 8)
| ((in & 0x00000000ff000000ull) << 8)
| ((in & 0x0000000000ff0000ull) << 24)
| ((in & 0x000000000000ff00ull) << 40)
| ((in & 0x00000000000000ffull) << 56);
}
/***
*** Convert the EccHeader structure between different endians.
***/
/*
* A debugging function for printing the Ecc header.
*/
void print_hex(char *label, guint8 *values, int n)
{ PrintCLI(label);
while(n--)
PrintCLI("%02x ",*values++);
PrintCLI("\n");
}
void PrintEccHeader(EccHeader *eh)
{ char buf[16];
PrintCLI(_("\nContents of Ecc Header:\n\n"));
strncpy(buf, (char*)eh->cookie, 12); buf[12] = 0;
PrintCLI("cookie %s\n",buf);
strncpy(buf, (char*)eh->method, 4); buf[4] = 0;
PrintCLI("method %s\n",buf);
print_hex("methodFlags ", (guint8*)eh->methodFlags, 4);
print_hex("mediumFP ", eh->mediumFP, 16);
print_hex("mediumSum ", eh->mediumSum, 16);
print_hex("eccSum ", eh->eccSum, 16);
print_hex("sectors ", eh->sectors, 8);
PrintCLI("sectors (native) %lld\n", uchar_to_gint64(eh->sectors));
PrintCLI("dataBytes %8x\n", eh->dataBytes);
PrintCLI("eccBytes %8x\n", eh->eccBytes);
PrintCLI("creatorVersion %8x\n", eh->creatorVersion);
PrintCLI("neededVersion %8x\n", eh->neededVersion);
PrintCLI("fpSector %8x\n", eh->fpSector);
PrintCLI("selfCRC %8x\n", eh->selfCRC);
print_hex("crcSum ", eh->crcSum, 16);
PrintCLI("inLast %8x\n", eh->inLast);
PrintCLI("sectorsPerLayer %lld\n", eh->sectorsPerLayer);
PrintCLI("sectorsAddedByEcc %lld\n", eh->sectorsAddedByEcc);
PrintCLI("\n");
}
void print_crc_block(CrcBlock *cb)
{ char buf[16];
PrintCLI("\nContents of CrcBlock:\n\n");
strncpy(buf, (char*)cb->cookie, 12); buf[12] = 0;
PrintCLI("cookie %s\n",buf);
strncpy(buf, (char*)cb->method, 4); buf[4] = 0;
PrintCLI("method %s\n",buf);
print_hex("methodFlags ", (guint8*)cb->methodFlags, 4);
PrintCLI("creatorVersion %8x\n", cb->creatorVersion);
PrintCLI("neededVersion %8x\n", cb->neededVersion);
PrintCLI("fpSector %8x\n", cb->fpSector);
print_hex("mediumFP ", cb->mediumFP, 16);
print_hex("mediumSum ", cb->mediumSum, 16);
PrintCLI("dataSectors %ll16x\n ",cb->dataSectors);
PrintCLI("inLast %8x\n", cb->inLast);
PrintCLI("dataBytes %8x\n", cb->dataBytes);
PrintCLI("eccBytes %8x\n", cb->eccBytes);
PrintCLI("sectorsPerLayer %lld\n", cb->sectorsPerLayer);
PrintCLI("selfCRC %8x\n", cb->selfCRC);
PrintCLI("\n");
}
/*
* This is the most annoying part of the endian conversions.
*/
//#define VERBOSE
void SwapEccHeaderBytes(EccHeader *eh)
{
#ifdef VERBOSE
printf("before swap:\n");
print_ecc_header(eh);
#endif
eh->dataBytes = SwapBytes32(eh->dataBytes);
eh->eccBytes = SwapBytes32(eh->eccBytes);
eh->creatorVersion = SwapBytes32(eh->creatorVersion);
eh->neededVersion = SwapBytes32(eh->neededVersion);
eh->fpSector = SwapBytes32(eh->fpSector);
eh->inLast = SwapBytes32(eh->inLast);
eh->sectorsPerLayer = SwapBytes64(eh->sectorsPerLayer);
eh->sectorsAddedByEcc = SwapBytes64(eh->sectorsAddedByEcc);
#ifdef VERBOSE
printf("after swap:\n");
print_ecc_header(eh);
#endif
}
void SwapCrcBlockBytes(CrcBlock *cb)
{
#ifdef VERBOSE
printf("before swap:\n");
print_crc_block(cb);
#endif
cb->creatorVersion = SwapBytes32(cb->creatorVersion);
cb->neededVersion = SwapBytes32(cb->neededVersion);
cb->fpSector = SwapBytes32(cb->fpSector);
cb->dataSectors = SwapBytes64(cb->dataSectors);
cb->inLast = SwapBytes32(cb->inLast);
cb->dataBytes = SwapBytes32(cb->dataBytes);
cb->eccBytes = SwapBytes32(cb->eccBytes);
cb->sectorsPerLayer = SwapBytes64(cb->sectorsPerLayer);
#ifdef VERBOSE
printf("after swap:\n");
print_crc_block(cb);
#endif
}
void SwapDefectiveHeaderBytes(DefectiveSectorHeader *dsh)
{
dsh->lba = SwapBytes64(dsh->lba);
dsh->sectorSize = SwapBytes32(dsh->sectorSize);
dsh->properties = SwapBytes32(dsh->properties);
dsh->dshFormat = SwapBytes32(dsh->dshFormat);
dsh->nSectors = SwapBytes32(dsh->nSectors);
}

38
src/galois-inlines.h Normal file
View File

@@ -0,0 +1,38 @@
/* 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"
/*
* The following routine is performance critical.
*/
static inline int mod_fieldmax(int x)
{
while (x >= GF_FIELDMAX)
{
x -= GF_FIELDMAX;
x = (x >> GF_SYMBOLSIZE) + (x & GF_FIELDMAX);
}
return x;
}

200
src/galois.c Normal file
View File

@@ -0,0 +1,200 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2017 Carsten Gnoerlich.
*
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
*
* 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 "galois-inlines.h"
/***
*** Galois field arithmetic.
***
* Calculations are done over the extension field GF(2**n).
* Be careful not to overgeneralize these arithmetics;
* they only work for the case of GF(p**n) with p being prime.
*/
/* Initialize the Galois field tables */
GaloisTables* CreateGaloisTables(gint32 gf_generator)
{ GaloisTables *gt = g_malloc0(sizeof(GaloisTables));
gint32 b,log;
/* Allocate the tables.
The encoder uses a special version of alpha_to which has the mod_fieldmax()
folded into the table. */
gt->gfGenerator = gf_generator;
gt->indexOf = g_malloc(GF_FIELDSIZE * sizeof(gint32));
gt->alphaTo = g_malloc(GF_FIELDSIZE * sizeof(gint32));
gt->encAlphaTo = g_malloc(2*GF_FIELDSIZE * sizeof(gint32));
/* create the log/ilog values */
for(b=1, log=0; log<GF_FIELDMAX; log++)
{ gt->indexOf[b] = log;
gt->alphaTo[log] = b;
b = b << 1;
if(b & GF_FIELDSIZE)
b = b ^ gf_generator;
}
if(b!=1) Stop("Failed to create the Galois field log tables!\n");
/* we're even closed using infinity (makes things easier) */
gt->indexOf[0] = GF_ALPHA0; /* log(0) = inf */
gt->alphaTo[GF_ALPHA0] = 0; /* and the other way around */
for(b=0; b<2*GF_FIELDSIZE; b++)
gt->encAlphaTo[b] = gt->alphaTo[mod_fieldmax(b)];
return gt;
}
void FreeGaloisTables(GaloisTables *gt)
{
if(gt->indexOf) g_free(gt->indexOf);
if(gt->alphaTo) g_free(gt->alphaTo);
if(gt->encAlphaTo) g_free(gt->encAlphaTo);
g_free(gt);
}
/***
*** Create the Reed-Solomon generator polynomial
*** and some auxiliary data structures.
*/
ReedSolomonTables *CreateReedSolomonTables(GaloisTables *gt,
gint32 first_consecutive_root,
gint32 prim_elem,
int nroots_in)
{ ReedSolomonTables *rt = g_malloc0(sizeof(ReedSolomonTables));
int lut_size, feedback;
gint32 i,j,root;
guint8 *lut;
rt->gfTables = gt;
rt->fcr = first_consecutive_root;
rt->primElem = prim_elem;
rt->nroots = nroots_in;
rt->ndata = GF_FIELDMAX - rt->nroots;
rt->gpoly = g_malloc((rt->nroots+1) * sizeof(gint32));
/* Create the RS code generator polynomial */
rt->gpoly[0] = 1;
for(i=0, root=first_consecutive_root*prim_elem; i<rt->nroots; i++, root+=prim_elem)
{ rt->gpoly[i+1] = 1;
/* Multiply gpoly by alpha**(root+x) */
for(j=i; j>0; j--)
{
if(rt->gpoly[j] != 0)
rt->gpoly[j] = rt->gpoly[j-1] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[j]] + root)];
else
rt->gpoly[j] = rt->gpoly[j-1];
}
rt->gpoly[0] = gt->alphaTo[mod_fieldmax(gt->indexOf[rt->gpoly[0]] + root)];
}
/* Store the polynomials index for faster encoding */
for(i=0; i<=rt->nroots; i++)
rt->gpoly[i] = gt->indexOf[rt->gpoly[i]];
#if 0
/* for the precalculated unrolled loops only */
for(i=gt->nroots-1; i>0; i--)
PrintCLI(
" par_idx[((++spk)&%d)] ^= enc_alpha_to[feedback + %3d];\n",
nroots-1,gt->gpoly[i]);
PrintCLI(" par_idx[sp] = enc_alpha_to[feedback + %3d];\n",
gt->gpoly[0]);
#endif
/*
* Initialize the shift pointer so that we will come out at shiftPtr==0
* respectively (ndata+sp) mod nroots = 0 after working in all ndata layers.
*/
rt->shiftInit = rt->nroots - rt->ndata % rt->nroots;
if(rt->shiftInit == rt->nroots)
rt->shiftInit = 0;
/*
* Initialize lookup tables for both encoder types.
* The 32bit portable encoder will shift them to word boundaries,
* while the SSE2 encoder does direct unaligned reads.
*/
lut_size = (rt->nroots+15)&~15;
lut_size += 16;
for(i=0; i<GF_FIELDSIZE; i++)
rt->bLut[i] = g_malloc0(2*lut_size);
for(feedback=0; feedback<256; feedback++)
{ gint32 *gpoly = rt->gpoly + rt->nroots;
gint32 *enc_alpha_to = gt->encAlphaTo;
int nroots = rt->nroots;
for(i=0; i<nroots; i++)
{ guint8 value = (guint8)enc_alpha_to[feedback + *--gpoly];
rt->bLut[feedback][i] = rt->bLut[feedback][nroots+i] = value;
}
}
/*
* Prepare lookup table for syndrome calculation.
*/
lut = rt->synLut = g_malloc(rt->nroots * GF_FIELDSIZE * sizeof(int));
for(i=0; i<rt->nroots; i++)
for(j=0; j<GF_FIELDSIZE; j++)
*lut++ = gt->alphaTo[mod_fieldmax(gt->indexOf[j] + (rt->fcr+i)*rt->primElem)];
return rt;
}
void FreeReedSolomonTables(ReedSolomonTables *rt)
{ int i;
if(rt->gpoly) g_free(rt->gpoly);
for(i=0; i<GF_FIELDSIZE; i++)
{ g_free(rt->bLut[i]);
}
g_free(rt->synLut);
g_free(rt);
}

795
src/help-dialogs.c Normal file
View File

@@ -0,0 +1,795 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#include "help-dialogs.h"
/***
*** Online help system for the preferences
***/
/*
* Create a help window
*/
/* Close button response */
static void close_cb(GtkWidget *widget, gpointer data)
{ LabelWithOnlineHelp *lwoh = (LabelWithOnlineHelp*)data;
gtk_widget_hide(lwoh->helpWindow);
}
/* Do not destroy the window when closed via the window manager */
static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{ LabelWithOnlineHelp *lwoh = (LabelWithOnlineHelp*)data;
gtk_widget_hide(lwoh->helpWindow);
return TRUE;
}
/*
* Get a new integer variable from the lastSizes array
*/
static int* get_new_int(LabelWithOnlineHelp* lwoh)
{ int *var = g_malloc0(sizeof(int));
if(!lwoh->lastSizes)
lwoh->lastSizes = g_ptr_array_new();
g_ptr_array_add(lwoh->lastSizes, var);
return var;
}
/*
* Callback for the help link
*/
static gint help_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{ GtkWidget *lab = GTK_BIN(widget)->child;
LabelWithOnlineHelp *lwoh = (LabelWithOnlineHelp*)data;
switch(event->type)
{ case GDK_BUTTON_PRESS:
if(!lwoh->inside) return FALSE; /* Defect in certain Gtk versions? */
gtk_widget_show_all(GTK_WIDGET(lwoh->helpWindow));
break;
case GDK_ENTER_NOTIFY:
gtk_label_set_markup(GTK_LABEL(lab), lwoh->highlitText);
lwoh->inside = TRUE;
break;
case GDK_LEAVE_NOTIFY:
gtk_label_set_markup(GTK_LABEL(lab), lwoh->normalText);
lwoh->inside = FALSE;
break;
default: break;
}
return FALSE;
}
/*
* Create a frame labeled with a link to the help system
*/
LabelWithOnlineHelp* CreateLabelWithOnlineHelp(char *title, char *ascii_text)
{ GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *vbox, *hbox, *button;
GtkWidget *ebox = gtk_event_box_new();
LabelWithOnlineHelp *lwoh;
/*** Initialize online help context */
lwoh = g_malloc0(sizeof(LabelWithOnlineHelp));
lwoh->normalLabel = gtk_label_new(NULL);
lwoh->linkLabel = gtk_label_new(NULL);
lwoh->linkBox = ebox;
lwoh->windowTitle = g_locale_to_utf8(title, -1, NULL, NULL, NULL);
SetOnlineHelpLinkText(lwoh, ascii_text);
gtk_label_set_markup(GTK_LABEL(lwoh->normalLabel), lwoh->normalText);
/*** Create the help window */
lwoh->helpWindow = window;
gtk_window_set_title(GTK_WINDOW(window), lwoh->windowTitle);
gtk_window_set_icon(GTK_WINDOW(window), Closure->windowIcon);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
lwoh->outerPadding = 12;
gtk_container_set_border_width(GTK_CONTAINER(window), lwoh->outerPadding);
lwoh->outerPadding *= 2;
/* Connect window with the close button from the window manager */
g_signal_connect(window, "delete_event", G_CALLBACK(delete_cb), lwoh);
/* Create the main layout of the window */
lwoh->vbox = vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(close_cb), lwoh);
gtk_box_pack_end(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 6);
/*** Put link label into an event box */
gtk_widget_set_events(ebox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(ebox), "button_press_event", G_CALLBACK(help_cb), (gpointer)lwoh);
g_signal_connect(G_OBJECT(ebox), "enter_notify_event", G_CALLBACK(help_cb), (gpointer)lwoh);
g_signal_connect(G_OBJECT(ebox), "leave_notify_event", G_CALLBACK(help_cb), (gpointer)lwoh);
gtk_label_set_markup(GTK_LABEL(lwoh->linkLabel), lwoh->normalText);
gtk_container_add(GTK_CONTAINER(ebox), lwoh->linkLabel);
return lwoh;
}
LabelWithOnlineHelp* CloneLabelWithOnlineHelp(LabelWithOnlineHelp *orig, char *ascii_text)
{ LabelWithOnlineHelp *lwoh;
GtkWidget *ebox = gtk_event_box_new();
/*** Initialize online help context from given one */
lwoh = g_malloc0(sizeof(LabelWithOnlineHelp));
lwoh->helpWindow = orig->helpWindow;
/*** Only replace the labels */
lwoh->normalLabel = gtk_label_new(NULL);
lwoh->linkLabel = gtk_label_new(NULL);
lwoh->linkBox = ebox;
lwoh->windowTitle = g_strdup("ignore");
SetOnlineHelpLinkText(lwoh, ascii_text);
/*** Put link label into an event box */
gtk_widget_set_events(ebox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(ebox), "button_press_event", G_CALLBACK(help_cb), (gpointer)lwoh);
g_signal_connect(G_OBJECT(ebox), "enter_notify_event", G_CALLBACK(help_cb), (gpointer)lwoh);
g_signal_connect(G_OBJECT(ebox), "leave_notify_event", G_CALLBACK(help_cb), (gpointer)lwoh);
gtk_label_set_markup(GTK_LABEL(lwoh->normalLabel), lwoh->normalText);
gtk_label_set_markup(GTK_LABEL(lwoh->linkLabel), lwoh->normalText);
gtk_container_add(GTK_CONTAINER(ebox), lwoh->linkLabel);
return lwoh;
}
void SetOnlineHelpLinkText(LabelWithOnlineHelp *lwoh, char *ascii_text)
{ char text[strlen(ascii_text)+80];
if(lwoh->normalText) g_free(lwoh->normalText);
if(lwoh->highlitText) g_free(lwoh->highlitText);
lwoh->normalText = g_locale_to_utf8(ascii_text, -1, NULL, NULL, NULL);
g_sprintf(text, "<span underline=\"single\" color=\"blue\">%s</span>", ascii_text);
lwoh->highlitText = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
}
void FreeLabelWithOnlineHelp(LabelWithOnlineHelp *lwoh)
{
if(lwoh->lastSizes)
{ int i;
for(i=0; i<lwoh->lastSizes->len; i++)
{ int *var = g_ptr_array_index(lwoh->lastSizes, i);
g_free(var);
}
g_ptr_array_free(lwoh->lastSizes, FALSE);
}
g_free(lwoh->windowTitle);
g_free(lwoh->normalText);
g_free(lwoh->highlitText);
g_free(lwoh);
}
/*
* Add a paragraph of text to the help window
*/
static gboolean wrapper_fix_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{ int *last_width = (int*)data;
int label_width = widget->allocation.width;
if(*last_width == label_width) /* short circuit expose events */
return FALSE; /* without size changes */
*last_width = label_width;
/* This is a hack. We feed the label its own allocation to make it redraw.
Note that we subtract 4 or else the window would never shrink again. */
if(label_width<0 || label_width>200)
gtk_widget_set_size_request(widget, label_width-4, -1);
return FALSE;
}
void AddHelpParagraph(LabelWithOnlineHelp *lwoh, char *format, ...)
{ GtkWidget *label = gtk_label_new(NULL);
va_list argp;
char *text,*utf;
va_start(argp, format);
text = g_strdup_vprintf(format, argp);
va_end(argp);
utf = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(label), utf);
g_free(utf);
g_free(text);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(lwoh->vbox), label, FALSE, FALSE, 0);
/* Work around some bugs in the gtk line wrapper code.
By default lines are wrapped at the length of
"This long string gives a good enough length for any line to have."
which is, well, stupid. */
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
g_signal_connect(label, "expose_event", G_CALLBACK(wrapper_fix_cb), get_new_int(lwoh));
}
/*
* Add an item list to the help window.
* The list may be preceeded by an optional paragraph of text.
*/
void AddHelpListItem(LabelWithOnlineHelp *lwoh, char *format, ...)
{ GtkWidget *label = gtk_label_new(NULL);
GtkWidget *bullet = gtk_label_new(" - ");
GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
va_list argp;
char *text,*utf;
gtk_box_pack_start(GTK_BOX(lwoh->vbox), hbox, FALSE, FALSE, 0);
gtk_misc_set_alignment(GTK_MISC(bullet), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), bullet, FALSE, FALSE, 0);
va_start(argp, format);
text = g_strdup_vprintf(format, argp);
va_end(argp);
utf = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(label), utf);
g_free(utf);
g_free(text);
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
/* Work around some bugs in the gtk line wrapper code.
By default lines are wrapped at the length of
"This long string gives a good enough length for any line to have."
which is, well, stupid. */
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
g_signal_connect(label, "expose_event", G_CALLBACK(wrapper_fix_cb), get_new_int(lwoh));
}
/*
* Add a (fully functional!) widget set to the help window
*/
void AddHelpWidget(LabelWithOnlineHelp *lwoh, GtkWidget *widget)
{
gtk_box_pack_start(GTK_BOX(lwoh->vbox), widget, FALSE, FALSE, 10);
gtk_box_pack_start(GTK_BOX(lwoh->vbox), gtk_hseparator_new(), FALSE, FALSE, 10);
}
/***
*** The log viewer
***/
static void log_destroy_cb(GtkWidget *widget, gpointer data)
{
/* Avoid race condition with next function */
g_mutex_lock(Closure->logLock);
Closure->logWidget = NULL;
Closure->logScroll = NULL;
Closure->logBuffer = NULL;
g_mutex_unlock(Closure->logLock);
}
static gboolean log_jump_func(gpointer data)
{ GtkAdjustment *a;
GtkTextIter end;
/* Locking is needed as user might destroy the window
while we are updating it */
g_mutex_lock(Closure->logLock);
if(!Closure->logWidget)
{ g_mutex_unlock(Closure->logLock);
return FALSE;
}
gtk_text_buffer_get_end_iter(Closure->logBuffer, &end);
gtk_text_buffer_place_cursor(Closure->logBuffer, &end);
a = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(Closure->logScroll));
gtk_adjustment_set_value(a, a->upper - a->page_size);
gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(Closure->logScroll), a);
g_mutex_unlock(Closure->logLock);
return FALSE;
}
static gboolean log_idle_func(gpointer data)
{
g_mutex_lock(Closure->logLock);
if(Closure->logBuffer)
gtk_text_buffer_set_text(Closure->logBuffer, Closure->logString->str, Closure->logString->len);
g_mutex_unlock(Closure->logLock);
g_idle_add(log_jump_func, NULL);
return FALSE;
}
void UpdateLog()
{ static int unique_addr;
if(Closure->logWidget)
{ g_idle_remove_by_data(&unique_addr);
g_idle_add(log_idle_func, &unique_addr);
}
}
void ShowLog()
{ GtkWidget *w;
if(Closure->logWidget)
{ gtk_widget_show(Closure->logWidget);
return;
}
w = ShowTextfile(_("windowtitle|Log data"),
_("<big>Log data</big>\n"
"<i>Protocol of the current or previous action</i>"),
"*LOG*", &Closure->logScroll, &Closure->logBuffer);
g_signal_connect(G_OBJECT(w), "destroy", G_CALLBACK(log_destroy_cb), NULL);
Closure->logWidget = w;
}
/***
*** Specific help dialogs
***/
void ShowGPL()
{
ShowTextfile(_("windowtitle|GNU General Public License"),
_("<big>GNU General Public License</big>\n"
"<i>The license terms of dvdisaster.</i>"),
"COPYING", NULL, NULL);
}
/*
* Dialog for displaying text files
*/
char *find_file(char *file, size_t *size, char *lang)
{ char *path;
char lang_suffix[3];
guint64 stat_size;
lang_suffix[0] = lang_suffix[2] = 0;
if(lang)
{
lang_suffix[0] = lang[0];
lang_suffix[1] = lang[1];
}
/* Test for absolute path first. */
if(*file == '/')
{
if(lang)
path = g_strdup_printf("%s.%s", file, lang_suffix);
else
path = g_strdup(file);
if(LargeStat(path, &stat_size))
{
*size = stat_size;
return path;
}
g_free(path);
return NULL;
}
/* Try file in bin dir */
if(Closure->binDir)
{ if(lang)
path = g_strdup_printf("%s/%s.%s",Closure->binDir, file, lang_suffix);
else path = g_strdup_printf("%s/%s",Closure->binDir, file);
if(LargeStat(path, &stat_size))
{ *size = stat_size;
return path;
}
g_free(path);
}
/* Try file in doc dir */
if(Closure->docDir)
{ if(lang)
path = g_strdup_printf("%s/%s.%s",Closure->docDir, file, lang_suffix);
else path = g_strdup_printf("%s/%s",Closure->docDir, file);
if(LargeStat(path, &stat_size))
{ *size = stat_size;
return path;
}
g_free(path);
}
return NULL;
}
GtkWidget* ShowTextfile(char *title, char *explanation, char *file,
GtkScrolledWindow **scroll_out, GtkTextBuffer **buffer_out)
{ GtkWidget *dialog, *scroll_win, *vbox, *lab, *sep, *view;
GtkTextBuffer *buffer;
GtkTextIter start;
char *path;
char *utf,*buf;
size_t size = 0;
/*** Read the text file */
if(*file != '*')
{
if( !(path = find_file(file, &size, NULL))
&& !(path = find_file(file, &size, (char*)g_getenv("LANG")))
&& !(path = find_file(file, &size, "en"))
)
{ char *trans = _utf("File\n%s\nnot present");
buf = g_strdup_printf(trans, file);
size = strlen(buf);
}
else
{ FILE *fptr = portable_fopen(path, "rb");
size_t bytes_read;
if(!fptr)
{ char *trans = _utf("File\n%s\nnot accessible");
buf = g_strdup_printf(trans, file);
size = strlen(buf);
}
else
{ buf = g_malloc(size);
bytes_read = fread(buf, 1, size, fptr);
fclose(fptr);
g_free(path);
if(bytes_read < size)
{ char *trans = _utf("\n<- Error: Text file truncated here");
size = bytes_read + strlen(trans);
buf = realloc(buf, size+1);
strcpy(&buf[bytes_read], trans);
}
}
}
}
else
{ g_mutex_lock(Closure->logLock);
buf = Closure->logString->str;
size = Closure->logString->len;
g_mutex_unlock(Closure->logLock);
}
/*** Create the dialog */
utf = g_locale_to_utf8(title, -1, NULL, NULL, NULL);
dialog = gtk_dialog_new_with_buttons(utf, Closure->window, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, NULL);
g_free(utf);
gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 600);
g_signal_connect_swapped(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, TRUE, TRUE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
lab = gtk_label_new(NULL);
utf = g_locale_to_utf8(explanation, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lab), utf);
g_free(utf);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(vbox), lab, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
scroll_win = gtk_scrolled_window_new(NULL, NULL);
gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 5);
if(scroll_out) *scroll_out = GTK_SCROLLED_WINDOW(scroll_win);
view = gtk_text_view_new();
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
if(buffer_out) *buffer_out = buffer;
gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
gtk_text_buffer_set_text(buffer, buf, size);
gtk_text_buffer_get_start_iter(buffer, &start);
gtk_text_buffer_place_cursor(buffer, &start);
gtk_container_add(GTK_CONTAINER(scroll_win), view);
/* Show it */
gtk_widget_show_all(dialog);
if(*file != '*')
g_free(buf);
return dialog;
}
/*
* About dialog
*/
static void show_modifying(void)
{ ShowTextfile(_("windowtitle|Modifying dvdisaster"),
_("<big>Modifying dvdisaster</big>\n"
"<i>Your changes are not ours.</i>"),
"README.MODIFYING", NULL, NULL);
}
static gint about_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{ GtkWidget *lab = GTK_BIN(widget)->child;
char *label = (char*)data;
char text[strlen(label)+80];
char *utf;
static int inside;
switch(event->type)
{ case GDK_BUTTON_PRESS:
if(!inside) return FALSE; /* Defect in certain Gtk versions? */
if(!strcmp(label,"GPL")) ShowGPL();
else if(!strcmp(label,"MODIFYING")) show_modifying();
else if(strlen(label) > 4 && !strncmp(label, "http", 4)) ShowHTML(g_strdup(label));
else ShowPDF(g_strdup(label));
break;
case GDK_ENTER_NOTIFY:
g_sprintf(text, "<span underline=\"single\" color=\"blue\">%s</span>", label);
utf = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lab), utf);
g_free(utf);
inside = TRUE;
break;
case GDK_LEAVE_NOTIFY:
g_sprintf(text, "<span color=\"blue\">%s</span>", label);
utf = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lab), utf);
g_free(utf);
inside = FALSE;
break;
default: break;
}
return FALSE;
}
void AboutText(GtkWidget *parent, char *format, ...)
{ GtkWidget *lab;
char *tmp, *utf_text;
va_list argp;
va_start(argp, format);
lab = gtk_label_new(NULL);
tmp = g_strdup_vprintf(format, argp);
utf_text = g_locale_to_utf8(tmp, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lab), utf_text);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(parent), lab, FALSE, FALSE, 0);
g_free(tmp);
g_free(utf_text);
va_end(argp);
}
void AboutLink(GtkWidget *parent, char *label, char *action)
{ GtkWidget *ebox,*lab;
char text[strlen(label)+80];
char *label_copy = strdup(label);
char *utf;
ebox = gtk_event_box_new();
gtk_widget_set_events(ebox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK);
g_signal_connect(G_OBJECT(ebox), "button_press_event", G_CALLBACK(about_cb), (gpointer)action);
g_signal_connect(G_OBJECT(ebox), "enter_notify_event", G_CALLBACK(about_cb), (gpointer)label_copy);
g_signal_connect(G_OBJECT(ebox), "leave_notify_event", G_CALLBACK(about_cb), (gpointer)label_copy);
gtk_box_pack_start(GTK_BOX(parent), ebox, FALSE, FALSE, 0);
lab = gtk_label_new(NULL);
g_sprintf(text, "<span color=\"blue\">%s</span>", label);
utf = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lab), utf);
gtk_container_add(GTK_CONTAINER(ebox), lab);
g_free(utf);
}
void AboutTextWithLink(GtkWidget *parent, char *text, char *action)
{ char *copy,*head,*end_of_line;
char *link_start,*link_end;
char *utf;
head = copy = g_strdup(text);
while(*head)
{ end_of_line = strchr(head, '\n');
if(end_of_line && *end_of_line == '\n')
*end_of_line = 0;
link_start = strchr(head, '[');
link_end = strchr(head, ']');
if(link_start && link_end)
{ GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
*link_start++ = *link_end++ = 0;
if(*head)
{ GtkWidget *lab = gtk_label_new(NULL);
utf = g_locale_to_utf8(head, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lab), utf);
gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 0);
g_free(utf);
}
AboutLink(hbox, link_start, action);
if(*link_end)
{ GtkWidget *lab = gtk_label_new(NULL);
utf = g_locale_to_utf8(link_end, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lab), utf);
gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 0);
g_free(utf);
}
}
else AboutText(parent, head);
if(end_of_line) head = end_of_line+1;
else break;
}
g_free(copy);
}
void AboutDialog()
{ GtkWidget *about, *vbox, *sep;
char *text;
#ifndef MODIFIED_SOURCE
const char *lang;
#endif
/* Create the dialog */
about = gtk_dialog_new_with_buttons(_utf("windowtitle|About dvdisaster"),
Closure->window, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, NULL);
g_signal_connect_swapped(about, "response", G_CALLBACK(gtk_widget_destroy), about);
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(about)->vbox), vbox, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
/* Insert the labels */
text = g_strdup_printf("<span weight=\"bold\" size=\"xx-large\">dvdisaster</span><i> "
"Version %s</i>",
Closure->cookedVersion);
AboutText(vbox, text);
g_free(text);
#ifdef MODIFIED_SOURCE
AboutTextWithLink(vbox,
_("[Modified version]\n"
"Copyright 2019-2020 Stephane Lesimple\n"
"Copyright 2005-2017 Debian Optical Media Tools Team\n"
"Copyright 2004-2017 Carsten Gnoerlich"),
"MODIFYING");
#else
AboutText(vbox, _("Copyright 2004-2017 Carsten Gnoerlich"));
#endif
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 10);
AboutText(vbox, _("dvdisaster provides a margin of safety against data loss\n"
"on optical media caused by aging or scratches.\n"
"It creates error correction data which is used to recover\n"
"unreadable sectors if the disc becomes damaged later on.\n"));
AboutTextWithLink(vbox, _("This software comes with <b>absolutely no warranty</b>.\n"
"This is free software and you are welcome to redistribute it\n"
"under the conditions of the [GNU General Public License].\n"),
"GPL");
#ifdef MODIFIED_SOURCE
AboutTextWithLink(vbox, _("\nThis version is <b>not the original</b>. It has been patched\n"
"for Debian to support DVD-ROMs (with and without encryption),\n"
"and subsequently patched again to support a CLI-only build, among other things.\n\n"
"Please do not bother the original authors of dvdisaster nor the Debian maintainer\n"
"but submit bugreports against [GitHub] instead.\n"),
"https://github.com/speed47/dvdisaster");
#else
lang = g_getenv("LANG");
if(lang && !strncmp(lang, "de", 2))
{ AboutTextWithLink(vbox, "\n[http://www.dvdisaster.de]", "http://www.dvdisaster.de");
}
else
{ AboutTextWithLink(vbox, "\n[http://www.dvdisaster.com]", "http://www.dvdisaster.com");
}
AboutText(vbox, _("\ne-mail: carsten@dvdisaster.org -or- cgnoerlich@fsfe.org"));
#ifdef SYS_NETBSD
AboutText(vbox, _("\nNetBSD port: Sergey Svishchev &lt;svs@ropnet.ru&gt;"));
#endif
#endif
/* Show it */
gtk_widget_show_all(about);
}

2139
src/heuristic-lec.c Normal file

File diff suppressed because it is too large Load Diff

83
src/icon-factory.c Normal file
View File

@@ -0,0 +1,83 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#include "inlined-icons.h"
/***
*** Create our icon factory
***/
static GdkPixbuf* create_icon(GtkIconFactory *ifact, char *name, const guint8 *inline_data)
{ GdkPixbuf *pb;
GtkIconSet *iset;
int width, height, rowstride;
/* gdk_pixbuf_new_from_inline() deprecated; recommended to replace with GResource XML crap.
One day I'll get rid of GTK+. I swear.
pb = gdk_pixbuf_new_from_inline(-1, inline_data, FALSE, NULL);
*/
rowstride = (inline_data[12] << 24) + (inline_data[13] << 16) + (inline_data[14] << 8) + inline_data[15];
width = (inline_data[16] << 24) + (inline_data[17] << 16) + (inline_data[18] << 8) + inline_data[19];
height = (inline_data[20] << 24) + (inline_data[21] << 16) + (inline_data[22] << 8) + inline_data[23];
pb = gdk_pixbuf_new_from_data(inline_data+24, GDK_COLORSPACE_RGB, TRUE, 8,
width, height, rowstride, NULL, NULL);
iset = gtk_icon_set_new_from_pixbuf(pb);
gtk_icon_factory_add(ifact, name, iset);
return pb;
}
void CreateIconFactory()
{ GtkIconFactory *ifact;
/*** Create and register our icon factory */
ifact = gtk_icon_factory_new();
gtk_icon_factory_add_default(ifact);
/*** Our action icons */
create_icon(ifact, "dvdisaster-open-ecc", dvdisaster_open_ecc);
create_icon(ifact, "dvdisaster-open-img", dvdisaster_open_img);
create_icon(ifact, "dvdisaster-cd", dvdisaster_cd);
create_icon(ifact, "dvdisaster-read", dvdisaster_read);
Closure->windowIcon = create_icon(ifact, "dvdisaster-create", dvdisaster_create);
create_icon(ifact, "dvdisaster-scan", dvdisaster_scan);
create_icon(ifact, "dvdisaster-fix", dvdisaster_fix);
create_icon(ifact, "dvdisaster-verify", dvdisaster_verify);
/*** Stock GTK icons to defeat theming */
create_icon(ifact, "dvdisaster-gtk-help", dvdisaster_gtk_help);
create_icon(ifact, "dvdisaster-gtk-index", dvdisaster_gtk_index);
create_icon(ifact, "dvdisaster-gtk-preferences", dvdisaster_gtk_preferences);
create_icon(ifact, "dvdisaster-gtk-quit", dvdisaster_gtk_quit);
create_icon(ifact, "dvdisaster-gtk-stop", dvdisaster_gtk_stop);
}

349
src/image.c Normal file
View File

@@ -0,0 +1,349 @@
/* 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 "scsi-layer.h"
#include "udf.h"
/***
*** Open image from a given device or file.
***
* Note that OpenImageFromDevice() is located in scsi-layer.c
* for convenience reasons.
*/
Image *OpenImageFromFile(char *filename, int flags, mode_t mode)
{ LargeFile *file;
file = LargeOpen(filename, flags, mode);
if(file)
{ Image *image = g_malloc0(sizeof(Image));
image->fpSector = -1;
image->type = IMAGE_FILE;
image->file = file;
CalcSectors(file->size, &image->sectorSize, &image->inLast);
ExamineUDF(image);
ExamineECC(image);
GetImageFingerprint(image, NULL, FINGERPRINT_SECTOR);
return image;
}
return NULL;
}
/***
*** Find out whether the image contains ECC information.
***/
void ExamineECC(Image *image)
{ int i;
Verbose("ExamineECC() started\n");
for(i=0; i<Closure->methodList->len; i++)
{ Method *method = g_ptr_array_index(Closure->methodList, i);
char buf[5];
strncpy(buf,method->name,4);
buf[4]=0;
Verbose("...trying %s\n", buf);
if( method->recognizeEccImage
&& method->recognizeEccImage(image))
{ Verbose("...augmented image found\n");
image->eccMethod = method;
image->expectedSectors = method->expectedImageSize(image);
return;
}
}
Verbose("...no augmented image detected.\n");
}
/***
*** Open a ECC file for an existing image
***/
Image* OpenEccFileForImage(Image *image, char *filename, int flags, mode_t mode)
{ int new_image = 0;
int i;
if(!image)
{ new_image = 1;
image = g_malloc0(sizeof(Image));
}
image->eccFile = LargeOpen(filename, flags, mode);
if(errno == EACCES)
image->eccFileState = ECCFILE_NOPERM;
else image->eccFileState = ECCFILE_MISSING;
if(!image->eccFile)
{ if(new_image)
{ g_free(image);
return NULL;
}
return image;
}
/* Determine codec for ecc file */
for(i=0; i<Closure->methodList->len; i++)
{ Method *method = g_ptr_array_index(Closure->methodList, i);
if(method->recognizeEccFile)
{ image->eccFileState = method->recognizeEccFile(image->eccFile, &image->eccFileHeader);
if(image->eccFileState == ECCFILE_PRESENT)
{ image->eccFileMethod = method;
image->expectedSectors = method->expectedImageSize(image);
GetImageFingerprint(image, NULL, image->eccFileHeader->fpSector);
return image;
}
}
}
/* bail out (unrecognized ecc file) */
LargeClose(image->eccFile);
image->eccFile = NULL;
if(new_image)
{ g_free(image);
return NULL;
}
return image;
}
/*
* Report inconsistencies found in image and ecc files.
*/
int ReportImageEccInconsistencies(Image *image)
{
/* No image file */
if(!image || image->type == IMAGE_NONE)
{ if(image) CloseImage(image);
#ifndef CLI
if(Closure->guiMode)
{ CreateMessage(_("Image file %s not present or permission denied.\n"), GTK_MESSAGE_ERROR, Closure->imageName);
return TRUE;
}
else
#endif
{ Stop(_("Image file %s not present or permission denied.\n"), Closure->imageName);
}
}
/* Image file but unknown ecc */
if(image->eccFile && !image->eccFileMethod)
{ CloseImage(image);
#ifndef CLI
if(Closure->guiMode)
{ CreateMessage(_("\nError correction file type unknown.\n"), GTK_MESSAGE_ERROR);
return TRUE;
}
else
#endif
{ Stop(_("\nError correction file type unknown.\n"));
}
}
/* Permission denied for ecc file */
if(!image->eccFile && image->eccFileState == ECCFILE_NOPERM)
{ CloseImage(image);
#ifndef CLI
if(Closure->guiMode)
{ CreateMessage(_("\nPermission denied on ecc file (perhaps not writeable?).\n"),
GTK_MESSAGE_ERROR);
return TRUE;
}
else
#endif
{ Stop(_("\nPermission denied on ecc file (perhaps not writeable?).\n"));
}
}
/* Augmented image but unknown ecc method */
if(!image->eccFile && !image->eccMethod)
{ CloseImage(image);
#ifndef CLI
if(Closure->guiMode)
{ CreateMessage(_("\nNo error correction file present.\n"
"No error correction data recognized in image.\n"), GTK_MESSAGE_ERROR);
return TRUE;
}
else
#endif
{ Stop(_("\nNo error correction file present.\n"
"No error correction data recognized in image.\n"));
}
}
return FALSE;
}
/***
*** Image access functions
***
* Once the image has been opened, the following functions
* provide transparent access to it (e.g. it does not matter
* whether the image resides in a file or on a optical drive.
* However these functions are not optimized for recovering
* data from a defective optical medium - go through the
* DeviceHandle related functions in scsi-layer.c in that case.
*/
// fixme: Demand AlignedBuffer instead of void *buf
int ImageReadSectors(Image* image, void *buf, guint64 first, int n)
{ guint64 first_defect;
switch(image->type)
{ case IMAGE_FILE:
if(!LargeSeek(image->file, first*2048))
Stop("ImageReadSectors(): seek failed");
if(LargeRead(image->file, buf, n*2048) == n*2048)
{ if(CheckForMissingSectors(buf, first, NULL, 0, n, &first_defect) == SECTOR_PRESENT)
return n;
else return 0;
}
else return 0;
break;
case IMAGE_MEDIUM:
if(!ReadSectorsFast(image->dh, buf, first, n))
return n;
else return 0;
break;
}
return 0;
}
/*
* Get the md5sum from the specified sector.
* Results are cached in the Image as multiple queries may occur.
*/
int GetImageFingerprint(Image *image, guint8 *fp_out, guint64 sector)
{ AlignedBuffer *ab;
int status;
/* Sector already cached? */
if(image->fpSector == sector)
switch(image->fpState)
{ case 0: /* not read */
break;
case 1: /* unreadable */
if(fp_out)
memset(fp_out, 0, 16);
Verbose("GetImageFingerprint(%lld): cached unreadable\n", sector);
return FALSE;
case 2: /* already cached */
if(fp_out)
memcpy(fp_out, image->imageFP, 16);
Verbose("GetImageFingerprint(%lld): cached\n", sector);
return TRUE;
}
ab = CreateAlignedBuffer(2048);
status = ImageReadSectors(image, ab->buf, sector, 1);
image->fpSector = sector;
if(status != 1) /* read error */
{ image->fpState = 1;
Verbose("GetImageFingerprint(%lld): not readable\n", sector);
}
else
{ struct MD5Context md5ctxt;
image->fpState = 2;
MD5Init(&md5ctxt);
MD5Update(&md5ctxt, ab->buf, 2048);
MD5Final(image->imageFP, &md5ctxt);
if(fp_out)
memcpy(fp_out, image->imageFP, 16);
Verbose("GetImageFingerprint(%lld): read & cached\n", sector);
}
FreeAlignedBuffer(ab);
return image->fpState == 2;
}
/*
* Truncate the image, update internal structures
*/
int TruncateImage(Image *image, guint64 new_size)
{ int result;
if(image->type != IMAGE_FILE)
Stop("TruncateImage: called for non-file type image!\n");
result = LargeTruncate(image->file, (gint64)new_size);
CalcSectors(image->file->size, &image->sectorSize, &image->inLast);
return result;
}
/*
* Close the image
*/
void CloseImage(Image *image)
{
switch(image->type)
{ case IMAGE_FILE:
LargeClose(image->file);
break;
case IMAGE_MEDIUM:
CloseDevice(image->dh);
break;
}
if(image->eccHeader)
g_free(image->eccHeader);
if(image->isoInfo)
FreeIsoInfo(image->isoInfo);
if(image->eccFile)
LargeClose(image->eccFile);
if(image->eccFileHeader)
g_free(image->eccFileHeader);
if(image->cachedLayout)
g_free(image->cachedLayout);
g_free(image);
}

491
src/l-ec.c Normal file
View File

@@ -0,0 +1,491 @@
/* 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 "galois-inlines.h"
/***
*** Mapping between cd frame and parity vectors
***/
/*
* Mapping of frame bytes to P/Q Vectors
*/
int PToByteIndex(int p, int i)
{ return 12 + p + i*86;
}
void ByteIndexToP(int b, int *p, int *i)
{ *p = (b-12)%86;
*i = (b-12)/86;
}
int QToByteIndex(int q, int i)
{ int offset = 12 + (q & 1);
if(i == 43) return 2248+q;
if(i == 44) return 2300+q;
q&=~1;
return offset + (q*43 + i*88) % 2236;
}
void ByteIndexToQ(int b, int *q, int *i)
{ int x,y,offset;
if(b >= 2300)
{ *i = 44;
*q = (b-2300);
return;
}
if(b >= 2248)
{ *i = 43;
*q = (b-2248);
return;
}
offset = b&1;
b = (b-12)/2;
x = b/43;
y = (b-(x*43))%26;
*i = b-(x*43);
*q = 2*((x+26-y)%26)+offset;
}
/*
* Debugging output
*/
void PrintVector(unsigned char *vector, int len, int n)
{ int i;
g_printf("%c#%2d =", len == P_VECTOR_SIZE ? 'P' : 'Q', n);
for(i=0; i<len; i++)
g_printf(" %2x", vector[i]);
g_printf("\n");
}
/*
* There are 86 vectors of P-parity, yielding a RS(26,24) code.
*/
void GetPVector(unsigned char *frame, unsigned char *data, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
data[i] = frame[w_idx];
}
void SetPVector(unsigned char *frame, unsigned char *data, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] = data[i];
}
void FillPVector(unsigned char *frame, unsigned char data, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] = data;
}
void OrPVector(unsigned char *frame, unsigned char value, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] |= value;
}
void AndPVector(unsigned char *frame, unsigned char value, int n)
{ int i;
int w_idx = n+12;
for(i=0; i<26; i++, w_idx+=86)
frame[w_idx] &= value;
}
/*
* There are 52 vectors of Q-parity, yielding a RS(45,43) code.
*/
void GetQVector(unsigned char *frame, unsigned char *data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
data[i] = frame[(w_idx % 2236) + offset];
data[43] = frame[2248 + n];
data[44] = frame[2300 + n];
}
void SetQVector(unsigned char *frame, unsigned char *data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] = data[i];
frame[2248 + n] = data[43];
frame[2300 + n] = data[44];
}
void FillQVector(unsigned char *frame, unsigned char data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] = data;
frame[2248 + n] = data;
frame[2300 + n] = data;
}
void OrQVector(unsigned char *frame, unsigned char data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] |= data;
frame[2248 + n] |= data;
frame[2300 + n] |= data;
}
void AndQVector(unsigned char *frame, unsigned char data, int n)
{ int offset = 12 + (n & 1);
int w_idx = (n&~1) * 43;
int i;
for(i=0; i<43; i++, w_idx+=88)
frame[(w_idx % 2236) + offset] &= data;
frame[2248 + n] &= data;
frame[2300 + n] &= data;
}
/***
*** C2 error counting
***/
int CountC2Errors(unsigned char *frame)
{ int i,count = 0;
frame += 2352;
for(i=0; i<294; i++, frame++)
{ if(*frame & 0x01) count++;
if(*frame & 0x02) count++;
if(*frame & 0x04) count++;
if(*frame & 0x08) count++;
if(*frame & 0x10) count++;
if(*frame & 0x20) count++;
if(*frame & 0x40) count++;
if(*frame & 0x80) count++;
}
return count;
}
/***
*** L-EC error correction for CD raw data sectors
***/
/*
* These could be used from ReedSolomonTables,
* but hardcoding them is faster.
*/
#define NROOTS 2
#define LEC_FIRST_ROOT 0 //GF_ALPHA0
#define LEC_PRIM_ELEM 1
#define LEC_PRIMTH_ROOT 1
/*
* Calculate the error syndrome
*/
int DecodePQ(ReedSolomonTables *rt, unsigned char *data, int padding,
int *erasure_list, int erasure_count)
{ GaloisTables *gt = rt->gfTables;
int syndrome[NROOTS];
int lambda[NROOTS+1];
int omega[NROOTS+1];
int b[NROOTS+1];
int reg[NROOTS+1];
int root[NROOTS];
int loc[NROOTS];
int syn_error;
int deg_lambda,lambda_roots;
int deg_omega;
int shortened_size = GF_FIELDMAX - padding;
int corrected = 0;
int i,j,k;
int r,el;
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
for(i=0; i<NROOTS; i++)
syndrome[i] = data[0];
for(j=1; j<shortened_size; j++)
for(i=0; i<NROOTS; i++)
if(syndrome[i] == 0)
syndrome[i] = data[j];
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
/*** Convert syndrome to index form, check for nonzero condition. */
syn_error = 0;
for(i=0; i<NROOTS; i++)
{ syn_error |= syndrome[i];
syndrome[i] = gt->indexOf[syndrome[i]];
}
/*** If the syndrome is zero, everything is fine. */
if(!syn_error)
return 0;
/*** Initialize lambda to be the erasure locator polynomial */
lambda[0] = 1;
lambda[1] = lambda[2] = 0;
erasure_list[0] += padding;
erasure_list[1] += padding;
if(erasure_count > 2) /* sanity check */
erasure_count = 0;
if(erasure_count > 0)
{ lambda[1] = gt->alphaTo[mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
for(i=1; i<erasure_count; i++)
{ int u = mod_fieldmax(LEC_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[i]));
for(j=i+1; j>0; j--)
{ int tmp = gt->indexOf[lambda[j-1]];
if(tmp != GF_ALPHA0)
lambda[j] ^= gt->alphaTo[mod_fieldmax(u + tmp)];
}
}
}
for(i=0; i<NROOTS+1; i++)
b[i] = gt->indexOf[lambda[i]];
/*** Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
r = erasure_count; /* r is the step number */
el = erasure_count;
/* Compute discrepancy at the r-th step in poly-form */
while(++r <= NROOTS)
{ int discr_r = 0;
for(i=0; i<r; i++)
if((lambda[i] != 0) && (syndrome[r-i-1] != GF_ALPHA0))
discr_r ^= gt->alphaTo[mod_fieldmax(gt->indexOf[lambda[i]] + syndrome[r-i-1])];
discr_r = gt->indexOf[discr_r];
if(discr_r == GF_ALPHA0)
{ /* B(x) = x*B(x) */
memmove(b+1, b, NROOTS*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
else
{ int t[NROOTS+1];
/* T(x) = lambda(x) - discr_r*x*b(x) */
t[0] = lambda[0];
for(i=0; i<NROOTS; i++)
{ if(b[i] != GF_ALPHA0)
t[i+1] = lambda[i+1] ^ gt->alphaTo[mod_fieldmax(discr_r + b[i])];
else t[i+1] = lambda[i+1];
}
if(2*el <= r+erasure_count-1)
{ el = r + erasure_count - el;
/* B(x) <-- inv(discr_r) * lambda(x) */
for(i=0; i<=NROOTS; i++)
b[i] = (lambda[i] == 0) ? GF_ALPHA0
: mod_fieldmax(gt->indexOf[lambda[i]] - discr_r + GF_FIELDMAX);
}
else
{ /* 2 lines below: B(x) <-- x*B(x) */
memmove(b+1, b, NROOTS*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
memcpy(lambda, t, (NROOTS+1)*sizeof(t[0]));
}
}
/*** Convert lambda to index form and compute deg(lambda(x)) */
deg_lambda = 0;
for(i=0; i<NROOTS+1; i++)
{ lambda[i] = gt->indexOf[lambda[i]];
if(lambda[i] != GF_ALPHA0)
deg_lambda = i;
}
/*** Find roots of the error+erasure locator polynomial by Chien search */
memcpy(reg+1, lambda+1, NROOTS*sizeof(reg[0]));
lambda_roots = 0; /* Number of roots of lambda(x) */
for(i=1, k=LEC_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+LEC_PRIMTH_ROOT))
{ int q=1; /* lambda[0] is always 0 */
for(j=deg_lambda; j>0; j--)
{ if(reg[j] != GF_ALPHA0)
{ reg[j] = mod_fieldmax(reg[j] + j);
q ^= gt->alphaTo[reg[j]];
}
}
if(q != 0) continue; /* Not a root */
/* store root in index-form and the error location number */
root[lambda_roots] = i;
loc[lambda_roots] = k;
/* If we've already found max possible roots, abort the search to save time */
if(++lambda_roots == deg_lambda) break;
}
/* deg(lambda) unequal to number of roots => uncorrectable error detected
This is not reliable for very small numbers of roots, e.g. nroots = 2 */
if(deg_lambda != lambda_roots)
{ return -1;
}
/* Compute err+eras evaluator poly omega(x) = syn(x)*lambda(x)
(modulo x**nroots). in index form. Also find deg(omega). */
deg_omega = deg_lambda-1;
for(i=0; i<=deg_omega; i++)
{ int tmp = 0;
for(j=i; j>=0; j--)
{ if((syndrome[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0))
tmp ^= gt->alphaTo[mod_fieldmax(syndrome[i - j] + lambda[j])];
}
omega[i] = gt->indexOf[tmp];
}
/* Compute error values in poly-form.
num1 = omega(inv(X(l))),
num2 = inv(X(l))**(FIRST_ROOT-1) and
den = lambda_pr(inv(X(l))) all in poly-form. */
for(j=lambda_roots-1; j>=0; j--)
{ int num1 = 0;
int num2;
int den;
int location = loc[j];
for(i=deg_omega; i>=0; i--)
{ if(omega[i] != GF_ALPHA0)
num1 ^= gt->alphaTo[mod_fieldmax(omega[i] + i * root[j])];
}
num2 = gt->alphaTo[mod_fieldmax(root[j] * (LEC_FIRST_ROOT - 1) + GF_FIELDMAX)];
den = 0;
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
for(i=MIN(deg_lambda, NROOTS-1) & ~1; i>=0; i-=2)
{ if(lambda[i+1] != GF_ALPHA0)
den ^= gt->alphaTo[mod_fieldmax(lambda[i+1] + i * root[j])];
}
/* Apply error to data */
if(num1 != 0 && location >= padding)
{
corrected++;
data[location-padding] ^= gt->alphaTo[mod_fieldmax(gt->indexOf[num1] + gt->indexOf[num2]
+ GF_FIELDMAX - gt->indexOf[den])];
/* If no erasures were given, at most one error was corrected.
Return its position in erasure_list[0]. */
if(!erasure_count)
erasure_list[0] = location-padding;
}
#if 1
else return -3;
#endif
}
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
for(i=0; i<NROOTS; i++)
syndrome[i] = data[0];
for(j=1; j<shortened_size; j++)
for(i=0; i<NROOTS; i++)
{ if(syndrome[i] == 0)
syndrome[i] = data[j];
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
+ (LEC_FIRST_ROOT+i)*LEC_PRIM_ELEM)];
}
/*** Convert syndrome to index form, check for nonzero condition. */
#if 1
for(i=0; i<NROOTS; i++)
if(syndrome[i])
return -2;
#endif
return corrected;
}

383
src/large-io.c Normal file
View File

@@ -0,0 +1,383 @@
/* 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"
/***
*** Wrappers around the standard low level file system interface.
***
* This is pointless for GNU/Linux, but gives us the possibility to
* hide differences in OS-specific semantics.
*
* Note the different return value semantics from standard functions:
* - LargeOpen() returns a LargeFile pointer on success and NULL otherwise;
* - LargeRead() and LargeWrite() return the number of bytes read/written;
* - the remaining functions return True on success or False on failure.
*
* Also, individual behaviour may deviate from standard functions.
*/
#ifdef SYS_MINGW
#include <windows.h>
#define large_stat _stati64
#define large_lseek _lseeki64
/* The original windows ftruncate has off_size (32bit) */
int large_ftruncate(int fd, gint64 size)
{ gint32 handle;
if((handle=_get_osfhandle(fd)) == -1)
return -1;
if(_lseeki64(fd, size, SEEK_SET) == -1)
return -1;
if(SetEndOfFile((HANDLE)handle) == 0)
return -1;
return 0;
}
#else
#define large_ftruncate ftruncate
#define large_stat stat
#define large_lseek lseek
#endif /* SYS_MINGW */
/*
* convert special chars in file names to correct OS encoding
*/
static gchar* os_path(char *path_in)
{ gchar *cp_path = g_filename_from_utf8(path_in, -1, NULL, NULL, NULL);
if(cp_path == NULL)
{ errno = EINVAL;
return NULL;
}
REMEMBER(cp_path);
return cp_path;
}
/*
* Large stat replacement (queries only file size)
*/
int LargeStat(char *path, guint64 *length_return)
{ struct stat mystat;
gchar *cp_path = os_path(path);
if(!cp_path) return FALSE;
if(large_stat(cp_path, &mystat) == -1)
{ g_free(cp_path);
return FALSE;
}
g_free(cp_path);
if(!S_ISREG(mystat.st_mode))
return FALSE;
*length_return = mystat.st_size;
return TRUE;
}
/*
* Stat() variant for testing directories
*/
int DirStat(char *path)
{ struct stat mystat;
gchar *cp_path = os_path(path);
if(!cp_path) return FALSE;
if(large_stat(cp_path, &mystat) == -1)
{ g_free(cp_path);
return FALSE;
}
g_free(cp_path);
if(!S_ISDIR(mystat.st_mode))
return FALSE;
return TRUE;
}
/*
* Open a file
*/
LargeFile* LargeOpen(char *name, int flags, mode_t mode)
{ LargeFile *lf = g_malloc0(sizeof(LargeFile));
struct stat mystat;
gchar *cp_path;
#ifdef HAVE_O_LARGEFILE
flags |= O_LARGEFILE;
#endif
#ifdef SYS_MINGW
flags |= O_BINARY;
#endif
cp_path = os_path(name);
if(!cp_path)
{ g_free(lf); return FALSE;
}
/* Do not try to open directories etc. */
if( (large_stat(cp_path, &mystat) == 0)
&& !S_ISREG(mystat.st_mode))
{ g_free(cp_path), g_free(lf); return NULL;
}
lf->fileHandle = open(cp_path, flags, mode);
g_free(cp_path);
if(lf->fileHandle == -1)
{ g_free(lf); return NULL;
}
lf->path = g_strdup(name);
LargeStat(name, &lf->size); /* Do NOT use cp_path! */
lf->flags = flags;
return lf;
}
/*
* Seeking in large files.
* Note: Seeking beyond the end of a split file is undefined.
*/
int LargeSeek(LargeFile *lf, off_t pos)
{
lf->offset = pos;
if(large_lseek(lf->fileHandle, pos, SEEK_SET) != pos)
return FALSE;
return TRUE;
}
/*
* EOF predicate for large files.
*
* Note: Works only correctly for read only files!
*/
int LargeEOF(LargeFile *lf)
{
return lf->offset >= lf->size;
}
/*
* Reading large files
*/
ssize_t LargeRead(LargeFile *lf, void *buf, size_t count)
{ ssize_t n;
n = read(lf->fileHandle, buf, count);
lf->offset += n;
return n;
}
/*
* Writing large files
*/
#ifndef CLI
static void insert_buttons(GtkDialog *dialog)
{
gtk_dialog_add_buttons(dialog,
GTK_STOCK_REDO , 1,
GTK_STOCK_CANCEL, 0, NULL);
}
#endif
static ssize_t xwrite(int fdes, void *buf_base, size_t count)
{ unsigned char *buf = (unsigned char*)buf_base;
ssize_t total = 0;
/* Simply fail when going out of space in command line mode */
#ifndef CLI
if(!Closure->guiMode)
#endif
{ while(count)
{ ssize_t n = write(fdes, buf, count);
if(n<=0) return total; /* error occurred */
if(n>0) /* write at least partially successful */
{ total += n;
count -= n;
buf += n;
}
}
return total;
}
#ifndef CLI
/* Give the user a chance to free more space in GUI mode.
When running out of space, the last write() may complete
with n<count but no error condition, so we try writing
until a real error hits (n = -1). */
while(count)
{ ssize_t n = write(fdes, buf, count);
if(n <= 0) /* error occurred */
{ int answer;
if(errno != ENOSPC) return total;
answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, insert_buttons,
_("Error while writing the file:\n\n%s\n\n"
"You can redo this operation after freeing some space."),
strerror(errno),n);
if(!answer) return total;
}
if(n>0) /* write at least partially successful */
{ total += n;
count -= n;
buf += n;
}
}
return total;
#endif
}
ssize_t LargeWrite(LargeFile *lf, void *buf, size_t count)
{ ssize_t n;
n = xwrite(lf->fileHandle, buf, count);
lf->offset += n;
return n;
}
/*
* Large file closing
*/
int LargeClose(LargeFile *lf)
{ int result = TRUE;
result = (close(lf->fileHandle) == 0);
/* Free the LargeFile struct and return results */
if(lf->path) g_free(lf->path);
g_free(lf);
return result;
}
/*
* Large file truncation
*/
int LargeTruncate(LargeFile *lf, off_t length)
{ int result;
result = (large_ftruncate(lf->fileHandle, length) == 0);
if(result)
lf->size = length;
return result;
}
/*
* Large file unlinking
*/
int LargeUnlink(char *path)
{ gchar *cp_path;
int result;
cp_path = os_path(path);
if(!cp_path) return FALSE;
result = unlink(cp_path);
g_free(cp_path);
return result == 0;
}
/***
*** Wrappers around other IO
***/
FILE *portable_fopen(char *path, char *modes)
{ FILE *file;
char *cp_path;
cp_path = os_path(path);
file = fopen(cp_path, modes);
g_free(cp_path);
return file;
}
#ifdef SYS_MINGW
int portable_mkdir(char *path)
{ int status;
char *cp_path;
cp_path = os_path(path);
status = mkdir(cp_path);
g_free(cp_path);
return status;
}
#endif
/***
*** Convenience functions
***/
/*
* Append the given file suffix if none is already present
*/
char *ApplyAutoSuffix(char *filename, char *suffix)
{ char *out;
if(!filename || !*filename || strrchr(filename, '.'))
return filename;
out = g_strdup_printf("%s.%s",filename,suffix);
g_free(filename);
return out;
}

83
src/logfile.c Normal file
View File

@@ -0,0 +1,83 @@
/* 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 <time.h>
/*
* Create default dotfile path
*/
void DefaultLogFile()
{
#ifndef SYS_MINGW
Closure->logFile = g_strdup_printf("%s/.dvdisaster.log", g_getenv("HOME"));
#else
Closure->logFile = g_strdup_printf("%s/dvdisaster.log", Closure->homeDir); /* portable mode */
#endif
}
/*
* Print time stamp to log file
*/
void LogTimeStamp()
{ time_t now;
if(!Closure->logFileEnabled)
return;
time(&now);
PrintLogFile("*\n* %s\n* logging started at %s*\n",
Closure->versionString, ctime(&now));
}
/*
* Print a message to the log file.
* Tries hard to make the log messages survive a system crash.
*/
void VPrintLogFile(char *format, va_list argp)
{ FILE *file;
if(!Closure->logFileStamped)
{ Closure->logFileStamped = TRUE;
LogTimeStamp();
}
file = fopen(Closure->logFile, "a");
if(!file)
return;
g_vfprintf(file, format, argp);
fflush(file);
fclose(file);
}
void PrintLogFile(char *format, ...)
{ va_list argp;
va_start(argp, format);
VPrintLogFile(format, argp);
va_end(argp);
}

497
src/main-window.c Normal file
View File

@@ -0,0 +1,497 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
/***
*** Global callbacks
***/
static void destroy_cb(GtkWidget *widget, gpointer data)
{
/* If an action is currently running with spawned threads,
give them time to terminate cleanly. */
if(Closure->subThread)
{ Closure->stopActions = STOP_SHUTDOWN_ALL;
g_thread_join(Closure->subThread);
}
gtk_main_quit();
}
static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{
return FALSE;
}
/***
*** The right-side action buttons
***/
/*
* Callback for the action buttons
*/
static void action_cb(GtkWidget *widget, gpointer data)
{ gint action = GPOINTER_TO_INT(data);
Method *method = NULL;
if(action != ACTION_STOP)
{
/* Clear the log buffer, request new log file time stamp */
if(action != ACTION_CREATE_CONT)
{ g_mutex_lock(Closure->logLock);
g_string_truncate(Closure->logString, 0);
g_string_printf(Closure->logString, _("log: %s\n"), Closure->versionString);
g_mutex_unlock(Closure->logLock);
Closure->logFileStamped = FALSE;
}
/* Make sure we're using the current file selections */
g_free(Closure->imageName);
Closure->imageName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->imageEntry)));
if(Closure->autoSuffix)
{ Closure->imageName = ApplyAutoSuffix(Closure->imageName, "iso");
gtk_entry_set_text(GTK_ENTRY(Closure->imageEntry), Closure->imageName);
}
g_free(Closure->eccName);
Closure->eccName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->eccEntry)));
if(Closure->autoSuffix)
{ Closure->eccName = ApplyAutoSuffix(Closure->eccName, "ecc");
gtk_entry_set_text(GTK_ENTRY(Closure->eccEntry), Closure->eccName);
}
/* The ecc file may not be labeled as an .iso image */
if(Closure->eccName)
{ int len = strlen(Closure->eccName);
if(!strcmp(Closure->eccName, Closure->imageName))
{ CreateMessage(_("The .iso image and error correction file\n"
"must not be the same file!\n\n"
"If you intended to create or use an .iso image\n"
"which is augmented with error correction data,\n"
"please leave the error correction file name blank."),
GTK_MESSAGE_ERROR);
return;
}
if(!strcmp(Closure->eccName+len-4, ".iso"))
{ CreateMessage(_("The error correction file type must not be \".iso\".\n\n"
"If you intended to create or use an .iso image\n"
"which is augmented with error correction data,\n"
"please leave the error correction file name blank."),
GTK_MESSAGE_ERROR);
return;
}
}
/* Reset warnings which may be temporarily disabled during an action */
Closure->noMissingWarnings = FALSE;
/* Do not queue up stop actions */
Closure->stopActions = FALSE;
}
/* Dispatch action */
switch(action)
{ case ACTION_STOP:
Closure->stopActions = STOP_CURRENT_ACTION;
break;
case ACTION_READ:
AllowActions(FALSE);
if(Closure->adaptiveRead)
{ gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), 2);
ResetAdaptiveReadWindow();
CreateGThread((GThreadFunc)ReadMediumAdaptive, (gpointer)0);
}
else
{ gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), 1);
Closure->additionalSpiralColor = 1;
ResetLinearReadWindow();
CreateGThread((GThreadFunc)ReadMediumLinear, (gpointer)0);
}
break;
case ACTION_CREATE:
case ACTION_CREATE_CONT:
method = FindMethod(Closure->methodName);
if(!method)
{ CreateMessage(_("\nMethod %s not available.\n"
"Use -m without parameters for a method list.\n"),
GTK_MESSAGE_ERROR, Closure->methodName);
break;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), method->tabWindowIndex);
method->resetCreateWindow(method);
AllowActions(FALSE);
CreateGThread((GThreadFunc)method->create, (gpointer)method);
break;
case ACTION_FIX:
{ Image *image;
image = OpenImageFromFile(Closure->imageName, O_RDWR, IMG_PERMS);
image = OpenEccFileForImage(image, Closure->eccName, O_RDWR, IMG_PERMS);
if(ReportImageEccInconsistencies(image)) /* abort if no method found */
return;
/* Determine method. Ecc files win over augmented ecc. */
if(image && image->eccFileMethod) method = image->eccFileMethod;
else if(image && image->eccMethod) method = image->eccMethod;
else { CreateMessage(_("Internal error: No suitable method for repairing image."),
GTK_MESSAGE_ERROR);
return;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), method->tabWindowIndex+1);
method->resetFixWindow(method);
AllowActions(FALSE);
CreateGThread((GThreadFunc)method->fix, (gpointer)image);
}
break;
case ACTION_SCAN:
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), 1);
Closure->additionalSpiralColor = -1;
ResetLinearReadWindow();
AllowActions(FALSE);
CreateGThread((GThreadFunc)ReadMediumLinear, (gpointer)1);
break;
case ACTION_VERIFY:
/* If something is wrong with the .iso or .ecc files
we fall back to the RS01 method for verifying since it is robust
against missing files. */
{ Image *image;
image = OpenImageFromFile(Closure->imageName, O_RDONLY, IMG_PERMS);
image = OpenEccFileForImage(image, Closure->eccName, O_RDONLY, IMG_PERMS);
/* Determine method. Ecc files win over augmented ecc. */
if(image && image->eccFileMethod) method = image->eccFileMethod;
else if(image && image->eccMethod) method = image->eccMethod;
else if(!(method = FindMethod("RS01")))
{ CreateMessage(_("RS01 method not available for comparing files."),
GTK_MESSAGE_ERROR);
return;
}
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), method->tabWindowIndex+2);
method->resetVerifyWindow(method);
AllowActions(FALSE);
CreateGThread((GThreadFunc)method->verify, (gpointer)image);
break;
}
}
}
/*
* Start an action from another thread
*/
static gboolean action_idle_func(gpointer action)
{
Closure->enableCurveSwitch = TRUE;
action_cb(NULL, action);
return FALSE;
}
void ContinueWithAction(int action)
{
g_idle_add(action_idle_func, GINT_TO_POINTER(action));
}
/*
* Create the action buttons and the associated notebook pages
*/
static GtkWidget *create_button(char *label, char *icon)
{ GtkWidget *button,*box,*image,*lab;
char *utf_label = g_locale_to_utf8(label, -1, NULL, NULL, NULL);
button = gtk_button_new();
box = gtk_vbox_new(FALSE, 0);
image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_LARGE_TOOLBAR);
lab = gtk_label_new(utf_label);
g_free(utf_label);
gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), lab, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(button), box);
// gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
return button;
}
static GtkWidget* create_action_bar(GtkNotebook *notebook)
{ GtkWidget *vbox, *outer_vbox, *wid, *content, *ignore;
int window_number = FIRST_CREATE_WINDOW;
unsigned int i;
outer_vbox = gtk_vbox_new(TRUE, 0);
vbox = gtk_vbox_new(FALSE, 0); /* needed for vertical spacing */
gtk_box_pack_start(GTK_BOX(outer_vbox), vbox, TRUE, TRUE, 3);
/*** Read */
Closure->readButton = wid = create_button(_("button|Read"), "dvdisaster-read");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_READ);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
AttachTooltip(wid, _("tooltip|Read Image"), _("Reads an optical disc image into a file (or tries to complete an existing image file)."));
content = gtk_vbox_new(FALSE, 0); /* read linear window */
ignore = gtk_label_new("read_tab_l");
gtk_notebook_append_page(notebook, content, ignore);
CreateLinearReadWindow(content);
content = gtk_vbox_new(FALSE, 0); /* read adaptive window */
ignore = gtk_label_new("read_tab_a");
gtk_notebook_append_page(notebook, content, ignore);
CreateAdaptiveReadWindow(content);
/*** Create */
Closure->createButton = wid = create_button(_("button|Create"), "dvdisaster-create");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_CREATE);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
AttachTooltip(wid, _("tooltip|Create error correction data"), _("Creates error correction data. Requires an image file."));
/*** Scan */
Closure->scanButton = wid = create_button(_("button|Scan"), "dvdisaster-scan");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_SCAN);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
AttachTooltip(wid, _("tooltip|Scan medium"), _("Scans medium for unreadable sectors."));
/*** Fix */
Closure->fixButton = wid = create_button(_("button|Fix"), "dvdisaster-fix");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_FIX);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
AttachTooltip(wid, _("tooltip|Repair image"), _("Repairs an image. Requires an image file and error correction data."));
/*** Verify */
Closure->testButton = wid = create_button(_("button|Verify"), "dvdisaster-verify");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_VERIFY);
gtk_box_pack_start(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
AttachTooltip(wid, _("tooltip|Consistency check"), _("Tests consistency of error correction data and image file."));
/*** Stop */
wid = create_button(_("button|Stop"), "dvdisaster-gtk-stop");
g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(action_cb), (gpointer)ACTION_STOP);
gtk_box_pack_end(GTK_BOX(vbox), wid, FALSE, FALSE, 0);
AttachTooltip(wid, _("tooltip|Abort action"), _("Aborts an ongoing action."));
/*** Block drive related actions if no drives were found */
if(!Closure->deviceNodes->len)
{ gtk_widget_set_sensitive(Closure->readButton, FALSE);
gtk_widget_set_sensitive(Closure->scanButton, FALSE);
}
/*** Create notebook windows for the methods */
for(i=0; i<Closure->methodList->len; i++)
{ Method *method = g_ptr_array_index(Closure->methodList, i);
method->tabWindowIndex = window_number;
window_number += 3;
/* Create window */
content = gtk_vbox_new(FALSE, 0);
ignore = gtk_label_new("create_tab");
gtk_notebook_append_page(notebook, content, ignore);
method->createCreateWindow(method, content);
/* Fix window */
content = gtk_vbox_new(FALSE, 0);
ignore = gtk_label_new("fix_tab");
gtk_notebook_append_page(notebook, content, ignore);
method->createFixWindow(method, content);
/* Verify window */
content = gtk_vbox_new(FALSE, 0);
ignore = gtk_label_new("verify_tab");
gtk_notebook_append_page(notebook, content, ignore);
method->createVerifyWindow(method, content);
}
return outer_vbox;
}
/***
*** Create the main window
***/
/*
* Show the log data
*/
static void log_cb(GtkWidget *widget, gpointer data)
{ ShowLog();
}
void CreateMainWindow(int *argc, char ***argv)
{ GtkWidget *window,*wid,*outer_box,*middle_box,*status_box,*sep;
GtkWidget *box, *icon, *button;
char title[80];
int sig_okay = TRUE;
/*** Initialize GTK+ */
gtk_init(argc, argv);
/*** Some style tinkering */
gtk_rc_parse_string("style \"dvdisaster-style\"\n"
"{ GtkMenuBar::shadow_type = none\n"
"}\n"
"class \"GtkMenuBar\" style \"dvdisaster-style\"\n");
/*** Create our icons */
CreateIconFactory();
/*** Open the main window */
g_snprintf(title, 80, "dvdisaster-%s", Closure->cookedVersion);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), title);
if(sig_okay)
gtk_window_set_default_size(GTK_WINDOW(window), -1, 550);
gtk_window_set_icon(GTK_WINDOW(window), Closure->windowIcon);
Closure->window = GTK_WINDOW(window);
/* Connect with the close button from the window manager */
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_cb), NULL);
/* and with destroy events */
g_signal_connect(window, "destroy", G_CALLBACK(destroy_cb), NULL);
/*** Initialize the tooltips struct */
Closure->tooltips = gtk_tooltips_new();
/*** Create the sub parts of the GUI */
outer_box = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), outer_box);
/* Menu and tool bar */
wid = CreateMenuBar(outer_box);
gtk_box_pack_start(GTK_BOX(outer_box), wid, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(outer_box), sep, FALSE, FALSE, 0);
wid = CreateToolBar(outer_box);
gtk_box_pack_start(GTK_BOX(outer_box), wid, FALSE, FALSE, 3);
/* Middle part */
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(outer_box), sep, FALSE, FALSE, 0);
middle_box = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(outer_box), middle_box, TRUE, TRUE, 0);
wid = Closure->notebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(wid), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(wid), FALSE);
gtk_box_pack_start(GTK_BOX(middle_box), wid, TRUE, TRUE, 0);
CreateWelcomePage(GTK_NOTEBOOK(Closure->notebook));
wid = create_action_bar((GTK_NOTEBOOK(Closure->notebook)));
gtk_box_pack_end(GTK_BOX(middle_box), wid, FALSE, FALSE, 3);
sep = gtk_vseparator_new();
gtk_box_pack_end(GTK_BOX(middle_box), sep, FALSE, FALSE, 0);
/* Status bar enclosure */
status_box = gtk_hbox_new(FALSE, 0);
gtk_box_pack_end(GTK_BOX(outer_box), status_box, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_end(GTK_BOX(outer_box), sep, FALSE, FALSE, 0);
/* Status bar contents. */
Closure->status = GTK_LABEL(gtk_label_new(NULL));
gtk_label_set_ellipsize(GTK_LABEL(Closure->status), PANGO_ELLIPSIZE_END);
gtk_misc_set_alignment(GTK_MISC(Closure->status), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(status_box), GTK_WIDGET(Closure->status), TRUE, TRUE, 5);
button = gtk_button_new();
gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
gtk_box_pack_end(GTK_BOX(status_box), button, FALSE, FALSE, 5);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(log_cb), NULL);
AttachTooltip(button,
_("tooltip|Protocol for current action"),
_("Displays additional information created during the current or last action."));
box = gtk_hbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(button), box);
icon = gtk_image_new_from_stock("dvdisaster-gtk-index", GTK_ICON_SIZE_SMALL_TOOLBAR);
gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 2);
wid = gtk_label_new(_utf("View log"));
gtk_box_pack_start(GTK_BOX(box), wid, FALSE, FALSE, 0);
/* And enter the main loop */
gtk_widget_show_all(window);
gtk_main();
}

55
src/maintenance.c Normal file
View File

@@ -0,0 +1,55 @@
/* 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"
#if 0
void Maintenance1(char *debug_arg)
{
printf("\nMaintenance stub called with arg: %s\n\n", debug_arg);
exit(0);
}
#else
void Maintenance1(char *debug_arg)
{ GaloisTables *gt = CreateGaloisTables(RS_GENERATOR_POLY);
ReedSolomonTables *rt = CreateReedSolomonTables(gt, RS_FIRST_ROOT, RS_PRIM_ELEM, 32);
unsigned char data[2048], parity[32*2048];
int i;
memset(parity, 0, 32*2048);
for(i=0; i<223; i++)
{ int shift = (rt->shiftInit + i) % 32;
memset(data, i, 2048);
EncodeNextLayer(rt, data, parity, 2048, shift);
}
for(i=0; i<32; i++)
printf("%02x ", parity[i]);
printf("\n");
}
#endif

363
src/md5.c Normal file
View File

@@ -0,0 +1,363 @@
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
/* This code slightly modified to fit into Samba by
abartlet@samba.org Jun 2001
and to fit the cifs vfs by
Steve French sfrench@us.ibm.com */
/* Replacement of __u32 with guint32,
removal of hmac* functions
and addition of AsciiDigest(), print_sum() and main()
by Carsten Gn<47>rlich for the dvdisaster project, 2004-2006. */
/* byteReverse() and MD5Final() changed for clean endian-independent
guint32->unsigned char conversion
by Carsten Gn<47>rlich for the dvdisaster project, 2015. */
#if defined(SIMPLE_MD5SUM) /* simple-md5sum*/
#include <string.h>
#else /* dvdisaster */
#include "dvdisaster.h"
#endif
#include "md5.h"
static void MD5Transform(guint32 buf[4], guint32 const in[16]);
/*
* Note: this code is harmless on little-endian machines.
*/
#ifdef HAVE_LITTLE_ENDIAN
#define byteReverse(buf, longs) /* even more harmless */
#else
static void
byteReverse(unsigned char *buf, unsigned longs)
{
guint32 t;
do {
t = buf[0]; buf[0] = buf[3]; buf[3] = t;
t = buf[1]; buf[1] = buf[2]; buf[2] = t;
buf += 4;
} while (--longs);
}
#endif
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void
MD5Init(struct MD5Context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void
MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
{
register guint32 t;
/* Update bitcount */
t = ctx->bits[0];
if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
ctx->bits[1]++; /* Carry from low to high */
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
/* Handle any leading odd-sized chunks */
if (t) {
unsigned char *p = (unsigned char *) ctx->in + t;
t = 64 - t;
if (len < t) {
memmove(p, buf, len);
return;
}
memmove(p, buf, t);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (guint32 *) ctx->in);
buf += t;
len -= t;
}
/* Process data in 64-byte chunks */
while (len >= 64) {
memmove(ctx->in, buf, 64);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (guint32 *) ctx->in);
buf += 64;
len -= 64;
}
/* Handle any remaining bytes of data. */
memmove(ctx->in, buf, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
MD5Final(unsigned char digest[16], struct MD5Context *ctx)
{
unsigned int count;
unsigned char *p;
unsigned char *in;
/* Compute number of bytes mod 64 */
count = (ctx->bits[0] >> 3) & 0x3F;
/* Set the first char of padding to 0x80. This is safe since there is
always at least one byte free */
p = ctx->in + count;
*p++ = 0x80;
/* Bytes of padding needed to make 64 bytes */
count = 64 - 1 - count;
/* Pad out to 56 mod 64 */
if (count < 8) {
/* Two lots of padding: Pad the first block to 64 bytes */
memset(p, 0, count);
byteReverse(ctx->in, 16);
MD5Transform(ctx->buf, (guint32 *) ctx->in);
/* Now fill the next block with 56 bytes */
memset(ctx->in, 0, 56);
} else {
/* Pad block to 56 bytes */
memset(p, 0, count - 8);
}
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
/* changed by cg to prevent compiler warnings */
in = &ctx->in[56];
#ifdef HAVE_LITTLE_ENDIAN
*in++ = (unsigned char)((ctx->bits[0] ) & 0xff);
*in++ = (unsigned char)((ctx->bits[0] >> 8) & 0xff);
*in++ = (unsigned char)((ctx->bits[0] >> 16) & 0xff);
*in++ = (unsigned char)((ctx->bits[0] >> 24) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] ) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] >> 8) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] >> 16) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] >> 24) & 0xff);
#else
*in++ = (unsigned char)((ctx->bits[0] >> 24) & 0xff);
*in++ = (unsigned char)((ctx->bits[0] >> 16) & 0xff);
*in++ = (unsigned char)((ctx->bits[0] >> 8) & 0xff);
*in++ = (unsigned char)((ctx->bits[0] ) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] >> 24) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] >> 16) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] >> 8) & 0xff);
*in++ = (unsigned char)((ctx->bits[1] ) & 0xff);
#endif
MD5Transform(ctx->buf, (guint32 *) ctx->in);
byteReverse((unsigned char *) ctx->buf, 4);
memmove(digest, ctx->buf, 16);
memset(ctx, 0, sizeof(struct MD5Context)); /* In case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
static void
MD5Transform(guint32 buf[4], guint32 const in[16])
{
register guint32 a, b, c, d;
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
/***
*** The following functions have been added for the dvdisaster project.
***/
/*
* Convert md5 digest into printable format.
*/
static char hextab[] = "0123456789abcdef";
void AsciiDigest(char *out, unsigned char *in)
{ int i,o;
for(i=0, o=0; i<16; i++)
{ out[o++] = hextab[in[i]>>4];
out[o++] = hextab[in[i]&0x0f];
}
out[o] = 0;
}
/*
* Wrapper for creating a simple md5sum binary.
* This emulates "md5sum -b", as md5sum is not available per
* default on all target platforms.
*/
#ifdef SIMPLE_MD5SUM
#include <stdio.h>
static void print_sum(char *path)
{ FILE *file;
struct MD5Context ctxt;
unsigned char buf[32768];
unsigned char digest[16];
char ascii_digest[33];
int size = 0;
file = fopen(path, "rb");
if(!file) return;
MD5Init(&ctxt);
while(!feof(file))
{ int n = fread(buf, 1, 32768, file);
if(!n) break;
MD5Update(&ctxt, buf, n);
size += n;
}
MD5Final(digest, &ctxt);
AsciiDigest(ascii_digest, digest);
printf("%s *%s\n", ascii_digest, path);
fclose(file);
}
int main(int argc, char *argv[])
{ int i;
for(i=1; i<argc; i++)
print_sum(argv[i]);
return 0;
}
#endif

21
src/md5.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef MD5_H
#define MD5_H
#if defined(SIMPLE_MD5SUM)
typedef unsigned int guint32;
#endif
typedef struct MD5Context {
guint32 buf[4];
guint32 bits[2];
unsigned char in[64];
} MD5Context;
void MD5Init(struct MD5Context *context);
void MD5Update(struct MD5Context *context, unsigned char const *buf,
unsigned len);
void MD5Final(unsigned char digest[16], struct MD5Context *context);
void AsciiDigest(char*, unsigned char*);
#endif /* MD5_H */

591
src/medium-info.c Normal file
View File

@@ -0,0 +1,591 @@
/* 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 "scsi-layer.h"
#include "udf.h"
#ifndef CLI
/*
* Local data
*/
typedef struct _medium_info
{ GtkLabel *profileDescr;
GtkLabel *physicalType;
GtkLabel *bookType;
GtkLabel *manufID;
GtkLabel *discStatus;
GtkLabel *usedCapacity1;
GtkLabel *usedCapacity2;
GtkLabel *blankCapacity;
GtkLabel *isoLabel;
GtkLabel *isoSize;
GtkLabel *isoTime;
GtkLabel *eccState;
GtkLabel *eccSize;
GtkLabel *eccVersion;
} medium_info;
/***
*** Find out about the medium
***/
static void print_defaults(medium_info *mi)
{ SetLabelText(mi->physicalType, _("Medium not present"));
SetLabelText(mi->manufID, "-");
SetLabelText(mi->profileDescr, "-");
SetLabelText(mi->discStatus, "-");
SetLabelText(mi->usedCapacity1, "-");
SetLabelText(mi->usedCapacity2, " ");
SetLabelText(mi->blankCapacity, "-");
SetLabelText(mi->isoLabel, "-");
SetLabelText(mi->isoSize, "-");
SetLabelText(mi->isoTime, "-");
SetLabelText(mi->eccState, "-");
SetLabelText(mi->eccSize, "-");
SetLabelText(mi->eccVersion, "-");
}
#endif
static void print_tab(char *label, int tab_width)
{ char *translation=_(label);
int length = tab_width-g_utf8_strlen(translation, -1);
char pad[tab_width+1];
if(length < 1) pad[0] = 0;
else
{ memset(pad, ' ', length);
pad[length]=0;
}
PrintCLI("%s%s", translation, pad);
}
void PrintMediumInfo(void *mi_ptr)
{ Image *image;
DeviceHandle *dh;
#ifndef CLI
medium_info *mi=(medium_info*)mi_ptr;
#endif
char *disc_status;
char *sess_status;
int tab_width=30;
#ifndef CLI
if(!mi) /* create dummy medium_info in CLI mode so that PrintCLIorLabel() won't crash */
{ mi=alloca(sizeof(medium_info));
memset(mi, 0, sizeof(medium_info));
}
#endif
#ifndef CLI
if(Closure->guiMode)
print_defaults(mi);
#endif
image = OpenImageFromDevice(Closure->device);
if(!image) return;
dh = image->dh;
QueryBlankCapacity(dh);
/* Medium properties */
PrintCLI(_("Physical medium info"));
PrintCLI("\n\n");
tab_width=GetLongestTranslation("Medium type:",
"Book type:",
"Manuf.-ID:",
"Drive profile:",
"Disc status:",
"Used sectors:",
"Blank capacity:",
NULL)+1;
print_tab("Medium type:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->physicalType,
#else
NULL,
#endif
"%s\n", dh->typeDescr);
print_tab("Book type:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->bookType,
#else
NULL,
#endif
"%s\n", dh->bookDescr);
print_tab("Manuf.-ID:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->manufID,
#else
NULL,
#endif
"%s\n", dh->manuID);
print_tab("Drive profile:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->profileDescr,
#else
NULL,
#endif
"%s\n", dh->profileDescr);
switch(dh->discStatus&3)
{ case 0: disc_status = g_strdup(_("empty")); break;
case 1: disc_status = g_strdup(_("appendable")); break;
case 2: disc_status = g_strdup(_("finalized")); break;
default: disc_status = g_strdup(_("unknown")); break;
}
switch((dh->discStatus>>2)&3)
{ case 0: sess_status = g_strdup(_("empty")); break;
case 1: sess_status = g_strdup(_("incomplete")); break;
case 2: sess_status = g_strdup(_("damaged")); break;
default: sess_status = g_strdup(_("complete")); break;
}
print_tab("Disc status:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->discStatus,
#else
NULL,
#endif
_("%s (%d sessions; last session %s)\n"),
disc_status, dh->sessions, sess_status);
g_free(disc_status);
g_free(sess_status);
print_tab("Used sectors:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->usedCapacity1,
#else
NULL,
#endif
_("%lld sectors (%lld MiB), from READ CAPACITY\n"),
dh->readCapacity+1, (dh->readCapacity+1)>>9);
print_tab(" ",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->usedCapacity2,
#else
NULL,
#endif
_("%lld sectors (%lld MiB), from DVD structure\n"),
dh->userAreaSize, dh->userAreaSize>>9);
print_tab("Blank capacity:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->blankCapacity,
#else
NULL,
#endif
_("%lld sectors (%lld MiB)\n"),
dh->blankCapacity, (dh->blankCapacity)>>9);
/* Filesystem properties */
if(image->isoInfo)
{ tab_width=GetLongestTranslation("Medium label:",
"File system size:",
"Creation time:",
NULL)+1;
PrintCLI("\n\n");
PrintCLI(_("Filesystem info"));
PrintCLI("\n\n");
print_tab("Medium label:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->isoLabel,
#else
NULL,
#endif
"%s\n", image->isoInfo->volumeLabel);
print_tab("File system size:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->isoSize,
#else
NULL,
#endif
_("%d sectors (%lld MiB)\n"),
image->isoInfo->volumeSize, (gint64)image->isoInfo->volumeSize>>9);
print_tab("Creation time:",tab_width);
PrintCLIorLabel(
#ifndef CLI
mi->isoTime,
#else
NULL,
#endif
"%s\n", image->isoInfo->creationDate);
}
/* Augmented image properties
fixme: verify RS03 correctness */
if(image->eccHeader)
{ EccHeader *eh = image->eccHeader;
int major = eh->creatorVersion/10000;
int minor = (eh->creatorVersion%10000)/100;
int micro = eh->creatorVersion%100;
char method[5];
tab_width=GetLongestTranslation("Error correction data:",
"Augmented image size:",
"dvdisaster version:",
NULL)+1;
PrintCLI("\n\n");
PrintCLI(_("Augmented image info"));
PrintCLI("\n\n");
memcpy(method, eh->method, 4);
method[4] = 0;
print_tab("Error correction data:",tab_width);
#ifndef CLI
PrintCLIorLabel(mi->eccState, _("%s, %d roots, %4.1f%% redundancy.\n"),
#else
PrintCLIorLabel(NULL, _("%s, %d roots, %4.1f%% redundancy.\n"),
#endif
method, eh->eccBytes,
((double)eh->eccBytes*100.0)/(double)eh->dataBytes);
print_tab("Augmented image size:",tab_width);
#ifndef CLI
PrintCLIorLabel(mi->eccSize, _("%lld sectors (%lld MiB)\n"),
#else
PrintCLIorLabel(NULL, _("%lld sectors (%lld MiB)\n"),
#endif
image->expectedSectors, image->expectedSectors>>9);
print_tab("dvdisaster version:", tab_width);
if(micro)
#ifndef CLI
PrintCLIorLabel(mi->eccVersion, "%d.%d.%d", major, minor, micro);
#else
PrintCLIorLabel(NULL, "%d.%d.%d", major, minor, micro);
#endif
else
#ifndef CLI
PrintCLIorLabel(mi->eccVersion, "%d.%d", major, minor);
#else
PrintCLIorLabel(NULL, "%d.%d", major, minor);
#endif
}
/* Clean up */
CloseImage(image);
}
/***
*** GUI callbacks
***/
#ifndef CLI
/*
* Callback for drive selection
*/
static void drive_select_cb(GtkWidget *widget, gpointer data)
{ int n;
char *dnode;
if(!Closure->deviceNodes->len) /* No drives available */
return;
n = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
if(n<0)
return;
dnode = g_ptr_array_index(Closure->deviceNodes, n);
g_free(Closure->device);
Closure->device = g_strdup(dnode);
gtk_combo_box_set_active(GTK_COMBO_BOX(Closure->driveCombo), n);
}
/*
* Callback for updating the medium information
*/
static void update_cb(GtkWidget *widget, gpointer data)
{ PrintMediumInfo((medium_info*)data);
}
/*
* Close notification
*/
static void mi_destroy_cb(GtkWidget *widget, gpointer data)
{
Closure->mediumWindow = NULL;
Closure->mediumDrive = NULL;
g_free(Closure->mediumInfoContext);
}
/***
*** Create the medium info window
***/
void CreateMediumInfoWindow()
{ GtkWidget *dialog,*vbox,*hbox,*table,*button,*lab,*sep,*frame,*combo_box;
medium_info *mi;
int i;
int dev_idx = 0;
if(Closure->mediumWindow)
{ gtk_widget_show(Closure->mediumWindow);
return;
}
/*** Create the dialog */
dialog = gtk_dialog_new_with_buttons(_utf("windowtitle|Medium info"),
Closure->window, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT, NULL);
g_signal_connect_swapped(dialog, "response", G_CALLBACK(gtk_widget_destroy), dialog);
Closure->mediumInfoContext = mi = g_malloc0(sizeof(medium_info));
/*** Inner vbox and title */
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, TRUE, TRUE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
lab = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(lab),
_utf("<big>Medium info</big>\n"
"<i>Properties of the currently inserted medium</i>"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(vbox), lab, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), gtk_label_new(" "), FALSE, FALSE, 0);
/*** Drive selection */
frame = gtk_frame_new(_utf("Drive selection"));
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
hbox = gtk_hbox_new(FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
gtk_container_add(GTK_CONTAINER(frame), hbox);
lab = gtk_label_new(_utf("Drive:"));
gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 0);
lab = gtk_label_new(" ");
gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 0);
combo_box = gtk_combo_box_new_text();
gtk_box_pack_start(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
g_signal_connect(G_OBJECT(combo_box), "changed", G_CALLBACK(drive_select_cb), NULL);
for(i=0; i<Closure->deviceNames->len; i++)
{
gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box),
g_ptr_array_index(Closure->deviceNames,i));
if(!strcmp(Closure->device, g_ptr_array_index(Closure->deviceNodes,i)))
dev_idx = i;
}
if(!Closure->deviceNodes->len)
{ gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), _utf("No drives found"));
}
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), dev_idx);
lab = gtk_label_new(_utf(" "));
gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 0);
button = gtk_button_new_with_label(_utf("Update medium info"));
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(update_cb), mi);
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 10);
/*** Medium info */
frame = gtk_frame_new(_utf("Physical medium info"));
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
table = gtk_table_new(2, 8, FALSE);
gtk_container_set_border_width(GTK_CONTAINER(table), 5);
gtk_container_add(GTK_CONTAINER(frame), table);
lab = gtk_label_new(_utf("Medium type:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->physicalType = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Book type:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->bookType = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Manuf.-ID:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->manufID = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Drive profile:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 3, 4, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->profileDescr = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Disc status:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 4, 5, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->discStatus = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 4, 5, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Used sectors:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 5, 6, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->usedCapacity1 = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 5, 6, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(" ");
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 6, 7, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->usedCapacity2 = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 6, 7, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Blank capacity:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 7, 8, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->blankCapacity = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 7, 8, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
/*** Filesystem info */
frame = gtk_frame_new(_utf("Filesystem info"));
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
table = gtk_table_new(2, 3, FALSE);
gtk_container_set_border_width(GTK_CONTAINER(table), 5);
gtk_container_add(GTK_CONTAINER(frame), table);
lab = gtk_label_new(_utf("Medium label:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->isoLabel = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("File system size:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->isoSize = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Creation time:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->isoTime = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
/*** Error correction info */
frame = gtk_frame_new(_utf("Augmented image info"));
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
table = gtk_table_new(2, 3, FALSE);
gtk_container_set_border_width(GTK_CONTAINER(table), 5);
gtk_container_add(GTK_CONTAINER(frame), table);
lab = gtk_label_new(_utf("Error correction data:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->eccState = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("Augmented image size:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->eccSize = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(_utf("dvdisaster version:"));
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 0, 1, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = gtk_label_new(" ");
mi->eccVersion = GTK_LABEL(lab);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), lab, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
/*** Show it */
g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(mi_destroy_cb), NULL);
Closure->mediumWindow = dialog;
Closure->mediumDrive = combo_box;
gtk_widget_show_all(dialog);
PrintMediumInfo(mi);
}
#endif

353
src/memtrack.c Normal file
View File

@@ -0,0 +1,353 @@
/* 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/>.
*/
#ifdef WITH_MEMDEBUG_YES
#define _GNU_SOURCE
#if !defined(SYS_FREEBSD) /* FreeBSD declares malloc() in stdlib.h */
#include <malloc.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <glib/gprintf.h>
/*
* We're not pulling in dvdisaster.h on purpose...
*/
void Stop(char*, ...);
/***
*** Special routines for keeping track of memory allocation.
***
* memtrack.c currently uses malloc() and friends.
* This is probably a bad idea for those operating systems where g_malloc()
* and malloc() are different and not compatible.
* We must keep an eye on that.
*/
/***
*** Keeping track of allocated pointers.
***/
/*
* A structure containing info about each pointer which was
* produced through our memory allocation routines.
*/
typedef struct _memchunk
{ void *ptr; /* allocated memory chunk */
int size; /* size of chunk */
char *file; /* source file this was allocated in */
int line; /* line number of source file */
} memchunk;
/*
* Since we're compiled in optionally,
* we do not use the common Closure struct.
*/
static struct _memchunk **ptrhash[64]; /* 64 buckets of memory chunks */
static int phCnt[64];
static int phMax[64];
static int currentAllocation; /* current memory allocation */
static int peakAllocation; /* maximum allocation */
static GMutex phMutex;
/*
* Remember an allocated pointer.
*/
void remember(void *ptr, int size, char *file, int line)
{ memchunk *mc;
int hash_idx;
g_mutex_lock(&phMutex);
hash_idx = (((long long)ptr)>>3)&63;
if(phCnt[hash_idx] >= phMax[hash_idx])
{ if(!phMax[hash_idx]) phMax[hash_idx] = 16;
else phMax[hash_idx] *= 2;
if(!(ptrhash[hash_idx] = realloc(ptrhash[hash_idx], sizeof(memchunk*)*phMax[hash_idx])))
Stop("can't realloc memchunk hashtable");
}
if(!(mc=malloc(sizeof(memchunk))))
Stop("can't alloc memchunk");
ptrhash[hash_idx][phCnt[hash_idx]++] = mc;
mc->ptr = ptr;
mc->size = size;
mc->file = file;
mc->line = line;
currentAllocation += size;
if(currentAllocation > peakAllocation)
peakAllocation = currentAllocation;
g_mutex_unlock(&phMutex);
}
/*
* Remove a remembered pointer from the hash bucket.
*/
int forget(void *ptr)
{ memchunk **ptrlist;
int hash_idx;
int i;
g_mutex_lock(&phMutex);
hash_idx = (((long)ptr)>>3)&63;
ptrlist = ptrhash[hash_idx];
for(i=0; i<phCnt[hash_idx]; i++)
if(ptrlist[i]->ptr==ptr)
{ currentAllocation -= ptrlist[i]->size;
free(ptrlist[i]);
phCnt[hash_idx]--;
if(phCnt[hash_idx] > 0)
ptrlist[i] = ptrlist[phCnt[hash_idx]];
g_mutex_unlock(&phMutex);
return 0;
}
g_mutex_unlock(&phMutex);
return 1;
}
/*
* Print the contents of the ptrlist.
*/
static void print_ptr(memchunk *mc, int size)
{ char strbuf[16];
char *ptr = (char*)mc->ptr;
int j;
/* print the pointer */
for(j=0; j<15; j++)
{ if(ptr[j]<32) break;
strbuf[j] = ptr[j];
}
#ifdef HAVE_64BIT
if(j)
{ strbuf[j]=0;
g_printf("Address 0x%llx (\"%s\"), %d bytes, from %s, line %d\n",
(unsigned long long)mc->ptr,strbuf,mc->size,mc->file,mc->line);
}
else
g_printf("Address 0x%llx (binary data), %d bytes, from %s, line %d\n",
(unsigned long long)mc->ptr,mc->size,mc->file,mc->line);
#else /* hopefully 32BIT */
if(j)
{ strbuf[j]=0;
g_printf("Address 0x%lx (\"%s\"), %d bytes, from %s, line %d\n",
(unsigned long)mc->ptr,strbuf,mc->size,mc->file,mc->line);
}
else
g_printf("Address 0x%lx (binary data), %d bytes, from %s, line %d\n",
(unsigned long)mc->ptr,mc->size,mc->file,mc->line);
#endif
}
static void print_ptrs(char *msg)
{ int bucket,i,n=0;
g_printf("%s", msg);
for(bucket=0; bucket<64; bucket++)
for(i=0; i<phCnt[bucket]; i++)
{
print_ptr(ptrhash[bucket][i], 15);
n++;
}
g_printf("%d memory chunks total.\n",n);
}
/***
*** Replacements for the libc memory allocators.
***/
/*
* Protected malloc().
*/
void *malloc_ext(int size, char* file, int line)
{ void *ptr;
#if 0
printf("allocating %d bytes from file %s, line %d\n", size, file, line);
#endif
if(!(ptr = calloc(1,size)))
Stop("out of memory while allocating %d bytes",size);
remember(ptr,size,file,line);
return ptr;
}
/*
* Protected try_malloc().
*/
void *try_malloc_ext(int size, char* file, int line)
{ void *ptr;
if((ptr = calloc(1,size)))
remember(ptr,size,file,line);
return ptr;
}
/*
* Protected realloc().
*/
void *realloc_ext(void *ptr, int size, char *file, int line)
{ void *ret;
if(ptr && forget(ptr))
{ g_printf("trying to realloc undefined pointer 0x%lx\n"
"file: %s, line: %d",(long)ptr,file,line);
exit(EXIT_FAILURE);
}
if(!(ret=realloc(ptr,size)))
{ g_printf("out of memory for ptr 0x%lx, %d bytes\n",(long)ptr,size);
exit(EXIT_FAILURE);
}
remember(ret,size,file,line);
return ret;
}
/*
* Free and forget a pointer
*/
void free_ext(void *ptr, char *file, int line)
{
if(forget(ptr))
{ g_printf("trying to free undefined pointer 0x%lx\n"
"file: %s, line: %d",(long)ptr,file,line);
exit(EXIT_FAILURE);
}
free(ptr);
}
/*
* String duplication.
*/
char *strdup_ext(const char *string, char *file, int line)
{ int length = strlen(string)+1;
char *copy;
if(!(copy = calloc(1,length)))
Stop("out of memory while allocating %d bytes",length);
strcpy(copy,string);
remember(copy,length,file,line);
return copy;
}
/*
* The allocating printf()s
*/
char* strdup_printf_ext(char *format, char *file, int line, ...)
{ va_list argp;
char *ret;
va_start(argp,line);
ret = g_strdup_vprintf(format, argp);
remember(ret,strlen(ret),file,line);
va_end(argp);
return ret;
}
char* strdup_vprintf_ext(char *format, va_list ap, char *file, int line)
{ char *ret;
ret = g_strdup_vprintf(format, ap);
remember(ret,strlen(ret),file,line);
return ret;
}
/*
* The utf8 converter
*/
gchar* g_locale_to_utf8_ext(const gchar *str, gssize len,
gsize *bytes_read, gsize *bytes_written_out, GError **error,
char *file, int line)
{ char *ret;
gsize bytes_written;
ret = g_locale_to_utf8(str, len, bytes_read, &bytes_written, error);
remember(ret, bytes_written, file, line);
if(bytes_written_out)
*bytes_written_out = bytes_written;
return ret;
}
/***
*** Checking for memory leaks.
***/
void check_memleaks(void)
{ int i,memleak = 0;
/*** See if some memory chunks have been left over */
for(i=0; i<64; i++)
if(phCnt[i])
memleak = 1;
if(memleak)
{ char msg[80];
sprintf(msg,"\ndvdisaster:\nMemory leak warning,"
" non-freed memory chunks detected.\n\n");
print_ptrs(msg);
}
else g_printf("dvdisaster: No memory leaks found.\n");
}
#endif

589
src/menubar.c Normal file
View File

@@ -0,0 +1,589 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#include <limits.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
/***
*** Forward declarations
***/
static void file_select_cb(GtkWidget*, gpointer);
/***
*** The Menu and Toolbar action dispatcher
***/
typedef enum
{ MENU_FILE_IMAGE,
MENU_FILE_IMAGE_OK,
MENU_FILE_IMAGE_CANCEL,
MENU_FILE_IMAGE_DESTROY,
MENU_FILE_ECC,
MENU_FILE_ECC_OK,
MENU_FILE_ECC_CANCEL,
MENU_FILE_ECC_DESTROY,
MENU_FILE_QUIT,
MENU_TOOLS_MEDIUM_INFO,
MENU_TOOLS_RAW_EDITOR,
MENU_PREFERENCES,
MENU_HELP_MANUAL,
MENU_HELP_ABOUT,
MENU_HELP_GPL,
MENU_HELP_CHANGELOG,
MENU_HELP_CREDITS,
MENU_HELP_TODO
} menu_actions;
static void menu_cb(GtkWidget *widget, gpointer data)
{
switch(GPOINTER_TO_INT(data))
{ case MENU_FILE_IMAGE:
case MENU_FILE_ECC:
file_select_cb(widget, data);
break;
case MENU_FILE_QUIT:
/* If an action is currently running with spawned threads,
give them time to terminate cleanly. */
if(Closure->subThread)
{ Closure->stopActions = STOP_SHUTDOWN_ALL;
g_thread_join(Closure->subThread);
}
/* Extract current file selections so that they are saved in the .dvdisaster file */
g_free(Closure->imageName);
Closure->imageName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->imageEntry)));
if(!Closure->imageName || !strlen(Closure->imageName))
{ if(Closure->imageName) g_free(Closure->imageName);
Closure->imageName = g_strdup("none");
}
g_free(Closure->eccName);
Closure->eccName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->eccEntry)));
if(!Closure->eccName || !strlen(Closure->eccName))
{ if(Closure->eccName) g_free(Closure->eccName);
Closure->eccName = g_strdup("none");
}
/* and quit */
gtk_main_quit();
break;
case MENU_TOOLS_MEDIUM_INFO:
CreateMediumInfoWindow();
break;
case MENU_TOOLS_RAW_EDITOR:
CreateRawEditor();
break;
case MENU_PREFERENCES:
CreatePreferencesWindow();
break;
case MENU_HELP_MANUAL:
ShowPDF(NULL);
break;
case MENU_HELP_ABOUT:
AboutDialog();
break;
case MENU_HELP_GPL:
ShowGPL();
break;
case MENU_HELP_CHANGELOG:
ShowTextfile(_("windowtitle|Change log"),
_("<big>Change log</big>\n"
"<i>Major differences from earlier program versions.</i>"),
"CHANGELOG", NULL, NULL);
break;
case MENU_HELP_CREDITS:
ShowTextfile(_("windowtitle|Credits"),
_("<big>Credits</big>\n"
"<i>Thanks go out to...</i>"),
"CREDITS", NULL, NULL);
break;
case MENU_HELP_TODO:
ShowTextfile(_("windowtitle|To do list"),
_("<big>To do list</big>\n"
"<i>A sneak preview of coming features ... perhaps ;-)</i>"),
"TODO", NULL, NULL);
break;
default:
g_print("Menu/Toolbar action %d\n",GPOINTER_TO_INT(data));
break;
}
}
/***
*** The Menu system
***/
/*
* Helper functions for creating the menu system
*/
static GtkWidget* add_menu_button(GtkWidget *parent, char *title, int action)
{ char *utf_title = g_locale_to_utf8(title, -1, NULL, NULL, NULL);
GtkWidget *item;
item = gtk_menu_item_new_with_label(utf_title);
g_free(utf_title);
gtk_menu_shell_append(GTK_MENU_SHELL(parent), item);
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(menu_cb), GINT_TO_POINTER(action));
return item;
}
static void add_menu_separator(GtkWidget *parent)
{ GtkWidget *sep;
sep = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(parent), sep);
}
#if 0
static void append_sub_menu(GtkWidget *parent, GtkWidget *strip, char *name)
{ char *utf_name = g_locale_to_utf8(name, -1, NULL, NULL, NULL);
GtkWidget *anchor;
anchor = gtk_menu_item_new_with_label(utf_name);
g_free(utf_name);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(anchor), strip);
gtk_menu_shell_append(GTK_MENU_SHELL(parent), anchor);
}
#endif
/*
* Assemble the menu system.
* Using the itemfactory would make things more complicated wrt localization.
*/
GtkWidget *CreateMenuBar(GtkWidget *parent)
{ GtkWidget *menu_bar, *menu_anchor, *menu_strip, *item;
/* The overall menu bar */
menu_bar = gtk_menu_bar_new();
// gtk_widget_set_name(menu_bar, "menu-bar");
/* The file menu */
menu_strip = gtk_menu_new();
Closure->fileMenuImage = add_menu_button(menu_strip, _("menu|Select Image"), MENU_FILE_IMAGE);
Closure->fileMenuEcc = add_menu_button(menu_strip, _("menu|Select Parity File"), MENU_FILE_ECC);
add_menu_button(menu_strip, _("menu|Quit"), MENU_FILE_QUIT);
menu_anchor = gtk_menu_item_new_with_label(_utf("menu|File"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_anchor), menu_strip);
gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_anchor);
/* The tools menu */
menu_strip = gtk_menu_new();
item = add_menu_button(menu_strip, _("menu|Medium info"), MENU_TOOLS_MEDIUM_INFO);
if(!Closure->deviceNodes->len)
gtk_widget_set_sensitive(item, FALSE);
if(Closure->debugMode && !Closure->screenShotMode)
add_menu_button(menu_strip, _("menu|Raw sector editor"), MENU_TOOLS_RAW_EDITOR);
Closure->toolMenuAnchor = menu_anchor = gtk_menu_item_new_with_label(_utf("menu|Tools"));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_anchor), menu_strip);
gtk_menu_bar_append(GTK_MENU_BAR(menu_bar), menu_anchor);
/* The help menu */
menu_strip = gtk_menu_new();
add_menu_button(menu_strip, _("menu|About"), MENU_HELP_ABOUT);
add_menu_button(menu_strip, _("menu|User manual"), MENU_HELP_MANUAL);
add_menu_separator(menu_strip);
add_menu_button(menu_strip, _("menu|Credits"), MENU_HELP_CREDITS);
add_menu_button(menu_strip, _("menu|Licence (GPL)"), MENU_HELP_GPL);
add_menu_separator(menu_strip);
add_menu_button(menu_strip, _("menu|Change log"), MENU_HELP_CHANGELOG);
add_menu_button(menu_strip, _("menu|To do list"), MENU_HELP_TODO);
menu_anchor = gtk_menu_item_new_with_label(_utf("menu|Help"));
gtk_menu_item_right_justify(GTK_MENU_ITEM(menu_anchor));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_anchor), menu_strip);
gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_anchor);
return menu_bar;
}
/***
*** The toolbar
***/
/*
* Callback for displaying the help message
*/
static gint tooltip_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{
switch(event->type)
{ case GDK_ENTER_NOTIFY:
gtk_label_set_text(Closure->status, (gchar*)data);
break;
case GDK_LEAVE_NOTIFY:
gtk_label_set_text(Closure->status, "");
break;
default:
break;
}
return FALSE; /* don't intercept the default button callbacks! */
}
void AttachTooltip(GtkWidget *widget, char *short_descr, char *long_descr)
{ char *long_copy = g_locale_to_utf8(long_descr, -1, NULL, NULL, NULL);
char *short_copy = g_locale_to_utf8(short_descr, -1, NULL, NULL, NULL);
g_signal_connect(G_OBJECT(widget), "enter_notify_event", G_CALLBACK(tooltip_cb), (gpointer)long_copy);
g_signal_connect(G_OBJECT(widget), "leave_notify_event", G_CALLBACK(tooltip_cb), (gpointer)long_copy);
gtk_tooltips_set_tip(Closure->tooltips, widget, short_copy, long_copy);
g_free(short_copy);
FORGET(long_copy); /* long_copy must be kept during programs life */
}
/*
* Callback for drive selection
*/
static void drive_select_cb(GtkWidget *widget, gpointer data)
{ int n;
char *dnode;
if(!Closure->deviceNodes->len) /* No drives available */
return;
n = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
if(n<0)
return;
dnode = g_ptr_array_index(Closure->deviceNodes, n);
g_free(Closure->device);
Closure->device = g_strdup(dnode);
if(Closure->mediumDrive) /* propagate to medium info window */
gtk_combo_box_set_active(GTK_COMBO_BOX(Closure->mediumDrive), n);
}
/*
* Callback for the image and ecc file selection.
* Creates and runs the file selection dialogs.
*/
static void file_select_cb(GtkWidget *widget, gpointer data)
{ int action = GPOINTER_TO_INT(data);
switch(action)
{ /*** Image file selection */
case MENU_FILE_IMAGE:
if(!Closure->imageFileSel)
{ Closure->imageFileSel = gtk_file_selection_new(_utf("windowtitle|Image file selection"));
ReverseCancelOK(GTK_DIALOG(Closure->imageFileSel));
g_signal_connect(G_OBJECT(Closure->imageFileSel), "destroy",
G_CALLBACK(file_select_cb), GINT_TO_POINTER(MENU_FILE_IMAGE_DESTROY));
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(Closure->imageFileSel)->ok_button),"clicked",
G_CALLBACK(file_select_cb), GINT_TO_POINTER(MENU_FILE_IMAGE_OK));
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(Closure->imageFileSel)->cancel_button),"clicked",
G_CALLBACK(file_select_cb), GINT_TO_POINTER(MENU_FILE_IMAGE_CANCEL));
}
gtk_file_selection_set_filename(GTK_FILE_SELECTION(Closure->imageFileSel),
gtk_entry_get_text(GTK_ENTRY(Closure->imageEntry)));
gtk_widget_show(Closure->imageFileSel);
break;
case MENU_FILE_IMAGE_DESTROY:
Closure->imageFileSel = NULL;
break;
case MENU_FILE_IMAGE_OK:
g_free(Closure->imageName);
Closure->imageName = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(Closure->imageFileSel)));
if(Closure->autoSuffix)
Closure->imageName = ApplyAutoSuffix(Closure->imageName, "iso");
gtk_entry_set_text(GTK_ENTRY(Closure->imageEntry), Closure->imageName);
gtk_editable_set_position(GTK_EDITABLE(Closure->imageEntry), -1);
gtk_widget_hide(Closure->imageFileSel);
break;
case MENU_FILE_IMAGE_CANCEL:
gtk_widget_hide(Closure->imageFileSel);
break;
/*** Same stuff again for ecc file selection */
case MENU_FILE_ECC:
if(!Closure->eccFileSel)
{ Closure->eccFileSel = gtk_file_selection_new(_utf("windowtitle|Error correction file selection"));
ReverseCancelOK(GTK_DIALOG(Closure->eccFileSel));
g_signal_connect(G_OBJECT(Closure->eccFileSel), "destroy",
G_CALLBACK(file_select_cb), GINT_TO_POINTER(MENU_FILE_ECC_DESTROY));
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(Closure->eccFileSel)->ok_button),"clicked",
G_CALLBACK(file_select_cb), GINT_TO_POINTER(MENU_FILE_ECC_OK));
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(Closure->eccFileSel)->cancel_button),"clicked",
G_CALLBACK(file_select_cb), GINT_TO_POINTER(MENU_FILE_ECC_CANCEL));
}
gtk_file_selection_set_filename(GTK_FILE_SELECTION(Closure->eccFileSel),
gtk_entry_get_text(GTK_ENTRY(Closure->eccEntry)));
gtk_widget_show(Closure->eccFileSel);
break;
case MENU_FILE_ECC_DESTROY:
Closure->eccFileSel = NULL;
break;
case MENU_FILE_ECC_OK:
g_free(Closure->eccName);
Closure->eccName = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(Closure->eccFileSel)));
if(Closure->autoSuffix)
Closure->eccName = ApplyAutoSuffix(Closure->eccName, "ecc");
gtk_entry_set_text(GTK_ENTRY(Closure->eccEntry), Closure->eccName);
gtk_editable_set_position(GTK_EDITABLE(Closure->eccEntry), -1);
gtk_widget_hide(Closure->eccFileSel);
break;
case MENU_FILE_ECC_CANCEL:
gtk_widget_hide(Closure->eccFileSel);
break;
}
}
/*
* Set file path for a text entry.
* Completes relative paths.
*/
void set_path(GtkWidget *entry, char *path)
{
if(path[0] == '/' || path[0] == '\\' || path[1] == ':' || strlen(path) < 1)
{ gtk_entry_set_text(GTK_ENTRY(entry), path);
gtk_editable_set_position(GTK_EDITABLE(entry), -1);
}
else
{ char buf[PATH_MAX + strlen(path) + 2];
if (!getcwd(buf, PATH_MAX)) return;
strcat(buf,"/");
strcat(buf,path);
gtk_entry_set_text(GTK_ENTRY(entry), buf);
gtk_editable_set_position(GTK_EDITABLE(entry), -1);
}
}
/*
* Callback for adding file suffixes
*/
static void suffix_cb(GtkWidget *widget, gpointer data)
{ int ecc_file = GPOINTER_TO_INT(data);
if(!Closure->autoSuffix)
return;
if(!ecc_file)
{ Closure->imageName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->imageEntry)));
Closure->imageName = ApplyAutoSuffix(Closure->imageName, "iso");
gtk_entry_set_text(GTK_ENTRY(Closure->imageEntry), Closure->imageName);
}
else
{ Closure->eccName = g_strdup(gtk_entry_get_text(GTK_ENTRY(Closure->eccEntry)));
Closure->eccName = ApplyAutoSuffix(Closure->eccName, "ecc");
gtk_entry_set_text(GTK_ENTRY(Closure->eccEntry), Closure->eccName);
}
}
/*
* Create the toolbar
*/
GtkWidget *CreateToolBar(GtkWidget *parent)
{ GtkWidget *box, *button, *ebox, *icon, *prefs, *help, *quit, *sep, *space;
GtkWidget *combo_box;
int dev_idx = 0;
unsigned int i;
/*** Create the toolbar */
box = gtk_hbox_new(FALSE, 0);
/*** Drive selection */
space = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(box), space, FALSE, FALSE, 5);
ebox = gtk_event_box_new();
gtk_widget_set_events(ebox, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
gtk_box_pack_start(GTK_BOX(box), ebox, FALSE, FALSE, 0);
AttachTooltip(ebox, _("tooltip|Drive selection"), _("Use the nearby drop-down list to select the input drive."));
icon = gtk_image_new_from_stock("dvdisaster-cd", GTK_ICON_SIZE_LARGE_TOOLBAR);
gtk_container_add(GTK_CONTAINER(ebox), icon);
Closure->driveCombo = combo_box = gtk_combo_box_new_text();
g_signal_connect(G_OBJECT(combo_box), "changed", G_CALLBACK(drive_select_cb), NULL);
for(i=0; i<Closure->deviceNames->len; i++)
{
gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box),
g_ptr_array_index(Closure->deviceNames,i));
if(!strcmp(Closure->device, g_ptr_array_index(Closure->deviceNodes,i)))
dev_idx = i;
}
if(!Closure->deviceNodes->len)
{ gtk_combo_box_append_text(GTK_COMBO_BOX(combo_box), _utf("No drives found"));
}
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), dev_idx);
gtk_widget_set_size_request(combo_box, 200, -1);
gtk_box_pack_start(GTK_BOX(box), combo_box, FALSE, FALSE, 7);
AttachTooltip(combo_box, _("tooltip|Drive selection"), _("Selects the input drive for reading images."));
space = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(box), space, FALSE, FALSE, 1);
sep = gtk_vseparator_new();
gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 3);
/*** Image file selection */
icon = gtk_image_new_from_stock("dvdisaster-open-img", GTK_ICON_SIZE_LARGE_TOOLBAR);
button = gtk_button_new();
gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
gtk_container_add(GTK_CONTAINER(button), icon);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(file_select_cb),
GINT_TO_POINTER(MENU_FILE_IMAGE));
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
Closure->imageEntry = gtk_entry_new();
set_path(Closure->imageEntry, Closure->imageName);
g_signal_connect(G_OBJECT(Closure->imageEntry), "activate",
G_CALLBACK(suffix_cb), GINT_TO_POINTER(FALSE));
gtk_box_pack_start(GTK_BOX(box), Closure->imageEntry, TRUE, TRUE, 0);
space = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(box), space, FALSE, FALSE, 5);
sep = gtk_vseparator_new();
gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 3);
AttachTooltip(button, _("tooltip|Image file selection"), _("Selects a new image file."));
AttachTooltip(Closure->imageEntry, _("tooltip|Current image file"), _("Shows the name of the current image file."));
/*** Ecc file selection */
icon = gtk_image_new_from_stock("dvdisaster-open-ecc", GTK_ICON_SIZE_LARGE_TOOLBAR);
button = gtk_button_new();
gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
gtk_container_add(GTK_CONTAINER(button), icon);
g_signal_connect(G_OBJECT(button), "clicked",
G_CALLBACK(file_select_cb),
GINT_TO_POINTER(MENU_FILE_ECC));
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
Closure->eccEntry = gtk_entry_new();
set_path(Closure->eccEntry, Closure->eccName);
g_signal_connect(G_OBJECT(Closure->eccEntry), "activate",
G_CALLBACK(suffix_cb), GINT_TO_POINTER(TRUE));
gtk_box_pack_start(GTK_BOX(box), Closure->eccEntry, TRUE, TRUE, 0);
space = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(box), space, FALSE, FALSE, 5);
sep = gtk_vseparator_new();
gtk_box_pack_start(GTK_BOX(box), sep, FALSE, FALSE, 3);
AttachTooltip(button, _("tooltip|Error correction file selection"), _("Selects a new error correction file."));
AttachTooltip(Closure->eccEntry, _("tooltip|Current error correction file"), _("Shows the name of the current error correction file."));
/*** Preferences button */
icon = gtk_image_new_from_stock("dvdisaster-gtk-preferences", GTK_ICON_SIZE_LARGE_TOOLBAR);
Closure->prefsButton = prefs = gtk_button_new();
gtk_button_set_relief(GTK_BUTTON(prefs), GTK_RELIEF_NONE);
gtk_container_add(GTK_CONTAINER(prefs), icon);
g_signal_connect(G_OBJECT(prefs), "clicked", G_CALLBACK(menu_cb), (gpointer)MENU_PREFERENCES);
gtk_box_pack_start(GTK_BOX(box), prefs, FALSE, FALSE, 0);
AttachTooltip(prefs, _("tooltip|Preferences"), _("Customize settings for creating images, error correction files and other stuff."));
/*** Help button */
icon = gtk_image_new_from_stock("dvdisaster-gtk-help", GTK_ICON_SIZE_LARGE_TOOLBAR);
Closure->helpButton = help = gtk_button_new();
gtk_button_set_relief(GTK_BUTTON(help), GTK_RELIEF_NONE);
gtk_container_add(GTK_CONTAINER(help), icon);
g_signal_connect(G_OBJECT(help), "clicked", G_CALLBACK(menu_cb), (gpointer)MENU_HELP_MANUAL);
gtk_box_pack_start(GTK_BOX(box), help, FALSE, FALSE, 0);
AttachTooltip(help, _("tooltip|User manual"), _("Displays the user manual (external PDF viewer required)."));
/*** Quit button */
icon = gtk_image_new_from_stock("dvdisaster-gtk-quit", GTK_ICON_SIZE_LARGE_TOOLBAR);
quit = gtk_button_new();
gtk_button_set_relief(GTK_BUTTON(quit), GTK_RELIEF_NONE);
gtk_container_add(GTK_CONTAINER(quit), icon);
g_signal_connect(G_OBJECT(quit), "clicked", G_CALLBACK(menu_cb), (gpointer)MENU_FILE_QUIT);
gtk_box_pack_start(GTK_BOX(box), quit, FALSE, FALSE, 0);
AttachTooltip(quit, _("tooltip|Quit"), _("Quit dvdisaster"));
return box;
}

12
src/method-link.c Normal file
View File

@@ -0,0 +1,12 @@
/* Automatically generated wrapper for registering the methods */
void BindMethods(void)
{
void register_rs01(void);
void register_rs02(void);
void register_rs03(void);
register_rs01();
register_rs02();
register_rs03();
}

102
src/method.c Normal file
View File

@@ -0,0 +1,102 @@
/* 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"
/***
*** Collect the available methods
***/
/*
* Invite all methods for registration
*/
void CollectMethods(void)
{
BindMethods();
}
/*
* All methods register by calling this
*/
void RegisterMethod(Method *method)
{
g_ptr_array_add(Closure->methodList, method);
}
/*
* List the available methods
*/
void ListMethods(void)
{ char name[5];
unsigned int i;
PrintCLI(_("\nList of available methods:\n\n"));
name[4] = 0;
for(i=0; i<Closure->methodList->len; i++)
{ Method *method = g_ptr_array_index(Closure->methodList, i);
strncpy(name, method->name, 4);
PrintCLI("%s, enable with -m%s: %s\n",name,name,method->description);
}
}
/*
* Call the method destructors
*/
void CallMethodDestructors(void)
{ unsigned int i;
for(i=0; i<Closure->methodList->len; i++)
{ Method *method = g_ptr_array_index(Closure->methodList, i);
method->destroy(method);
if(method->menuEntry) g_free(method->menuEntry);
if(method->description) g_free(method->description);
}
}
/***
*** Determine methods from name or ecc information
***/
/*
* Find a method by name
*/
Method *FindMethod(char *name)
{ unsigned int i;
for(i=0; i<Closure->methodList->len; i++)
{ Method *method = g_ptr_array_index(Closure->methodList, i);
if(!strncmp(method->name, name, 4))
return method;
}
return NULL;
}

1360
src/misc.c Normal file

File diff suppressed because it is too large Load Diff

3212
src/preferences.c Normal file

File diff suppressed because it is too large Load Diff

647
src/print-sense.c Normal file
View File

@@ -0,0 +1,647 @@
/* 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"
/***
*** Error texts taken from /usr/src/linux/drivers/scsi/constants.c,
*** which contained the following notice:
*
* ASCII values for a number of symbolic constants, printing functions,
* etc.
* Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422)
*
*/
static const char *snstext[] = {
"None", /* There is no sense information */
"Recovered Error", /* The last command completed successfully
but used error correction */
"Not Ready", /* The addressed target is not ready */
"Medium Error", /* Data error detected on the medium */
"Hardware Error", /* Controller or device failure */
"Illegal Request",
"Unit Attention", /* Removable medium was changed, or
the target has been reset */
"Data Protect", /* Access to the data is blocked */
"Blank Check", /* Reached unexpected written or unwritten
region of the medium */
"Key=9", /* Vendor specific */
"Copy Aborted", /* COPY or COMPARE was aborted */
"Aborted Command", /* The target aborted the command */
"Equal", /* A SEARCH DATA command found data equal */
"Volume Overflow", /* Medium full with still data to be written */
"Miscompare", /* Source data and data on the medium
do not agree */
"Key=15", /* Reserved */
};
struct error_info{
unsigned char code1, code2;
unsigned short int devices;
const char * text;
};
#define D 0x0001 /* DIRECT ACCESS DEVICE (disk) */
#define T 0x0002 /* SEQUENTIAL ACCESS DEVICE (tape) */
#define L 0x0004 /* PRINTER DEVICE */
#define P 0x0008 /* PROCESSOR DEVICE */
#define W 0x0010 /* WRITE ONCE READ MULTIPLE DEVICE */
#define R 0x0020 /* READ ONLY (CD-ROM) DEVICE */
#define S 0x0040 /* SCANNER DEVICE */
#define O 0x0080 /* OPTICAL MEMORY DEVICE */
#define M 0x0100 /* MEDIA CHANGER DEVICE */
#define C 0x0200 /* COMMUNICATION DEVICE */
#define A 0x0400 /* ARRAY STORAGE */
#define E 0x0800 /* ENCLOSURE SERVICES DEVICE */
#define B 0x1000 /* SIMPLIFIED DIRECT ACCESS DEVICE */
#define K 0x2000 /* OPTICAL CARD READER/WRITER DEVICE */
static struct error_info additional[] =
{
{0x00,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"No additional sense information"},
{0x00,0x01,T,"Filemark detected"},
{0x00,0x02,T|S,"End-of-partition/medium detected"},
{0x00,0x03,T,"Setmark detected"},
{0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
{0x00,0x05,T|L|S,"End-of-data detected"},
{0x00,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"I/O process terminated"},
{0x00,0x11,R,"Audio play operation in progress"},
{0x00,0x12,R,"Audio play operation paused"},
{0x00,0x13,R,"Audio play operation successfully completed"},
{0x00,0x14,R,"Audio play operation stopped due to error"},
{0x00,0x15,R,"No current audio status to return"},
{0x00,0x16,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Operation in progress"},
{0x00,0x17,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning requested"},
{0x01,0x00,D|W|O|B|K,"No index/sector signal"},
{0x02,0x00,D|W|R|O|M|B|K,"No seek complete"},
{0x03,0x00,D|T|L|W|S|O|B|K,"Peripheral device write fault"},
{0x03,0x01,T,"No write current"},
{0x03,0x02,T,"Excessive write errors"},
{0x04,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,cause not reportable"},
{0x04,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit is in process of becoming ready"},
{0x04,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,initializing cmd. required"},
{0x04,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,manual intervention required"},
{0x04,0x04,D|T|L|R|O|B,"Logical unit not ready,format in progress"},
{0x04,0x05,D|T|W|O|M|C|A|B|K,"Logical unit not ready,rebuild in progress"},
{0x04,0x06,D|T|W|O|M|C|A|B|K,"Logical unit not ready,recalculation in progress"},
{0x04,0x07,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,operation in progress"},
{0x04,0x08,R,"Logical unit not ready,long write in progress"},
{0x04,0x09,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,self-test in progress"},
{0x05,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit does not respond to selection"},
{0x06,0x00,D|W|R|O|M|B|K,"No reference position found"},
{0x07,0x00,D|T|L|W|R|S|O|M|B|K,"Multiple peripheral devices selected"},
{0x08,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication failure"},
{0x08,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication time-out"},
{0x08,0x02,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication parity error"},
{0x08,0x03,D|T|R|O|M|B|K,"Logical unit communication CRC error (Ultra-DMA)"},
{0x08,0x04,D|T|L|P|W|R|S|O|C|K,"Unreachable copy target"},
{0x09,0x00,D|T|W|R|O|B,"Track following error"},
{0x09,0x01,W|R|O|K,"Tracking servo failure"},
{0x09,0x02,W|R|O|K,"Focus servo failure"},
{0x09,0x03,W|R|O,"Spindle servo failure"},
{0x09,0x04,D|T|W|R|O|B,"Head select fault"},
{0x0A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Error log overflow"},
{0x0B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning"},
{0x0B,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - specified temperature exceeded"},
{0x0B,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - enclosure degraded"},
{0x0C,0x00,T|R|S,"Write error"},
{0x0C,0x01,K,"Write error - recovered with auto reallocation"},
{0x0C,0x02,D|W|O|B|K,"Write error - auto reallocation failed"},
{0x0C,0x03,D|W|O|B|K,"Write error - recommend reassignment"},
{0x0C,0x04,D|T|W|O|B,"Compression check miscompare error"},
{0x0C,0x05,D|T|W|O|B,"Data expansion occurred during compression"},
{0x0C,0x06,D|T|W|O|B,"Block not compressible"},
{0x0C,0x07,R,"Write error - recovery needed"},
{0x0C,0x08,R,"Write error - recovery failed"},
{0x0C,0x09,R,"Write error - loss of streaming"},
{0x0C,0x0A,R,"Write error - padding blocks added"},
{0x10,0x00,D|W|O|B|K,"Id CRC or ECC error"},
{0x11,0x00,D|T|W|R|S|O|B|K,"Unrecovered read error"},
{0x11,0x01,D|T|W|R|S|O|B|K,"Read retries exhausted"},
{0x11,0x02,D|T|W|R|S|O|B|K,"Error too long to correct"},
{0x11,0x03,D|T|W|S|O|B|K,"Multiple read errors"},
{0x11,0x04,D|W|O|B|K,"Unrecovered read error - auto reallocate failed"},
{0x11,0x05,W|R|O|B,"L-EC uncorrectable error"},
{0x11,0x06,W|R|O|B,"CIRC unrecovered error"},
{0x11,0x07,W|O|B,"Data re-synchronization error"},
{0x11,0x08,T,"Incomplete block read"},
{0x11,0x09,T,"No gap found"},
{0x11,0x0A,D|T|O|B|K,"Miscorrected error"},
{0x11,0x0B,D|W|O|B|K,"Unrecovered read error - recommend reassignment"},
{0x11,0x0C,D|W|O|B|K,"Unrecovered read error - recommend rewrite the data"},
{0x11,0x0D,D|T|W|R|O|B,"De-compression CRC error"},
{0x11,0x0E,D|T|W|R|O|B,"Cannot decompress using declared algorithm"},
{0x11,0x0F,R,"Error reading UPC/EAN number"},
{0x11,0x10,R,"Error reading ISRC number"},
{0x11,0x11,R,"Read error - loss of streaming"},
{0x12,0x00,D|W|O|B|K,"Address mark not found for id field"},
{0x13,0x00,D|W|O|B|K,"Address mark not found for data field"},
{0x14,0x00,D|T|L|W|R|S|O|B|K,"Recorded entity not found"},
{0x14,0x01,D|T|W|R|O|B|K,"Record not found"},
{0x14,0x02,T,"Filemark or setmark not found"},
{0x14,0x03,T,"End-of-data not found"},
{0x14,0x04,T,"Block sequence error"},
{0x14,0x05,D|T|W|O|B|K,"Record not found - recommend reassignment"},
{0x14,0x06,D|T|W|O|B|K,"Record not found - data auto-reallocated"},
{0x15,0x00,D|T|L|W|R|S|O|M|B|K,"Random positioning error"},
{0x15,0x01,D|T|L|W|R|S|O|M|B|K,"Mechanical positioning error"},
{0x15,0x02,D|T|W|R|O|B|K,"Positioning error detected by read of medium"},
{0x16,0x00,D|W|O|B|K,"Data synchronization mark error"},
{0x16,0x01,D|W|O|B|K,"Data sync error - data rewritten"},
{0x16,0x02,D|W|O|B|K,"Data sync error - recommend rewrite"},
{0x16,0x03,D|W|O|B|K,"Data sync error - data auto-reallocated"},
{0x16,0x04,D|W|O|B|K,"Data sync error - recommend reassignment"},
{0x17,0x00,D|T|W|R|S|O|B|K,"Recovered data with no error correction applied"},
{0x17,0x01,D|T|W|R|S|O|B|K,"Recovered data with retries"},
{0x17,0x02,D|T|W|R|O|B|K,"Recovered data with positive head offset"},
{0x17,0x03,D|T|W|R|O|B|K,"Recovered data with negative head offset"},
{0x17,0x04,W|R|O|B,"Recovered data with retries and/or circ applied"},
{0x17,0x05,D|W|R|O|B|K,"Recovered data using previous sector id"},
{0x17,0x06,D|W|O|B|K,"Recovered data without ecc - data auto-reallocated"},
{0x17,0x07,D|W|R|O|B|K,"Recovered data without ecc - recommend reassignment"},
{0x17,0x08,D|W|R|O|B|K,"Recovered data without ecc - recommend rewrite"},
{0x17,0x09,D|W|R|O|B|K,"Recovered data without ecc - data rewritten"},
{0x18,0x00,D|T|W|R|O|B|K,"Recovered data with error correction applied"},
{0x18,0x01,D|W|R|O|B|K,"Recovered data with error corr. & retries applied"},
{0x18,0x02,D|W|R|O|B|K,"Recovered data - data auto-reallocated"},
{0x18,0x03,R,"Recovered data with CIRC"},
{0x18,0x04,R,"Recovered data with L-EC"},
{0x18,0x05,D|W|R|O|B|K,"Recovered data - recommend reassignment"},
{0x18,0x06,D|W|R|O|B|K,"Recovered data - recommend rewrite"},
{0x18,0x07,D|W|O|B|K,"Recovered data with ecc - data rewritten"},
{0x19,0x00,D|O|K,"Defect list error"},
{0x19,0x01,D|O|K,"Defect list not available"},
{0x19,0x02,D|O|K,"Defect list error in primary list"},
{0x19,0x03,D|O|K,"Defect list error in grown list"},
{0x1A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter list length error"},
{0x1B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Synchronous data transfer error"},
{0x1C,0x00,D|O|B|K,"Defect list not found"},
{0x1C,0x01,D|O|B|K,"Primary defect list not found"},
{0x1C,0x02,D|O|B|K,"Grown defect list not found"},
{0x1D,0x00,D|T|W|R|O|B|K,"Miscompare during verify operation"},
{0x1E,0x00,D|W|O|B|K,"Recovered id with ecc correction"},
{0x1F,0x00,D|O|K,"Partial defect list transfer"},
{0x20,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid command operation code"},
{0x21,0x00,D|T|W|R|O|M|B|K,"Logical block address out of range"},
{0x21,0x01,D|T|W|R|O|M|B|K,"Invalid element address"},
{0x22,0x00,D,"Illegal function (use 20 00,24 00,or 26 00)"},
{0x24,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in cdb"},
{0x24,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"CDB decryption error"},
{0x25,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not supported"},
{0x26,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in parameter list"},
{0x26,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter not supported"},
{0x26,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter value invalid"},
{0x26,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Threshold parameters not supported"},
{0x26,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid release of persistent reservation"},
{0x26,0x05,D|T|L|P|W|R|S|O|M|C|A|B|K,"Data decryption error"},
{0x26,0x06,D|T|L|P|W|R|S|O|C|K,"Too many target descriptors"},
{0x26,0x07,D|T|L|P|W|R|S|O|C|K,"Unsupported target descriptor type code"},
{0x26,0x08,D|T|L|P|W|R|S|O|C|K,"Too many segment descriptors"},
{0x26,0x09,D|T|L|P|W|R|S|O|C|K,"Unsupported segment descriptor type code"},
{0x26,0x0A,D|T|L|P|W|R|S|O|C|K,"Unexpected inexact segment"},
{0x26,0x0B,D|T|L|P|W|R|S|O|C|K,"Inline data length exceeded"},
{0x26,0x0C,D|T|L|P|W|R|S|O|C|K,"Invalid operation for copy source or destination"},
{0x26,0x0D,D|T|L|P|W|R|S|O|C|K,"Copy segment granularity violation"},
{0x27,0x00,D|T|W|R|O|B|K,"Write protected"},
{0x27,0x01,D|T|W|R|O|B|K,"Hardware write protected"},
{0x27,0x02,D|T|W|R|O|B|K,"Logical unit software write protected"},
{0x27,0x03,T|R,"Associated write protect"},
{0x27,0x04,T|R,"Persistent write protect"},
{0x27,0x05,T|R,"Permanent write protect"},
{0x28,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Not ready to ready change,medium may have changed"},
{0x28,0x01,D|T|W|R|O|M|B,"Import or export element accessed"},
{0x29,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on,reset,or bus device reset occurred"},
{0x29,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on occurred"},
{0x29,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi bus reset occurred"},
{0x29,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Bus device reset function occurred"},
{0x29,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Device internal reset"},
{0x29,0x05,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to single-ended"},
{0x29,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to lvd"},
{0x2A,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Parameters changed"},
{0x2A,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Mode parameters changed"},
{0x2A,0x02,D|T|L|W|R|S|O|M|C|A|E|K,"Log parameters changed"},
{0x2A,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Reservations preempted"},
{0x2A,0x04,D|T|L|P|W|R|S|O|M|C|A|E,"Reservations released"},
{0x2A,0x05,D|T|L|P|W|R|S|O|M|C|A|E,"Registrations preempted"},
{0x2B,0x00,D|T|L|P|W|R|S|O|C|K,"Copy cannot execute since host cannot disconnect"},
{0x2C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command sequence error"},
{0x2C,0x01,S,"Too many windows specified"},
{0x2C,0x02,S,"Invalid combination of windows specified"},
{0x2C,0x03,R,"Current program area is not empty"},
{0x2C,0x04,R,"Current program area is empty"},
{0x2C,0x05,B,"Illegal power condition request"},
{0x2D,0x00,T,"Overwrite error on update in place"},
{0x2F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Commands cleared by another initiator"},
{0x30,0x00,D|T|W|R|O|M|B|K,"Incompatible medium installed"},
{0x30,0x01,D|T|W|R|O|B|K,"Cannot read medium - unknown format"},
{0x30,0x02,D|T|W|R|O|B|K,"Cannot read medium - incompatible format"},
{0x30,0x03,D|T|R|K,"Cleaning cartridge installed"},
{0x30,0x04,D|T|W|R|O|B|K,"Cannot write medium - unknown format"},
{0x30,0x05,D|T|W|R|O|B|K,"Cannot write medium - incompatible format"},
{0x30,0x06,D|T|W|R|O|B,"Cannot format medium - incompatible medium"},
{0x30,0x07,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning failure"},
{0x30,0x08,R,"Cannot write - application code mismatch"},
{0x30,0x09,R,"Current session not fixated for append"},
{0x31,0x00,D|T|W|R|O|B|K,"Medium format corrupted"},
{0x31,0x01,D|L|R|O|B,"Format command failed"},
{0x32,0x00,D|W|O|B|K,"No defect spare location available"},
{0x32,0x01,D|W|O|B|K,"Defect list update failure"},
{0x33,0x00,T,"Tape length error"},
{0x34,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure failure"},
{0x35,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services failure"},
{0x35,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Unsupported enclosure function"},
{0x35,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services unavailable"},
{0x35,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer failure"},
{0x35,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer refused"},
{0x36,0x00,L,"Ribbon,ink,or toner failure"},
{0x37,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Rounded parameter"},
{0x38,0x00,B,"Event status notification"},
{0x38,0x02,B,"Esn - power management class event"},
{0x38,0x04,B,"Esn - media class event"},
{0x38,0x06,B,"Esn - device busy class event"},
{0x39,0x00,D|T|L|W|R|S|O|M|C|A|E|K,"Saving parameters not supported"},
{0x3A,0x00,D|T|L|W|R|S|O|M|B|K,"Medium not present"},
{0x3A,0x01,D|T|W|R|O|M|B|K,"Medium not present - tray closed"},
{0x3A,0x02,D|T|W|R|O|M|B|K,"Medium not present - tray open"},
{0x3A,0x03,D|T|W|R|O|M|B,"Medium not present - loadable"},
{0x3A,0x04,D|T|W|R|O|M|B,"Medium not present - medium auxiliary memory accessible"},
{0x3B,0x00,T|L,"Sequential positioning error"},
{0x3B,0x01,T,"Tape position error at beginning-of-medium"},
{0x3B,0x02,T,"Tape position error at end-of-medium"},
{0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
{0x3B,0x04,L,"Slew failure"},
{0x3B,0x05,L,"Paper jam"},
{0x3B,0x06,L,"Failed to sense top-of-form"},
{0x3B,0x07,L,"Failed to sense bottom-of-form"},
{0x3B,0x08,T,"Reposition error"},
{0x3B,0x09,S,"Read past end of medium"},
{0x3B,0x0A,S,"Read past beginning of medium"},
{0x3B,0x0B,S,"Position past end of medium"},
{0x3B,0x0C,T|S,"Position past beginning of medium"},
{0x3B,0x0D,D|T|W|R|O|M|B|K,"Medium destination element full"},
{0x3B,0x0E,D|T|W|R|O|M|B|K,"Medium source element empty"},
{0x3B,0x0F,R,"End of medium reached"},
{0x3B,0x11,D|T|W|R|O|M|B|K,"Medium magazine not accessible"},
{0x3B,0x12,D|T|W|R|O|M|B|K,"Medium magazine removed"},
{0x3B,0x13,D|T|W|R|O|M|B|K,"Medium magazine inserted"},
{0x3B,0x14,D|T|W|R|O|M|B|K,"Medium magazine locked"},
{0x3B,0x15,D|T|W|R|O|M|B|K,"Medium magazine unlocked"},
{0x3B,0x16,R,"Mechanical positioning or changer error"},
{0x3D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|K,"Invalid bits in identify message"},
{0x3E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit has not self-configured yet"},
{0x3E,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failure"},
{0x3E,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Timeout on logical unit"},
{0x3E,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-test"},
{0x3E,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit unable to update self-test log"},
{0x3F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Target operating conditions have changed"},
{0x3F,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Microcode has been changed"},
{0x3F,0x02,D|T|L|P|W|R|S|O|M|C|B|K,"Changed operating definition"},
{0x3F,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Inquiry data has changed"},
{0x3F,0x04,D|T|W|R|O|M|C|A|E|B|K,"Component device attached"},
{0x3F,0x05,D|T|W|R|O|M|C|A|E|B|K,"Device identifier changed"},
{0x3F,0x06,D|T|W|R|O|M|C|A|E|B,"Redundancy group created or modified"},
{0x3F,0x07,D|T|W|R|O|M|C|A|E|B,"Redundancy group deleted"},
{0x3F,0x08,D|T|W|R|O|M|C|A|E|B,"Spare created or modified"},
{0x3F,0x09,D|T|W|R|O|M|C|A|E|B,"Spare deleted"},
{0x3F,0x0A,D|T|W|R|O|M|C|A|E|B|K,"Volume set created or modified"},
{0x3F,0x0B,D|T|W|R|O|M|C|A|E|B|K,"Volume set deleted"},
{0x3F,0x0C,D|T|W|R|O|M|C|A|E|B|K,"Volume set deassigned"},
{0x3F,0x0D,D|T|W|R|O|M|C|A|E|B|K,"Volume set reassigned"},
{0x3F,0x0E,D|T|L|P|W|R|S|O|M|C|A|E,"Reported luns data has changed"},
{0x3F,0x10,D|T|W|R|O|M|B,"Medium loadable"},
{0x3F,0x11,D|T|W|R|O|M|B,"Medium auxiliary memory accessible"},
{0x40,0x00,D,"Ram failure (should use 40 nn)"},
/*
* FIXME(eric) - need a way to represent wildcards here.
*/
{0x40,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Diagnostic failure on component nn (80h-ffh)"},
{0x41,0x00,D,"Data path failure (should use 40 nn)"},
{0x42,0x00,D,"Power-on or self-test failure (should use 40 nn)"},
{0x43,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Message error"},
{0x44,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Internal target failure"},
{0x45,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Select or reselect failure"},
{0x46,0x00,D|T|L|P|W|R|S|O|M|C|B|K,"Unsuccessful soft reset"},
{0x47,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error"},
{0x47,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase CRC error detected"},
{0x47,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error detected during st data phase"},
{0x47,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Information unit CRC error detected"},
{0x47,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Asynchronous information protection error detected"},
{0x48,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Initiator detected error message received"},
{0x49,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid message error"},
{0x4A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command phase error"},
{0x4B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase error"},
{0x4C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-configuration"},
/*
* FIXME(eric) - need a way to represent wildcards here.
*/
{0x4D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Tagged overlapped commands (nn = queue tag)"},
{0x4E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Overlapped commands attempted"},
{0x50,0x00,T,"Write append error"},
{0x50,0x01,T,"Write append position error"},
{0x50,0x02,T,"Position error related to timing"},
{0x51,0x00,T|R|O,"Erase failure"},
{0x52,0x00,T,"Cartridge fault"},
{0x53,0x00,D|T|L|W|R|S|O|M|B|K,"Media load or eject failed"},
{0x53,0x01,T,"Unload tape failure"},
{0x53,0x02,D|T|W|R|O|M|B|K,"Medium removal prevented"},
{0x54,0x00,P,"Scsi to host system interface failure"},
{0x55,0x00,P,"System resource failure"},
{0x55,0x01,D|O|B|K,"System buffer full"},
{0x55,0x02,D|T|L|P|W|R|S|O|M|A|E|K,"Insufficient reservation resources"},
{0x55,0x03,D|T|L|P|W|R|S|O|M|C|A|E,"Insufficient resources"},
{0x55,0x04,D|T|L|P|W|R|S|O|M|A|E,"Insufficient registration resources"},
{0x57,0x00,R,"Unable to recover table-of-contents"},
{0x58,0x00,O,"Generation does not exist"},
{0x59,0x00,O,"Updated block read"},
{0x5A,0x00,D|T|L|P|W|R|S|O|M|B|K,"Operator request or state change input"},
{0x5A,0x01,D|T|W|R|O|M|B|K,"Operator medium removal request"},
{0x5A,0x02,D|T|W|R|O|A|B|K,"Operator selected write protect"},
{0x5A,0x03,D|T|W|R|O|A|B|K,"Operator selected write permit"},
{0x5B,0x00,D|T|L|P|W|R|S|O|M|K,"Log exception"},
{0x5B,0x01,D|T|L|P|W|R|S|O|M|K,"Threshold condition met"},
{0x5B,0x02,D|T|L|P|W|R|S|O|M|K,"Log counter at maximum"},
{0x5B,0x03,D|T|L|P|W|R|S|O|M|K,"Log list codes exhausted"},
{0x5C,0x00,D|O,"Rpl status change"},
{0x5C,0x01,D|O,"Spindles synchronized"},
{0x5C,0x02,D|O,"Spindles not synchronized"},
{0x5D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded"},
{0x5D,0x01,R|B,"Media failure prediction threshold exceeded"},
{0x5D,0x02,R,"Logical unit failure prediction threshold exceeded"},
{0x5D,0x10,D|B,"Hardware impending failure general hard drive failure"},
{0x5D,0x11,D|B,"Hardware impending failure drive error rate too high"},
{0x5D,0x12,D|B,"Hardware impending failure data error rate too high"},
{0x5D,0x13,D|B,"Hardware impending failure seek error rate too high"},
{0x5D,0x14,D|B,"Hardware impending failure too many block reassigns"},
{0x5D,0x15,D|B,"Hardware impending failure access times too high"},
{0x5D,0x16,D|B,"Hardware impending failure start unit times too high"},
{0x5D,0x17,D|B,"Hardware impending failure channel parametrics"},
{0x5D,0x18,D|B,"Hardware impending failure controller detected"},
{0x5D,0x19,D|B,"Hardware impending failure throughput performance"},
{0x5D,0x1A,D|B,"Hardware impending failure seek time performance"},
{0x5D,0x1B,D|B,"Hardware impending failure spin-up retry count"},
{0x5D,0x1C,D|B,"Hardware impending failure drive calibration retry count"},
{0x5D,0x20,D|B,"Controller impending failure general hard drive failure"},
{0x5D,0x21,D|B,"Controller impending failure drive error rate too high"},
{0x5D,0x22,D|B,"Controller impending failure data error rate too high"},
{0x5D,0x23,D|B,"Controller impending failure seek error rate too high"},
{0x5D,0x24,D|B,"Controller impending failure too many block reassigns"},
{0x5D,0x25,D|B,"Controller impending failure access times too high"},
{0x5D,0x26,D|B,"Controller impending failure start unit times too high"},
{0x5D,0x27,D|B,"Controller impending failure channel parametrics"},
{0x5D,0x28,D|B,"Controller impending failure controller detected"},
{0x5D,0x29,D|B,"Controller impending failure throughput performance"},
{0x5D,0x2A,D|B,"Controller impending failure seek time performance"},
{0x5D,0x2B,D|B,"Controller impending failure spin-up retry count"},
{0x5D,0x2C,D|B,"Controller impending failure drive calibration retry count"},
{0x5D,0x30,D|B,"Data channel impending failure general hard drive failure"},
{0x5D,0x31,D|B,"Data channel impending failure drive error rate too high"},
{0x5D,0x32,D|B,"Data channel impending failure data error rate too high"},
{0x5D,0x33,D|B,"Data channel impending failure seek error rate too high"},
{0x5D,0x34,D|B,"Data channel impending failure too many block reassigns"},
{0x5D,0x35,D|B,"Data channel impending failure access times too high"},
{0x5D,0x36,D|B,"Data channel impending failure start unit times too high"},
{0x5D,0x37,D|B,"Data channel impending failure channel parametrics"},
{0x5D,0x38,D|B,"Data channel impending failure controller detected"},
{0x5D,0x39,D|B,"Data channel impending failure throughput performance"},
{0x5D,0x3A,D|B,"Data channel impending failure seek time performance"},
{0x5D,0x3B,D|B,"Data channel impending failure spin-up retry count"},
{0x5D,0x3C,D|B,"Data channel impending failure drive calibration retry count"},
{0x5D,0x40,D|B,"Servo impending failure general hard drive failure"},
{0x5D,0x41,D|B,"Servo impending failure drive error rate too high"},
{0x5D,0x42,D|B,"Servo impending failure data error rate too high"},
{0x5D,0x43,D|B,"Servo impending failure seek error rate too high"},
{0x5D,0x44,D|B,"Servo impending failure too many block reassigns"},
{0x5D,0x45,D|B,"Servo impending failure access times too high"},
{0x5D,0x46,D|B,"Servo impending failure start unit times too high"},
{0x5D,0x47,D|B,"Servo impending failure channel parametrics"},
{0x5D,0x48,D|B,"Servo impending failure controller detected"},
{0x5D,0x49,D|B,"Servo impending failure throughput performance"},
{0x5D,0x4A,D|B,"Servo impending failure seek time performance"},
{0x5D,0x4B,D|B,"Servo impending failure spin-up retry count"},
{0x5D,0x4C,D|B,"Servo impending failure drive calibration retry count"},
{0x5D,0x50,D|B,"Spindle impending failure general hard drive failure"},
{0x5D,0x51,D|B,"Spindle impending failure drive error rate too high"},
{0x5D,0x52,D|B,"Spindle impending failure data error rate too high"},
{0x5D,0x53,D|B,"Spindle impending failure seek error rate too high"},
{0x5D,0x54,D|B,"Spindle impending failure too many block reassigns"},
{0x5D,0x55,D|B,"Spindle impending failure access times too high"},
{0x5D,0x56,D|B,"Spindle impending failure start unit times too high"},
{0x5D,0x57,D|B,"Spindle impending failure channel parametrics"},
{0x5D,0x58,D|B,"Spindle impending failure controller detected"},
{0x5D,0x59,D|B,"Spindle impending failure throughput performance"},
{0x5D,0x5A,D|B,"Spindle impending failure seek time performance"},
{0x5D,0x5B,D|B,"Spindle impending failure spin-up retry count"},
{0x5D,0x5C,D|B,"Spindle impending failure drive calibration retry count"},
{0x5D,0x60,D|B,"Firmware impending failure general hard drive failure"},
{0x5D,0x61,D|B,"Firmware impending failure drive error rate too high"},
{0x5D,0x62,D|B,"Firmware impending failure data error rate too high"},
{0x5D,0x63,D|B,"Firmware impending failure seek error rate too high"},
{0x5D,0x64,D|B,"Firmware impending failure too many block reassigns"},
{0x5D,0x65,D|B,"Firmware impending failure access times too high"},
{0x5D,0x66,D|B,"Firmware impending failure start unit times too high"},
{0x5D,0x67,D|B,"Firmware impending failure channel parametrics"},
{0x5D,0x68,D|B,"Firmware impending failure controller detected"},
{0x5D,0x69,D|B,"Firmware impending failure throughput performance"},
{0x5D,0x6A,D|B,"Firmware impending failure seek time performance"},
{0x5D,0x6B,D|B,"Firmware impending failure spin-up retry count"},
{0x5D,0x6C,D|B,"Firmware impending failure drive calibration retry count"},
{0x5D,0xFF,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded (false)"},
{0x5E,0x00,D|T|L|P|W|R|S|O|C|A|K,"Low power condition on"},
{0x5E,0x01,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by timer"},
{0x5E,0x02,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by timer"},
{0x5E,0x03,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by command"},
{0x5E,0x04,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by command"},
{0x5E,0x41,B,"Power state change to active"},
{0x5E,0x42,B,"Power state change to idle"},
{0x5E,0x43,B,"Power state change to standby"},
{0x5E,0x45,B,"Power state change to sleep"},
{0x5E,0x47,B|K,"Power state change to device control"},
{0x60,0x00,S,"Lamp failure"},
{0x61,0x00,S,"Video acquisition error"},
{0x61,0x01,S,"Unable to acquire video"},
{0x61,0x02,S,"Out of focus"},
{0x62,0x00,S,"Scan head positioning error"},
{0x63,0x00,R,"End of user area encountered on this track"},
{0x63,0x01,R,"Packet does not fit in available space"},
{0x64,0x00,R,"Illegal mode for this track"},
{0x64,0x01,R,"Invalid packet size"},
{0x65,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Voltage fault"},
{0x66,0x00,S,"Automatic document feeder cover up"},
{0x66,0x01,S,"Automatic document feeder lift up"},
{0x66,0x02,S,"Document jam in automatic document feeder"},
{0x66,0x03,S,"Document miss feed automatic in document feeder"},
{0x67,0x00,A,"Configuration failure"},
{0x67,0x01,A,"Configuration of incapable logical units failed"},
{0x67,0x02,A,"Add logical unit failed"},
{0x67,0x03,A,"Modification of logical unit failed"},
{0x67,0x04,A,"Exchange of logical unit failed"},
{0x67,0x05,A,"Remove of logical unit failed"},
{0x67,0x06,A,"Attachment of logical unit failed"},
{0x67,0x07,A,"Creation of logical unit failed"},
{0x67,0x08,A,"Assign failure occurred"},
{0x67,0x09,A,"Multiply assigned logical unit"},
{0x68,0x00,A,"Logical unit not configured"},
{0x69,0x00,A,"Data loss on logical unit"},
{0x69,0x01,A,"Multiple logical unit failures"},
{0x69,0x02,A,"Parity/data mismatch"},
{0x6A,0x00,A,"Informational,refer to log"},
{0x6B,0x00,A,"State change has occurred"},
{0x6B,0x01,A,"Redundancy level got better"},
{0x6B,0x02,A,"Redundancy level got worse"},
{0x6C,0x00,A,"Rebuild failure occurred"},
{0x6D,0x00,A,"Recalculate failure occurred"},
{0x6E,0x00,A,"Command to logical unit failed"},
{0x6F,0x00,R,"Copy protection key exchange failure - authentication failure"},
{0x6F,0x01,R,"Copy protection key exchange failure - key not present"},
{0x6F,0x02,R,"Copy protection key exchange failure - key not established"},
{0x6F,0x03,R,"Read of scrambled sector without authentication"},
{0x6F,0x04,R,"Media region code is mismatched to logical unit region"},
{0x6F,0x05,R,"Drive region must be permanent/region reset count error"},
/*
* FIXME(eric) - need a way to represent wildcards here.
*/
{0x70,0x00,T,"Decompression exception short algorithm id of nn"},
{0x71,0x00,T,"Decompression exception long algorithm id"},
{0x72,0x00,R,"Session fixation error"},
{0x72,0x01,R,"Session fixation error writing lead-in"},
{0x72,0x02,R,"Session fixation error writing lead-out"},
{0x72,0x03,R,"Session fixation error - incomplete track in session"},
{0x72,0x04,R,"Empty or partially written reserved track"},
{0x72,0x05,R,"No more track reservations allowed"},
{0x73,0x00,R,"Cd control error"},
{0x73,0x01,R,"Power calibration area almost full"},
{0x73,0x02,R,"Power calibration area is full"},
{0x73,0x03,R,"Power calibration area error"},
{0x73,0x04,R,"Program memory area update failure"},
{0x73,0x05,R,"Program memory area is full"},
{0x73,0x06,R,"RMA/PMA is full"},
/*
* Faked errors by software L-EC
*/
{0xff,0x00,R,"No data returned"},
{0xff,0x01,R,"EDC failure in RAW sector"},
{0xff,0x02,R,"Wrong MSF in RAW sector"},
{0xff,0x03,R,"RAW reading > 16 sectors at once not supported"},
{0xff,0x04,R,"RAW buffer not allocated"},
{0xff,0x05,R,"Invalid zero sector"},
{0xff,0x06,R,"Sector accumulated for analysis"},
{0xff,0x07,R,"Recovery failed"},
{0xff,0x08,R,"Invalid sector; possibly random data returned"},
/*
* Errors created by the SCSI layer simulation code
*/
{0xff, 0xe0,R,"mode not supported by SCSI simulation"},
/*
* Faked error by defect simulation mode
*/
{0xff,0xfe,R,"sg driver ioctl() failed"},
{0xff,0xff,R,"[dvdisaster: Simulated medium defect]"},
{0, 0, 0, NULL}
};
/***
*** Okay, and here is our wrapper.
***/
/*
* Remember the most recent sense error.
* Sense error printing is optional, so they are not
* automatically printed by the low-level functions.
*/
static int sense_key, asc, ascq;
void RememberSense(int k, int a, int aq)
{ sense_key = k;
asc = a;
ascq = aq;
}
/*
* Get static string describing the sense error.
*/
char *GetSenseString(int sense_key, int asc, int ascq, int verbose)
{ static char text[256];
struct error_info *ei;
int raw_reader_error;
const char *sns_text;
char sep;
int idx,len;
/* Special treatment for our fake SCSI errors */
raw_reader_error = (asc == 255) && (ascq < 255);
if(!raw_reader_error)
{ sep = ';';
sns_text = snstext[sense_key];
}
else
{ sep = ':';
sns_text = "Raw Reader";
}
/* Go print them */
if(sense_key <0 || sense_key > 15)
g_snprintf(text, 255, _("Sense error (0x%02x); "),sense_key);
else
{ if(verbose) g_snprintf(text, 255, _("Sense error: %s%c "),sns_text, sep);
else g_snprintf(text, 255, "%s%c ",sns_text, sep);
}
idx = strlen(text);
len = 255-idx;
for(ei = additional; ei->text; ei++)
if(ei->code1 == asc && ei->code2 == ascq)
{ g_snprintf(text+idx, len, "%s.",ei->text);
break;
}
if(!ei->text)
g_snprintf(text+idx, len, _("unknown asc/ascq code (0x%02x, 0x%02x)."),asc,ascq);
return text;
}
/*
* Get static string describing the last sense error.
*/
char* GetLastSenseString(int verbose)
{ return GetSenseString(sense_key, asc, ascq, verbose);
}
void GetLastSense(int *key_out, int *asc_out, int *ascq_out)
{ *key_out = sense_key;
*asc_out = asc;
*ascq_out = ascq;
}

150
src/random.c Normal file
View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* This is derived from the Berkeley source:
* @(#)random.c 5.5 (Berkeley) 7/6/88
* It was reworked for the GNU C Library by Roland McGrath.
* Rewritten to use reentrant functions by Ulrich Drepper, 1995.
*/
/*
* This is derived from glibc-2.1:
* glibc2.1/stdlib/random.c
* glibc2.1/stdlib/random_r.c
* Code was reworked and cut down to be a work-alike of the default
* settings of srandom() and random() by Carsten Gn<47>rlich, 2004.
*
* Note that the original code is much more sophisticated than this one,
* so you will probably want to review the real thing if you are
* interested in the inner workings of the original glibc functions.
*
* There's nothing special about this code; it's simply here to
* provide consistency among systems with different random()
* implementations. E.g. it makes sure that dvdisaster produces
* the same "random" images on all supported platforms.
*
* Note that unlike in the real thing, you must call SRandom()
* before using Random() the first time.
*/
#include "dvdisaster.h"
/* Some hardcoded values from glibc's default setting. */
#define MY_DEG 31
#define MY_SEP 3
/*
* State information for the random number generator.
*/
static gint32 *fptr; /* Front pointer. */
static gint32 *rptr; /* Rear pointer. */
static gint32 state[MY_DEG]; /* Array of state values. */
static gint32 *end_ptr; /* Pointer behind state table. */
/* Initialize the random number generator based on the given seed
* via a linear congruential generator.
* Then, the pointers are set to known locations that are exactly
* MY_SEP places apart.
* Lastly, it cycles the state information a given number of times
* to get rid of any initial dependencies introduced by the L.C.R.N.G.
*/
void SRandom(gint32 seed)
{ gint32 i;
gint32 word;
/* We must make sure the seed is not 0.
* Take arbitrarily 1 in this case. */
if (!seed) seed = 1;
/* This does: state[i] = (16807 * state[i - 1]) % 2147483647;
but avoids overflowing 31 bits. */
state[0] = word = seed;
for (i=1; i < MY_DEG; i++)
{ gint32 hi = word / 127773;
gint32 lo = word % 127773;
word = 16807 * lo - 2836 * hi;
if (word < 0) word += 2147483647;
state[i] = word;
}
/* Now prepare the pointers and cycle the state info 10 times around */
fptr = state + MY_SEP;
rptr = state;
end_ptr = state + MY_DEG;
for(i=10*MY_DEG; i; i--)
Random();
}
/* Deliver the next pseudo-random number in the current series.
* This uses only the trinomial branch of the original code,
* which is supposed to give the best results.
* The basic operation is to add the number at the rear pointer into
* the one at the front pointer. Then both pointers are advanced to the next
* location cyclically in the table. The value returned is the sum generated,
* reduced to 31 bits by throwing away the "least random" low bit.
* Note: The code takes advantage of the fact that both the front and
* rear pointers can't wrap on the same call by not testing the rear
* pointer if the front one has wrapped. Returns a 31-bit random number.
*/
gint32 Random(void)
{ gint32 val,result;
val = *fptr += *rptr;
/* Chucking least random bit. */
result = (val >> 1) & 0x7fffffff;
++fptr;
if(fptr >= end_ptr)
{ fptr = state;
++rptr;
}
else
{ ++rptr;
if(rptr >= end_ptr)
rptr = state;
}
return result;
}
/* Create a 32-bit random value from two sequential 31-bit values.
* Note that this is all simple stuff to produce a sequence of "different"
* numbers; it's not meant to deliver cryptographically strong random
* sequences.
*/
guint32 Random32(void)
{ guint32 value;
value = (Random() & 0xffff);
value <<= 16;
value |= (Random() & 0xffff);
return value;
}

1132
src/raw-editor.c Normal file

File diff suppressed because it is too large Load Diff

380
src/raw-sector-cache.c Normal file
View File

@@ -0,0 +1,380 @@
/* 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"
/*
* Write the raw dump header
*/
static void init_defective_sector_file(char *path, RawBuffer *rb, LargeFile **file, DefectiveSectorHeader *dsh)
{ int n;
*file = LargeOpen(path, O_RDWR | O_CREAT, IMG_PERMS);
if(!*file)
Stop(_("Could not open %s: %s"), path, strerror(errno));
memset(dsh, 0, sizeof(DefectiveSectorHeader));
dsh->lba = rb->lba;
dsh->sectorSize = CD_RAW_DUMP_SIZE;
if(rb->xaMode)
dsh->properties |= DSH_XA_MODE;
if(rb->validFP)
{ memcpy(dsh->mediumFP, rb->mediumFP, 16);
dsh->properties |= DSH_HAS_FINGERPRINT;
}
#ifdef HAVE_BIG_ENDIAN
SwapDefectiveHeaderBytes(dsh);
#endif
n = LargeWrite(*file, dsh, sizeof(DefectiveSectorHeader));
#ifdef HAVE_BIG_ENDIAN
SwapDefectiveHeaderBytes(dsh);
#endif
if(n != sizeof(DefectiveSectorHeader))
Stop(_("Failed writing to defective sector file: %s"), strerror(errno));
}
/*
* Open raw dump, read the header
*/
static void open_defective_sector_file(RawBuffer *rb, char *path, LargeFile **file,
DefectiveSectorHeader *dsh)
{ guint64 length;
int n;
*file = LargeOpen(path, O_RDWR, IMG_PERMS);
if(!*file) return;
LargeStat(path, &length);
n = LargeRead(*file, dsh, sizeof(DefectiveSectorHeader));
if(n != sizeof(DefectiveSectorHeader))
Stop(_("Failed reading from defective sector file: %s"), strerror(errno));
#ifdef HAVE_BIG_ENDIAN
SwapDefectiveHeaderBytes(dsh);
#endif
dsh->nSectors = (length-sizeof(DefectiveSectorHeader))/dsh->sectorSize;
if(dsh->nSectors*dsh->sectorSize+sizeof(DefectiveSectorHeader) != length)
Stop(_("Defective sector file is truncated"));
/* Expand the old non-C2 raw dumps to new size */
if(dsh->sectorSize == 2352) /* old non C2-style raw dump? */
{ unsigned char *buf,*ptr;
unsigned char zero[296];
int i,n;
PrintCLI(" * Expanding raw dump for sector %lld from 2352 to %d bytes *\n",
(long long)dsh->lba, MAX_RAW_TRANSFER_SIZE);
buf = g_malloc(dsh->sectorSize*dsh->nSectors);
for(i=0, ptr=buf; i<dsh->nSectors; i++, ptr+=2352)
{ int n=LargeRead(*file, ptr, dsh->sectorSize);
if(n != dsh->sectorSize)
Stop(_("Failed reading from defective sector file: %s"), strerror(errno));
}
memset(zero, 0, 296);
dsh->sectorSize = MAX_RAW_TRANSFER_SIZE;
if(!LargeSeek(*file, 0))
Stop(_("Failed seeking in defective sector file: %s"), strerror(errno));
#ifdef HAVE_BIG_ENDIAN
SwapDefectiveHeaderBytes(dsh);
#endif
n = LargeWrite(*file, dsh, sizeof(DefectiveSectorHeader));
#ifdef HAVE_BIG_ENDIAN
SwapDefectiveHeaderBytes(dsh);
#endif
if(n != sizeof(DefectiveSectorHeader))
Stop(_("Failed writing to defective sector file: %s"), strerror(errno));
for(i=0, ptr=buf; i<dsh->nSectors; i++, ptr+=2352)
{ n=LargeWrite(*file, ptr, 2352);
if(n != 2352)
Stop(_("Failed writing to defective sector file: %s"), strerror(errno));
n=LargeWrite(*file, zero, 296);
if(n != 296)
Stop(_("Failed writing to defective sector file: %s"), strerror(errno));
}
if(!LargeSeek(*file, sizeof(DefectiveSectorHeader)))
Stop(_("Failed seeking in defective sector file: %s"), strerror(errno));
g_free(buf);
}
/* If the cache file has no fingerprint, add it now */
if(!(dsh->properties & DSH_HAS_FINGERPRINT) && rb->validFP)
{ memcpy(dsh->mediumFP, rb->mediumFP, 16);
dsh->properties |= DSH_HAS_FINGERPRINT;
if(!LargeSeek(*file, 0))
Stop(_("Failed seeking in defective sector file: %s"), strerror(errno));
#ifdef HAVE_BIG_ENDIAN
SwapDefectiveHeaderBytes(dsh);
#endif
n = LargeWrite(*file, dsh, sizeof(DefectiveSectorHeader));
#ifdef HAVE_BIG_ENDIAN
SwapDefectiveHeaderBytes(dsh);
#endif
if(n != sizeof(DefectiveSectorHeader))
Stop(_("Failed writing to defective sector file: %s"), strerror(errno));
}
/* Verify cache and medium fingerprint */
if((dsh->properties & DSH_HAS_FINGERPRINT) && rb->validFP)
{ if(memcmp(dsh->mediumFP, rb->mediumFP, 16))
Stop(_("Fingerprints of medium and defective sector cache do not match!"));
}
}
/*
* Append RawBuffer contents to defective sector dump
*/
int SaveDefectiveSector(RawBuffer *rb, int can_c2_scan)
{ LargeFile *file;
DefectiveSectorHeader *dsh = alloca(sizeof(DefectiveSectorHeader));
unsigned char *cache_sectors = NULL;
char *filename;
guint64 length,offset;
int count=0;
int i,j,idx;
if(!rb->samplesRead)
return 0; /* Nothing to be done */
/* Open cache file */
filename = g_strdup_printf("%s/%s%lld.raw",
Closure->dDumpDir, Closure->dDumpPrefix,
(long long)rb->lba);
if(!LargeStat(filename, &length))
{
PrintCLIorLabel(
#ifndef CLI
Closure->status,
#else
NULL,
#endif
_(" [Creating new cache file %s]\n"), filename);
init_defective_sector_file(filename, rb, &file, dsh);
}
else
{ open_defective_sector_file(rb, filename, &file, dsh);
if(!file)
Stop(_("Could not open %s: %s"), filename, strerror(errno));
}
/* Read already cached sectors */
if(dsh->nSectors > 0)
{ if(!LargeSeek(file, sizeof(DefectiveSectorHeader)))
Stop(_("Failed seeking in defective sector file: %s"), strerror(errno));
cache_sectors = g_malloc(dsh->sectorSize*dsh->nSectors);
for(i=0, idx=0; i<dsh->nSectors; i++, idx+=dsh->sectorSize)
{ int n=LargeRead(file, cache_sectors+idx, dsh->sectorSize);
if(n != dsh->sectorSize)
Stop(_("Failed reading from defective sector file: %s"), strerror(errno));
}
}
/* Store sectors which are not already cached */
offset = sizeof(DefectiveSectorHeader) + dsh->sectorSize*dsh->nSectors;
if(!LargeSeek(file, offset))
Stop(_("Failed seeking in defective sector file: %s"), strerror(errno));
for(i=0; i<rb->samplesRead; i++)
{ int new_sector = TRUE;
/* See comment below on C2 mask field to understand rb->sampleSize-1 */
if(cache_sectors) /* Sector already in cache? */
{
for(j=0, idx=0; j<dsh->nSectors; j++, idx+=dsh->sectorSize)
{ if(!memcmp(rb->rawBuf[i], cache_sectors+idx, rb->sampleSize-1))
{ new_sector = FALSE;
break;
}
}
}
for(j=0; j<i; j++) /* Some drives return cached data after first read */
{ if(!memcmp(rb->rawBuf[i], rb->rawBuf[j], rb->sampleSize-1))
{ new_sector = FALSE;
break;
}
}
if(new_sector) /* same sector already in cache */
{ int n;
/* The C2 mask field is not used; so we put a flag into it
to mark raw sectors containing C2 error information. */
if(can_c2_scan)
rb->rawBuf[i][CD_RAW_DUMP_SIZE-1] = 1;
n=LargeWrite(file, rb->rawBuf[i], dsh->sectorSize);
if(n != dsh->sectorSize)
Stop(_("Failed writing to defective sector file: %s"), strerror(errno));
count++;
}
}
LargeClose(file);
PrintCLIorLabel(
#ifndef CLI
Closure->status,
#else
NULL,
#endif
_(" [Appended %d/%d sectors to cache file %s; LBA=%lld, ssize=%d, %d sectors]\n"),
count, rb->samplesRead, filename, dsh->lba, dsh->sectorSize, dsh->nSectors);
g_free(filename);
if(cache_sectors)
g_free(cache_sectors);
return count;
}
/*
* Read sectors from the defective sector dump,
* feed them into the raw buffer one by one
* and retry recovery.
*/
int TryDefectiveSectorCache(RawBuffer *rb, unsigned char *outbuf)
{ DefectiveSectorHeader dsh;
LargeFile *file;
char *path;
int status;
int last_sector;
int i;
path = g_strdup_printf("%s/%s%lld.raw",
Closure->dDumpDir, Closure->dDumpPrefix,
(long long)rb->lba);
open_defective_sector_file(rb, path, &file, &dsh);
g_free(path);
if(!file) /* No cache file */
return -1;
/* skip sectors added in current pass */
last_sector = dsh.nSectors - rb->samplesRead;
ReallocRawBuffer(rb, dsh.nSectors);
for(i=0; i<last_sector; i++)
{ int n;
n = LargeRead(file, rb->workBuf->buf, dsh.sectorSize);
if(n != dsh.sectorSize)
Stop(_("Failed reading from defective sector file: %s"), strerror(errno));
status = TryCDFrameRecovery(rb, outbuf);
if(!status)
{
PrintCLIorLabel(
#ifndef CLI
Closure->status,
#else
NULL,
#endif
" [Success after processing cached sector %d]\n", i+1);
return status;
}
}
LargeClose(file);
return -1;
}
/*
* Read sectors from the defective sector dump
*/
void ReadDefectiveSectorFile(DefectiveSectorHeader *dsh, RawBuffer *rb, char *path)
{ LargeFile *file;
open_defective_sector_file(rb, path, &file, dsh);
if(!file)
{ Stop(_("Could not open %s: %s"), path, strerror(errno));
return;
}
rb->lba = dsh->lba;
if(dsh->properties & DSH_XA_MODE)
rb->dataOffset = 24;
else rb->dataOffset = 16;
ReallocRawBuffer(rb, dsh->nSectors);
for(rb->samplesRead=0; rb->samplesRead<dsh->nSectors; )
{ int n=LargeRead(file, rb->rawBuf[rb->samplesRead], dsh->sectorSize);
if(n != dsh->sectorSize)
{ Stop(_("Failed reading from defective sector file: %s"), strerror(errno));
return;
}
rb->samplesRead++;
UpdateFrameStats(rb);
CollectGoodVectors(rb);
}
LargeClose(file);
}

377
src/read-adaptive-window.c Normal file
View File

@@ -0,0 +1,377 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
/***
*** Spiral drawing and updating
***/
static long long int readable, correctable, missing;
static int percent,min_required;
static GdkColor *footer_color;
#define REDRAW_TITLE 1<<0
#define REDRAW_SUBTITLE 1<<1
#define REDRAW_PROGRESS 1<<2
#define REDRAW_ERRORMSG 1<<3
static int draw_text(GdkDrawable *d, PangoLayout *l, char *text, int x, int y, GdkColor *color, int redraw)
{ static GdkPixmap *pixmap;
static int pixmap_width, pixmap_height;
int w,h,pw;
int erase_to = Closure->readAdaptiveSpiral->mx - Closure->readAdaptiveSpiral->diameter/2;
SetText(l, text, &w, &h);
pw = erase_to-x;
if(pw > pixmap_width || h > pixmap_height)
{ if(pixmap) g_object_unref(pixmap);
pixmap = gdk_pixmap_new(d, pw, h, -1);
pixmap_width = pw;
pixmap_height = h;
}
if(redraw) /* redraw using double buffering to prevent flicker */
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->background);
gdk_draw_rectangle(pixmap, Closure->drawGC, TRUE, 0, 0, pw, h);
gdk_gc_set_rgb_fg_color(Closure->drawGC, color);
gdk_draw_layout(pixmap, Closure->drawGC, 0, 0, l);
gdk_draw_drawable(d, Closure->drawGC, pixmap, 0, 0, x, y, pw, h);
}
return h;
}
static void redraw_labels(GtkWidget *widget, int erase_mask)
{ GdkDrawable *d = Closure->readAdaptiveDrawingArea->window;
char buf[256];
int x,y,w,h;
/* Draw the labels */
x = 10;
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
y = Closure->readAdaptiveSpiral->my - Closure->readAdaptiveSpiral->diameter/2;
h = draw_text(d, Closure->readLinearCurve->layout,
_("Adaptive reading:"), x, y, Closure->foreground, erase_mask & REDRAW_TITLE);
y += h+h/2;
if(Closure->readAdaptiveSubtitle)
{ char *c = Closure->readAdaptiveSubtitle + strlen(Closure->readAdaptiveSubtitle)/2;
while(*c && *c != ' ') /* find point to split text in middle */
c++;
if(c) /* split text into two lines */
{ *c = 0;
h = draw_text(d, Closure->readLinearCurve->layout,
Closure->readAdaptiveSubtitle, x, y, Closure->foreground,
erase_mask & REDRAW_SUBTITLE);
h = draw_text(d, Closure->readLinearCurve->layout,
c+1, x, y+h, Closure->foreground,
erase_mask & REDRAW_SUBTITLE);
*c = ' ';
}
else /* draw text in one line */
{ h = draw_text(d, Closure->readLinearCurve->layout,
Closure->readAdaptiveSubtitle, x, y, Closure->foreground,
erase_mask & REDRAW_SUBTITLE);
}
}
y += 4*h;
h = draw_text(d, Closure->readLinearCurve->layout,
_("Sectors processed"), x, y, Closure->foreground, erase_mask & REDRAW_TITLE);
y += h;
snprintf(buf, 255, " %s: %lld", _("readable"), readable);
h = draw_text(d, Closure->readLinearCurve->layout, buf, x, y, Closure->foreground, erase_mask & REDRAW_PROGRESS);
y += h;
snprintf(buf, 255, " %s: %lld", _("correctable"), correctable);
h = draw_text(d, Closure->readLinearCurve->layout, buf, x, y, Closure->foreground, erase_mask & REDRAW_PROGRESS);
y += h;
snprintf(buf, 255, " %s: %lld", _("missing"), missing);
h = draw_text(d, Closure->readLinearCurve->layout, buf, x, y, Closure->foreground, erase_mask & REDRAW_PROGRESS);
if(min_required > 0 && readable > 0)
{ int percent = round(((1000*readable)/(readable+correctable+missing)));
if(!missing) /* Make sure target percentage is reached */
percent = min_required; /* in spite of rounding errors */
y += h;
snprintf(buf, 255, _("Readable: %d.%d%% / %d.%d%% required"),
percent/10, percent%10,
min_required/10, min_required%10);
h = draw_text(d, Closure->readLinearCurve->layout, buf, x, y, Closure->foreground, erase_mask & REDRAW_PROGRESS);
}
y += h;
snprintf(buf, 255, _("Total recoverable: %d.%d%%"), percent/10, percent%10);
h = draw_text(d, Closure->readLinearCurve->layout, buf, x, y, Closure->foreground, erase_mask & REDRAW_PROGRESS);
if(Closure->readAdaptiveErrorMsg && erase_mask & REDRAW_ERRORMSG)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, footer_color);
SetText(Closure->readLinearCurve->layout, Closure->readAdaptiveErrorMsg, &w, &h);
y = Closure->readAdaptiveSpiral->my + Closure->readAdaptiveSpiral->diameter/2 - h;
gdk_draw_layout(d, Closure->drawGC, x, y, Closure->readLinearCurve->layout);
}
}
static void redraw_spiral(GtkWidget *widget)
{
DrawSpiral(Closure->readAdaptiveSpiral);
}
/* Calculate the geometry of the spiral */
static void update_geometry(GtkWidget *widget)
{ GtkAllocation *a = &widget->allocation;
Closure->readAdaptiveSpiral->mx = a->width - 15 - Closure->readAdaptiveSpiral->diameter / 2;
Closure->readAdaptiveSpiral->my = a->height / 2;
}
/* Expose event handler */
static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
SetSpiralWidget(Closure->readAdaptiveSpiral, widget);
if(event->count) /* Exposure compression */
return TRUE;
update_geometry(widget);
redraw_labels(widget, ~0);
redraw_spiral(widget);
return TRUE;
}
/*
* Clip the spiral. Simply remove the clipping elements to avoid flicker.
*/
static gboolean clip_idle_func(gpointer data)
{ Spiral *spiral = Closure->readAdaptiveSpiral;
int i;
if(spiral->segmentClipping < spiral->segmentCount)
{ GdkColor *outline = spiral->outline;
int clipping = spiral->segmentClipping;
spiral->outline = Closure->background;
spiral->segmentClipping = spiral->segmentCount;
for(i=clipping; i < spiral->segmentCount; i++)
DrawSpiralSegment(spiral, Closure->background, i);
spiral->outline = outline;
spiral->segmentClipping = clipping;
/* Now redraw the last turn */
for(i=ADAPTIVE_READ_SPIRAL_SIZE-300; i<=clipping; i++)
DrawSpiralSegment(spiral, Closure->background, i);
}
return FALSE;
}
void ClipReadAdaptiveSpiral(int segments)
{
Closure->readAdaptiveSpiral->segmentClipping = segments;
g_idle_add(clip_idle_func, NULL);
}
/*
* Change the segment color.
* Segment numbers are passed with an offset of 100,
* since another routine is occasionally doing an
* g_idle_remove_by_data(GINT_TO_POINTER(REDRAW_PROGRESS)),
* with REDRAW_PROGRESS being 4 which would make segment 4 fail to redraw.
* One of the many places where the Gtk+ API is not well thought out.
*/
static gboolean segment_idle_func(gpointer data)
{ int segment = GPOINTER_TO_INT(data);
segment-=100;
DrawSpiralSegment(Closure->readAdaptiveSpiral,
Closure->readAdaptiveSpiral->segmentColor[segment],
segment);
return FALSE;
}
void ChangeSegmentColor(GdkColor *color, int segment)
{
Closure->readAdaptiveSpiral->segmentColor[segment] = color;
if(Closure->readAdaptiveSpiral->cursorPos == segment)
Closure->readAdaptiveSpiral->colorUnderCursor = color;
else g_idle_add(segment_idle_func, GINT_TO_POINTER(100+segment));
}
/*
* Remove the white markers drawn during the fill operation
*/
static gboolean remove_fill_idle_func(gpointer data)
{ Spiral *spiral = Closure->readAdaptiveSpiral;
int i;
for(i=0; i<spiral->segmentCount; i++)
if(spiral->segmentColor[i] == Closure->whiteSector)
DrawSpiralSegment(spiral, Closure->background, i);
return FALSE;
}
void RemoveFillMarkers()
{
g_idle_add(remove_fill_idle_func, NULL);
}
/***
*** Redraw the label in our window
***/
static gboolean label_redraw_idle_func(gpointer data)
{ int erase_mask = GPOINTER_TO_INT(data);
redraw_labels(Closure->readAdaptiveDrawingArea, erase_mask);
return FALSE;
}
void SetAdaptiveReadSubtitle(char *title)
{
if(Closure->readAdaptiveSubtitle)
g_free(Closure->readAdaptiveSubtitle);
Closure->readAdaptiveSubtitle = g_strdup(title);
g_idle_add(label_redraw_idle_func, GINT_TO_POINTER(REDRAW_SUBTITLE));
}
void SetAdaptiveReadFootline(char *msg, GdkColor *color)
{
if(Closure->readAdaptiveErrorMsg)
g_free(Closure->readAdaptiveErrorMsg);
Closure->readAdaptiveErrorMsg = g_strdup(msg);
footer_color = color;
g_idle_add(label_redraw_idle_func, GINT_TO_POINTER(REDRAW_ERRORMSG));
}
void UpdateAdaptiveResults(gint64 r, gint64 c, gint64 m, int p)
{ readable = r;
correctable = c;
missing = m;
percent = p;
g_idle_remove_by_data(GINT_TO_POINTER(REDRAW_PROGRESS));
g_idle_add(label_redraw_idle_func, GINT_TO_POINTER(REDRAW_PROGRESS));
}
/***
*** Reset the notebook contents for new read action
***/
void ResetAdaptiveReadWindow()
{ FillSpiral(Closure->readAdaptiveSpiral, Closure->background);
// DrawSpiral(Closure->readAdaptiveSpiral);
if(Closure->readAdaptiveSubtitle)
g_free(Closure->readAdaptiveSubtitle);
if(Closure->readAdaptiveErrorMsg)
g_free(Closure->readAdaptiveErrorMsg);
Closure->readAdaptiveSubtitle = NULL;
Closure->readAdaptiveErrorMsg = NULL;
readable = correctable = missing = 0;
percent = min_required = 0;
if(Closure->readAdaptiveDrawingArea->window)
{ static GdkRectangle rect;
GtkAllocation *a = &Closure->readAdaptiveDrawingArea->allocation;
rect.x = rect.y = 0;
rect.width = a->width;
rect.height = a->height;
gdk_window_clear(Closure->readAdaptiveDrawingArea->window);
gdk_window_invalidate_rect(Closure->readAdaptiveDrawingArea->window, &rect, FALSE);
}
}
/*
* Set the minimum required data recovery value
*/
void SetAdaptiveReadMinimumPercentage(int value)
{ min_required = value;
}
/***
*** Create the notebook contents for the reading and scanning action
***/
void CreateAdaptiveReadWindow(GtkWidget *parent)
{ GtkWidget *sep,*d_area;
Closure->readAdaptiveHeadline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(Closure->readAdaptiveHeadline), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(Closure->readAdaptiveHeadline), 5, 0);
gtk_label_set_ellipsize(GTK_LABEL(Closure->readAdaptiveHeadline), PANGO_ELLIPSIZE_END);
gtk_box_pack_start(GTK_BOX(parent), Closure->readAdaptiveHeadline, FALSE, FALSE, 3);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
d_area = Closure->readAdaptiveDrawingArea = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(parent), d_area, TRUE, TRUE, 0);
g_signal_connect(G_OBJECT(d_area), "expose_event", G_CALLBACK(expose_cb), NULL);
Closure->readAdaptiveSpiral = CreateSpiral(Closure->grid, Closure->background, 10, 5,
ADAPTIVE_READ_SPIRAL_SIZE);
gtk_widget_set_size_request(d_area, -1, Closure->readAdaptiveSpiral->diameter);
}

2150
src/read-adaptive.c Normal file

File diff suppressed because it is too large Load Diff

447
src/read-linear-window.c Normal file
View File

@@ -0,0 +1,447 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#include "read-linear.h"
#include "scsi-layer.h"
#define C2_CLAMP_VALUE 2352
/***
*** Forward declarations
***/
static void redraw_curve(void);
static void update_geometry(void);
/***
*** Routines for updating the GUI from the action thread.
***/
/*
* Set the (predicted) maximum reading speed
*/
static gboolean max_speed_idle_func(gpointer data)
{
gdk_window_clear(Closure->readLinearDrawingArea->window);
update_geometry();
redraw_curve();
return FALSE;
}
void InitializeCurve(void *rc_ptr, int max_rate, int can_c2)
{ read_closure *rc = (read_closure*)rc_ptr;
int i;
Closure->readLinearCurve->maxY = max_rate;
Closure->readLinearCurve->maxX = rc->image->dh->sectors/512;
Closure->readLinearCurve->logMaxY = C2_CLAMP_VALUE;
if(can_c2) Closure->readLinearCurve->enable = DRAW_FCURVE | DRAW_LCURVE;
else Closure->readLinearCurve->enable = DRAW_FCURVE;
rc->lastCopied = (1000*rc->firstSector)/rc->image->dh->sectors;
rc->lastPlotted = rc->lastSegment = rc->lastCopied;
rc->lastPlottedY = 0;
if(Closure->readLinearSpiral)
for(i=rc->lastCopied-1; i>=0; i--)
{ Closure->readLinearSpiral->segmentColor[i] = Closure->blueSector;
Closure->readLinearCurve->ivalue[i] = 0;
}
g_idle_add(max_speed_idle_func, NULL);
}
/*
* Drawing the reading speed curve
*/
typedef struct
{ read_closure *rc;
int percent;
} curve_info;
static gboolean curve_idle_func(gpointer data)
{ curve_info *ci = (curve_info*)data;
read_closure *rc=ci->rc;
gint x0,y0;
char *utf,buf[80];
gint i;
gint resize_curve = FALSE;
/*** Update the textual output */
g_snprintf(buf, 80, _("Current Speed: %d.%dx"),
(int)Closure->readLinearCurve->fvalue[ci->percent],
(int)(fmod(10*Closure->readLinearCurve->fvalue[ci->percent],10)));
utf = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
gtk_label_set_text(GTK_LABEL(Closure->readLinearSpeed), utf);
g_free(utf);
g_snprintf(buf, 80, _("Unreadable / skipped sectors: %lld"), Closure->readErrors);
utf = g_locale_to_utf8(buf, -1, NULL, NULL, NULL);
gtk_label_set_text(GTK_LABEL(Closure->readLinearErrors), utf);
g_free(utf);
/*** Draw the changed spiral segments */
for(i=rc->lastSegment; i<ci->percent; i++)
switch(Closure->readLinearCurve->ivalue[i])
{ case 0: DrawSpiralSegment(Closure->readLinearSpiral, Closure->blueSector, i); break;
case 1: DrawSpiralSegment(Closure->readLinearSpiral, Closure->greenSector, i); break;
case 2: DrawSpiralSegment(Closure->readLinearSpiral, Closure->redSector, i); break;
case 3: DrawSpiralSegment(Closure->readLinearSpiral, Closure->darkSector, i); break;
case 4: DrawSpiralSegment(Closure->readLinearSpiral, Closure->yellowSector, i); break;
}
rc->lastSegment = ci->percent;
if(rc->pass) /* 2nd or higher reading pass, don't touch the curve */
{ g_free(ci);
g_mutex_lock(rc->rendererMutex);
rc->activeRenderers--;
g_mutex_unlock(rc->rendererMutex);
return FALSE;
}
/*** Resize the Y axes if speed value exceeds current maximum */
for(i=rc->lastPlotted+1; i<=ci->percent; i++)
if(Closure->readLinearCurve->fvalue[i] > Closure->readLinearCurve->maxY)
resize_curve = TRUE;
if(resize_curve)
{ Closure->readLinearCurve->maxY = Closure->readLinearCurve->fvalue[ci->percent] + 1;
update_geometry();
gdk_window_clear(Closure->readLinearDrawingArea->window);
redraw_curve();
rc->lastPlotted = ci->percent;
rc->lastPlottedY = CurveY(Closure->readLinearCurve, Closure->readLinearCurve->fvalue[ci->percent]);
g_free(ci);
g_mutex_lock(rc->rendererMutex);
rc->activeRenderers--;
g_mutex_unlock(rc->rendererMutex);
return FALSE;
}
/*** Draw the changed curve part */
x0 = CurveX(Closure->readLinearCurve, rc->lastPlotted);
y0 = CurveY(Closure->readLinearCurve, Closure->readLinearCurve->fvalue[rc->lastPlotted]);
if(rc->lastPlottedY) y0 = rc->lastPlottedY;
for(i=rc->lastPlotted+1; i<=ci->percent; i++)
{ gint x1 = CurveX(Closure->readLinearCurve, i);
gint y1 = CurveY(Closure->readLinearCurve, Closure->readLinearCurve->fvalue[i]);
gint l1 = CurveLogY(Closure->readLinearCurve, Closure->readLinearCurve->lvalue[i]);
if(Closure->readLinearCurve->lvalue[i])
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->logColor);
gdk_draw_rectangle(Closure->readLinearDrawingArea->window,
Closure->drawGC, TRUE,
x0, l1,
x0==x1 ? 1 : x1-x0, Closure->readLinearCurve->bottomLY-l1);
}
if(x0<x1)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->curveColor);
gdk_draw_line(Closure->readLinearDrawingArea->window,
Closure->drawGC,
x0, y0, x1, y1);
rc->lastPlotted = ci->percent;
x0 = x1;
rc->lastPlottedY = y0 = y1;
}
}
g_free(ci);
g_mutex_lock(rc->rendererMutex);
rc->activeRenderers--;
g_mutex_unlock(rc->rendererMutex);
return FALSE;
}
/*
* Add one new data point
*/
void AddCurveValues(void *rc_ptr, int percent, int color, int c2)
{ read_closure *rc = (read_closure*)rc_ptr;
curve_info *ci;
int i;
if(percent < 0 || percent > 1000)
return;
ci = g_malloc(sizeof(curve_info));
ci->rc = rc;
ci->percent = percent;
/*** Mark unused speed values between lastCopied and Percent */
if(!rc->pass)
{ int c2_clamped = c2 < C2_CLAMP_VALUE ? c2 : C2_CLAMP_VALUE;
Closure->readLinearCurve->fvalue[percent] = rc->speed;
Closure->readLinearCurve->lvalue[percent] = c2_clamped;
for(i=rc->lastCopied+1; i<percent; i++)
{ Closure->readLinearCurve->fvalue[i] = rc->speed > 0.0 ? -1.0 : 0.0;
Closure->readLinearCurve->lvalue[i] = c2_clamped;
}
}
/*** Mark the spiral segments between lastCopied and Percent*/
/* lastCopied+1 ? */
if(rc->lastCopied <= percent)
{ for(i=rc->lastCopied; i<=percent; i++)
Closure->readLinearCurve->ivalue[i] = color;
rc->lastCopied = percent;
}
g_mutex_lock(rc->rendererMutex);
rc->activeRenderers++;
g_mutex_unlock(rc->rendererMutex);
g_idle_add(curve_idle_func, ci);
}
/*
* Mark existing sectors with the dark green color.
*/
static gboolean curve_mark_idle_func(gpointer data)
{
DrawSpiral(Closure->readLinearSpiral);
return FALSE;
}
void MarkExistingSectors(void)
{ int i;
int x = Closure->readLinearCurve->rightX + 20;
Closure->additionalSpiralColor = 3;
DrawSpiralLabel(Closure->readLinearSpiral, Closure->readLinearCurve->layout,
_("Already present"), Closure->darkSector, x, -1);
for(i=0; i<1000; i++)
if(Closure->readLinearSpiral->segmentColor[i] == Closure->greenSector)
{ Closure->readLinearSpiral->segmentColor[i] = Closure->darkSector;
Closure->readLinearCurve->ivalue[i] = 3;
}
g_idle_add(curve_mark_idle_func, NULL);
}
/*
* Redraw the whole curve
*/
/* Calculate the geometry of the curve and spiral */
static void update_geometry(void)
{ GtkWidget *widget = Closure->readLinearDrawingArea;
GtkAllocation *a = &widget->allocation;
/* Curve geometry */
UpdateCurveGeometry(Closure->readLinearCurve, "99x",
Closure->readLinearSpiral->diameter + 30);
/* Spiral center */
Closure->readLinearSpiral->mx = a->width - 15 - Closure->readLinearSpiral->diameter / 2;
Closure->readLinearSpiral->my = a->height / 2;
if(Closure->crcBuf && Closure->crcBuf->crcCached)
{ int w,h;
SetText(Closure->readLinearCurve->layout, _("Sectors with CRC errors"), &w, &h);
Closure->readLinearSpiral->my -= h;
}
/* Label positions in the foot line */
gtk_box_set_child_packing(GTK_BOX(Closure->readLinearFootlineBox), Closure->readLinearSpeed,
TRUE, TRUE, Closure->readLinearCurve->leftX, GTK_PACK_START);
gtk_box_set_child_packing(GTK_BOX(Closure->readLinearFootlineBox), Closure->readLinearErrors,
TRUE, TRUE, Closure->readLinearCurve->leftX, GTK_PACK_START);
}
static void redraw_curve(void)
{ GdkDrawable *d = Closure->readLinearDrawingArea->window;
int x,w,h;
int pos = 1;
/* Draw and label the spiral */
x = Closure->readLinearCurve->rightX + 20;
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->curveColor);
SetText(Closure->readLinearCurve->layout, _("Medium state"), &w, &h);
gdk_draw_layout(d, Closure->drawGC,
x,
Closure->readLinearCurve->topY - h - 5,
Closure->readLinearCurve->layout);
if(Closure->additionalSpiralColor == 0)
DrawSpiralLabel(Closure->readLinearSpiral, Closure->readLinearCurve->layout,
_("Not touched this time"), Closure->curveColor, x, -1);
if(Closure->additionalSpiralColor == 3)
DrawSpiralLabel(Closure->readLinearSpiral, Closure->readLinearCurve->layout,
_("Already present"), Closure->darkSector, x, -1);
DrawSpiralLabel(Closure->readLinearSpiral, Closure->readLinearCurve->layout,
_("Successfully read"), Closure->greenSector, x, pos++);
if(Closure->crcBuf && Closure->crcBuf->crcCached)
DrawSpiralLabel(Closure->readLinearSpiral, Closure->readLinearCurve->layout,
_("Sectors with CRC errors"), Closure->yellowSector, x, pos++);
DrawSpiralLabel(Closure->readLinearSpiral, Closure->readLinearCurve->layout,
_("Unreadable / skipped"), Closure->redSector, x, pos++);
DrawSpiral(Closure->readLinearSpiral);
/* Redraw the curve */
RedrawAxes(Closure->readLinearCurve);
RedrawCurve(Closure->readLinearCurve, 1000);
}
static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
SetSpiralWidget(Closure->readLinearSpiral, widget);
if(event->count) /* Exposure compression */
return TRUE;
update_geometry();
redraw_curve();
return TRUE;
}
/***
*** Reset the notebook contents for new scan/read action
***/
void ResetLinearReadWindow()
{
gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->readLinearNotebook), 0);
ZeroCurve(Closure->readLinearCurve);
FillSpiral(Closure->readLinearSpiral, Closure->background);
DrawSpiral(Closure->readLinearSpiral);
}
/*
* Re-layout and redraw the read window while it is in use.
* Required to add the information that CRC data is available,
* since this happens when the the initial rendering of the window
* contents have already been carried out.
*/
static gboolean redraw_idle_func(gpointer data)
{ GdkRectangle rect;
GdkWindow *window;
gint ignore;
/* Trigger an expose event for the drawing area. */
window = gtk_widget_get_parent_window(Closure->readLinearDrawingArea);
if(window)
{ gdk_window_get_geometry(window, &rect.x, &rect.y, &rect.width, &rect.height, &ignore);
gdk_window_invalidate_rect(window, &rect, TRUE);
}
return FALSE;
}
void RedrawReadLinearWindow(void)
{
g_idle_add(redraw_idle_func, NULL);
}
/***
*** Create the notebook contents for the reading and scanning action
***/
void CreateLinearReadWindow(GtkWidget *parent)
{ GtkWidget *sep,*ignore,*d_area,*notebook,*hbox;
Closure->readLinearHeadline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(Closure->readLinearHeadline), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(Closure->readLinearHeadline), 5, 0);
gtk_label_set_ellipsize(GTK_LABEL(Closure->readLinearHeadline), PANGO_ELLIPSIZE_END);
gtk_box_pack_start(GTK_BOX(parent), Closure->readLinearHeadline, FALSE, FALSE, 3);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
d_area = Closure->readLinearDrawingArea = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(parent), d_area, TRUE, TRUE, 0);
g_signal_connect(G_OBJECT(d_area), "expose_event", G_CALLBACK(expose_cb), NULL);
notebook = Closure->readLinearNotebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
gtk_box_pack_end(GTK_BOX(parent), notebook, FALSE, FALSE, 0);
hbox = Closure->readLinearFootlineBox = gtk_hbox_new(FALSE, 0);
Closure->readLinearSpeed = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(Closure->readLinearSpeed), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), Closure->readLinearSpeed, FALSE, FALSE, 0);
Closure->readLinearErrors = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(Closure->readLinearErrors), 1.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), Closure->readLinearErrors, TRUE, TRUE, 0);
ignore = gtk_label_new("progress_tab");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, ignore);
Closure->readLinearFootline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(Closure->readLinearFootline), 0.0, 0.5);
gtk_misc_set_padding(GTK_MISC(Closure->readLinearFootline), 5, 0);
ignore = gtk_label_new("footer_tab");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), Closure->readLinearFootline, ignore);
Closure->readLinearCurve = CreateCurve(d_area, _("Speed"), "%dx", 1000, CURVE_MEGABYTES);
Closure->readLinearCurve->leftLogLabel = g_strdup(_("C2 errors"));
Closure->readLinearSpiral = CreateSpiral(Closure->grid, Closure->background, 10, 5, 1000);
}

1492
src/read-linear.c Normal file

File diff suppressed because it is too large Load Diff

97
src/read-linear.h Normal file
View File

@@ -0,0 +1,97 @@
/* 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/>.
*/
#ifndef READ_LINEAR_H
#define READ_LINEAR_H
/*
* Local data package used during reading
*/
#define READ_BUFFERS 128 /* equals 4MB of buffer space */
typedef struct
{ LargeFile *readerImage; /* we need two file handles to prevent LargeSeek() */
LargeFile *writerImage; /* race conditions between the reader and writer */
Image *image;
Method *eccMethod; /* Ecc method selected for this image */
EccHeader *eccHeader; /* accompanying Ecc header */
GThread *worker;
struct MD5Context md5ctxt; /* Complete image checksum (RS01) */
struct MD5Context dataCtxt; /* Image section checksums (RS02) */
struct MD5Context crcCtxt; /* Image section checksums (RS02) */
struct MD5Context eccCtxt; /* Ecc layer checksum (RS02) */
struct MD5Context metaCtxt; /* Ecc meta checksum (RS02) */
int doChecksumsFromImage; /* calculate sector CRC and MD5 from image */
int doChecksumsFromCodec; /* let codec compute its own/additional checksums */
int savedSectorSkip;
char *volumeLabel;
/* Data exchange between reader and worker */
struct _AlignedBuffer *alignedBuf[READ_BUFFERS];
gint64 bufferedSector[READ_BUFFERS];
int nSectors[READ_BUFFERS];
int bufState[READ_BUFFERS];
GMutex *mutex;
GCond *canRead, *canWrite;
int readPtr,writePtr;
char *workerError;
/* for usage within the reader */
gint64 firstSector, lastSector; /* reading range */
gint64 readPos; /* current sector reading position */
Bitmap *readMap; /* map of already read sectors */
gint64 readMarker;
int rereading; /* TRUE if working on existing image */
char *msg;
GTimer *speedTimer,*readTimer;
int unreportedError;
int earlyTermination;
int scanMode;
int lastPercent;
int firstSpeedValue;
double speed,lastSpeed;
gint64 readOK, lastReadOK;
int previousReadErrors;
int previousCRCErrors;
gint64 deadWritten;
gint64 lastErrorsPrinted;
int pass;
int maxC2; /* max C2 error since last output */
int crcIncomplete; /* CRC information was found incomplete (RS03 only) */
/* for drawing the curve and spiral */
gint lastCopied;
gint lastSegment;
gint lastPlotted;
gint lastPlottedY;
gint activeRenderers;
GMutex *rendererMutex;
} read_closure;
#endif /* READ_LINEAR_H */

1051
src/recover-raw.c Normal file

File diff suppressed because it is too large Load Diff

64
src/rs-decoder.c Normal file
View File

@@ -0,0 +1,64 @@
/* 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 "galois-inlines.h"
/***
*** Reed-Solomon decoding (work in progress; incomplete)
***/
/*
* Test and report the error syndrome.
*/
int TestErrorSyndromes(ReedSolomonTables *rt, unsigned char *data)
{ int syndrome[rt->nroots];
int syn_error;
int i,j;
/*** Form the syndromes: Evaluate data(x) at roots of g(x) */
for(i=0; i<rt->nroots; i++)
syndrome[i] = data[0];
for(j=1; j<GF_FIELDMAX; j++)
for(i=0; i<rt->nroots; i++)
if(syndrome[i] == 0)
syndrome[i] = data[j];
else syndrome[i] = data[j] ^ rt->synLut[(i<<8) + syndrome[i]];
#if 0
else syndrome[i] = data[j] ^ gt->alphaTo[mod_fieldmax(gt->indexOf[syndrome[i]]
+ (rt->fcr+i)*rt->primElem)];
#endif
/*** Check for nonzero condition. */
syn_error = 0;
for(i=0; i<rt->nroots; i++)
syn_error |= syndrome[i];
/*** If the syndrome is zero, everything is fine. */
return syn_error;
}

122
src/rs-encoder-altivec.c Normal file
View File

@@ -0,0 +1,122 @@
/* 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"
#ifdef HAVE_ALTIVEC
# include <altivec.h>
#endif
#include <signal.h>
#include <setjmp.h>
/***
*** Reed-Solomon encoding using AltiVec intrinsics
***
*** Based on rs-encoder-altivec.c
*** AltiVec version by michael.klein@puffin.lb.shuttle.de
***/
/* AltiVec version */
#ifdef HAVE_ALTIVEC
static volatile int AltiVecPresent;
static jmp_buf jmpbuf;
void sig_ill_handler(int sig)
{
AltiVecPresent = 0;
siglongjmp(jmpbuf, 0);
}
int ProbeAltiVec(void)
{
sig_t old_handler;
AltiVecPresent = 1;
old_handler = signal(SIGILL, sig_ill_handler);
if(!sigsetjmp(jmpbuf, 0))
{
vector unsigned char v;
asm volatile("vor %0, %0, %0": "=v"(v));
}
signal(SIGILL, old_handler);
return AltiVecPresent;
}
void encode_next_layer_altivec(ReedSolomonTables *rt, unsigned char *data, unsigned char *parity, guint64 layer_size, int shift)
{ gint32 *gf_index_of = rt->gfTables->indexOf;
gint32 *enc_alpha_to = rt->gfTables->encAlphaTo;
gint32 *rs_gpoly = rt->gpoly;
int nroots = rt->nroots;
int nroots_aligned = (nroots+15)&~15;
int nroots_full = nroots_aligned>>4;
int i,j;
for(i=0; i<layer_size; i++)
{ int feedback = gf_index_of[data[i] ^ parity[shift]];
int offset = nroots-shift-1;
if(feedback != GF_ALPHA0) /* non-zero feedback term */
{ guint8 *par_idx = (guint8*)parity;
guint8 *e_lut = rt->bLut[feedback]+offset;
vector unsigned char par, lut, out, msq, lsq, mask;
/* Process lut in 128 bit steps */
mask = vec_lvsl(0, e_lut);
for(j=nroots_full; j; j--)
{
par = vec_ld(0, par_idx);
msq = vec_ld(0, e_lut);
lsq = vec_ld(15, e_lut);
lut = vec_perm(msq, lsq, mask);
out = vec_xor(par, lut);
vec_st(out, 0, par_idx);
par_idx += 16;
e_lut += 16;
}
parity[shift] = enc_alpha_to[feedback + rs_gpoly[0]];
}
else /* zero feedback term */
parity[shift] = 0;
parity += nroots_aligned;
}
}
#else /* don't have ALTIVEC */
int ProbeAltiVec()
{ return 0;
}
void encode_next_layer_altivec(ReedSolomonTables *rt, unsigned char *data, unsigned char *parity, guint64 layer_size, int shift)
{
Stop("Mega borkage - EncodeNextLayerAltiVec() stub called.\n");
}
#endif /* HAVE_ALTIVEC */

112
src/rs-encoder-sse2.c Normal file
View File

@@ -0,0 +1,112 @@
/* 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"
#ifdef HAVE_SSE2
#include <emmintrin.h>
#ifdef HAVE_CPUID
#include <cpuid.h>
#else
#include "compat/cpuid.h"
#endif
#endif
/***
*** Reed-Solomon encoding using SSE2 intrinsics
***/
/* SSE 2 version */
#ifdef HAVE_SSE2
int ProbeSSE2(void)
{ unsigned int eax, ebx, ecx, edx;
if(!__get_cpuid(1, &eax, &ebx, &ecx, &edx))
{ Verbose("[ProbeSSE2: get_cpuid() failed]\n");
return 0;
}
if(edx & bit_SSE2)
{ Verbose("[ProbeSSE2: SSE2 available]\n");
return 1;
}
else
{ Verbose("[ProbeSSE2: no SSE2]\n");
return 0;
}
}
void encode_next_layer_sse2(ReedSolomonTables *rt, unsigned char *data, unsigned char *parity, guint64 layer_size, int shift)
{ gint32 *gf_index_of = rt->gfTables->indexOf;
gint32 *enc_alpha_to = rt->gfTables->encAlphaTo;
gint32 *rs_gpoly = rt->gpoly;
int nroots = rt->nroots;
int nroots_aligned = (nroots+15)&~15;
int nroots_full = nroots_aligned>>4;
int i,j;
for(i=0; i<layer_size; i++)
{ int feedback = gf_index_of[data[i] ^ parity[shift]];
int offset = nroots-shift-1;
if(feedback != GF_ALPHA0) /* non-zero feedback term */
{ guint8 *par_idx = (guint8*)parity;
guint8 *e_lut = rt->bLut[feedback]+offset;
__m128i par, lut, out;
/* Process lut in 128 bit steps */
for(j=nroots_full; j; j--)
{
par = _mm_load_si128((__m128i*)par_idx);
lut = _mm_loadu_si128((__m128i*)e_lut);
out = _mm_xor_si128(par, lut);
_mm_store_si128((__m128i*)par_idx, out);
par_idx += 16;
e_lut += 16;
}
parity[shift] = enc_alpha_to[feedback + rs_gpoly[0]];
}
else /* zero feedback term */
parity[shift] = 0;
parity += nroots_aligned;
}
}
#else /* don't have SSE2 */
/* Stub functions to keep the linker happy.
* Should never be executed.
*/
int ProbeSSE2()
{ return 0;
}
void encode_next_layer_sse2(ReedSolomonTables *rt, unsigned char *data, unsigned char *parity, guint64 layer_size, int shift)
{
Stop("Mega borkage - EncodeNextLayerSSE2() stub called.\n");
}
#endif /* HAVE_SSE2 */

286
src/rs-encoder.c Normal file
View File

@@ -0,0 +1,286 @@
/* 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"
/***
*** Reed-Solomon encoding
***/
/* Portable (non-SSE2) version.
* Using 32bit operands seems to be a good choice for the lowest
* common denominator between the non-SSE2 systems.
*/
#ifdef HAVE_BIG_ENDIAN
#define SHIFT_LEFT <<
#define SHIFT_RIGHT >>
#else
#define SHIFT_LEFT >>
#define SHIFT_RIGHT <<
#endif /* HAVE_BIG_ENDIAN */
static void encode_next_layer_portable(ReedSolomonTables *rt, unsigned char *data, unsigned char *parity, guint64 layer_size, int shift)
{ gint32 *gf_index_of = rt->gfTables->indexOf;
gint32 *enc_alpha_to = rt->gfTables->encAlphaTo;
gint32 *rs_gpoly = rt->gpoly;
int nroots = rt->nroots;
int nroots_aligned = (nroots+15)&~15;
int nroots_aligned32 = (nroots+3)&~3;
int nroots_full = nroots_aligned32>>2;
int i,j;
for(i=0; i<layer_size; i++)
{ int feedback = gf_index_of[data[i] ^ parity[shift]];
int offset = nroots-shift-1;
int byte_offset = offset&3;
if(feedback != GF_ALPHA0) /* non-zero feedback term */
{ guint32 *par_idx = (guint32*)parity;
guint32 *e_lut = ((guint32*)(rt->bLut[feedback]+(offset&~3)));
/* Process lut in 32 bit steps */
switch(byte_offset)
{ case 0:
for(j=nroots_full; j; j--)
*par_idx++ ^= *e_lut++;
break;
case 1:
{ for(j=nroots_full; j; j--)
{ guint32 span = *e_lut SHIFT_LEFT 8;
e_lut++;
span |= *e_lut SHIFT_RIGHT 24;
*par_idx++ ^= span;
}
}
break;
case 2:
{ for(j=nroots_full; j; j--)
{ guint32 span = *e_lut SHIFT_LEFT 16;
e_lut++;
span |= *e_lut SHIFT_RIGHT 16;
*par_idx++ ^= span;
}
}
break;
case 3:
{ for(j=nroots_full; j; j--)
{ guint32 span = *e_lut SHIFT_LEFT 24;
e_lut++;
span |= *e_lut SHIFT_RIGHT 8;
*par_idx++ ^= span;
}
}
break;
}
parity[shift] = enc_alpha_to[feedback + rs_gpoly[0]];
}
else /* zero feedback term */
parity[shift] = 0;
parity += nroots_aligned;
}
}
/* 64bit integer (non-SSE2) version.
* May perform better on systems which have shared FPU/SSE2 units
* between several cores.
*/
static void encode_next_layer_64bit(ReedSolomonTables *rt, unsigned char *data, unsigned char *parity, guint64 layer_size, int shift)
{ gint32 *gf_index_of = rt->gfTables->indexOf;
gint32 *enc_alpha_to = rt->gfTables->encAlphaTo;
gint32 *rs_gpoly = rt->gpoly;
int nroots = rt->nroots;
int nroots_aligned = (nroots+15)&~15;
int nroots_aligned64 = (nroots+7)&~7;
int nroots_full = nroots_aligned64>>3;
int i,j;
for(i=0; i<layer_size; i++)
{ int feedback = gf_index_of[data[i] ^ parity[shift]];
int offset = nroots-shift-1;
int byte_offset = offset&7;
if(feedback != GF_ALPHA0) /* non-zero feedback term */
{ guint64 *par_idx = (guint64*)parity;
guint64 *e_lut = ((guint64*)(rt->bLut[feedback]+(offset&~7)));
/* Process lut in 64 bit steps */
switch(byte_offset)
{ case 0:
for(j=nroots_full; j; j--)
*par_idx++ ^= *e_lut++;
break;
case 1:
{ for(j=nroots_full; j; j--)
{ guint64 span = *e_lut SHIFT_LEFT 8;
e_lut++;
span |= *e_lut SHIFT_RIGHT 56;
*par_idx++ ^= span;
}
}
break;
case 2:
{ for(j=nroots_full; j; j--)
{ guint64 span = *e_lut SHIFT_LEFT 16;
e_lut++;
span |= *e_lut SHIFT_RIGHT 48;
*par_idx++ ^= span;
}
}
break;
case 3:
{ for(j=nroots_full; j; j--)
{ guint64 span = *e_lut SHIFT_LEFT 24;
e_lut++;
span |= *e_lut SHIFT_RIGHT 40;
*par_idx++ ^= span;
}
}
break;
case 4:
{ for(j=nroots_full; j; j--)
{ guint64 span = *e_lut SHIFT_LEFT 32;
e_lut++;
span |= *e_lut SHIFT_RIGHT 32;
*par_idx++ ^= span;
}
}
break;
case 5:
{ for(j=nroots_full; j; j--)
{ guint64 span = *e_lut SHIFT_LEFT 40;
e_lut++;
span |= *e_lut SHIFT_RIGHT 24;
*par_idx++ ^= span;
}
}
break;
case 6:
{ for(j=nroots_full; j; j--)
{ guint64 span = *e_lut SHIFT_LEFT 48;
e_lut++;
span |= *e_lut SHIFT_RIGHT 16;
*par_idx++ ^= span;
}
}
break;
case 7:
{ for(j=nroots_full; j; j--)
{ guint64 span = *e_lut SHIFT_LEFT 56;
e_lut++;
span |= *e_lut SHIFT_RIGHT 8;
*par_idx++ ^= span;
}
}
break;
}
parity[shift] = enc_alpha_to[feedback + rs_gpoly[0]];
}
else /* zero feedback term */
parity[shift] = 0;
parity += nroots_aligned;
}
}
/*
* Dispatch upon availability of SSE2 intrinsics
*/
void encode_next_layer_sse2(ReedSolomonTables*, unsigned char*, unsigned char*, guint64, int);
void encode_next_layer_altivec(ReedSolomonTables*, unsigned char*, unsigned char*, guint64, int);
void EncodeNextLayer(ReedSolomonTables *rt, unsigned char *data, unsigned char *parity, guint64 layer_size, int shift)
{
switch(Closure->encodingAlgorithm)
{ case ENCODING_ALG_32BIT:
encode_next_layer_portable(rt, data, parity, layer_size, shift);
break;
case ENCODING_ALG_64BIT:
encode_next_layer_64bit(rt, data, parity, layer_size, shift);
break;
case ENCODING_ALG_SSE2:
encode_next_layer_sse2(rt, data, parity, layer_size, shift);
break;
case ENCODING_ALG_ALTIVEC:
encode_next_layer_altivec(rt, data, parity, layer_size, shift);
break;
case ENCODING_ALG_DEFAULT:
if(Closure->useSSE2)
encode_next_layer_sse2(rt, data, parity, layer_size, shift);
else if(Closure->useAltiVec)
encode_next_layer_altivec(rt, data, parity, layer_size, shift);
else
encode_next_layer_portable(rt, data, parity, layer_size, shift);
break;
}
}
/*
* Provide textual description for current encoder parameters
*/
void DescribeRSEncoder(char **algorithm, char **iostrategy)
{
switch(Closure->encodingAlgorithm)
{ case ENCODING_ALG_32BIT:
*algorithm="32bit";
break;
case ENCODING_ALG_64BIT:
*algorithm="64bit";
break;
case ENCODING_ALG_SSE2:
*algorithm="SSE2";
break;
case ENCODING_ALG_ALTIVEC:
*algorithm="AltiVec";
break;
case ENCODING_ALG_DEFAULT:
if(Closure->useSSE2)
*algorithm="SSE2";
else if(Closure->useAltiVec)
*algorithm="AltiVec";
else
*algorithm="64bit";
break;
}
if(Closure->encodingIOStrategy == IO_STRATEGY_MMAP)
*iostrategy="mmap";
else *iostrategy="read/write";
}

421
src/rs01-common.c Normal file
View 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);
}

1122
src/rs01-create.c Normal file

File diff suppressed because it is too large Load Diff

849
src/rs01-fix.c Normal file
View File

@@ -0,0 +1,849 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2017 Carsten Gnoerlich.
*
* The Reed-Solomon error correction draws a lot of inspiration - and even code -
* from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
*
* 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"
#include "galois-inlines.h"
/*
* Read crc values from the .ecc file.
*/
static void read_crc(LargeFile *ecc, guint32 *buf, int first_sector, int n_sectors)
{ int n;
if(!LargeSeek(ecc, (gint64)(sizeof(EccHeader) + first_sector*sizeof(guint32))))
Stop(_("Failed seeking in crc area: %s"), strerror(errno));
n = LargeRead(ecc, buf, sizeof(guint32)*n_sectors);
if(n != sizeof(guint32)*n_sectors)
Stop(_("problem reading crc data: %s"),strerror(errno));
}
/***
*** Fix the medium sectors.
***
*/
/*
* Local data package used during fixing
*/
typedef struct
{
#ifndef CLI
RS01Widgets *wl;
#endif
GaloisTables *gt;
ReedSolomonTables *rt;
Image *image;
int earlyTermination;
char *msg;
unsigned char *imgBlock[256];
guint32 *crcBuf[256];
} fix_closure;
static void fix_cleanup(gpointer data)
{ fix_closure *fc = (fix_closure*)data;
int i;
UnregisterCleanup();
#ifndef CLI
if(Closure->guiMode)
{ if(fc->earlyTermination)
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by unrecoverable error.</span>"),
Closure->redMarkup);
AllowActions(TRUE);
}
#endif
/** Clean up */
if(fc->image) CloseImage(fc->image);
if(fc->msg) g_free(fc->msg);
for(i=0; i<256; i++)
{ if(fc->imgBlock[i])
g_free(fc->imgBlock[i]);
if(fc->crcBuf[i])
g_free(fc->crcBuf[i]);
}
if(fc->gt) FreeGaloisTables(fc->gt);
if(fc->rt) FreeReedSolomonTables(fc->rt);
g_free(fc);
#ifndef CLI
if(Closure->guiMode)
g_thread_exit(0);
#endif
}
/*
* Try to repair the image
*/
void RS01Fix(Image *image)
{
#ifndef CLI
Method *method = FindMethod("RS01");
RS01Widgets *wl = (RS01Widgets*)method->widgetList;
#endif
GaloisTables *gt;
ReedSolomonTables *rt;
fix_closure *fc = g_malloc0(sizeof(fix_closure));
EccHeader *eh = NULL;
unsigned char parity[256];
int erasure_count,erasure_list[256],erasure_map[256];
gint64 block_idx[256];
gint64 s,si;
int i,j,k,n;
gint64 corrected, uncorrected;
gint64 parity_block = 0;
guint64 expected_image_size;
int worst_ecc,damaged_ecc,damaged_sec,percent,last_percent = -1;
int cache_size,cache_sector,cache_offset = 0;
int local_plot_max;
char *t = NULL;
gint32 nroots; /* These are copied to increase performance. */
gint32 ndata;
gint32 *gf_index_of;
gint32 *gf_alpha_to;
/*** Register the cleanup procedure for GUI mode */
fc->image = image;
#ifndef CLI
fc->wl = wl;
#endif
fc->earlyTermination = TRUE;
RegisterCleanup(_("Repairing of image aborted"), fix_cleanup, fc);
eh = image->eccFileHeader;
/*** Announce what we are going to do. */
fc->msg = g_strdup_printf(_("Error correction file using Method RS01, %d roots, %4.1f%% redundancy."),
eh->eccBytes,
((double)eh->eccBytes*100.0)/(double)eh->dataBytes);
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->fixHeadline),
_("<big>Repairing the image.</big>\n<i>%s</i>"),fc->msg);
RS01SetFixMaxValues(wl, eh->dataBytes, eh->eccBytes, image->sectorSize);
}
#endif
PrintLog(_("\nFix mode(%s): Repairable sectors will be fixed in the image.\n"),
"RS01");
/*** Do some trivial comparisons between the .ecc file and the image file */
if(!eh->inLast) /* field is unused/zero in versions prior to 0.66 */
eh->inLast = 2048;
expected_image_size = 2048*(image->expectedSectors-1)+eh->inLast;
/* Special case: If the iso file is a few bytes too short
or too long, and the last bytes are zeroes, the
codec won't discover the mismatch from the CRC sum.
Fill up the missing bytes with zeroes here; this
will either be correct or picked up by the CRC
compare later. */
if(image->sectorSize == image->expectedSectors
&& image->inLast < eh->inLast)
{ int padding = eh->inLast - image->inLast;
unsigned char buf[padding];
int n;
memset(buf, 0, padding);
LargeSeek(image->file, image->file->size);
n = LargeWrite(image->file, buf, padding);
image->file->size += n;
image->inLast += n;
if(n != padding)
Stop(_("Failed writing to sector %lld in image [%s]: %s"),
image->sectorSize, "SC", strerror(errno));
}
if(image->file->size > expected_image_size)
{ gint64 diff = image->sectorSize - image->expectedSectors;
char *trans = _("The image file is %lld sectors longer as noted in the\n"
"ecc file. This might simply be zero padding, especially\n"
"on dual layer DVD media, but could also mean that\n"
"the image and ecc files do not belong together.\n\n%s");
if(diff>0 && diff<=2)
{
int answer = ModalWarningOrCLI(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, NULL,
_("Image file is %lld sectors longer than expected.\n"
"Assuming this is a TAO mode medium.\n"
"%lld sectors will be removed from the image end.\n"),
diff, diff);
if(!answer)
{
#ifndef CLI
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
#endif
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
image->sectorSize -= diff;
image->inLast = eh->inLast;
if(!LargeTruncate(image->file, expected_image_size))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno));
}
#ifndef CLI
if(diff>2 && Closure->guiMode)
{ int answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, NULL,
trans,
diff,
_("Is it okay to remove the superfluous sectors?"));
if(!answer)
{ SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
image->sectorSize -= diff;
image->inLast = eh->inLast;
if(!LargeTruncate(image->file, expected_image_size))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno));
PrintLog(_("Image has been truncated by %lld sectors.\n"), diff);
}
#endif
if(diff>2 &&
#ifndef CLI
!Closure->guiMode)
#else
1)
#endif
{ if(!Closure->truncate)
Stop(trans,
diff,
_("Add the --truncate option to the program call\n"
"to have the superfluous sectors removed."));
image->sectorSize -= diff;
image->inLast = eh->inLast;
if(!LargeTruncate(image->file, expected_image_size))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno));
PrintLog(_("Image has been truncated by %lld sectors.\n"), diff);
}
}
if(image->sectorSize == image->expectedSectors && image->inLast > eh->inLast)
{ int difference = image->inLast - eh->inLast;
#ifndef CLI
if(Closure->guiMode)
{ int answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, NULL,
_("The image file is %d bytes longer than noted\n"
"in the ecc file. Shall the superfluous bytes\n"
"be removed from the image file?\n"),
difference);
if(!answer)
{ SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
}
#endif
#ifndef CLI
if(!Closure->guiMode && !Closure->truncate)
#else
if(!Closure->truncate)
#endif
Stop(_("The image file is %d bytes longer than noted\n"
"in the ecc file.\n"
"Add the --truncate option to the program call\n"
"to have the superfluous sectors removed."),
difference);
if(!LargeTruncate(image->file, expected_image_size))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno));
PrintLog(_("Image has been truncated by %d bytes.\n"), difference);
image->inLast = eh->inLast;
}
if(image->sectorSize < image->expectedSectors)
{ int answer;
answer = ModalWarningOrCLI(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, NULL,
_("Image file appears to be truncated.\n"
"Consider completing it with another reading pass before going on.\n"), NULL);
if(!answer)
{
#ifndef CLI
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
#endif
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
}
if(image->fpState != FP_PRESENT)
{ int answer;
answer = ModalWarningOrCLI(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, NULL,
_("Sector %d is missing. Can not compare image and ecc fingerprints.\n"
"Double check that image and ecc file belong together.\n"),
eh->fpSector);
if(!answer)
{
#ifndef CLI
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
#endif
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
}
else if(memcmp(image->imageFP, eh->mediumFP, 16))
Stop(_("Fingerprints of image and ecc file do not match.\n"
"Image and ecc file do not belong together.\n"));
/*** Set up the Galois field arithmetic */
gt = fc->gt = CreateGaloisTables(RS_GENERATOR_POLY);
rt = fc->rt = CreateReedSolomonTables(gt, RS_FIRST_ROOT, RS_PRIM_ELEM, eh->eccBytes);
gf_index_of = gt->indexOf;
gf_alpha_to = gt->alphaTo;
nroots = rt->nroots;
ndata = rt->ndata;
/*** Prepare buffers for ecc code processing.
Our ecc blocks are built from ndata medium sectors spread over the full medium size.
We read cache_size * ndata medium sectors ahead. */
cache_size = 2*Closure->cacheMiB; /* ndata medium sectors are approx. 0.5MiB */
for(i=0; i<ndata; i++)
{ fc->imgBlock[i] = g_malloc(cache_size*2048);
fc->crcBuf[i] = g_malloc(sizeof(int) * cache_size);
}
/*** Setup the block counters for mapping medium sectors to
ecc blocks */
s = (image->expectedSectors+ndata-1)/ndata;
for(si=0, i=0; i<ndata; si+=s, i++)
block_idx[i] = si;
cache_sector = cache_size; /* forces instant reload of cache */
/*** Verify ecc information for the medium image. */
corrected = uncorrected = 0;
worst_ecc = damaged_ecc = damaged_sec = local_plot_max = 0;
for(si=0; si<s; si++)
{
#ifndef CLI
if(Closure->stopActions) /* User hit the Stop button */
{ if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
#endif
/* Read the next batch of (cache_size * ndata) medium sectors
if the cache ran empty. */
if(cache_sector >= cache_size)
{
if(s-si < cache_size)
cache_size = s-si;
for(i=0; i<ndata; i++)
{ int offset = 0;
for(j=0; j<cache_size; j++)
{ RS01ReadSector(image, fc->imgBlock[i]+offset, block_idx[i]+j);
offset += 2048;
}
read_crc(image->eccFile, fc->crcBuf[i], block_idx[i], cache_size);
}
cache_sector = cache_offset = 0;
}
/* Determine erasures based on the "dead sector" marker */
erasure_count = 0;
for(i=0; i<ndata; i++)
{ guint32 crc = Crc32(fc->imgBlock[i]+cache_offset, 2048);
erasure_map[i] = 0;
if(block_idx[i] < image->expectedSectors) /* ignore the padding sectors! */
{ int err=CheckForMissingSector(fc->imgBlock[i]+cache_offset, block_idx[i], NULL, 0);
if(err != SECTOR_PRESENT)
{ erasure_map[i] = 1;
erasure_list[erasure_count++] = i;
}
else if(crc != fc->crcBuf[i][cache_sector])
{ erasure_map[i] = 3;
erasure_list[erasure_count++] = i;
PrintCLI(_("CRC error in sector %lld\n"),block_idx[i]);
}
}
}
if(!erasure_count) /* Skip completely read blocks */
{ parity_block+=2048;
goto skip;
}
else
{ damaged_ecc++;
damaged_sec+=erasure_count;
}
if(erasure_count>worst_ecc)
worst_ecc = erasure_count;
if(erasure_count>local_plot_max)
local_plot_max = erasure_count;
/* Turn the ndata medium sectors into 2048 ecc blocks
and try to correct them. */
if(erasure_count>nroots) /* uncorrectable */
{
#ifndef CLI
if(!Closure->guiMode)
#endif
{ PrintCLI(_("* %3d unrepairable sectors: "), erasure_count);
for(i=0; i<erasure_count; i++)
PrintCLI("%lld ", block_idx[erasure_list[i]]);
PrintCLI("\n");
}
uncorrected += erasure_count;
parity_block+=2048;
/* For truncated images, make sure we leave no "zero holes" in the image
by writing the sector(s) with our "dead sector" markers. */
for(i=0; i<erasure_count; i++)
{ gint64 idx = block_idx[erasure_list[i]];
unsigned char buf[2048];
if(idx < image->sectorSize)
continue; /* It's (already) dead, Jim ;-) */
if(!LargeSeek(image->file, (gint64)(2048*idx)))
Stop(_("Failed seeking to sector %lld in image [%s]: %s"),
idx, "FD", strerror(errno));
CreateMissingSector(buf, idx, eh->mediumFP, eh->fpSector, NULL);
n = LargeWrite(image->file, buf, 2048);
if(n != 2048)
Stop(_("Failed writing to sector %lld in image [%s]: %s"),
idx, "WD", strerror(errno));
}
}
else /* try to correct them */
{ int bi;
for(bi=0; bi<2048; bi++)
{ int offset = cache_offset+bi;
int r, deg_lambda, el, deg_omega;
int u,q,tmp,num1,num2,den,discr_r;
int lambda[nroots+1], s[nroots]; /* Err+Eras Locator poly * and syndrome poly */
int b[nroots+1], t[nroots+1], omega[nroots+1];
int root[nroots], reg[nroots+1], loc[nroots];
int syn_error, count;
/* Read the parity bytes */
if(!LargeSeek(image->eccFile, (gint64)(sizeof(EccHeader) + image->expectedSectors*sizeof(guint32) + nroots*parity_block)))
Stop(_("Failed seeking in ecc area: %s"), strerror(errno));
n = LargeRead(image->eccFile, parity, nroots);
if(n != nroots)
Stop(_("Can't read ecc file:\n%s"),strerror(errno));
parity_block++;
/* Form the syndromes; i.e., evaluate data(x) at roots of g(x) */
for(i=0; i<nroots; i++)
s[i] = fc->imgBlock[0][offset];
for(j=1; j<GF_FIELDMAX; j++)
{ int data = j>=ndata ? parity[j-ndata] : fc->imgBlock[j][offset];
for(i=0;i<nroots;i++)
{ if(s[i] == 0) s[i] = data;
else s[i] = data ^ gf_alpha_to[mod_fieldmax(gf_index_of[s[i]] + (RS_FIRST_ROOT+i)*RS_PRIM_ELEM)];
}
}
/* Convert syndromes to index form, check for nonzero condition */
syn_error = 0;
for(i=0; i<nroots; i++)
{ syn_error |= s[i];
s[i] = gf_index_of[s[i]];
}
/* If it is already correct by coincidence,
we have nothing to do any further */
if(!syn_error) continue;
/* NOTE: Since we already know all our erasure positions,
we could do away simpler than by using the Berlekamp and Chien
algorithms.I've left them in this release to have a reference
implementation Phil's library code which can be compared
against later optimized versions. */
/* Init lambda to be the erasure locator polynomial */
memset(lambda+1, 0, nroots*sizeof(lambda[0]));
lambda[0] = 1;
lambda[1] = gf_alpha_to[mod_fieldmax(RS_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
for(i=1; i<erasure_count; i++)
{ u = mod_fieldmax(RS_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[i]));
for(j=i+1; j>0; j--)
{ tmp = gf_index_of[lambda[j-1]];
if(tmp != GF_ALPHA0)
lambda[j] ^= gf_alpha_to[mod_fieldmax(u + tmp)];
}
}
for(i=0; i<nroots+1; i++)
b[i] = gf_index_of[lambda[i]];
/* Begin Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
r = erasure_count; /* r is the step number */
el = erasure_count;
while(++r <= nroots) /* Compute discrepancy at the r-th step in poly-form */
{
discr_r = 0;
for(i=0; i<r; i++)
if((lambda[i] != 0) && (s[r-i-1] != GF_ALPHA0))
discr_r ^= gf_alpha_to[mod_fieldmax(gf_index_of[lambda[i]] + s[r-i-1])];
discr_r = gf_index_of[discr_r]; /* Index form */
if(discr_r == GF_ALPHA0)
{
/* B(x) = x*B(x) */
memmove(b+1, b, nroots*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
else
{ /* T(x) = lambda(x) - discr_r*x*b(x) */
t[0] = lambda[0];
for(i=0; i<nroots; i++)
{ if(b[i] != GF_ALPHA0)
t[i+1] = lambda[i+1] ^ gf_alpha_to[mod_fieldmax(discr_r + b[i])];
else t[i+1] = lambda[i+1];
}
if(2*el <= r+erasure_count-1)
{ el = r + erasure_count - el;
/* B(x) <-- inv(discr_r) * lambda(x) */
for(i=0; i<=nroots; i++)
b[i] = (lambda[i] == 0) ? GF_ALPHA0 : mod_fieldmax(gf_index_of[lambda[i]] - discr_r + GF_FIELDMAX);
}
else
{ /* 2 lines below: B(x) <-- x*B(x) */
memmove(b+1, b, nroots*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
memcpy(lambda,t,(nroots+1)*sizeof(t[0]));
}
}
/* Convert lambda to index form and compute deg(lambda(x)) */
deg_lambda = 0;
for(i=0; i<nroots+1; i++)
{ lambda[i] = gf_index_of[lambda[i]];
if(lambda[i] != GF_ALPHA0)
deg_lambda = i;
}
/* Find roots of the error+erasure locator polynomial by Chien search */
memcpy(reg+1, lambda+1, nroots*sizeof(reg[0]));
count = 0; /* Number of roots of lambda(x) */
for(i=1, k=RS_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+RS_PRIMTH_ROOT))
{ q=1; /* lambda[0] is always 0 */
for(j=deg_lambda; j>0; j--)
{ if(reg[j] != GF_ALPHA0)
{ reg[j] = mod_fieldmax(reg[j] + j);
q ^= gf_alpha_to[reg[j]];
}
}
if(q != 0) continue; /* Not a root */
/* store root (index-form) and error location number */
root[count] = i;
loc[count] = k;
/* If we've already found max possible roots, abort the search to save time */
if(++count == deg_lambda) break;
//if(++count >= deg_lambda) break;
}
/* deg(lambda) unequal to number of roots => uncorrectable error detected */
if(deg_lambda != count)
{ PrintLog("Decoder problem (%d != %d) for %d sectors: ", deg_lambda, count, erasure_count);
for(i=0; i<erasure_count; i++)
{ gint64 idx = block_idx[erasure_list[i]];
PrintLog("%lld ", idx);
}
PrintLog("\n");
break;
}
/* Compute err+eras evaluator poly omega(x) = s(x)*lambda(x)
(modulo x**nroots). in index form. Also find deg(omega). */
deg_omega = deg_lambda-1;
for(i=0; i<=deg_omega; i++)
{ tmp = 0;
for(j=i; j>=0; j--)
{ if((s[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0))
tmp ^= gf_alpha_to[mod_fieldmax(s[i - j] + lambda[j])];
}
omega[i] = gf_index_of[tmp];
}
/* Compute error values in poly-form.
num1 = omega(inv(X(l))),
num2 = inv(X(l))**(FIRST_ROOT-1) and
den = lambda_pr(inv(X(l))) all in poly-form. */
for(j=count-1; j>=0; j--)
{ num1 = 0;
for(i=deg_omega; i>=0; i--)
{ if(omega[i] != GF_ALPHA0)
num1 ^= gf_alpha_to[mod_fieldmax(omega[i] + i * root[j])];
}
num2 = gf_alpha_to[mod_fieldmax(root[j] * (RS_FIRST_ROOT - 1) + GF_FIELDMAX)];
den = 0;
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
for(i=MIN(deg_lambda, nroots-1) & ~1; i>=0; i-=2)
{ if(lambda[i+1] != GF_ALPHA0)
den ^= gf_alpha_to[mod_fieldmax(lambda[i+1] + i * root[j])];
}
/* Apply error to data */
if(num1 != 0)
{ int location = loc[j];
if(location >= 0 && location < ndata)
{ if(erasure_map[location] == 3)
{ int old = fc->imgBlock[location][offset];
int new = old ^ gf_alpha_to[mod_fieldmax(gf_index_of[num1] + gf_index_of[num2] + GF_FIELDMAX - gf_index_of[den])];
PrintCLI(_("-> Error located in sector %lld at byte %4d (value %02x '%c', expected %02x '%c')\n"),
block_idx[location], bi,
old, canprint(old) ? old : '.',
new, canprint(new) ? new : '.');
}
if(!erasure_map[location])
PrintLog(_("Unexpected byte error in sector %lld, byte %d\n"),
block_idx[location], bi);
fc->imgBlock[location][offset] ^= gf_alpha_to[mod_fieldmax(gf_index_of[num1] + gf_index_of[num2] + GF_FIELDMAX - gf_index_of[den])];
}
else
PrintLog(_("Bad error location %d; corrupted .ecc file?\n"), location);
}
}
}
}
/*** Report if any sectors could be recovered.
Write the recovered sectors to the image file .*/
if(erasure_count && erasure_count<=nroots)
{ PrintCLI(_(" %3d repaired sectors: "), erasure_count);
for(i=0; i<erasure_count; i++)
{ gint64 idx = block_idx[erasure_list[i]];
int length;
PrintCLI("%lld ", idx);
/* Write the recovered sector */
if(!LargeSeek(image->file, (gint64)(2048*idx)))
Stop(_("Failed seeking to sector %lld in image [%s]: %s"),
idx, "FW", strerror(errno));
if(idx < image->expectedSectors-1) length = 2048;
else length = eh->inLast;
n = LargeWrite(image->file, cache_offset+fc->imgBlock[erasure_list[i]], length);
if(n != length)
Stop(_("could not write medium sector %lld:\n%s"),idx,strerror(errno));
}
PrintCLI("\n");
corrected += erasure_count;
}
skip:
/* Advance the cache pointers */
cache_sector++;
cache_offset += 2048;
/* Report progress */
percent = (1000*(si+1))/s;
if(last_percent != percent)
{
#ifndef CLI
if(Closure->guiMode)
{
RS01AddFixValues(wl, percent, local_plot_max);
local_plot_max = 0;
RS01UpdateFixResults(wl, corrected, uncorrected);
}
else
#endif
PrintProgress(_("Ecc progress: %3d.%1d%%"),percent/10,percent%10);
last_percent = percent;
}
/* Increment the block indices */
for(i=0; i<ndata; i++)
block_idx[i]++;
}
/*** Print results */
PrintProgress(_("Ecc progress: 100.0%%\n"));
if(corrected > 0) PrintLog(_("Repaired sectors: %lld \n"),corrected);
if(uncorrected > 0)
{ PrintLog(_("Unrepaired sectors: %lld\n"), uncorrected);
#ifndef CLI
if(Closure->guiMode)
SwitchAndSetFootline(wl->fixNotebook, 1, wl->fixFootline,
_("Image sectors could not be fully restored "
"(%lld repaired; <span %s>%lld unrepaired</span>)"),
corrected, Closure->redMarkup, uncorrected);
#endif
}
else
{ if(!corrected)
{ t=_("Good! All sectors are already present.");
PrintLog("%s\n", t);
}
else
{ t=_("Good! All sectors are repaired.");
PrintLog("%s\n", t);
}
}
if(corrected > 0 || uncorrected > 0)
PrintLog(_("Erasure counts per ecc block: avg = %.1f; worst = %d.\n"),
(double)damaged_sec/(double)damaged_ecc,worst_ecc);
#ifndef CLI
if(Closure->guiMode && t)
SwitchAndSetFootline(wl->fixNotebook, 1, wl->fixFootline,
"%s %s", _("Repair results:"), t);
#endif
/*** Clean up */
fc->earlyTermination = FALSE;
terminate:
fix_cleanup((gpointer)fc);
}

177
src/rs01-includes.h Normal file
View File

@@ -0,0 +1,177 @@
/* 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/>.
*/
#ifndef RS01INCLUDES_H
#define RS01INCLUDES_H
#ifndef CLI
/* Data structs from rs01-window.c */
typedef struct
{
/*** Widgets for RS01 encoding */
GtkWidget *encHeadline;
GtkWidget *encLabel1;
GtkWidget *encPBar1;
GtkWidget *encLabel2;
GtkWidget *encPBar2;
GtkWidget *encFootline;
GtkWidget *encFootline2;
GtkWidget *curveButton;
/*** Widgets for RS01 fixing */
GtkWidget *fixHeadline;
GtkWidget *fixDrawingArea;
GtkWidget *fixNotebook;
GtkWidget *fixFootline;
GtkWidget *fixFootlineBox;
GtkWidget *fixCorrected;
GtkWidget *fixProgress;
GtkWidget *fixUncorrected;
Curve *fixCurve;
/*** Widgets for RS01 verify action */
GtkWidget *cmpHeadline;
GtkWidget *cmpImageNotebook;
GtkWidget *cmpImageSectors;
GtkWidget *cmpChkSumErrors;
GtkWidget *cmpMissingSectors;
GtkWidget *cmpImageMd5Sum;
GtkWidget *cmpImageResult;
GtkWidget *cmpEccNotebook;
GtkWidget *cmpEccEmptyMsg;
GtkWidget *cmpEccCreatedBy;
GtkWidget *cmpEccMethod;
GtkWidget *cmpEccRequires;
GtkWidget *cmpEccMediumSectors;
GtkWidget *cmpEccImgMd5Sum;
GtkWidget *cmpEccFingerprint;
GtkWidget *cmpEccBlocks;
GtkWidget *cmpEccMd5Sum;
GtkWidget *cmpEccResult;
GtkWidget *cmpDrawingArea;
Spiral *cmpSpiral;
PangoLayout *cmpLayout;
/*** Widgets in the Preferences window */
GtkWidget *radio1A,*radio2A,*radio3A,*radio4A;
GtkWidget *radio1B,*radio2B,*radio3B,*radio4B;
GtkWidget *radio4LabelA, *radio4LabelB;
GtkWidget *redundancyScaleA, *redundancyScaleB;
GtkWidget *redundancySpinA, *redundancySpinB;
GtkWidget *cacheScaleA, *cacheScaleB;
LabelWithOnlineHelp *cacheLwoh;
/*** Some state vars used during fixing */
gint64 corrected;
gint64 uncorrected;
gint64 nSectors;
int eccBytes;
int dataBytes;
int percent, lastPercent;
} RS01Widgets;
#endif
/*
* local working closure for internal checksums
*/
typedef struct
{ struct MD5Context md5ctxt; /* Complete image checksum */
} RS01CksumClosure;
#ifndef CLI
/*
* These are exported via the Method struct
*/
void CreateRS01EWindow(Method*, GtkWidget*);
void CreateRS01FWindow(Method*, GtkWidget*);
void CreateRS01PrefsPage(Method*, GtkWidget*);
void ResetRS01EncodeWindow(Method*);
void ResetRS01FixWindow(Method*);
void ResetRS01PrefsPage(Method*);
void RS01ShowCurveButton(Method*);
void ResetRS01VerifyWindow(Method*);
void CreateRS01VerifyWindow(Method*, GtkWidget*);
#endif
/*
* These are exported (resp. only used) in ecc-rs01.c and rs01*.c
* and should not be called from somewhere else as we can not
* rely on the method plug-in being available.
* If you need similar functions in your own codec,
* please copy these functions over to the respective plug-in.
*/
/* rs01-common.c */
#define READABLE_IMAGE 0
#define READABLE_ECC 0
#define WRITEABLE_IMAGE (1<<0)
#define WRITEABLE_ECC (1<<1)
#define PRINT_MODE (1<<4)
#define CREATE_CRC ((1<<1) | (1<<5))
CrcBuf *RS01GetCrcBuf(Image*);
void RS01ResetCksums(Image*);
void RS01UpdateCksums(Image*, gint64, unsigned char*);
int RS01FinalizeCksums(Image*);
void RS01ReadSector(Image*, unsigned char*, gint64);
void RS01ScanImage(Method*, Image*, struct MD5Context*, int);
int RS01Recognize(LargeFile*, EccHeader**);
guint64 RS01ExpectedImageSize(Image*);
/* rs01-create.c */
void RS01Create(void);
/* rs01-fix.c */
void RS01Fix(Image*);
#ifndef CLI
/* rs01-window.c */
void RS01AddFixValues(RS01Widgets*, int, int);
void RS01SetFixMaxValues(RS01Widgets*, int, int, gint64);
void RS01UpdateFixResults(RS01Widgets*, gint64, gint64);
#endif
/* rs01-verify.c */
#define VERIFY_IMAGE_SEGMENTS 1000
void RS01Verify(Image*);
void RS01AddVerifyValues(Method*, int, gint64, gint64, gint64, gint64);
#endif

962
src/rs01-verify.c Normal file
View File

@@ -0,0 +1,962 @@
/* 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"
#ifndef CLI
/***
*** Reset the verify output window
***/
void ResetRS01VerifyWindow(Method *self)
{ RS01Widgets *wl = (RS01Widgets*)self->widgetList;
SetLabelText(GTK_LABEL(wl->cmpChkSumErrors), "-");
SetLabelText(GTK_LABEL(wl->cmpMissingSectors), "0");
SetLabelText(GTK_LABEL(wl->cmpImageMd5Sum), "-");
SetLabelText(GTK_LABEL(wl->cmpImageResult), "");
SwitchAndSetFootline(wl->cmpImageNotebook, 1, NULL, NULL);
SetLabelText(GTK_LABEL(wl->cmpEccEmptyMsg), "");
SetLabelText(GTK_LABEL(wl->cmpEccCreatedBy), "dvdisaster");
SetLabelText(GTK_LABEL(wl->cmpEccMethod), "");
SetLabelText(GTK_LABEL(wl->cmpEccRequires), "");
SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors), "");
SetLabelText(GTK_LABEL(wl->cmpEccImgMd5Sum), "");
SetLabelText(GTK_LABEL(wl->cmpEccFingerprint), _("n/a"));
SetLabelText(GTK_LABEL(wl->cmpEccBlocks), "");
SetLabelText(GTK_LABEL(wl->cmpEccMd5Sum), "");
SetLabelText(GTK_LABEL(wl->cmpEccResult), "");
SwitchAndSetFootline(wl->cmpEccNotebook, 0, NULL, NULL);
wl->lastPercent = 0;
FillSpiral(wl->cmpSpiral, Closure->background);
DrawSpiral(wl->cmpSpiral);
}
/***
*** Manage the image spiral
***/
/*
* Update part of the spiral
*/
typedef struct _spiral_idle_info
{ Spiral *cmpSpiral;
GdkColor *segColor;
int from, to;
} spiral_idle_info;
static gboolean spiral_idle_func(gpointer data)
{ spiral_idle_info *sii = (spiral_idle_info*)data;
int i;
for(i=sii->from; i<=sii->to; i++)
DrawSpiralSegment(sii->cmpSpiral, sii->segColor, i-1);
g_free(sii);
return FALSE;
}
void RS01AddVerifyValues(Method *method, int percent,
gint64 totalMissing, gint64 totalCrcErrors,
gint64 newMissing, gint64 newCrcErrors)
{ RS01Widgets *wl = (RS01Widgets*)method->widgetList;
spiral_idle_info *sii = g_malloc(sizeof(spiral_idle_info));
if(percent < 0 || percent > VERIFY_IMAGE_SEGMENTS)
return;
if(newMissing)
SetLabelText(GTK_LABEL(wl->cmpMissingSectors), "<span %s>%lld</span>",
Closure->redMarkup, totalMissing);
if(newCrcErrors)
SetLabelText(GTK_LABEL(wl->cmpChkSumErrors), "<span %s>%lld</span>",
Closure->redMarkup, totalCrcErrors);
sii->cmpSpiral = wl->cmpSpiral;
sii->segColor = Closure->greenSector;
if(newCrcErrors) sii->segColor = Closure->yellowSector;
if(newMissing) sii->segColor = Closure->redSector;
sii->from = wl->lastPercent+1;
sii->to = percent;
wl->lastPercent = percent;
g_idle_add(spiral_idle_func, sii);
}
/*
* Redraw whole spiral
*/
static void redraw_spiral(RS01Widgets *wl)
{ int x = wl->cmpSpiral->mx - wl->cmpSpiral->diameter/2 + 10;
DrawSpiralLabel(wl->cmpSpiral, wl->cmpLayout,
_("Good sectors"), Closure->greenSector, x, 1);
DrawSpiralLabel(wl->cmpSpiral, wl->cmpLayout,
_("Sectors with CRC errors"), Closure->yellowSector, x, 2);
DrawSpiralLabel(wl->cmpSpiral, wl->cmpLayout,
_("Missing sectors"), Closure->redSector, x, 3);
DrawSpiral(wl->cmpSpiral);
}
/*
* expose event handler for the spiral
*/
static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
GtkAllocation *a = &widget->allocation;
int w,h,size;
/* Finish spiral initialization */
if(!wl->cmpLayout)
{ SetSpiralWidget(wl->cmpSpiral, widget);
wl->cmpLayout = gtk_widget_create_pango_layout(widget, NULL);
}
SetText(wl->cmpLayout, _("Missing sectors"), &w, &h);
size = wl->cmpSpiral->diameter + 20 + 3*(10+h); /* approx. size of spiral + labels */
wl->cmpSpiral->mx = a->width / 2;
wl->cmpSpiral->my = (wl->cmpSpiral->diameter + a->height - size)/2;
if(event->count) /* Exposure compression */
{ return TRUE;
}
/* Redraw the spiral */
redraw_spiral(wl);
return TRUE;
}
/***
*** Create the notebook contents for the verify output
***/
void CreateRS01VerifyWindow(Method *self, GtkWidget *parent)
{ RS01Widgets *wl = (RS01Widgets*)self->widgetList;
GtkWidget *sep,*notebook,*table,*table2,*ignore,*lab,*frame,*d_area;
wl->cmpHeadline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->cmpHeadline), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(wl->cmpHeadline), 5, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->cmpHeadline, FALSE, FALSE, 3);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
table = gtk_table_new(2, 2, FALSE);
gtk_container_set_border_width(GTK_CONTAINER(table), 5);
gtk_box_pack_start(GTK_BOX(parent), table, TRUE, TRUE, 0);
/*** Image info */
frame = gtk_frame_new(_utf("Image file summary"));
gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 5);
notebook = wl->cmpImageNotebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
gtk_container_add(GTK_CONTAINER(frame), notebook);
ignore = gtk_label_new("no image");
lab = gtk_label_new(_utf("No image present."));
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), lab, ignore);
table2 = gtk_table_new(2, 5, FALSE);
ignore = gtk_label_new("image info");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table2, ignore);
gtk_container_set_border_width(GTK_CONTAINER(table2), 5);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Medium sectors:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpImageSectors = gtk_label_new("0");
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Checksum errors:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpChkSumErrors = gtk_label_new("0");
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Missing Sectors:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpMissingSectors = gtk_label_new("0");
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Image checksum:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 3, 4, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpImageMd5Sum = gtk_label_new("0");
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = wl->cmpImageResult = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 0, 2, 4, 5, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 4);
/*** Image spiral */
frame = gtk_frame_new(_utf("Image state"));
gtk_table_attach(GTK_TABLE(table), frame, 1, 2, 0, 2, GTK_SHRINK | GTK_FILL, GTK_EXPAND | GTK_FILL, 5, 5);
wl->cmpSpiral = CreateSpiral(Closure->grid, Closure->background, 10, 5, VERIFY_IMAGE_SEGMENTS);
d_area = wl->cmpDrawingArea = gtk_drawing_area_new();
gtk_widget_set_size_request(d_area, wl->cmpSpiral->diameter+20, -1);
gtk_container_add(GTK_CONTAINER(frame), d_area);
g_signal_connect(G_OBJECT(d_area), "expose_event", G_CALLBACK(expose_cb), (gpointer)wl);
/*** Ecc info */
frame = gtk_frame_new(_utf("Error correction file summary"));
gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 5, 5);
notebook = wl->cmpEccNotebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
gtk_container_add(GTK_CONTAINER(frame), notebook);
ignore = gtk_label_new("no ecc file");
lab = wl->cmpEccEmptyMsg = gtk_label_new("");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), lab, ignore);
table2 = gtk_table_new(2, 9, FALSE);
ignore = gtk_label_new("ecc info");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table2, ignore);
gtk_container_set_border_width(GTK_CONTAINER(table2), 5);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Created by:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccCreatedBy = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Method:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccMethod = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Requires:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccRequires = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Medium sectors:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 3, 4, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccMediumSectors = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Image checksum:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 4, 5, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccImgMd5Sum = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 4, 5, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Fingerprint:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 5, 6, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccFingerprint = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 5, 6, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Ecc blocks:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 6, 7, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccBlocks = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 6, 7, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
SetLabelText(GTK_LABEL(lab), _("Ecc checksum:"));
gtk_table_attach(GTK_TABLE(table2), lab, 0, 1, 7, 8, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 2 );
lab = wl->cmpEccMd5Sum = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 1, 2, 7, 8, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
lab = wl->cmpEccResult = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(lab), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table2), lab, 0, 2, 8, 9, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 5, 4);
}
#endif
/***
*** Verify the prefix.* files
***/
typedef struct
{ Image *image;
} verify_closure;
static void cleanup(gpointer data)
{ verify_closure *vc = (verify_closure*)data;
UnregisterCleanup();
#ifndef CLI
if(Closure->guiMode)
AllowActions(TRUE);
#endif
if(vc->image) CloseImage(vc->image);
g_free(vc);
#ifndef CLI
if(Closure->guiMode)
g_thread_exit(0);
#endif
}
void RS01Verify(Image *image)
{ verify_closure *vc = g_malloc0(sizeof(verify_closure));
Method *self = FindMethod("RS01");
#ifndef CLI
RS01Widgets *wl = (RS01Widgets*)self->widgetList;
#endif
char idigest[33],edigest[33];
gint64 excess_sectors = 0;
#ifndef CLI
char *ecc_advice = NULL;
#endif
EccHeader *eh;
gint8 method[5];
int ecc_in_last = 0;
gint64 ecc_blocks,ecc_expected,count;
struct MD5Context md5ctxt;
int percent,last_percent;
unsigned char digest[16];
unsigned char buf[1024];
idigest[0] = 0;
/*** Prepare for early termination */
RegisterCleanup(_("Comparison aborted"), cleanup, vc);
/*** Examine the .iso file */
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpHeadline), "<big>%s</big>\n<i>%s</i>",
_("Comparing image and error correction files."),
_("- Checking image file -"));
#endif
vc->image = image;
#ifndef CLI
if(image && image->eccFile)
{ if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpChkSumErrors), "0");
}
else
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpChkSumErrors), _("n/a"));
#endif
/* Report basic image properties */
PrintLog("\n%s: ", Closure->imageName);
if(!image || !image->file)
{ PrintLog(_("not present\n"));
#ifndef CLI
if(Closure->guiMode)
SwitchAndSetFootline(wl->cmpImageNotebook, 0, NULL, NULL);
#endif
goto process_ecc;
}
if(image->inLast == 2048)
{ PrintLog(_("present, contains %lld medium sectors.\n"), image->sectorSize);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpImageSectors), "%lld", image->sectorSize);
#endif
}
else
{ PrintLog(_("present, contains %lld medium sectors and %d bytes.\n"),
image->sectorSize-1, image->inLast);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpImageSectors), _("%lld sectors + %d bytes"),
image->sectorSize-1, image->inLast);
#endif
}
if(!Closure->quickVerify)
RS01ScanImage(self, image, NULL, PRINT_MODE);
#ifndef CLI
if(Closure->stopActions)
{ if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
SetLabelText(GTK_LABEL(wl->cmpImageResult),
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
goto terminate;
}
#endif
/*** Peek into the ecc file to get expected sector count */
if(image->eccFile)
{ guint64 diff = 0;
if(image->sectorSize < image->expectedSectors)
{ diff = image->expectedSectors - image->sectorSize;
PrintLog(_("* truncated image : %lld sectors too short\n"), diff);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpImageSectors),
_("<span %s>%lld (%lld sectors too short)</span>"),
Closure->redMarkup, image->sectorSize, diff);
#endif
image->sectorsMissing += diff;
}
if(image->sectorSize > image->expectedSectors)
{ excess_sectors = image->sectorSize - image->expectedSectors;
}
}
/*** Show summary of image read */
#ifndef CLI
if(Closure->guiMode)
{ if(image->crcErrors)
SetLabelText(GTK_LABEL(wl->cmpChkSumErrors),
"<span %s>%lld</span>", Closure->redMarkup, image->crcErrors);
if(image->sectorsMissing)
SetLabelText(GTK_LABEL(wl->cmpMissingSectors),
"<span %s>%lld</span>", Closure->redMarkup, image->sectorsMissing);
}
#endif
if(excess_sectors)
{ PrintLog(_("* image too long : %lld excess sectors\n"), excess_sectors);
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpImageSectors),
_("<span %s>%lld (%lld excess sectors)</span>"),
Closure->redMarkup, image->sectorSize, excess_sectors);
SetLabelText(GTK_LABEL(wl->cmpImageResult),
_("<span %s>Bad image.</span>"),
Closure->redMarkup);
}
#endif
}
else if(Closure->quickVerify)
{ PrintLog(_("* quick mode : image NOT scanned\n"));
}
else
{ if(!image->sectorsMissing)
{
AsciiDigest(idigest, image->mediumSum);
if(!image->crcErrors)
{ PrintLog(_("- good image : all sectors present\n"
"- image md5sum : %s\n"),idigest);
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpImageResult),_("<span %s>Good image.</span>"), Closure->greenMarkup);
SetLabelText(GTK_LABEL(wl->cmpImageMd5Sum), "%s", idigest);
}
#endif
}
else
{ PrintLog(_("* suspicious image : all sectors present, but %lld CRC errors\n"
"- image md5sum : %s\n"),image->crcErrors,idigest);
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpImageResult), _("<span %s>Image complete, but contains checksum errors!</span>"), Closure->redMarkup);
SetLabelText(GTK_LABEL(wl->cmpImageMd5Sum), "%s", idigest);
}
#endif
}
}
else /* sectors are missing */
{ if(!image->crcErrors)
PrintLog(_("* BAD image : %lld sectors missing\n"), image->sectorsMissing);
else PrintLog(_("* BAD image : %lld sectors missing, %lld CRC errors\n"),
image->sectorsMissing, image->crcErrors);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpImageResult),
_("<span %s>Bad image.</span>"), Closure->redMarkup);
#endif
}
}
/*** The .ecc file */
process_ecc:
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpHeadline), "<big>%s</big>\n<i>%s</i>",
_("Comparing image and error correction files."),
_("- Checking ecc file -"));
#endif
PrintLog("\n%s: ", Closure->eccName);
if(!image)
{ PrintLog(_("not present\n"));
#ifndef CLI
if(Closure->guiMode)
SwitchAndSetFootline(wl->cmpEccNotebook, 0,
wl->cmpEccEmptyMsg,_("No error correction file present."));
#endif
goto skip_ecc;
}
if(image && !image->eccFile)
{
switch(image->eccFileState)
{ case ECCFILE_NOPERM:
PrintLog(_("permission denied\n"));
break;
case ECCFILE_MISSING:
PrintLog(_("not present\n"));
break;
case ECCFILE_INVALID:
PrintLog(_("invalid\n"));
break;
case ECCFILE_DEFECTIVE_HEADER:
PrintLog(_("defective header (unusable)\n"));
break;
case ECCFILE_WRONG_CODEC:
PrintLog(_("unknown codec (unusable)\n"));
break;
default:
PrintLog(_("unusable\n"));
break;
}
#ifndef CLI
if(Closure->guiMode)
SwitchAndSetFootline(wl->cmpEccNotebook, 0,
wl->cmpEccEmptyMsg,_("No error correction file present."));
#endif
goto skip_ecc;
}
eh = image->eccFileHeader; /* simply an alias */
/* Version number of dvdisaster used for creating the ecc file */
if(!eh->neededVersion) /* The V0.41.* series did not fill in this field. */
eh->neededVersion = 4100;
if(eh->creatorVersion)
{ int major = eh->creatorVersion/10000;
int minor = (eh->creatorVersion%10000)/100;
int micro = eh->creatorVersion%100;
char *unstable="";
/* Suppress (unstable) output in debug mode to facilitate regression tests */
if((eh->methodFlags[3] & MFLAG_DEVEL) && !Closure->regtestMode)
unstable=" (unstable)";
if(micro) /* version format x.xx.x */
{ char *format = "%s-%d.%d.%d%s";
PrintLog(format, _("created by dvdisaster"), major, minor, micro, unstable);
PrintLog("\n");
#ifndef CLI
if(Closure->guiMode)
{ SwitchAndSetFootline(wl->cmpEccNotebook, 1,
wl->cmpEccCreatedBy,
format, "dvdisaster",
major, minor, micro, unstable);
}
#endif
}
else /* version format x.xx */
{ char *format = "%s-%d.%d%s";
PrintLog(format, _("created by dvdisaster"),
major, minor, unstable);
PrintLog("\n");
#ifndef CLI
if(Closure->guiMode)
SwitchAndSetFootline(wl->cmpEccNotebook, 1,
wl->cmpEccCreatedBy, format, "dvdisaster",
major, minor, unstable);
#endif
}
}
else
{ PrintLog(_("created by dvdisaster-0.41.x.\n"));
#ifndef CLI
if(Closure->guiMode)
SwitchAndSetFootline(wl->cmpEccNotebook, 1,
wl->cmpEccCreatedBy, "dvdisaster-0.41.x");
#endif
}
/* Information on RS01 properties */
memcpy(method, eh->method, 4); method[4] = 0;
PrintLog(_("- method : %4s, %d roots, %4.1f%% redundancy.\n"),
method, eh->eccBytes,
((double)eh->eccBytes*100.0)/(double)eh->dataBytes);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccMethod), _("%4s, %d roots, %4.1f%% redundancy"),
method, eh->eccBytes,
((double)eh->eccBytes*100.0)/(double)eh->dataBytes);
#endif
/* Show and verify needed version */
if(Closure->version >= eh->neededVersion)
{ PrintLog(_("- requires : dvdisaster-%d.%d (good)\n"),
eh->neededVersion/10000,
(eh->neededVersion%10000)/100);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccRequires), "dvdisaster-%d.%d",
eh->neededVersion/10000,
(eh->neededVersion%10000)/100);
#endif
}
else
{ PrintLog(_("* requires : dvdisaster-%d.%d (BAD)\n"
"* Warning : The following output might be incorrect.\n"
"* : Please visit http://www.dvdisaster.org for an upgrade.\n"),
eh->neededVersion/10000,
(eh->neededVersion%10000)/100);
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpEccRequires),
"<span %s>dvdisaster-%d.%d</span>",
Closure->redMarkup,
eh->neededVersion/10000,
(eh->neededVersion%10000)/100);
if(!ecc_advice)
ecc_advice = g_strdup_printf(_("<span %s>Please upgrade your version of dvdisaster!</span>"), Closure->redMarkup);
}
#endif
}
/* image size comparison */
if(eh->creatorVersion >= 6600 && eh->inLast != 2048) /* image file whose length is */
ecc_in_last = eh->inLast; /* not a multiple of 2048 */
if(!image->file)
{ if(!ecc_in_last)
{ PrintLog(_("- medium sectors : %lld\n"), image->expectedSectors);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors), "%lld", image->expectedSectors);
#endif
}
else
{ PrintLog(_("- medium sectors : %lld sectors + %d bytes\n"),
image->expectedSectors-1, ecc_in_last);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors),
_("%lld sectors + %d bytes"),
image->expectedSectors-1, ecc_in_last);
#endif
}
}
if(image->file)
{
if(image->sectorSize == image->expectedSectors
&& (!ecc_in_last || image->inLast == eh->inLast))
{ if(!ecc_in_last)
{ PrintLog(_("- medium sectors : %lld (good)\n"), image->expectedSectors);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors), "%lld", image->expectedSectors);
#endif
}
else
{ PrintLog(_("- medium sectors : %lld sectors + %d bytes (good)\n"),
image->expectedSectors-1, ecc_in_last);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors),
_("%lld sectors + %d bytes"),
image->expectedSectors-1, ecc_in_last);
#endif
}
}
else /* sector sizes differ */
{ /* TAO case (1 or 2 sectors more than expected) */
if(image->sectorSize > image->expectedSectors && image->sectorSize - image->expectedSectors <= 2)
{ PrintLog(_("* medium sectors : %lld (BAD, perhaps TAO/DAO mismatch)\n"), image->expectedSectors);
#ifndef CLI
if(Closure->guiMode)
{ if(!ecc_in_last)
SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors), "<span %s>%lld</span>",
Closure->redMarkup, image->expectedSectors);
else SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors), "<span %s>%lld sectors + %d bytes</span>",
Closure->redMarkup, image->expectedSectors-1, ecc_in_last);
}
#endif
}
else /* more than 2 Sectors difference */
{ if(!ecc_in_last)
{ PrintLog(_("* medium sectors : %lld (BAD)\n"), image->expectedSectors);
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors), "<span %s>%lld</span>",
Closure->redMarkup, image->expectedSectors);
if(!ecc_advice)
ecc_advice = g_strdup_printf(_("<span %s>Image size does not match error correction file.</span>"), Closure->redMarkup);
}
#endif
}
else /* byte size difference */
{ PrintLog(_("* medium sectors : %lld sectors + %d bytes (BAD)\n"),
image->expectedSectors-1, ecc_in_last);
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpEccMediumSectors),
_("<span %s>%lld sectors + %d bytes</span>"),
Closure->redMarkup, image->expectedSectors-1, ecc_in_last);
if(!ecc_advice)
ecc_advice = g_strdup_printf(_("<span %s>Image size does not match error correction file.</span>"), Closure->redMarkup);
}
#endif
}
}
}
}
/*** Show and verify image md5sum */
if(!Closure->quickVerify)
{ AsciiDigest(edigest, eh->mediumSum);
if(image && image->file && !image->sectorsMissing && !excess_sectors)
{ int n = !memcmp(eh->mediumSum, image->mediumSum, 16);
if(n) PrintLog(_("- image md5sum : %s (good)\n"),edigest);
else PrintLog(_("* image md5sum : %s (BAD)\n"),edigest);
#ifndef CLI
if(Closure->guiMode)
{ if(n) SetLabelText(GTK_LABEL(wl->cmpEccImgMd5Sum), "%s", edigest);
else
{ SetLabelText(GTK_LABEL(wl->cmpEccImgMd5Sum), "<span %s>%s</span>", Closure->redMarkup, edigest);
SetLabelText(GTK_LABEL(wl->cmpImageMd5Sum), "<span %s>%s</span>", Closure->redMarkup, idigest);
}
}
#endif
}
else
{ PrintLog(_("- image md5sum : %s\n"),edigest);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccImgMd5Sum), "%s", edigest);
#endif
}
}
if(image && image->file)
{ if(image->fpState != FP_PRESENT)
{ PrintLog(_("* fingerprint match: NOT POSSIBLE - related sector is missing in image!\n"));
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccFingerprint), _("<span %s>missing sector prevents calculation</span>"), Closure->redMarkup);
#endif
}
else
{
if(memcmp(image->imageFP, eh->mediumFP, 16))
{ PrintLog(_("* fingerprint match: MISMATCH - .iso and .ecc don't belong together!\n"));
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpEccFingerprint),
_("<span %s>mismatch</span>"), Closure->redMarkup);
if(!ecc_advice)
ecc_advice = g_strdup_printf(_("<span %s>Image and error correction files do not belong together!</span>"), Closure->redMarkup);
}
#endif
}
else
{ PrintLog(_("- fingerprint match: good\n"));
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccFingerprint), _("good"));
#endif
}
}
}
/* Show and verify the number of ecc blocks */
if(Closure->quickVerify) /* terminate early */
{ PrintLog(_("* quick mode : ecc file NOT scanned\n"));
goto terminate;
}
ecc_expected = 2048*((image->expectedSectors+eh->dataBytes-1)/eh->dataBytes);
ecc_blocks = (image->eccFile->size-image->expectedSectors*sizeof(guint32)-sizeof(EccHeader))/eh->eccBytes;
if(ecc_expected == ecc_blocks)
{ PrintLog(_("- ecc blocks : %lld (good)\n"),ecc_blocks);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccBlocks), "%lld", ecc_blocks);
#endif
}
else
{ PrintLog(_("* ecc blocks : %lld (BAD, expected %lld)\n"),ecc_blocks,ecc_expected);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccBlocks), _("<span %s>%lld (bad, expected %lld)</span>"),Closure->redMarkup,ecc_blocks,ecc_expected);
#endif
}
/*** Test ecc file against its own md5sum */
MD5Init(&md5ctxt);
last_percent = -1;
count = sizeof(EccHeader);
if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader)))
Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
while(!LargeEOF(image->eccFile))
{ int n = LargeRead(image->eccFile, buf, 1024);
MD5Update(&md5ctxt, buf, n);
count += n;
percent = (100*count)/image->eccFile->size;
if(last_percent != percent)
{
#ifndef CLI
if(!Closure->guiMode)
#endif
PrintProgress(_("- ecc md5sum : %3d%%"),percent);
#ifndef CLI
else SetLabelText(GTK_LABEL(wl->cmpEccMd5Sum), "%3d%%", percent);
#endif
last_percent = percent;
}
#ifndef CLI
if(Closure->stopActions)
{ if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
SetLabelText(GTK_LABEL(wl->cmpEccResult),
_("<span %s>Aborted by user request!</span>"), Closure->redMarkup);
goto terminate;
}
#endif
}
MD5Final(digest, &md5ctxt);
AsciiDigest(edigest, digest);
if(memcmp(eh->eccSum, digest, 16))
{ PrintLog(_("* ecc md5sum : BAD, ecc file may be damaged!\n"));
#ifndef CLI
if(Closure->guiMode)
{ SetLabelText(GTK_LABEL(wl->cmpEccMd5Sum), _("<span %s>bad</span>"), Closure->redMarkup);
if(!ecc_advice)
ecc_advice = g_strdup_printf(_("<span %s>Error correction file may be damaged!</span>"), Closure->redMarkup);
}
#endif
}
else
{ PrintLog(_("- ecc md5sum : %s (good)\n"),edigest);
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->cmpEccMd5Sum), "%s", edigest);
#endif
}
skip_ecc:
PrintLog("\n");
#ifndef CLI
if(Closure->guiMode)
{ if(ecc_advice)
{ SetLabelText(GTK_LABEL(wl->cmpEccResult), ecc_advice);
g_free(ecc_advice);
}
else SetLabelText(GTK_LABEL(wl->cmpEccResult),
_("<span %s>Good error correction file.</span>"),
Closure->greenMarkup);
}
#endif
/*** Close and clean up */
terminate:
cleanup(vc);
}

915
src/rs01-window.c Normal file
View File

@@ -0,0 +1,915 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#include "rs01-includes.h"
/***
*** Forward declarations
***/
static void redraw_curve(RS01Widgets*);
static void update_geometry(RS01Widgets*);
/* Protected widget access */
static void activate_toggle_button(GtkToggleButton *toggle, int state)
{ if(toggle) gtk_toggle_button_set_active(toggle, state);
}
static void set_range_value(GtkRange *range, int value)
{ if(range) gtk_range_set_value(range, value);
}
static void set_spin_button_value(GtkSpinButton *spin, int value)
{ if(spin) gtk_spin_button_set_value(spin, value);
}
/***
*** Encoding window
***/
/*
* Reset the notebook contents for new encoding action
*/
void ResetRS01EncodeWindow(Method *method)
{ RS01Widgets *wl = (RS01Widgets*)method->widgetList;
SetProgress(wl->encPBar1, 0, 100);
SetProgress(wl->encPBar2, 0, 100);
gtk_widget_hide(wl->encLabel2);
gtk_widget_hide(wl->encPBar2);
gtk_widget_hide(wl->curveButton);
gtk_label_set_text(GTK_LABEL(wl->encFootline), "");
gtk_label_set_text(GTK_LABEL(wl->encFootline2), "");
}
/*
* Show the button for switching to the reading curve
*/
static gboolean show_button_idle_func(gpointer data)
{ Method *method = (Method*)data;
RS01Widgets *wl = (RS01Widgets*)method->widgetList;
gtk_widget_show(wl->curveButton);
return FALSE;
}
void RS01ShowCurveButton(Method *method)
{
g_idle_add(show_button_idle_func, method);
}
/*
* Switch back to the reading curve (read and create mode only)
*/
static gboolean curve_button_cb(GtkWidget *wid, gpointer action)
{ gtk_notebook_set_current_page(GTK_NOTEBOOK(Closure->notebook), 1);
return FALSE;
}
/*
* Create the notebook contents for creating an error correction file
*/
void CreateRS01EWindow(Method *method, GtkWidget *parent)
{ RS01Widgets *wl;
GtkWidget *sep,*wid,*pbar,*table,*hbox;
if(!method->widgetList)
{ wl = g_malloc0(sizeof(RS01Widgets));
method->widgetList = wl;
}
else wl = method->widgetList;
wl->encHeadline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->encHeadline), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(wl->encHeadline), 5, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->encHeadline, FALSE, FALSE, 3);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
table = gtk_table_new(2, 2, FALSE);
gtk_box_pack_start(GTK_BOX(parent), table, FALSE, FALSE, 30);
wl->encLabel1 = wid = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(wid),
_utf("<b>1. Calculating image sector checksums:</b>"));
gtk_misc_set_alignment(GTK_MISC(wid), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), wid, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 20, 20);
pbar = wl->encPBar1 = gtk_progress_bar_new();
gtk_table_attach(GTK_TABLE(table), pbar, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 20, 20);
wl->encLabel2 = wid = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(wid),
_utf("<b>2. Creating error correction data:</b>"));
gtk_misc_set_alignment(GTK_MISC(wid), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), wid, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 20, 20);
pbar = wl->encPBar2 = gtk_progress_bar_new();
gtk_table_attach(GTK_TABLE(table), pbar, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 20, 20);
wl->encFootline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->encFootline), 0.0, 0.5);
gtk_misc_set_padding(GTK_MISC(wl->encFootline), 20, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->encFootline, FALSE, FALSE, 3);
wl->encFootline2 = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->encFootline2), 0.0, 0.5);
gtk_misc_set_padding(GTK_MISC(wl->encFootline2), 20, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->encFootline2, FALSE, FALSE, 3);
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
wid = gtk_label_new(NULL);
gtk_misc_set_padding(GTK_MISC(wid), 10, 0);
gtk_box_pack_start(GTK_BOX(hbox), wid, FALSE, FALSE, 0);
wl->curveButton = gtk_button_new_with_label(_utf("Show reading speed curve"));
g_signal_connect(G_OBJECT(wl->curveButton), "clicked", G_CALLBACK(curve_button_cb), NULL);
gtk_box_pack_start(GTK_BOX(hbox), wl->curveButton, FALSE, FALSE, 0);
}
/***
*** "Fix" window
***/
/*
* Set the media size and ecc capacity
*/
static gboolean set_max_idle_func(gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
redraw_curve(wl);
return FALSE;
}
void RS01SetFixMaxValues(RS01Widgets *wl, int data_bytes, int ecc_bytes, gint64 sectors)
{
wl->dataBytes = data_bytes;
wl->eccBytes = ecc_bytes;
wl->nSectors = sectors;
wl->fixCurve->maxX = 100;
wl->fixCurve->maxY = ecc_bytes - (ecc_bytes % 5) + 5;
g_idle_add(set_max_idle_func, wl);
}
/*
* Update the corrected / uncorrected numbers
*/
static gboolean results_idle_func(gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
SetLabelText(GTK_LABEL(wl->fixCorrected), _("Repaired: %lld"), wl->corrected);
SetLabelText(GTK_LABEL(wl->fixUncorrected), _("Unrepairable: <span %s>%lld</span>"),Closure->redMarkup, wl->uncorrected);
SetLabelText(GTK_LABEL(wl->fixProgress), _("Progress: %3d.%1d%%"), wl->percent/10, wl->percent%10);
return FALSE;
}
void RS01UpdateFixResults(RS01Widgets *wl, gint64 corrected, gint64 uncorrected)
{
wl->corrected = corrected;
wl->uncorrected = uncorrected;
g_idle_add(results_idle_func, wl);
}
/*
* Update the error curve
*/
static gboolean curve_idle_func(gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
gint x0 = CurveX(wl->fixCurve, (double)wl->lastPercent);
gint x1 = CurveX(wl->fixCurve, (double)wl->percent);
gint y = CurveY(wl->fixCurve, wl->fixCurve->ivalue[wl->percent]);
gint i;
/*** Mark unused ecc values */
for(i=wl->lastPercent+1; i<wl->percent; i++)
wl->fixCurve->ivalue[i] = wl->fixCurve->ivalue[wl->percent];
/*** Resize the Y axes if error values exceeds current maximum */
if(wl->fixCurve->ivalue[wl->percent] > wl->fixCurve->maxY)
{ wl->fixCurve->maxY = wl->fixCurve->ivalue[wl->percent];
wl->fixCurve->maxY = wl->fixCurve->maxY - (wl->fixCurve->maxY % 5) + 5;
update_geometry(wl);
gdk_window_clear(wl->fixCurve->widget->window);
redraw_curve(wl);
wl->lastPercent = wl->percent;
return FALSE;
}
/*** Draw the error value */
if(wl->fixCurve->ivalue[wl->percent] > 0)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->barColor);
gdk_draw_rectangle(wl->fixCurve->widget->window,
Closure->drawGC, TRUE,
x0, y, x0==x1 ? 1 : x1-x0, wl->fixCurve->bottomY-y);
}
wl->lastPercent = wl->percent;
/* Redraw the ecc capacity threshold line */
y = CurveY(wl->fixCurve, wl->eccBytes);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->greenSector);
gdk_draw_line(wl->fixCurve->widget->window,
Closure->drawGC,
wl->fixCurve->leftX-6, y, wl->fixCurve->rightX+6, y);
return FALSE;
}
/*
* Add one new data point
*/
void RS01AddFixValues(RS01Widgets *wl, int percent, int ecc_max)
{
if(percent < 0 || percent > 1000)
return;
wl->fixCurve->ivalue[percent] = ecc_max;
wl->percent = percent;
g_idle_add(curve_idle_func, wl);
}
/*
* Redraw the whole curve
*/
/* Calculate the geometry of the curve and spiral */
static void update_geometry(RS01Widgets *wl)
{
/* Curve geometry */
UpdateCurveGeometry(wl->fixCurve, "999", 20);
/* Label positions in the foot line */
gtk_box_set_child_packing(GTK_BOX(wl->fixFootlineBox), wl->fixCorrected,
TRUE, TRUE, wl->fixCurve->leftX, GTK_PACK_START);
gtk_box_set_child_packing(GTK_BOX(wl->fixFootlineBox), wl->fixUncorrected,
TRUE, TRUE, wl->fixCurve->leftX, GTK_PACK_START);
}
static void redraw_curve(RS01Widgets *wl)
{ int y;
/* Redraw the curve */
RedrawAxes(wl->fixCurve);
RedrawCurve(wl->fixCurve, wl->percent);
/* Ecc capacity threshold line */
y = CurveY(wl->fixCurve, wl->eccBytes);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->greenSector);
gdk_draw_line(wl->fixCurve->widget->window,
Closure->drawGC,
wl->fixCurve->leftX-6, y, wl->fixCurve->rightX+6, y);
}
/*
* Expose callback
*/
static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
if(event->count) /* Exposure compression */
{ return TRUE;
}
update_geometry(wl);
redraw_curve(wl);
return TRUE;
}
/*
* Reset the notebook contents for new fixing action
*/
void ResetRS01FixWindow(Method *method)
{ RS01Widgets *wl = (RS01Widgets*)method->widgetList;
gtk_notebook_set_current_page(GTK_NOTEBOOK(wl->fixNotebook), 0);
ZeroCurve(wl->fixCurve);
RS01UpdateFixResults(wl, 0, 0);
if(wl->fixCurve && wl->fixCurve->widget)
{ gdk_window_clear(wl->fixCurve->widget->window);
redraw_curve(wl);
}
wl->percent = 0;
wl->lastPercent = 0;
}
/*
* Create the notebook contents for fixing an image
*/
void CreateRS01FWindow(Method *method, GtkWidget *parent)
{ RS01Widgets *wl;
GtkWidget *sep,*ignore,*d_area,*notebook,*hbox;
if(!method->widgetList)
{ wl = g_malloc0(sizeof(RS01Widgets));
method->widgetList = wl;
}
else wl = method->widgetList;
wl->fixHeadline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixHeadline), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(wl->fixHeadline), 5, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->fixHeadline, FALSE, FALSE, 3);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
d_area = wl->fixDrawingArea = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(parent), d_area, TRUE, TRUE, 0);
g_signal_connect(G_OBJECT (d_area), "expose_event", G_CALLBACK(expose_cb), (gpointer)wl);
notebook = wl->fixNotebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
gtk_box_pack_end(GTK_BOX(parent), notebook, FALSE, FALSE, 0);
hbox = wl->fixFootlineBox = gtk_hbox_new(TRUE, 0);
wl->fixCorrected = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixCorrected), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), wl->fixCorrected, TRUE, TRUE, 0);
wl->fixProgress = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixProgress), 0.5, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), wl->fixProgress, TRUE, TRUE, 0);
wl->fixUncorrected = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixUncorrected), 1.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), wl->fixUncorrected, TRUE, TRUE, 0);
ignore = gtk_label_new("progress_tab");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, ignore);
wl->fixFootline = gtk_label_new("Footline");
gtk_misc_set_alignment(GTK_MISC(wl->fixFootline), 0.0, 0.5);
gtk_misc_set_padding(GTK_MISC(wl->fixFootline), 5, 0);
ignore = gtk_label_new("footer_tab");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), wl->fixFootline, ignore);
wl->fixCurve = CreateCurve(d_area, _("Errors/Ecc block"), "%d", 1000, CURVE_PERCENT);
wl->fixCurve->enable = DRAW_ICURVE;
}
/***
*** Create the preferences page for setting redundancy etc.
***/
#define SYMBOLSIZE 8
#define FIELDSIZE (1<<SYMBOLSIZE)
#define FIELDMAX (FIELDSIZE-1)
enum
{ PREF_NROOTS = 0,
PREF_CACHE = 1,
PREF_ECC_SIZE = 2
};
#ifdef HAVE_32BIT
static int cache_size[] = { 8, 16, 32, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536 };
#else
static int cache_size[] = { 8, 16, 32, 64, 96, 128, 192, 256, 384, 512, 768,
1024, 1536, 2048, 2560, 3072, 4096, 5120, 6144, 7168, 8192 };
#endif
static gchar* format_cb(GtkScale *scale, gdouble value, gpointer data)
{ int nroots = value;
int ndata = FIELDMAX - nroots;
char *label;
if(GPOINTER_TO_INT(data) == PREF_CACHE)
{
label = g_strdup(" ");
}
else
label = g_strdup_printf(_utf("%4.1f%% redundancy (%d roots)"),
((double)nroots*100.0)/(double)ndata,
nroots);
FORGET(label); /* will be g_free()ed by the scale */
return label;
}
static void cache_cb(GtkWidget *widget, gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
LabelWithOnlineHelp *lwoh = wl->cacheLwoh;
int value;
char *text, *utf;
value = gtk_range_get_value(GTK_RANGE(widget));
Closure->cacheMiB = cache_size[value];
text = g_strdup_printf(_("%d MiB of file cache"), Closure->cacheMiB);
utf = g_locale_to_utf8(text, -1, NULL, NULL, NULL);
gtk_label_set_markup(GTK_LABEL(lwoh->normalLabel), utf);
gtk_label_set_markup(GTK_LABEL(lwoh->linkLabel), utf);
SetOnlineHelpLinkText(lwoh, text);
UpdateMethodPreferences();
g_free(text);
g_free(utf);
}
static void nroots_cb(GtkWidget *widget, gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
int value;
value = gtk_range_get_value(GTK_RANGE(widget));
if(Closure->redundancy) g_free(Closure->redundancy);
Closure->redundancy = g_strdup_printf("%d", value);
if(widget == wl->redundancyScaleA)
set_range_value(GTK_RANGE(wl->redundancyScaleB), value);
else set_range_value(GTK_RANGE(wl->redundancyScaleA), value);
UpdateMethodPreferences();
}
static void ecc_size_cb(GtkWidget *widget, gpointer data)
{ RS01Widgets *wl = (RS01Widgets*)data;
int value;
value = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
if(Closure->redundancy) g_free(Closure->redundancy);
Closure->redundancy = g_strdup_printf("%dm", value);
if(widget == wl->redundancySpinA)
gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl->redundancySpinB), atoi(Closure->redundancy));
else gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl->redundancySpinA), atoi(Closure->redundancy));
UpdateMethodPreferences();
}
static void toggle_cb(GtkWidget *widget, gpointer data)
{ Method *method = (Method*)data;
RS01Widgets *wl = (RS01Widgets*)method->widgetList;
int state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
if(state == TRUE)
{ if(widget == wl->radio3A || widget == wl->radio3B)
{ gtk_widget_set_sensitive(wl->redundancyScaleA, TRUE);
gtk_widget_set_sensitive(wl->redundancyScaleB, TRUE);
}
else
{ gtk_widget_set_sensitive(wl->redundancyScaleA, FALSE);
gtk_widget_set_sensitive(wl->redundancyScaleB, FALSE);
}
if(widget == wl->radio4A || widget == wl->radio4B)
{ gtk_widget_set_sensitive(wl->redundancySpinA, TRUE);
gtk_widget_set_sensitive(wl->redundancySpinB, TRUE);
gtk_widget_set_sensitive(wl->radio4LabelA, TRUE);
gtk_widget_set_sensitive(wl->radio4LabelB, TRUE);
}
else
{ gtk_widget_set_sensitive(wl->redundancySpinA, FALSE);
gtk_widget_set_sensitive(wl->redundancySpinB, FALSE);
gtk_widget_set_sensitive(wl->radio4LabelA, FALSE);
gtk_widget_set_sensitive(wl->radio4LabelB, FALSE);
}
if( widget == wl->radio1A /* Normal */
|| widget == wl->radio1B)
{
set_range_value(GTK_RANGE(wl->redundancyScaleA), 32);
set_range_value(GTK_RANGE(wl->redundancyScaleB), 32);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio1A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio1B), TRUE);
if(Closure->redundancy) g_free(Closure->redundancy);
Closure->redundancy = g_strdup("normal");
}
if( widget == wl->radio2A /* High */
|| widget == wl->radio2B)
{
set_range_value(GTK_RANGE(wl->redundancyScaleA), 64);
set_range_value(GTK_RANGE(wl->redundancyScaleB), 64);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio2A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio2B), TRUE);
if(Closure->redundancy) g_free(Closure->redundancy);
Closure->redundancy = g_strdup("high");
}
if( widget == wl->radio3A /* number of roots */
|| widget == wl->radio3B)
{ int nroots = gtk_range_get_value(GTK_RANGE(wl->redundancyScaleA));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio3A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio3B), TRUE);
if(Closure->redundancy) g_free(Closure->redundancy);
Closure->redundancy = g_strdup_printf("%d", nroots);
}
if( widget == wl->radio4A /* relative to space usage */
|| widget == wl->radio4B)
{ int space = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wl->redundancySpinA));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio4A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio4B), TRUE);
if(Closure->redundancy) g_free(Closure->redundancy);
Closure->redundancy = g_strdup_printf("%dm", space);
}
UpdateMethodPreferences();
}
}
void ResetRS01PrefsPage(Method *method)
{ RS01Widgets *wl = (RS01Widgets*)method->widgetList;
int index;
/* Redundancy selection */
if(Closure->redundancy)
{
if(!strcmp(Closure->redundancy, "normal"))
{ if(wl->radio1A && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wl->radio1A)) == FALSE)
{ activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio1A), TRUE);
activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio1B), TRUE);
}
}
else if(!strcmp(Closure->redundancy, "high"))
{ if(wl->radio2A && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wl->radio2A)) == FALSE)
{ activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio2A), TRUE);
activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio2B), TRUE);
}
}
else
{ int last = strlen(Closure->redundancy)-1;
if(Closure->redundancy[last] == 'm')
{ if(wl->redundancySpinA)
{ int old = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(wl->redundancySpinA));
int new;
Closure->redundancy[last] = 0;
new = atoi(Closure->redundancy);
Closure->redundancy[last] = 'm';
if(new != old)
{ set_spin_button_value(GTK_SPIN_BUTTON(wl->redundancySpinA), new);
set_spin_button_value(GTK_SPIN_BUTTON(wl->redundancySpinB), new);
}
if(wl->radio4A && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wl->radio4A)) == FALSE)
{ activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio4A), TRUE);
activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio4B), TRUE);
}
}
}
else
{ if(wl->redundancyScaleA)
{ int old = gtk_range_get_value(GTK_RANGE(wl->redundancyScaleA));
int new = atoi(Closure->redundancy);
if(new != old)
{ set_range_value(GTK_RANGE(wl->redundancyScaleA), new);
set_range_value(GTK_RANGE(wl->redundancyScaleB), new);
}
if(wl->radio3A && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wl->radio3A)) == FALSE)
{ activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio3A), TRUE);
activate_toggle_button(GTK_TOGGLE_BUTTON(wl->radio3B), TRUE);
}
}
}
}
}
/* Memory caching */
for(index = 0; index < sizeof(cache_size)/sizeof(int); index++)
if(cache_size[index] > Closure->cacheMiB)
break;
set_range_value(GTK_RANGE(wl->cacheScaleA), index > 0 ? index-1 : index);
set_range_value(GTK_RANGE(wl->cacheScaleB), index > 0 ? index-1 : index);
}
void CreateRS01PrefsPage(Method *method, GtkWidget *parent)
{ RS01Widgets *wl = (RS01Widgets*)method->widgetList;
GtkWidget *frame, *hbox, *vbox, *lab, *scale, *spin;
GtkWidget *radio;
LabelWithOnlineHelp *lwoh;
unsigned int i, index;
char *text;
/*** Redundancy selection */
frame = gtk_frame_new(_utf("Redundancy for new error correction files"));
gtk_box_pack_start(GTK_BOX(parent), frame, FALSE, FALSE, 0);
vbox = gtk_vbox_new(FALSE, 10);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
gtk_container_add(GTK_CONTAINER(frame), vbox);
/* Normal redundancy */
lwoh = CreateLabelWithOnlineHelp(_("Normal redundancy"), _("Normal"));
RegisterPreferencesHelpWindow(lwoh);
for(i=0; i<2; i++)
{ GtkWidget *hbox = gtk_hbox_new(FALSE, 4);
radio = gtk_radio_button_new(NULL);
g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(toggle_cb), method);
gtk_box_pack_start(GTK_BOX(hbox), radio, FALSE, FALSE, 0);
if(!i)
{ wl->radio1A = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->linkBox, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
}
else
{ wl->radio1B = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->normalLabel, FALSE, FALSE, 0);
AddHelpWidget(lwoh, hbox);
}
}
AddHelpParagraph(lwoh, _("<b>Normal redundancy</b>\n\n"
"The preset \"normal\" creates a redundancy of 14.3%%.\n"
"It invokes optimized program code to speed up the "
"error correction file creation."));
/* High redundancy */
lwoh = CreateLabelWithOnlineHelp(_("High redundancy"), _("High"));
RegisterPreferencesHelpWindow(lwoh);
for(i=0; i<2; i++)
{ GtkWidget *hbox = gtk_hbox_new(FALSE, 4);
radio = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(i?wl->radio1B:wl->radio1A));
g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(toggle_cb), method);
gtk_box_pack_start(GTK_BOX(hbox), radio, FALSE, FALSE, 0);
if(!i)
{ wl->radio2A = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->linkBox, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
}
else
{ wl->radio2B = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->normalLabel, FALSE, FALSE, 0);
AddHelpWidget(lwoh, hbox);
}
}
AddHelpParagraph(lwoh, _("<b>High redundancy</b>\n\n"
"The preset \"high\" creates a redundancy of 33.5%%.\n"
"It invokes optimized program code to speed up the "
"error correction file creation."));
/* User-selected redundancy */
lwoh = CreateLabelWithOnlineHelp(_("Other redundancy"), _("Other"));
RegisterPreferencesHelpWindow(lwoh);
for(i=0; i<2; i++)
{ hbox = gtk_hbox_new(FALSE, 4);
radio = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(i?wl->radio1B:wl->radio1A));
g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(toggle_cb), method);
gtk_box_pack_start(GTK_BOX(hbox), radio, FALSE, FALSE, 0);
if(!i)
{ wl->radio3A = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->linkBox, FALSE, FALSE, 0);
}
else
{ wl->radio3B = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->normalLabel, FALSE, FALSE, 0);
}
scale = gtk_hscale_new_with_range(8,100,1);
gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_RIGHT);
gtk_range_set_increments(GTK_RANGE(scale), 1, 1);
gtk_range_set_value(GTK_RANGE(scale), 32);
gtk_widget_set_sensitive(scale, FALSE);
g_signal_connect(scale, "format-value", G_CALLBACK(format_cb), (gpointer)PREF_NROOTS);
g_signal_connect(scale, "value-changed", G_CALLBACK(nroots_cb), (gpointer)wl);
gtk_container_add(GTK_CONTAINER(hbox), scale);
if(!i)
{ wl->redundancyScaleA = scale;
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
}
else
{ wl->redundancyScaleB = scale;
AddHelpWidget(lwoh, hbox);
}
}
AddHelpParagraph(lwoh, _("<b>Other redundancy</b>\n\n"
"Specifies the redundancy by percent.\n"
"An error correction file with x%% redundancy "
"will be approximately x%% of the size of the "
"corresponding image file."));
/* Space-delimited redundancy */
lwoh = CreateLabelWithOnlineHelp(_("Space-delimited redundancy"), _("Use at most"));
RegisterPreferencesHelpWindow(lwoh);
for(i=0; i<2; i++)
{ hbox = gtk_hbox_new(FALSE, 4);
radio = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(i?wl->radio1B:wl->radio1A));
g_signal_connect(G_OBJECT(radio), "toggled", G_CALLBACK(toggle_cb), method);
gtk_box_pack_start(GTK_BOX(hbox), radio, FALSE, FALSE, 0);
if(!i)
{ wl->radio4A = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->linkBox, FALSE, FALSE, 0);
}
else
{ wl->radio4B = radio;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->normalLabel, FALSE, FALSE, 0);
}
spin = gtk_spin_button_new_with_range(0, 100000, 100);
g_signal_connect(spin, "value-changed", G_CALLBACK(ecc_size_cb), (gpointer)wl);
gtk_entry_set_width_chars(GTK_ENTRY(spin), 8);
gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0);
lab = gtk_label_new(_utf("MiB for error correction data"));
gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 0);
gtk_widget_set_sensitive(spin, FALSE);
gtk_widget_set_sensitive(lab, FALSE);
if(!i)
{ wl->redundancySpinA = spin;
wl->radio4LabelA = lab;
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
}
else
{ wl->redundancySpinB = spin;
wl->radio4LabelB = lab;
AddHelpWidget(lwoh, hbox);
}
}
AddHelpParagraph(lwoh, _("<b>Space-delimited redundancy</b>\n\n"
"Specifies the maximum size of the error correction file in MiB. "
"dvdisaster will choose a suitable redundancy setting so that "
"the overall size of the error correction file does not exceed "
"the given limit.\n\n"
"<b>Advance notice:</b> When using the same size setting for "
"images of vastly different size, smaller images receive more "
"redundancy than larger ones. This is usually not what you want."));
/*** Preset redundancy values */
if(Closure->redundancy)
{ if(!strcmp(Closure->redundancy, "normal"))
{ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio1A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio1B), TRUE);
}
else if(!strcmp(Closure->redundancy, "high"))
{ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio2A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio2B), TRUE);
}
else
{ int last = strlen(Closure->redundancy)-1;
if(Closure->redundancy[last] == 'm')
{ Closure->redundancy[last] = 0;
gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl->redundancySpinA), atoi(Closure->redundancy));
gtk_spin_button_set_value(GTK_SPIN_BUTTON(wl->redundancySpinB), atoi(Closure->redundancy));
Closure->redundancy[last] = 'm';
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio4A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio4B), TRUE);
}
else
{ gtk_range_set_value(GTK_RANGE(wl->redundancyScaleA), atoi(Closure->redundancy));
gtk_range_set_value(GTK_RANGE(wl->redundancyScaleB), atoi(Closure->redundancy));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio3A), TRUE);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wl->radio3B), TRUE);
}
}
}
/* Memory utilization */
frame = gtk_frame_new(_utf("Memory utilization"));
gtk_box_pack_start(GTK_BOX(parent), frame, FALSE, FALSE, 0);
text = g_strdup_printf(_("%d MiB of file cache"), Closure->cacheMiB);
lwoh = CreateLabelWithOnlineHelp(_("File cache"), text);
RegisterPreferencesHelpWindow(lwoh);
g_free(text);
wl->cacheLwoh = lwoh;
LockLabelSize(GTK_LABEL(lwoh->normalLabel), _utf("%d MiB of file cache"), 2222);
LockLabelSize(GTK_LABEL(lwoh->linkLabel), _utf("%d MiB of file cache"), 2222);
for(i=0; i<2; i++)
{ GtkWidget *hbox = gtk_hbox_new(FALSE, 4);
int n_entries = sizeof(cache_size)/sizeof(int);
lab = gtk_label_new(_utf("Use"));
gtk_box_pack_start(GTK_BOX(hbox), lab, FALSE, FALSE, 0);
for(index = 0; index < n_entries; index++)
if(cache_size[index] > Closure->cacheMiB)
break;
scale = gtk_hscale_new_with_range(0,n_entries-1,1);
gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_RIGHT);
gtk_range_set_increments(GTK_RANGE(scale), 1, 1);
gtk_range_set_value(GTK_RANGE(scale), index > 0 ? index-1 : index);
g_signal_connect(scale, "format-value", G_CALLBACK(format_cb), (gpointer)PREF_CACHE);
g_signal_connect(scale, "value-changed", G_CALLBACK(cache_cb), (gpointer)wl);
gtk_box_pack_start(GTK_BOX(hbox), scale, TRUE, TRUE, 0);
if(!i)
{ wl->cacheScaleA = scale;
gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
gtk_box_pack_start(GTK_BOX(hbox), lwoh->linkBox, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(frame), hbox);
}
else
{ wl->cacheScaleB = scale;
gtk_box_pack_start(GTK_BOX(hbox), lwoh->normalLabel, FALSE, FALSE, 0);
AddHelpWidget(lwoh, hbox);
}
}
AddHelpParagraph(lwoh, _("<b>File cache</b>\n\n"
"dvdisaster optimizes access to the image and error correction "
"files by maintaining its own cache. "
"The preset of 32MiB is suitable for most systems."));
}

812
src/rs02-common.c Normal file
View File

@@ -0,0 +1,812 @@
/* 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;
}

1200
src/rs02-create.c Normal file

File diff suppressed because it is too large Load Diff

910
src/rs02-fix.c Normal file
View File

@@ -0,0 +1,910 @@
/* 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 "galois-inlines.h"
/***
*** Internal housekeeping
***/
typedef struct
{ Image *image;
EccHeader *eh;
#ifndef CLI
RS02Widgets *wl;
#endif
RS02Layout *lay;
GaloisTables *gt;
ReedSolomonTables *rt;
int earlyTermination;
char *msg;
unsigned char *imgBlock[255];
} fix_closure;
static void fix_cleanup(gpointer data)
{ fix_closure *fc = (fix_closure*)data;
int i;
UnregisterCleanup();
#ifndef CLI
if(Closure->guiMode)
{ if(fc->earlyTermination)
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by unrecoverable error.</span>"),
Closure->redMarkup);
AllowActions(TRUE);
}
#endif
/** Clean up */
if(fc->image) CloseImage(fc->image);
if(fc->msg) g_free(fc->msg);
for(i=0; i<255; i++)
{ if(fc->imgBlock[i])
g_free(fc->imgBlock[i]);
}
if(fc->lay) g_free(fc->lay);
if(fc->gt) FreeGaloisTables(fc->gt);
if(fc->rt) FreeReedSolomonTables(fc->rt);
g_free(fc);
#ifndef CLI
if(Closure->guiMode)
g_thread_exit(0);
#endif
}
/*
* Expand a truncated image
*/
static void expand_image(fix_closure *fc, gint64 new_size)
{ Image *image = fc->image;
int last_percent, percent;
gint64 sectors, new_sectors;
if(!LargeSeek(image->file, image->file->size))
Stop(_("Failed seeking to end of image: %s\n"), strerror(errno));
last_percent = 0;
new_sectors = new_size - image->sectorSize;
for(sectors = 0; sectors < new_sectors; sectors++)
{ unsigned char buf[2048];
int n;
CreateMissingSector(buf, image->sectorSize+sectors,
fc->eh->mediumFP, FINGERPRINT_SECTOR,
"RS02 fix placeholder");
n = LargeWrite(image->file, buf, 2048);
if(n != 2048)
Stop(_("Failed expanding the image: %s\n"), strerror(errno));
percent = (100*sectors) / new_sectors;
if(last_percent != percent)
{
#ifndef CLI
if(Closure->guiMode)
;
else
#endif
PrintProgress(_("Expanding image: %3d%%"), percent);
last_percent = percent;
}
}
#ifndef CLI
if(Closure->guiMode)
;
else
#endif
{
PrintProgress(_("Expanding image: %3d%%"), 100);
PrintProgress("\n");
}
image->sectorSize = new_size;
image->file->size = new_size;
}
/***
*** Test and fix the current image.
***/
void RS02Fix(Image *image)
{
#ifndef CLI
Method *self = FindMethod("RS02");
RS02Widgets *wl = (RS02Widgets*)self->widgetList;
#endif
RS02Layout *lay;
fix_closure *fc = g_malloc0(sizeof(fix_closure));
EccHeader *eh;
#ifdef HAVE_BIG_ENDIAN
EccHeader *eh_swapped;
#endif
gint32 *gf_index_of;
gint32 *gf_alpha_to;
gint64 block_idx[255];
gint64 s;
guint32 crc_buf[512];
gint64 crc_sector_byte;
int nroots,ndata;
int crc_idx, ecc_idx;
int crc_valid = TRUE;
int cache_size, cache_sector, cache_offset;
int erasure_count,erasure_list[255],erasure_map[255];
int error_count;
int percent, last_percent;
int worst_ecc = 0, local_plot_max = 0;
int i,j;
gint64 crc_errors=0;
gint64 data_count=0;
gint64 ecc_count=0;
gint64 crc_count=0;
gint64 data_corr=0;
gint64 ecc_corr=0;
gint64 corrected=0;
gint64 uncorrected=0;
gint64 damaged_sectors=0;
gint64 damaged_eccblocks=0;
gint64 damaged_eccsecs=0;
gint64 expected_sectors;
char *t=NULL;
/*** Register the cleanup procedure for GUI mode */
fc->image = image;
#ifndef CLI
fc->wl = wl;
#endif
fc->earlyTermination = TRUE;
RegisterCleanup(_("Repairing of image aborted"), fix_cleanup, fc);
/*** Open the image file */
#ifndef CLI
if(Closure->guiMode)
SetLabelText(GTK_LABEL(wl->fixHeadline),
_("<big>Repairing the image.</big>\n<i>%s</i>"),
_("Opening files..."));
#endif
eh = fc->eh = image->eccHeader;
lay = fc->lay = RS02LayoutFromImage(image);
ndata = lay->ndata;
nroots = lay->nroots;
/*** Set up the Galois field arithmetic */
fc->gt = CreateGaloisTables(RS_GENERATOR_POLY);
fc->rt = CreateReedSolomonTables(fc->gt, RS_FIRST_ROOT, RS_PRIM_ELEM, nroots);
gf_index_of = fc->gt->indexOf;
gf_alpha_to = fc->gt->alphaTo;
/*** Expand a truncated image with "dead sector" markers */
expected_sectors = lay->eccSectors+lay->dataSectors;
if(image->sectorSize < expected_sectors)
expand_image(fc, expected_sectors);
/*** Announce what we going to do */
#ifndef CLI
if(Closure->guiMode)
{ char *msg = g_strdup_printf(_("Image contains error correction data: Method RS02, %d roots, %4.1f%% redundancy."),
eh->eccBytes,
((double)eh->eccBytes*100.0)/(double)eh->dataBytes);
SetLabelText(GTK_LABEL(wl->fixHeadline),
_("<big>Repairing the image.</big>\n<i>%s</i>"), msg);
RS02SetFixMaxValues(wl, eh->dataBytes, eh->eccBytes, expected_sectors);
g_free(msg);
}
#endif
PrintLog(_("\nFix mode(%s): Repairable sectors will be fixed in the image.\n"),
"RS02");
/*** Truncate an image with trailing garbage */
if(image->sectorSize > expected_sectors)
{ gint64 diff = image->sectorSize - expected_sectors;
char *trans = _("The image file is %lld sectors longer as noted in the\n"
"ecc data. This might simply be zero padding, but could\n"
"also mean that the image was manipulated after appending\n"
"the error correction information.\n\n%s");
if(diff>0 && diff<=2)
{ int answer;
answer = ModalWarningOrCLI(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, NULL,
_("Image file is %lld sectors longer than expected.\n"
"Assuming this is a TAO mode medium.\n"
"%lld sectors will be removed from the image end.\n"),
diff, diff);
if(!answer)
{
#ifndef CLI
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
fc->earlyTermination = FALSE; /* suppress respective error message */
#endif
goto terminate;
}
if(!TruncateImage(image, (gint64)(2048*expected_sectors)))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno));
}
#ifndef CLI
if(diff>2 && Closure->guiMode)
{ int answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL, NULL,
trans,
diff,
_("Is it okay to remove the superfluous sectors?"));
if(!answer)
{ SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
if(!TruncateImage(image, (gint64)(2048*expected_sectors)))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno));
PrintLog(_("Image has been truncated by %lld sectors.\n"), diff);
}
#endif
#ifndef CLI
if(diff>2 && !Closure->guiMode)
#else
if(diff>2)
#endif
{ if(!Closure->truncate)
Stop(trans,
diff,
_("Add the --truncate option to the program call\n"
"to have the superfluous sectors removed."));
if(!TruncateImage(image, (gint64)(2048*expected_sectors)))
Stop(_("Could not truncate %s: %s\n"),Closure->imageName,strerror(errno));
PrintLog(_("Image has been truncated by %lld sectors.\n"), diff);
}
}
/*** Rewrite all headers from the one which was given us as a reference */
#ifdef HAVE_BIG_ENDIAN
eh_swapped = g_malloc(sizeof(EccHeader));
memcpy(eh_swapped, eh, sizeof(EccHeader));
SwapEccHeaderBytes(eh_swapped);
WriteRS02Headers(image->file, lay, eh_swapped);
g_free(eh_swapped);
#else
WriteRS02Headers(image->file, lay, eh);
#endif
/*** Prepare buffers for ecc code processing.
The first lay->protecedSectors are protected by ecc information.
The medium is logically divided into ndata layers and nroots slices.
Taking one sector from each layer and slice produces on ecc block
on which the error correction is carried out.
There is a total of lay->sectorsPerLayer ecc blocks.
A portion of cache_size sectors is read ahead from each layer/slice,
giving a total cache size of 255*cache_size. */
cache_size = 2*Closure->cacheMiB; /* ndata+nroots=255 medium sectors are approx. 0.5MiB */
for(i=0; i<255; i++)
fc->imgBlock[i] = g_malloc(cache_size*2048);
/*** Setup the block counters for mapping medium sectors to ecc blocks.
Error correction begins at lay->CrcLayerIndex so that we have a chance
of repairing the CRC information before we need it. */
for(s=0, i=0; i<lay->ndata; s+=lay->sectorsPerLayer, i++)
block_idx[i] = s + lay->firstCrcLayerIndex;
ecc_idx = lay->firstCrcLayerIndex;
cache_sector = cache_size; /* forces instant reload of imgBlock cache */
cache_offset = 2048*cache_sector;
/*** CRCs for the first ecc block are taken from the ecc header.
Preset pointers accordingly. */
crc_sector_byte = 2048 *(lay->dataSectors + 2); /* first sector with CRC info */
crc_idx = 0;
memcpy(crc_buf, (char*)eh + 2048, sizeof(guint32) * lay->ndata);
/*** Test ecc blocks and attempt error correction */
last_percent = -1;
for(s=0; s<lay->sectorsPerLayer; s++)
{ gint64 si = (s + lay->firstCrcLayerIndex) % lay->sectorsPerLayer;
int bi;
/* See if user hit the Stop button */
#ifndef CLI
if(Closure->stopActions)
{ if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
SwitchAndSetFootline(fc->wl->fixNotebook, 1,
fc->wl->fixFootline,
_("<span %s>Aborted by user request!</span>"),
Closure->redMarkup);
fc->earlyTermination = FALSE; /* suppress respective error message */
goto terminate;
}
#endif
/* Make sure to wrap the block_idx[] ptr properly */
if(!si)
{ gint64 bs;
for(bs=0, i=0; i<lay->ndata; bs+=lay->sectorsPerLayer, i++)
block_idx[i] = bs;
ecc_idx = 0;
}
if(s == 1) /* force CRC reload */
crc_idx = 512;
/* Fill cache with the next batch of cache_size ecc blocks. */
if(cache_sector >= cache_size)
{
if(lay->sectorsPerLayer-si < cache_size)
cache_size = lay->sectorsPerLayer-si;
for(i=0; i<ndata; i++) /* Read data portion */
{ int offset = 0;
for(j=0; j<cache_size; j++)
{ RS02ReadSector(image, lay, fc->imgBlock[i]+offset, block_idx[i]+j);
offset += 2048;
}
}
for(i=0; i<nroots; i++) /* and ecc portion */
{ int offset = 0;
for(j=0; j<cache_size; j++)
{ gint64 esi = RS02EccSectorIndex(lay, i, ecc_idx+j);
if(!LargeSeek(image->file, 2048*esi))
Stop(_("Failed seeking to sector %lld in image: %s"), esi, strerror(errno));
if(LargeRead(image->file, fc->imgBlock[i+ndata]+offset, 2048) != 2048)
Stop(_("Failed reading sector %lld in image: %s"), esi, strerror(errno));
offset += 2048;
}
}
cache_sector = cache_offset = 0;
}
/* Look for erasures based on the "dead sector" marker and CRC sums */
erasure_count = error_count = 0;
for(i=0; i<lay->ndata; i++) /* Check the data sectors */
{
erasure_map[i] = 0;
if(block_idx[i] < lay->protectedSectors) /* ignore the padding sectors! */
{ int err = CheckForMissingSector(fc->imgBlock[i]+cache_offset, block_idx[i],
eh->mediumFP, eh->fpSector);
if(err != SECTOR_PRESENT)
{ erasure_map[i] = 1;
erasure_list[erasure_count++] = i;
damaged_sectors++;
}
if(block_idx[i] < lay->dataSectors) /* only data sectors have CRCs */
{ guint32 crc = Crc32(fc->imgBlock[i]+cache_offset, 2048);
int err;
if(crc_idx >= 512)
{ if(!LargeSeek(image->file, crc_sector_byte))
Stop(_("Failed seeking in crc area: %s"), strerror(errno));
if(LargeRead(image->file, crc_buf, 2048) != 2048)
Stop(_("problem reading crc data: %s"), strerror(errno));
err = CheckForMissingSector((unsigned char*)crc_buf, crc_sector_byte/2048,
eh->mediumFP, eh->fpSector);
crc_sector_byte += 2048;
crc_idx = 0;
crc_valid = (err == SECTOR_PRESENT);
}
if(crc_valid && !erasure_map[i] && crc != crc_buf[crc_idx])
{ erasure_map[i] = 3;
erasure_list[erasure_count++] = i;
PrintCLI(_("CRC error in sector %lld\n"),block_idx[i]);
damaged_sectors++;
crc_errors++;
}
data_count++;
crc_idx++;
}
else if(block_idx[i] >= lay->dataSectors + 2) crc_count++;
}
}
for(i=lay->ndata; i<GF_FIELDMAX; i++) /* Check the ecc sectors */
{ gint64 ecc_sector = RS02EccSectorIndex(lay, i-ndata, ecc_idx);
int err = CheckForMissingSector(fc->imgBlock[i]+cache_offset,
ecc_sector,
eh->mediumFP, eh->fpSector);
if(err)
{ erasure_map[i] = 1;
erasure_list[erasure_count++] = i;
damaged_sectors++;
}
else erasure_map[i] = 0;
ecc_count++;
}
/* Trivially reject uncorrectable ecc block */
if(erasure_count>lay->nroots) /* uncorrectable */
{
#ifndef CLI
if(!Closure->guiMode)
#endif
{ PrintCLI(_("* Ecc block %lld: %3d unrepairable sectors: "), s, erasure_count);
for(i=0; i<erasure_count; i++)
{ gint64 loc = erasure_list[i];
if(loc < ndata) PrintCLI("%lld ", block_idx[loc]);
else PrintCLI("%lld ", RS02EccSectorIndex(lay, loc-ndata, ecc_idx));
}
PrintCLI("\n");
}
uncorrected += erasure_count;
goto skip;
}
/* Build ecc block and attempt to correct it */
for(bi=0; bi<2048; bi++) /* Run through each ecc block byte */
{ int offset = cache_offset+bi;
int r, deg_lambda, el, deg_omega;
int u,q,tmp,num1,num2,den,discr_r;
int lambda[nroots+1], syn[nroots]; /* Err+Eras Locator poly * and syndrome poly */
int b[nroots+1], t[nroots+1], omega[nroots+1];
int root[nroots], reg[nroots+1], loc[nroots];
int syn_error, count;
int k;
/* Form the syndromes; i.e., evaluate data(x) at roots of g(x) */
for(i=0; i<nroots; i++)
syn[i] = fc->imgBlock[0][offset];
for(j=1; j<GF_FIELDMAX; j++)
{ int data = fc->imgBlock[j][offset];
for(i=0;i<nroots;i++)
{ if(syn[i] == 0) syn[i] = data;
else syn[i] = data ^ gf_alpha_to[mod_fieldmax(gf_index_of[syn[i]] + (RS_FIRST_ROOT+i)*RS_PRIM_ELEM)];
}
}
/* Convert syndromes to index form, check for nonzero condition */
syn_error = 0;
for(i=0; i<nroots; i++)
{ syn_error |= syn[i];
syn[i] = gf_index_of[syn[i]];
}
/* If it is already correct by coincidence, we have nothing to do any further */
if(syn_error) damaged_eccblocks++;
else continue;
//printf("Syndrome error for ecc block %lld, byte %d\n",s,bi);
/* If we have found any erasures,
initialize lambda to be the erasure locator polynomial */
memset(lambda+1, 0, nroots*sizeof(lambda[0]));
lambda[0] = 1;
if(erasure_count > 0)
{ lambda[1] = gf_alpha_to[mod_fieldmax(RS_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[0]))];
for(i=1; i<erasure_count; i++)
{ u = mod_fieldmax(RS_PRIM_ELEM*(GF_FIELDMAX-1-erasure_list[i]));
for(j=i+1; j>0; j--)
{ tmp = gf_index_of[lambda[j-1]];
if(tmp != GF_ALPHA0)
lambda[j] ^= gf_alpha_to[mod_fieldmax(u + tmp)];
}
}
}
for(i=0; i<nroots+1; i++)
b[i] = gf_index_of[lambda[i]];
/* Begin Berlekamp-Massey algorithm to determine error+erasure locator polynomial */
r = erasure_count; /* r is the step number */
el = erasure_count;
while(++r <= nroots) /* Compute discrepancy at the r-th step in poly-form */
{
discr_r = 0;
for(i=0; i<r; i++)
if((lambda[i] != 0) && (syn[r-i-1] != GF_ALPHA0))
discr_r ^= gf_alpha_to[mod_fieldmax(gf_index_of[lambda[i]] + syn[r-i-1])];
discr_r = gf_index_of[discr_r]; /* Index form */
if(discr_r == GF_ALPHA0)
{ /* B(x) = x*B(x) */
memmove(b+1, b, nroots*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
else
{ /* T(x) = lambda(x) - discr_r*x*b(x) */
t[0] = lambda[0];
for(i=0; i<nroots; i++)
{ if(b[i] != GF_ALPHA0)
t[i+1] = lambda[i+1] ^ gf_alpha_to[mod_fieldmax(discr_r + b[i])];
else t[i+1] = lambda[i+1];
}
if(2*el <= r+erasure_count-1)
{ el = r + erasure_count - el;
/* B(x) <-- inv(discr_r) * lambda(x) */
for(i=0; i<=nroots; i++)
b[i] = (lambda[i] == 0) ? GF_ALPHA0 : mod_fieldmax(gf_index_of[lambda[i]] - discr_r + GF_FIELDMAX);
}
else
{ /* 2 lines below: B(x) <-- x*B(x) */
memmove(b+1, b, nroots*sizeof(b[0]));
b[0] = GF_ALPHA0;
}
memcpy(lambda,t,(nroots+1)*sizeof(t[0]));
}
}
/* Convert lambda to index form and compute deg(lambda(x)) */
deg_lambda = 0;
for(i=0; i<nroots+1; i++)
{ lambda[i] = gf_index_of[lambda[i]];
if(lambda[i] != GF_ALPHA0)
deg_lambda = i;
}
/* Find roots of the error+erasure locator polynomial by Chien search */
memcpy(reg+1, lambda+1, nroots*sizeof(reg[0]));
count = 0; /* Number of roots of lambda(x) */
for(i=1, k=RS_PRIMTH_ROOT-1; i<=GF_FIELDMAX; i++, k=mod_fieldmax(k+RS_PRIMTH_ROOT))
{ q=1; /* lambda[0] is always 0 */
for(j=deg_lambda; j>0; j--)
{ if(reg[j] != GF_ALPHA0)
{ reg[j] = mod_fieldmax(reg[j] + j);
q ^= gf_alpha_to[reg[j]];
}
}
if(q != 0) continue; /* Not a root */
/* store root (index-form) and error location number */
root[count] = i;
loc[count] = k;
/* If we've already found max possible roots, abort the search to save time */
if(++count == deg_lambda) break;
}
/* deg(lambda) unequal to number of roots => uncorrectable error detected */
if(deg_lambda != count)
{ PrintLog("Decoder problem (%d != %d) for %d sectors: ", deg_lambda, count, erasure_count);
for(i=0; i<erasure_count; i++)
{ gint64 loc = erasure_list[i];
if(loc < ndata) PrintLog("%lld ", block_idx[loc]);
else PrintLog("%lld ", RS02EccSectorIndex(lay, loc-ndata, ecc_idx));
}
PrintLog("\n");
uncorrected += erasure_count;
goto skip;
}
/* Compute err+eras evaluator poly omega(x) = syn(x)*lambda(x)
(modulo x**nroots). in index form. Also find deg(omega). */
deg_omega = deg_lambda-1;
for(i=0; i<=deg_omega; i++)
{ tmp = 0;
for(j=i; j>=0; j--)
{ if((syn[i - j] != GF_ALPHA0) && (lambda[j] != GF_ALPHA0))
tmp ^= gf_alpha_to[mod_fieldmax(syn[i - j] + lambda[j])];
}
omega[i] = gf_index_of[tmp];
}
/* Compute error values in poly-form.
num1 = omega(inv(X(l))),
num2 = inv(X(l))**(FIRST_ROOT-1) and
den = lambda_pr(inv(X(l))) all in poly-form. */
for(j=count-1; j>=0; j--)
{ num1 = 0;
for(i=deg_omega; i>=0; i--)
{ if(omega[i] != GF_ALPHA0)
num1 ^= gf_alpha_to[mod_fieldmax(omega[i] + i * root[j])];
}
num2 = gf_alpha_to[mod_fieldmax(root[j] * (RS_FIRST_ROOT - 1) + GF_FIELDMAX)];
den = 0;
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
for(i=MIN(deg_lambda, nroots-1) & ~1; i>=0; i-=2)
{ if(lambda[i+1] != GF_ALPHA0)
den ^= gf_alpha_to[mod_fieldmax(lambda[i+1] + i * root[j])];
}
/* Apply error to data */
if(num1 != 0)
{ int location = loc[j];
if(erasure_map[location] != 1) /* erasure came from CRC error */
{ int old = fc->imgBlock[location][offset];
int new = old ^ gf_alpha_to[mod_fieldmax(gf_index_of[num1] + gf_index_of[num2] + GF_FIELDMAX - gf_index_of[den])];
char *msg;
gint64 sector;
if(erasure_map[location] == 3) /* erasure came from CRC error */
{ msg = _("-> CRC-predicted error in sector %lld at byte %4d (value %02x '%c', expected %02x '%c')\n");
}
else
{ msg = _("-> Non-predicted error in sector %lld at byte %4d (value %02x '%c', expected %02x '%c')\n");
if(erasure_map[location] == 0) /* remember error location */
{ erasure_map[location] = 7;
error_count++;
}
}
if(location < ndata)
sector = block_idx[location];
else sector = RS02EccSectorIndex(lay, location-ndata, ecc_idx);
PrintCLI(msg,
sector, bi,
old, canprint(old) ? old : '.',
new, canprint(new) ? new : '.');
}
fc->imgBlock[location][offset] ^= gf_alpha_to[mod_fieldmax(gf_index_of[num1] + gf_index_of[num2] + GF_FIELDMAX - gf_index_of[den])];
}
}
}
/* Write corrected sectors back to disk
and report them */
erasure_count += error_count; /* total errors encountered */
if(erasure_count)
{ PrintCLI(_(" %3d repaired sectors: "), erasure_count);
for(i=0; i<255; i++)
{ gint64 sec;
char type='?';
int n;
if(!erasure_map[i]) continue;
switch(erasure_map[i])
{ case 1: /* dead sector */
type = 'd';
break;
case 3: /* crc error */
type = 'c';
break;
case 7: /* other (new) error */
type = 'n';
damaged_sectors++;
break;
}
if(i < ndata) { data_corr++; sec = block_idx[i]; }
else { ecc_corr++; sec = RS02EccSectorIndex(lay, i-ndata, ecc_idx); }
corrected++;
PrintCLI("%lld%c ", sec, type);
/* Write the recovered sector */
if(!LargeSeek(image->file, (gint64)(2048*sec)))
Stop(_("Failed seeking to sector %lld in image [%s]: %s"),
sec, "FW", strerror(errno));
/* augmented images can not have sizes not a multiple of 2048,
e.g. we need not to examine the ->inLast value. */
n = LargeWrite(image->file, cache_offset+fc->imgBlock[i], 2048);
if(n != 2048)
Stop(_("could not write medium sector %lld:\n%s"), sec, strerror(errno));
}
PrintCLI("\n");
}
skip:
/* Collect some damage statistics */
if(erasure_count)
damaged_eccsecs++;
if(erasure_count>worst_ecc)
worst_ecc = erasure_count;
if(erasure_count>local_plot_max)
local_plot_max = erasure_count;
/* Advance the cache pointers */
cache_sector++;
cache_offset += 2048;
/* Report progress */
percent = (1000*s)/lay->sectorsPerLayer;
if(last_percent != percent)
{
#ifndef CLI
if(Closure->guiMode)
{
RS02AddFixValues(wl, percent, local_plot_max);
local_plot_max = 0;
//if(last_corrected != corrected || last_uncorrected != uncorrected)
RS02UpdateFixResults(wl, corrected, uncorrected);
}
else
#endif
PrintProgress(_("Ecc progress: %3d.%1d%%"),percent/10,percent%10);
last_percent = percent;
}
/* Increment the block indices */
for(i=0; i<lay->ndata; i++)
block_idx[i]++;
ecc_idx++;
}
/*** Print results */
PrintProgress(_("Ecc progress: 100.0%%\n"));
if(corrected > 0) PrintLog(_("Repaired sectors: %lld (%lld data, %lld ecc)\n"),
corrected, data_corr, ecc_corr);
if(uncorrected > 0)
{ PrintLog(_("Unrepaired sectors: %lld\n"), uncorrected);
#ifndef CLI
if(Closure->guiMode)
SwitchAndSetFootline(wl->fixNotebook, 1, wl->fixFootline,
_("Image sectors could not be fully restored "
"(%lld repaired; <span %s>%lld unrepaired</span>)"),
corrected, Closure->redMarkup, uncorrected);
#endif
}
else
{ if(!corrected)
{ t=_("Good! All sectors are already present.");
PrintLog("%s\n", t);
}
else
{ t=_("Good! All sectors are repaired.");
PrintLog("%s\n", t);
}
}
if(corrected > 0 || uncorrected > 0)
PrintLog(_("Erasure counts per ecc block: avg = %.1f; worst = %d.\n"),
(double)damaged_sectors/(double)damaged_eccsecs,worst_ecc);
#ifndef CLI
if(Closure->guiMode && t)
SwitchAndSetFootline(wl->fixNotebook, 1, wl->fixFootline,
"%s %s", _("Repair results:"), t);
#endif
Verbose("\nSummary of processed sectors:\n");
Verbose("%lld damaged sectors\n", damaged_sectors);
Verbose("%lld CRC errors\n", crc_errors);
Verbose("%lld of %lld ecc blocks damaged (%lld / %lld sectors)\n",
damaged_eccblocks, 2048*lay->sectorsPerLayer,
damaged_eccsecs, lay->sectorsPerLayer);
if(data_count != lay->dataSectors)
g_printf("ONLY %lld of %lld data sectors processed\n",
(long long int)data_count, (long long int)lay->dataSectors);
else Verbose("all data sectors processed\n");
if(crc_count != lay->crcSectors)
g_printf("%lld of %lld crc sectors processed\n",
(long long int)crc_count, (long long int)lay->crcSectors);
else Verbose("all crc sectors processed\n");
if(ecc_count != lay->rsSectors)
g_printf("%lld of %lld ecc sectors processed\n",
(long long int)ecc_count, (long long int)lay->rsSectors);
else Verbose("all ecc sectors processed\n");
/*** Clean up */
fc->earlyTermination = FALSE;
terminate:
fix_cleanup((gpointer)fc);
}

205
src/rs02-includes.h Normal file
View File

@@ -0,0 +1,205 @@
/* 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/>.
*/
#ifndef RS02INCLUDES_H
#define RS02INCLUDES_H
#ifndef CLI
/* Data structs from rs02-window.c */
typedef struct
{
/*** Widgets for RS02 encoding */
GtkWidget *encHeadline;
GtkWidget *encLabel1;
GtkWidget *encPBar1;
GtkWidget *encLabel2;
GtkWidget *encPBar2;
GtkWidget *encFootline;
GtkWidget *encFootline2;
/*** Widgets for RS02 fixing */
GtkWidget *fixHeadline;
GtkWidget *fixDrawingArea;
GtkWidget *fixNotebook;
GtkWidget *fixFootline;
GtkWidget *fixFootlineBox;
GtkWidget *fixCorrected;
GtkWidget *fixProgress;
GtkWidget *fixUncorrected;
Curve *fixCurve;
/*** Widgets for RS02 verify action */
GtkWidget *cmpHeadline;
GtkWidget *cmpDrawingArea;
GtkWidget *cmpChkSumErrors;
GtkWidget *cmpMissingSectors;
Spiral *cmpSpiral;
PangoLayout *cmpLayout;
GtkWidget *cmpImageSectors;
GtkWidget *cmpImageMd5Sum;
GtkWidget *cmpEccHeaders;
GtkWidget *cmpDataSection;
GtkWidget *cmpCrcSection;
GtkWidget *cmpEccSection;
GtkWidget *cmpImageResult;
GtkWidget *cmpEccNotebook;
GtkWidget *cmpEccCreatedBy;
GtkWidget *cmpEccMethod;
GtkWidget *cmpEccRequires;
GtkWidget *cmpEccMediumSectors;
GtkWidget *cmpEcc1Name;
GtkWidget *cmpEcc2Name;
GtkWidget *cmpEcc3Name;
GtkWidget *cmpEcc1Msg;
GtkWidget *cmpEcc2Msg;
GtkWidget *cmpEcc3Msg;
GtkWidget *cmpEccResult;
/*** Widgets in the Preferences window */
GtkWidget *radio1A, *radio1B, *radio2A, *radio2B;
GtkWidget *cdButtonA, *dvdButton1A, *dvdButton2A, *bdButton1A, *bdButton2A, *bdButton3A, *bdButton4A;
GtkWidget *cdButtonB, *dvdButton1B, *dvdButton2B, *bdButton1B, *bdButton2B, *bdButton3B, *bdButton4B;
GtkWidget *cdUndoButtonA, *dvdUndoButton1A, *dvdUndoButton2A, *bdUndoButton1A, *bdUndoButton2A, *bdUndoButton3A, *bdUndoButton4A;
GtkWidget *cdUndoButtonB, *dvdUndoButton1B, *dvdUndoButton2B, *bdUndoButton1B, *bdUndoButton2B, *bdUndoButton3B, *bdUndoButton4B;
GtkWidget *cdEntryA, *dvdEntry1A, *dvdEntry2A, *bdEntry1A, *bdEntry2A, *bdEntry3A, *bdEntry4A, *otherEntryA;
GtkWidget *cdEntryB, *dvdEntry1B, *dvdEntry2B, *bdEntry1B, *bdEntry2B, *bdEntry3B, *bdEntry4B, *otherEntryB;
GtkWidget *cacheScaleA, *cacheScaleB;
LabelWithOnlineHelp *cacheLwoh;
/*** Some state vars used during fixing */
gint64 corrected;
gint64 uncorrected;
gint64 nSectors;
int eccBytes;
int dataBytes;
int percent, lastPercent;
} RS02Widgets;
#endif
/*
* local working closure for internal checksums
*/
typedef struct
{ struct _RS02Layout *lay; /* Codec data layout */
struct MD5Context md5ctxt; /* Complete image checksum (currently unused) */
struct MD5Context dataCtxt; /* md5sum of original iso image portion */
struct MD5Context crcCtxt;
struct MD5Context eccCtxt;
struct MD5Context metaCtxt;
} RS02CksumClosure;
/*
* These are exported via the Method struct
*/
#ifndef CLI
void CreateRS02EncWindow(Method*, GtkWidget*);
void CreateRS02FixWindow(Method*, GtkWidget*);
void CreateRS02PrefsPage(Method*, GtkWidget*);
void ResetRS02EncWindow(Method*);
void ResetRS02FixWindow(Method*);
void ResetRS02PrefsPage(Method*);
void ReadRS02Preferences(Method*);
void ResetRS02VerifyWindow(Method*);
void CreateRS02VerifyWindow(Method*, GtkWidget*);
#endif
/*
* These are exported (resp. only used) in ecc-rs02.c and rs02*.c
* and should not be called from somewhere else as we can not
* rely on the method plug-in being available.
* If you need similar functions in your own codec,
* please copy these functions over to the respective plug-in.
*/
/* rs02-common.c */
typedef struct _RS02Layout
{ EccHeader *eh; /* header for this image/ecc file */
guint64 dataSectors; /* number of sectors used for image data */
guint64 crcSectors; /* number of sectors needed for CRC32 sector checkums */
guint64 firstEccHeader; /* location of first ecc header */
guint64 headers; /* number of ecc header ("master block") repeats */
guint64 headerModulo; /* Modulo for header repeats */
guint64 protectedSectors; /* number of sectors protected by Reed-Solomon data */
guint64 rsSectors; /* number of sectors needed for Reed-Solomon data */
guint64 eccSectors; /* total number of sectors added to image */
guint64 sectorsPerLayer; /* sectors per RS layer (the are ndata layers) */
guint64 firstCrcLayerIndex; /* first slice containing a CRC32 data block */
guint64 mediumCapacity; /* selected medium capacity */
int nroots,ndata; /* RS encoding specification */
double redundancy; /* resulting redundancy */
} RS02Layout;
CrcBuf *RS02GetCrcBuf(Image*);
void RS02ResetCksums(Image*);
void RS02UpdateCksums(Image*, gint64, unsigned char*);
int RS02FinalizeCksums(Image*);
void RS02ReadSector(Image*, RS02Layout*, unsigned char*, gint64);
gint64 RS02EccSectorIndex(RS02Layout*, gint64, gint64);
gint64 RS02SectorIndex(RS02Layout*, gint64, gint64);
void RS02SliceIndex(RS02Layout*, gint64, gint64*, gint64*);
RS02Layout *CalcRS02Layout(Image*);
RS02Layout *RS02LayoutFromImage(Image*);
guint64 RS02ExpectedImageSize(Image*);
void WriteRS02Headers(LargeFile*, RS02Layout*, EccHeader*);
/* rs02-create.c */
void RS02Create(void);
/* rs02-fix.c */
void RS02Fix(Image*);
/* rs02-recognize.c */
int RS02Recognize(Image*);
#ifndef CLI
/* rs02-window.c */
void RS02AddFixValues(RS02Widgets*, int, int);
void RS02SetFixMaxValues(RS02Widgets*, int, int, gint64);
void RS02UpdateFixResults(RS02Widgets*, gint64, gint64);
#endif
/* rs02-verify.c */
#define VERIFY_IMAGE_SEGMENTS 1000
void RS02Verify(Image*);
#endif

315
src/rs02-recognize.c Normal file
View File

@@ -0,0 +1,315 @@
/* 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 "udf.h"
/***
*** Recognize RS02 error correction data in the image
***/
#ifndef CLI
/*
* Dialog components for disabling RS02 search
*/
static void no_rs02_cb(GtkWidget *widget, gpointer data)
{ int state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
Closure->examineRS02 = !state;
UpdatePrefsExhaustiveSearch();
}
static void insert_buttons(GtkDialog *dialog)
{ GtkWidget *check,*align;
gtk_dialog_add_buttons(dialog,
_utf("Skip RS02 test"), 1,
_utf("Continue searching"), 0, NULL);
align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), align, FALSE, FALSE, 0);
check = gtk_check_button_new_with_label(_utf("Disable RS02 initialization in the preferences"));
gtk_container_add(GTK_CONTAINER(align), check);
gtk_container_set_border_width(GTK_CONTAINER(align), 10);
g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(no_rs02_cb), NULL);
gtk_widget_show(align);
gtk_widget_show(check);
}
#endif
/*
* See whether a given header is valid for RS02
*/
enum { HEADER_FOUND, TRY_NEXT_HEADER, TRY_NEXT_MODULO};
static int try_sector(Image *image, gint64 pos, EccHeader **ehptr, unsigned char *secbuf)
{ EccHeader *eh;
unsigned char fingerprint[16];
guint32 recorded_crc;
guint32 real_crc;
int fp_read = 0;
/* Try reading the sector */
Verbose("try_sector: trying sector %lld\n", pos);
if(ImageReadSectors(image, secbuf, pos, 2) != 2)
{ Verbose("try_sector: read error, trying next header\n");
return TRY_NEXT_HEADER;
}
eh = (EccHeader*)secbuf;
/* See if the magic cookie is there. If not, searching within
this modulo makes no sense for write-once media.
However if the medium is rewriteable, there might be trash
data behind the image. So finding an invalid sector
does not imply there is not RS02 data present.
Workaround for mistakenly recognizing RS03 headers added. */
if(strncmp((char*)eh->cookie, "*dvdisaster*RS02", 16))
{ if(image->type == IMAGE_MEDIUM && image->dh->rewriteable)
{ Verbose("try_sector: no cookie but rewriteable medium: skipping header\n");
return TRY_NEXT_HEADER;
}
else
{ Verbose("try_sector: no cookie, skipping current modulo\n");
return TRY_NEXT_MODULO;
}
}
else Verbose("try_sector: header at %lld: magic cookie found\n", (long long int)pos);
/* Calculate CRC */
recorded_crc = eh->selfCRC;
#ifdef HAVE_BIG_ENDIAN
eh->selfCRC = 0x47504c00;
#else
eh->selfCRC = 0x4c5047;
#endif
real_crc = Crc32((unsigned char*)eh, sizeof(EccHeader));
if(real_crc != recorded_crc)
{ Verbose("try_sector: CRC failed, skipping header\n");
return TRY_NEXT_HEADER;
}
eh = g_malloc(sizeof(EccHeader));
memcpy(eh, secbuf, sizeof(EccHeader));
#ifdef HAVE_BIG_ENDIAN
SwapEccHeaderBytes(eh);
#endif
eh->selfCRC = recorded_crc;
Verbose("try_sector: CRC okay\n");
/* Compare medium fingerprint with that recorded in Ecc header.
Note that GetImageFingerprint provides internal caching;
the sector is not read repeatedly */
fp_read = GetImageFingerprint(image, fingerprint, eh->fpSector);
if(!fp_read) /* be optimistic if fingerprint sector is unreadable */
{ *ehptr = eh;
Verbose("try_sector: read error in fingerprint sector\n");
return HEADER_FOUND;
}
if(!memcmp(fingerprint, eh->mediumFP, 16)) /* good fingerprint */
{ *ehptr = eh;
Verbose("try_sector: fingerprint okay, header good\n");
return HEADER_FOUND;
}
/* This might be a header from a larger previous session.
Discard it and continue */
Verbose("try_sector: fingerprint mismatch, skipping sector\n");
g_free(eh);
return TRY_NEXT_HEADER;
}
/*
* RS02 header search
*/
int RS02Recognize(Image *image)
{ AlignedBuffer *ab = CreateAlignedBuffer(4096);
Bitmap *try_next_header, *try_next_modulo;
gint64 pos;
gint64 header_modulo;
int read_count = 0;
int answered_continue = FALSE;
gint64 max_sectors = 0;
switch(image->type)
{ case IMAGE_FILE:
Verbose("RS02Recognize: file %s\n", image->file->path);
break;
case IMAGE_MEDIUM:
Verbose("RS02Recognize: medium %s\n", image->dh->device);
break;
default:
Verbose("RS02Recognize: unknown type %d\n", image->type);
break;
}
/*** Quick search at fixed offsets relative to ISO filesystem */
if(image->isoInfo)
{ gint64 iso_size = image->isoInfo->volumeSize;
/* Iso size is correct; look for root sector at +2 */
if(try_sector(image, iso_size, &image->eccHeader, ab->buf) == HEADER_FOUND)
{ Verbose("Root sector search at +0 successful\n");
FreeAlignedBuffer(ab);
return TRUE;
}
/* Strange stuff. Sometimes the iso size is increased by 150
sectors by the burning software. */
if(try_sector(image, iso_size-150, &image->eccHeader, ab->buf) == HEADER_FOUND)
{ Verbose("Root sector search at -150 successful\n");
FreeAlignedBuffer(ab);
return TRUE;
}
}
/*** No exhaustive search on optical media unless explicitly requested. */
if(!Closure->examineRS02 && image->type == IMAGE_MEDIUM)
{ Verbose("RS02Recognize: skipping exhaustive RS02 search\n");
FreeAlignedBuffer(ab);
return FALSE;
}
/*** Normal exhaustive search */
header_modulo = (gint64)1<<62;
switch(image->type)
{ case IMAGE_FILE:
max_sectors = image->file->size/2048;
break;
case IMAGE_MEDIUM:
max_sectors = MAX(image->dh->readCapacity, image->dh->userAreaSize);
break;
}
if(max_sectors == 0)
Stop("max_sectors uninitialized");
try_next_header = CreateBitmap0(max_sectors);
try_next_modulo = CreateBitmap0(max_sectors);
if(image->type == IMAGE_MEDIUM)
Verbose("Medium rewriteable: %s\n", image->dh->rewriteable ? "TRUE" : "FALSE");
/*** Search for the headers */
if(image->type == IMAGE_FILE) /* Seeking on hard disc is cheap */
answered_continue = TRUE;
while(header_modulo >= 32)
{ pos = max_sectors & ~(header_modulo - 1);
Verbose("FindHeaderInMedium: Trying modulo %lld\n", header_modulo);
while(pos > 0)
{ int result;
#ifndef CLI
if(Closure->stopActions)
goto bail_out;
#endif
if(GetBit(try_next_header, pos))
{ Verbose("Sector %lld cached; skipping\n", pos);
goto check_next_header;
}
if(GetBit(try_next_modulo, pos))
{ Verbose("Sector %lld cached; skipping modulo\n", pos);
goto check_next_modulo;
}
result = try_sector(image, pos, &image->eccHeader, ab->buf);
switch(result)
{ case TRY_NEXT_HEADER:
SetBit(try_next_header, pos);
read_count++;
if(!answered_continue && read_count > 5)
{
#ifndef CLI
if(Closure->guiMode)
{ int answer = ModalDialog(GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, insert_buttons,
_("Faster medium initialization\n\n"
"Searching this medium for error correction data may take a long time.\n"
"Press \"Skip RS02 test\" if you are certain that this medium was\n"
"not augmented with RS02 error correction data."));
if(answer) goto bail_out;
answered_continue = TRUE;
}
#endif
}
goto check_next_header;
case TRY_NEXT_MODULO:
SetBit(try_next_modulo, pos);
goto check_next_modulo;
case HEADER_FOUND:
FreeBitmap(try_next_header);
FreeBitmap(try_next_modulo);
FreeAlignedBuffer(ab);
return TRUE;
}
check_next_header:
pos -= header_modulo;
}
check_next_modulo:
header_modulo >>= 1;
}
#ifndef CLI
bail_out:
#endif
FreeBitmap(try_next_header);
FreeBitmap(try_next_modulo);
FreeAlignedBuffer(ab);
return FALSE;
}

1202
src/rs02-verify.c Normal file

File diff suppressed because it is too large Load Diff

1382
src/rs02-window.c Normal file

File diff suppressed because it is too large Load Diff

685
src/rs03-common.c Normal file
View File

@@ -0,0 +1,685 @@
/* 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 "rs03-includes.h"
/***
*** Read and buffer CRC information from RS03 error correction data
***/
CrcBuf *RS03GetCrcBuf(Image *image)
{ RS03CksumClosure *csc;
CrcBuf *cbuf;
RS03Layout *lay;
EccHeader *eh;
AlignedBuffer *ab = CreateAlignedBuffer(2048);
guint32 *crc_buf = (guint32*)ab->buf;
gint64 block_idx[256];
gint64 crc_sector,s;
int i;
int crc_valid = 1;
/* Allocate buffer for ascending sector order CRCs */
if(image->eccFileHeader)
{ eh = image->eccFileHeader;
csc = (RS03CksumClosure*)image->eccFileMethod->ckSumClosure;
lay = CalcRS03Layout(image, ECC_FILE);
cbuf = CreateCrcBuf(image);
}
else
{ eh = image->eccHeader;
csc = (RS03CksumClosure*)image->eccMethod->ckSumClosure;
lay = CalcRS03Layout(image, ECC_IMAGE);
cbuf = CreateCrcBuf(image);
cbuf->coveredSectors=lay->firstCrcPos;
}
csc->signatureErrors=0;
if(csc->lay) g_free(csc->lay);
csc->lay = lay;
/* First sector containing crc data */
crc_sector = lay->firstCrcPos;
/* Initialize ecc block index pointers.
Note that CRC blocks are shifted by one
(each ECC block contains the CRC for the next ECC block) */
for(s=0, i=0; i<lay->ndata; s+=lay->sectorsPerLayer, i++)
block_idx[i] = s+1;
/* Cycle through the ecc blocks.
Each ecc block contains the CRCs for the following ecc block;
these are rearranged in ascending sector order. */
for(s=0; s<lay->sectorsPerLayer; s++)
{ int err;
/* Get CRC sector for current ecc block */
if(image->eccFile && image->eccFileState == ECCFILE_PRESENT) /* read from separate ecc file */
{ if(!LargeSeek(image->eccFile, (gint64)(2048*(lay->firstCrcPos+s))))
CreateMissingSector(ab->buf, crc_sector, image->imageFP, FINGERPRINT_SECTOR, NULL);
else
if(LargeRead(image->eccFile, ab->buf, 2048) != 2048)
CreateMissingSector(ab->buf, crc_sector, image->imageFP, FINGERPRINT_SECTOR, NULL);
}
else /* read from augmented image */
{ int n = ImageReadSectors(image, ab->buf, crc_sector, 1);
if(n!=1)
CreateMissingSector(ab->buf, crc_sector, image->imageFP, FINGERPRINT_SECTOR, NULL);
}
err = CheckForMissingSector(ab->buf, crc_sector, eh->mediumFP, eh->fpSector);
#if 0
if(err != SECTOR_PRESENT)
{ int source_type;
if(image->eccFile && image->eccFileState == ECCFILE_PRESENT)
source_type = SOURCE_ECCFILE;
else source_type = image->type == IMAGE_FILE ? SOURCE_IMAGE : SOURCE_MEDIUM;
if(!unrecoverable_sectors && err != SECTOR_MISSING)
PrintLog("\n");
ExplainMissingSector(ab->buf, crc_sector, err, source_type, &unrecoverable_sectors);
}
#endif
crc_sector++;
crc_valid = (err == SECTOR_PRESENT);
/* Check the CrcBlock data structure */
if(crc_valid)
{ CrcBlock *cb = (CrcBlock*)ab->buf;
if( memcmp(cb->cookie, "*dvdisaster*", 12)
||memcmp(cb->method, "RS03", 4))
{ crc_valid = FALSE;
csc->signatureErrors++;
}
else
{ guint32 recorded_crc = cb->selfCRC;
guint32 real_crc;
#ifdef HAVE_BIG_ENDIAN
cb->selfCRC = 0x47504c00;
#else
cb->selfCRC = 0x4c5047;
#endif
real_crc = Crc32((unsigned char*)cb, 2048);
if(real_crc != recorded_crc)
{ crc_valid = FALSE;
csc->signatureErrors++;
}
}
}
/* Go through all data sectors of current ecc block;
distribute the CRC values */
for(i=0; i<lay->ndata-1; i++)
{
/* CRC sums for the first ecc block are contained in the last
CRC sector. Wrap the block_idx accordingly. */
if(s == lay->sectorsPerLayer-1)
block_idx[i] = i*lay->sectorsPerLayer;
/* Sort crc into appropriate place if CRC block is valid */
if(crc_valid && block_idx[i] < cbuf->crcSize) // Cave padding sectors!
{ cbuf->crcbuf[block_idx[i]] = crc_buf[i];
SetBit(cbuf->valid,block_idx[i]);
}
block_idx[i]++;
}
}
FreeAlignedBuffer(ab);
/* The ecc header records only the md5 sum of the data portion (if at all),
but not that of the whole image, so flag the md5 sums as missing. */
cbuf->md5State = MD5_BUILDING;
if(eh->methodFlags[0] & MFLAG_DATA_MD5)
{ memcpy(cbuf->dataMD5sum, eh->mediumSum, 16);
cbuf->md5State |= MD5_DATA_COMPLETE;
}
return cbuf;
}
/***
*** Read one or more image sectors from the .iso file.
***/
void RS03ReadSectors(Image *image, RS03Layout *lay, unsigned char *buf,
gint64 layer, gint64 layer_sector, gint64 how_many, int flags)
{ LargeFile *target_file = NULL;
gint64 start_sector=0;
gint64 stop_sector=0;
gint64 byte_size = how_many * 2048;
gint64 img_file_sector_size;
gint64 ecc_file_sector_size=0;
gint64 target_file_sector_size;
int in_last;
gint64 n;
if(layer < 0 || layer > 255)
Stop("RS03ReadSectors: layer %lld out of range 0 .. 255\n", layer);
if(layer_sector < 0 || layer_sector >= lay->sectorsPerLayer)
Stop("RS03ReadSectors: offset %lld out of range 0 .. %lld)\n",
layer_sector, lay->sectorsPerLayer-1);
/* "Image" file size may not be a multiple of 2048 */
in_last = image->file->size % 2048;
img_file_sector_size = image->file->size/2048;
if(in_last) img_file_sector_size++;
/* Ignore trailing garbage in the image file */
if(lay->target == ECC_FILE)
{ /* If the image is longer as expected in the ecc file,
truncate sizes to the values recorded in the ecc file. */
if(img_file_sector_size > lay->dataSectors)
{ img_file_sector_size = lay->dataSectors;
in_last = lay->eh->inLast;
}
/* If the image has the right sector size, but contains
a few bytes more in this sector as expected, truncate
the value. However if the last sector contains some
bytes less than expected, keep the smaller value
to prevent reading past the image later on. */
if( img_file_sector_size == lay->dataSectors
&& in_last > lay->eh->inLast)
{ in_last = lay->eh->inLast;
}
/* Ecc file size is currently considered to be a multiple
of the sector size (which is normally the case).
If the ecc file is tuncated by a few bytes,
the last incomplete sector is ignored. */
ecc_file_sector_size = image->eccFile->size/2048;
}
/* Read out of the data layer */
if(layer < lay->ndata-1)
{ if(!(flags & RS03_READ_DATA))
Stop("RS03ReadSectors: trying to read data layer, but flag not set\n");
start_sector = layer*lay->sectorsPerLayer + layer_sector;
stop_sector = start_sector + how_many - 1;
if(stop_sector >= (layer+1)*lay->sectorsPerLayer)
Stop("RS03ReadSectors: range %lld..%lld crosses layer boundary\n",
start_sector, stop_sector);
target_file = image->file;
}
/* Read out of the crc layer */
if(layer == lay->ndata-1)
{ if(!(flags & RS03_READ_CRC))
Stop("RS03ReadSectors: trying to read crc layer, but flag not set\n");
start_sector = lay->firstCrcPos + layer_sector;
stop_sector = start_sector + how_many - 1;
if(lay->target == ECC_IMAGE)
target_file = image->file;
else target_file = image->eccFile;
}
/*** Read out of the ecc layers */
target_file_sector_size = img_file_sector_size;
if(layer >= lay->ndata)
{ if(!(flags & RS03_READ_ECC))
Stop("RS03ReadSectors: trying to read ecc layer, but flag not set\n");
start_sector = lay->firstEccPos + (layer-lay->ndata)*lay->sectorsPerLayer + layer_sector;
stop_sector = start_sector + how_many - 1;
if(lay->target == ECC_IMAGE)
target_file = image->file;
else
{ target_file = image->eccFile;
target_file_sector_size = ecc_file_sector_size;
in_last = 0; /* Ecc file size if always a multiple of 2048 */
}
}
/* Reading beyond the image returns
- dead sectors if the image was truncated
- padding sectors if the real end of the image is exceeded.
Create them in memory; shorten read range accordingly */
if(stop_sector >= target_file_sector_size)
{ unsigned char *bufptr = buf;
char *volume_label = NULL;
guint64 expected_sectors;
#if 0 //FIXME
if(rc->image->isoInfo && rc->image->isoInfo->volumeLabel[0])
rc->volumeLabel = g_strdup(rc->image->isoInfo->volumeLabel);
#endif
if(lay->target == ECC_FILE)
expected_sectors = lay->dataSectors;
else
expected_sectors = lay->totalSectors;
for(n=start_sector; n<=stop_sector; n++)
{
if(n>=target_file_sector_size)
{ guint8 *fp = lay->eh ? lay->eh->mediumFP : NULL;
if(n>=expected_sectors)
{ CreatePaddingSector(bufptr, n, fp, FINGERPRINT_SECTOR);
}
else
{ CreateMissingSector(bufptr, n, fp, FINGERPRINT_SECTOR, volume_label);
}
byte_size -= 2048;
}
bufptr += 2048;
}
}
if(byte_size<=0)
return;
/* Image with ecc files may have an incomplete last sector.
Deal with it appropriately. */
if(lay->target == ECC_FILE && in_last)
{ if(start_sector <= target_file_sector_size-1
&& target_file_sector_size-1 <= stop_sector)
{
memset(buf, 0, byte_size);
byte_size = byte_size - 2048 + in_last;
}
}
/* All sectors are consecutively readable in image case */
if(!LargeSeek(target_file, (gint64)(2048*start_sector)))
Stop(_("Failed seeking to sector %lld in image: %s"),
start_sector, strerror(errno));
n = LargeRead(target_file, buf, byte_size);
if(n != byte_size)
Stop(_("Failed reading sector %lld in image: %s"),
start_sector, strerror(errno));
}
/***
*** Calculate position of n-th sector of the given layer in the image.
***/
gint64 RS03SectorIndex(RS03Layout *lay, gint64 layer, gint64 n)
{
if(lay->target == ECC_IMAGE)
return layer*lay->sectorsPerLayer+n;
/* Image portion in ecc file case */
if(layer < lay->ndata-1)
return layer*lay->sectorsPerLayer+n;
/* Layers located in the ecc file */
if(layer == lay->ndata-1) /* CRC layer */
return lay->firstCrcPos + n;
/* Ecc layers */
return lay->firstEccPos + (layer-lay->ndata)*lay->sectorsPerLayer + n;
}
/***
*** Calculation of the image layout
***/
static int get_roots(gint64 data_sectors, gint64 medium_capacity)
{ gint64 sectors_per_layer = medium_capacity/GF_FIELDMAX;
int ndata = (data_sectors + 2 +sectors_per_layer - 1) / sectors_per_layer;
return GF_FIELDMAX - ndata - 1;
}
static gint64 ecc_file_size(gint64 sectors, int nr)
{ int nd = GF_FIELDMAX - nr;
gint64 bytesize;
bytesize = 4096 + 2048*(nr+1)*((sectors+nd-1)/nd);
return (bytesize+0xfffff)/0x100000; /* size in MiB */
}
RS03Layout *CalcRS03Layout(Image *image, int target)
{ RS03Layout *lay = g_malloc0(sizeof(RS03Layout));
/* See if layout has already been cached in the image */
if(image->cachedLayout)
{ RS03Layout *ptr = (RS03Layout*)image->cachedLayout;
if(strncmp((char*)(ptr->eh->method), "RS03", 4))
{ Verbose("CalcRS03Layout(): removed cached layout from other codec\n");
g_free(image->cachedLayout);
}
else
{ if(((RS03Layout*)image->cachedLayout)->target != target)
{ Verbose("CalcRS03Layout(): removed cached layout from RS03, wrong target\n");
g_free(image->cachedLayout);
}
{ Verbose("CalcRS03Layout(): returning cached layout (%s)\n",
((RS03Layout*)image->cachedLayout)->target == ECC_FILE ? "file" : "augmented");
memcpy(lay, image->cachedLayout, sizeof(RS03Layout));
return lay;
}
}
}
lay->target = target;
/* We are going to create an error correction file */
if(target == ECC_FILE)
{ guint64 filesize;
int n_roots = 0;
char last = 0;
lay->eh = image->eccFileHeader;
if(lay->eh) /* Header given; get number of roots from there */
{ n_roots = lay->eh->eccBytes;
lay->dataSectors = uchar_to_gint64(lay->eh->sectors);
lay->inLast = lay->eh->inLast;
}
else /* Calculate number of roots */
{
/* Calculate image size in sectors */
if(!LargeStat(Closure->imageName, &filesize))
Stop(_("Image file %s not present."),Closure->imageName);
CalcSectors(filesize, &lay->dataSectors, &lay->inLast);
/* Calculate wanted redundancy from Closure->redundancy */
if(Closure->redundancy) /* get last char of redundancy parameter */
{ int len = strlen(Closure->redundancy);
if(len) last = Closure->redundancy[len-1];
}
switch(last)
{ case '%':
{ double p = atof(Closure->redundancy);
if(p<3.2 || p>200.0)
Stop(_("Redundancy %4.1f%% out of useful range [3.2%%..200%%]"),p);
n_roots = (int)round((GF_FIELDMAX*p) / (100.0+p));
break;
}
case 'm':
{ gint64 ecc_size;
ecc_size = strtoll(Closure->redundancy, NULL, 10);
if( ecc_size < ecc_file_size(lay->dataSectors, 8)
|| ecc_size > ecc_file_size(lay->dataSectors, 170))
Stop(_("Ecc file size %lldm out of useful range [%lld .. %lld]"),
ecc_size,
ecc_file_size(lay->dataSectors, 8),
ecc_file_size(lay->dataSectors, 170));
for(n_roots=170; n_roots>8; n_roots--)
if(ecc_size >= ecc_file_size(lay->dataSectors, n_roots))
break;
break;
}
default:
if(!Closure->redundancy || !strcmp(Closure->redundancy, "normal")) n_roots = 32;
else if(!strcmp(Closure->redundancy, "high")) n_roots = 64;
else n_roots = atoi(Closure->redundancy);
break;
}
}
if(n_roots < 8 || n_roots > 170)
Stop(_("Redundancy %d out of useful range [8..170]."),n_roots);
/* Now we have settled for the number of roots,
so calculate the layout. */
lay->dataPadding = 0; /* always zero for ecc files */
lay->nroots = n_roots;
lay->ndata = GF_FIELDMAX - n_roots;
lay->sectorsPerLayer = (lay->dataSectors + lay->ndata - 2)/(lay->ndata-1);
lay->totalSectors = lay->dataSectors + 2 + (lay->nroots+1)*lay->sectorsPerLayer;
lay->mediumCapacity = 0; /* unused for ecc files */
lay->eccHeaderPos = 0;
lay->firstCrcPos = 2;
lay->firstEccPos = lay->firstCrcPos + lay->sectorsPerLayer;
lay->redundancy = ((double)lay->nroots*100.0)/(double)lay->ndata;
}
/* We are going to augment an image file */
if(target == ECC_IMAGE)
{ gint64 dataSectors;
/* Determine smallest possible medium format which
can hold the image plus at least 8 roots for ecc.
Overriding the medium size via --debug is not recommended
as it may render the image irrecoverable in the error case. */
lay->eh = image->eccHeader;
if(lay->eh)
{ dataSectors = uchar_to_gint64(lay->eh->sectors);
}
else
{ dataSectors = image->sectorSize;
if(Closure->debugMode && Closure->mediumSize)
{ if(dataSectors >= Closure->mediumSize)
Stop(_("Medium size smaller than image size (%lld < %lld)"), Closure->mediumSize, dataSectors);
lay->mediumCapacity = Closure->mediumSize;
}
else
{ if(get_roots(dataSectors, CDR_SIZE) >= 8)
lay->mediumCapacity = CDR_SIZE; /* CDR */
else if(get_roots(dataSectors, DVD_SL_SIZE) >= 8)
lay->mediumCapacity = DVD_SL_SIZE; /* Single layered DVD */
else if(get_roots(dataSectors, DVD_DL_SIZE) >= 8)
lay->mediumCapacity = DVD_DL_SIZE; /* Double layered DVD */
else if(get_roots(dataSectors, BD_SL_SIZE) >= 8) {
/* Single layered BD */
if (Closure->noBdrDefectManagement)
lay->mediumCapacity = BD_SL_SIZE_NODM;
else
lay->mediumCapacity = BD_SL_SIZE;
}
else if(get_roots(dataSectors, BD_DL_SIZE) >= 8) {
/* Double layered BD */
if (Closure->noBdrDefectManagement)
lay->mediumCapacity = BD_DL_SIZE_NODM;
else
lay->mediumCapacity = BD_DL_SIZE;
}
else if(get_roots(dataSectors, BDXL_TL_SIZE) >= 8) {
/* Triple layered BDXL */
if (Closure->noBdrDefectManagement)
lay->mediumCapacity = BDXL_TL_SIZE_NODM;
else
lay->mediumCapacity = BDXL_TL_SIZE;
}
else {
/* Quadruple layered BDXL */
if (Closure->noBdrDefectManagement)
lay->mediumCapacity = BDXL_QL_SIZE_NODM;
else
lay->mediumCapacity = BDXL_QL_SIZE;
}
}
}
/* Calculate the image layout */
if(lay->eh) lay->sectorsPerLayer = lay->eh->sectorsPerLayer;
else lay->sectorsPerLayer = lay->mediumCapacity/GF_FIELDMAX;
lay->dataSectors = dataSectors;
lay->totalSectors = GF_FIELDMAX*lay->sectorsPerLayer;
lay->ndata = (dataSectors + 2 + lay->sectorsPerLayer - 1) / lay->sectorsPerLayer;
if(lay->ndata < 84) /* we clip redundancy at 170 roots */
{ Verbose("Redundancy clipped from %d to %d\n", lay->ndata, 84);
lay->ndata = 84;
}
lay->dataPadding = lay->ndata * lay->sectorsPerLayer - lay->dataSectors - 2;
lay->ndata++; /* CRC layer is also protected and counted as part of the data portion */
lay->nroots = GF_FIELDMAX-lay->ndata;
lay->redundancy = ((double)lay->nroots*100.0)/(double)lay->ndata;
lay->eccHeaderPos = lay->dataSectors;
lay->firstCrcPos = (lay->ndata-1)*lay->sectorsPerLayer;
lay->firstEccPos = lay->firstCrcPos + lay->sectorsPerLayer;
}
/* Debugging output */
if(target == ECC_FILE)
Verbose("Calculated layout for RS03 file:\n");
else Verbose("Calculated layout for RS03 image:\n");
Verbose("data sectors = %lld\n", lay->dataSectors);
Verbose("data padding = %lld\n", lay->dataPadding);
Verbose("layer size = %lld\n", lay->sectorsPerLayer);
Verbose("total sectors = %lld\n", lay->totalSectors);
Verbose("medium capacity = %lld\n", lay->mediumCapacity);
Verbose("header position = %lld\n", lay->eccHeaderPos);
Verbose("first CRC sector = %lld\n", lay->firstCrcPos);
Verbose("first ECC sector = %lld\n", lay->firstEccPos);
Verbose("ndata = %d\n", lay->ndata);
Verbose("nroots = %d (%4.1f%%)\n", lay->nroots, lay->redundancy);
Verbose("\n");
image->cachedLayout = g_malloc(sizeof(RS03Layout));
memcpy(image->cachedLayout, lay, sizeof(RS03Layout));
return lay;
}
/*
* Determine expected size of image.
* In case of ecc files, only the iso image size is reported.
*/
guint64 RS03ExpectedImageSize(Image *image)
{ EccHeader *eh=image->eccHeader;
guint64 size = 0;
if(!eh && image->eccFileHeader)
eh=image->eccFileHeader;
if(!eh) return 0;
if(eh->methodFlags[0] & MFLAG_ECC_FILE)
size = uchar_to_gint64(eh->sectors); /* ecc file */
else
size = 255*eh->sectorsPerLayer; /* augmented image */
return size;
}
/***
*** Write the RS03 header into the image.
***/
void WriteRS03Header(LargeFile *file, RS03Layout *lay, EccHeader *eh)
{ int n;
if(!LargeSeek(file, 2048*lay->eccHeaderPos))
Stop(_("Failed seeking to ecc header at %lld: %s\n"), lay->eccHeaderPos, strerror(errno));
n = LargeWrite(file, eh, sizeof(EccHeader));
if(n != sizeof(EccHeader))
Stop(_("Failed writing ecc header at %lld: %s\n"), lay->eccHeaderPos, strerror(errno));
}
/***
*** Reconstruct the RS03 header from a CRC block
***/
void ReconstructRS03Header(EccHeader *eh, CrcBlock *cb)
{ int i;
#ifdef HAVE_BIG_ENDIAN
SwapCrcBlockBytes(cb);
#endif
memset(eh, 0, sizeof(EccHeader));
memcpy(eh->cookie, "*dvdisaster*", 12);
memcpy(eh->method, "RS03", 4);
for(i=0; i<4; i++)
eh->methodFlags[i] = cb->methodFlags[i];
memcpy(eh->mediumFP, cb->mediumFP, 16);
memcpy(eh->mediumSum, cb->mediumSum, 16);
gint64_to_uchar(eh->sectors, cb->dataSectors);
eh->dataBytes = cb->dataBytes;
eh->eccBytes = cb->eccBytes;
eh->creatorVersion = cb->creatorVersion;
eh->neededVersion = cb->neededVersion;
eh->fpSector = cb->fpSector;
eh->inLast = cb->inLast;
eh->sectorsPerLayer = cb->sectorsPerLayer;
eh->selfCRC = 0x4c5047;
#ifdef HAVE_BIG_ENDIAN
eh->selfCRC = 0x47504c00;
#endif
eh->selfCRC = Crc32((unsigned char*)eh, 4096);
}

1493
src/rs03-create.c Normal file

File diff suppressed because it is too large Load Diff

1003
src/rs03-fix.c Normal file

File diff suppressed because it is too large Load Diff

225
src/rs03-includes.h Normal file
View File

@@ -0,0 +1,225 @@
/* 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/>.
*/
#ifndef RS03INCLUDES_H
#define RS03INCLUDES_H
#ifndef CLI
/* Data structs from rs03-window.c */
typedef struct
{
/*** Widgets for RS03 encoding */
GtkWidget *encHeadline;
GtkWidget *encLabel1;
GtkWidget *encPBar1;
GtkWidget *encLabel2;
GtkWidget *encPBar2;
GtkWidget *encLabel3;
GtkWidget *encThreads;
GtkWidget *encLabel4;
GtkWidget *encPerformance;
GtkWidget *encLabel5;
GtkWidget *encBottleneck;
GtkWidget *encFootline;
GtkWidget *encFootline2;
/*** Widgets for RS03 fixing */
GtkWidget *fixHeadline;
GtkWidget *fixDrawingArea;
GtkWidget *fixNotebook;
GtkWidget *fixFootline;
GtkWidget *fixFootlineBox;
GtkWidget *fixCorrected;
GtkWidget *fixProgress;
GtkWidget *fixUncorrected;
Curve *fixCurve;
/*** Widgets for RS03 verify action */
GtkWidget *cmpHeadline;
GtkWidget *cmpDrawingArea;
GtkWidget *cmpChkSumErrors;
GtkWidget *cmpMissingSectors;
Spiral *cmpSpiral;
PangoLayout *cmpLayout;
GtkWidget *cmpImageNotebook;
GtkWidget *cmpImageSectors;
GtkWidget *cmpImageMd5Sum;
GtkWidget *cmpDataSection;
GtkWidget *cmpCrcSection;
GtkWidget *cmpEccSection;
GtkWidget *cmpImageErasure;
GtkWidget *cmpImagePrognosis;
GtkWidget *cmpImageErasureCnt;
GtkWidget *cmpImagePrognosisMsg;
GtkWidget *cmpImageResult;
GtkWidget *cmpEccCreatedBy;
GtkWidget *cmpEccMethod;
GtkWidget *cmpEccType;
GtkWidget *cmpEccRequires;
GtkWidget *cmpEccDataCrc;
GtkWidget *cmpEccDataCrcVal;
GtkWidget *cmpEccFingerprint;
GtkWidget *cmpEccResult;
GtkWidget *cmpEccSynLabel;
GtkWidget *cmpEccSyndromes;
/*** Widgets in the Preferences window */
GtkWidget *eccFileA, *eccFileB;
GtkWidget *eccImageA, *eccImageB;
GtkWidget *radio1A,*radio2A,*radio3A,*radio4A;
GtkWidget *radio1B,*radio2B,*radio3B,*radio4B;
GtkWidget *radio4LabelA, *radio4LabelB;
GtkWidget *redundancyNotebook;
GtkWidget *redundancyScaleA, *redundancyScaleB;
GtkWidget *redundancySpinA, *redundancySpinB;
GtkWidget *prefetchScaleA, *prefetchScaleB;
GtkWidget *threadsScaleA, *threadsScaleB;
GtkWidget *eaRadio1A,*eaRadio2A,*eaRadio3A,*eaRadio4A;
GtkWidget *eaRadio1B,*eaRadio2B,*eaRadio3B,*eaRadio4B;
GtkWidget *ioRadio1A,*ioRadio2A;
GtkWidget *ioRadio1B,*ioRadio2B;
LabelWithOnlineHelp *prefetchLwoh;
LabelWithOnlineHelp *threadsLwoh;
/*** Some state vars used during fixing */
gint64 corrected;
gint64 uncorrected;
gint64 nSectors;
int eccBytes;
int dataBytes;
int percent, lastPercent;
} RS03Widgets;
#endif
/*
* local working closure for internal checksums
*/
typedef struct
{ struct _RS03Layout *lay; /* Codec data layout */
guint64 signatureErrors; /* number of Checksum with invalid sigs */
} RS03CksumClosure;
/*
* These are exported via the Method struct
*/
#ifndef CLI
void CreateRS03EncWindow(Method*, GtkWidget*);
void CreateRS03FixWindow(Method*, GtkWidget*);
void CreateRS03PrefsPage(Method*, GtkWidget*);
void ResetRS03EncWindow(Method*);
void ResetRS03FixWindow(Method*);
void ResetRS03PrefsPage(Method*);
void ReadRS03Preferences(Method*);
void ResetRS03VerifyWindow(Method*);
void CreateRS03VerifyWindow(Method*, GtkWidget*);
#endif
/*
* These are exported (resp. only used) in ecc-rs03.c and rs03*.c
* and should not be called from somewhere else as we can not
* rely on the method plug-in being available.
* If you need similar functions in your own codec,
* please copy these functions over to the respective plug-in.
*/
/* rs03-common.c */
typedef struct _RS03Layout
{ EccHeader *eh; /* header for this image/ecc file */
guint64 dataSectors; /* number of sectors used for image data */
guint64 dataPadding; /* padding sectors in last data layer */
guint64 totalSectors; /* data+padding+header+crc+ecc */
guint64 sectorsPerLayer; /* sectors per RS layer (the are ndata layers) */
guint64 mediumCapacity; /* selected medium capacity */
guint64 eccHeaderPos; /* location of first ecc header */
guint64 firstCrcPos; /* location of first crc sector */
guint64 firstEccPos; /* location of first ecc sector */
int nroots,ndata; /* RS encoding specification */
int inLast; /* contents of last image file sector */
double redundancy; /* resulting redundancy */
int target; /* 0: ecc file; 1: augmented image */
} RS03Layout;
#define RS03_READ_NOTHING 0x00
#define RS03_READ_DATA 0x01
#define RS03_READ_CRC 0x02
#define RS03_READ_ECC 0x04
#define RS03_READ_ALL 0x07
CrcBuf *RS03GetCrcBuf(Image *image);
void RS03ReadSectors(Image*, RS03Layout*, unsigned char*, gint64, gint64, gint64, int);
gint64 RS03SectorIndex(RS03Layout*, gint64, gint64);
RS03Layout *CalcRS03Layout(Image*, int);
guint64 RS03ExpectedImageSize(Image*);
void WriteRS03Header(LargeFile*, RS03Layout*, EccHeader*);
void ReconstructRS03Header(EccHeader*, CrcBlock*);
/* rs03-create.c */
void RS03Create(void);
/* rs03-fix.c */
void RS03Fix(Image*);
/* rs03-recognize.c */
int RS03RecognizeFile(LargeFile*, EccHeader**);
int RS03RecognizeImage(Image*);
#ifndef CLI
/* rs03-window.c */
void RS03AddFixValues(RS03Widgets*, int, int);
void RS03SetFixMaxValues(RS03Widgets*, int, int, gint64);
void RS03UpdateFixResults(RS03Widgets*, gint64, gint64);
#endif
/* rs03-verify.c */
#define VERIFY_IMAGE_SEGMENTS 1000
void RS03Verify(Image*);
/* temporary single threaded versions */
void RS03SCreate(void);
#ifndef CLI
void CreateRS03SEncWindow(Method*, GtkWidget*);
void ResetRS03SEncWindow(Method*);
#endif
#endif

1037
src/rs03-preferences.c Normal file

File diff suppressed because it is too large Load Diff

592
src/rs03-recognize.c Normal file
View File

@@ -0,0 +1,592 @@
/* 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 "udf.h"
#include "rs03-includes.h"
/*
* Aux. functions
*/
static int valid_crc_block(unsigned char *buf, guint64 sector, int image_expected)
{ CrcBlock *cb = alloca(2048);
guint32 recorded_crc, real_crc;
memcpy(cb, buf, 2048);
/* See if the magic cookie is there */
if( strncmp((char*)cb->cookie, "*dvdisaster*", 12)
|| strncmp((char*)cb->method, "RS03", 4))
return 0;
/* Examine the checksum */
recorded_crc = cb->selfCRC;
#ifdef HAVE_BIG_ENDIAN
cb->selfCRC = 0x47504c00;
#else
cb->selfCRC = 0x4c5047;
#endif
real_crc = Crc32((unsigned char*)cb, 2048);
if(real_crc != recorded_crc)
{ Verbose(".. invalid CRC block %lld\n", (unsigned long long)sector);
return 1;
}
/* If an ecc file header is found in the image (which might
rightfully contain ecc files), ignore it */
if(image_expected && (cb->methodFlags[0] & MFLAG_ECC_FILE))
{ Verbose(".. Crc block from ecc file in image - IGNORED\n");
return 1;
}
return 2;
}
/***
*** Recognize a RS03 error correction file
***/
int RS03RecognizeFile(LargeFile *ecc_file, EccHeader **eh)
{ int crc_block = 0;
int n;
Verbose("RS03RecognizeFile(): examining %s\n", ecc_file->path);
*eh = g_malloc(sizeof(EccHeader));
/*** First see whether we have a valid ecc header. */
LargeSeek(ecc_file, 0);
n = LargeRead(ecc_file, *eh, sizeof(EccHeader));
/* short read -> definitely not an ecc file */
if(n != sizeof(EccHeader))
{ g_free(*eh);
*eh=NULL;
Verbose("RS03RecognizeFile(): short read for ecc header\n");
return ECCFILE_INVALID;
}
/* Validate the header */
if(!strncmp((char*)(*eh)->cookie, "*dvdisaster*", 12))
{ guint32 recorded_crc,real_crc;
/* 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);
#ifdef HAVE_BIG_ENDIAN
SwapEccHeaderBytes(*eh);
#endif
if(real_crc != recorded_crc)
{ Verbose("RS03RecognizeFile(): checksum error in ecc header\n");
}
else
{ if(!strncmp((char*)(*eh)->method, "RS03", 4))
{ Verbose("RS03RecognizeFile(): ecc header found\n");
return ECCFILE_PRESENT;
}
else
{ Verbose("RS03RecognizeFile(): wrong codec\n");
g_free(*eh);
*eh=NULL;
return ECCFILE_WRONG_CODEC;
}
}
}
else Verbose("RS03RecognizeFile(): no magic cookie in header\n");
/* No ecc header found; search for CRC blocks.
The CRC block follow directly after the ecc header,
so simply continue reading in 2048 chunks until
the file ends. There is no good criterion for stopping
the read earlier since the file may be truncated and/or
contain mangled contents in many unpredictable ways. */
Verbose("RS03RecognizeFile(): exhaustive search for CRC blocks started\n");
for(;;)
{ unsigned char buf[2048];
n = LargeRead(ecc_file, buf, 2048);
if(n != 2048)
{ if(n== 0 && LargeEOF(ecc_file))
Verbose("RS03RecognizeFile(): end of file reached\n");
else Verbose("RS03RecognizeFile(): short read for CRC sector %d\n", crc_block);
g_free(*eh);
*eh=NULL;
return ECCFILE_INVALID;
}
if(valid_crc_block(buf, crc_block++, FALSE) == 2)
{ ReconstructRS03Header(*eh, (CrcBlock*)buf);
Verbose("** Success: sector %d, rediscovered format with %d roots\n",
crc_block+1, (*eh)->eccBytes);
/* Rewrite the missing ecc header if possible */
if(ecc_file->flags & O_RDWR || ecc_file->flags & O_WRONLY)
{ int success=0;
EccHeader *le_eh = *eh;
char buf[4096];
memset(buf, 0, 4096);
#ifdef HAVE_BIG_ENDIAN
/* eh contains the recovered ecc header in native endian format,
which is what we need it in, but it must be written out in
little endian. So we have to create an extra copy for writing
out when on a big endian machine. */
memcpy(buf, *eh, 4096);
le_eh = (EccHeader*)buf;
SwapEccHeaderBytes(le_eh);
le_eh->selfCRC = 0x47504c00;
le_eh->selfCRC = Crc32((unsigned char*)buf, 4096);
#endif
if(LargeSeek(ecc_file, 0))
{ if(LargeWrite(ecc_file, le_eh, 4096))
success=1;
}
if(success) Verbose("** Missing ecc header rewritten\n");
else Verbose("** Note: Could not rewrite ecc header!\n");
}
return ECCFILE_PRESENT;
}
}
/* Still nothing found. */
g_free(*eh);
*eh=NULL;
Verbose("RS03RecognizeFile(): no ecc found\n");
return ECCFILE_INVALID;
}
/***
*** 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
static EccHeader* valid_header(unsigned char *buf, gint64 hdr_pos, int image_expected)
{ 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))
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;
/* If an ecc file header is found in the image (which might
rightfully contain ecc files), ignore it */
if(image_expected && (eh->methodFlags[0] & MFLAG_ECC_FILE))
{ Verbose(".. Ecc file header in image - IGNORED\n");
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 //FIXME
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(Image *image)
{ EccHeader *eh = NULL;
gint64 hdr_pos;
IsoInfo *ii;
unsigned char buf[4096];
switch(image->type)
{ case IMAGE_FILE:
Verbose("FindRS03HeaderInImage: file %s\n", image->file->path);
break;
case IMAGE_MEDIUM:
Verbose("FindRS03HeaderInImage: medium %s\n", image->dh->device);
break;
default:
Verbose("FindRS03HeaderInImage: unknown type %d\n", image->type);
break;
}
/*** Try to find the header behind the ISO image */
ii = image->isoInfo;
if(!ii) Verbose(" . NO ISO structures found!\n");
if(ii)
{ hdr_pos = ii->volumeSize;
if(ImageReadSectors(image, buf, hdr_pos, 2) == 2)
{ eh = valid_header(buf, hdr_pos, TRUE);
if(eh)
{ Verbose("FindRS03HeaderInImage(): Header found at pos +0\n");
return eh;
}
}
hdr_pos = ii->volumeSize - 150;
if(ImageReadSectors(image, buf, hdr_pos, 2) == 2)
{ eh = valid_header(buf, hdr_pos, TRUE);
if(eh)
{ Verbose("FindRS03HeaderInImage(): Header found at pos -150\n");
return eh;
}
}
}
return NULL;
}
typedef struct
{ AlignedBuffer *layer[256];
AlignedBuffer *ab;
RS03Layout *layout[256];
int layer_checked[256];
} recognize_context;
static void free_recognize_context(recognize_context *rc)
{ int i;
if(rc->ab) FreeAlignedBuffer(rc->ab);
for(i=0; i<255; i++)
{ if(rc->layer[i])
FreeAlignedBuffer(rc->layer[i]);
if(rc->layout[i])
g_free(rc->layout[i]);
}
g_free(rc);
}
int RS03RecognizeImage(Image *image)
{ recognize_context *rc = g_malloc0(sizeof(recognize_context));
guint64 image_sectors;
guint64 layer_size;
int untested_layers;
int layer, layer_sector;
int i;
switch(image->type)
{ case IMAGE_FILE:
Verbose("RS03RecognizeImage: file %s\n", image->file->path);
if(image->eccFile)
Stop("Internal error: RS03RecognizeImage() called with ecc file\n");
image_sectors = image->sectorSize;
break;
case IMAGE_MEDIUM:
Verbose("RS03RecognizeImage: medium %s\n", image->dh->device);
image_sectors = MAX(image->dh->readCapacity, image->dh->userAreaSize);
break;
default:
Verbose("RS03RecognizeImage: unknown type %d\n", image->type);
free_recognize_context(rc);
return FALSE;
break;
}
/* Easy shot: Locate the ecc header in the image */
if (!Closure->debugMode || !Closure->ignoreRS03header)
{ image->eccHeader = FindRS03HeaderInImage(image);
if(image->eccHeader)
{ free_recognize_context(rc);
return TRUE;
}
}
/* No exhaustive search on optical media unless explicitly okayed by user */
if(!Closure->examineRS03 && image->type == IMAGE_MEDIUM)
{ free_recognize_context(rc);
Verbose("RS03RecognizeImage: skipping exhaustive RS03 search\n");
return FALSE;
}
/* Determine image size in augmented case. */
Verbose("RS03RecognizeImage: No EH, entering exhaustive search\n");
if(Closure->debugMode && Closure->mediumSize > 170)
{ layer_size = Closure->mediumSize/GF_FIELDMAX;
Verbose("Warning: image size set to %lld for debugging!\n", Closure->mediumSize);
}
else
{ if(image_sectors < CDR_SIZE) layer_size = CDR_SIZE/GF_FIELDMAX;
else if(image_sectors < DVD_SL_SIZE) layer_size = DVD_SL_SIZE/GF_FIELDMAX;
else if(image_sectors < DVD_DL_SIZE) layer_size = DVD_DL_SIZE/GF_FIELDMAX;
else if(image_sectors < BD_SL_SIZE)
layer_size = (Closure->noBdrDefectManagement ? BD_SL_SIZE_NODM : BD_SL_SIZE)/GF_FIELDMAX;
else if(image_sectors < BD_DL_SIZE)
layer_size = (Closure->noBdrDefectManagement ? BD_DL_SIZE_NODM : BD_DL_SIZE)/GF_FIELDMAX;
else if(image_sectors < BDXL_TL_SIZE)
layer_size = (Closure->noBdrDefectManagement ? BDXL_TL_SIZE_NODM : BDXL_TL_SIZE)/GF_FIELDMAX;
else layer_size = (Closure->noBdrDefectManagement ? BDXL_QL_SIZE_NODM : BDXL_QL_SIZE)/GF_FIELDMAX;
}
Verbose(".. trying layer size %lld\n", layer_size);
/*
* Try a quick scan for the CRC sectors in order
* to re-discover the layout.
*/
Verbose("Scanning layers for signatures.\n");
/* Prepare layout for all possible cases (8..170 roots) */
for(i=84; i<=247; i++) /* allowed range of ndata */
{ RS03Layout *lay;
rc->layout[i] = lay = g_malloc0(sizeof(RS03Layout));
lay->eh = NULL;
lay->dataSectors = (i-1)*layer_size-2;
lay->dataPadding = 0;
lay->totalSectors = GF_FIELDMAX*layer_size;
lay->sectorsPerLayer = layer_size;
lay->mediumCapacity = 0;
lay->eccHeaderPos = lay->dataSectors;
lay->firstCrcPos = (i-1)*layer_size;
lay->firstEccPos = i*layer_size;
lay->nroots = GF_FIELDMAX-i;
lay->ndata = i;
lay->inLast = 2048;
lay->target = ECC_IMAGE;
}
untested_layers = 247-84+1;
rc->ab = CreateAlignedBuffer(2048);
for(layer_sector = 0; layer_sector < layer_size; layer_sector++)
{ CrcBlock *cb = (CrcBlock*)rc->ab->buf;
Verbose("- layer slice %d\n", layer_sector);
for(layer = 84; layer <= 247; layer++)
{ if(!rc->layer_checked[layer])
{ gint64 sector;
int crc_state;
sector = RS03SectorIndex(rc->layout[layer], layer, layer_sector);
/* reading beyond the image won't yield anything */
if(sector >= image_sectors)
goto mark_invalid_layer;
switch(image->type)
{ case IMAGE_FILE:
RS03ReadSectors(image, rc->layout[layer], rc->ab->buf,
layer, layer_sector, 1, RS03_READ_ALL);
if(CheckForMissingSector(rc->ab->buf, sector, NULL, 0) != SECTOR_PRESENT)
continue; /* unreadble -> can't decide */
break;
case IMAGE_MEDIUM:
{ int n;
n = ImageReadSectors(image, rc->ab->buf, sector, 1);
if(!n)
continue; /* unreadble -> can't decide */
}
}
/* CRC header found? */
crc_state = valid_crc_block(rc->ab->buf, sector, TRUE);
if(crc_state)
{ int nroots=255-layer-1;
if(crc_state == 1) /* corrupted crc header, try this layer again later */
continue;
Verbose("** Success: sector %lld, rediscovered format with %d roots\n",
sector, nroots);
image->eccHeader = g_malloc(sizeof(EccHeader));
ReconstructRS03Header(image->eccHeader, cb);
/* Note: Rewriting the missing ecc header makes no sense here
as we do not have access to the image written in the reading
functions, and the error correction will restore it anyways.
(contrary to the situation with ecc files) */
free_recognize_context(rc);
return TRUE;
}
/* Sector readable but not a CRC header -> skip this layer */
mark_invalid_layer:
if(!rc->layer_checked[layer])
{ rc->layer_checked[layer] = 1;
untested_layers--;
}
if(untested_layers <= 0)
{ Verbose("** All layers tested -> no RS03 data found\n");
free_recognize_context(rc);
return FALSE;
}
}
}
Verbose("-> %d untested layers remaining\n", untested_layers);
}
Verbose("-- whole medium/image scanned; %d layers remain untested\n", untested_layers);
Verbose("-- giving now up as ecc-based search is not yet implemented\n");
free_recognize_context(rc);
return FALSE;
/*
* TODO: Assemble all ecc blocks and see whether the error corrction
* succeeds for a certain number of roots
*/
#if 0
for(i=0; i<255; i++)
rc->layer[i] = CreateAlignedBuffer(2048);
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);
image->eccHeader = g_malloc(sizeof(EccHeader));
ReconstructRS03Header(image->eccHeader, cb);
//FIXME: endianess okay?
free_recognize_context(rc);
return TRUE;
}
}
}
#endif
free_recognize_context(rc);
return FALSE;
}

1422
src/rs03-verify.c Normal file

File diff suppressed because it is too large Load Diff

384
src/rs03-window.c Normal file
View File

@@ -0,0 +1,384 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#include "rs03-includes.h"
/***
*** Forward declarations
***/
static void redraw_curve(RS03Widgets*);
static void update_geometry(RS03Widgets*);
/***
*** Encoding window
***/
void ResetRS03EncWindow(Method *method)
{ RS03Widgets *wl = (RS03Widgets*)method->widgetList;
SetProgress(wl->encPBar1, 0, 100);
SetProgress(wl->encPBar2, 0, 100);
gtk_widget_hide(wl->encLabel2);
gtk_widget_hide(wl->encPBar2);
gtk_widget_hide(wl->encLabel3);
gtk_widget_hide(wl->encLabel4);
gtk_widget_hide(wl->encLabel5);
gtk_widget_hide(wl->encThreads);
gtk_widget_hide(wl->encPerformance);
gtk_widget_hide(wl->encBottleneck);
gtk_label_set_text(GTK_LABEL(wl->encFootline), "");
gtk_label_set_text(GTK_LABEL(wl->encFootline2), "");
}
void CreateRS03EncWindow(Method *method, GtkWidget *parent)
{ GtkWidget *wid,*table,*pbar,*sep;
RS03Widgets *wl;
if(!method->widgetList)
{ wl = g_malloc0(sizeof(RS03Widgets));
method->widgetList = wl;
}
else wl = method->widgetList;
wl->encHeadline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->encHeadline), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(wl->encHeadline), 5, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->encHeadline, FALSE, FALSE, 3);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
table = gtk_table_new(2, 5, FALSE);
gtk_box_pack_start(GTK_BOX(parent), table, FALSE, FALSE, 30);
wl->encLabel1 = wid = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(wid),
_utf("<b>1. Reserving space:</b>"));
gtk_misc_set_alignment(GTK_MISC(wid), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), wid, 0, 1, 0, 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10, 20);
pbar = wl->encPBar1 = gtk_progress_bar_new();
gtk_table_attach(GTK_TABLE(table), pbar, 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 10, 20);
wl->encLabel2 = wid = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(wid),
_utf("<b>2. Creating error correction data:</b>"));
gtk_misc_set_alignment(GTK_MISC(wid), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), wid, 0, 1, 1, 2, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10, 20);
pbar = wl->encPBar2 = gtk_progress_bar_new();
gtk_table_attach(GTK_TABLE(table), pbar, 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 10, 20);
wl->encLabel3 = wid = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wid), 1.0, 0.0);
gtk_label_set_markup(GTK_LABEL(wid),_utf("<b>Encoder info:</b>"));
gtk_table_attach(GTK_TABLE(table), wid, 0, 1, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10, 5);
wl->encThreads = wid = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wid), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), wid, 1, 2, 2, 3, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10,5);
wl->encLabel4 = wid = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wid), 1.0, 0.0);
gtk_label_set_markup(GTK_LABEL(wid),_utf("<b>Performance:</b>"));
gtk_table_attach(GTK_TABLE(table), wid, 0, 1, 4, 5, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10, 5);
wl->encPerformance = wid = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wid), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), wid, 1, 2, 4, 5, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10, 5);
wl->encLabel5 = wid = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wid), 1.0, 0.0);
gtk_label_set_markup(GTK_LABEL(wid),_utf("<b>State:</b>"));
gtk_table_attach(GTK_TABLE(table), wid, 0, 1, 5, 6, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10, 5);
wl->encBottleneck = wid = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wid), 0.0, 0.0);
gtk_table_attach(GTK_TABLE(table), wid, 1, 2, 5, 6, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 10, 5);
wl->encFootline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->encFootline), 0.0, 0.5);
gtk_misc_set_padding(GTK_MISC(wl->encFootline), 20, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->encFootline, FALSE, FALSE, 3);
wl->encFootline2 = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->encFootline2), 0.0, 0.5);
gtk_misc_set_padding(GTK_MISC(wl->encFootline2), 20, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->encFootline2, FALSE, FALSE, 3);
}
/***
*** Fix window
***/
/*
* Set the media size and ecc capacity
*/
static gboolean set_max_idle_func(gpointer data)
{ RS03Widgets *wl = (RS03Widgets*)data;
redraw_curve(wl);
return FALSE;
}
void RS03SetFixMaxValues(RS03Widgets *wl, int data_bytes, int ecc_bytes, gint64 sectors)
{
wl->dataBytes = data_bytes;
wl->eccBytes = ecc_bytes;
wl->nSectors = sectors;
wl->fixCurve->maxX = 100;
wl->fixCurve->maxY = ecc_bytes - (ecc_bytes % 5) + 5;
g_idle_add(set_max_idle_func, wl);
}
/*
* Update the corrected / uncorrected numbers
*/
static gboolean results_idle_func(gpointer data)
{ RS03Widgets *wl = (RS03Widgets*)data;
SetLabelText(GTK_LABEL(wl->fixCorrected), _("Repaired: %lld"), wl->corrected);
SetLabelText(GTK_LABEL(wl->fixUncorrected), _("Unrepairable: <span %s>%lld</span>"),Closure->redMarkup, wl->uncorrected);
SetLabelText(GTK_LABEL(wl->fixProgress), _("Progress: %3d.%1d%%"), wl->percent/10, wl->percent%10);
return FALSE;
}
void RS03UpdateFixResults(RS03Widgets *wl, gint64 corrected, gint64 uncorrected)
{
wl->corrected = corrected;
wl->uncorrected = uncorrected;
g_idle_add(results_idle_func, wl);
}
/*
* Update the error curve
*/
static gboolean curve_idle_func(gpointer data)
{ RS03Widgets *wl = (RS03Widgets*)data;
gint x0 = CurveX(wl->fixCurve, (double)wl->lastPercent);
gint x1 = CurveX(wl->fixCurve, (double)wl->percent);
gint y = CurveY(wl->fixCurve, wl->fixCurve->ivalue[wl->percent]);
gint i;
/*** Mark unused ecc values */
for(i=wl->lastPercent+1; i<wl->percent; i++)
wl->fixCurve->ivalue[i] = wl->fixCurve->ivalue[wl->percent];
/*** Resize the Y axes if error values exceeds current maximum */
if(wl->fixCurve->ivalue[wl->percent] > wl->fixCurve->maxY)
{ wl->fixCurve->maxY = wl->fixCurve->ivalue[wl->percent];
wl->fixCurve->maxY = wl->fixCurve->maxY - (wl->fixCurve->maxY % 5) + 5;
update_geometry(wl);
gdk_window_clear(wl->fixCurve->widget->window);
redraw_curve(wl);
wl->lastPercent = wl->percent;
return FALSE;
}
/*** Draw the error value */
if(wl->fixCurve->ivalue[wl->percent] > 0)
{ gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->barColor);
gdk_draw_rectangle(wl->fixCurve->widget->window,
Closure->drawGC, TRUE,
x0, y, x0==x1 ? 1 : x1-x0, wl->fixCurve->bottomY-y);
}
wl->lastPercent = wl->percent;
/* Redraw the ecc capacity threshold line */
y = CurveY(wl->fixCurve, wl->eccBytes);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->greenSector);
gdk_draw_line(wl->fixCurve->widget->window,
Closure->drawGC,
wl->fixCurve->leftX-6, y, wl->fixCurve->rightX+6, y);
return FALSE;
}
/*
* Add one new data point
*/
void RS03AddFixValues(RS03Widgets *wl, int percent, int ecc_max)
{
if(percent < 0 || percent > 1000)
return;
wl->fixCurve->ivalue[percent] = ecc_max;
wl->percent = percent;
g_idle_add(curve_idle_func, wl);
}
/*
* Redraw the whole curve
*/
/* Calculate the geometry of the curve and spiral */
static void update_geometry(RS03Widgets *wl)
{
/* Curve geometry */
UpdateCurveGeometry(wl->fixCurve, "999", 20);
/* Label positions in the foot line */
gtk_box_set_child_packing(GTK_BOX(wl->fixFootlineBox), wl->fixCorrected,
TRUE, TRUE, wl->fixCurve->leftX, GTK_PACK_START);
gtk_box_set_child_packing(GTK_BOX(wl->fixFootlineBox), wl->fixUncorrected,
TRUE, TRUE, wl->fixCurve->leftX, GTK_PACK_START);
}
static void redraw_curve(RS03Widgets *wl)
{ int y;
/* Redraw the curve */
RedrawAxes(wl->fixCurve);
RedrawCurve(wl->fixCurve, wl->percent);
/* Ecc capacity threshold line */
y = CurveY(wl->fixCurve, wl->eccBytes);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->greenSector);
gdk_draw_line(wl->fixCurve->widget->window,
Closure->drawGC,
wl->fixCurve->leftX-6, y, wl->fixCurve->rightX+6, y);
}
/*
* Expose callback
*/
static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{ RS03Widgets *wl = (RS03Widgets*)data;
if(event->count) /* Exposure compression */
return TRUE;
update_geometry(wl);
redraw_curve(wl);
return TRUE;
}
void ResetRS03FixWindow(Method *method)
{ RS03Widgets *wl = (RS03Widgets*)method->widgetList;
gtk_notebook_set_current_page(GTK_NOTEBOOK(wl->fixNotebook), 0);
ZeroCurve(wl->fixCurve);
RS03UpdateFixResults(wl, 0, 0);
if(wl->fixCurve && wl->fixCurve->widget)
{ gdk_window_clear(wl->fixCurve->widget->window);
redraw_curve(wl);
}
wl->percent = 0;
wl->lastPercent = 0;
}
/*
* Create the Fix window contents
*/
void CreateRS03FixWindow(Method *method, GtkWidget *parent)
{ RS03Widgets *wl;
GtkWidget *sep,*ignore,*d_area,*notebook,*hbox;
if(!method->widgetList)
{ wl = g_malloc0(sizeof(RS03Widgets));
method->widgetList = wl;
}
else wl = method->widgetList;
wl->fixHeadline = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixHeadline), 0.0, 0.0);
gtk_misc_set_padding(GTK_MISC(wl->fixHeadline), 5, 0);
gtk_box_pack_start(GTK_BOX(parent), wl->fixHeadline, FALSE, FALSE, 3);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
sep = gtk_hseparator_new();
gtk_box_pack_start(GTK_BOX(parent), sep, FALSE, FALSE, 0);
d_area = wl->fixDrawingArea = gtk_drawing_area_new();
gtk_box_pack_start(GTK_BOX(parent), d_area, TRUE, TRUE, 0);
g_signal_connect(G_OBJECT (d_area), "expose_event", G_CALLBACK(expose_cb), (gpointer)wl);
notebook = wl->fixNotebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
gtk_box_pack_end(GTK_BOX(parent), notebook, FALSE, FALSE, 0);
hbox = wl->fixFootlineBox = gtk_hbox_new(TRUE, 0);
wl->fixCorrected = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixCorrected), 0.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), wl->fixCorrected, TRUE, TRUE, 0);
wl->fixProgress = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixProgress), 0.5, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), wl->fixProgress, TRUE, TRUE, 0);
wl->fixUncorrected = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(wl->fixUncorrected), 1.0, 0.0);
gtk_box_pack_start(GTK_BOX(hbox), wl->fixUncorrected, TRUE, TRUE, 0);
ignore = gtk_label_new("progress_tab");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), hbox, ignore);
wl->fixFootline = gtk_label_new("Footline");
gtk_misc_set_alignment(GTK_MISC(wl->fixFootline), 0.0, 0.5);
gtk_misc_set_padding(GTK_MISC(wl->fixFootline), 5, 0);
ignore = gtk_label_new("footer_tab");
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), wl->fixFootline, ignore);
wl->fixCurve = CreateCurve(d_area, _("Errors/Ecc block"), "%d", 1000, CURVE_PERCENT);
wl->fixCurve->enable = DRAW_ICURVE;
}

244
src/scsi-freebsd.c Normal file
View File

@@ -0,0 +1,244 @@
/* 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 "scsi-layer.h"
#include "udf.h"
#if defined(SYS_FREEBSD) || defined(SYS_KFREEBSD)
/* SCSI wrappers for FreeBSD are still work in progress. */
char* DefaultDevice()
{ DeviceHandle *dh;
GDir *dir;
const char* dev;
int dev_type;
/* As a convenience, add the simulated drive first. */
InitSimulatedCD();
/*** Look for suitable devices */
dir = g_dir_open("/dev", 0, NULL);
if(!dir)
{ PrintLog(_("Can not access /dev for devices\n"
"No drives will be pre-selected.\n"));
return g_strdup("no_drives");
}
dh = g_malloc(sizeof(DeviceHandle));
/* iterate through /dev/pass<n> */
while((dev = g_dir_read_name(dir)))
{
if(!strncmp(dev,"pass",4))
{ char buf[80];
sprintf(buf,"/dev/%s", dev);
memset(dh, 0, sizeof(DeviceHandle));
/* see if we can open it */
dh->camdev = cam_open_pass(buf, O_RDWR, NULL);
dh->device = buf;
if(!dh->camdev) /* device not even present */
continue;
dh->ccb = cam_getccb(dh->camdev);
if(!dh->ccb)
continue;
/* make sure its a CDROM type drive */
dev_type = InquireDevice(dh, 1);
cam_freeccb(dh->ccb);
cam_close_device(dh->camdev);
if(dev_type != 5) /* not a CD/DVD ROM */
continue;
/* remember it in our device list */
g_ptr_array_add(Closure->deviceNodes, g_strdup(buf));
sprintf(buf, "%s (/dev/%s)", dh->devinfo, dev);
g_ptr_array_add(Closure->deviceNames, g_strdup(buf));
}
}
g_dir_close(dir);
g_free(dh);
if(Closure->deviceNodes->len)
return g_strdup(g_ptr_array_index(Closure->deviceNodes, 0));
else
{ PrintLog(_("No optical drives found in /dev.\n"
"No drives will be pre-selected.\n"));
return g_strdup("no_drives");
}
}
DeviceHandle* OpenDevice(char *device)
{ DeviceHandle *dh;
dh = g_malloc0(sizeof(DeviceHandle));
/* Make sure we do not overrun any memory due
to differently sized sense structures */
dh->senseSize = sizeof(Sense);
if(dh->senseSize > sizeof(struct scsi_sense_data))
dh->senseSize = sizeof(struct scsi_sense_data);
if(!strcmp(device, "sim-cd"))
{ if(!Closure->simulateCD) /* can happen via resource file / last-device */
{ g_free(dh);
return NULL;
}
dh->simImage = LargeOpen(Closure->simulateCD, O_RDONLY, IMG_PERMS);
if(!dh->simImage)
{ g_free(dh);
Stop(_("Could not open %s: %s"), Closure->simulateCD, strerror(errno));
return NULL;
}
}
else
{ dh->camdev = cam_open_pass(device, O_RDWR, NULL);
if(!dh->camdev)
{ g_free(dh);
Stop(_("Could not open %s: %s"),device, strerror(errno));
return NULL;
}
dh->ccb = cam_getccb(dh->camdev);
if(!dh->ccb)
{ cam_close_device(dh->camdev);
g_free(dh);
Stop("Could not allocate ccb for %s", device);
return NULL;
}
}
dh->device = g_strdup(device);
return dh;
}
void CloseDevice(DeviceHandle *dh)
{
if(dh->canReadDefective)
SetRawMode(dh, MODE_PAGE_UNSET);
if(dh->rawBuffer)
FreeRawBuffer(dh->rawBuffer);
if(dh->ccb)
cam_freeccb(dh->ccb);
if(dh->camdev)
cam_close_device(dh->camdev);
if(dh->device)
g_free(dh->device);
if(dh->typeDescr)
g_free(dh->typeDescr);
if(dh->mediumDescr)
g_free(dh->mediumDescr);
if(dh->defects)
FreeBitmap(dh->defects);
g_free(dh);
}
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{ union ccb *ccb = dh->ccb;
u_int32_t flags = 0;
if(dh->simImage)
return SimulateSendPacket(dh, cmd, cdb_size, buf, size, sense, data_mode);
bzero(&(&ccb->ccb_h)[1],
sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
switch(data_mode)
{ case DATA_READ:
flags = CAM_DIR_IN;
break;
case DATA_WRITE:
flags = CAM_DIR_OUT;
break;
case DATA_NONE:
flags = CAM_DIR_NONE;
break;
default:
Stop("illegal data_mode: %d", data_mode);
}
cam_fill_csio(&ccb->csio, 1, NULL, flags, CAM_TAG_ACTION_NONE,//MSG_SIMPLE_Q_TAG,
buf, size, sizeof(struct scsi_sense_data), cdb_size,
120*1000); /* 120 secs timeout */
memcpy(ccb->csio.cdb_io.cdb_bytes, cmd, cdb_size);
/* Send ccb */
if(cam_send_ccb(dh->camdev, ccb) < 0)
{ printf("cam_send failed\n");
cam_error_print(dh->camdev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stdout);
return -1;
}
/* Extract sense data */
memcpy(sense, &(ccb->csio.sense_data), dh->senseSize);
if((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
return 0;
/* See what went wrong (not covering all cases) */
switch(ccb->csio.scsi_status)
{ case 0x08: /* BUSY */
PrintLog("SendPacket: Target busy.\n");
break;
case 0x18: /* Reservation conflict */
PrintLog("SendPacket: Reservation conflict.\n");
break;
}
return -1;
}
#endif /* defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) */

2766
src/scsi-layer.c Normal file

File diff suppressed because it is too large Load Diff

269
src/scsi-layer.h Normal file
View File

@@ -0,0 +1,269 @@
/* 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/>.
*/
#ifndef SCSI_LAYER_H
#define SCSI_LAYER_H
#ifdef SYS_LINUX
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#endif
#ifdef SYS_MINGW
#include <windows.h>
#include <winioctl.h>
#endif
#if defined(SYS_FREEBSD) || defined(SYS_KFREEBSD)
#include <camlib.h>
#endif
/***
*** Global settings
***/
/* Theretically not needed, but using less causes DMA breakage
on some chipsets. */
#define MIN_TRANSFER_LEN 4
/***
*** Define the Sense data structure.
***/
/*
* Linux already has one
*/
#if defined(SYS_LINUX)
#define MAX_CDB_SIZE CDROM_PACKET_SIZE
/* Now globally defined for all OSes here */
//typedef struct request_sense Sense;
#elif defined(SYS_FREEBSD) || defined(SYS_KFREEBSD)
#define MAX_CDB_SIZE SCSI_MAX_CDBLEN
#else /* SYS_UNKNOWN and others. */
#define MAX_CDB_SIZE 16 /* longest possible SCSI command */
#endif
/*
* The sense struct is named differently on various OSes,
* Some have subtle differences in the used data types.
* To avoid typecasting mayhem we simply reproduce the Linux
* version here and use it on all OS versions.
*/
typedef struct _Sense {
guint8 error_code : 7;
guint8 valid : 1;
guint8 segment_number;
guint8 sense_key : 4;
guint8 reserved2 : 1;
guint8 ili : 1;
guint8 reserved1 : 2;
guint8 information[4];
guint8 add_sense_len;
guint8 command_info[4];
guint8 asc;
guint8 ascq;
guint8 fruc;
guint8 sks[3];
guint8 asb[46];
} Sense;
/***
*** The DeviceHandle is pretty much our device abstraction layer.
***
* It contains info about the opened device and the inserted medium.
*/
typedef struct _DeviceHandle
{ /*
* OS-specific data for device access
*/
#if defined(SYS_LINUX) || defined(SYS_NETBSD)
int fd; /* device file descriptor */
#elif defined(SYS_FREEBSD) || defined(SYS_KFREEBSD)
struct cam_device *camdev; /* camlib device handle */
union ccb *ccb;
#elif defined(SYS_MINGW)
HANDLE fd; /* Windows SPTI file handle for the device */
#endif
/*
* Simulated images
*/
LargeFile *simImage; /* Image for simulation mode */
int pass; /* provided by the reader to simulate failure in specific passes */
/*
* OS-independent data about the device
*/
char *device; /* /dev/foo or whatever the OS uses to name it */
char devinfo[34]; /* whole device info string from INQUIRY */
char vendor[34]; /* vendor and product info only */
Sense sense; /* sense data from last operation */
int senseSize; /* OS may have differently sized struct */
double singleRate; /* supposed KB/sec @ single speed */
int maxRate; /* guessed maximum transfer rate */
int clusterSize; /* number of sectors per cluster */
/*
* Raw reading support
*/
int canReadDefective; /* TRUE if drive claims to raw read uncorrectable sectors */
int canC2Scan; /* TRUE if drive supports C2 error scanning */
int c2[MAX_CLUSTER_SIZE]; /* C2 errors per sector */
unsigned char previousReadMode;/* read mode prior to switching to raw reads */
unsigned char previousRetries; /* retries prior to switching */
unsigned char currentReadMode; /* current raw read mode */
RawBuffer *rawBuffer; /* for performing raw read analysis */
int (*read)(struct _DeviceHandle*, unsigned char*, int, int);
int (*readRaw)(struct _DeviceHandle*, unsigned char*, int, int);
/*
* Information about currently inserted medium
*/
gint64 sectors; /* actually used number of sectors */
int sessions; /* number of sessions */
int layers; /* and layers */
char manuID[20]; /* Manufacturer info from ADIP/lead-in */
int mainType; /* CD or DVD */
int subType; /* see enum below */
char *typeDescr; /* human readable form of subType */
int bookType; /* book type */
char *bookDescr; /* human readable of above */
int profile; /* profile selected by drive */
char *profileDescr; /* human readable form of above */
char *shortProfile; /* short version of above */
int isDash; /* DVD- */
int isPlus; /* DVD+ */
int incomplete; /* disc is not finalized or otherwise broken */
int discStatus; /* obtained from READ DISC INFORMATION query */
int rewriteable;
char *mediumDescr; /* textual description of medium */
/*
* size alternatives from different sources
*/
gint64 readCapacity; /* value returned by READ CAPACITY */
gint64 userAreaSize; /* size of user area according to DVD Info struct */
gint64 blankCapacity; /* blank capacity (maybe 0 if query failed) */
/*
* debugging stuff
*/
Bitmap *defects; /* for defect simulation */
} DeviceHandle;
/*
* Media types seem not to be standardized anywhere,
* so we make up our own here.
*/
#define MAIN_TYPE_MASK 0xf0
#define CD 0x10
#define DATA1 0x11
#define XA21 0x12
#define DVD 0x20
#define DVD_RAM 0x21
#define DVD_DASH_R 0x22
#define DVD_DASH_RW 0x23
#define DVD_PLUS_R 0x24
#define DVD_PLUS_RW 0x25
#define DVD_DASH_R_DL 0x26
#define DVD_DASH_RW_DL 0x27
#define DVD_PLUS_R_DL 0x28
#define DVD_PLUS_RW_DL 0x29
#define BD 0x40
#define BD_R 0x41
#define BD_RE 0x42
#define UNSUPPORTED 0x00
/* transport io modes */
#define DATA_WRITE 0
#define DATA_READ 1
#define DATA_NONE 2
/***
*** Exported functions
***/
/*
* OS-dependent wrappers from scsi-<os>.c
*/
DeviceHandle* OpenDevice(char*);
#ifdef SYS_MINGW
DeviceHandle* open_spti_device(char*);
#endif
int SendPacket(DeviceHandle*, unsigned char*, int, unsigned char*, int, Sense*, int);
int SimulateSendPacket(DeviceHandle*, unsigned char*, int, unsigned char*, int, Sense*, int);
/***
*** scsi-layer.c
***
* The really user-visible stuff
*/
enum
{ MODE_PAGE_UNSET,
MODE_PAGE_SET
};
int QueryBlankCapacity(DeviceHandle*);
gint64 CurrentMediumSize(int);
void CloseDevice(DeviceHandle*);
int InquireDevice(DeviceHandle*, int);
void SetRawMode(DeviceHandle*, int);
void SpinupDevice(DeviceHandle*);
void LoadMedium(struct _DeviceHandle*, int);
int TestUnitReady(DeviceHandle*);
int ReadSectors(DeviceHandle*, unsigned char*, gint64, int);
int ReadSectorsFast(DeviceHandle*, unsigned char*, gint64, int);
#endif /* SCSI_LAYER_H */

349
src/scsi-linux.c Normal file
View File

@@ -0,0 +1,349 @@
/* 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 "scsi-layer.h"
#include "udf.h"
/***
*** The GNU/Linux SCSI wrapper. Uses the uniform CDROM driver. Kernel 2.6.x recommended.
***/
#ifdef SYS_LINUX
#include <linux/param.h>
#include <scsi/sg.h>
char* DefaultDevice()
{ DeviceHandle *dh;
GDir *dir;
const char* dev;
int dev_type;
/* As a convenience, add the simulated drive first. */
InitSimulatedCD();
/* Now probe the physical drives. */
dir = g_dir_open("/dev", 0, NULL);
if(!dir)
{ PrintLog(_("Can not access /dev for devices\n"
"No drives will be pre-selected.\n"));
return g_strdup("/dev/cdrom");
}
dh = g_malloc(sizeof(DeviceHandle));
while((dev = g_dir_read_name(dir)))
{
if(!strncmp(dev,"scd",3) || !strncmp(dev,"sr", 2))
{ char buf[80];
sprintf(buf,"/dev/%s", dev);
memset(dh, 0, sizeof(DeviceHandle));
dh->fd = open(buf, O_RDWR | O_NONBLOCK);
dh->device = buf;
if(dh->fd < 0) /* device not even present */
continue;
dev_type = InquireDevice(dh, 1);
close(dh->fd);
if(dev_type != 5) /* not a CD/DVD ROM */
continue;
g_ptr_array_add(Closure->deviceNodes, g_strdup(buf));
sprintf(buf, "%s (/dev/%s)", dh->devinfo, dev);
g_ptr_array_add(Closure->deviceNames, g_strdup(buf));
}
}
g_dir_close(dir);
g_free(dh);
if(Closure->deviceNodes->len)
return g_strdup(g_ptr_array_index(Closure->deviceNodes, 0));
else
{ PrintLog(_("No optical drives found in /dev.\n"
"No drives will be pre-selected.\n"));
return g_strdup("/dev/cdrom");
}
}
DeviceHandle* OpenDevice(char *device)
{ DeviceHandle *dh;
dh = g_malloc0(sizeof(DeviceHandle));
dh->senseSize = sizeof(Sense);
if(!strcmp(device, "sim-cd"))
{ if(!Closure->simulateCD) /* can happen via resource file / last-device */
{ g_free(dh);
return NULL;
}
dh->simImage = LargeOpen(Closure->simulateCD, O_RDONLY, IMG_PERMS);
if(!dh->simImage)
{ g_free(dh);
Stop(_("Could not open %s: %s"), Closure->simulateCD, strerror(errno));
return NULL;
}
}
else
{ dh->fd = open(device, O_RDWR | O_NONBLOCK);
if(dh->fd < 0)
{ g_free(dh);
Stop(_("Could not open %s: %s"),device, strerror(errno));
return NULL;
}
}
dh->device = g_strdup(device);
return dh;
}
void CloseDevice(DeviceHandle *dh)
{
if(dh->simImage)
LargeClose(dh->simImage);
if(dh->canReadDefective)
SetRawMode(dh, MODE_PAGE_UNSET);
if(dh->rawBuffer)
FreeRawBuffer(dh->rawBuffer);
if(dh->fd)
close(dh->fd);
if(dh->device)
g_free(dh->device);
if(dh->typeDescr)
g_free(dh->typeDescr);
if(dh->mediumDescr)
g_free(dh->mediumDescr);
if(dh->defects)
FreeBitmap(dh->defects);
g_free(dh);
}
//#define ASSERT_CDB_LENGTH
#ifdef ASSERT_CDB_LENGTH
static void assert_cdb_length(unsigned char cdb, int cdb_size, int expected_size)
{
if(cdb_size != expected_size)
PrintLog("SendPacket(): Wrong size %d for opcode %0x (expected %d)\n",
cdb_size, cdb, expected_size);
}
static void assert_cdb_direction(unsigned char cdb, int expected, int given)
{
if(expected != given)
PrintLog("SendPacket(): Wrong data direction %d for opcode %0x (expected %d)\n",
given, cdb, expected);
}
static void test_cdb(unsigned char *cdb, int cdb_size, int direction)
{
switch(cdb[0])
{ case 0x00: assert_cdb_length(cdb[0], cdb_size, 6); /* TEST UNIT READY */
assert_cdb_direction(cdb[0], DATA_NONE, direction);
break;
case 0x12: assert_cdb_length(cdb[0], cdb_size, 6); /* INQUIRY */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x1b: assert_cdb_length(cdb[0], cdb_size, 6); /* START STOP */
assert_cdb_direction(cdb[0], DATA_NONE, direction);
break;
case 0x1e: assert_cdb_length(cdb[0], cdb_size, 6); /* PREVENT ALLOW MEDIUM REMOVAL */
assert_cdb_direction(cdb[0], DATA_NONE, direction);
break;
case 0x23: assert_cdb_length(cdb[0], cdb_size, 10); /* READ FORMAT CAPACITIES */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x25: assert_cdb_length(cdb[0], cdb_size, 10); /* READ CAPACITY */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x28: assert_cdb_length(cdb[0], cdb_size, 10); /* READ(10) */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x43: assert_cdb_length(cdb[0], cdb_size, 10); /* READ TOC/PMA/ATIP */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x46: assert_cdb_length(cdb[0], cdb_size, 10); /* GET CONFIGURATION */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x51: assert_cdb_length(cdb[0], cdb_size, 10); /* READ DISC INFORMATION */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x52: assert_cdb_length(cdb[0], cdb_size, 10); /* READ TRACK INFORMATION */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x55: assert_cdb_length(cdb[0], cdb_size, 10); /* MODE SELECT */
assert_cdb_direction(cdb[0], DATA_WRITE, direction);
break;
case 0x5a: assert_cdb_length(cdb[0], cdb_size, 10); /* MODE SENSE */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0xad: assert_cdb_length(cdb[0], cdb_size, 12); /* READ DVD STRUCTURE */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0xbe: assert_cdb_length(cdb[0], cdb_size, 12); /* READ CD */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
default:
PrintLog("SendPacket(): Unknown opcode %0x\n", cdb[0]);
}
}
#endif
/*
* The CDROM ioctl() interface has been used since the first dvdisaster release.
* However with recent 2.6 kernels it seems to become outdated - several parallel
* SCSI cards are already exhibiting failures using this interface.
* Starting with dvdisaster 0.79.3, the SG_IO interface has become
* the default now. You can revert back to old behaviour using --driver=cdrom.
*/
static int send_packet_cdrom(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{ struct cdrom_generic_command cgc;
#ifdef ASSERT_CDB_LENGTH
test_cdb(cmd, cdb_size, data_mode);
#endif
memset(&cgc, 0, sizeof(cgc));
memcpy(cgc.cmd, cmd, MAX_CDB_SIZE); /* GNU/Linux ignores the CDB size */
cgc.buffer = buf;
cgc.buflen = size;
cgc.sense = (struct request_sense*)sense;
cgc.timeout = 10*60*HZ; /* 10 minutes; a timeout hangs newer kernels */
switch(data_mode)
{ case DATA_READ:
cgc.data_direction = CGC_DATA_READ;
break;
case DATA_WRITE:
cgc.data_direction = CGC_DATA_WRITE;
break;
case DATA_NONE:
cgc.data_direction = CGC_DATA_NONE;
break;
default:
Stop("illegal data_mode: %d", data_mode);
}
return ioctl(dh->fd, CDROM_SEND_PACKET, &cgc);
}
/*
* Access to the drive through the generic SCSI interface
* has been added in dvdisaster 0.72 - it seems to be better
* maintained than the older CDROM_SEND_PACKET interface now.
* Especially parallel SCSI cdroms require this now.
*/
static int send_packet_generic(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{ struct sg_io_hdr sg_io;
#ifdef ASSERT_CDB_LENGTH
test_cdb(cmd, cdb_size, data_mode);
#endif
memset(&sg_io, 0, sizeof(sg_io));
sg_io.interface_id = 'S';
switch(data_mode)
{ case DATA_READ:
sg_io.dxfer_direction = SG_DXFER_FROM_DEV;
break;
case DATA_WRITE:
sg_io.dxfer_direction = SG_DXFER_TO_DEV;
break;
case DATA_NONE:
sg_io.dxfer_direction = SG_DXFER_NONE;
break;
default:
Stop("illegal data_mode: %d", data_mode);
}
sg_io.cmd_len = cdb_size;
sg_io.mx_sb_len = sizeof(Sense);
sg_io.dxfer_len = size;
sg_io.dxferp = buf;
sg_io.cmdp = cmd;
sg_io.sbp = (unsigned char*)sense;
sg_io.timeout = 10*60*1000;
sg_io.flags = SG_FLAG_LUN_INHIBIT|SG_FLAG_DIRECT_IO;
if(ioctl(dh->fd,SG_IO,&sg_io))
{ dh->sense.sense_key = 3; /* pseudo error indicating */
dh->sense.asc = 255; /* ioctl() failure */
dh->sense.ascq = 254;
return -1;
}
if(sg_io.status)
return -1;
#if 0
if ((sg_io.info&SG_INFO_OK_MASK) == SG_INFO_OK)
return 0;
#endif
return 0;
}
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{
if(dh->simImage)
return SimulateSendPacket(dh, cmd, cdb_size, buf, size, sense, data_mode);
switch(Closure->useSCSIDriver)
{
case DRIVER_SG:
return send_packet_generic(dh, cmd, cdb_size, buf, size, sense, data_mode);
case DRIVER_CDROM:
return send_packet_cdrom(dh, cmd, cdb_size, buf, size, sense, data_mode);
default:
Stop("no SCSI driver selected");
break;
}
return -1;
}
#endif /* SYS_LINUX */

229
src/scsi-netbsd.c Normal file
View File

@@ -0,0 +1,229 @@
/* 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/>.
*/
/* NetBSD support by Sergey Svishchev <svs@ropnet.ru>.
*/
#include "dvdisaster.h"
#include "scsi-layer.h"
#include "udf.h"
#ifdef SYS_NETBSD
#include <sys/device.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/scsiio.h>
#include <util.h>
char* DefaultDevice()
{ DeviceHandle *dh;
char *disknames, *p, raw;
int dev_type, sysctl_mib[2];
size_t sysctl_len;
/* As a convenience, add the simulated drive first. */
InitSimulatedCD();
/* Now probe the physical drives. */
raw = 'a' + getrawpartition();
sysctl_mib[0] = CTL_HW;
sysctl_mib[1] = HW_DISKNAMES;
if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
PrintLog("Failed to get value of sysctl `hw.disknames'\n");
return g_strdup("no_drives");
}
if (!(disknames = g_malloc(sysctl_len))) {
PrintLog("Out of memory constructing scan device list\n");
return g_strdup("no_drives");
}
if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
PrintLog("Failed to get value of sysctl `hw.disknames'\n");
g_free(disknames);
return g_strdup("no_drives");
}
dh = g_malloc(sizeof(DeviceHandle));
for (p = strtok(disknames, " "); p; p = strtok(NULL, " "))
{
if(!strncmp(p,"cd",2))
{ char buf[80];
sprintf(buf,"/dev/r%s%c", p, raw);
memset(dh, 0, sizeof(DeviceHandle));
dh->fd = open(buf, O_RDONLY | O_NONBLOCK);
dh->device = buf;
if(dh->fd < 0) /* device not even present */
continue;
dev_type = InquireDevice(dh, 1);
close(dh->fd);
if(dev_type != 5) /* not a CD/DVD ROM */
continue;
g_ptr_array_add(Closure->deviceNodes, g_strdup(buf));
sprintf(buf, "%s (/dev/r%s%c)", dh->devinfo, p, raw);
g_ptr_array_add(Closure->deviceNames, g_strdup(buf));
}
}
g_free(dh);
if(Closure->deviceNodes->len)
return g_strdup(g_ptr_array_index(Closure->deviceNodes, 0));
else
{ PrintLog(_("No optical drives found.\n"
"No drives will be pre-selected.\n"));
return g_strdup("no_drives");
}
}
DeviceHandle* OpenDevice(char *device)
{ DeviceHandle *dh;
dh = g_malloc0(sizeof(DeviceHandle));
dh->senseSize = sizeof(Sense);
if(!strcmp(device, "sim-cd"))
{ if(!Closure->simulateCD) /* can happen via resource file / last-device */
{ g_free(dh);
return NULL;
}
dh->simImage = LargeOpen(Closure->simulateCD, O_RDONLY, IMG_PERMS);
if(!dh->simImage)
{ g_free(dh);
Stop(_("Could not open %s: %s"), Closure->simulateCD, strerror(errno));
return NULL;
}
}
else
{ dh->fd = open(device, O_RDWR | O_NONBLOCK);
if(dh->fd < 0)
{ g_free(dh);
Stop(_("Could not open %s: %s"),device, strerror(errno));
return NULL;
}
}
dh->device = g_strdup(device);
return dh;
}
void CloseDevice(DeviceHandle *dh)
{
if(dh->canReadDefective)
SetRawMode(dh, MODE_PAGE_UNSET);
if(dh->rawBuffer)
FreeRawBuffer(dh->rawBuffer);
if(dh->fd)
close(dh->fd);
if(dh->device)
g_free(dh->device);
if(dh->typeDescr)
g_free(dh->typeDescr);
if(dh->mediumDescr)
g_free(dh->mediumDescr);
if(dh->defects)
FreeBitmap(dh->defects);
g_free(dh);
}
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{ struct scsireq sc;
int sense_len;
int rc;
if(dh->simImage)
return SimulateSendPacket(dh, cmd, cdb_size, buf, size, sense, data_mode);
/* prepare the scsi request */
memset(&sc, 0, sizeof(sc));
memcpy(sc.cmd, cmd, cdb_size);
sc.cmdlen = cdb_size;
sc.databuf = (char *)buf;
sc.datalen = size;
sc.senselen = 24; /* Maybe SENSEBUFLEN would be better? -cg */
sc.timeout = 60000; /* linux uses 5000 = 5 * HZ */
switch(data_mode)
{ case DATA_READ:
sc.flags = SCCMD_READ;
break;
case DATA_WRITE:
sc.flags = SCCMD_WRITE;
break;
case DATA_NONE:
sc.flags = 0;
break;
default:
Stop("illegal data_mode: %d", data_mode);
}
/* Send the request and save the sense data. */
rc = ioctl(dh->fd, SCIOCCOMMAND, &sc);
sense_len = sc.senselen_used;
if(sense_len > dh->senseSize)
sense_len = dh->senseSize;
memcpy(sense, sc.sense, sense_len);
/* See what we've got back */
if(rc<0) return rc; /* does not happen on SCSI errors */
switch(sc.retsts)
{ case SCCMD_OK: /* everything went fine */
return 0;
case SCCMD_TIMEOUT: /* we don't know how to handle that yet */
PrintLog("SendPacket() - SCSI timeout\n");
return -1;
case SCCMD_BUSY: /* same here */
PrintLog("SendPacket() - target busy\n");
return -1;
case SCCMD_SENSE: /* SCSI error occurred, sense data available */
return -1;
default: /* something other went wrong */
return -1;
}
return -1; /* unreachable */
}
#endif /* SYS_NETBSD */

327
src/scsi-simulated.c Normal file
View File

@@ -0,0 +1,327 @@
/* 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 "scsi-layer.h"
/***
*** Add a menu entry for a simulated CD drive.
***/
void InitSimulatedCD()
{
if(!Closure->simulateCD)
return;
g_ptr_array_add(Closure->deviceNodes, g_strdup("sim-cd"));
g_ptr_array_add(Closure->deviceNames, g_strdup_printf(_("Simulated CD (%s)"), Closure->simulateCD));
}
/***
*** Simulate the SCSI device
***/
/*
* While we're at it, check the CDB interface for proper usage.
*/
static void assert_cdb_length(unsigned char cdb, int cdb_size, int expected_size)
{
if(cdb_size != expected_size)
PrintLog("SendPacket(): Wrong size %d for opcode %0x (expected %d)\n",
cdb_size, cdb, expected_size);
}
static void assert_cdb_direction(unsigned char cdb, int expected, int given)
{
if(expected != given)
PrintLog("SendPacket(): Wrong data direction %d for opcode %0x (expected %d)\n",
given, cdb, expected);
}
static void write_sense(Sense *sense, int sense_key, int asc, int ascq)
{
sense->sense_key=sense_key;
sense->asc=asc;
sense->ascq=ascq;
}
int SimulateSendPacket(DeviceHandle *dh, unsigned char *cdb, int cdb_size, unsigned char *out_buf, int size, Sense *sense, int direction)
{ unsigned char buf[2048];
int real_size;
int alloc_len;
char *nodelay;
switch(cdb[0])
{
case 0x00: assert_cdb_length(cdb[0], cdb_size, 6); /* TEST UNIT READY */
assert_cdb_direction(cdb[0], DATA_NONE, direction);
return 0;
break;
case 0x12: assert_cdb_length(cdb[0], cdb_size, 6); /* INQUIRY */
assert_cdb_direction(cdb[0], DATA_READ, direction);
real_size = (size < 36 ? size : 36);
buf[0] = 0x05; /* PERIPHERAL DEVICE TYPE */
buf[4] = real_size-4; /* ADDITIONAL LENGTH */
memcpy(&buf[8], "Simulate", 8); /* VENDOR ID */
memcpy(&buf[16],"d CD drive ", 16); /* PRODUCT ID */
memcpy(&buf[32],"1.00", 4); /* VERSION */
memcpy(out_buf, buf, real_size);
return real_size;
break;
case 0x1b: assert_cdb_length(cdb[0], cdb_size, 6); /* START STOP */
assert_cdb_direction(cdb[0], DATA_NONE, direction);
return 0;
break;
case 0x1e: assert_cdb_length(cdb[0], cdb_size, 6); /* PREVENT ALLOW MEDIUM REMOVAL */
assert_cdb_direction(cdb[0], DATA_NONE, direction);
return 0;
break;
#if 0
case 0x23: assert_cdb_length(cdb[0], cdb_size, 10); /* READ FORMAT CAPACITIES */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
#endif
case 0x25: assert_cdb_length(cdb[0], cdb_size, 10); /* READ CAPACITY */
assert_cdb_direction(cdb[0], DATA_READ, direction);
{ guint64 sectors;
sectors = dh->simImage->size/2048 - 1;
memset(buf, 0, 16);
buf[0] = (sectors >> 24) & 0xff;
buf[1] = (sectors >> 16) & 0xff;
buf[2] = (sectors >> 8 ) & 0xff;
buf[3] = sectors & 0xff;
buf[6] = (2048>>8)&0xff;
memcpy(out_buf, buf, size);
return 0;
}
break;
#if 0
case 0x28: assert_cdb_length(cdb[0], cdb_size, 10); /* READ(10) */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
#endif
case 0x43: assert_cdb_length(cdb[0], cdb_size, 10); /* READ TOC/PMA/ATIP */
assert_cdb_direction(cdb[0], DATA_READ, direction);
alloc_len = cdb[7]<<8 | cdb[8];
switch(cdb[2] & 0xf) /* format field */
{ case 0x00: /* formatted TOC */
memset(out_buf, 0, 12);
out_buf[0] = 0;
out_buf[1] = 10; /* data length */
out_buf[5] = 0x14; /* CONTROL (data CD) */
return 0;
case 0x02: /* full/raw TOC */
memset(out_buf, 0, 26);
out_buf[0] = 0;
out_buf[1] = 24; /* data length for two track desciptors*/
out_buf[3] = 1; /* 1 session (= last session) */
out_buf[7] = 0xa0; /* POINT: first track number */
out_buf[13] = 0; /* disc type: mode 1 */
return 0;
case 0x04: /* full ATIP */
memset(out_buf, 0, 16);
out_buf[0] = 0;
out_buf[1] = 16;
out_buf[ 8] = 0x61;
out_buf[ 9] = 0x1a;
out_buf[10] = 0x41;
out_buf[11] = 0x00;
out_buf[12] = 0x4f;
out_buf[13] = 0x3b;
out_buf[14] = 0x4a;
return 0;
default:
printf("Simulation of READ TOC failed for format %d\n", cdb[2] & 0xf);
return -1;
}
break;
case 0x46: assert_cdb_length(cdb[0], cdb_size, 10); /* GET CONFIGURATION */
assert_cdb_direction(cdb[0], DATA_READ, direction);
alloc_len = cdb[7]<<8 | cdb[8];
memset(out_buf, 0, 8);
out_buf[3] = 8; /* data length */
out_buf[7] = 9; /* CD-R profile */
return 0;
break;
case 0x51: assert_cdb_length(cdb[0], cdb_size, 10); /* READ DISC INFORMATION */
assert_cdb_direction(cdb[0], DATA_READ, direction);
alloc_len = cdb[7]<<8 | cdb[8];
memset(buf, 0, 32);
buf[0] = 0;
buf[1] = 32; /* We are a strange CD without OPC tables. */
buf[2] = 0x1e; /* finalized and complete */
buf[8] = 0; /* DATA1 format */
memcpy(out_buf, buf, alloc_len);
return 0;
break;
#if 0
case 0x52: assert_cdb_length(cdb[0], cdb_size, 10); /* READ TRACK INFORMATION */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0x55: assert_cdb_length(cdb[0], cdb_size, 10); /* MODE SELECT */
assert_cdb_direction(cdb[0], DATA_WRITE, direction);
break;
case 0x5a: assert_cdb_length(cdb[0], cdb_size, 10); /* MODE SENSE */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
case 0xad: assert_cdb_length(cdb[0], cdb_size, 12); /* READ DVD STRUCTURE */
assert_cdb_direction(cdb[0], DATA_READ, direction);
break;
#endif
case 0xbe: assert_cdb_length(cdb[0], cdb_size, 12); /* READ CD */
assert_cdb_direction(cdb[0], DATA_READ, direction);
{ guint64 lba,last_pos;
useconds_t delay;
int fact;
int i;
if(cdb[9] & ~16) /* We can only deliver user data */
{ write_sense(sense, 5, 0xff, 0xe0);
return -1;
}
lba = cdb[2] << 24 | cdb[3] << 16 | cdb[4] << 8 | cdb[5];
alloc_len = cdb[6] << 16 | cdb[7] << 8 | cdb[8];
last_pos = 2048*lba + 2048*alloc_len - 1;
if(last_pos > dh->simImage->size)
{ write_sense(sense, 5, 0x21, 0x00); /* Illegal LBA */
return -1;
}
if(!LargeSeek(dh->simImage, 2048*lba))
{ write_sense(sense, 5, 0x02, 0x00); /* no seek complete */
return -1;
}
if(LargeRead(dh->simImage, out_buf, alloc_len*2048) != alloc_len*2048)
{ write_sense(sense, 5, 0x11, 0x00); /* unrecovered read error */
return -1;
}
/* Check for dead sector markers and pass them on
as read errors */
for(i=0; i<alloc_len; i++)
{ int err;
err = CheckForMissingSector(out_buf+2048*i, lba+i, NULL, 0);
if(err != SECTOR_PRESENT)
{ if(err == SECTOR_WITH_SIMULATION_HINT)
{ char *sim_hint = GetSimulationHint(out_buf+2048*i);
/* simulated hardware error */
if(!strcmp(sim_hint, "hardware failure"))
{ g_free(sim_hint);
memset(out_buf, 0, alloc_len*2048);
write_sense(sense, 4, 0x09, 0x02); /* hardware error, focus servo failure */
return -1;
}
/* pass on dead sector marker from this sector */
if(!strcmp(sim_hint, "pass as dead sector marker"))
{ g_free(sim_hint);
continue;
}
/* sector becomes readable in pass 3 */
if(!strcmp(sim_hint, "readable in pass 3"))
{ g_free(sim_hint);
if(dh->pass == 2)
{ memset(out_buf+i*2048, 64, 2048);
continue;
}
else
{ memset(out_buf, 0, alloc_len*2048);
write_sense(sense, 3, 0x11, 0x00); /* unrecovered read error */
return -1;
}
}
/* unknown simulation code becomes standard read error */
g_free(sim_hint);
memset(out_buf, 0, alloc_len*2048);
write_sense(sense, 3, 0x11, 0x00); /* unrecovered read error */
return -1;
}
else /* standard read error */
{ memset(out_buf, 0, alloc_len*2048);
write_sense(sense, 3, 0x11, 0x00); /* unrecovered read error */
return -1;
}
}
}
nodelay = getenv("DVDISASTER_SCSI_SIMULATED_NODELAY");
if (!nodelay || strcmp(nodelay, "1")) {
fact = (int)(200.0*sin(-0.5+(double)lba/6280.0));
delay = (500+fact)*alloc_len;
usleep(delay);
}
}
return 0;
break;
default:
printf("SendPacket(): Unknown opcode %0x\n", cdb[0]);
PrintLog("SendPacket(): Unknown opcode %0x\n", cdb[0]);
return -1;
}
}

54
src/scsi-unknown.c Normal file
View File

@@ -0,0 +1,54 @@
/* 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 "scsi-layer.h"
#include "udf.h"
#if defined(SYS_UNKNOWN) || defined(SYS_HURD)
/* Dummy routines so that we can compile on unknown architectures
for which we don't have SCSI support yet. */
char* DefaultDevice()
{
PrintLog(_("* Unsupported operating system - no SCSI layer available.\n"
"* No drives can be used.\n"));
return strdup("no_drives");
}
DeviceHandle* OpenDevice(char *device)
{ return NULL;
}
void CloseDevice(DeviceHandle *dh)
{ if(dh) g_free(dh);
}
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{
return -1;
}
#endif /* defined(SYS_UNKNOWN) || defined(SYS_HURD) */

287
src/scsi-win32.c Normal file
View File

@@ -0,0 +1,287 @@
/* 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 "scsi-layer.h"
#include "udf.h"
/**
** Windows wrapper.
**
* As of dvdisaster 0.73.1, Windows 2000 SP4 is the minimum
* system requirement and thus only SPTI is supported.
*/
#ifdef SYS_MINGW
/*
* The drive letter ordering is remembered for
* historical reasons (it was once useful for ASPI mapping)
*/
static int drive_letters[26];
static int cd_dvd_drives = 0;
char* DefaultDevice()
{
char drive_letter;
char device_path[16],buf[80];
DeviceHandle *dh;
/* As a convenience, add the simulated drive first. */
InitSimulatedCD();
/* Now probe the physical drives. */
for(drive_letter='C'; drive_letter<='Z'; drive_letter++)
{ UINT drive_type;
g_sprintf(device_path, "%c:\\", drive_letter);
drive_type = GetDriveType(device_path);
if(drive_type == DRIVE_CDROM)
{ drive_letters[cd_dvd_drives] = drive_letter;
cd_dvd_drives++;
/* Collect drives accessible via SPTI */
sprintf(buf, "%c:", drive_letter);
if((dh = open_spti_device(buf)))
{ InquireDevice(dh, 1);
g_ptr_array_add(Closure->deviceNodes, g_strdup(buf));
sprintf(buf, "%c: %s", drive_letter, dh->devinfo);
g_ptr_array_add(Closure->deviceNames, g_strdup(buf));
CloseDevice(dh);
}
}
}
if(Closure->deviceNodes->len)
return g_strdup(g_ptr_array_index(Closure->deviceNodes, 0));
else
{ PrintLog(_("No optical drives found in /dev.\n"
"No drives will be pre-selected.\n"));
return g_strdup("No_Drive_found");
}
}
/*
* Close the SPTI device.
*/
void CloseDevice(DeviceHandle *dh)
{
if(dh->simImage)
LargeClose(dh->simImage);
if(dh->canReadDefective)
SetRawMode(dh, MODE_PAGE_UNSET);
if(dh->rawBuffer)
FreeRawBuffer(dh->rawBuffer);
if(dh->fd) /* SPTI cleanup */
CloseHandle(dh->fd);
if(dh->typeDescr)
g_free(dh->typeDescr);
if(dh->mediumDescr)
g_free(dh->mediumDescr);
if(dh->defects)
FreeBitmap(dh->defects);
g_free(dh->device);
g_free(dh);
}
/**
** The SPTI wrapper.
**/
/*
* The Mingw includes are missing the following structs
* for sending SCSI packets via SPTI.
*/
typedef struct
{ USHORT Length;
UCHAR ScsiStatus;
UCHAR PathId;
UCHAR TargetId;
UCHAR Lun;
UCHAR CdbLength;
UCHAR SenseInfoLength;
UCHAR DataIn;
ULONG DataTransferLength;
ULONG TimeOutValue;
PVOID DataBuffer;
ULONG SenseInfoOffset;
UCHAR Cdb[16];
} SCSI_PASS_THROUGH_DIRECT;
typedef struct
{ SCSI_PASS_THROUGH_DIRECT spt;
ULONG Filler;
UCHAR SenseBuf[32];
} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
#define IOCTL_SCSI_BASE 0x00000004
#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS )
/*
* Open the device using SPTI.
*/
DeviceHandle* open_spti_device(char *device)
{ DeviceHandle *dh;
char device_path[16];
if( strlen(device) != 2
|| toupper(*device) < 'C'
|| toupper(*device) > 'Z'
|| device[1] != ':')
{
Stop(_("\nIllegal device name \"%s\" (use devices \"C:\" ... \"Z:\")"),device);
return NULL;
}
dh = g_malloc0(sizeof(DeviceHandle));
dh->device = g_strdup(device);
g_sprintf(device_path, "\\\\.\\%c:", toupper(*device));
dh->fd = CreateFile(device_path,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL );
if(dh->fd == INVALID_HANDLE_VALUE) /* Might be Win9x or missing priviledges */
{ g_free(dh->device);
g_free(dh);
return NULL;
}
return dh;
}
/*
* Send the SCSI command through SPTI.
* At least W2K seems to have problems with SCSI commands timing out;
* it even manages to do a spontaneous reboot because of that occasionally.
* So we set the time out value to a conservative 120 secs.
*/
static int send_spti_packet(HANDLE fd, unsigned char *cmd, int cdb_size, char *buf, int size, Sense *sense, int data_mode)
{ SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER ss;
BOOL success;
ULONG returned;
memset(&ss, 0, sizeof(ss));
ss.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
ss.spt.CdbLength = cdb_size;
ss.spt.SenseInfoLength = 24;
ss.spt.TargetId = 1;
switch(data_mode)
{ case DATA_WRITE:
ss.spt.DataIn = 0; /* SCSI_IOCTL_DATA_OUT */
break;
case DATA_READ:
ss.spt.DataIn = 1; /* SCSI_IOCTL_DATA_IN */
break;
case DATA_NONE:
ss.spt.DataIn = 2; /* SCSI_IOCTL_DATA_UNSPECIFIED */
break;
default:
Stop("illegal data_mode: %d",data_mode);
return -1;
}
ss.spt.DataTransferLength = size;
ss.spt.TimeOutValue = 120; /* see comment above */
ss.spt.DataBuffer = buf;
ss.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseBuf);
memcpy(ss.spt.Cdb, cmd, cdb_size);
success = DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&ss, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
&ss, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
&returned, FALSE);
memcpy(sense, ss.SenseBuf, 24); /* Hmm. What a kludge. */
if(!success || ss.spt.ScsiStatus) return -1;
return 0;
}
/*
* Open the device
*/
DeviceHandle* OpenDevice(char *device)
{ DeviceHandle *dh = NULL;
if(!strcmp(device, "sim-cd"))
{ if(!Closure->simulateCD) /* can happen via resource file / last-device */
return NULL;
dh = g_malloc0(sizeof(DeviceHandle));
dh->senseSize = sizeof(Sense);
dh->simImage = LargeOpen(Closure->simulateCD, O_RDONLY, IMG_PERMS);
if(!dh->simImage)
{ g_free(dh);
Stop(_("Could not open %s: %s"), Closure->simulateCD, strerror(errno));
return NULL;
}
dh->device = g_strdup(device);
}
else {
if( (*device >= 'c' && *device <= 'z')
|| (*device >= 'C' && *device <= 'Z'))
dh = open_spti_device(Closure->device);
if(!dh)
{ Stop(_("Could not open device %s."), device);
return NULL;
}
}
return dh;
}
/*
* Translate Scsi wrapper into SPTI call
*/
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
{
if(dh->simImage)
return SimulateSendPacket(dh, cmd, cdb_size, buf, size, sense, data_mode);
return send_spti_packet(dh->fd, cmd, cdb_size, (char *)buf, size, sense, data_mode);
}
#endif /* SYS_MINGW */

408
src/show-html.c Normal file
View File

@@ -0,0 +1,408 @@
/* dvdisaster: Additional error correction for optical media.
* Copyright (C) 2004-2012 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.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
#include <sys/wait.h>
#endif
#ifdef SYS_MINGW
#include "windows.h"
#include "shellapi.h"
#endif
/***
*** Ask user to specify his browser
***/
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
#define SEARCH_BUTTON 1
typedef struct
{ GtkWidget *dialog;
GtkWidget *entry;
GtkWidget *search;
GtkWidget *filesel;
GtkWidget *fileok;
GtkWidget *filecancel;
char *url;
} browser_dialog_info;
static void response_cb(GtkWidget *widget, int response, gpointer data)
{ browser_dialog_info *bdi = (browser_dialog_info*)data;
switch(response)
{ case GTK_RESPONSE_ACCEPT:
if(Closure->browser) g_free(Closure->browser);
Closure->browser = g_strdup(gtk_entry_get_text(GTK_ENTRY(bdi->entry)));
ShowHTML(bdi->url);
break;
case GTK_RESPONSE_REJECT:
if(bdi->url) g_free(bdi->url);
break;
}
gtk_widget_destroy(widget);
if(bdi->filesel)
gtk_widget_destroy(bdi->filesel);
g_free(bdi);
}
static void search_cb(GtkWidget *widget, gpointer data)
{ browser_dialog_info *bdi = (browser_dialog_info*)data;
if(widget == bdi->search)
{ bdi->filesel = gtk_file_selection_new(_utf("windowtitle|Choose a browser"));
bdi->fileok = GTK_FILE_SELECTION(bdi->filesel)->ok_button;
bdi->filecancel = GTK_FILE_SELECTION(bdi->filesel)->cancel_button;
ReverseCancelOK(GTK_DIALOG(bdi->filesel));
gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(bdi->filesel));
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->ok_button), "clicked",
G_CALLBACK(search_cb), bdi);
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->cancel_button), "clicked",
G_CALLBACK(search_cb), bdi);
gtk_widget_show(bdi->filesel);
}
if(widget == bdi->fileok)
{
if(Closure->browser) g_free(Closure->browser);
Closure->browser = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(bdi->filesel)));
ShowHTML(bdi->url);
gtk_widget_destroy(bdi->filesel);
gtk_widget_destroy(bdi->dialog);
g_free(bdi);
return;
}
if(widget == bdi->filecancel)
{ gtk_widget_destroy(bdi->filesel);
bdi->filesel = NULL;
}
}
static void browser_dialog(char *url)
{ GtkWidget *dialog, *vbox, *hbox, *label, *entry, *button;
browser_dialog_info *bdi = g_malloc0(sizeof(browser_dialog_info));
/* Create the dialog */
dialog = gtk_dialog_new_with_buttons(_utf("windowtitle|Browser required"),
Closure->window, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
bdi->dialog = dialog;
if(url)
{ bdi->url = g_strdup(url);
}
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
/* Insert the contents */
label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label), _utf("<b>Could not find a suitable browser.</b>\n\n"
"Which browser would you like to use\n"
"for reading the online documentation?\n\n"
"Please enter its name (e.g. mozilla) or\n"
"use the \"Search\" button for a file dialog.\n")),
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 10);
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 10);
bdi->entry = entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 10);
bdi->search = button = gtk_button_new_with_label(_utf("Search"));
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(search_cb), bdi);
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 10);
/* Show it */
g_signal_connect(dialog, "response", G_CALLBACK(response_cb), bdi);
gtk_widget_show_all(dialog);
}
#endif /* SYS_ unix-like */
/***
*** Show the manual in an external browser
***/
/*
* Check the child processes exit status
* to find whether the browser could be invoked.
*/
typedef struct
{ pid_t pid;
char *url;
GtkWidget *msg;
int seconds;
} browser_info;
static void msg_destroy_cb(GtkWidget *widget, gpointer data)
{ browser_info *bi = (browser_info*)data;
bi->msg = NULL;
}
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
/*
* The following list of browsers and html wrappers
* will be tried one at a time until one entry succeeds by:
* - returning zero
* - not returning within 60 seconds
*/
static int browser_index;
static void try_browser(browser_info*);
static char *browsers[] =
{ "user-selection",
"xdg-open",
"gnome-open",
"htmlview",
"firefox",
"mozilla",
"konqueror",
"epiphany",
"opera",
"/Applications/Safari.app/Contents/MacOS/Safari", /* better way to do this? */
NULL
};
static gboolean browser_timeout_func(gpointer data)
{ browser_info *bi = (browser_info*)data;
int status;
waitpid(bi->pid, &status, WNOHANG);
/* At least mozilla returns random values under FreeBSD on success,
so we can't rely on the return value exept our own 110 one. */
if(WIFEXITED(status))
{
switch(WEXITSTATUS(status))
{ case 110: /* browser did not execute */
browser_index++;
if(!browsers[browser_index]) /* all browsers from the list failed */
{ browser_dialog(bi->url);
if(bi->msg)
gtk_widget_destroy(bi->msg);
if(bi->url)
g_free(bi->url);
g_free(bi);
}
else /* try next browser from list */
{ bi->seconds = 0;
try_browser(bi);
}
return FALSE;
case 0: /* browser assumed to be successful */
default:
if(bi->msg)
gtk_widget_destroy(bi->msg);
if(bi->url)
g_free(bi->url);
g_free(bi);
return FALSE;
}
}
bi->seconds++;
if(bi->seconds == 10 && bi->msg)
{ gtk_widget_destroy(bi->msg);
bi->msg = NULL;
}
return bi->seconds > 60 ? FALSE : TRUE;
}
#endif /* SYS_ unix-like */
#ifdef SYS_MINGW
static gboolean browser_timeout_func(gpointer data)
{ browser_info *bi = (browser_info*)data;
bi->seconds++;
if(bi->seconds >= 10)
{ if(bi->msg)
{ gtk_widget_destroy(bi->msg);
bi->msg = NULL;
}
if(bi->url) g_free(bi->url);
g_free(bi);
return FALSE;
}
return TRUE;
}
#endif /* SYS_MINGW */
/*
* Invoke the browser
*/
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
static void try_browser(browser_info *bi)
{ pid_t pid;
bi->pid = pid = fork();
if(pid == -1)
{ printf("fork failed\n");
return;
}
/* make the parent remember and wait() for the browser */
if(pid > 0)
{ g_timeout_add(1000, browser_timeout_func, (gpointer)bi);
if(browser_index)
{ g_free(Closure->browser);
Closure->browser = g_strdup(browsers[browser_index]);
}
}
/* try calling the browser */
if(pid == 0)
{ char *argv[10];
int argc = 0;
argv[argc++] = browser_index ? browsers[browser_index] : Closure->browser;
argv[argc++] = bi->url;
argv[argc++] = NULL;
execvp(argv[0], argv);
_exit(110); /* couldn't execute */
}
}
#endif /* SYS_ unix-like */
void ShowHTML(char *target)
{ browser_info *bi = g_malloc0(sizeof(browser_info));
guint64 ignore;
const char *lang;
char *path = NULL;
int http_url;
/* If no target is given, select between translations of the manual. */
if(!target) target = g_strdup("index.html");
http_url = strlen(target) > 4 && !strncmp(target, "http", 4);
if(!http_url && !strchr(target, '/')) /* create full path */
{
if(!Closure->docDir)
{
CreateMessage(_("Documentation not installed."), GTK_MESSAGE_ERROR);
g_free(bi);
return;
}
lang = g_getenv("LANG");
if(lang)
{ if(!strncmp(lang, "ru", 2))
#ifdef SYS_MINGW
path = g_strdup_printf("%s\\ru\\%s",Closure->docDir,target);
#else
path = g_strdup_printf("%s/ru/%s",Closure->docDir,target);
#endif
else if(!strncmp(lang, "de", 2))
#ifdef SYS_MINGW
path = g_strdup_printf("%s\\de\\%s",Closure->docDir,target);
#else
path = g_strdup_printf("%s/de/%s",Closure->docDir,target);
#endif
}
if(!path)
{
#ifdef SYS_MINGW
path = g_strdup_printf("%s\\en\\%s",Closure->docDir,target);
#else
path = g_strdup_printf("%s/en/%s",Closure->docDir,target);
#endif
}
#ifdef SYS_MINGW
if(!LargeStat(path, &ignore))
{
g_free(path); /* the local dir is Windows specific */
path = g_strdup_printf("%s\\local\\%s",Closure->docDir,target);
}
#endif
g_free(target);
bi->url = path;
}
else bi->url = target;
if(!http_url && !LargeStat(bi->url, &ignore))
{
CreateMessage(_("Documentation file\n%s\nnot found.\n"), GTK_MESSAGE_ERROR, bi->url);
g_free(bi);
g_free(bi->url);
return;
}
/* Lock the help button and show a message for 10 seconds. */
TimedInsensitive(Closure->helpButton, 10000);
bi->msg = CreateMessage(_("Please hang on until the browser comes up!"), GTK_MESSAGE_INFO);
g_signal_connect(G_OBJECT(bi->msg), "destroy", G_CALLBACK(msg_destroy_cb), bi);
#ifdef SYS_MINGW
/* Okay, Billy wins big time here ;-) */
ShellExecute(NULL, "open", bi->url, NULL, NULL, SW_SHOWNORMAL);
g_timeout_add(1000, browser_timeout_func, (gpointer)bi);
#endif
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
/* Try the first browser */
browser_index = 0;
try_browser(bi);
#endif
}

365
src/show-manual.c Normal file
View File

@@ -0,0 +1,365 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
#include <sys/wait.h>
#endif
#ifdef SYS_MINGW
#include "windows.h"
#include "shellapi.h"
#endif
/***
*** Ask user to specify his viewer
***/
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
#define SEARCH_BUTTON 1
typedef struct
{ GtkWidget *dialog;
GtkWidget *entry;
GtkWidget *search;
GtkWidget *filesel;
GtkWidget *fileok;
GtkWidget *filecancel;
char *path;
} viewer_dialog_info;
static void response_cb(GtkWidget *widget, int response, gpointer data)
{ viewer_dialog_info *bdi = (viewer_dialog_info*)data;
switch(response)
{ case GTK_RESPONSE_ACCEPT:
if(Closure->viewer) g_free(Closure->viewer);
Closure->viewer = g_strdup(gtk_entry_get_text(GTK_ENTRY(bdi->entry)));
ShowPDF(bdi->path);
break;
case GTK_RESPONSE_REJECT:
if(bdi->path) g_free(bdi->path);
break;
}
gtk_widget_destroy(widget);
if(bdi->filesel)
gtk_widget_destroy(bdi->filesel);
g_free(bdi);
}
static void search_cb(GtkWidget *widget, gpointer data)
{ viewer_dialog_info *bdi = (viewer_dialog_info*)data;
if(widget == bdi->search)
{ bdi->filesel = gtk_file_selection_new(_utf("windowtitle|Choose a PDF viewer"));
bdi->fileok = GTK_FILE_SELECTION(bdi->filesel)->ok_button;
bdi->filecancel = GTK_FILE_SELECTION(bdi->filesel)->cancel_button;
ReverseCancelOK(GTK_DIALOG(bdi->filesel));
gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(bdi->filesel));
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->ok_button), "clicked",
G_CALLBACK(search_cb), bdi);
g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(bdi->filesel)->cancel_button), "clicked",
G_CALLBACK(search_cb), bdi);
gtk_widget_show(bdi->filesel);
}
if(widget == bdi->fileok)
{
if(Closure->viewer) g_free(Closure->viewer);
Closure->viewer = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(bdi->filesel)));
ShowPDF(bdi->path);
gtk_widget_destroy(bdi->filesel);
gtk_widget_destroy(bdi->dialog);
g_free(bdi);
return;
}
if(widget == bdi->filecancel)
{ gtk_widget_destroy(bdi->filesel);
bdi->filesel = NULL;
}
}
static void viewer_dialog(char *path)
{ GtkWidget *dialog, *vbox, *hbox, *label, *entry, *button;
viewer_dialog_info *bdi = g_malloc0(sizeof(viewer_dialog_info));
/* Create the dialog */
dialog = gtk_dialog_new_with_buttons(_utf("windowtitle|PDF viewer required"),
Closure->window, GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL);
bdi->dialog = dialog;
if(path)
{ bdi->path = g_strdup(path);
}
vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
/* Insert the contents */
label = gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(label), _utf("<b>Could not find a suitable PDF viewer.</b>\n\n"
"Which PDF viewer would you like to use\n"
"for reading the online documentation?\n\n"
"Please enter its name (e.g. xpdf) or\n"
"use the \"Search\" button for a file dialog.\n")),
gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 10);
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 10);
bdi->entry = entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 10);
bdi->search = button = gtk_button_new_with_label(_utf("Search"));
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(search_cb), bdi);
gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 10);
/* Show it */
g_signal_connect(dialog, "response", G_CALLBACK(response_cb), bdi);
gtk_widget_show_all(dialog);
}
#endif /* SYS_ unix-like */
/***
*** Show the manual in an external viewer
***/
/*
* Check the child processes exit status
* to find whether the viewer could be invoked.
*/
typedef struct
{ pid_t pid;
char *path;
GtkWidget *msg;
int seconds;
} viewer_info;
static void msg_destroy_cb(GtkWidget *widget, gpointer data)
{ viewer_info *bi = (viewer_info*)data;
bi->msg = NULL;
}
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
/*
* The following list of viewers
* will be tried one at a time until one entry succeeds by:
* - returning zero
* - not returning within 60 seconds
*/
static int viewer_index;
static void try_viewer(viewer_info*);
static char *viewers[] =
{ "evince",
"xpdf",
"okular",
"gv",
"mupdf",
"pdfcube",
"zathura",
NULL
};
static gboolean viewer_timeout_func(gpointer data)
{ viewer_info *bi = (viewer_info*)data;
int status;
waitpid(bi->pid, &status, WNOHANG);
/* At least mozilla returns random values under FreeBSD on success,
so we can't rely on the return value exept our own 110 one. */
if(WIFEXITED(status))
{
switch(WEXITSTATUS(status))
{ case 110: /* viewer did not execute */
viewer_index++;
if(!viewers[viewer_index]) /* all viewers from the list failed */
{ viewer_dialog(bi->path);
if(bi->msg)
gtk_widget_destroy(bi->msg);
if(bi->path)
g_free(bi->path);
g_free(bi);
}
else /* try next viewer from list */
{ bi->seconds = 0;
try_viewer(bi);
}
return FALSE;
case 0: /* viewer assumed to be successful */
default:
if(bi->msg)
gtk_widget_destroy(bi->msg);
if(bi->path)
g_free(bi->path);
g_free(bi);
return FALSE;
}
}
bi->seconds++;
if(bi->seconds == 10 && bi->msg)
{ gtk_widget_destroy(bi->msg);
bi->msg = NULL;
}
return bi->seconds > 60 ? FALSE : TRUE;
}
#endif /* SYS_ unix-like */
#ifdef SYS_MINGW
static gboolean viewer_timeout_func(gpointer data)
{ viewer_info *bi = (viewer_info*)data;
bi->seconds++;
if(bi->seconds >= 10)
{ if(bi->msg)
{ gtk_widget_destroy(bi->msg);
bi->msg = NULL;
}
//if(bi->url) g_free(bi->url);
g_free(bi);
return FALSE;
}
return TRUE;
}
#endif /* SYS_MINGW */
/*
* Invoke the viewer
*/
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_KFREEBSD) || \
defined(SYS_NETBSD) || defined(SYS_HURD)
static void try_viewer(viewer_info *bi)
{ pid_t pid;
bi->pid = pid = fork();
if(pid == -1)
{ printf("fork failed\n");
return;
}
/* make the parent remember and wait() for the viewer */
if(pid > 0)
{ g_timeout_add(1000, viewer_timeout_func, (gpointer)bi);
if(viewer_index)
{ g_free(Closure->viewer);
Closure->viewer = g_strdup(viewers[viewer_index]);
}
}
/* try calling the viewer */
if(pid == 0)
{ char *argv[10];
int argc = 0;
argv[argc++] = viewer_index ? viewers[viewer_index] : Closure->viewer;
argv[argc++] = bi->path;
argv[argc++] = NULL;
execvp(argv[0], argv);
_exit(110); /* couldn't execute */
}
}
#endif /* SYS_ unix-like */
void ShowPDF(char *target)
{ viewer_info *bi = g_malloc0(sizeof(viewer_info));
guint64 ignore;
if(!Closure->docDir)
{
CreateMessage(_("Documentation not installed."), GTK_MESSAGE_ERROR);
g_free(bi);
return;
}
/* If no target is given, show the manual. */
if(!target)
{ bi->path = g_strdup_printf("%s/manual.pdf",Closure->docDir);
}
else
if(*target != '/') bi->path = g_strdup_printf("%s/%s",Closure->docDir, target);
else bi->path = g_strdup(target);
if(!LargeStat(bi->path, &ignore))
{
CreateMessage(_("Documentation file\n%s\nnot found.\n"), GTK_MESSAGE_ERROR, bi->path);
g_free(bi->path);
g_free(bi);
return;
}
/* Lock the help button and show a message for 10 seconds. */
TimedInsensitive(Closure->helpButton, 10000);
bi->msg = CreateMessage(_("Please hang on until the viewer comes up!"), GTK_MESSAGE_INFO);
g_signal_connect(G_OBJECT(bi->msg), "destroy", G_CALLBACK(msg_destroy_cb), bi);
/* Try the first viwer */
#ifdef SYS_MINGW
/* Okay, Billy wins big time here ;-) */
ShellExecute(NULL, "open", bi->path, NULL, NULL, SW_SHOWNORMAL);
g_timeout_add(1000, viewer_timeout_func, (gpointer)bi);
#endif
#if defined(SYS_LINUX) || defined(SYS_FREEBSD) || defined(SYS_DARWIN) || defined(SYS_NETBSD)
viewer_index = 0;
try_viewer(bi);
#endif
}

1711
src/smart-lec.c Normal file

File diff suppressed because it is too large Load Diff

265
src/spiral.c Normal file
View File

@@ -0,0 +1,265 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
/***
*** Archimede did not publish his source,
*** so we have to write our own routines ;-)
*/
/*
* Allocate and fill in the spiral data structure
*/
Spiral* CreateSpiral(GdkColor *outline, GdkColor *fill,
int start_radius, int segment_size, int n_segments)
{ Spiral *spiral = g_malloc0(sizeof(Spiral));
double a = 0.0;
double scale_o = start_radius + segment_size;
double ring_expand;
int i;
spiral->startRadius = start_radius;
spiral->segmentSize = segment_size;
spiral->segmentCount = spiral->segmentClipping = n_segments;
spiral->segmentPos = g_malloc(n_segments * sizeof(double));
spiral->segmentColor = g_malloc(n_segments * sizeof(GdkColor*));
spiral->outline = outline;
spiral->cursorPos = -1;
for(i=0; i<n_segments; i++)
{
spiral->segmentPos[i] = a;
spiral->segmentColor[i] = fill;
ring_expand = ((double)segment_size * a) / (2.0*M_PI);
a += atan((double)segment_size / scale_o);
scale_o = (double)start_radius + ring_expand + (double)segment_size;
}
spiral->diameter = 2.0 * scale_o;
return spiral;
}
void SetSpiralWidget(Spiral *spiral, GtkWidget *widget)
{ GtkAllocation *al = &widget->allocation;
if(!spiral->drawable)
{ spiral->drawable = widget->window;
spiral->mx = al->width/2;
spiral->my = al->height/2;
}
}
void FreeSpiral(Spiral *spiral)
{ g_free(spiral->segmentPos);
g_free(spiral->segmentColor);
g_free(spiral);
}
/*
* Fill spiral segments with given color
*/
void FillSpiral(Spiral *spiral, GdkColor *color)
{ int i;
if(spiral)
for(i=0; i<spiral->segmentCount; i++)
spiral->segmentColor[i] = color;
}
/*
* Draw the whole spiral
*/
void DrawSpiral(Spiral *spiral)
{ double a;
int xi0,yi0,xo0,yo0;
double scale_i,scale_o;
int i;
GdkPoint points[4];
if(!spiral->drawable) return;
scale_i = spiral->startRadius;
scale_o = spiral->startRadius + spiral->segmentSize;
xi0 = spiral->mx + spiral->startRadius;
yi0 = yo0 = spiral->my;
xo0 = xi0 + spiral->segmentSize;
for(a=0.0, i=0; i<spiral->segmentClipping; i++)
{ int xi1,yi1,xo1,yo1;
double ring_expand = ((double)spiral->segmentSize * a) / (2.0*M_PI);
a += atan((double)spiral->segmentSize / scale_o);
scale_i = (double)spiral->startRadius + ring_expand;
scale_o = scale_i + spiral->segmentSize;
xi1 = spiral->mx + scale_i*cos(a);
yi1 = spiral->my + scale_i*sin(a);
xo1 = spiral->mx + scale_o*cos(a);
yo1 = spiral->my + scale_o*sin(a);
points[0].x = xi0; points[0].y = yi0;
points[1].x = xo0; points[1].y = yo0;
points[2].x = xo1; points[2].y = yo1;
points[3].x = xi1; points[3].y = yi1;
gdk_gc_set_rgb_fg_color(Closure->drawGC, spiral->segmentColor[i]);
gdk_draw_polygon(spiral->drawable, Closure->drawGC, TRUE, points, 4);
gdk_gc_set_rgb_fg_color(Closure->drawGC, spiral->outline);
gdk_draw_polygon(spiral->drawable, Closure->drawGC, FALSE, points, 4);
xi0 = xi1; yi0 = yi1;
xo0 = xo1; yo0 = yo1;
}
}
/*
* Draw just one segment of the spiral
*/
void DrawSpiralSegment(Spiral *spiral, GdkColor *color, int segment)
{ double a;
double scale_i,scale_o,ring_expand;
GdkPoint points[4];
if(segment<0 || segment>=spiral->segmentClipping)
return;
a = spiral->segmentPos[segment];
ring_expand = ((double)spiral->segmentSize * a) / (2.0*M_PI);
scale_i = (double)spiral->startRadius + ring_expand;
scale_o = scale_i + spiral->segmentSize;
points[0].x = spiral->mx + scale_i*cos(a);
points[0].y = spiral->my + scale_i*sin(a);
points[1].x = spiral->mx + scale_o*cos(a);
points[1].y = spiral->my + scale_o*sin(a);
a += atan((double)spiral->segmentSize / scale_o);
ring_expand = ((double)spiral->segmentSize * a) / (2.0*M_PI);
scale_i = (double)spiral->startRadius + ring_expand;
scale_o = scale_i + spiral->segmentSize;
points[3].x = spiral->mx + scale_i*cos(a);
points[3].y = spiral->my + scale_i*sin(a);
points[2].x = spiral->mx + scale_o*cos(a);
points[2].y = spiral->my + scale_o*sin(a);
spiral->segmentColor[segment] = color;
gdk_gc_set_rgb_fg_color(Closure->drawGC, color);
gdk_draw_polygon(spiral->drawable, Closure->drawGC, TRUE, points, 4);
gdk_gc_set_rgb_fg_color(Closure->drawGC, spiral->outline);
gdk_draw_polygon(spiral->drawable, Closure->drawGC, FALSE, points, 4);
}
/*
* Draw a label above or below the spiral
*/
void DrawSpiralLabel(Spiral *spiral, PangoLayout *layout,
char *text, GdkColor *color, int x, int line)
{ GdkDrawable *d = spiral->drawable;
int w,h,y;
SetText(layout, text, &w, &h);
if(line > 0) y = spiral->my + spiral->diameter / 2 + 20 + (line-1) * (10 + h);
else y = spiral->my - spiral->diameter / 2 - 20 - h + (line+1) * (10 + h);
gdk_gc_set_rgb_fg_color(Closure->drawGC, color);
gdk_draw_rectangle(d, Closure->drawGC, TRUE, x, y+(h-6)/2, 6, 6);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->grid);
gdk_draw_rectangle(d, Closure->drawGC, FALSE, x, y+(h-6)/2, 6, 6);
gdk_gc_set_rgb_fg_color(Closure->drawGC, Closure->foreground);
gdk_draw_layout(d, Closure->drawGC, x+10, y, layout);
}
/*
* Move the cursor (a highlighted segment) to a new position.
* Moving to segment -1 means to disable the cursor.
*/
void MoveSpiralCursor(Spiral *spiral, int to_segment)
{
if(to_segment == spiral->cursorPos)
return;
if(to_segment > spiral->segmentClipping)
return;
/* Erase old cursor */
if(spiral->cursorPos >= 0)
DrawSpiralSegment(spiral, spiral->colorUnderCursor, spiral->cursorPos);
/* Moving to -1 means cursor off */
spiral->cursorPos = to_segment;
if(to_segment < 0)
return;
if(to_segment > spiral->segmentCount-1)
{ spiral->cursorPos = -1;
return;
}
/* Draw cursor at new place */
spiral->colorUnderCursor = spiral->segmentColor[to_segment];
DrawSpiralSegment(spiral, Closure->blueSector, to_segment);
}
/*
* Wrapper for moving the spiral cursor from non-GUI thread
*/
typedef struct _cursor_info
{ Spiral *spiral;
int segment;
} cursor_info;
static gboolean cursor_idle_func(gpointer data)
{ cursor_info *ci = (cursor_info*)data;
MoveSpiralCursor(ci->spiral, ci->segment);
g_free(ci);
return FALSE;
}
void ChangeSpiralCursor(Spiral *spiral, int segment)
{
if(segment != spiral->cursorPos)
{ cursor_info *ci = g_malloc(sizeof(cursor_info));
ci->spiral = spiral;
ci->segment = segment;
g_idle_add(cursor_idle_func, ci);
}
}

980
src/udf.c Normal file
View File

@@ -0,0 +1,980 @@
/* 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 "udf.h"
/***
*** Rudimentary UDF and ISO filesystem parsing.
***
* Information about UDF and ISO was gathered from ECMA-119,
* ECMA-167, ECMA-167 and the UDF 2.6 standard from OSTA.org.
* However, no claims are made to be actually conformant to any
* of those standards.
*/
/*
* ECMA sector indices start with 1;
* this definition make it easier to use the standard for reading strings
* from the buffer.
* begin and end are the bp postions of the string start and end position.
*/
/*
* 8bit values
*/
#define bp_get_byte(buf, idx) (buf[(idx)-1])
#define bp_set_byte(buf, idx, byte) (buf[(idx)-1] = (unsigned char)byte)
/*
* 16bit both byte order
*/
#ifdef HAVE_BIG_ENDIAN
#define bp_get_short_bbo(buf, idx) (*((guint16*)(buf+idx+1)))
#else
#define bp_get_short_bbo(buf, idx) (*((guint16*)(buf+idx-1)))
#endif
static void bp_set_short_bbo(unsigned char *buf, int idx, guint16 value)
{ unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
*buf++ = wx;
*buf++ = yz;
}
static void bp_set_short_lsb(unsigned char *buf, int idx, guint16 value)
{ unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
}
static void bp_set_short_msb(unsigned char *buf, int idx, guint16 value)
{ unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = wx;
*buf++ = yz;
}
/*
* 32bit single byte order
*/
#ifdef HAVE_BIG_ENDIAN
#define bp_get_long_lsb(buf, idx) (((guint32)buf[(idx)+2])<<24|((guint32)buf[(idx)+1])<<16|((guint32)buf[(idx)])<<8|((guint32)buf[(idx)-1]))
#else
#define bp_get_long_lsb(buf, idx) (*((guint32*)(buf+idx-1)))
#endif
#define bp_get_long_msb(buf, idx) (((guint32)buf[(idx)-1])<<24|((guint32)buf[idx])<<16|((guint32)buf[(idx)+1])<<8|((guint32)buf[(idx)+2]))
static void bp_set_long_lsb(unsigned char *buf, int idx, guint32 value)
{ unsigned char st = (value >> 24) & 0xff;
unsigned char uv = (value >> 16) & 0xff;
unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
*buf++ = uv;
*buf++ = st;
}
static void bp_set_long_msb(unsigned char *buf, int idx, guint32 value)
{ unsigned char st = (value >> 24) & 0xff;
unsigned char uv = (value >> 16) & 0xff;
unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = st;
*buf++ = uv;
*buf++ = wx;
*buf++ = yz;
}
/*
* 32bit both byte order
*/
#ifdef HAVE_BIG_ENDIAN
#define bp_get_long_bbo(buf, idx) (*((guint32*)(buf+idx+3)))
#else
#define bp_get_long_bbo(buf, idx) (*((guint32*)(buf+idx-1)))
#endif
static void bp_set_long_bbo(unsigned char *buf, int idx, guint32 value)
{ unsigned char st = (value >> 24) & 0xff;
unsigned char uv = (value >> 16) & 0xff;
unsigned char wx = (value >> 8) & 0xff;
unsigned char yz = value & 0xff;
buf = buf + idx - 1;
*buf++ = yz;
*buf++ = wx;
*buf++ = uv;
*buf++ = st;
*buf++ = st;
*buf++ = uv;
*buf++ = wx;
*buf++ = yz;
}
static void bp_get(unsigned char *dest, unsigned char *src, int begin, int end)
{ int length = end-begin+1;
strncpy((char*)dest, (char*)src+begin-1, length);
}
#if 0
static void bp_set(unsigned char *dest, unsigned char *src, int begin, int end)
{ int length = end-begin+1;
memcpy((char*)dest+begin-1, (char*)src, length);
}
#endif
/*
* String operations
*/
static void bp_get_string(unsigned char *dest, unsigned char *src, int begin, int end)
{ int length = end-begin+1;
strncpy((char*)dest, (char*)src+begin-1, length);
dest[length] = 0;
}
static void bp_set_string(unsigned char *dest, char *src, int begin, int end)
{ int length = end-begin+1;
strncpy((char*)(dest+begin-1), src, length);
}
/*
* D1 string conversion currently simply expands the string to 16bit characters.
*/
static void bp_set_d1_string(unsigned char *dest, char *src, int begin, int end)
{
dest = dest + begin - 1;
while(begin<=end-2 && *src)
{ *dest++ = 0;
*dest++ = (unsigned char)*src++;
begin += 2;
}
}
/*
* Date values
*/
static void get_date(unsigned char *date, unsigned char *buf, int idx)
{
idx--;
sprintf((char*)date, "dd-mm-yyyy hh:mm:ss.ss");
bp_get(date+ 6, buf, idx+ 1, idx+ 4);
bp_get(date+ 3, buf, idx+ 5, idx+ 6);
bp_get(date+ 0, buf, idx+ 7, idx+ 8);
bp_get(date+11, buf, idx+ 9, idx+10);
bp_get(date+14, buf, idx+11, idx+12);
bp_get(date+17, buf, idx+13, idx+14);
bp_get(date+20, buf, idx+15, idx+16);
}
static void set_date(unsigned char *date, int idx,
int year, int month, int day,
int hour, int minute, int second, int hsecond,
int gmt_offset)
{
sprintf((char*)(date+idx-1), "%04d%02d%02d%02d%02d%02d%02d",
year, month, day, hour, minute, second, hsecond);
*(date+idx+15) = (unsigned char)gmt_offset;
}
static void set_binary_date(unsigned char *dest, int idx,
int year, int month, int day,
int hour, int minute, int second,
int gmt_offset)
{
dest = dest + idx - 1;
*dest++ = (unsigned char)year;
*dest++ = (unsigned char)month;
*dest++ = (unsigned char)day;
*dest++ = (unsigned char)hour;
*dest++ = (unsigned char)minute;
*dest++ = (unsigned char)second;
*dest++ = (unsigned char)gmt_offset;
}
static void beautify_dchar(char *dchar)
{ int idx = strlen(dchar)-1;
while(idx>=0)
{ if(dchar[idx] != ' ') break;
dchar[idx--] = 0;
}
while(idx>0)
{ if(dchar[idx-1] != ' ')
dchar[idx] = tolower((int)dchar[idx]);
idx--;
}
if(!*dchar) strcpy(dchar, _("Unnamed"));
}
/***
*** Extract some useful information from the ISO file system
***/
void FreeIsoInfo(IsoInfo *ii)
{
g_free(ii);
}
static IsoInfo* examine_primary_vd(unsigned char *buf)
{ IsoInfo *ii = g_malloc(sizeof(IsoInfo));
unsigned char vlabel[33];
unsigned char str[80];
guint32 x,vss;
unsigned char date[32];
bp_get_string(str, buf, 9, 40);
Verbose(" System identifier : |%s|\n", str);
bp_get_string(vlabel, buf, 41, 72);
Verbose(" Volume identifier : |%s|\n", vlabel);
vss = bp_get_long_bbo(buf, 81);
Verbose(" Volume space size : %d sectors\n", vss);
x = bp_get_short_bbo(buf, 121);
Verbose(" Volume set size : %d\n", x);
x = bp_get_short_bbo(buf, 125);
Verbose(" Volume sequence size : %d\n", x);
x = bp_get_short_bbo(buf, 129);
Verbose(" Logical block size : %d\n", x);
x = bp_get_long_bbo(buf, 133);
Verbose(" Path table size : %d bytes\n", x);
x = bp_get_long_lsb(buf, 141);
Verbose(" L-Path table location : %d\n", x);
x = bp_get_long_lsb(buf, 145);
Verbose(" Opt L-Path table location : %d\n", x);
x = bp_get_long_msb(buf, 149);
Verbose(" M-Path table location : %d\n", x);
x = bp_get_long_msb(buf, 153);
Verbose(" Opt M-Path table location : %d\n", x);
/* 157 .. 190 directory record */
/* 191 .. 318 Volume set identifier */
/* 319 .. 446 Publisher Identifier */
/* 447 .. 574 Data Preparer Identifier */
/* 575 .. 702 Application Identfier */
/* 703 .. 739 Copyright File Identifier */
/* 740 .. 776 Abstract File Identifier */
/* 777 .. 813 Bibliographic File Identifier */
get_date(date, buf, 814);
Verbose(" Volume creation date/time : %s\n", date);
get_date(str, buf, 831);
Verbose(" Volume modification d/t : %s\n", str);
get_date(str, buf, 848);
Verbose(" Volume expiration d/t : %s\n", str);
get_date(str, buf, 865);
Verbose(" Volume effective d/t : %s\n", str);
x = bp_get_byte(buf,882);
Verbose(" File structure version : %d\n", x);
/* Extract information for IsoInfo */
ii->volumeSize = vss;
if(!Closure->screenShotMode)
{ strcpy(ii->volumeLabel, (char*)vlabel);
beautify_dchar(ii->volumeLabel);
}
else strcpy(ii->volumeLabel, _("Example disc"));
strcpy(ii->creationDate, (char*)date);
ii->creationDate[10] = 0;
return ii;
}
static IsoInfo* examine_iso(Image *image)
{ AlignedBuffer *ab = CreateAlignedBuffer(2048);
unsigned char *buf = ab->buf;
IsoInfo *ii = NULL;
int sector;
int vdt,vdt_ver;
unsigned char sid[6];
Verbose(" Examining the ISO file system...\n");
/*** Iterate over the volume decriptors */
for(sector=16; sector<32; sector++)
{
#ifndef CLI
if(Closure->stopActions)
continue;
#endif
if(ImageReadSectors(image, buf, sector, 1) != 1)
{ Verbose(" Sector %2d: unreadable\n", sector);
continue;
}
vdt = bp_get_byte(buf, 1); /* Volume descriptor type */
bp_get_string(sid, buf, 2, 6); /* Standard identifier */
vdt_ver = bp_get_byte(buf,7); /* Volume descriptor version */
Verbose(" Sector %2d:\n"
" Volume descriptor type = %d\n"
" Volume descriptor version = %d\n"
" Standard identifier = %s\n",
sector, vdt, vdt_ver, sid);
if(strncmp((char*)sid,"CD001",5))
{ Verbose(" * Wrong or missing standard identifier.\n");
continue;
}
switch(vdt)
{ case 0: Verbose(" -> boot record: *skipped*\n");
break;
case 1: Verbose(" -> primary volume descriptor:\n");
ii = examine_primary_vd(buf);
break;
case 2: Verbose(" -> supplementary volume descriptor: *skipped*\n");
break;
case 255: Verbose(" -> volume descriptor set terminator;\n"
" end of ISO file system parsing.\n");
goto finished;
break;
default : Verbose(" -> unknown volume descriptor: *skipped*\n");
break;
}
}
finished:
FreeAlignedBuffer(ab);
return ii;
}
/***
*** The main wrapper for visiting the ISO and UDF file system structures
***/
void ExamineUDF(Image *image)
{
if(!image) return;
if(image->type == IMAGE_MEDIUM) Verbose("\nExamineUDF(Device: %s)\n", image->dh->devinfo);
if(image->type == IMAGE_FILE ) Verbose("\nExamineUDF(File: %s)\n", image->file->path);
image->isoInfo = examine_iso(image);
Verbose(" Examining the UDF file system...\n");
Verbose(" not yet implemented.\n\n");
}
/***
*** Rudimentary support for creating .iso images.
*** Currently only used for creating the random image;
* can only create .iso images containing exactly one file.
*/
/*
* Directory record writing
*/
static IsoDir* create_iso_dir()
{ IsoDir *id = g_malloc0(sizeof(IsoDir));
id->dir = g_malloc0(2048);
id->nSectors = 1;
return id;
}
static void free_iso_dir(IsoDir *id)
{ g_free(id->dir);
g_free(id);
}
static void add_directory_record(IsoDir *iso_dir,
int extent, int file_flags, int file_identifier,
int year, int month, int day,
int hour, int minute, int second, int gmt_offset)
{ unsigned char *dir = iso_dir->dir + iso_dir->tail;
iso_dir->tail += 34;
bp_set_byte(dir, 1, 34); /* Length of directory record */
bp_set_byte(dir, 2, 0); /* Extended attribute length */
bp_set_long_bbo(dir, 3, extent); /* Location of Extent */
bp_set_long_bbo(dir, 11, 2048); /* Data length of file section */
set_binary_date(dir, 19, /* Binary date rep of creation time */
year,month,day,hour,minute,second,gmt_offset);
bp_set_byte(dir, 26, file_flags); /* File flags (we are a directory) */
bp_set_byte(dir, 27, 0); /* File unit size if interleaved */
bp_set_byte(dir, 28, 0); /* Interleave gap size */
bp_set_short_bbo(dir, 29, 1); /* Volume sequence number */
bp_set_byte(dir, 33, 1); /* Length of file identifier */
bp_set_byte(dir, 34, file_identifier); /* File identifier */
}
static void add_file_record(IsoDir *iso_dir, char *filename, guint64 size, int extent,
int year, int month, int day,
int hour, int minute, int second, int gmt_offset)
{ unsigned char *dir = iso_dir->dir + iso_dir->tail;
int sl = strlen(filename);
int entry_len = 34+sl;
iso_dir->tail += entry_len;
bp_set_byte(dir, 1, entry_len); /* Length of directory record */
bp_set_byte(dir, 2, 0); /* Extended attribute length */
bp_set_long_bbo(dir, 3, extent); /* Location of Extent */
bp_set_long_bbo(dir, 11, size); /* Data length of file section */
set_binary_date(dir, 19, /* Binary date rep of creation time */
year,month,day,hour,minute,second,gmt_offset);
bp_set_byte(dir, 26, 0); /* File flags (we are a file) */
bp_set_byte(dir, 27, 0); /* File unit size if interleaved */
bp_set_byte(dir, 28, 0); /* Interleave gap size */
bp_set_short_bbo(dir, 29, 1); /* Volume sequence number */
bp_set_byte(dir, 33, sl); /* Length of file identifier */
bp_set_string(dir, filename, 34, 34+sl); /* File name */
}
static void add_d1_file_record(IsoDir *iso_dir, char *filename, guint64 size, int extent,
int year, int month, int day,
int hour, int minute, int second, int gmt_offset)
{ unsigned char *dir = iso_dir->dir + iso_dir->tail;
int sl = 2*strlen(filename);
int entry_len = 34+sl;
iso_dir->tail += entry_len;
bp_set_byte(dir, 1, entry_len); /* Length of directory record */
bp_set_byte(dir, 2, 0); /* Extended attribute length */
bp_set_long_bbo(dir, 3, extent); /* Location of Extent */
bp_set_long_bbo(dir, 11, size); /* Data length of file section */
set_binary_date(dir, 19, /* Binary date rep of creation time */
year,month,day,hour,minute,second,gmt_offset);
bp_set_byte(dir, 26, 0); /* File flags (we are a file) */
bp_set_byte(dir, 27, 0); /* File unit size if interleaved */
bp_set_byte(dir, 28, 0); /* Interleave gap size */
bp_set_short_bbo(dir, 29, 1); /* Volume sequence number */
bp_set_byte(dir, 33, sl); /* Length of file identifier */
bp_set_d1_string(dir, filename, 34, 34+sl); /* File name */
}
/*
* Path tables
*/
static IsoPathTable* create_iso_path_table()
{ IsoPathTable *ipt = g_malloc0(sizeof(IsoPathTable));
ipt->lpath = g_malloc0(2048);
ipt->mpath = g_malloc0(2048);
ipt->nSectors = 1;
return ipt;
}
static void free_iso_path_table(IsoPathTable *ipt)
{ g_free(ipt->lpath);
g_free(ipt->mpath);
g_free(ipt);
}
static void add_path(IsoPathTable *ipt, int extent)
{ unsigned char *lpath = ipt->lpath + ipt->tail;
unsigned char *mpath = ipt->mpath + ipt->tail;
bp_set_byte(lpath, 1, 1); /* Length of directory identifier (root=1) */
bp_set_byte(mpath, 1, 1);
bp_set_byte(lpath, 2, 0); /* Extended attribute length */
bp_set_byte(mpath, 2, 0); /* Extended attribute length */
bp_set_long_lsb(lpath, 3, extent); /* Location of extent */
bp_set_long_msb(mpath, 3, extent);
bp_set_short_lsb(lpath, 7, 1); /* Parent directory number */
bp_set_short_msb(mpath, 7, 1);
bp_set_byte(lpath, 9, 0); /* Directory identifier (root=0) */
bp_set_byte(mpath, 9, 0);
}
/*
* Initialize the IsoHeader structure
*/
IsoHeader* InitIsoHeader(void)
{ IsoHeader *ih = g_malloc0(sizeof(IsoHeader));
unsigned char *pvd,*svd;
char blank[128], string[128];
memset(blank, ' ', 128);
/*** Initialize primary volume descriptor (sector 16). */
ih->pvd = pvd = g_malloc0(2048);
ih->dirSectors = 1;
/* 1..8: Standard identifier */
bp_set_byte(pvd, 1, 1); /* Volume descriptor type 1 */
bp_set_string(pvd, "CD001", 2, 6); /* Standard identifier */
bp_set_byte(pvd, 7, 1); /* Volume descriptor version 1 */
/* 9..40: System identifier */
bp_set_string(pvd, blank, 9, 40);
/* 41..72: Volume identifier */
memcpy(string, blank, 128);
memcpy(string, "RANDOM IMAGE", 12);
bp_set_string(pvd, string, 41, 72);
/* 73.. 80: Unused, zero bytes */
/* 81.. 88: Volume space size, filled in at WriteIsoHeader() */
/* 89..120: Unused, zero bytes */
/* 121..124: Volume set size */
bp_set_short_bbo(pvd, 121, 1);
/* 125..128: Volume sequence number */
bp_set_short_bbo(pvd, 125, 1);
/* 129..132: Logical block size */
bp_set_short_bbo(pvd, 129, 2048);
/* 133..140: Path table size, filled in at WriteIsoHeader() */
/* 141..144: L Path table location, filled in at WriteIsoHeader() */
/* 145..148: Optional L Path table location, filled in at WriteIsoHeader() */
/* 149..152: M Path table location, filled in at WriteIsoHeader() */
/* 153..156: Optional M Path table location, filled in at WriteIsoHeader() */
/* 157..190: Directory record for root dir, filled in at WriteIsoHeader() */
/* 191..318: Volume set identifier */
bp_set_string(pvd, blank, 191, 318);
/* 319..446: Publisher identifier */
bp_set_string(pvd, blank, 319, 446);
/* 447..574: Data preparer identifier */
bp_set_string(pvd, blank, 447, 574);
/* 575..702 Application Identfier */
bp_set_string(pvd, blank, 575, 702);
/* 703..739 Copyright File Identifier */
bp_set_string(pvd, blank, 703, 739);
/* 740..776 Abstract File Identifier */
bp_set_string(pvd, blank, 740, 776);
/* 777..813 Bibliographic File Identifier */
bp_set_string(pvd, blank, 777, 813);
/* 814..830 Volume creation time, filled in by WriteIsoHeader() */
/* 831..847 Volume modification time, filled in by WriteIsoHeader() */
/* 848..864 Volume expiration time, filled in by WriteIsoHeader() */
/* 865..881 Volume effective time, filled in by WriteIsoHeader() */
/* 882 File structure version */
bp_set_byte(pvd, 882, 1);
/* 883 Reserved, zero */
/* 884..1395 for application use */
/* 1396..2048 Reserved, zero */
/*** Initialize supplementary volume descriptor (sector 17). */
ih->svd = svd = g_malloc0(2048);
ih->dirSectors = 2;
/* 1..8: Standard identifier */
bp_set_byte(svd, 1, 2); /* Volume descriptor type 1 */
bp_set_string(svd, "CD001", 2, 6); /* Standard identifier */
bp_set_byte(svd, 7, 1); /* Volume descriptor version 1 */
bp_set_byte(svd, 8, 0); /* Volume flags */
/* 9..40: System identifier (zero filled) */
/* 41..72: Volume identifier */
bp_set_d1_string(svd, "random image", 41, 72);
/* 73.. 80: Unused, zero bytes */
/* 81.. 88: Volume space size, filled in at WriteIsoHeader() */
/* 89..120: Escape sequences */
bp_set_byte(svd, 89, 37);
bp_set_byte(svd, 90, 47);
bp_set_byte(svd, 91, 64);
/* 121..124: Volume set size */
bp_set_short_bbo(svd, 121, 1);
/* 125..128: Volume sequence number */
bp_set_short_bbo(svd, 125, 1);
/* 129..132: Logical block size */
bp_set_short_bbo(svd, 129, 2048);
/* 133..140: Path table size, filled in at WriteIsoHeader() */
/* 141..144: L Path table location, filled in at WriteIsoHeader() */
/* 145..148: Optional L Path table location, filled in at WriteIsoHeader() */
/* 149..152: M Path table location, filled in at WriteIsoHeader() */
/* 153..156: Optional M Path table location, filled in at WriteIsoHeader() */
/* 157..190: Directory record for root dir, filled in at WriteIsoHeader() */
/* The standard says to fill the identifier field with blanks,
but actual writing software seems to use zeroes. */
/* 191..318: Volume set identifier */
//bp_set_string(svd, blank, 191, 318);
/* 319..446: Publisher identifier */
//bp_set_string(svd, blank, 319, 446);
/* 447..574: Data preparer identifier */
//bp_set_string(svd, blank, 447, 574);
/* 575..702 Application Identfier */
//bp_set_string(svd, blank, 575, 702);
/* 703..739 Copyright File Identifier */
//bp_set_string(svd, blank, 703, 739);
/* 740..776 Abstract File Identifier */
//bp_set_string(svd, blank, 740, 776);
/* 777..813 Bibliographic File Identifier */
//bp_set_string(svd, blank, 777, 813);
/* 814..830 Volume creation time, filled in by WriteIsoHeader() */
/* 831..847 Volume modification time, filled in by WriteIsoHeader() */
/* 848..864 Volume expiration time, filled in by WriteIsoHeader() */
/* 865..881 Volume effective time, filled in by WriteIsoHeader() */
/* 882 File structure version */
bp_set_byte(svd, 882, 1);
/* 883 Reserved, zero */
/* 884..1395 for application use */
/* 1396..2048 Reserved, zero */
/*** The volume descriptor set terminator (sector 17)
is trivial and created during WriteIsoHeader() */
ih->dirSectors = 3;
/*** Allocate space for directories and path tables */
ih->proot = create_iso_dir();
add_directory_record(ih->proot, 21, 2, 0,
106, 7, 16, 12, 35, 46, 8);
add_directory_record(ih->proot, 21, 2, 1,
106, 7, 16, 12, 35, 46, 8);
ih->ppath = create_iso_path_table();
add_path(ih->ppath, 21);
ih->sroot = create_iso_dir();
add_directory_record(ih->sroot, 24, 2, 0,
106, 7, 16, 12, 35, 46, 8);
add_directory_record(ih->sroot, 24, 2, 1,
106, 7, 16, 12, 35, 46, 8);
ih->spath = create_iso_path_table();
add_path(ih->spath, 24);
ih->dirSectors += 6;
return ih;
}
void FreeIsoHeader(IsoHeader *ih)
{
free_iso_dir(ih->proot);
free_iso_dir(ih->sroot);
free_iso_path_table(ih->ppath);
free_iso_path_table(ih->spath);
g_free(ih->pvd);
g_free(ih->svd);
g_free(ih);
}
/*
* Add file to iso image (works currently only for one file!)
*/
void AddFile(IsoHeader *ih, char *name, guint64 size)
{ static int n;
char iso[22], joliet[strlen(name)+3];
n++;
sprintf(iso,"RAN_%04d.DAT;1", n);
add_file_record(ih->proot, iso, size, 25,
106, 7, 16, 12, 28, 10, 8);
sprintf(joliet,"%s;1", name);
add_d1_file_record(ih->sroot, joliet, size, 25,
106, 7, 16, 12, 28, 10, 8);
ih->volumeSpace += (size+2047)/2048;
}
/*
* Write out the IsoHeader, return number of sectors written
*/
void WriteIsoHeader(IsoHeader *ih, LargeFile *image)
{ unsigned char zero[2048], sector[2048];
unsigned char *pvd,*svd;
int i;
/* The first 16 sectors are zeroed out */
memset(zero, 0, 2048);
for(i=0; i<16; i++)
{ int n = LargeWrite(image, zero, 2048);
if(n != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)i, strerror(errno));
}
/* Complete the primary volume descriptor */
pvd = ih->pvd;
/* 81..88: Volume space size */
ih->volumeSpace += 16 + ih->dirSectors;
bp_set_long_bbo(pvd, 81, ih->volumeSpace);
/* 133..140: Path table size, Fixme: use real values */
/* 141..144: L Path table location */
/* 145..148: Optional L Path table location */
/* 149..152: M Path table location */
/* 153..156: Optional M Path table location */
bp_set_long_bbo(pvd, 133, 10);
bp_set_long_lsb(pvd, 141, 19);
bp_set_long_lsb(pvd, 145, 0);
bp_set_long_msb(pvd, 149, 20);
bp_set_long_msb(pvd, 153, 0);
/* 157..190: Directory record for root directory, Fixme: use real values */
bp_set_byte(pvd, 157, 34); /* Length of directory record */
bp_set_byte(pvd, 158, 0); /* Extended atribute length */
bp_set_long_bbo(pvd, 159, 21); /* Location of Extent */
bp_set_long_bbo(pvd, 167, 2048); /* Data length of file section */
set_binary_date(pvd, 175, /* Binary date rep of creation time */
106, 7, 16, 10, 35, 46, 8);
bp_set_byte(pvd, 182, 2); /* File flags (we are a directory) */
bp_set_byte(pvd, 183, 0); /* File unit size if interleaved */
bp_set_byte(pvd, 184, 0); /* Interleave gap size */
bp_set_short_bbo(pvd, 185, 1); /* Volume sequence number */
bp_set_byte(pvd, 189, 1); /* Length of file identifier */
bp_set_byte(pvd, 190, 0); /* File identifier */
/* 814..830 Volume creation time */
set_date(pvd, 814, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 831..847 Volume modification time */
set_date(pvd, 831, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 848..864 Volume expiration time */
set_date(pvd, 848, 2106, 7, 16, 10, 35, 46, 23, 8);
/* 865..881 Volume effective time */
set_date(pvd, 865, 2006, 7, 16, 10, 35, 46, 23, 8);
/* Write the pvd */
if(LargeWrite(image, pvd, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)16, strerror(errno));
/* Create the supplementary volume descriptor */
svd = ih->svd;
/* 81..88: Volume space size */
bp_set_long_bbo(svd, 81, ih->volumeSpace);
/* 133..140: Path table size, Fixme: use real values */
/* 141..144: L Path table location */
/* 145..148: Optional L Path table location */
/* 149..152: M Path table location */
/* 153..156: Optional M Path table location */
bp_set_long_bbo(svd, 133, 10);
bp_set_long_lsb(svd, 141, 22);
bp_set_long_lsb(svd, 145, 0);
bp_set_long_msb(svd, 149, 23);
bp_set_long_msb(svd, 153, 0);
/* 157..190: Directory record for root directory, Fixme: use real values */
bp_set_byte(svd, 157, 34); /* Length of directory record */
bp_set_byte(svd, 158, 0); /* Extended attribute length */
bp_set_long_bbo(svd, 159, 24); /* Location of Extent */
bp_set_long_bbo(svd, 167, 2048); /* Data length of file section */
set_binary_date(svd, 175, /* Binary date rep of creation time */
106, 7, 16, 10, 35, 46, 8);
bp_set_byte(svd, 182, 2); /* File flags (we are a directory) */
bp_set_byte(svd, 183, 0); /* File unit size if interleaved */
bp_set_byte(svd, 184, 0); /* Interleave gap size */
bp_set_short_bbo(svd, 185, 1); /* Volume sequence number */
bp_set_byte(svd, 189, 1); /* Length of file identifier */
bp_set_byte(svd, 190, 0); /* File identifier */
/* 814..830 Volume creation time */
set_date(svd, 814, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 831..847 Volume modification time */
set_date(svd, 831, 2006, 7, 16, 10, 35, 46, 23, 8);
/* 848..864 Volume expiration time */
set_date(svd, 848, 2106, 7, 16, 10, 35, 46, 23, 8);
/* 865..881 Volume effective time */
set_date(svd, 865, 2006, 7, 16, 10, 35, 46, 23, 8);
/* Write the svd */
if(LargeWrite(image, svd, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)17, strerror(errno));
/*** Create and write the volume descriptor set terminator */
/* 1..8: Standard identifier */
memset(sector, 0, 2048);
bp_set_byte(sector, 1, 255); /* Volume descriptor set terminator */
bp_set_string(sector, "CD001", 2, 6); /* Standard identifier */
bp_set_byte(sector, 7, 1); /* Volume descriptor version 1 */
if(LargeWrite(image, sector, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)18, strerror(errno));
/*** Write the primary and supplementary path tables and root directories */
if(LargeWrite(image, ih->ppath->lpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)19, strerror(errno));
if(LargeWrite(image, ih->ppath->mpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)20, strerror(errno));
if(LargeWrite(image, ih->proot->dir, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)21, strerror(errno));
if(LargeWrite(image, ih->spath->lpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)22, strerror(errno));
if(LargeWrite(image, ih->spath->mpath, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)23, strerror(errno));
if(LargeWrite(image, ih->sroot->dir, 2048) != 2048)
Stop(_("Failed writing to sector %lld in image: %s"), (gint64)24, strerror(errno));
}

74
src/udf.h Normal file
View File

@@ -0,0 +1,74 @@
/* 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/>.
*/
#ifndef UDF_H
#define UDF_H
#include "scsi-layer.h"
/*
* Structure and functions for examining an existing .iso image
*/
typedef struct _IsoInfo
{ guint32 volumeSize;
char volumeLabel[33];
char creationDate[32];
} IsoInfo;
void FreeIsoInfo(IsoInfo*);
void ExamineUDF(Image*);
/*
* Structure and functions for creating an .iso image
*/
typedef struct _IsoDir
{ unsigned char *dir; /* memory allocated for dir, multiples of 2048 */
int nSectors; /* number of sectors */
int tail; /* next place for inserting new entry */
} IsoDir;
typedef struct _IsoPathTable
{ unsigned char *lpath; /* memory allocated for L path table, multiples of 2048 */
unsigned char *mpath; /* memory allocated for R path table, multiples of 2048 */
int nSectors; /* number of sectors */
int tail; /* next place for inserting new entry */
} IsoPathTable;
typedef struct _IsoHeader
{ unsigned char *pvd; /* primary volume descriptor */
IsoDir *proot; /* its root directory */
IsoPathTable *ppath; /* and path table */
unsigned char *svd; /* supplementary volume descriptor */
IsoDir *sroot; /* its root directory */
IsoPathTable *spath; /* and path table */
int dirSectors;
guint64 volumeSpace;
} IsoHeader;
IsoHeader* InitIsoHeader(void);
void AddFile(IsoHeader*, char*, guint64);
void FreeIsoHeader(IsoHeader*);
void WriteIsoHeader(IsoHeader*, LargeFile*);
#endif

156
src/welcome-window.c Normal file
View File

@@ -0,0 +1,156 @@
/* 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/>.
*/
// DVDISASTER_GUI_FILE
#include "dvdisaster.h"
/***
*** Creates the welcome window shown after startup.
***/
/*
* The welcome window is shown first,
* so it is convenient to initialize our GC when it is exposed.
*/
static void toggle_cb(GtkWidget *widget, gpointer data)
{ int state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
Closure->welcomeMessage = state;
}
static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{ GtkWidget *box = (GtkWidget*)data;
if(!Closure->drawGC)
{ GdkColor *bg = &widget->style->bg[0];
GdkColormap *cmap = gdk_colormap_get_system();
Closure->drawGC = gdk_gc_new(widget->window);
memcpy(Closure->background, bg, sizeof(GdkColor));
gdk_colormap_alloc_color(cmap, Closure->foreground, FALSE, TRUE);
Closure->grid->red = bg->red-bg->red/8;
Closure->grid->green = bg->green-bg->green/8;
Closure->grid->blue = bg->blue-bg->blue/8;
gdk_colormap_alloc_color(cmap, Closure->grid, FALSE, TRUE);
/* This can't be done at closure.c */
gdk_colormap_alloc_color(cmap, Closure->redText, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->greenText, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->barColor, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->logColor, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->curveColor, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->redSector, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->yellowSector, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->greenSector, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->blueSector, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->whiteSector, FALSE, TRUE);
gdk_colormap_alloc_color(cmap, Closure->darkSector, FALSE, TRUE);
/* Dirty trick for indenting the list:
draw an invisible dash before each indented line */
if(Closure->welcomeMessage || Closure->version != Closure->dotFileVersion)
{ GtkWidget *button;
Closure->invisibleDash = g_strdup_printf("<span color=\"#%02x%02x%02x\">-</span>",
bg->red>>8, bg->green>>8, bg->blue>>8);
AboutText(box, _("<b>Unofficial version.</b>\n\n"
"This is a modified version of the last <b>0.79.6</b> upstream pre-release.\n"
"<b>Patchlevel 3</b> adds support for a full CLI version, BD-R TL/QL,\n"
"a Windows build and an option to produce bigger BD-R RS03 images.\n"
"See the About box and changelog for more information.\n"
"The warning message from the pre-release version is retained below.\n"));
AboutText(box, _("<i>Please note:</i>\n"
"This is a <span color=\"#800000\">pre-release</span> for expert users.\n"
"It may contain unfinished features.\n"
"Adaptive reading is <span color=\"#800000\">unavailable</span> in this version.\n"
"It will be re-introduced in one of the next releases."));
gtk_box_pack_start(GTK_BOX(box), gtk_hseparator_new(), FALSE, FALSE, 10);
button = gtk_check_button_new_with_label(_utf("Show this message again"));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), Closure->welcomeMessage);
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(toggle_cb), NULL);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
gtk_widget_show_all(box);
}
}
Closure->dotFileVersion = Closure->version;
return FALSE;
}
/*
* Create the window
*/
void CreateWelcomePage(GtkNotebook *notebook)
{ GtkWidget *box,*align,*ignore;
int show_msg;
show_msg = Closure->welcomeMessage || Closure->version != Closure->dotFileVersion;
align = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
ignore = gtk_label_new("welcome_tab");
box = show_msg ? gtk_vbox_new(FALSE, 0) : gtk_hbox_new(FALSE, 10);
g_signal_connect(G_OBJECT(align), "expose_event", G_CALLBACK(expose_cb), box);
gtk_notebook_append_page(notebook, align, ignore);
gtk_container_add(GTK_CONTAINER(align), box);
if(!show_msg)
{ return; // simply leave the window blank
#if 0 // would print a centered dvdisaster logo
GtkWidget *widget;
widget = gtk_image_new_from_stock("dvdisaster-create", GTK_ICON_SIZE_LARGE_TOOLBAR);
gtk_box_pack_start(GTK_BOX(box), widget, FALSE, FALSE, 0);
AboutText(box, "<span weight=\"bold\" size=\"xx-large\">dvdisaster</span>");
return;
#endif
}
AboutText(box, _("<span weight=\"bold\" size=\"xx-large\">Welcome to dvdisaster!</span>"));
AboutText(box, _("\ndvdisaster creates error correction data to protect\n"
"optical media (CD,DVD,BD) against data loss.\n"));
AboutTextWithLink(box, _("Please see the [manual] for typical uses of dvdisaster.\n\n"),
"manual.pdf");
AboutText(box, _("<i>New in this Version:</i>"));
/* actual list is generated in the expose event handler */
}

2
src/winres.rc Normal file
View File

@@ -0,0 +1,2 @@
1 ICON "icons/win.ico"