/*
* 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;
}