+#undef ALPHA
+#undef BETA
+
+ throbix++; if (!throbber[throbix]) throbix = 0;
+}
+
+static void report_progress(secaddr pos)
+ { update_progress(pos); progress_update(&progress); }
+
+static dvd_reader_t *dvd;
+static int dvdfd = -1, outfd = -1;
+static dvd_file_t *vob;
+static const char *mapfile; static FILE *mapfp;
+static const char *errfile; static FILE *errfp;
+
+struct badblock { secaddr start, end; };
+DEFVEC(badblock_v, struct badblock);
+static badblock_v badblocks = VEC_INIT;
+
+static int compare_badblock(const void *a, const void *b)
+{
+ const struct badblock *ba = a, *bb = b;
+
+ if (ba->start < bb->start) return (-1);
+ else if (ba->start > bb->start) return (+1);
+
+ if (ba->end < bb->end) return (-1);
+ else if (ba->end > bb->end) return (+1);
+
+ return (0);
+}
+
+static double bad_block_delay = 0.0;
+static double good_block_delay = 0.0;
+
+static ssize_t read_sectors(secaddr pos, void *buf, secaddr want)
+{
+ ssize_t n, done;
+ size_t lo, mid, hi;
+ int fakeerr = 0;
+ struct badblock *bad, *best;
+ unsigned char *p = buf;
+
+ if (badblocks.n) {
+ best = 0; lo = 0; hi = badblocks.n;
+#ifdef DEBUG
+ progress_clear();
+ printf(";; searching badblocks for %"PRIuSEC" .. %"PRIuSEC"\n",
+ pos, pos + want);
+#endif
+ while (lo < hi) {
+ mid = lo + (hi - lo)/2; bad = &badblocks.v[mid];
+#ifdef DEBUG
+ printf(";; try %zu (%"PRIuSEC" .. %"PRIuSEC")... ",
+ mid, bad->start, bad->end);
+#endif
+ if (pos < bad->start) { D( printf("high\n"); ) best = bad; hi = mid; }
+ else if (pos >= bad->end) { D( printf("low\n"); ) lo = mid + 1; }
+ else { D( printf("match!\n"); ) errno = EIO; return (-1); }
+ }
+#ifdef DEBUG
+ if (best)
+ printf(";; next is %"PRIuSEC" .. %"PRIuSEC"\n",
+ best->start, best->end);
+#endif
+ if (best && pos + want > best->start)
+ { want = best->start - pos; fakeerr = EIO; sit(bad_block_delay); }
+ }
+ done = 0;
+ while (want) {
+ if (vob)
+ { errno = 0; n = DVDReadBlocks(vob, pos - file->start, want, p); }
+ else if (file) {
+ if (lseek(dvdfd, (off_t)pos*SECTORSZ, SEEK_SET) < 0)
+ bail_syserr(errno, "failed to seek to sector %"PRIuSEC"", pos);
+ errno = 0; n = read(dvdfd, p, want*SECTORSZ);
+ if (n >= 0) n /= SECTORSZ;
+ } else {
+ memset(p, 0, want*SECTORSZ);
+ n = want;
+ }
+
+ if (n > 0) { done += n; pos += n; p += n*SECTORSZ; want -= n; }
+ else if (!n) break;
+ else if (errno == EIO && errfile) {
+ open_file_on_demand(errfile, &errfp, "bad-sector error log");
+ fprintf(errfp, "%"PRIuSEC" %"PRIuSEC"\n", pos, pos + 1);
+ check_write(errfp, "bad-sector error log");
+ break;
+ } else if (errno != EINTR) break;
+ }
+ if (fakeerr && !errno) errno = fakeerr;
+ else if (done > 0 && good_block_delay) sit(done*good_block_delay);
+ return (!done && errno ? -1 : done);
+}
+
+static void record_bad_sectors(secaddr bad_lo, secaddr bad_hi)
+{
+ char fn[MAXFNSZ];
+
+ if (!mapfile) return;
+
+ open_file_on_demand(mapfile, &mapfp, "bad-sector region map");
+ fprintf(mapfp, "%"PRIuSEC" %"PRIuSEC"", bad_lo, bad_hi);
+
+ if (file && id_kind(file->id) != RAW) {
+ store_filename(fn, file->id);
+ fprintf(mapfp, " # `%s' %"PRIuSEC" .. %"PRIuSEC" of %"PRIuSEC"",
+ fn, bad_lo - file->start, bad_hi - file->start,
+ file->end - file->start);
+ }
+
+ fputc('\n', mapfp);
+ check_write(mapfp, "bad-sector region map");
+}
+
+static void recovered(secaddr bad_lo, secaddr bad_hi)
+{
+ char fn[MAXFNSZ];
+
+ progress_clear(&progress);
+
+ if (!file || id_kind(file->id) == RAW)
+ moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC")",
+ bad_hi - bad_lo, bad_lo, bad_hi);