rs01-create.c 44.4 KB
Newer Older
1
/*  dvdisaster: Additional error correction for optical media.
2
 *  Copyright (C) 2004-2017 Carsten Gnoerlich.
3 4 5 6
 *
 *  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/
 *
7 8 9 10 11 12
 *  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
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation, either version 3 of the License, or
15 16
 *  (at your option) any later version.
 *
17
 *  dvdisaster is distributed in the hope that it will be useful,
18 19 20 21 22
 *  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
23
 *  along with dvdisaster. If not, see <http://www.gnu.org/licenses/>.
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 */

#include "dvdisaster.h"

#include "rs01-includes.h"

/***
 *** Interpret our redundancy settings
 ***
 * a) "normal" -> nroots=32; "high" -> nroots=64
 * b) "n"      -> nroots = n
 * c) "n%"     -> use closest nroots for given redundancy in percent
 * d) "nm"     -> choose redundancy so that .ecc file does not exceed n megabytes
 */

39
static guint64 ecc_file_size(guint64 sectors, int nr)
40
{  int nd = GF_FIELDMAX - nr;
41
   guint64 bytesize; 
42 43 44

   bytesize = 4096 + 4*sectors + 2048*nr*((sectors+nd-1)/nd);

45
   return (bytesize+0xfffff)/0x100000;   /* size in MiB */
46 47 48 49 50 51 52
}		 

static int calculate_redundancy(char *image_name)
{  int nr = 0;
   char last = 0; 
   double p;
   int ignore;
53
   guint64 fs,sectors,filesize;
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

   if(Closure->redundancy) /* get last char of redundancy parameter */
   {  int len = strlen(Closure->redundancy);

      if(len) last = Closure->redundancy[len-1];
   }

   switch(last)
   {  case '%' : p = atof(Closure->redundancy);
                 if(p<3.2 || p>64.5) Stop(_("Redundancy %4.1f%% out of useful range [3.2%%..64.5%%]"),p);
		 nr = (int)round((GF_FIELDMAX*p) / (100.0+p));
	         break;

      case 'm' : if(!LargeStat(image_name, &filesize))
  	         {  nr = 32;   /* If the image file is not present, simply return 32. */
		    break;     /* Later stages will fail anyways, but can report the error */
	         }             /* in a more meaningful context. */

	         CalcSectors(filesize, &sectors, &ignore);

	         fs = strtoll(Closure->redundancy, NULL, 10);
	         if(fs < ecc_file_size(sectors, 8) || fs > ecc_file_size(sectors, 100))
		   Stop(_("Ecc file size %lldm out of useful range [%lld .. %lld]"),
			fs, ecc_file_size(sectors, 8), ecc_file_size(sectors, 100));
		 for(nr=100; nr>8; nr--)
		   if(fs >= ecc_file_size(sectors, nr))
		     break;
	         break;

      default:
	if(!Closure->redundancy || !strcmp(Closure->redundancy, "normal")) nr = 32; 
	else if(!strcmp(Closure->redundancy, "high")) nr = 64;
	else nr = atoi(Closure->redundancy);
	break;
   }

   if(nr < 8 || nr > 100)
     Stop(_("Redundancy %d out of useful range [8..100]."),nr);

   return nr;
}

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
/***
 *** Remove the image file 
 ***/

static void unlink_image(GtkWidget *label)
{
   if(LargeUnlink(Closure->imageName))
   {    PrintLog(_("\nImage file %s deleted.\n"),Closure->imageName);

        if(Closure->guiMode)
	  SetLabelText(GTK_LABEL(label),
		       _("\nImage file %s deleted.\n"), Closure->imageName);
   }
   else 
   {  if(!Closure->guiMode)
111
         PrintLog("\n");
112

113 114 115
      ModalWarning(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, NULL,
		   _("Image file %s not deleted: %s\n"),
		   Closure->imageName, strerror(errno));
116 117 118
   }
}

119 120 121 122 123 124 125 126 127 128 129 130 131
/***
 *** Create parity information for the medium sectors.
 ***/

/*
 * Local data package used during encoding
 */

typedef struct
{  Method *self;
   RS01Widgets *wl;
   GaloisTables *gt;
   ReedSolomonTables *rt;
132
   Image *image;
133 134 135 136 137 138 139 140 141 142
   int earlyTermination;
   unsigned char *data;
   unsigned char *parity;
   char *msg;
   GTimer *timer;
} ecc_closure;

static void ecc_cleanup(gpointer data)
{  ecc_closure *ec = (ecc_closure*)data;

143
   UnregisterCleanup();
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

   if(Closure->guiMode)
   {  if(ec->earlyTermination)
        SetLabelText(GTK_LABEL(ec->wl->encFootline),
		     _("<span %s>Aborted by unrecoverable error.</span>"),
		     Closure->redMarkup); 
      AllowActions(TRUE);
   }

   /** Clean up */

   if(ec->gt) FreeGaloisTables(ec->gt);
   if(ec->rt) FreeReedSolomonTables(ec->rt);
   if(ec->data) g_free(ec->data);
   if(ec->parity) g_free(ec->parity);

160
   if(ec->image) CloseImage(ec->image);
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
   if(ec->msg)   g_free(ec->msg);
   if(ec->timer) g_timer_destroy(ec->timer);

   if(Closure->enableCurveSwitch)
   {  Closure->enableCurveSwitch = FALSE;
      RS01ShowCurveButton(ec->self);
   }

   g_free(ec);

   if(Closure->guiMode)
      g_thread_exit(0);
}

/*
 * Create the parity file.
 */

enum { NORMAL, HIGH, GENERIC };

181 182 183
void RS01Create(void)
{  Method *self = FindMethod("RS01");
   RS01Widgets *wl = (RS01Widgets*)self->widgetList;
184 185 186
   GaloisTables *gt;
   ReedSolomonTables *rt;
   ecc_closure *ec = g_malloc0(sizeof(ecc_closure));
187 188 189 190 191
   struct MD5Context md5Ctxt;
   EccHeader *eh;
   Image *image;
   guint64 block_idx[256];  /* must be >= ndata */
   guint64 s,si,n;
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
   int i;
   int percent = 0,max_percent,progress = 0, last_percent = -1;
   guint64 n_parity_blocks,n_layer_sectors;
   guint64 n_parity_bytes,n_layer_bytes;
   guint64 chunk;
   int layer;
   int loop_type = GENERIC;
   gint32 nroots;         /* These are copied to increase performance. */
   gint32 ndata;
   gint32 *gf_index_of;
   gint32 *enc_alpha_to;
   gint32 *rs_gpoly;

   /*** Register the cleanup procedure for GUI mode */

   ec->self = self;
   ec->wl = wl;
   ec->earlyTermination = TRUE;
   RegisterCleanup(_("Error correction file creation aborted"), ecc_cleanup, ec);

   /*** Set up the Galois field arithmetic */

   /* Calculate number of roots (= max. number of erasures)
      and number of data bytes from redundancy setting */

   if(!Closure->redundancy || !strcmp(Closure->redundancy, "normal")) 
	                                          loop_type = NORMAL;
   else if(!strcmp(Closure->redundancy, "high"))  loop_type = HIGH;

   i  = calculate_redundancy(Closure->imageName);
   gt = ec->gt = CreateGaloisTables(RS_GENERATOR_POLY);
   rt = ec->rt = CreateReedSolomonTables(gt, RS_FIRST_ROOT, RS_PRIM_ELEM, i);

   nroots       = rt->nroots;
   ndata        = rt->ndata;
   rs_gpoly     = rt->gpoly;
   enc_alpha_to = gt->encAlphaTo;
   gf_index_of  = gt->indexOf;

   /*** Announce what we are going to do */

   ec->msg = g_strdup_printf(_("Encoding with Method RS01: %d roots, %4.1f%% redundancy."),
			     nroots,
			     ((double)nroots*100.0)/(double)ndata);

   if(Closure->guiMode)
     SetLabelText(GTK_LABEL(wl->encHeadline),
		  _("<big>Creating the error correction file.</big>\n<i>%s</i>"), ec->msg);

   /*** Test the image file and create the CRC sums */

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
   /* Get rid of old ecc file (if any exists) */

   if(LargeStat(Closure->eccName, &n))
   {  
      if(ConfirmEccDeletion(Closure->eccName))
	 LargeUnlink(Closure->eccName);
      else
      {  SetLabelText(GTK_LABEL(ec->wl->encFootline),
		      _("<span %s>Aborted to keep existing ecc file.</span>"),
		      Closure->redMarkup); 
	 ec->earlyTermination = FALSE;
	 goto terminate;
      }
   }

   /* Open image and ecc files */

   PrintLog(_("\nOpening %s"), Closure->imageName);

   image = OpenImageFromFile(Closure->imageName, O_RDONLY, IMG_PERMS);
   ec->image = image;
   if(!image)
   {  PrintLog(": %s.\n", strerror(errno));
      Stop(_("Image file %s: %s."),Closure->imageName, strerror(errno));
   }
   if(image->inLast == 2048)
        PrintLog(_(": %lld medium sectors.\n"), image->sectorSize);
   else PrintLog(_(": %lld medium sectors and %d bytes.\n"), 
		   image->sectorSize-1, image->inLast);
272

273 274 275 276 277 278
   if(!Closure->eccName || !strlen(Closure->eccName))
     Stop(_("No error correction file specified!\n"));

   image->eccFile = LargeOpen(Closure->eccName, O_RDWR | O_CREAT, IMG_PERMS);
   if(!image->eccFile)
      Stop(_("Can't open %s:\n%s"),Closure->eccName,strerror(errno));
279 280 281

   ec->timer   = g_timer_new();

282 283 284
   /* Try to use CRC values created during last read */

   if(CrcBufValid(Closure->crcBuf, NULL, NULL))   
285 286 287 288 289 290 291 292
   {  guint32 crc_idx;
      int percent, last_percent = 0;
      char *msg = _("Writing sector checksums: %3d%%");

      if(Closure->guiMode)
	SetLabelText(GTK_LABEL(wl->encLabel1),
		     _("<b>1. Writing image sector checksums:</b>"));

293
      memcpy(image->mediumSum, Closure->crcBuf->imageMD5sum, 16);
294
      MD5Init(&md5Ctxt);    /*  md5sum of CRC portion of ecc file */
295 296 297

      /* Write out the cached CRC sectors */

298
      if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader)))
299 300
         Stop(_("Failed skipping the ecc header: %s"),strerror(errno));

301
      for(crc_idx=0; crc_idx<image->sectorSize; crc_idx+=1024)
302 303 304
      {  int ci,n,size; 
	 guint32 *crcbuf;

305 306
	 if(crc_idx + 1024 > image->sectorSize)
	       ci = image->sectorSize - crc_idx;
307 308 309
	 else  ci = 1024;

	 size   = ci*sizeof(guint32);
310
	 crcbuf = &Closure->crcBuf->crcbuf[crc_idx];
311

312 313
	 n = LargeWrite(image->eccFile, crcbuf, size);
	 MD5Update(&md5Ctxt, (unsigned char*)crcbuf, size);
314 315 316 317

	 if(size != n)
	   Stop(_("Error writing CRC information: %s"), strerror(errno));

318
         percent = (100*crc_idx)/image->sectorSize;
319 320 321 322 323 324 325 326 327 328 329 330
         if(last_percent != percent) 
         {  PrintProgress(msg,percent);

            if(Closure->guiMode)
	      SetProgress(wl->encPBar1, percent, 100);

	    last_percent = percent;
	 }
      }	    

      PrintProgress(msg, 100);
   }
331 332 333 334 335

   /* Cached crc buffer can not be used. 
      Scan image for missing sectors and calculate the checksums.
      Checksums are only computed locally and not provided in the cache. */
   else   
336 337 338 339
   {  if(Closure->guiMode)
       SetLabelText(GTK_LABEL(wl->encLabel1),
		    _("<b>1. Calculating image sector checksums:</b>"));

340 341 342
      FreeCrcBuf(Closure->crcBuf);  /* just a defensive measure */
      Closure->crcBuf = NULL;
     
343
      RS01ScanImage(self, image, &md5Ctxt, CREATE_CRC);
344

345 346 347
      if(image->sectorsMissing)
      {  LargeClose(image->eccFile); /* Will be deleted anyways; no need to test for errors */
	 image->eccFile = NULL;
348 349 350 351 352

	 LargeUnlink(Closure->eccName);  /* Do not leave a CRC-only .ecc file behind */

	 if(Closure->stopActions)   
	 {
353 354 355 356
	    if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
	       SetLabelText(GTK_LABEL(wl->encFootline), 
			    _("<span %s>Aborted by user request!</span> (partial error correction file removed)"),
			    Closure->redMarkup); 
357 358 359 360 361 362 363
	   ec->earlyTermination = FALSE;  /* suppress respective error message */
	   goto terminate;
	 }
	 else 
	 {  if(Closure->guiMode)
	     SetProgress(wl->encPBar1, 100, 100);

364
	    Stop(_("%lld sectors unread or missing due to errors.\n"), image->sectorsMissing);
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
	 }
      }
   }

   PrintTimeToLog(ec->timer, "for CRC writing/generation.\n");

   if(Closure->guiMode)
   {  SetProgress(wl->encPBar1, 100, 100);
      ShowWidget(wl->encPBar2);
      ShowWidget(wl->encLabel2);
   }

   if(!Closure->guiMode)
     PrintLog("%s\n",ec->msg);

   /*** Prepare Ecc file header.
        The .eccSum will be filled in after all ecc blocks have been created. */

383 384 385 386
   image->eccFileHeader = eh = g_malloc0(sizeof(EccHeader));
   memcpy(eh->cookie, "*dvdisaster*", 12);
   memcpy(eh->method, "RS01", 4);
   eh->methodFlags[0] = 1;
387 388
   if(!Closure->regtestMode)
     eh->methodFlags[3] = Closure->releaseFlags;
389 390 391
   gint64_to_uchar(eh->sectors, image->sectorSize);
   eh->dataBytes       = ndata;
   eh->eccBytes        = nroots;
392

393 394 395
   eh->creatorVersion  = Closure->version;
   eh->fpSector        = FINGERPRINT_SECTOR;
   eh->inLast          = image->inLast;
396 397 398 399 400 401 402

   /* dvdisaster 0.66 brings some extensions which are not compatible with
      prior versions. These are:
      - If the methodFlags contains any other bits set than methodFlags[0] == 1,
        prior versions will incorrectly reject ecc files as being produced by
	version 0.40.7 due to a bug in the version processing code.
	So ecc files tagged with -devel or -rc status will not work with prior
403 404
	versions. But they are experimental versions available only through CVS, 
	so this issue is not as big as it appears.
405 406 407 408 409 410
      - Version 0.66 records the inLast value in the ecc file to facilitate
        processing non-image files. Previous versions do not use this field
	and may round up file length to the next multiple of 2048 when doing
	error correction.
   */

411 412 413
   if(image->inLast != 2048)
        eh->neededVersion = 6600;
   else eh->neededVersion = 5500;
414

415 416
   memcpy(eh->mediumFP, image->imageFP, 16);
   memcpy(eh->mediumSum, image->mediumSum, 16);
417

418
   if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader) + image->sectorSize*sizeof(guint32)))
419 420 421 422 423
	Stop(_("Failed skipping ecc+crc header: %s"),strerror(errno));

   /*** Allocate buffers for the parity calculation and image data caching. 

        The algorithm builds the parity file consecutively in chunks of n_parity_blocks.
424
        We use all the amount of memory allowed by cacheMiB for caching the parity blocks. */
425

426
   n_parity_blocks = ((guint64)Closure->cacheMiB<<20) / (guint64)nroots;
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
   n_parity_blocks &= ~0x7ff;                   /* round down to multiple of 2048 */
   n_parity_bytes  = (guint64)nroots * n_parity_blocks;

   /* Each chunk of parity blocks is built iteratively by processing the data in layers
      (first all bytes at pos 0, then pos 1, until ndata layers have been processed).
      So one buffer of n_layer_bytes = n_parity_blocks needs to be buffered.
      For practical reasons we require that the layer size is a multiple of the
      medium sector size of 2048 bytes. */

   n_layer_bytes   = n_parity_blocks;
   n_layer_sectors = n_parity_blocks/2048;

   if(n_layer_sectors*2048 != n_parity_blocks)
     Stop("Internal error: parity blocks are not a multiple of sector size.\n");

   ec->parity = g_try_malloc(n_parity_bytes);
   ec->data   = g_try_malloc(n_layer_bytes);

   if(!ec->parity || !ec->data)
      Stop(_("Failed allocating memory for I/O cache.\n"
447
	     "Cache size is currently %d MiB.\n"
448
	     "Try reducing it.\n"),
449
	   Closure->cacheMiB);
450 451 452 453 454

   /*** Setup the block counters for mapping medium sectors to ecc blocks 
        The image is divided into ndata sections;
        with each section spanning s sectors. */

455
   s = (image->sectorSize+ndata-1)/ndata;
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495

   for(si=0, i=0; i<ndata; si+=s, i++)
     block_idx[i] = si;

   /*** Create ecc information for the medium image. */ 

   max_percent = ndata * ((s / n_layer_sectors) + 1);
   g_timer_start(ec->timer);

   /* Process the image.
      From each section a chunk of n_layer_sectors is read in at once.
      So after (s/n_layer_sectors)+1 iterations the whole image has been processed. */

   for(chunk=0; chunk<s; chunk+=n_layer_sectors) 
   {  guint64 actual_layer_bytes,actual_layer_sectors;

      /* Prepare the parity data for the next chunk. */

      memset(ec->parity, 0, n_parity_bytes);

      /* The last chunk may contain fewer sectors. */

      if(chunk+n_layer_sectors < s)
           actual_layer_sectors = n_layer_sectors;
      else actual_layer_sectors = s-chunk;

      actual_layer_bytes   = 2048*actual_layer_sectors;

      /* Work each of the ndata data layers 
	 into the parity data of the current chunk. */

      switch(loop_type)
      { case NORMAL:  /* Inner loop unrolled for nroots = 32. */
	{int sp=1;    /* sp==1 makes sure sp==0 after ndata bytes [since (223+1) mod 32 = 0]*/
  
         for(layer=0; layer<ndata; layer++)
	 {  int offset = 0;
            unsigned char *par_idx = ec->parity;

	    if(Closure->stopActions) /* User hit the Stop button */
496 497 498 499
	    {  if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
		  SetLabelText(GTK_LABEL(wl->encFootline), 
			       _("<span %s>Aborted by user request!</span> (partial error correction file removed)"),
			       Closure->redMarkup); 
500
	       ec->earlyTermination = FALSE;  /* suppress respective error message */
501 502
	       LargeClose(image->eccFile);
	       image->eccFile = NULL;
503 504 505 506 507 508 509
	       LargeUnlink(Closure->eccName); /* Do not leave partial .ecc file behind */
	       goto terminate;
	    }

	    /* Read the next data sectors of this layer. */

	    for(si=0; si<actual_layer_sectors; si++)
510
	    {  RS01ReadSector(image, ec->data+offset, block_idx[layer]);
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
	       block_idx[layer]++;
	       offset += 2048;
	    }

	    /* Now process the data bytes of the current layer. */

	    for(si=0; si<actual_layer_bytes; si++)
	    {  register int feedback;

	       feedback = gf_index_of[ec->data[si] ^ par_idx[sp]];

	       if(feedback != GF_ALPHA0) /* non-zero feedback term */
	       {  register int spk = sp;

                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 249];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  59];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   4];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  43];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 126];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 251];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  97];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  30];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   3];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 213];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  50];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 170];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   5];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  24];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   5];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 170];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  50];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 213];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   3];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  30];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  97];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 251];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 126];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  43];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   4];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  59];
                  par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 249];

		  par_idx[sp] = enc_alpha_to[feedback];  /* feedback + 0 */
	       }
	       else                   /* zero feedback term */
		 par_idx[sp] = 0;

	       par_idx += nroots;
	    }

	    sp = (sp+1) & 31;         /* shift */

	    /* Report progress */

	    progress++;
	    percent = (1000*progress)/max_percent;
	    if(last_percent != percent) 
	    {  if(Closure->guiMode)
	          SetProgress(wl->encPBar2, percent, 1000);
	       else
	          PrintProgress(_("Ecc generation: %3d.%1d%%"), percent/10, percent%10);
	       last_percent = percent;
	    }
	 }
	}
	break;

        case HIGH: /* Inner loop is unrolled for nroots = 64. */
	{int sp=1; /* sp==1 makes sure sp==0 after ndata bytes [since (191+1) mod 64 = 0] */
         for(layer=0; layer<ndata; layer++)
	 {  int offset = 0;
	    unsigned char *par_idx = ec->parity;

	    if(Closure->stopActions) /* User hit the Stop button */
589 590 591 592
	    {  if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
		  SetLabelText(GTK_LABEL(wl->encFootline), 
			       _("<span %s>Aborted by user request!</span> (partial error correction file removed)"),
			       Closure->redMarkup); 
593
	       ec->earlyTermination = FALSE;   /* suppress respective error message */
594 595
	       LargeClose(image->eccFile);
	       image->eccFile = NULL;
596 597 598 599 600 601 602
	       LargeUnlink(Closure->eccName);  /* Do not leave partial .ecc file behind */
	       goto terminate;
	    }

	    /* Read the next data sectors of this layer. */

	    for(si=0; si<actual_layer_sectors; si++)
603
	    {  RS01ReadSector(image, ec->data+offset, block_idx[layer]);
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
	       block_idx[layer]++;
	       offset += 2048;
	    }

	    /* Now process the data bytes of the current layer. */

	    for(si=0; si<actual_layer_bytes; si++)
	    {  register int feedback;

	       feedback = gf_index_of[ec->data[si] ^ par_idx[sp]];

	       if(feedback != GF_ALPHA0) /* non-zero feedback term */
	       {  register int spk = sp;

                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  98];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 247];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 160];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  15];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  96];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  27];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  87];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 175];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  64];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 170];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  53];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  39];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 236];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  39];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  58];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  82];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  44];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  89];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  97];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 182];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  80];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 120];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  40];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 104];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  73];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  73];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  12];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 152];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 205];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  96];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  50];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  21];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 147];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  35];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 241];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  30];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 242];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 145];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 242];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 115];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 148];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  70];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 127];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  71];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  83];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 172];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 224];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 104];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 177];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +   0];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  39];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 194];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  50];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +   9];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +   0];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 208];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 217];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 254];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 165];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 181];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 168];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  97];
                  par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  45];

                  par_idx[sp] = enc_alpha_to[feedback +  44];
	       }
	       else                   /* zero feedback term */
		 par_idx[sp] = 0;

	       par_idx += nroots;
	    }

	    sp = (sp+1) & 63;         /* shift */

	    /* Report progress */

	    progress++;
	    percent = (1000*progress)/max_percent;
	    if(last_percent != percent) 
	    {  if(Closure->guiMode)
	          SetProgress(wl->encPBar2, percent, 1000);
	       else
	          PrintProgress(_("Ecc generation: %3d.%1d%%"), percent/10, percent%10);
	       last_percent = percent;
	    }
	 }
	}
	break;

        default:   /* general case for nroots other than 32 or 64 */
	{int sp = nroots - ndata % nroots; /* => (ndata + sp) mod nroots = 0 so that parity */
	                                   /* is aligned at sp=0 after ndata iterations */
      	 if(sp==nroots) sp=0;

	 for(layer=0; layer<ndata; layer++)
	 {  int offset = 0;
            unsigned char *par_idx = ec->parity;

	    if(Closure->stopActions) /* User hit the Stop button */
717 718 719 720
	    {  if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
		  SetLabelText(GTK_LABEL(wl->encFootline), 
			       _("<span %s>Aborted by user request!</span>"),
			       Closure->redMarkup); 
721
	       ec->earlyTermination = FALSE;   /* suppress respective error message */
722 723
	       LargeClose(image->eccFile);
	       image->eccFile = NULL;
724 725 726 727 728 729 730
	       LargeUnlink(Closure->eccName);  /* Do not leave partial .ecc file behind */
	       goto terminate;
	    }

            /* Read the next data sectors of this layer. */

   	    for(si=0; si<actual_layer_sectors; si++)
731
	    {  RS01ReadSector(image, ec->data+offset, block_idx[layer]);
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
	       block_idx[layer]++;
	       offset += 2048;
	    }

	    /* Now process the data bytes of the current layer. */

	    for(si=0; si<actual_layer_bytes; si++)
	    {  register int feedback;

	       feedback = gf_index_of[ec->data[si] ^ par_idx[sp]];

	       if(feedback != GF_ALPHA0) /* non-zero feedback term */
	       {  register int spk = sp+1;
		  register int *gpoly = rs_gpoly + nroots;

		  switch(nroots-spk)
		  {  
		    case 110: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 109: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 108: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 107: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 106: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 105: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 104: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 103: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 102: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 101: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 100: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 99: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 98: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 97: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 96: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 95: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 94: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 93: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 92: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 91: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 90: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 89: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 88: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 87: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 86: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 85: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 84: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 83: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 82: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 81: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 80: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 79: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 78: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 77: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 76: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 75: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 74: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 73: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 72: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 71: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 70: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 69: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 68: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 67: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 66: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 65: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 64: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 63: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 62: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 61: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 60: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 59: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 58: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 57: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 56: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 55: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 54: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 53: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 52: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 51: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 50: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 49: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 48: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 47: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 46: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 45: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 44: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 43: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 42: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 41: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 40: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 39: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 38: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 37: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 36: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 35: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 34: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 33: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 32: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 31: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 30: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 29: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 28: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 27: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 26: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 25: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 24: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 23: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 22: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 21: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 20: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 19: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 18: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 17: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 16: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 15: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 14: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 13: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 12: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 11: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 10: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  9: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  8: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  7: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  6: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  5: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  4: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  3: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  2: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  1: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		  }

		  spk = 0;
		  
		  switch(sp)
		  {
                    case 110: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 109: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 108: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 107: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 106: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 105: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 104: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 103: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 102: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 101: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		    case 100: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 99: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 98: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 97: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 96: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 95: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 94: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 93: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 92: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 91: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 90: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 89: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 88: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 87: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 86: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 85: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 84: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 83: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 82: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 81: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 80: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 79: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 78: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 77: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 76: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 75: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 74: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 73: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 72: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 71: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 70: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 69: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 68: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 67: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 66: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 65: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 64: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 63: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 62: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 61: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 60: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 59: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 58: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 57: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 56: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 55: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 54: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 53: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 52: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 51: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 50: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 49: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 48: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 47: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 46: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 45: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 44: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 43: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 42: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 41: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 40: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 39: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 38: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 37: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 36: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 35: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 34: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 33: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 32: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 31: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 30: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 29: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 28: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 27: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 26: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 25: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 24: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 23: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 22: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 21: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 20: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 19: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 18: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 17: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 16: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 15: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 14: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 13: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 12: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 11: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case 10: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  9: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  8: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  7: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  6: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  5: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  4: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  3: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  2: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		     case  1: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
		  }

		  par_idx[sp] = enc_alpha_to[feedback + rs_gpoly[0]];
	       }
	       else                   /* zero feedback term */
		 par_idx[sp] = 0;

	       par_idx += nroots;
	    }

	    if(++sp>=nroots) sp=0;   /* shift */

	    /* Report progress */

	    progress++;
	    percent = (1000*progress)/max_percent;
	    if(last_percent != percent) 
	    {  if(Closure->guiMode)
	          SetProgress(wl->encPBar2, percent, 1000);
	       else
	          PrintProgress(_("Ecc generation: %3d.%1d%%"), percent/10, percent%10);
	       last_percent = percent;
	    }
	 }
	}
	break;
      }

      /* Write the nroots bytes of parity information */

1005
      n = LargeWrite(image->eccFile, ec->parity, nroots*actual_layer_bytes);
1006 1007 1008 1009

      if(n != nroots*actual_layer_bytes)
        Stop(_("could not write to ecc file \"%s\":\n%s"),Closure->eccName,strerror(errno));

1010
      MD5Update(&md5Ctxt, ec->parity, nroots*actual_layer_bytes);
1011 1012 1013 1014
   }

   /*** Complete the ecc header and write it out */

1015
   MD5Final(eh->eccSum, &md5Ctxt);
1016

1017
   LargeSeek(image->eccFile, 0);
1018
#ifdef HAVE_BIG_ENDIAN
1019
   SwapEccHeaderBytes(eh);
1020
#endif
1021
   n = LargeWrite(image->eccFile, eh, sizeof(EccHeader));
1022 1023 1024
   if(n != sizeof(EccHeader))
     Stop(_("Can't write ecc header:\n%s"),strerror(errno));

1025
   if(!LargeClose(image->eccFile))
1026
     Stop(_("Error closing error correction file:\n%s"), strerror(errno));
1027
   image->eccFile = NULL;
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040

   PrintTimeToLog(ec->timer, "for ECC generation.\n");

   PrintProgress(_("Ecc generation: 100.0%%\n"));
   PrintLog(_("Error correction file \"%s\" created.\n"
	       "Make sure to keep this file on a reliable medium.\n"),
	     Closure->eccName);
   
   if(Closure->guiMode)
   {  SetProgress(wl->encPBar2, 100, 100);

      SetLabelText(GTK_LABEL(wl->encFootline), 
		   _("The error correction file has been successfully created.\n"
1041
		     "Make sure to keep this file on a reliable medium.")); 
1042 1043 1044
   }

   /*** If the --unlink option or respective GUI switch is set, 
1045
	unlink the image. */
1046 1047

   if(Closure->unlinkImage)
1048 1049 1050
   {  if(ec->image) CloseImage(ec->image);
      ec->image = NULL;
      unlink_image(Closure->guiMode ? wl->encFootline2 : NULL);
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
   }

   /*** Clean up */

   ec->earlyTermination = FALSE;

terminate:
   ecc_cleanup((gpointer)ec);
}