Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

paranoia.c

/*
 * This file has been modified for the cdrkit suite.
 *
 * The behaviour and appearence of the program code below can differ to a major
 * extent from the version distributed by the original author(s).
 *
 * For details, see Changelog file distributed with the cdrkit package. If you
 * received this file from another source then ask the distributing person for
 * a log of modifications.
 *
 */

/* @(#)paranoia.c 1.33 04/08/17 J. Schilling from cdparanoia-III-alpha9.8 */
/*
 *    Modifications to make the code portable Copyright (c) 2002 J. Schilling
 */
/*
 * CopyPolicy: GNU Public License 2 applies
 * Copyright (C) by Monty (xiphmont@mit.edu)
 *
 * Toplevel file for the paranoia abstraction over the cdda lib
 *
 */

/* immediate todo:: */
/* Allow disabling of root fixups? */

/*
 * Dupe bytes are creeping into cases that require greater overlap
 * than a single fragment can provide.  We need to check against a
 * larger area* (+/-32 sectors of root?) to better eliminate
 *  dupes. Of course this leads to other problems... Is it actually a
 *  practically solvable problem?
 */
/* Bimodal overlap distributions break us. */
/* scratch detection/tolerance not implemented yet */

/*
 * Da new shtick: verification now a two-step assymetric process.
 *
 * A single 'verified/reconstructed' data segment cache, and then the
 * multiple fragment cache
 *
 * verify a newly read block against previous blocks; do it only this
 * once. We maintain a list of 'verified sections' from these matches.
 *
 * We then glom these verified areas into a new data buffer.
 * Defragmentation fixups are allowed here alone.
 *
 * We also now track where read boundaries actually happened; do not
 * verify across matching boundaries.
 */

/*
 * Silence.  "It's BAAAAAAaaack."
 *
 * audio is now treated as great continents of values floating on a
 * mantle of molten silence.  Silence is not handled by basic
 * verification at all; we simply anchor sections of nonzero audio to a
 * position and fill in everything else as silence.  We also note the
 * audio that interfaces with silence; an edge must be 'wet'.
 */

#include <mconfig.h>
#include <allocax.h>
#include <stdxlib.h>
#include <unixstd.h>
#include <standard.h>
#include <utypes.h>
#include <stdio.h>
#include <strdefs.h>
#include "p_block.h"
#include "cdda_paranoia.h"
#include "overlap.h"
#include "gap.h"
#include "isort.h"
#include "pmalloc.h"

/*
 * used by: i_iterate_stage2() / i_stage2_each()
 */
typedef struct sync_result {
      long        offset;
      long        begin;
      long        end;
} sync_result;

static inline long re(root_block *root);
static inline long rb(root_block *root);
static inline long rs(root_block *root);
static inline Int16_t *rv(root_block *root);
static inline long i_paranoia_overlap(Int16_t *buffA, Int16_t *buffB,
                                      long offsetA, long offsetB,
                                      long sizeA, long sizeB,
                                      long *ret_begin, long *ret_end);
static inline long i_paranoia_overlap2(Int16_t *buffA, Int16_t *buffB,
                                       Uchar *flagsA, Uchar *flagsB,
                                       long offsetA, long offsetB,
                                       long sizeA, long sizeB,
                                       long *ret_begin, long *ret_end);
static inline long do_const_sync(c_block *A,
                                 sort_info *B, Uchar *flagB,
                                 long posA, long posB,
                                 long *begin, long *end, long *offset);
static inline long try_sort_sync(cdrom_paranoia *p,
                                 sort_info *A, Uchar *Aflags,
                                 c_block *B,
                                 long post, long *begin, long *end,
                                 long *offset, void (*callback) (long, int));
static inline void stage1_matched(c_block *old, c_block *new,
                                  long matchbegin, long matchend, 
                                                                   long matchoffset, 
                                                                   void (*callback) (long, int));
static long i_iterate_stage1(cdrom_paranoia *p, c_block *old, c_block *new,
                             void (*callback) (long, int));
static long i_stage1(cdrom_paranoia *p, c_block *new, 
                                          void (*callback) (long, int));
static long i_iterate_stage2(cdrom_paranoia *p, v_fragment *v, sync_result *r, 
                                                        void (*callback)(long, int));
static void i_silence_test(root_block *root);
static long i_silence_match(root_block *root, v_fragment *v,
                            void (*callback) (long, int));
static long i_stage2_each(root_block *root, v_fragment *v,
                          void (*callback) (long, int));
static int i_init_root(root_block *root, v_fragment *v, 
                       long begin, void (*callback)(long, int));
static int vsort(const void *a, const void *b);
static int i_stage2(cdrom_paranoia *p, long beginword, long endword, 
                                      void (*callback)(long, int));
static void i_end_case(cdrom_paranoia *p, long endword, 
                       void (*callback)(long, int));
static void verify_skip_case(cdrom_paranoia *p, 
                             void (*callback)(long, int));
void paranoia_free(cdrom_paranoia *p);
void paranoia_modeset(cdrom_paranoia *p, int enable);
long paranoia_seek(cdrom_paranoia *p, long seek, int mode);
c_block *i_read_c_block(cdrom_paranoia *p, long beginword, long endword, 
                                                void (*callback)(long,int));
Int16_t *paranoia_read(cdrom_paranoia *p, void (*callback)(long, int));
Int16_t *paranoia_read_limited(cdrom_paranoia *p, void (*callback)(long, int),
                               int max_retries);
void paranoia_overlapset(cdrom_paranoia *p, long overlap);


static inline long re(root_block *root)
{
      if (!root)
            return (-1);
      if (!root->vector)
            return (-1);
      return (ce(root->vector));
}

static inline long rb(root_block *root)
{
      if (!root)
            return (-1);
      if (!root->vector)
            return (-1);
      return (cb(root->vector));
}

static inline long rs(root_block *root)
{
      if (!root)
            return (-1);
      if (!root->vector)
            return (-1);
      return (cs(root->vector));
}

static inline Int16_t *rv(root_block *root)
{
      if (!root)
            return (NULL);
      if (!root->vector)
            return (NULL);
      return (cv(root->vector));
}

#define     rc(r) ((r)->vector)

/*
 * matching and analysis code
 */
static inline long
i_paranoia_overlap(Int16_t *buffA, Int16_t *buffB, long offsetA, long offsetB, 
                   long sizeA, long sizeB, long *ret_begin, long *ret_end)
{
      long  beginA = offsetA;
      long  endA = offsetA;
      long  beginB = offsetB;
      long  endB = offsetB;

      for (; beginA >= 0 && beginB >= 0; beginA--, beginB--)
            if (buffA[beginA] != buffB[beginB])
                  break;
      beginA++;
      beginB++;

      for (; endA < sizeA && endB < sizeB; endA++, endB++)
            if (buffA[endA] != buffB[endB])
                  break;

      if (ret_begin)
            *ret_begin = beginA;
      if (ret_end)
            *ret_end = endA;
      return (endA - beginA);
}

static inline long
i_paranoia_overlap2(Int16_t *buffA, Int16_t *buffB, Uchar *flagsA, 
                    Uchar *flagsB, long offsetA, long offsetB, long sizeA, 
                    long sizeB, long *ret_begin, long *ret_end)
{
      long        beginA = offsetA;
      long        endA = offsetA;
      long        beginB = offsetB;
      long        endB = offsetB;

      for (; beginA >= 0 && beginB >= 0; beginA--, beginB--) {
            if (buffA[beginA] != buffB[beginB])
                  break;
            /*
             * don't allow matching across matching sector boundaries
             */
            if ((flagsA[beginA] & flagsB[beginB] & 1)) {
                  beginA--;
                  beginB--;
                  break;
            }
            /*
             * don't allow matching through known missing data
             */
            if ((flagsA[beginA] & 2) || (flagsB[beginB] & 2))
                  break;
      }
      beginA++;
      beginB++;

      for (; endA < sizeA && endB < sizeB; endA++, endB++) {
            if (buffA[endA] != buffB[endB])
                  break;
            /*
             * don't allow matching across matching sector boundaries
             */
            if ((flagsA[endA] & flagsB[endB] & 1) && endA != beginA) {
                  break;
            }
            /*
             * don't allow matching through known missing data
             */
            if ((flagsA[endA] & 2) || (flagsB[endB] & 2))
                  break;
      }

      if (ret_begin)
            *ret_begin = beginA;
      if (ret_end)
            *ret_end = endA;
      return (endA - beginA);
}

/* Top level of the first stage matcher */

/*
 * We match each analysis point of new to the preexisting blocks
 * recursively.  We can also optionally maintain a list of fragments of
 * the preexisting block that didn't match anything, and match them back
 * afterward.
 */
#define     OVERLAP_ADJ (MIN_WORDS_OVERLAP/2-1)

static inline long
do_const_sync(c_block *A, sort_info *B, Uchar *flagB, long posA, long posB, 
              long *begin, long *end, long *offset)
{
      Uchar       *flagA = A->flags;
      long        ret = 0;

      if (flagB == NULL)
            ret = i_paranoia_overlap(cv(A), iv(B), posA, posB,
                  cs(A), is(B), begin, end);
      else if ((flagB[posB] & 2) == 0)
            ret = i_paranoia_overlap2(cv(A), iv(B), flagA, flagB, posA, posB, cs(A),
                  is(B), begin, end);

      if (ret > MIN_WORDS_SEARCH) {
            *offset = (posA + cb(A)) - (posB + ib(B));
            *begin += cb(A);
            *end += cb(A);
            return (ret);
      }
      return (0);
}

/*
 * post is w.r.t. B.  in stage one, we post from old.  In stage 2 we
 * post from root. Begin, end, offset count from B's frame of
 * reference
 */
static inline long
try_sort_sync(cdrom_paranoia *p, sort_info *A, Uchar *Aflags, c_block *B, 
              long post, long *begin, long *end, long *offset,
              void (*callback)(long, int))
{

      long        dynoverlap = p->dynoverlap;
      sort_link   *ptr = NULL;
      Uchar       *Bflags = B->flags;

      /*
       * block flag matches 0x02 (unmatchable)
       */
      if (Bflags == NULL || (Bflags[post - cb(B)] & 2) == 0) {
            /*
             * always try absolute offset zero first!
             */
            {
                  long        zeropos = post - ib(A);

                  if (zeropos >= 0 && zeropos < is(A)) {
                        if (cv(B)[post - cb(B)] == iv(A)[zeropos]) {
                              if (do_const_sync(B, A, Aflags,
                                          post - cb(B), zeropos,
                                          begin, end, offset)) {

                                    offset_add_value(p, &(p->stage1), *offset, callback);

                                    return (1);
                              }
                        }
                  }
            }
      } else
            return (0);

      ptr = sort_getmatch(A, post - ib(A), dynoverlap, cv(B)[post - cb(B)]);

      while (ptr) {

            if (do_const_sync(B, A, Aflags,
                        post - cb(B), ipos(A, ptr),
                        begin, end, offset)) {
                  offset_add_value(p, &(p->stage1), *offset, callback);
                  return (1);
            }
            ptr = sort_nextmatch(A, ptr);
      }

      *begin = -1;
      *end = -1;
      *offset = -1;
      return (0);
}

static inline void
stage1_matched(c_block *old, c_block *new, long matchbegin, long matchend, 
               long matchoffset, void (*callback)(long, int))
{
      long        i;
      long        oldadjbegin = matchbegin - cb(old);
      long        oldadjend = matchend - cb(old);
      long        newadjbegin = matchbegin - matchoffset - cb(new);
      long        newadjend = matchend - matchoffset - cb(new);

      if (matchbegin - matchoffset <= cb(new) ||
            matchbegin <= cb(old) ||
            (new->flags[newadjbegin] & 1) ||
            (old->flags[oldadjbegin] & 1)) {
            if (matchoffset)
                  if (callback)
                        (*callback) (matchbegin, PARANOIA_CB_FIXUP_EDGE);
      } else if (callback)
            (*callback) (matchbegin, PARANOIA_CB_FIXUP_ATOM);

      if (matchend - matchoffset >= ce(new) ||
            (new->flags[newadjend] & 1) ||
            matchend >= ce(old) ||
            (old->flags[oldadjend] & 1)) {
            if (matchoffset)
                  if (callback)
                        (*callback) (matchend, PARANOIA_CB_FIXUP_EDGE);
      } else if (callback)
            (*callback) (matchend, PARANOIA_CB_FIXUP_ATOM);

      /*
       * Mark the verification flags.  Don't mark the first or last OVERLAP/2
       * elements so that overlapping fragments have to overlap by OVERLAP to
       * actually merge. We also remove elements from the sort such that
       * later sorts do not have to sift through already matched data
       */
      newadjbegin += OVERLAP_ADJ;
      newadjend -= OVERLAP_ADJ;
      for (i = newadjbegin; i < newadjend; i++)
            new->flags[i] |= 4;     /* mark verified */

      oldadjbegin += OVERLAP_ADJ;
      oldadjend -= OVERLAP_ADJ;
      for (i = oldadjbegin; i < oldadjend; i++)
            old->flags[i] |= 4;     /* mark verified */

}

#define     CB_NULL           (void (*)(long, int))0

static long 
i_iterate_stage1(cdrom_paranoia *p, c_block *old, c_block *new, 
                 void (*callback)(long, int))
{

      long        matchbegin = -1;
      long        matchend = -1;
      long        matchoffset;

      /*
       * we no longer try to spread the stage one search area by dynoverlap
       */
      long        searchend = min(ce(old), ce(new));
      long        searchbegin = max(cb(old), cb(new));
      long        searchsize = searchend - searchbegin;
      sort_info   *i = p->sortcache;
      long        ret = 0;
      long        j;

      long        tried = 0;
      long        matched = 0;

      if (searchsize <= 0)
            return (0);

      /*
       * match return values are in terms of the new vector, not old
       */
      for (j = searchbegin; j < searchend; j += 23) {
            if ((new->flags[j - cb(new)] & 6) == 0) {
                  tried++;
                  if (try_sort_sync(p, i, new->flags, old, j, &matchbegin, &matchend, &matchoffset,
                              callback) == 1) {

                        matched += matchend - matchbegin;

                        /*
                         * purely cosmetic: if we're matching zeros,
                         * don't use the callback because they will
                         * appear to be all skewed
                         */
                        {
                              long        jj = matchbegin - cb(old);
                              long        end = matchend - cb(old);

                              for (; jj < end; jj++)
                                    if (cv(old)[jj] != 0)
                                          break;
                              if (jj < end) {
                                    stage1_matched(old, new, matchbegin, matchend, matchoffset, callback);
                              } else {
                                    stage1_matched(old, new, matchbegin, matchend, matchoffset, CB_NULL);
                              }
                        }
                        ret++;
                        if (matchend - 1 > j)
                              j = matchend - 1;
                  }
            }
      }
#ifdef NOISY
      fprintf(stderr, "iterate_stage1: search area=%ld[%ld-%ld] tried=%ld matched=%ld spans=%ld\n",
            searchsize, searchbegin, searchend, tried, matched, ret);
#endif

      return (ret);
}

static long 
i_stage1(cdrom_paranoia *p, c_block *new, void (*callback)(long, int))
{

      long        size = cs(new);
      c_block           *ptr = c_last(p);
      int         ret = 0;
      long        begin = 0;
      long        end;

      if (ptr)
            sort_setup(p->sortcache, cv(new), &cb(new), cs(new),
                  cb(new), ce(new));

      while (ptr && ptr != new) {

            if (callback)
                  (*callback) (cb(new), PARANOIA_CB_VERIFY);
            i_iterate_stage1(p, ptr, new, callback);

            ptr = c_prev(ptr);
      }

      /*
       * parse the verified areas of new into v_fragments
       */
      begin = 0;
      while (begin < size) {
            for (; begin < size; begin++)
                  if (new->flags[begin] & 4)
                        break;
            for (end = begin; end < size; end++)
                  if ((new->flags[end] & 4) == 0)
                        break;
            if (begin >= size)
                  break;

            ret++;

            new_v_fragment(p, new, cb(new) + max(0, begin - OVERLAP_ADJ),
                  cb(new) + min(size, end + OVERLAP_ADJ),
                  (end + OVERLAP_ADJ >= size && new->lastsector));

            begin = end;
      }

      return (ret);
}

/*
 * reconcile v_fragments to root buffer.  Free if matched, fragment/fixup root
 * if necessary
 *
 * do *not* match using zero posts
 */
static long 
i_iterate_stage2(cdrom_paranoia *p, v_fragment *v, sync_result *r, 
                 void (*callback)(long, int))
{
      root_block  *root = &(p->root);
      long        matchbegin = -1;
      long        matchend = -1;
      long        offset;
      long        fbv;
      long        fev;

#ifdef NOISY
      fprintf(stderr, "Stage 2 search: fbv=%ld fev=%ld\n", fb(v), fe(v));
#endif

      if (min(fe(v) + p->dynoverlap, re(root)) -
            max(fb(v) - p->dynoverlap, rb(root)) <= 0)
            return (0);

      if (callback)
            (*callback) (fb(v), PARANOIA_CB_VERIFY);

      /*
       * just a bit of v; determine the correct area
       */
      fbv = max(fb(v), rb(root) - p->dynoverlap);

      /*
       * we want to avoid zeroes
       */
      while (fbv < fe(v) && fv(v)[fbv - fb(v)] == 0)
            fbv++;
      if (fbv == fe(v))
            return (0);
      fev = min(min(fbv + 256, re(root) + p->dynoverlap), fe(v));

      {
            /*
             * spread the search area a bit.  We post from root, so
             * containment must strictly adhere to root
             */
            long        searchend = min(fev + p->dynoverlap, re(root));
            long        searchbegin = max(fbv - p->dynoverlap, rb(root));
            sort_info   *i = p->sortcache;
            long        j;

            sort_setup(i, fv(v), &fb(v), fs(v), fbv, fev);
            for (j = searchbegin; j < searchend; j += 23) {
                  while (j < searchend && rv(root)[j - rb(root)] == 0)
                        j++;
                  if (j == searchend)
                        break;

                  if (try_sort_sync(p, i, (Uchar *)0, rc(root), j,
                              &matchbegin, &matchend, &offset, callback)) {

                        r->begin = matchbegin;
                        r->end = matchend;
                        r->offset = -offset;
                        if (offset)
                              if (callback)
                                    (*callback) (r->begin, PARANOIA_CB_FIXUP_EDGE);
                        return (1);
                  }
            }
      }
      return (0);
}

/*
 * simple test for a root vector that ends in silence
 */
static void i_silence_test(root_block *root)
{
      Int16_t           *vec = rv(root);
      long        end = re(root) - rb(root) - 1;
      long        j;

      for (j = end - 1; j >= 0; j--)
            if (vec[j] != 0)
                  break;
      if (j < 0 || end - j > MIN_SILENCE_BOUNDARY) {
            if (j < 0)
                  j = 0;
            root->silenceflag = 1;
            root->silencebegin = rb(root) + j;
            if (root->silencebegin < root->returnedlimit)
                  root->silencebegin = root->returnedlimit;
      }
}

/*
 * match into silence vectors at offset zero if at all possible.  This
 * also must be called with vectors in ascending begin order in case
 * there are nonzero islands
 */
static long
i_silence_match(root_block *root, v_fragment *v, void (*callback)(long, int))
{

      cdrom_paranoia    *p = v->p;
      Int16_t     *vec = fv(v);
      long        end = fs(v);
      long        begin;
      long        j;

      /*
       * does this vector begin wet?
       */
      if (end < MIN_SILENCE_BOUNDARY)
            return (0);
      for (j = 0; j < end; j++)
            if (vec[j] != 0)
                  break;
      if (j < MIN_SILENCE_BOUNDARY)
            return (0);
      j += fb(v);

      /*
       * is the new silent section ahead of the end of the old
       * by < p->dynoverlap?
       */
      if (fb(v) >= re(root) && fb(v) - p->dynoverlap < re(root)) {
            /*
             * extend the zeroed area of root
             * XXX dynarrays are not needed here.
             */
            long        addto = fb(v) + MIN_SILENCE_BOUNDARY - re(root);
/*          Int16_t           avec[addto];*/
#ifdef      HAVE_ALLOCA
            Int16_t           *avec = alloca(addto * sizeof (Int16_t));
#else
            Int16_t           *avec = _pmalloc(addto * sizeof (Int16_t));
#endif

            memset(avec, 0, sizeof (avec));
            c_append(rc(root), avec, addto);
#ifndef     HAVE_ALLOCA
            _pfree(avec);
#endif
      }
      /*
       * do we have an 'effortless' overlap?
       */
      begin = max(fb(v), root->silencebegin);
      end = min(j, re(root));

      if (begin < end) {
            /*
             * don't use it unless it will extend...
             */
            if (fe(v) > re(root)) {
                  long        voff = begin - fb(v);

                  c_remove(rc(root), begin - rb(root), -1);
                  c_append(rc(root), vec + voff, fs(v) - voff);
            }
            offset_add_value(p, &p->stage2, 0, callback);

      } else {
            if (j < begin) {
                  /*
                   * OK, we'll have to force it a bit as the root is
                   * jittered forward
                   */
                  long        voff = j - fb(v);

                  /*
                   * don't use it unless it will extend...
                   */
                  if (begin + fs(v) - voff > re(root)) {
                        c_remove(rc(root), root->silencebegin - rb(root), -1);
                        c_append(rc(root), vec + voff, fs(v) - voff);
                  }
                  offset_add_value(p, &p->stage2, end - begin, callback);
            } else
                  return (0);
      }

      /*
       * test the new root vector for ending in silence
       */
      root->silenceflag = 0;
      i_silence_test(root);

      if (v->lastsector)
            root->lastsector = 1;
      free_v_fragment(v);
      return (1);
}

static long
i_stage2_each(root_block *root, v_fragment *v, void (*callback)(long, int))
{

      cdrom_paranoia *p = v->p;
      long        dynoverlap = p->dynoverlap / 2 * 2;

      if (!v || !v->one)
            return (0);

      if (!rv(root)) {
            return (0);
      } else {
            sync_result r;

            if (i_iterate_stage2(p, v, &r, callback)) {

                  long        begin = r.begin - rb(root);
                  long        end = r.end - rb(root);
                  long        offset = r.begin + r.offset - fb(v) - begin;
                  long        temp;
                  c_block           *l = NULL;

                  /*
                   * we have a match! We don't rematch off rift, we chase
                   * the match all the way to both extremes doing rift
                   * analysis.
                   */
#ifdef NOISY
                  fprintf(stderr, "Stage 2 match\n");
#endif

                  /*
                   * chase backward
                   * note that we don't extend back right now, only
                   * forward.
                   */
                  while ((begin + offset > 0 && begin > 0)) {
                        long        matchA = 0,
                                    matchB = 0,
                                    matchC = 0;
                        long        beginL = begin + offset;

                        if (l == NULL) {
                              Int16_t     *buff = _pmalloc(fs(v) * sizeof (Int16_t));

                              l = c_alloc(buff, fb(v), fs(v));
                              memcpy(buff, fv(v), fs(v) * sizeof (Int16_t));
                        }
                        i_analyze_rift_r(rv(root), cv(l),
                              rs(root), cs(l),
                              begin - 1, beginL - 1,
                              &matchA, &matchB, &matchC);

#ifdef NOISY
                        fprintf(stderr, "matching rootR: matchA:%ld matchB:%ld matchC:%ld\n",
                              matchA, matchB, matchC);
#endif

                        if (matchA) {
                              /*
                               * a problem with root
                               */
                              if (matchA > 0) {
                                    /*
                                     * dropped bytes; add back from v
                                     */
                                    if (callback)
                                          (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DROPPED);
                                    if (rb(root) + begin < p->root.returnedlimit)
                                          break;
                                    else {
                                          c_insert(rc(root), begin, cv(l) + beginL - matchA,
                                                matchA);
                                          offset -= matchA;
                                          begin += matchA;
                                          end += matchA;
                                    }
                              } else {
                                    /*
                                     * duplicate bytes; drop from root
                                     */
                                    if (callback)
                                          (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DUPED);
                                    if (rb(root) + begin + matchA < p->root.returnedlimit)
                                          break;
                                    else {
                                          c_remove(rc(root), begin + matchA, -matchA);
                                          offset -= matchA;
                                          begin += matchA;
                                          end += matchA;
                                    }
                              }
                        } else if (matchB) {
                              /*
                               * a problem with the fragment
                               */
                              if (matchB > 0) {
                                    /*
                                     * dropped bytes
                                     */
                                    if (callback)
                                          (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DROPPED);
                                    c_insert(l, beginL, rv(root) + begin - matchB,
                                          matchB);
                                    offset += matchB;
                              } else {
                                    /*
                                     * duplicate bytes
                                     */
                                    if (callback)
                                          (*callback) (begin + rb(root) - 1, PARANOIA_CB_FIXUP_DUPED);
                                    c_remove(l, beginL + matchB, -matchB);
                                    offset += matchB;
                              }
                        } else if (matchC) {
                              /*
                               * Uhh... problem with both
                               * Set 'disagree' flags in root
                               */
                              if (rb(root) + begin - matchC < p->root.returnedlimit)
                                    break;
                              c_overwrite(rc(root), begin - matchC,
                                    cv(l) + beginL - matchC, matchC);

                        } else {
                              /*
                               * do we have a mismatch due to silence
                               * beginning/end case?
                               * in the 'chase back' case, we don't
                               * do anything.
                               * Did not determine nature of
                               * difficulty... report and bail
                               */
                              /* RRR(*callback)(post,PARANOIA_CB_XXX); */
                              break;
                        }
                        /*
                         * not the most efficient way, but it will do
                         * for now
                         */
                        beginL = begin + offset;
                        i_paranoia_overlap(rv(root), cv(l),
                              begin, beginL,
                              rs(root), cs(l),
                              &begin, &end);
                  }

                  /*
                   * chase forward
                   */
                  temp = l ? cs(l) : fs(v);
                  while (end + offset < temp && end < rs(root)) {
                        long  matchA = 0,
                              matchB = 0,
                              matchC = 0;
                        long  beginL = begin + offset;
                        long  endL = end + offset;

                        if (l == NULL) {
                              Int16_t     *buff = _pmalloc(fs(v) * sizeof (Int16_t));

                              l = c_alloc(buff, fb(v), fs(v));
                              memcpy(buff, fv(v), fs(v) * sizeof (Int16_t));
                        }
                        i_analyze_rift_f(rv(root), cv(l),
                              rs(root), cs(l),
                              end, endL,
                              &matchA, &matchB, &matchC);

#ifdef NOISY
                        fprintf(stderr, "matching rootF: matchA:%ld matchB:%ld matchC:%ld\n",
                              matchA, matchB, matchC);
#endif

                        if (matchA) {
                              /*
                               * a problem with root
                               */
                              if (matchA > 0) {
                                    /*
                                     * dropped bytes; add back from v
                                     */
                                    if (callback)
                                          (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DROPPED);
                                    if (end + rb(root) < p->root.returnedlimit)
                                          break;
                                    c_insert(rc(root), end, cv(l) + endL, matchA);
                              } else {
                                    /*
                                     * duplicate bytes; drop from root
                                     */
                                    if (callback)
                                          (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DUPED);
                                    if (end + rb(root) < p->root.returnedlimit)
                                          break;
                                    c_remove(rc(root), end, -matchA);
                              }
                        } else if (matchB) {
                              /*
                               * a problem with the fragment
                               */
                              if (matchB > 0) {
                                    /*
                                     * dropped bytes
                                     */
                                    if (callback)
                                          (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DROPPED);
                                    c_insert(l, endL, rv(root) + end, matchB);
                              } else {
                                    /*
                                     * duplicate bytes
                                     */
                                    if (callback)
                                          (*callback) (end + rb(root), PARANOIA_CB_FIXUP_DUPED);
                                    c_remove(l, endL, -matchB);
                              }
                        } else if (matchC) {
                              /*
                               * Uhh... problem with both
                               * Set 'disagree' flags in root
                               */
                              if (end + rb(root) < p->root.returnedlimit)
                                    break;
                              c_overwrite(rc(root), end, cv(l) + endL, matchC);
                        } else {
                              analyze_rift_silence_f(rv(root), cv(l),
                                    rs(root), cs(l),
                                    end, endL,
                                    &matchA, &matchB);
                              if (matchA) {
                                    /*
                                     * silence in root
                                     * Can only do this if we haven't
                                     * already returned data
                                     */
                                    if (end + rb(root) >= p->root.returnedlimit) {
                                          c_remove(rc(root), end, -1);
                                    }
                              } else if (matchB) {
                                    /*
                                     * silence in fragment; lose it
                                     */
                                    if (l)
                                          i_cblock_destructor(l);
                                    free_v_fragment(v);
                                    return (1);

                              } else {
                                    /*
                                     * Could not determine nature of
                                     * difficulty... report and bail
                                     */
                                    /* RRR(*callback)(post,PARANOIA_CB_XXX); */
                              }
                              break;
                        }
                        /*
                         * not the most efficient way, but it will do for now
                         */
                        i_paranoia_overlap(rv(root), cv(l),
                              begin, beginL,
                              rs(root), cs(l),
                              (long *)0, &end);
                  }

                  /*
                   * if this extends our range, let's glom
                   */
                  {
                        long  sizeA = rs(root);
                        long  sizeB;
                        long  vecbegin;
                        Int16_t     *vector;

                        if (l) {
                              sizeB = cs(l);
                              vector = cv(l);
                              vecbegin = cb(l);
                        } else {
                              sizeB = fs(v);
                              vector = fv(v);
                              vecbegin = fb(v);
                        }

                        if (sizeB - offset > sizeA || v->lastsector) {
                              if (v->lastsector) {
                                    root->lastsector = 1;
                              }
                              if (end < sizeA)
                                    c_remove(rc(root), end, -1);

                              if (sizeB - offset - end)
                                    c_append(rc(root), vector + end + offset,
                                          sizeB - offset - end);

                              i_silence_test(root);

                              /*
                               * add offset into dynoverlap stats
                               */
                              offset_add_value(p, &p->stage2, offset + vecbegin - rb(root), callback);
                        }
                  }
                  if (l)
                        i_cblock_destructor(l);
                  free_v_fragment(v);
                  return (1);

            } else {
                  /*
                   * D'oh.  No match.  What to do with the fragment?
                   */
                  if (fe(v) + dynoverlap < re(root) && !root->silenceflag) {
                        /*
                         * It *should* have matched.  No good; free it.
                         */
                        free_v_fragment(v);
                  }
                  /*
                   * otherwise, we likely want this for an upcoming match
                   * we don't free the sort info (if it was collected)
                   */
                  return (0);

            }
      }
}

static int
i_init_root(root_block *root, v_fragment *v, long begin, 
            void (*callback)(long, int))
{
      if (fb(v) <= begin && fe(v) > begin) {

            root->lastsector = v->lastsector;
            root->returnedlimit = begin;

            if (rv(root)) {
                  i_cblock_destructor(rc(root));
                  rc(root) = NULL;
            }
            {
                  Int16_t           *buff = _pmalloc(fs(v) * sizeof (Int16_t));

                  memcpy(buff, fv(v), fs(v) * sizeof (Int16_t));
                  root->vector = c_alloc(buff, fb(v), fs(v));
            }
            i_silence_test(root);

            return (1);
      } else
            return (0);
}

static int vsort(const void *a, const void *b)
{
      return ((*(v_fragment **) a)->begin - (*(v_fragment **) b)->begin);
}

static int
i_stage2(cdrom_paranoia *p, long beginword, long endword, 
         void (*callback)(long, int))
{

      int         flag = 1;
      int         ret = 0;
      root_block  *root = &(p->root);

#ifdef NOISY
      fprintf(stderr, "Fragments:%ld\n", p->fragments->active);
      fflush(stderr);
#endif

      /*
       * even when the 'silence flag' is lit, we try to do non-silence
       * matching in the event that there are still audio vectors with
       * content to be sunk before the silence
       */
      while (flag) {
            /*
             * loop through all the current fragments
             */
            v_fragment  *first = v_first(p);
            long        active = p->fragments->active,
                        count = 0;
#ifdef      HAVE_DYN_ARRAYS
            v_fragment  *list[active];
#else
            v_fragment  **list = _pmalloc(active * sizeof (v_fragment *));
#endif

            while (first) {
                  v_fragment  *next = v_next(first);

                  list[count++] = first;
                  first = next;
            }

            flag = 0;
            if (count) {
                  /*
                   * sorted in ascending order of beginning
                   */
                  qsort(list, active, sizeof (v_fragment *), vsort);

                  /*
                   * we try a nonzero based match even if in silent mode
                   * in the case that there are still cached vectors to
                   * sink behind continent->ocean boundary
                   */
                  for (count = 0; count < active; count++) {
                        first = list[count];
                        if (first->one) {
                              if (rv(root) == NULL) {
                                    if (i_init_root(&(p->root), first, beginword, callback)) {
                                          free_v_fragment(first);
                                          flag = 1;
                                          ret++;
                                    }
                              } else {
                                    if (i_stage2_each(root, first, callback)) {
                                          ret++;
                                          flag = 1;
                                    }
                              }
                        }
                  }

                  /*
                   * silence handling
                   */
                  if (!flag && p->root.silenceflag) {
                        for (count = 0; count < active; count++) {
                              first = list[count];
                              if (first->one) {
                                    if (rv(root) != NULL) {
                                          if (i_silence_match(root, first, callback)) {
                                                ret++;
                                                flag = 1;
                                          }
                                    }
                              }
                        }
                  }
            }
#ifndef     HAVE_DYN_ARRAYS
            _pfree(list);
#endif
      }
      return (ret);
}

static void
i_end_case(cdrom_paranoia *p, long endword, void (*callback)(long, int))
{

      root_block  *root = &p->root;

      /*
       * have an 'end' flag; if we've just read in the last sector in a
       * session, set the flag.  If we verify to the end of a fragment
       * which has the end flag set, we're done (set a done flag).
       * Pad zeroes to the end of the read
       */
      if (root->lastsector == 0)
            return;
      if (endword < re(root))
            return;

      {
            long        addto = endword - re(root);
            char        *temp = _pcalloc(addto, sizeof (char) * 2);

            c_append(rc(root), (void *) temp, addto);
            _pfree(temp);

            /*
             * trash da cache
             */
            paranoia_resetcache(p);

      }
}

/*
 * We want to add a sector. Look through the caches for something that
 * spans.  Also look at the flags on the c_block... if this is an
 * obliterated sector, get a bit of a chunk past the obliteration.
 *
 * Not terribly smart right now, actually.  We can probably find
 * *some* match with a cache block somewhere.  Take it and continue it
 * through the skip
 */
static void
verify_skip_case(cdrom_paranoia *p, void (*callback)(long, int))
{

      root_block  *root = &(p->root);
      c_block           *graft = NULL;
      int         vflag = 0;
      int         gend = 0;
      long        post;

#ifdef NOISY
      fprintf(stderr, "\nskipping\n");
#endif

      if (rv(root) == NULL) {
            post = 0;
      } else {
            post = re(root);
      }
      if (post == -1)
            post = 0;

      if (callback)
            (*callback) (post, PARANOIA_CB_SKIP);

      if (p->enable & PARANOIA_MODE_NEVERSKIP)
            return;

      /*
       * We want to add a sector.  Look for a c_block that spans,
       * preferrably a verified area
       */
      {
            c_block           *c = c_first(p);

            while (c) {
                  long        cbegin = cb(c);
                  long        cend = ce(c);

                  if (cbegin <= post && cend > post) {
                        long        vend = post;

                        if (c->flags[post - cbegin] & 4) {
                              /*
                               * verified area!
                               */
                              while (vend < cend && (c->flags[vend - cbegin] & 4))
                                    vend++;
                              if (!vflag || vend > vflag) {
                                    graft = c;
                                    gend = vend;
                              }
                              vflag = 1;
                        } else {
                              /*
                               * not a verified area
                               */
                              if (!vflag) {
                                    while (vend < cend && (c->flags[vend - cbegin] & 4) == 0)
                                          vend++;
                                    if (graft == NULL || gend > vend) {
                                          /*
                                           * smallest unverified area
                                           */
                                          graft = c;
                                          gend = vend;
                                    }
                              }
                        }
                  }
                  c = c_next(c);
            }

            if (graft) {
                  long        cbegin = cb(graft);
                  long        cend = ce(graft);

                  while (gend < cend && (graft->flags[gend - cbegin] & 4))
                        gend++;
                  gend = min(gend + OVERLAP_ADJ, cend);

                  if (rv(root) == NULL) {
                        Int16_t     *buff = _pmalloc(cs(graft));

                        memcpy(buff, cv(graft), cs(graft));
                        rc(root) = c_alloc(buff, cb(graft), cs(graft));
                  } else {
                        c_append(rc(root), cv(graft) + post - cbegin,
                              gend - post);
                  }

                  root->returnedlimit = re(root);
                  return;
            }
      }

      /*
       * No?  Fine.  Great.  Write in some zeroes :-P
       */
      {
            void  *temp = _pcalloc(CD_FRAMESIZE_RAW, sizeof (Int16_t));

            if (rv(root) == NULL) {
                  rc(root) = c_alloc(temp, post, CD_FRAMESIZE_RAW);
            } else {
                  c_append(rc(root), temp, CD_FRAMESIZE_RAW);
                  _pfree(temp);
            }
            root->returnedlimit = re(root);
      }
}

/*
 * toplevel
 */
void paranoia_free(cdrom_paranoia *p)
{
      paranoia_resetall(p);
      sort_free(p->sortcache);
      _pfree(p);
}

void paranoia_modeset(cdrom_paranoia *p, int enable)
{
      p->enable = enable;
}

long paranoia_seek(cdrom_paranoia *p, long seek, int mode)
{
      long  sector;
      long  ret;

      switch (mode) {
      case SEEK_SET:
            sector = seek;
            break;
      case SEEK_END:
            sector = cdda_disc_lastsector(p->d) + seek;
            break;
      default:
            sector = p->cursor + seek;
            break;
      }

      if (cdda_sector_gettrack(p->d, sector) == -1)
            return (-1);

      i_cblock_destructor(p->root.vector);
      p->root.vector = NULL;
      p->root.lastsector = 0;
      p->root.returnedlimit = 0;

      ret = p->cursor;
      p->cursor = sector;

      i_paranoia_firstlast(p);

      /*
       * Evil hack to fix pregap patch for NEC drives! To be rooted out in a10
       */
      p->current_firstsector = sector;

      return (ret);
}

/*
 * returns last block read, -1 on error
 */
c_block *i_read_c_block(cdrom_paranoia *p, long beginword, long endword, 
                        void (*callback)(long, int))
{
      /*
       * why do it this way?  We need to read lots of sectors to kludge
       * around stupid read ahead buffers on cheap drives, as well as avoid
       * expensive back-seeking. We also want to 'jiggle' the start address
       * to try to break borderline drives more noticeably (and make broken
       * drives with unaddressable sectors behave more often).
       */
      long        readat;
      long        firstread;
      long        totaltoread = p->readahead;
      long        sectatonce = p->nsectors;
      long        driftcomp = (float) p->dyndrift / CD_FRAMEWORDS + .5;
      c_block           *new = NULL;
      root_block  *root = &p->root;
      Int16_t           *buffer = NULL;
      void        *bufbase = NULL;
      Uchar       *flags = NULL;
      long        sofar;
      long        dynoverlap = (p->dynoverlap + CD_FRAMEWORDS - 1) / CD_FRAMEWORDS;
      long        anyflag = 0;
      int         reduce = 0;
static      int         pagesize = -1;
#define     valign(x, a)      (((char *)(x)) + ((a) - 1 - ((((UIntptr_t)(x))-1)%(a))))

      /*
       * What is the first sector to read?  want some pre-buffer if we're not
       * at the extreme beginning of the disc
       */
      if (p->enable & (PARANOIA_MODE_VERIFY | PARANOIA_MODE_OVERLAP)) {

            /*
             * we want to jitter the read alignment boundary
             */
            long  target;

            if (rv(root) == NULL || rb(root) > beginword)
                  target = p->cursor - dynoverlap;
            else
                  target = re(root) / (CD_FRAMEWORDS) - dynoverlap;

            if (target + MIN_SECTOR_BACKUP > p->lastread && target <= p->lastread)
                  target = p->lastread - MIN_SECTOR_BACKUP;

            /*
             * we want to jitter the read alignment boundary, as some
             * drives, beginning from a specific point, will tend to
             * lose bytes between sectors in the same place.  Also, as
             * our vectors are being made up of multiple reads, we want
             * the overlap boundaries to move....
             */
            readat = (target & (~((long) JIGGLE_MODULO - 1))) + p->jitter;
            if (readat > target)
                  readat -= JIGGLE_MODULO;
            p->jitter++;
            if (p->jitter >= JIGGLE_MODULO)
                  p->jitter = 0;

      } else {
            readat = p->cursor;
      }

      readat += driftcomp;

      if (p->enable & (PARANOIA_MODE_OVERLAP | PARANOIA_MODE_VERIFY)) {
            flags = _pcalloc(totaltoread * CD_FRAMEWORDS, 1);
            new = new_c_block(p);
            recover_cache(p);
      } else {
            /*
             * in the case of root it's just the buffer
             */
            paranoia_resetall(p);
            new = new_c_block(p);
      }

      /*
       * Do not use valloc() as valloc() in glibc is buggy and returns memory
       * that cannot be passed to free().
       */
      if (pagesize < 0) {
            pagesize = getpagesize();
            if (pagesize < 0)
                  pagesize = 4096;  /* Just a guess */
      }
      reduce = pagesize / CD_FRAMESIZE_RAW;
      bufbase = _pmalloc(totaltoread * CD_FRAMESIZE_RAW + pagesize);
      buffer = (Int16_t *)valign(bufbase, pagesize);
      sofar = 0;
      firstread = -1;

      /*
       * actual read loop
       */
      while (sofar < totaltoread) {
            long  secread = sectatonce;
            long  adjread = readat;
            long  thisread;

            /*
             * don't under/overflow the audio session
             */
            if (adjread < p->current_firstsector) {
                  secread -= p->current_firstsector - adjread;
                  adjread = p->current_firstsector;
            }
            if (adjread + secread - 1 > p->current_lastsector)
                  secread = p->current_lastsector - adjread + 1;

            if (sofar + secread > totaltoread)
                  secread = totaltoread - sofar;

            /*
             * If we are inside the buffer, the transfers are no longer
             * page aligned. Reduce the transfer size to avoid problems.
             * Such problems are definitely known to appear on FreeBSD.
             */
            if ((sofar > 0) && (secread > (sectatonce - reduce)))
                  secread = sectatonce - reduce;

            if (secread > 0) {

                  if (firstread < 0)
                        firstread = adjread;
                  if ((thisread = cdda_read(p->d, buffer + sofar * CD_FRAMEWORDS, adjread,
                                    secread)) < secread) {

                        if (thisread < 0)
                              thisread = 0;

                        /*
                         * Uhhh... right.  Make something up. But
                         * don't make us seek backward!
                         */
                        if (callback)
                              (*callback) ((adjread + thisread) * CD_FRAMEWORDS, PARANOIA_CB_READERR);
                        memset(buffer + (sofar + thisread) * CD_FRAMEWORDS, 0,
                              CD_FRAMESIZE_RAW * (secread - thisread));
                        if (flags)
                              memset(flags + (sofar + thisread) * CD_FRAMEWORDS, 2,
                                    CD_FRAMEWORDS * (secread - thisread));
                  }
                  if (thisread != 0)
                        anyflag = 1;

                  if (flags && sofar != 0) {
                        /*
                         * Don't verify across overlaps that are too
                         * close to one another
                         */
                        int   i = 0;

                        for (i = -MIN_WORDS_OVERLAP / 2; i < MIN_WORDS_OVERLAP / 2; i++)
                              flags[sofar * CD_FRAMEWORDS + i] |= 1;
                  }
                  p->lastread = adjread + secread;

                  if (adjread + secread - 1 == p->current_lastsector)
                        new->lastsector = -1;

                  if (callback)
                        (*callback) ((adjread + secread - 1) * CD_FRAMEWORDS, PARANOIA_CB_READ);

                  sofar += secread;
                  readat = adjread + secread;
            } else if (readat < p->current_firstsector) {
                  readat += sectatonce;
                                    /*
                                     * due to being before the
                                     * readable area
                                     */
            } else {
                  break;      /* due to being past the readable area */
            }
      }

      if (anyflag) {
            new->vector = _pmalloc(totaltoread * CD_FRAMESIZE_RAW);
            memcpy(new->vector, buffer, totaltoread * CD_FRAMESIZE_RAW);
            _pfree(bufbase);

            new->begin = firstread * CD_FRAMEWORDS - p->dyndrift;
            new->size = sofar * CD_FRAMEWORDS;
            new->flags = flags;
      } else {
            if (new)
                  free_c_block(new);
            if (bufbase)
                  _pfree(bufbase);
            if (flags)
                  _pfree(flags);
            new = NULL;
            bufbase = NULL;
            flags = NULL;
      }
      return (new);
}

/*
 * The returned buffer is *not* to be freed by the caller.  It will
 * persist only until the next call to paranoia_read() for this p
 */
Int16_t *paranoia_read(cdrom_paranoia *p, void (*callback)(long, int))
{
      return (paranoia_read_limited(p, callback, 20));
}

/*
 * I added max_retry functionality this way in order to avoid breaking any
 * old apps using the nerw libs.  cdparanoia 9.8 will need the updated libs,
 * but nothing else will require it.
 */
Int16_t *paranoia_read_limited(cdrom_paranoia *p, void (*callback)(long, int),
                               int max_retries)
{
      long        beginword = p->cursor * (CD_FRAMEWORDS);
      long        endword = beginword + CD_FRAMEWORDS;
      long        retry_count = 0;
      long        lastend = -2;
      root_block  *root = &p->root;

      if (beginword > p->root.returnedlimit)
            p->root.returnedlimit = beginword;
      lastend = re(root);

      /*
       * First, is the sector we want already in the root?
       */
      while (rv(root) == NULL ||
            rb(root) > beginword ||
            (re(root) < endword + p->maxdynoverlap &&
                  p->enable & (PARANOIA_MODE_VERIFY | PARANOIA_MODE_OVERLAP)) ||
            re(root) < endword) {

            /*
             * Nope; we need to build or extend the root verified range
             */
            if (p->enable & (PARANOIA_MODE_VERIFY | PARANOIA_MODE_OVERLAP)) {
                  i_paranoia_trim(p, beginword, endword);
                  recover_cache(p);
                  if (rb(root) != -1 && p->root.lastsector)
                        i_end_case(p, endword + p->maxdynoverlap,
                              callback);
                  else
                        i_stage2(p, beginword,
                              endword + p->maxdynoverlap,
                              callback);
            } else
                  i_end_case(p, endword + p->maxdynoverlap,
                        callback);  /* only trips if we're already */
                                    /* done */

            if (!(rb(root) == -1 || rb(root) > beginword ||
                        re(root) < endword + p->maxdynoverlap))
                  break;

            /*
             * Hmm, need more.  Read another block
             */
            {
                  c_block     *new = i_read_c_block(p, beginword, endword, callback);

                  if (new) {
                        if (p->enable & (PARANOIA_MODE_OVERLAP | PARANOIA_MODE_VERIFY)) {

                              if (p->enable & PARANOIA_MODE_VERIFY)
                                    i_stage1(p, new, callback);
                              else {
                                    /*
                                     * just make v_fragments from the
                                     * boundary information.
                                     */
                                    long  begin = 0,
                                          end = 0;

                                    while (begin < cs(new)) {
                                          while (end < cs(new) && (new->flags[begin] & 1))
                                                begin++;
                                          end = begin + 1;
                                          while (end < cs(new) && (new->flags[end] & 1) == 0)
                                                end++;
                                          {
                                                new_v_fragment(p, new, begin + cb(new),
                                                      end + cb(new),
                                                      (new->lastsector && cb(new) + end == ce(new)));
                                          }
                                          begin = end;
                                    }
                              }

                        } else {

                              if (p->root.vector)
                                    i_cblock_destructor(p->root.vector);
                              free_elem(new->e, 0);
                              p->root.vector = new;

                              i_end_case(p, endword + p->maxdynoverlap,
                                    callback);

                        }
                  }
            }

            /*
             * Are we doing lots of retries?  **********************
             *
             * Check unaddressable sectors first.  There's no backoff
             * here; jiggle and minimum backseek handle that for us
             */
            if (rb(root) != -1 && lastend + 588 < re(root)) {
                  /*
                   * If we've not grown half a sector
                   */
                  lastend = re(root);
                  retry_count = 0;
            } else {
                  /*
                   * increase overlap or bail
                   */
                  retry_count++;

                  /*
                   * The better way to do this is to look at how many
                   * actual matches we're getting and what kind of gap
                   */
                  if (retry_count % 5 == 0) {
                        if (p->dynoverlap == p->maxdynoverlap ||
                                    retry_count == max_retries) {
                              verify_skip_case(p, callback);
                              retry_count = 0;
                        } else {
                              if (p->stage1.offpoints != -1) {    /* hack */
                                    p->dynoverlap *= 1.5;
                                    if (p->dynoverlap > p->maxdynoverlap)
                                          p->dynoverlap = p->maxdynoverlap;
                                    if (callback)
                                          (*callback) (p->dynoverlap, PARANOIA_CB_OVERLAP);
                              }
                        }
                  }
            }
      }
      p->cursor++;

      return (rv(root) + (beginword - rb(root)));
}

/*
 * a temporary hack
 */
void paranoia_overlapset(cdrom_paranoia *p, long overlap)
{
      p->dynoverlap = overlap * CD_FRAMEWORDS;
      p->stage1.offpoints = -1;
}

Generated by  Doxygen 1.6.0   Back to index