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