chore: move *.c *.h to src/, build in build/
This commit is contained in:
67
src/bitmap.c
Normal file
67
src/bitmap.c
Normal 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
28
src/build.c
Normal 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
82
src/cacheprobe.c
Normal 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
715
src/closure.c
Normal 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
247
src/crc32.c
Normal 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
262
src/crcbuf.c
Normal 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
379
src/curve.c
Normal 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
1368
src/debug.c
Normal file
File diff suppressed because it is too large
Load Diff
437
src/ds-marker.c
Normal file
437
src/ds-marker.c
Normal 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
1053
src/dvdisaster.c
Normal file
File diff suppressed because it is too large
Load Diff
1493
src/dvdisaster.h
Normal file
1493
src/dvdisaster.h
Normal file
File diff suppressed because it is too large
Load Diff
104
src/ecc-rs01.c
Normal file
104
src/ecc-rs01.c
Normal 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
108
src/ecc-rs02.c
Normal 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
109
src/ecc-rs03.c
Normal 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
181
src/endian.c
Normal 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
38
src/galois-inlines.h
Normal 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
200
src/galois.c
Normal 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
795
src/help-dialogs.c
Normal 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 <svs@ropnet.ru>"));
|
||||
#endif
|
||||
#endif
|
||||
/* Show it */
|
||||
|
||||
gtk_widget_show_all(about);
|
||||
}
|
||||
2139
src/heuristic-lec.c
Normal file
2139
src/heuristic-lec.c
Normal file
File diff suppressed because it is too large
Load Diff
83
src/icon-factory.c
Normal file
83
src/icon-factory.c
Normal 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
349
src/image.c
Normal 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
491
src/l-ec.c
Normal 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
383
src/large-io.c
Normal 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
83
src/logfile.c
Normal 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
497
src/main-window.c
Normal 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
55
src/maintenance.c
Normal 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
363
src/md5.c
Normal 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
21
src/md5.h
Normal 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
591
src/medium-info.c
Normal 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
353
src/memtrack.c
Normal 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
589
src/menubar.c
Normal 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
12
src/method-link.c
Normal 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
102
src/method.c
Normal 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
1360
src/misc.c
Normal file
File diff suppressed because it is too large
Load Diff
3212
src/preferences.c
Normal file
3212
src/preferences.c
Normal file
File diff suppressed because it is too large
Load Diff
647
src/print-sense.c
Normal file
647
src/print-sense.c
Normal 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
150
src/random.c
Normal 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
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
380
src/raw-sector-cache.c
Normal 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
377
src/read-adaptive-window.c
Normal 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
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
447
src/read-linear-window.c
Normal 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
1492
src/read-linear.c
Normal file
File diff suppressed because it is too large
Load Diff
97
src/read-linear.h
Normal file
97
src/read-linear.h
Normal 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
1051
src/recover-raw.c
Normal file
File diff suppressed because it is too large
Load Diff
64
src/rs-decoder.c
Normal file
64
src/rs-decoder.c
Normal 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
122
src/rs-encoder-altivec.c
Normal 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
112
src/rs-encoder-sse2.c
Normal 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
286
src/rs-encoder.c
Normal 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
421
src/rs01-common.c
Normal file
@@ -0,0 +1,421 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2017 Carsten Gnoerlich.
|
||||
*
|
||||
* Email: carsten@dvdisaster.org -or- cgnoerlich@fsfe.org
|
||||
* Project homepage: http://www.dvdisaster.org
|
||||
*
|
||||
* This file is part of dvdisaster.
|
||||
*
|
||||
* dvdisaster is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* dvdisaster is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dvdisaster.h"
|
||||
|
||||
#include "rs01-includes.h"
|
||||
|
||||
/***
|
||||
*** Recognize a RS01 error correction file
|
||||
***/
|
||||
|
||||
int RS01Recognize(LargeFile *ecc_file, EccHeader **eh)
|
||||
{ int n;
|
||||
|
||||
*eh = g_malloc(sizeof(EccHeader));
|
||||
|
||||
LargeSeek(ecc_file, 0);
|
||||
n = LargeRead(ecc_file, *eh, sizeof(EccHeader));
|
||||
|
||||
if(n != sizeof(EccHeader))
|
||||
{ g_free(*eh);
|
||||
return ECCFILE_INVALID;
|
||||
}
|
||||
|
||||
if(strncmp((char*)(*eh)->cookie, "*dvdisaster*", 12))
|
||||
{ g_free(*eh);
|
||||
return ECCFILE_DEFECTIVE_HEADER;
|
||||
}
|
||||
|
||||
if(!strncmp((char*)(*eh)->method, "RS01", 4))
|
||||
{
|
||||
#ifdef HAVE_BIG_ENDIAN
|
||||
SwapEccHeaderBytes(*eh);
|
||||
#endif
|
||||
return ECCFILE_PRESENT;
|
||||
}
|
||||
|
||||
g_free(*eh);
|
||||
return ECCFILE_WRONG_CODEC;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Read and buffer CRC information from RS01 file
|
||||
***/
|
||||
|
||||
CrcBuf *RS01GetCrcBuf(Image *image)
|
||||
{ LargeFile *file = image->eccFile;
|
||||
CrcBuf *cb;
|
||||
guint32 *buf;
|
||||
guint64 image_sectors_from_ecc, image_sectors_with_crc;
|
||||
guint64 crc_sectors,crc_remainder;
|
||||
guint64 i,j,sec_idx;
|
||||
|
||||
image_sectors_from_ecc = uchar_to_gint64(image->eccFileHeader->sectors);
|
||||
cb = CreateCrcBuf(image);
|
||||
buf = cb->crcbuf;
|
||||
|
||||
/* Seek to beginning of CRC sums */
|
||||
|
||||
if(!LargeSeek(file, (gint64)sizeof(EccHeader)))
|
||||
Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
|
||||
|
||||
/* Read crc sums. A sector of 2048 bytes contains 512 CRC sums. */
|
||||
|
||||
if(cb->allSectors < image_sectors_from_ecc)
|
||||
image_sectors_with_crc = cb->allSectors; // image is truncated
|
||||
else image_sectors_with_crc = image_sectors_from_ecc; // get all CRC sectors from ECC
|
||||
crc_sectors = image_sectors_with_crc / 512;
|
||||
sec_idx = 0;
|
||||
|
||||
for(i=0; i<crc_sectors; i++)
|
||||
{ if(LargeRead(file, buf, 2048) != 2048)
|
||||
Stop(_("Error reading CRC information: %s"),strerror(errno));
|
||||
buf += 512;
|
||||
|
||||
for(j=0; j<512; j++, sec_idx++)
|
||||
SetBit(cb->valid, sec_idx);
|
||||
}
|
||||
|
||||
crc_remainder = sizeof(guint32)*(image_sectors_with_crc % 512);
|
||||
if(crc_remainder)
|
||||
{ if(LargeRead(file, buf, crc_remainder) != crc_remainder)
|
||||
Stop(_("Error reading CRC information: %s"),strerror(errno));
|
||||
|
||||
for( ; sec_idx<image_sectors_with_crc; sec_idx++)
|
||||
SetBit(cb->valid, sec_idx);
|
||||
}
|
||||
|
||||
/* Copy the md5sum */
|
||||
|
||||
memcpy(cb->dataMD5sum, image->eccFileHeader->mediumSum, 16);
|
||||
memcpy(cb->imageMD5sum, image->eccFileHeader->mediumSum, 16);
|
||||
cb->md5State = MD5_COMPLETE;
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Internal checksum handling.
|
||||
***
|
||||
* Not overly complicated as we just have a global md5sum.
|
||||
*/
|
||||
|
||||
void RS01ResetCksums(Image *image)
|
||||
{ RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
|
||||
|
||||
MD5Init(&csc->md5ctxt);
|
||||
}
|
||||
|
||||
void RS01UpdateCksums(Image *image, gint64 sector, unsigned char *buf)
|
||||
{ RS01CksumClosure *csc = (RS01CksumClosure*)image->eccFileMethod->ckSumClosure;
|
||||
|
||||
MD5Update(&csc->md5ctxt, buf, 2048);
|
||||
}
|
||||
|
||||
int RS01FinalizeCksums(Image *image)
|
||||
{ Method *self = image->eccFileMethod;
|
||||
RS01CksumClosure *csc = (RS01CksumClosure*)self->ckSumClosure;
|
||||
guint8 image_fp[16];
|
||||
int good_fp;
|
||||
|
||||
MD5Final(image_fp, &csc->md5ctxt);
|
||||
|
||||
good_fp = !(memcmp(image_fp, image->eccFileHeader->mediumSum ,16));
|
||||
|
||||
if(good_fp)
|
||||
return 0;
|
||||
else return DATA_MD5_BAD;
|
||||
}
|
||||
|
||||
/***
|
||||
*** Read an image sector from the .iso file.
|
||||
***
|
||||
* Two special cases here:
|
||||
* - Missing sectors (beyond the range recorded in eh->sectors) will be padded with zeros,
|
||||
* since we need a multiple of ndata sectors for the parity generation.
|
||||
* - Missing sectors beyond the range recorded in ii->sectors, but before the real end
|
||||
* as defined above are treated as "dead sectors".
|
||||
*/
|
||||
|
||||
void RS01ReadSector(Image *image, unsigned char *buf, gint64 s)
|
||||
{ gint64 eh_sectors = uchar_to_gint64(image->eccFileHeader->sectors);
|
||||
|
||||
if(s >= image->sectorSize && s < eh_sectors)
|
||||
{
|
||||
CreateMissingSector(buf, s, NULL, 0, NULL); /* truncated image */
|
||||
}
|
||||
else if(s >= eh_sectors)
|
||||
{
|
||||
memset(buf, 0, 2048); /* zero padding for reads past the image */
|
||||
}
|
||||
else /* else normal read within the image */
|
||||
{ int n,expected;
|
||||
|
||||
if(!LargeSeek(image->file, (gint64)(2048*s)))
|
||||
Stop(_("Failed seeking to sector %lld in image: %s"),
|
||||
s, strerror(errno));
|
||||
|
||||
/* Prepare for short reads at the last image sector.
|
||||
Doesn't happen for CD and DVD media, but perhaps for future media? */
|
||||
|
||||
if(s < image->sectorSize-1) expected = 2048;
|
||||
else
|
||||
{ memset(buf, 0, 2048);
|
||||
expected = image->inLast;
|
||||
}
|
||||
|
||||
/* Finally, read the sector */
|
||||
|
||||
n = LargeRead(image->file, buf, expected);
|
||||
if(n != expected)
|
||||
Stop(_("Failed reading sector %lld in image: %s"),s,strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan the image for missing blocks.
|
||||
* If the ecc file is present, also compare the CRC sums.
|
||||
* If CREATE_CRC is requested, calculate the CRC sums.
|
||||
*
|
||||
* Actually this should be usable for all RS01 type ecc files.
|
||||
* But unless we have more than one codec, we'll label it as
|
||||
* as RS01 specific method.
|
||||
*/
|
||||
|
||||
#define CRCBUFSIZE (1024*256)
|
||||
|
||||
void RS01ScanImage(Method *method, Image* image, struct MD5Context *ecc_ctxt, int mode)
|
||||
{
|
||||
#ifndef CLI
|
||||
RS01Widgets *wl = NULL;
|
||||
#endif
|
||||
unsigned char buf[2048];
|
||||
guint32 *crcbuf = NULL;
|
||||
int unrecoverable_sectors = 0;
|
||||
int crcidx = 0;
|
||||
struct MD5Context image_md5;
|
||||
gint64 s, first_missing, last_missing;
|
||||
#ifndef CLI
|
||||
gint64 prev_missing = 0;
|
||||
gint64 prev_crc_errors = 0;
|
||||
#endif
|
||||
int last_percent,current_missing;
|
||||
char *msg;
|
||||
|
||||
/* Extract widget list from method */
|
||||
|
||||
#ifndef CLI
|
||||
if(method->widgetList)
|
||||
wl = (RS01Widgets*)method->widgetList;
|
||||
#endif
|
||||
|
||||
/* Position behind the ecc file header,
|
||||
initialize CRC buffer pointers */
|
||||
|
||||
if(image->eccFile)
|
||||
{ if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader)))
|
||||
Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
|
||||
|
||||
crcbuf = g_malloc(sizeof(guint32) * CRCBUFSIZE);
|
||||
crcidx = (mode & CREATE_CRC) ? 0 : CRCBUFSIZE;
|
||||
if(mode & CREATE_CRC)
|
||||
MD5Init(ecc_ctxt); /* md5sum of CRC portion of ecc file */
|
||||
}
|
||||
|
||||
/* Prepare for scanning the image and calculating its md5sum */
|
||||
|
||||
MD5Init(&image_md5); /* md5sum of image file itself */
|
||||
LargeSeek(image->file, 0); /* rewind image file */
|
||||
|
||||
if(mode & PRINT_MODE)
|
||||
msg = _("- testing sectors : %3d%%");
|
||||
else msg = _("Scanning image sectors: %3d%%");
|
||||
|
||||
last_percent = 0;
|
||||
image->sectorsMissing = 0;
|
||||
first_missing = last_missing = -1;
|
||||
|
||||
/* Go through all sectors and look for the "dead sector marker" */
|
||||
|
||||
for(s=0; s<image->sectorSize; s++)
|
||||
{ int n,percent,err;
|
||||
|
||||
/* Check for user interruption */
|
||||
|
||||
#ifndef CLI
|
||||
if(Closure->stopActions)
|
||||
{ image->sectorsMissing += image->sectorSize - s;
|
||||
if(crcbuf) g_free(crcbuf);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Read the next sector */
|
||||
|
||||
n = LargeRead(image->file, buf, 2048);
|
||||
if(n != 2048)
|
||||
{ if(s != image->sectorSize - 1 || n != image->inLast)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("premature end in image (only %d bytes): %s\n"),n,strerror(errno));
|
||||
}
|
||||
else /* Zero unused sectors for CRC generation */
|
||||
memset(buf+image->inLast, 0, 2048-image->inLast);
|
||||
}
|
||||
|
||||
/* Look for the dead sector marker */
|
||||
|
||||
err = CheckForMissingSector(buf, s, image->fpState == 2 ? image->imageFP : NULL,
|
||||
FINGERPRINT_SECTOR);
|
||||
if(err != SECTOR_PRESENT)
|
||||
{ current_missing = TRUE;
|
||||
ExplainMissingSector(buf, s, err, SOURCE_IMAGE, &unrecoverable_sectors);
|
||||
}
|
||||
else current_missing = FALSE;
|
||||
|
||||
if(current_missing)
|
||||
{ if(first_missing < 0) first_missing = s;
|
||||
last_missing = s;
|
||||
image->sectorsMissing++;
|
||||
}
|
||||
|
||||
/* Report dead sectors. Combine subsequent missing sectors into one report. */
|
||||
|
||||
if(mode & PRINT_MODE)
|
||||
if(!current_missing || s==image->sectorSize-1)
|
||||
{ if(first_missing>=0)
|
||||
{ if(first_missing == last_missing)
|
||||
PrintCLI(_("* missing sector : %lld\n"), first_missing);
|
||||
else PrintCLI(_("* missing sectors : %lld - %lld\n"), first_missing, last_missing);
|
||||
first_missing = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(image->eccFile) /* Do something with the CRC portion of the .ecc file */
|
||||
{
|
||||
/* If creation of the CRC32 is requested, do that. */
|
||||
|
||||
if(mode & CREATE_CRC)
|
||||
{ crcbuf[crcidx++] = Crc32(buf, 2048);
|
||||
|
||||
if(crcidx >= CRCBUFSIZE) /* write out CRC buffer contents */
|
||||
{ size_t size = CRCBUFSIZE*sizeof(guint32);
|
||||
|
||||
MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
|
||||
if(LargeWrite(image->eccFile, crcbuf, size) != size)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("Error writing CRC information: %s"),strerror(errno));
|
||||
}
|
||||
crcidx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* else do the CRC32 check. Missing sectors are skipped in the CRC report. */
|
||||
|
||||
else if(s < image->expectedSectors)
|
||||
{ guint32 crc = Crc32(buf, 2048);
|
||||
|
||||
/* If the CRC buf is exhausted, refill. */
|
||||
|
||||
if(crcidx >= CRCBUFSIZE)
|
||||
{ size_t remain = image->sectorSize-s;
|
||||
size_t size;
|
||||
|
||||
if(remain < CRCBUFSIZE)
|
||||
size = remain*sizeof(guint32);
|
||||
else size = CRCBUFSIZE*sizeof(guint32);
|
||||
|
||||
if(LargeRead(image->eccFile, crcbuf, size) != size)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("Error reading CRC information: %s"),strerror(errno));
|
||||
}
|
||||
crcidx = 0;
|
||||
}
|
||||
|
||||
if(crc != crcbuf[crcidx++] && !current_missing)
|
||||
{ PrintCLI(_("* CRC error, sector: %lld\n"), s);
|
||||
image->crcErrors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MD5Update(&image_md5, buf, n); /* update image md5sum */
|
||||
|
||||
#ifndef CLI
|
||||
if(Closure->guiMode && mode & PRINT_MODE)
|
||||
percent = (VERIFY_IMAGE_SEGMENTS*(s+1))/image->sectorSize;
|
||||
else
|
||||
#endif
|
||||
percent = (100*(s+1))/image->sectorSize;
|
||||
if(last_percent != percent)
|
||||
{ PrintProgress(msg,percent);
|
||||
|
||||
#ifndef CLI
|
||||
if(Closure->guiMode && mode & CREATE_CRC)
|
||||
SetProgress(wl->encPBar1, percent, 100);
|
||||
|
||||
if(Closure->guiMode && mode & PRINT_MODE)
|
||||
{ RS01AddVerifyValues(method, percent, image->sectorsMissing, image->crcErrors,
|
||||
image->sectorsMissing - prev_missing,
|
||||
image->crcErrors - prev_crc_errors);
|
||||
|
||||
prev_missing = image->sectorsMissing;
|
||||
prev_crc_errors = image->crcErrors;
|
||||
}
|
||||
#endif
|
||||
|
||||
last_percent = percent;
|
||||
}
|
||||
}
|
||||
|
||||
/*** Flush the rest of the CRC buffer */
|
||||
|
||||
if((mode & CREATE_CRC) && crcidx)
|
||||
{ size_t size = crcidx*sizeof(guint32);
|
||||
|
||||
MD5Update(ecc_ctxt, (unsigned char*)crcbuf, size);
|
||||
if(LargeWrite(image->eccFile, crcbuf, size) != size)
|
||||
{ if(crcbuf) g_free(crcbuf);
|
||||
Stop(_("Error writing CRC information: %s"),strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
/*** The image md5sum can only be calculated if all blocks have been successfully read. */
|
||||
|
||||
MD5Final(image->mediumSum, &image_md5);
|
||||
|
||||
LargeSeek(image->file, 0);
|
||||
if(crcbuf) g_free(crcbuf);
|
||||
}
|
||||
|
||||
/***
|
||||
*** Determine expected size of image
|
||||
***/
|
||||
|
||||
guint64 RS01ExpectedImageSize(Image *image)
|
||||
{ EccHeader *eh = image->eccFileHeader;
|
||||
|
||||
if(!eh) return 0;
|
||||
|
||||
return uchar_to_gint64(eh->sectors);
|
||||
}
|
||||
1122
src/rs01-create.c
Normal file
1122
src/rs01-create.c
Normal file
File diff suppressed because it is too large
Load Diff
849
src/rs01-fix.c
Normal file
849
src/rs01-fix.c
Normal 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
177
src/rs01-includes.h
Normal 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
962
src/rs01-verify.c
Normal 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
915
src/rs01-window.c
Normal 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
812
src/rs02-common.c
Normal 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
1200
src/rs02-create.c
Normal file
File diff suppressed because it is too large
Load Diff
910
src/rs02-fix.c
Normal file
910
src/rs02-fix.c
Normal 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
205
src/rs02-includes.h
Normal 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
315
src/rs02-recognize.c
Normal 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
1202
src/rs02-verify.c
Normal file
File diff suppressed because it is too large
Load Diff
1382
src/rs02-window.c
Normal file
1382
src/rs02-window.c
Normal file
File diff suppressed because it is too large
Load Diff
685
src/rs03-common.c
Normal file
685
src/rs03-common.c
Normal 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
1493
src/rs03-create.c
Normal file
File diff suppressed because it is too large
Load Diff
1003
src/rs03-fix.c
Normal file
1003
src/rs03-fix.c
Normal file
File diff suppressed because it is too large
Load Diff
225
src/rs03-includes.h
Normal file
225
src/rs03-includes.h
Normal 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
1037
src/rs03-preferences.c
Normal file
File diff suppressed because it is too large
Load Diff
592
src/rs03-recognize.c
Normal file
592
src/rs03-recognize.c
Normal 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
1422
src/rs03-verify.c
Normal file
File diff suppressed because it is too large
Load Diff
384
src/rs03-window.c
Normal file
384
src/rs03-window.c
Normal 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
244
src/scsi-freebsd.c
Normal 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
2766
src/scsi-layer.c
Normal file
File diff suppressed because it is too large
Load Diff
269
src/scsi-layer.h
Normal file
269
src/scsi-layer.h
Normal 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
349
src/scsi-linux.c
Normal 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
229
src/scsi-netbsd.c
Normal 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
327
src/scsi-simulated.c
Normal 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
54
src/scsi-unknown.c
Normal 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
287
src/scsi-win32.c
Normal 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
408
src/show-html.c
Normal 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
365
src/show-manual.c
Normal 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
1711
src/smart-lec.c
Normal file
File diff suppressed because it is too large
Load Diff
265
src/spiral.c
Normal file
265
src/spiral.c
Normal 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
980
src/udf.c
Normal 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
74
src/udf.h
Normal 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
156
src/welcome-window.c
Normal 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
2
src/winres.rc
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
1 ICON "icons/win.ico"
|
||||
Reference in New Issue
Block a user