1 /*  dvdisaster: Additional error correction for optical media.
2  *  Copyright (C) 2004-2015 Carsten Gnoerlich.
3  *
4  *  The Reed-Solomon error correction draws a lot of inspiration - and even code -
5  *  from Phil Karn's excellent Reed-Solomon library: http://www.ka9q.net/code/fec/
6  *
7  *  Email: carsten@dvdisaster.org  -or-  cgnoerlich@fsfe.org
8  *  Project homepage: http://www.dvdisaster.org
9  *
10  *  This file is part of dvdisaster.
11  *
12  *  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  *  (at your option) any later version.
16  *
17  *  dvdisaster is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  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 #include "dvdisaster.h"
27 
28 #include "rs01-includes.h"
29 
30 /***
31  *** Interpret our redundancy settings
32  ***
33  * a) "normal" -> nroots=32; "high" -> nroots=64
34  * b) "n"      -> nroots = n
35  * c) "n%"     -> use closest nroots for given redundancy in percent
36  * d) "nm"     -> choose redundancy so that .ecc file does not exceed n megabytes
37  */
38 
39 static guint64 ecc_file_size(guint64 sectors, int nr)
40 {  int nd = GF_FIELDMAX - nr;
41    guint64 bytesize;
encode_next_layer_portable(ReedSolomonTables * rt,unsigned char * data,unsigned char * parity,guint64 layer_size,int shift)42 
43    bytesize = 4096 + 4*sectors + 2048*nr*((sectors+nd-1)/nd);
44 
45    return (bytesize+0xfffff)/0x100000;   /* size in MiB */
46 }
47 
48 static int calculate_redundancy(char *image_name)
49 {  int nr = 0;
50    char last = 0;
51    double p;
52    int ignore;
53    guint64 fs,sectors,filesize;
54 
55    if(Closure->redundancy) /* get last char of redundancy parameter */
56    {  int len = strlen(Closure->redundancy);
57 
58       if(len) last = Closure->redundancy[len-1];
59    }
60 
61    switch(last)
62    {  case '%' : p = atof(Closure->redundancy);
63                  if(p<3.2 || p>64.5) Stop(_("Redundancy %4.1f%% out of useful range [3.2%%..64.5%%]"),p);
64 		 nr = (int)round((GF_FIELDMAX*p) / (100.0+p));
65 	         break;
66 
67       case 'm' : if(!LargeStat(image_name, &filesize))
68   	         {  nr = 32;   /* If the image file is not present, simply return 32. */
69 		    break;     /* Later stages will fail anyways, but can report the error */
70 	         }             /* in a more meaningful context. */
71 
72 	         CalcSectors(filesize, &sectors, &ignore);
73 
74 	         fs = strtoll(Closure->redundancy, NULL, 10);
75 	         if(fs < ecc_file_size(sectors, 8) || fs > ecc_file_size(sectors, 100))
76 		   Stop(_("Ecc file size %lldm out of useful range [%lld .. %lld]"),
77 			fs, ecc_file_size(sectors, 8), ecc_file_size(sectors, 100));
78 		 for(nr=100; nr>8; nr--)
79 		   if(fs >= ecc_file_size(sectors, nr))
80 		     break;
81 	         break;
82 
83       default:
84 	if(!Closure->redundancy || !strcmp(Closure->redundancy, "normal")) nr = 32;
85 	else if(!strcmp(Closure->redundancy, "high")) nr = 64;
86 	else nr = atoi(Closure->redundancy);
87 	break;
88    }
89 
90    if(nr < 8 || nr > 100)
91      Stop(_("Redundancy %d out of useful range [8..100]."),nr);
92 
93    return nr;
94 }
95 
96 /***
97  *** Remove the image file
98  ***/
99 
100 static void unlink_image(GtkWidget *label)
101 {
102    if(LargeUnlink(Closure->imageName))
103    {    PrintLog(_("\nImage file %s deleted.\n"),Closure->imageName);
104 
105         if(Closure->guiMode)
106 	  SetLabelText(GTK_LABEL(label),
107 		       _("\nImage file %s deleted.\n"), Closure->imageName);
108    }
109    else
110    {  if(!Closure->guiMode)
111        PrintLog("\n");
112 
113        ModalWarning(GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, NULL,
encode_next_layer_64bit(ReedSolomonTables * rt,unsigned char * data,unsigned char * parity,guint64 layer_size,int shift)114 		    _("Image file %s not deleted: %s\n"),
115 		    Closure->imageName, strerror(errno));
116    }
117 }
118 
119 /***
120  *** Create parity information for the medium sectors.
121  ***/
122 
123 /*
124  * Local data package used during encoding
125  */
126 
127 typedef struct
128 {  Method *self;
129    RS01Widgets *wl;
130    GaloisTables *gt;
131    ReedSolomonTables *rt;
132    Image *image;
133    int earlyTermination;
134    unsigned char *data;
135    unsigned char *parity;
136    char *msg;
137    GTimer *timer;
138 } ecc_closure;
139 
140 static void ecc_cleanup(gpointer data)
141 {  ecc_closure *ec = (ecc_closure*)data;
142 
143    UnregisterCleanup();
144 
145    if(Closure->guiMode)
146    {  if(ec->earlyTermination)
147         SetLabelText(GTK_LABEL(ec->wl->encFootline),
148 		     _("<span %s>Aborted by unrecoverable error.</span>"),
149 		     Closure->redMarkup);
150       AllowActions(TRUE);
151    }
152 
153    /** Clean up */
154 
155    if(ec->gt) FreeGaloisTables(ec->gt);
156    if(ec->rt) FreeReedSolomonTables(ec->rt);
157    if(ec->data) g_free(ec->data);
158    if(ec->parity) g_free(ec->parity);
159 
160    if(ec->image) CloseImage(ec->image);
161    if(ec->msg)   g_free(ec->msg);
162    if(ec->timer) g_timer_destroy(ec->timer);
163 
164    if(Closure->enableCurveSwitch)
165    {  Closure->enableCurveSwitch = FALSE;
166       RS01ShowCurveButton(ec->self);
167    }
168 
169    g_free(ec);
170 
171    if(Closure->guiMode)
172       g_thread_exit(0);
173 }
174 
175 /*
176  * Create the parity file.
177  */
178 
179 enum { NORMAL, HIGH, GENERIC };
180 
181 void RS01Create(void)
182 {  Method *self = FindMethod("RS01");
183    RS01Widgets *wl = (RS01Widgets*)self->widgetList;
184    GaloisTables *gt;
185    ReedSolomonTables *rt;
186    ecc_closure *ec = g_malloc0(sizeof(ecc_closure));
187    struct MD5Context md5Ctxt;
188    EccHeader *eh;
189    Image *image;
190    guint64 block_idx[256];  /* must be >= ndata */
191    guint64 s,si,n;
192    int i;
193    int percent = 0,max_percent,progress = 0, last_percent = -1;
194    guint64 n_parity_blocks,n_layer_sectors;
195    guint64 n_parity_bytes,n_layer_bytes;
196    guint64 chunk;
197    int layer;
198    int loop_type = GENERIC;
199    gint32 nroots;         /* These are copied to increase performance. */
200    gint32 ndata;
201    gint32 *gf_index_of;
202    gint32 *enc_alpha_to;
203    gint32 *rs_gpoly;
204 
205    /*** Register the cleanup procedure for GUI mode */
206 
207    ec->self = self;
208    ec->wl = wl;
209    ec->earlyTermination = TRUE;
210    RegisterCleanup(_("Error correction file creation aborted"), ecc_cleanup, ec);
211 
212    /*** Set up the Galois field arithmetic */
213 
214    /* Calculate number of roots (= max. number of erasures)
215       and number of data bytes from redundancy setting */
216 
217    if(!Closure->redundancy || !strcmp(Closure->redundancy, "normal"))
218 	                                          loop_type = NORMAL;
219    else if(!strcmp(Closure->redundancy, "high"))  loop_type = HIGH;
220 
221    i  = calculate_redundancy(Closure->imageName);
222    gt = ec->gt = CreateGaloisTables(RS_GENERATOR_POLY);
223    rt = ec->rt = CreateReedSolomonTables(gt, RS_FIRST_ROOT, RS_PRIM_ELEM, i);
224 
225    nroots       = rt->nroots;
226    ndata        = rt->ndata;
227    rs_gpoly     = rt->gpoly;
EncodeNextLayer(ReedSolomonTables * rt,unsigned char * data,unsigned char * parity,guint64 layer_size,int shift)228    enc_alpha_to = gt->encAlphaTo;
229    gf_index_of  = gt->indexOf;
230 
231    /*** Announce what we are going to do */
232 
233    ec->msg = g_strdup_printf(_("Encoding with Method RS01: %d roots, %4.1f%% redundancy."),
234 			     nroots,
235 			     ((double)nroots*100.0)/(double)ndata);
236 
237    if(Closure->guiMode)
238      SetLabelText(GTK_LABEL(wl->encHeadline),
239 		  _("<big>Creating the error correction file.</big>\n<i>%s</i>"), ec->msg);
240 
241    /*** Test the image file and create the CRC sums */
242 
243    /* Get rid of old ecc file (if any exists) */
244 
245    if(LargeStat(Closure->eccName, &n))
246    {
247       if(ConfirmEccDeletion(Closure->eccName))
248 	 LargeUnlink(Closure->eccName);
249       else
250       {  SetLabelText(GTK_LABEL(ec->wl->encFootline),
251 		      _("<span %s>Aborted to keep existing ecc file.</span>"),
252 		      Closure->redMarkup);
253 	 ec->earlyTermination = FALSE;
254 	 goto terminate;
255       }
256    }
257 
DescribeRSEncoder(char ** algorithm,char ** iostrategy)258    /* Open image and ecc files */
259 
260    PrintLog(_("\nOpening %s"), Closure->imageName);
261 
262    image = OpenImageFromFile(Closure->imageName, O_RDONLY, IMG_PERMS);
263    ec->image = image;
264    if(!image)
265    {  PrintLog(": %s.\n", strerror(errno));
266       Stop(_("Image file %s: %s."),Closure->imageName, strerror(errno));
267    }
268    if(image->inLast == 2048)
269         PrintLog(_(": %lld medium sectors.\n"), image->sectorSize);
270    else PrintLog(_(": %lld medium sectors and %d bytes.\n"),
271 		   image->sectorSize-1, image->inLast);
272 
273    if(!Closure->eccName || !strlen(Closure->eccName))
274      Stop(_("No error correction file specified!\n"));
275 
276    image->eccFile = LargeOpen(Closure->eccName, O_RDWR | O_CREAT, IMG_PERMS);
277    if(!image->eccFile)
278       Stop(_("Can't open %s:\n%s"),Closure->eccName,strerror(errno));
279 
280    ec->timer   = g_timer_new();
281 
282    if(Closure->crcCache)   /* use CRC values created during last read */
283    {  guint32 crc_idx;
284       int percent, last_percent = 0;
285       char *msg = _("Writing sector checksums: %3d%%");
286 
287       if(Closure->guiMode)
288 	SetLabelText(GTK_LABEL(wl->encLabel1),
289 		     _("<b>1. Writing image sector checksums:</b>"));
290 
291       memcpy(image->mediumSum, Closure->md5Cache, 16);
292       MD5Init(&md5Ctxt);    /*  md5sum of CRC portion of ecc file */
293 
294       /* Write out the cached CRC sectors */
295 
296       if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader)))
297          Stop(_("Failed skipping the ecc header: %s"),strerror(errno));
298 
299       for(crc_idx=0; crc_idx<image->sectorSize; crc_idx+=1024)
300       {  int ci,n,size;
301 	 guint32 *crcbuf;
302 
303 	 if(crc_idx + 1024 > image->sectorSize)
304 	       ci = image->sectorSize - crc_idx;
305 	 else  ci = 1024;
306 
307 	 size   = ci*sizeof(guint32);
308 	 crcbuf = &Closure->crcCache[crc_idx];
309 
310 	 n = LargeWrite(image->eccFile, crcbuf, size);
311 	 MD5Update(&md5Ctxt, (unsigned char*)crcbuf, size);
312 
313 	 if(size != n)
314 	   Stop(_("Error writing CRC information: %s"), strerror(errno));
315 
316          percent = (100*crc_idx)/image->sectorSize;
317          if(last_percent != percent)
318          {  PrintProgress(msg,percent);
319 
320             if(Closure->guiMode)
321 	      SetProgress(wl->encPBar1, percent, 100);
322 
323 	    last_percent = percent;
324 	 }
325       }
326 
327       PrintProgress(msg, 100);
328    }
329    else   /* Scan image for missing sectors and calculate the checksums */
330    {  if(Closure->guiMode)
331        SetLabelText(GTK_LABEL(wl->encLabel1),
332 		    _("<b>1. Calculating image sector checksums:</b>"));
333 
334       RS01ScanImage(self, image, &md5Ctxt, CREATE_CRC);
335 
336       if(image->sectorsMissing)
337       {  LargeClose(image->eccFile); /* Will be deleted anyways; no need to test for errors */
338 	 image->eccFile = NULL;
339 
340 	 LargeUnlink(Closure->eccName);  /* Do not leave a CRC-only .ecc file behind */
341 
342 	 if(Closure->stopActions)
343 	 {
344 	    if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
345 	       SetLabelText(GTK_LABEL(wl->encFootline),
346 			    _("<span %s>Aborted by user request!</span> (partial error correction file removed)"),
347 			    Closure->redMarkup);
348 	   ec->earlyTermination = FALSE;  /* suppress respective error message */
349 	   goto terminate;
350 	 }
351 	 else
352 	 {  if(Closure->guiMode)
353 	     SetProgress(wl->encPBar1, 100, 100);
354 
355 	    Stop(_("%lld sectors unread or missing due to errors.\n"), image->sectorsMissing);
356 	 }
357       }
358    }
359 
360    PrintTimeToLog(ec->timer, "for CRC writing/generation.\n");
361 
362    if(Closure->guiMode)
363    {  SetProgress(wl->encPBar1, 100, 100);
364       ShowWidget(wl->encPBar2);
365       ShowWidget(wl->encLabel2);
366    }
367 
368    if(!Closure->guiMode)
369      PrintLog("%s\n",ec->msg);
370 
371    /*** Prepare Ecc file header.
372         The .eccSum will be filled in after all ecc blocks have been created. */
373 
374    image->eccFileHeader = eh = g_malloc0(sizeof(EccHeader));
375    memcpy(eh->cookie, "*dvdisaster*", 12);
376    memcpy(eh->method, "RS01", 4);
377    eh->methodFlags[0] = 1;
378    gint64_to_uchar(eh->sectors, image->sectorSize);
379    eh->dataBytes       = ndata;
380    eh->eccBytes        = nroots;
381 
382    eh->creatorVersion  = Closure->version;
383    eh->fpSector        = FINGERPRINT_SECTOR;
384    eh->inLast          = image->inLast;
385 
386    /* dvdisaster 0.66 brings some extensions which are not compatible with
387       prior versions. These are:
388       - If the methodFlags contains any other bits set than methodFlags[0] == 1,
389         prior versions will incorrectly reject ecc files as being produced by
390 	version 0.40.7 due to a bug in the version processing code.
391 	So ecc files tagged with -devel or -rc status will not work with prior
392 	versions. But they are experimental versions available only through CVS,
393 	so this issue is not as big as it appears.
394       - Version 0.66 records the inLast value in the ecc file to facilitate
395         processing non-image files. Previous versions do not use this field
396 	and may round up file length to the next multiple of 2048 when doing
397 	error correction.
398    */
399 
400    if(image->inLast != 2048)
401         eh->neededVersion = 6600;
402    else eh->neededVersion = 5500;
403 
404    memcpy(eh->mediumFP, image->imageFP, 16);
405    memcpy(eh->mediumSum, image->mediumSum, 16);
406 
407    if(!LargeSeek(image->eccFile, (gint64)sizeof(EccHeader) + image->sectorSize*sizeof(guint32)))
408 	Stop(_("Failed skipping ecc+crc header: %s"),strerror(errno));
409 
410    /*** Allocate buffers for the parity calculation and image data caching.
411 
412         The algorithm builds the parity file consecutively in chunks of n_parity_blocks.
413         We use all the amount of memory allowed by cacheMiB for caching the parity blocks. */
414 
415    n_parity_blocks = ((guint64)Closure->cacheMiB<<20) / (guint64)nroots;
416    n_parity_blocks &= ~0x7ff;                   /* round down to multiple of 2048 */
417    n_parity_bytes  = (guint64)nroots * n_parity_blocks;
418 
419    /* Each chunk of parity blocks is built iteratively by processing the data in layers
420       (first all bytes at pos 0, then pos 1, until ndata layers have been processed).
421       So one buffer of n_layer_bytes = n_parity_blocks needs to be buffered.
422       For practical reasons we require that the layer size is a multiple of the
423       medium sector size of 2048 bytes. */
424 
425    n_layer_bytes   = n_parity_blocks;
426    n_layer_sectors = n_parity_blocks/2048;
427 
428    if(n_layer_sectors*2048 != n_parity_blocks)
429      Stop("Internal error: parity blocks are not a multiple of sector size.\n");
430 
431    ec->parity = g_try_malloc(n_parity_bytes);
432    ec->data   = g_try_malloc(n_layer_bytes);
433 
434    if(!ec->parity || !ec->data)
435       Stop(_("Failed allocating memory for I/O cache.\n"
436 	     "Cache size is currently %d MiB.\n"
437 	     "Try reducing it.\n"),
438 	   Closure->cacheMiB);
439 
440    /*** Setup the block counters for mapping medium sectors to ecc blocks
441         The image is divided into ndata sections;
442         with each section spanning s sectors. */
443 
444    s = (image->sectorSize+ndata-1)/ndata;
445 
446    for(si=0, i=0; i<ndata; si+=s, i++)
447      block_idx[i] = si;
448 
449    /*** Create ecc information for the medium image. */
450 
451    max_percent = ndata * ((s / n_layer_sectors) + 1);
452    g_timer_start(ec->timer);
453 
454    /* Process the image.
455       From each section a chunk of n_layer_sectors is read in at once.
456       So after (s/n_layer_sectors)+1 iterations the whole image has been processed. */
457 
458    for(chunk=0; chunk<s; chunk+=n_layer_sectors)
459    {  guint64 actual_layer_bytes,actual_layer_sectors;
460 
461       /* Prepare the parity data for the next chunk. */
462 
463       memset(ec->parity, 0, n_parity_bytes);
464 
465       /* The last chunk may contain fewer sectors. */
466 
467       if(chunk+n_layer_sectors < s)
468            actual_layer_sectors = n_layer_sectors;
469       else actual_layer_sectors = s-chunk;
470 
471       actual_layer_bytes   = 2048*actual_layer_sectors;
472 
473       /* Work each of the ndata data layers
474 	 into the parity data of the current chunk. */
475 
476       switch(loop_type)
477       { case NORMAL:  /* Inner loop unrolled for nroots = 32. */
478 	{int sp=1;    /* sp==1 makes sure sp==0 after ndata bytes [since (223+1) mod 32 = 0]*/
479 
480          for(layer=0; layer<ndata; layer++)
481 	 {  int offset = 0;
482             unsigned char *par_idx = ec->parity;
483 
484 	    if(Closure->stopActions) /* User hit the Stop button */
485 	    {  if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
486 		  SetLabelText(GTK_LABEL(wl->encFootline),
487 			       _("<span %s>Aborted by user request!</span> (partial error correction file removed)"),
488 			       Closure->redMarkup);
489 	       ec->earlyTermination = FALSE;  /* suppress respective error message */
490 	       LargeClose(image->eccFile);
491 	       image->eccFile = NULL;
492 	       LargeUnlink(Closure->eccName); /* Do not leave partial .ecc file behind */
493 	       goto terminate;
494 	    }
495 
496 	    /* Read the next data sectors of this layer. */
497 
498 	    for(si=0; si<actual_layer_sectors; si++)
499 	    {  RS01ReadSector(image, ec->data+offset, block_idx[layer]);
500 	       block_idx[layer]++;
501 	       offset += 2048;
502 	    }
503 
504 	    /* Now process the data bytes of the current layer. */
505 
506 	    for(si=0; si<actual_layer_bytes; si++)
507 	    {  register int feedback;
508 
509 	       feedback = gf_index_of[ec->data[si] ^ par_idx[sp]];
510 
511 	       if(feedback != GF_ALPHA0) /* non-zero feedback term */
512 	       {  register int spk = sp;
513 
514                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 249];
515                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  59];
516                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
517                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   4];
518                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  43];
519                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 126];
520                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 251];
521                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  97];
522                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  30];
523                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   3];
524                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 213];
525                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  50];
526                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
527                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 170];
528                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   5];
529                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  24];
530                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   5];
531                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 170];
532                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
533                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  50];
534                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 213];
535                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   3];
536                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  30];
537                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  97];
538                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 251];
539                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 126];
540                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  43];
541                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +   4];
542                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  66];
543                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback +  59];
544                   par_idx[((++spk)&31)] ^= enc_alpha_to[feedback + 249];
545 
546 		  par_idx[sp] = enc_alpha_to[feedback];  /* feedback + 0 */
547 	       }
548 	       else                   /* zero feedback term */
549 		 par_idx[sp] = 0;
550 
551 	       par_idx += nroots;
552 	    }
553 
554 	    sp = (sp+1) & 31;         /* shift */
555 
556 	    /* Report progress */
557 
558 	    progress++;
559 	    percent = (1000*progress)/max_percent;
560 	    if(last_percent != percent)
561 	    {  if(Closure->guiMode)
562 	          SetProgress(wl->encPBar2, percent, 1000);
563 	       else
564 	          PrintProgress(_("Ecc generation: %3d.%1d%%"), percent/10, percent%10);
565 	       last_percent = percent;
566 	    }
567 	 }
568 	}
569 	break;
570 
571         case HIGH: /* Inner loop is unrolled for nroots = 64. */
572 	{int sp=1; /* sp==1 makes sure sp==0 after ndata bytes [since (191+1) mod 64 = 0] */
573          for(layer=0; layer<ndata; layer++)
574 	 {  int offset = 0;
575 	    unsigned char *par_idx = ec->parity;
576 
577 	    if(Closure->stopActions) /* User hit the Stop button */
578 	    {  if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
579 		  SetLabelText(GTK_LABEL(wl->encFootline),
580 			       _("<span %s>Aborted by user request!</span> (partial error correction file removed)"),
581 			       Closure->redMarkup);
582 	       ec->earlyTermination = FALSE;   /* suppress respective error message */
583 	       LargeClose(image->eccFile);
584 	       image->eccFile = NULL;
585 	       LargeUnlink(Closure->eccName);  /* Do not leave partial .ecc file behind */
586 	       goto terminate;
587 	    }
588 
589 	    /* Read the next data sectors of this layer. */
590 
591 	    for(si=0; si<actual_layer_sectors; si++)
592 	    {  RS01ReadSector(image, ec->data+offset, block_idx[layer]);
593 	       block_idx[layer]++;
594 	       offset += 2048;
595 	    }
596 
597 	    /* Now process the data bytes of the current layer. */
598 
599 	    for(si=0; si<actual_layer_bytes; si++)
600 	    {  register int feedback;
601 
602 	       feedback = gf_index_of[ec->data[si] ^ par_idx[sp]];
603 
604 	       if(feedback != GF_ALPHA0) /* non-zero feedback term */
605 	       {  register int spk = sp;
606 
607                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  98];
608                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 247];
609                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 160];
610                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  15];
611                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  96];
612                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  27];
613                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  87];
614                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 175];
615                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  64];
616                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 170];
617                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  53];
618                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  39];
619                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 236];
620                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  39];
621                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  58];
622                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  82];
623                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  44];
624                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  89];
625                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  97];
626                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 182];
627                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  80];
628                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 120];
629                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  40];
630                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 104];
631                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  73];
632                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  73];
633                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  12];
634                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 152];
635                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 205];
636                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  96];
637                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  50];
638                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  21];
639                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 147];
640                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  35];
641                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 241];
642                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  30];
643                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 242];
644                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 145];
645                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 242];
646                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 115];
647                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 148];
648                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  70];
649                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 127];
650                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  71];
651                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  83];
652                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 172];
653                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 224];
654                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 104];
655                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 177];
656                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +   0];
657                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  39];
658                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 194];
659                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  50];
660                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +   9];
661                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +   0];
662                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 208];
663                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 217];
664                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 254];
665                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 165];
666                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 181];
667                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback + 168];
668                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  97];
669                   par_idx[((++spk)&63)] ^= enc_alpha_to[feedback +  45];
670 
671                   par_idx[sp] = enc_alpha_to[feedback +  44];
672 	       }
673 	       else                   /* zero feedback term */
674 		 par_idx[sp] = 0;
675 
676 	       par_idx += nroots;
677 	    }
678 
679 	    sp = (sp+1) & 63;         /* shift */
680 
681 	    /* Report progress */
682 
683 	    progress++;
684 	    percent = (1000*progress)/max_percent;
685 	    if(last_percent != percent)
686 	    {  if(Closure->guiMode)
687 	          SetProgress(wl->encPBar2, percent, 1000);
688 	       else
689 	          PrintProgress(_("Ecc generation: %3d.%1d%%"), percent/10, percent%10);
690 	       last_percent = percent;
691 	    }
692 	 }
693 	}
694 	break;
695 
696         default:   /* general case for nroots other than 32 or 64 */
697 	{int sp = nroots - ndata % nroots; /* => (ndata + sp) mod nroots = 0 so that parity */
698 	                                   /* is aligned at sp=0 after ndata iterations */
699       	 if(sp==nroots) sp=0;
700 
701 	 for(layer=0; layer<ndata; layer++)
702 	 {  int offset = 0;
703             unsigned char *par_idx = ec->parity;
704 
705 	    if(Closure->stopActions) /* User hit the Stop button */
706 	    {  if(Closure->stopActions == STOP_CURRENT_ACTION) /* suppress memleak warning when closing window */
707 		  SetLabelText(GTK_LABEL(wl->encFootline),
708 			       _("<span %s>Aborted by user request!</span>"),
709 			       Closure->redMarkup);
710 	       ec->earlyTermination = FALSE;   /* suppress respective error message */
711 	       LargeClose(image->eccFile);
712 	       image->eccFile = NULL;
713 	       LargeUnlink(Closure->eccName);  /* Do not leave partial .ecc file behind */
714 	       goto terminate;
715 	    }
716 
717             /* Read the next data sectors of this layer. */
718 
719    	    for(si=0; si<actual_layer_sectors; si++)
720 	    {  RS01ReadSector(image, ec->data+offset, block_idx[layer]);
721 	       block_idx[layer]++;
722 	       offset += 2048;
723 	    }
724 
725 	    /* Now process the data bytes of the current layer. */
726 
727 	    for(si=0; si<actual_layer_bytes; si++)
728 	    {  register int feedback;
729 
730 	       feedback = gf_index_of[ec->data[si] ^ par_idx[sp]];
731 
732 	       if(feedback != GF_ALPHA0) /* non-zero feedback term */
733 	       {  register int spk = sp+1;
734 		  register int *gpoly = rs_gpoly + nroots;
735 
736 		  switch(nroots-spk)
737 		  {
738 		    case 110: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
739 		    case 109: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
740 		    case 108: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
741 		    case 107: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
742 		    case 106: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
743 		    case 105: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
744 		    case 104: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
745 		    case 103: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
746 		    case 102: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
747 		    case 101: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
748 		    case 100: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
749 		     case 99: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
750 		     case 98: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
751 		     case 97: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
752 		     case 96: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
753 		     case 95: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
754 		     case 94: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
755 		     case 93: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
756 		     case 92: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
757 		     case 91: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
758 		     case 90: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
759 		     case 89: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
760 		     case 88: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
761 		     case 87: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
762 		     case 86: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
763 		     case 85: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
764 		     case 84: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
765 		     case 83: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
766 		     case 82: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
767 		     case 81: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
768 		     case 80: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
769 		     case 79: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
770 		     case 78: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
771 		     case 77: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
772 		     case 76: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
773 		     case 75: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
774 		     case 74: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
775 		     case 73: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
776 		     case 72: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
777 		     case 71: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
778 		     case 70: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
779 		     case 69: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
780 		     case 68: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
781 		     case 67: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
782 		     case 66: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
783 		     case 65: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
784 		     case 64: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
785 		     case 63: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
786 		     case 62: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
787 		     case 61: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
788 		     case 60: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
789 		     case 59: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
790 		     case 58: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
791 		     case 57: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
792 		     case 56: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
793 		     case 55: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
794 		     case 54: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
795 		     case 53: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
796 		     case 52: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
797 		     case 51: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
798 		     case 50: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
799 		     case 49: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
800 		     case 48: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
801 		     case 47: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
802 		     case 46: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
803 		     case 45: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
804 		     case 44: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
805 		     case 43: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
806 		     case 42: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
807 		     case 41: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
808 		     case 40: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
809 		     case 39: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
810 		     case 38: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
811 		     case 37: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
812 		     case 36: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
813 		     case 35: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
814 		     case 34: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
815 		     case 33: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
816 		     case 32: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
817 		     case 31: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
818 		     case 30: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
819 		     case 29: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
820 		     case 28: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
821 		     case 27: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
822 		     case 26: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
823 		     case 25: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
824 		     case 24: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
825 		     case 23: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
826 		     case 22: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
827 		     case 21: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
828 		     case 20: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
829 		     case 19: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
830 		     case 18: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
831 		     case 17: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
832 		     case 16: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
833 		     case 15: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
834 		     case 14: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
835 		     case 13: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
836 		     case 12: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
837 		     case 11: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
838 		     case 10: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
839 		     case  9: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
840 		     case  8: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
841 		     case  7: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
842 		     case  6: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
843 		     case  5: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
844 		     case  4: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
845 		     case  3: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
846 		     case  2: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
847 		     case  1: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
848 		  }
849 
850 		  spk = 0;
851 
852 		  switch(sp)
853 		  {
854                     case 110: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
855 		    case 109: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
856 		    case 108: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
857 		    case 107: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
858 		    case 106: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
859 		    case 105: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
860 		    case 104: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
861 		    case 103: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
862 		    case 102: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
863 		    case 101: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
864 		    case 100: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
865 		     case 99: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
866 		     case 98: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
867 		     case 97: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
868 		     case 96: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
869 		     case 95: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
870 		     case 94: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
871 		     case 93: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
872 		     case 92: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
873 		     case 91: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
874 		     case 90: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
875 		     case 89: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
876 		     case 88: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
877 		     case 87: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
878 		     case 86: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
879 		     case 85: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
880 		     case 84: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
881 		     case 83: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
882 		     case 82: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
883 		     case 81: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
884 		     case 80: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
885 		     case 79: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
886 		     case 78: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
887 		     case 77: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
888 		     case 76: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
889 		     case 75: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
890 		     case 74: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
891 		     case 73: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
892 		     case 72: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
893 		     case 71: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
894 		     case 70: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
895 		     case 69: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
896 		     case 68: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
897 		     case 67: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
898 		     case 66: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
899 		     case 65: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
900 		     case 64: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
901 		     case 63: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
902 		     case 62: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
903 		     case 61: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
904 		     case 60: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
905 		     case 59: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
906 		     case 58: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
907 		     case 57: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
908 		     case 56: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
909 		     case 55: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
910 		     case 54: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
911 		     case 53: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
912 		     case 52: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
913 		     case 51: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
914 		     case 50: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
915 		     case 49: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
916 		     case 48: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
917 		     case 47: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
918 		     case 46: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
919 		     case 45: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
920 		     case 44: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
921 		     case 43: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
922 		     case 42: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
923 		     case 41: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
924 		     case 40: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
925 		     case 39: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
926 		     case 38: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
927 		     case 37: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
928 		     case 36: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
929 		     case 35: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
930 		     case 34: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
931 		     case 33: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
932 		     case 32: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
933 		     case 31: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
934 		     case 30: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
935 		     case 29: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
936 		     case 28: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
937 		     case 27: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
938 		     case 26: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
939 		     case 25: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
940 		     case 24: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
941 		     case 23: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
942 		     case 22: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
943 		     case 21: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
944 		     case 20: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
945 		     case 19: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
946 		     case 18: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
947 		     case 17: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
948 		     case 16: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
949 		     case 15: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
950 		     case 14: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
951 		     case 13: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
952 		     case 12: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
953 		     case 11: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
954 		     case 10: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
955 		     case  9: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
956 		     case  8: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
957 		     case  7: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
958 		     case  6: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
959 		     case  5: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
960 		     case  4: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
961 		     case  3: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
962 		     case  2: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
963 		     case  1: par_idx[spk++] ^= enc_alpha_to[feedback + *--gpoly];
964 		  }
965 
966 		  par_idx[sp] = enc_alpha_to[feedback + rs_gpoly[0]];
967 	       }
968 	       else                   /* zero feedback term */
969 		 par_idx[sp] = 0;
970 
971 	       par_idx += nroots;
972 	    }
973 
974 	    if(++sp>=nroots) sp=0;   /* shift */
975 
976 	    /* Report progress */
977 
978 	    progress++;
979 	    percent = (1000*progress)/max_percent;
980 	    if(last_percent != percent)
981 	    {  if(Closure->guiMode)
982 	          SetProgress(wl->encPBar2, percent, 1000);
983 	       else
984 	          PrintProgress(_("Ecc generation: %3d.%1d%%"), percent/10, percent%10);
985 	       last_percent = percent;
986 	    }
987 	 }
988 	}
989 	break;
990       }
991 
992       /* Write the nroots bytes of parity information */
993 
994       n = LargeWrite(image->eccFile, ec->parity, nroots*actual_layer_bytes);
995 
996       if(n != nroots*actual_layer_bytes)
997         Stop(_("could not write to ecc file \"%s\":\n%s"),Closure->eccName,strerror(errno));
998 
999       MD5Update(&md5Ctxt, ec->parity, nroots*actual_layer_bytes);
1000    }
1001 
1002    /*** Complete the ecc header and write it out */
1003 
1004    MD5Final(eh->eccSum, &md5Ctxt);
1005 
1006    LargeSeek(image->eccFile, 0);
1007 #ifdef HAVE_BIG_ENDIAN
1008    SwapEccHeaderBytes(eh);
1009 #endif
1010    n = LargeWrite(image->eccFile, eh, sizeof(EccHeader));
1011    if(n != sizeof(EccHeader))
1012      Stop(_("Can't write ecc header:\n%s"),strerror(errno));
1013 
1014    if(!LargeClose(image->eccFile))
1015      Stop(_("Error closing error correction file:\n%s"), strerror(errno));
1016    image->eccFile = NULL;
1017 
1018    PrintTimeToLog(ec->timer, "for ECC generation.\n");
1019 
1020    PrintProgress(_("Ecc generation: 100.0%%\n"));
1021    PrintLog(_("Error correction file \"%s\" created.\n"
1022 	       "Make sure to keep this file on a reliable medium.\n"),
1023 	     Closure->eccName);
1024 
1025    if(Closure->guiMode)
1026    {  SetProgress(wl->encPBar2, 100, 100);
1027 
1028       SetLabelText(GTK_LABEL(wl->encFootline),
1029 		   _("The error correction file has been successfully created.\n"
1030 		     "Make sure to keep this file on a reliable medium."));
1031    }
1032 
1033    /*** If the --unlink option or respective GUI switch is set,
1034 	unlink the image. */
1035 
1036    if(Closure->unlinkImage)
1037    {  if(ec->image) CloseImage(ec->image);
1038       ec->image = NULL;
1039       unlink_image(Closure->guiMode ? wl->encFootline2 : NULL);
1040    }
1041 
1042    /*** Clean up */
1043 
1044    ec->earlyTermination = FALSE;
1045 
1046 terminate:
1047    ecc_cleanup((gpointer)ec);
1048 }
1049 
1050