summaryrefslogtreecommitdiff
path: root/libparanoia
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
commit71dc8760ff4de5f365330d1bc571d934deb54af9 (patch)
tree7346d42a282562a3937d82307012b5857d642ce6 /libparanoia
downloadcdrkit-upstream.tar.gz
Imported Upstream version 1.1.11upstream/1.1.11upstream
Diffstat (limited to 'libparanoia')
-rw-r--r--libparanoia/CMakeLists.txt6
-rw-r--r--libparanoia/README.interface75
-rw-r--r--libparanoia/README.paranoia83
-rw-r--r--libparanoia/cdda_paranoia.h108
-rw-r--r--libparanoia/gap.c244
-rw-r--r--libparanoia/gap.h46
-rw-r--r--libparanoia/isort.c159
-rw-r--r--libparanoia/isort.h69
-rw-r--r--libparanoia/overlap.c228
-rw-r--r--libparanoia/overlap.h33
-rw-r--r--libparanoia/overlapdef.txt20
-rw-r--r--libparanoia/p_block.c474
-rw-r--r--libparanoia/p_block.h210
-rw-r--r--libparanoia/paranoia.c1741
-rw-r--r--libparanoia/pmalloc.c89
-rw-r--r--libparanoia/pmalloc.h42
16 files changed, 3627 insertions, 0 deletions
diff --git a/libparanoia/CMakeLists.txt b/libparanoia/CMakeLists.txt
new file mode 100644
index 0000000..4319496
--- /dev/null
+++ b/libparanoia/CMakeLists.txt
@@ -0,0 +1,6 @@
+PROJECT (LIBparanoia C)
+INCLUDE_DIRECTORIES(../include ${CMAKE_BINARY_DIR} ../wodim ${CMAKE_BINARY_DIR}/include)
+ADD_DEFINITIONS(-DHAVE_CONFIG_H)
+SET(LIBparanoia_SRCS gap.c isort.c overlap.c p_block.c paranoia.c pmalloc.c)
+
+ADD_LIBRARY (paranoia STATIC ${LIBparanoia_SRCS})
diff --git a/libparanoia/README.interface b/libparanoia/README.interface
new file mode 100644
index 0000000..a7a44b4
--- /dev/null
+++ b/libparanoia/README.interface
@@ -0,0 +1,75 @@
+/*
+ * Exports (libparanoia) cdda_paranoia.h
+ */
+cdrom_paranoia *paranoia_init __PR((void * d, int nsectors));
+void paranoia_modeset __PR((cdrom_paranoia * p, int mode));
+long paranoia_seek __PR((cdrom_paranoia * p, long seek, int mode));
+Int16_t *paranoia_read __PR((cdrom_paranoia * p, void (*callback) (long, int)));
+Int16_t *paranoia_read_limited __PR((cdrom_paranoia * p, void (*callback) (long, int), int maxretries));
+void paranoia_free __PR((cdrom_paranoia * p));
+void paranoia_overlapset __PR((cdrom_paranoia * p, long overlap));
+
+/*
+ * Exports ?? (libparanoia) overlap.h
+ */
+extern void paranoia_resetall __PR((cdrom_paranoia * p));
+extern void paranoia_resetcache __PR((cdrom_paranoia * p));
+
+Supported:
+
+PARANOIA_MODE_VERIFY
+PARANOIA_MODE_OVERLAP
+PARANOIA_MODE_NEVERSKIP
+
+Unsupported:
+
+PARANOIA_MODE_FRAGMENT
+PARANOIA_MODE_SCRATCH
+PARANOIA_MODE_REPAIR
+
+
+
+/*
+ * Imports (global Code)
+ */
+cdda_disc_firstsector (cdrom_drive *d) -> long sector
+cdda_disc_lastsector (cdrom_drive *d) -> long sector
+cdda_read (cdrom_drive *d, void *buffer, long beginsector, long sectors) -> long sectors
+cdda_sector_gettrack (cdrom_drive *d,long sector) -> int trackno
+cdda_track_audiop (cdrom_drive *d,int track) -> int ??? /* Is audiotrack */
+cdda_track_firstsector (cdrom_drive *d,int track) -> long sector
+cdda_track_lastsector (cdrom_drive *d,int track) -> long sector
+cdda_tracks (cdrom_drive *d) -> int tracks
+
+callback (long inpos, int function)
+
+/*
+ * Imports (libc)
+ */
+calloc
+free
+malloc
+realloc
+
+memcmp
+memcpy
+memmove
+memset
+
+qsort
+
+/*--------------------------------------------------------------------------*/
+usalp = usal_open();
+bufsize = usal_bufsize(usalp, CDR_BUF_SIZE);
+nsecs = bufsize / SEC_SIZE;
+
+cdp = paranoia_init(usalp, nsecs);
+# paranoia_modeset(cdp, mode);
+# paranoia_overlapset(cdp, overlap);
+
+while (not ready) {
+ bp = paranoia_read(cdp, NULL);
+ write(f, bp, SEC_SISE);
+}
+
+paranoia_free(cdp);
diff --git a/libparanoia/README.paranoia b/libparanoia/README.paranoia
new file mode 100644
index 0000000..4864c32
--- /dev/null
+++ b/libparanoia/README.paranoia
@@ -0,0 +1,83 @@
+# @(#)README.paranoia 1.1 97/04/04 J. Schilling from Monty (xiphmont@mit.edu)
+README.paranoia
+
+Paranoia II:
+(c) Monty
+xiphmont@mit.edu
+
+Hi there.
+
+All CDROM drives are not created equal. You're probably using this
+patch because yours is a little less equal than others-- or maybe you
+just keep your CD collection in a box of full of gravel. Jewel cases
+are for wimps; you know what I'm talking about.
+
+This patch adds extra-robust interframe syncronization, code to detect
+scratches (and hold sync across the scratch) and finally routines to
+filter out scratches as best possible. These are all handled
+automatically by fairly modular code.
+
+1) extra interframe syncronization -------------------------------------
+
+Some CD drives can read audio data from an exact starting point to an
+exact ending point flawlessly; these are rare. A larger number of
+drives read from only an approximately correct starting point, but do
+manage to get all the data in-tact from wherever they manage to begin
+the read. Stock cdda2wav is coded with this in mind.
+
+More drives, especially IDE-like and recent ATAPI drives, suffer from
+framing misalignments within atomic multi-sector read operations.
+Cdda2wav is also set, by default, to read 75 sectors at a time from
+ATAPI drives, which Linux's IDE driver breaks into multiple 8 sector
+reads to conserve kernel memory. Both of these things will break
+cdda2wav; the symptoms include cracks or pops within the read sample.
+
+The "Paranoia" patch will verify the alignment of *every* byte read by
+cdda2wav. The goal is data integrity, not performance: a minimum 50%
+speed hit will result from the patch, likely more if your CDROM
+suffers from the framing bugs the patch corrects.
+
+2) scratch detection and tolerance ------------------------------------
+
+Because overlap syncronization requires matching exact, perfect sample
+sections, scratched CDs typically cause normal cdda2wav to bail when
+overlap syncronization is turned on. The second part of the
+"Paranoia" patch ignores scratches in the read bitstream and
+syncronizes using the data that can still be "trusted". This step
+also collects first-pass scratch detection information used by the
+third section of the patch.
+
+Expect performance to drop through the floor on a badly scratched CD;
+the code can no longer use a simple case of exact matching for speedy
+correlation. Maintaining solid sync across a scratch is processor
+bound; later I'll add speedier algorithms for correlation than the
+brute force method currently used. Scratch detection imposes little
+additional overhead on a non-scratched CD.
+
+3) scratch repair -----------------------------------------------------
+
+Scratches are an irrevocable loss of data. Still, it's usually
+possible to reconstruct a sample closer to the original data than the
+raw hissing, fluttering and crackling that severe scratches in the CD
+surface produce.
+
+Scratch filtering is the 'hard part' of this patch. It needs to do
+two things; first, find the 'pops' that escaped detection in section 2
+of the patch and secondly fill in the gaps. Both are acheived using
+delta (slew) averaging and forward and backward linear predictive
+interpolation (with IIR filters) to fill in known gaps as well as look
+for 'problem values' in stretches of sample known to be scratched.
+
+THE SCRATCH DETECTION IS NOT PERFECT. Nor is the repair perfect,
+although it is easier than detecting scratches reliably. Both are
+cases of coming arbitrarily close to perfection; Paranoia is designed
+with light to moderate damage in mind, but will still recover a
+listenable sample from heavy damage.
+
+Paranoia still has quite a bit of room for improvement in the scratch
+repair code... If cdda2wav + Paranoia is not doing an acceptable job
+on CDs that you just gotta have, drop me a line; I didn't want to sink
+months into a project I wasn't certain anyone would ever use :-)
+
+Monty
+
diff --git a/libparanoia/cdda_paranoia.h b/libparanoia/cdda_paranoia.h
new file mode 100644
index 0000000..24a2411
--- /dev/null
+++ b/libparanoia/cdda_paranoia.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)cdda_paranoia.h 1.20 04/02/20 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)
+ *
+ */
+
+#ifndef _CDROM_PARANOIA_H
+#define _CDROM_PARANOIA_H
+
+#ifndef _MCONFIG_H
+#include <mconfig.h>
+#endif
+#ifndef _UTYPES_H
+#include <utypes.h>
+#endif
+
+#ifndef __GNUC__
+#define inline
+#endif
+
+#define CD_FRAMESIZE_RAW 2352
+#define CD_FRAMEWORDS (CD_FRAMESIZE_RAW/2)
+
+/*
+ * Second parameter of the callback function
+ */
+#define PARANOIA_CB_READ 0 /* Read off adjust ??? */
+#define PARANOIA_CB_VERIFY 1 /* Verifying jitter */
+#define PARANOIA_CB_FIXUP_EDGE 2 /* Fixed edge jitter */
+#define PARANOIA_CB_FIXUP_ATOM 3 /* Fixed atom jitter */
+#define PARANOIA_CB_SCRATCH 4 /* Unsupported */
+#define PARANOIA_CB_REPAIR 5 /* Unsupported */
+#define PARANOIA_CB_SKIP 6 /* Skip exhausted retry */
+#define PARANOIA_CB_DRIFT 7 /* Drift detected */
+#define PARANOIA_CB_BACKOFF 8 /* Unsupported */
+#define PARANOIA_CB_OVERLAP 9 /* Dyn Overlap adjust */
+#define PARANOIA_CB_FIXUP_DROPPED 10 /* Fixed dropped bytes */
+#define PARANOIA_CB_FIXUP_DUPED 11 /* Fixed duplicate bytes */
+#define PARANOIA_CB_READERR 12 /* Hard read error */
+
+/*
+ * Cdparanoia modes to be set with paranoia_modeset()
+ */
+#define PARANOIA_MODE_FULL 0xFF
+#define PARANOIA_MODE_DISABLE 0
+
+#define PARANOIA_MODE_VERIFY 1 /* Verify data integrity in overlap area */
+#define PARANOIA_MODE_FRAGMENT 2 /* unsupported */
+#define PARANOIA_MODE_OVERLAP 4 /* Perform overlapped reads */
+#define PARANOIA_MODE_SCRATCH 8 /* unsupported */
+#define PARANOIA_MODE_REPAIR 16 /* unsupported */
+#define PARANOIA_MODE_NEVERSKIP 32 /* Do not skip failed reads (retry maxretries) */
+
+
+#ifndef CDP_COMPILE
+typedef void cdrom_paranoia;
+#endif
+
+/*
+ * The interface from libcdparanoia to the high level caller
+ */
+extern cdrom_paranoia *paranoia_init(void * d, int nsectors);
+extern void paranoia_dynoverlapset(cdrom_paranoia * p, int minoverlap,
+ int maxoverlap);
+extern void paranoia_modeset(cdrom_paranoia * p, int mode);
+extern long paranoia_seek(cdrom_paranoia * p, long seek, int mode);
+extern Int16_t *paranoia_read(cdrom_paranoia * p, void (*callback) (long, int));
+extern Int16_t *paranoia_read_limited(cdrom_paranoia * p,
+ void (*callback) (long, int),
+ int maxretries);
+extern void paranoia_free(cdrom_paranoia * p);
+extern void paranoia_overlapset(cdrom_paranoia * p, long overlap);
+
+#ifndef HAVE_MEMMOVE
+#define memmove(dst, src, size) movebytes((src), (dst), (size))
+#endif
+
+
+/*
+ * The callback interface from libparanoia to the CD-ROM interface
+ */
+extern long cdda_disc_firstsector(void *d); /* -> long sector */
+extern long cdda_disc_lastsector(void *d); /* -> long sector */
+/* -> long sectors */
+extern long cdda_read(void *d, void *buffer, long beginsector, long sectors);
+extern int cdda_sector_gettrack(void *d, long sector); /* -> int trackno */
+extern int cdda_track_audiop(void *d, int track); /* -> int Is audiotrack */
+extern long cdda_track_firstsector(void *d, int track); /* -> long sector */
+extern long cdda_track_lastsector(void *d, int track); /* -> long sector */
+extern int cdda_tracks(void *d); /* -> int tracks */
+
+#endif /* _CDROM_PARANOIA_H */
diff --git a/libparanoia/gap.c b/libparanoia/gap.c
new file mode 100644
index 0000000..6bb1b92
--- /dev/null
+++ b/libparanoia/gap.c
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)gap.c 1.12 04/02/18 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)
+ *
+ * Gapa analysis support code for paranoia
+ *
+ */
+
+#include <mconfig.h>
+#include <standard.h>
+#include <utypes.h>
+#include <strdefs.h>
+#include "p_block.h"
+#include "cdda_paranoia.h"
+#include "gap.h"
+
+long i_paranoia_overlap_r(Int16_t * buffA, Int16_t * buffB, long offsetA,
+ long offsetB);
+long i_paranoia_overlap_f(Int16_t * buffA, Int16_t * buffB, long offsetA,
+ long offsetB, long sizeA, long sizeB);
+int i_stutter_or_gap(Int16_t * A, Int16_t * B, long offA, long offB, long gap);
+void i_analyze_rift_f(Int16_t * A, Int16_t * B,
+ long sizeA, long sizeB,
+ long aoffset, long boffset,
+ long *matchA, long *matchB, long *matchC);
+void i_analyze_rift_r(Int16_t * A, Int16_t * B,
+ long sizeA, long sizeB,
+ long aoffset, long boffset,
+ long *matchA, long *matchB, long *matchC);
+void analyze_rift_silence_f(Int16_t * A, Int16_t * B,
+ long sizeA, long sizeB,
+ long aoffset, long boffset,
+ long *matchA, long *matchB);
+
+/*
+ * Gap analysis code
+ */
+long i_paranoia_overlap_r(Int16_t *buffA, Int16_t *buffB, long offsetA,
+ long offsetB)
+{
+ long beginA = offsetA;
+ long beginB = offsetB;
+
+ for (; beginA >= 0 && beginB >= 0; beginA--, beginB--)
+ if (buffA[beginA] != buffB[beginB])
+ break;
+ beginA++;
+ beginB++;
+
+ return (offsetA - beginA);
+}
+
+long i_paranoia_overlap_f(Int16_t *buffA, Int16_t *buffB, long offsetA,
+ long offsetB, long sizeA, long sizeB)
+{
+ long endA = offsetA;
+ long endB = offsetB;
+
+ for (; endA < sizeA && endB < sizeB; endA++, endB++)
+ if (buffA[endA] != buffB[endB])
+ break;
+
+ return (endA - offsetA);
+}
+
+int i_stutter_or_gap(Int16_t *A, Int16_t *B, long offA, long offB, long gap)
+{
+ long a1 = offA;
+ long b1 = offB;
+
+ if (a1 < 0) {
+ b1 -= a1;
+ gap += a1;
+ a1 = 0;
+ }
+ return (memcmp(A + a1, B + b1, gap * 2));
+}
+
+/*
+ * riftv is the first value into the rift -> or <-
+ */
+void i_analyze_rift_f(Int16_t *A, Int16_t *B, long sizeA, long sizeB,
+ long aoffset, long boffset, long *matchA, long *matchB,
+ long *matchC)
+{
+
+ long apast = sizeA - aoffset;
+ long bpast = sizeB - boffset;
+ long i;
+
+ *matchA = 0, *matchB = 0, *matchC = 0;
+
+ /*
+ * Look for three possible matches... (A) Ariftv->B,
+ * (B) Briftv->A and (c) AB->AB.
+ */
+ for (i = 0; ; i++) {
+ if (i < bpast) /* A */
+ if (i_paranoia_overlap_f(A, B, aoffset, boffset + i, sizeA, sizeB) >= MIN_WORDS_RIFT) {
+ *matchA = i;
+ break;
+ }
+ if (i < apast) { /* B */
+ if (i_paranoia_overlap_f(A, B, aoffset + i, boffset, sizeA, sizeB) >= MIN_WORDS_RIFT) {
+ *matchB = i;
+ break;
+ }
+ if (i < bpast) /* C */
+ if (i_paranoia_overlap_f(A, B, aoffset + i, boffset + i, sizeA, sizeB) >= MIN_WORDS_RIFT) {
+ *matchC = i;
+ break;
+ }
+ } else if (i >= bpast)
+ break;
+
+ }
+
+ if (*matchA == 0 && *matchB == 0 && *matchC == 0)
+ return;
+
+ if (*matchC)
+ return;
+ if (*matchA) {
+ if (i_stutter_or_gap(A, B, aoffset - *matchA, boffset, *matchA))
+ return;
+ *matchB = -*matchA; /* signify we need to remove n bytes */
+ /* from B */
+ *matchA = 0;
+ return;
+ } else {
+ if (i_stutter_or_gap(B, A, boffset - *matchB, aoffset, *matchB))
+ return;
+ *matchA = -*matchB;
+ *matchB = 0;
+ return;
+ }
+}
+
+/*
+ * riftv must be first even val of rift moving back
+ */
+void i_analyze_rift_r(Int16_t *A, Int16_t *B, long sizeA, long sizeB,
+ long aoffset, long boffset, long *matchA, long *matchB,
+ long *matchC)
+{
+
+ long apast = aoffset + 1;
+ long bpast = boffset + 1;
+ long i;
+
+ *matchA = 0, *matchB = 0, *matchC = 0;
+
+ /*
+ * Look for three possible matches... (A) Ariftv->B, (B) Briftv->A and
+ * (c) AB->AB.
+ */
+ for (i = 0; ; i++) {
+ if (i < bpast) /* A */
+ if (i_paranoia_overlap_r(A, B, aoffset, boffset - i) >= MIN_WORDS_RIFT) {
+ *matchA = i;
+ break;
+ }
+ if (i < apast) { /* B */
+ if (i_paranoia_overlap_r(A, B, aoffset - i, boffset) >= MIN_WORDS_RIFT) {
+ *matchB = i;
+ break;
+ }
+ if (i < bpast) /* C */
+ if (i_paranoia_overlap_r(A, B, aoffset - i, boffset - i) >= MIN_WORDS_RIFT) {
+ *matchC = i;
+ break;
+ }
+ } else if (i >= bpast)
+ break;
+
+ }
+
+ if (*matchA == 0 && *matchB == 0 && *matchC == 0)
+ return;
+
+ if (*matchC)
+ return;
+
+ if (*matchA) {
+ if (i_stutter_or_gap(A, B, aoffset + 1, boffset - *matchA + 1, *matchA))
+ return;
+ *matchB = -*matchA; /* signify we need to remove n bytes */
+ /* from B */
+ *matchA = 0;
+ return;
+ } else {
+ if (i_stutter_or_gap(B, A, boffset + 1, aoffset - *matchB + 1, *matchB))
+ return;
+ *matchA = -*matchB;
+ *matchB = 0;
+ return;
+ }
+}
+
+void analyze_rift_silence_f(Int16_t *A, Int16_t *B, long sizeA, long sizeB,
+ long aoffset, long boffset, long *matchA,
+ long *matchB)
+{
+ *matchA = -1;
+ *matchB = -1;
+
+ sizeA = min(sizeA, aoffset + MIN_WORDS_RIFT);
+ sizeB = min(sizeB, boffset + MIN_WORDS_RIFT);
+
+ aoffset++;
+ boffset++;
+
+ while (aoffset < sizeA) {
+ if (A[aoffset] != A[aoffset - 1]) {
+ *matchA = 0;
+ break;
+ }
+ aoffset++;
+ }
+
+ while (boffset < sizeB) {
+ if (B[boffset] != B[boffset - 1]) {
+ *matchB = 0;
+ break;
+ }
+ boffset++;
+ }
+}
diff --git a/libparanoia/gap.h b/libparanoia/gap.h
new file mode 100644
index 0000000..f772ae3
--- /dev/null
+++ b/libparanoia/gap.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)gap.h 1.10 04/02/18 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)
+ */
+
+#ifndef _GAP_H_
+#define _GAP_H_
+
+extern long i_paranoia_overlap_r(Int16_t * buffA, Int16_t * buffB,
+ long offsetA, long offsetB);
+extern long i_paranoia_overlap_f(Int16_t * buffA, Int16_t * buffB,
+ long offsetA, long offsetB,
+ long sizeA, long sizeB);
+extern int i_stutter_or_gap(Int16_t * A, Int16_t * B,
+ long offA, long offB,
+ long gap);
+extern void i_analyze_rift_f(Int16_t * A, Int16_t * B,
+ long sizeA, long sizeB,
+ long aoffset, long boffset,
+ long *matchA, long *matchB, long *matchC);
+extern void i_analyze_rift_r(Int16_t * A, Int16_t * B,
+ long sizeA, long sizeB,
+ long aoffset, long boffset,
+ long *matchA, long *matchB, long *matchC);
+extern void analyze_rift_silence_f(Int16_t * A, Int16_t * B,
+ long sizeA, long sizeB,
+ long aoffset, long boffset,
+ long *matchA, long *matchB);
+
+#endif
diff --git a/libparanoia/isort.c b/libparanoia/isort.c
new file mode 100644
index 0000000..a3d38ad
--- /dev/null
+++ b/libparanoia/isort.c
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)isort.c 1.14 04/02/20 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)
+ *
+ * sorted vector abstraction for paranoia
+ *
+ */
+
+/*
+ * Old isort got a bit complex. This re-constrains complexity to
+ * give a go at speed through a more alpha-6-like mechanism.
+ */
+
+#include <mconfig.h>
+#include <stdxlib.h>
+#include <standard.h>
+#include <utypes.h>
+#include <strdefs.h>
+#include "p_block.h"
+#include "isort.h"
+#include "pmalloc.h"
+
+sort_info *sort_alloc(long size);
+void sort_unsortall(sort_info * i);
+void sort_free(sort_info * i);
+void sort_sort(sort_info * i, long sortlo, long sorthi);
+void sort_setup(sort_info * i, Int16_t * vector, long *abspos, long size,
+ long sortlo, long sorthi);
+sort_link *sort_getmatch(sort_info * i, long post, long overlap, int value);
+sort_link *sort_nextmatch(sort_info * i, sort_link * prev);
+
+
+sort_info *sort_alloc(long size)
+{
+ sort_info *ret = _pcalloc(1, sizeof (sort_info));
+
+ ret->vector = NULL;
+ ret->sortbegin = -1;
+ ret->size = -1;
+ ret->maxsize = size;
+
+ ret->head = _pcalloc(65536, sizeof (sort_link *));
+ ret->bucketusage = _pmalloc(65536 * sizeof (long));
+ ret->revindex = _pcalloc(size, sizeof (sort_link));
+ ret->lastbucket = 0;
+
+ return (ret);
+}
+
+void sort_unsortall(sort_info *i)
+{
+ if (i->lastbucket > 2000) { /* a guess */
+ memset(i->head, 0, 65536 * sizeof (sort_link *));
+ } else {
+ long b;
+
+ for (b = 0; b < i->lastbucket; b++)
+ i->head[i->bucketusage[b]] = NULL;
+ }
+
+ i->lastbucket = 0;
+ i->sortbegin = -1;
+}
+
+void sort_free(sort_info *i)
+{
+ _pfree(i->revindex);
+ _pfree(i->head);
+ _pfree(i->bucketusage);
+ _pfree(i);
+}
+
+void sort_sort(sort_info *i, long sortlo, long sorthi)
+{
+ long j;
+
+ for (j = sorthi - 1; j >= sortlo; j--) {
+ sort_link **hv = i->head + i->vector[j] + 32768;
+ sort_link *l = i->revindex + j;
+
+ if (*hv == NULL) {
+ i->bucketusage[i->lastbucket] = i->vector[j] + 32768;
+ i->lastbucket++;
+ }
+ l->next = *hv;
+ *hv = l;
+ }
+ i->sortbegin = 0;
+}
+
+/*
+ * size *must* be less than i->maxsize
+ */
+void sort_setup(sort_info *i, Int16_t *vector, long *abspos, long size,
+ long sortlo, long sorthi)
+{
+ if (i->sortbegin != -1)
+ sort_unsortall(i);
+
+ i->vector = vector;
+ i->size = size;
+ i->abspos = abspos;
+
+ i->lo = min(size, max(sortlo - *abspos, 0));
+ i->hi = max(0, min(sorthi - *abspos, size));
+}
+
+sort_link *sort_getmatch(sort_info *i, long post, long overlap, int value)
+{
+ sort_link *ret;
+
+ if (i->sortbegin == -1)
+ sort_sort(i, i->lo, i->hi);
+ /*
+ * Now we reuse lo and hi
+ */
+ post = max(0, min(i->size, post));
+ i->val = value + 32768;
+ i->lo = max(0, post - overlap); /* absolute position */
+ i->hi = min(i->size, post + overlap); /* absolute position */
+
+ ret = i->head[i->val];
+ while (ret) {
+ if (ipos(i, ret) < i->lo) {
+ ret = ret->next;
+ } else {
+ if (ipos(i, ret) >= i->hi)
+ ret = NULL;
+ break;
+ }
+ }
+/* i->head[i->val]=ret; */
+ return (ret);
+}
+
+sort_link *sort_nextmatch(sort_info *i, sort_link *prev)
+{
+ sort_link *ret = prev->next;
+
+ if (!ret || ipos(i, ret) >= i->hi)
+ return (NULL);
+ return (ret);
+}
diff --git a/libparanoia/isort.h b/libparanoia/isort.h
new file mode 100644
index 0000000..1b6661c
--- /dev/null
+++ b/libparanoia/isort.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)isort.h 1.10 04/02/18 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)
+ */
+
+#ifndef _ISORT_H_
+#define _ISORT_H_
+
+typedef struct sort_link {
+ struct sort_link *next;
+} sort_link;
+
+typedef struct sort_info {
+ Int16_t *vector; /* vector */
+ /* vec storage doesn't belong to us */
+
+ long *abspos; /* pointer for side effects */
+ long size; /* vector size */
+
+ long maxsize; /* maximum vector size */
+
+ long sortbegin; /* range of contiguous sorted area */
+ long lo;
+ long hi; /* current post, overlap range */
+ int val; /* ...and val */
+
+ /*
+ * sort structs
+ */
+ sort_link **head; /* sort buckets (65536) */
+
+ long *bucketusage; /* of used buckets (65536) */
+ long lastbucket;
+ sort_link *revindex;
+
+} sort_info;
+
+extern sort_info *sort_alloc(long size);
+extern void sort_unsortall(sort_info * i);
+extern void sort_setup(sort_info * i, Int16_t * vector, long *abspos,
+ long size, long sortlo, long sorthi);
+extern void sort_free(sort_info * i);
+extern sort_link *sort_getmatch(sort_info * i, long post, long overlap,
+ int value);
+extern sort_link *sort_nextmatch(sort_info * i, sort_link * prev);
+
+#define is(i) ((i)->size)
+#define ib(i) (*(i)->abspos)
+#define ie(i) ((i)->size + *(i)->abspos)
+#define iv(i) ((i)->vector)
+#define ipos(i, l) ((l) - (i)->revindex)
+
+#endif
diff --git a/libparanoia/overlap.c b/libparanoia/overlap.c
new file mode 100644
index 0000000..6f15baf
--- /dev/null
+++ b/libparanoia/overlap.c
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)overlap.c 1.11 04/02/20 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)
+ *
+ * Statistic code and cache management for overlap settings
+ *
+ */
+
+#include <mconfig.h>
+#include <stdxlib.h>
+#include <standard.h>
+#include <utypes.h>
+#include "p_block.h"
+#include "cdda_paranoia.h"
+#include "overlap.h"
+#include "isort.h"
+
+void paranoia_resetcache(cdrom_paranoia *p);
+void paranoia_resetall(cdrom_paranoia *p);
+void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword);
+void offset_adjust_settings(cdrom_paranoia *p, void (*callback)(long, int));
+void offset_add_value(cdrom_paranoia *p, offsets *o, long value,
+ void (*callback)(long, int));
+
+/*
+ * Internal cache management
+ */
+void paranoia_resetcache(cdrom_paranoia *p)
+{
+ c_block *c = c_first(p);
+ v_fragment *v;
+
+ while (c) {
+ free_c_block(c);
+ c = c_first(p);
+ }
+
+ v = v_first(p);
+ while (v) {
+ free_v_fragment(v);
+ v = v_first(p);
+ }
+}
+
+void paranoia_resetall(cdrom_paranoia *p)
+{
+ p->root.returnedlimit = 0;
+ p->dyndrift = 0;
+ p->root.lastsector = 0;
+
+ if (p->root.vector) {
+ i_cblock_destructor(p->root.vector);
+ p->root.vector = NULL;
+ }
+ paranoia_resetcache(p);
+}
+
+void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword)
+{
+ root_block *root = &(p->root);
+
+ if (root->vector != NULL) {
+ long target = beginword - p->maxdynoverlap;
+ long rbegin = cb(root->vector);
+ long rend = ce(root->vector);
+
+ if (rbegin > beginword)
+ goto rootfree;
+
+ if (rbegin + p->maxdynoverlap < beginword) {
+ if (target + MIN_WORDS_OVERLAP > rend)
+ goto rootfree;
+
+ {
+ long offset = target - rbegin;
+
+ c_removef(root->vector, offset);
+ }
+ } {
+ c_block *c = c_first(p);
+
+ while (c) {
+ c_block *next = c_next(c);
+
+ if (ce(c) < beginword - p->maxdynoverlap)
+ free_c_block(c);
+ c = next;
+ }
+ }
+
+ }
+ return;
+
+rootfree:
+
+ i_cblock_destructor(root->vector);
+ root->vector = NULL;
+ root->returnedlimit = -1;
+ root->lastsector = 0;
+
+}
+
+/*
+ * Statistical and heuristic[al? :-] management
+ */
+void offset_adjust_settings(cdrom_paranoia *p, void (*callback)(long, int))
+{
+ if (p->stage2.offpoints >= 10) {
+ /*
+ * drift: look at the average offset value. If it's over one
+ * sector, frob it. We just want a little hysteresis [sp?]
+ */
+ long av = (p->stage2.offpoints ? p->stage2.offaccum / p->stage2.offpoints : 0);
+
+ if (abs(av) > p->dynoverlap / 4) {
+ av = (av / MIN_SECTOR_EPSILON) * MIN_SECTOR_EPSILON;
+
+ if (callback)
+ (*callback) (ce(p->root.vector), PARANOIA_CB_DRIFT);
+ p->dyndrift += av;
+
+ /*
+ * Adjust all the values in the cache otherwise we get
+ * a (potentially unstable) feedback loop
+ */
+ {
+ c_block *c = c_first(p);
+ v_fragment *v = v_first(p);
+
+ while (v && v->one) {
+ /*
+ * safeguard beginning bounds case with
+ * a hammer
+ */
+ if (fb(v) < av || cb(v->one) < av) {
+ v->one = NULL;
+ } else {
+ fb(v) -= av;
+ }
+ v = v_next(v);
+ }
+ while (c) {
+ long adj = min(av, cb(c));
+
+ c_set(c, cb(c) - adj);
+ c = c_next(c);
+ }
+ }
+
+ p->stage2.offaccum = 0;
+ p->stage2.offmin = 0;
+ p->stage2.offmax = 0;
+ p->stage2.offpoints = 0;
+ p->stage2.newpoints = 0;
+ p->stage2.offdiff = 0;
+ }
+ }
+ if (p->stage1.offpoints >= 10) {
+ /*
+ * dynoverlap: we arbitrarily set it to 4x the running
+ * difference value, unless min/max are more
+ */
+ p->dynoverlap = (p->stage1.offpoints ? p->stage1.offdiff /
+ p->stage1.offpoints * 3 : CD_FRAMEWORDS);
+
+ if (p->dynoverlap < -p->stage1.offmin * 1.5)
+ p->dynoverlap = -p->stage1.offmin * 1.5;
+
+ if (p->dynoverlap < p->stage1.offmax * 1.5)
+ p->dynoverlap = p->stage1.offmax * 1.5;
+
+ if (p->dynoverlap < p->mindynoverlap)
+ p->dynoverlap = p->mindynoverlap;
+ if (p->dynoverlap > p->maxdynoverlap)
+ p->dynoverlap = p->maxdynoverlap;
+
+ if (callback)
+ (*callback) (p->dynoverlap, PARANOIA_CB_OVERLAP);
+
+ if (p->stage1.offpoints > 600) {
+ /*
+ * bit of a bug; this routine is called too often
+ * due to the overlap mesh alg we use in stage 1
+ */
+ p->stage1.offpoints /= 1.2;
+ p->stage1.offaccum /= 1.2;
+ p->stage1.offdiff /= 1.2;
+ }
+ p->stage1.offmin = 0;
+ p->stage1.offmax = 0;
+ p->stage1.newpoints = 0;
+ }
+}
+
+void offset_add_value(cdrom_paranoia *p, offsets *o, long value,
+ void (*callback)(long, int))
+{
+ if (o->offpoints != -1) {
+
+ o->offdiff += abs(value);
+ o->offpoints++;
+ o->newpoints++;
+ o->offaccum += value;
+ if (value < o->offmin)
+ o->offmin = value;
+ if (value > o->offmax)
+ o->offmax = value;
+
+ if (o->newpoints >= 10)
+ offset_adjust_settings(p, callback);
+ }
+}
diff --git a/libparanoia/overlap.h b/libparanoia/overlap.h
new file mode 100644
index 0000000..44db6e2
--- /dev/null
+++ b/libparanoia/overlap.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)overlap.h 1.7 04/02/18 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)
+ */
+
+#ifndef _OVERLAP_H_
+#define _OVERLAP_H_
+
+extern void paranoia_resetcache(cdrom_paranoia *p);
+extern void paranoia_resetall(cdrom_paranoia *p);
+extern void i_paranoia_trim(cdrom_paranoia *p, long beginword, long endword);
+extern void offset_adjust_settings(cdrom_paranoia *p,
+ void (*callback) (long, int));
+extern void offset_add_value(cdrom_paranoia *p, offsets *o, long value,
+ void (*callback) (long, int));
+
+#endif
diff --git a/libparanoia/overlapdef.txt b/libparanoia/overlapdef.txt
new file mode 100644
index 0000000..3aff6cf
--- /dev/null
+++ b/libparanoia/overlapdef.txt
@@ -0,0 +1,20 @@
+# @(#)overlapdef.txt 1.1 98/08/19 J. Schilling from Monty (xiphmont@mit.edu)
+
+ 0 70 100
+A |----------|-----|
+B |-----|---------|
+ 0 40 100
+
+offset=-30
+begin=30
+end=100
+
+ 0 70 100
+A |----------|-----|
+B |-----|---------|
+ 50 90 150
+
+offset=20
+begin=30
+end=100
+
diff --git a/libparanoia/p_block.c b/libparanoia/p_block.c
new file mode 100644
index 0000000..e37334b
--- /dev/null
+++ b/libparanoia/p_block.c
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)p_block.c 1.19 04/02/23 J. Schilling from cdparanoia-III-alpha9.8 */
+/*
+ * Modifications to make the code portable Copyright (c) 2002 J. Schilling
+ */
+#include <mconfig.h>
+#include <stdxlib.h>
+#include <standard.h>
+#include <utypes.h>
+#include <strdefs.h>
+#include "p_block.h"
+#include "cdda_paranoia.h"
+#include "pmalloc.h"
+
+linked_list *new_list(void *(*newp) (void), void (*freep) (void *));
+linked_element *add_elem(linked_list *l, void *elem);
+linked_element *new_elem(linked_list *list);
+void free_elem(linked_element *e, int free_ptr);
+void free_list(linked_list *list, int free_ptr);
+void *get_elem(linked_element *e);
+linked_list *copy_list(linked_list *list);
+static c_block *i_cblock_constructor(void);
+void i_cblock_destructor(c_block *c);
+c_block *new_c_block(cdrom_paranoia *p);
+void free_c_block(c_block *c);
+static v_fragment *i_vfragment_constructor(void);
+static void i_v_fragment_destructor(v_fragment *v);
+v_fragment *new_v_fragment(cdrom_paranoia *p, c_block *one, long begin,
+ long end, int last);
+void free_v_fragment(v_fragment *v);
+c_block *c_first(cdrom_paranoia *p);
+c_block *c_last(cdrom_paranoia *p);
+c_block *c_next(c_block *c);
+c_block *c_prev(c_block *c);
+v_fragment *v_first(cdrom_paranoia *p);
+v_fragment *v_last(cdrom_paranoia *p);
+v_fragment *v_next(v_fragment *v);
+v_fragment *v_prev(v_fragment *v);
+void recover_cache(cdrom_paranoia *p);
+Int16_t *v_buffer(v_fragment *v);
+c_block *c_alloc(Int16_t *vector, long begin, long size);
+void c_set(c_block *v, long begin);
+void c_remove(c_block *v, long cutpos, long cutsize);
+void c_overwrite(c_block *v, long pos, Int16_t *b, long size);
+void c_append(c_block *v, Int16_t *vector, long size);
+void c_removef(c_block *v, long cut);
+void i_paranoia_firstlast(cdrom_paranoia *p);
+cdrom_paranoia *paranoia_init(void *d, int nsectors);
+
+
+linked_list *new_list(void *(*newp)(void), void (*freep)(void *))
+{
+ linked_list *ret = _pcalloc(1, sizeof (linked_list));
+
+ ret->new_poly = newp;
+ ret->free_poly = freep;
+ return (ret);
+}
+
+linked_element *add_elem(linked_list *l, void *elem)
+{
+
+ linked_element *ret = _pcalloc(1, sizeof (linked_element));
+
+ ret->stamp = l->current++;
+ ret->ptr = elem;
+ ret->list = l;
+
+ if (l->head)
+ l->head->prev = ret;
+ else
+ l->tail = ret;
+ ret->next = l->head;
+ ret->prev = NULL;
+ l->head = ret;
+ l->active++;
+
+ return (ret);
+}
+
+linked_element *new_elem(linked_list *list)
+{
+ void *new = list->new_poly();
+
+ return (add_elem(list, new));
+}
+
+void free_elem(linked_element *e, int free_ptr)
+{
+ linked_list *l = e->list;
+
+ if (free_ptr)
+ l->free_poly(e->ptr);
+
+ if (e == l->head)
+ l->head = e->next;
+ if (e == l->tail)
+ l->tail = e->prev;
+
+ if (e->prev)
+ e->prev->next = e->next;
+ if (e->next)
+ e->next->prev = e->prev;
+
+ l->active--;
+ _pfree(e);
+}
+
+void free_list(linked_list *list, int free_ptr)
+{
+ while (list->head)
+ free_elem(list->head, free_ptr);
+ _pfree(list);
+}
+
+void *get_elem(linked_element *e)
+{
+ return (e->ptr);
+}
+
+linked_list *copy_list(linked_list *list)
+{
+ linked_list *new = new_list(list->new_poly, list->free_poly);
+ linked_element *i = list->tail;
+
+ while (i) {
+ add_elem(new, i->ptr);
+ i = i->prev;
+ }
+ return (new);
+}
+
+/**** C_block stuff ******************************************************/
+
+#define vp_cblock_constructor_func ((void*(*)(void))i_cblock_constructor)
+static c_block *i_cblock_constructor()
+{
+ c_block *ret = _pcalloc(1, sizeof (c_block));
+
+ return (ret);
+}
+
+#define vp_cblock_destructor_func ((void(*)(void*))i_cblock_destructor)
+void i_cblock_destructor(c_block *c)
+{
+ if (c) {
+ if (c->vector)
+ _pfree(c->vector);
+ if (c->flags)
+ _pfree(c->flags);
+ c->e = NULL;
+ _pfree(c);
+ }
+}
+
+c_block *new_c_block(cdrom_paranoia *p)
+{
+ linked_element *e = new_elem(p->cache);
+ c_block *c = e->ptr;
+
+ c->e = e;
+ c->p = p;
+ return (c);
+}
+
+void free_c_block(c_block *c)
+{
+ /*
+ * also rid ourselves of v_fragments that reference this block
+ */
+ v_fragment *v = v_first(c->p);
+
+ while (v) {
+ v_fragment *next = v_next(v);
+
+ if (v->one == c)
+ free_v_fragment(v);
+ v = next;
+ }
+
+ free_elem(c->e, 1);
+}
+
+#define vp_vfragment_constructor_func ((void*(*)(void))i_vfragment_constructor)
+static v_fragment *i_vfragment_constructor()
+{
+ v_fragment *ret = _pcalloc(1, sizeof (v_fragment));
+
+ return (ret);
+}
+
+#define vp_v_fragment_destructor_func ((void(*)(void*))i_v_fragment_destructor)
+static void i_v_fragment_destructor(v_fragment *v)
+{
+ _pfree(v);
+}
+
+v_fragment *new_v_fragment(cdrom_paranoia *p, c_block *one, long begin,
+ long end, int last)
+{
+ linked_element *e = new_elem(p->fragments);
+ v_fragment *b = e->ptr;
+
+ b->e = e;
+ b->p = p;
+
+ b->one = one;
+ b->begin = begin;
+ b->vector = one->vector + begin - one->begin;
+ b->size = end - begin;
+ b->lastsector = last;
+
+ return (b);
+}
+
+void free_v_fragment(v_fragment *v)
+{
+ free_elem(v->e, 1);
+}
+
+c_block *c_first(cdrom_paranoia *p)
+{
+ if (p->cache->head)
+ return (p->cache->head->ptr);
+ return (NULL);
+}
+
+c_block* c_last(cdrom_paranoia *p)
+{
+ if (p->cache->tail)
+ return (p->cache->tail->ptr);
+ return (NULL);
+}
+
+c_block *c_next(c_block *c)
+{
+ if (c->e->next)
+ return (c->e->next->ptr);
+ return (NULL);
+}
+
+c_block *c_prev(c_block *c)
+{
+ if (c->e->prev)
+ return (c->e->prev->ptr);
+ return (NULL);
+}
+
+v_fragment *v_first(cdrom_paranoia *p)
+{
+ if (p->fragments->head) {
+ return (p->fragments->head->ptr);
+ }
+ return (NULL);
+}
+
+v_fragment *v_last(cdrom_paranoia *p)
+{
+ if (p->fragments->tail)
+ return (p->fragments->tail->ptr);
+ return (NULL);
+}
+
+v_fragment *v_next(v_fragment *v)
+{
+ if (v->e->next)
+ return (v->e->next->ptr);
+ return (NULL);
+}
+
+v_fragment *v_prev(v_fragment *v)
+{
+ if (v->e->prev)
+ return (v->e->prev->ptr);
+ return (NULL);
+}
+
+void recover_cache(cdrom_paranoia *p)
+{
+ linked_list *l = p->cache;
+
+ /*
+ * Are we at/over our allowed cache size?
+ */
+ while (l->active > p->cache_limit) {
+ /*
+ * cull from the tail of the list
+ */
+ free_c_block(c_last(p));
+ }
+
+}
+
+Int16_t *v_buffer(v_fragment *v)
+{
+ if (!v->one)
+ return (NULL);
+ if (!cv(v->one))
+ return (NULL);
+ return (v->vector);
+}
+
+/*
+ * alloc a c_block not on a cache list
+ */
+c_block *c_alloc(Int16_t *vector, long begin, long size)
+{
+ c_block *c = _pcalloc(1, sizeof (c_block));
+
+ c->vector = vector;
+ c->begin = begin;
+ c->size = size;
+ return (c);
+}
+
+void c_set(c_block *v, long begin)
+{
+ v->begin = begin;
+}
+
+/*
+ * pos here is vector position from zero
+ */
+void c_insert(c_block *v, long pos, Int16_t *b, long size)
+{
+ int vs = cs(v);
+
+ if (pos < 0 || pos > vs)
+ return;
+
+ if (v->vector)
+ v->vector = _prealloc(v->vector, sizeof (Int16_t) * (size + vs));
+ else
+ v->vector = _pmalloc(sizeof (Int16_t) * size);
+
+ if (pos < vs)
+ memmove(v->vector + pos + size, v->vector + pos,
+ (vs - pos) * sizeof (Int16_t));
+ memcpy(v->vector + pos, b, size * sizeof (Int16_t));
+
+ v->size += size;
+}
+
+void c_remove(c_block *v, long cutpos, long cutsize)
+{
+ int vs = cs(v);
+
+ if (cutpos < 0 || cutpos > vs)
+ return;
+ if (cutpos + cutsize > vs)
+ cutsize = vs - cutpos;
+ if (cutsize < 0)
+ cutsize = vs - cutpos;
+ if (cutsize < 1)
+ return;
+
+ memmove(v->vector + cutpos, v->vector + cutpos + cutsize,
+ (vs - cutpos - cutsize) * sizeof (Int16_t));
+
+ v->size -= cutsize;
+}
+
+void c_overwrite(c_block *v, long pos, Int16_t *b, long size)
+{
+ int vs = cs(v);
+
+ if (pos < 0)
+ return;
+ if (pos + size > vs)
+ size = vs - pos;
+
+ memcpy(v->vector + pos, b, size * sizeof (Int16_t));
+}
+
+void c_append(c_block *v, Int16_t *vector, long size)
+{
+ int vs = cs(v);
+
+ /*
+ * update the vector
+ */
+ if (v->vector)
+ v->vector = _prealloc(v->vector, sizeof (Int16_t) * (size + vs));
+ else
+ v->vector = _pmalloc(sizeof (Int16_t) * size);
+ memcpy(v->vector + vs, vector, sizeof (Int16_t) * size);
+
+ v->size += size;
+}
+
+void c_removef(c_block *v, long cut)
+{
+ c_remove(v, 0, cut);
+ v->begin += cut;
+}
+
+
+
+/*
+ * Initialization
+ */
+void i_paranoia_firstlast(cdrom_paranoia *p)
+{
+ int i;
+ void *d = p->d;
+
+ p->current_lastsector = -1;
+ for (i = cdda_sector_gettrack(d, p->cursor); i < cdda_tracks(d); i++)
+ if (!cdda_track_audiop(d, i))
+ p->current_lastsector = cdda_track_lastsector(d, i - 1);
+ if (p->current_lastsector == -1)
+ p->current_lastsector = cdda_disc_lastsector(d);
+
+ p->current_firstsector = -1;
+ for (i = cdda_sector_gettrack(d, p->cursor); i > 0; i--)
+ if (!cdda_track_audiop(d, i))
+ p->current_firstsector = cdda_track_firstsector(d, i + 1);
+ if (p->current_firstsector == -1)
+ p->current_firstsector = cdda_disc_firstsector(d);
+
+}
+
+cdrom_paranoia *paranoia_init(void *d, int nsectors)
+{
+ cdrom_paranoia *p = _pcalloc(1, sizeof (cdrom_paranoia));
+
+ p->cache = new_list(vp_cblock_constructor_func,
+ vp_cblock_destructor_func);
+
+ p->fragments = new_list(vp_vfragment_constructor_func,
+ vp_v_fragment_destructor_func);
+
+ p->nsectors = nsectors;
+ p->readahead = 150;
+ p->sortcache = sort_alloc(p->readahead * CD_FRAMEWORDS);
+ p->d = d;
+ p->mindynoverlap = MIN_SECTOR_EPSILON;
+ p->maxdynoverlap = MAX_SECTOR_OVERLAP * CD_FRAMEWORDS;
+ p->maxdynoverlap = (nsectors - 1) * CD_FRAMEWORDS;
+ p->dynoverlap = MAX_SECTOR_OVERLAP * CD_FRAMEWORDS;
+ p->cache_limit = JIGGLE_MODULO;
+ p->enable = PARANOIA_MODE_FULL;
+ p->cursor = cdda_disc_firstsector(d);
+ p->lastread = -1000000;
+
+ /*
+ * One last one... in case data and audio tracks are mixed...
+ */
+ i_paranoia_firstlast(p);
+
+ return (p);
+}
+
+void paranoia_dynoverlapset(cdrom_paranoia *p, int minoverlap, int maxoverlap)
+{
+ if (minoverlap >= 0)
+ p->mindynoverlap = minoverlap;
+ if (maxoverlap > minoverlap)
+ p->maxdynoverlap = maxoverlap;
+
+ if (p->maxdynoverlap < p->mindynoverlap)
+ p->maxdynoverlap = p->mindynoverlap;
+}
diff --git a/libparanoia/p_block.h b/libparanoia/p_block.h
new file mode 100644
index 0000000..418c193
--- /dev/null
+++ b/libparanoia/p_block.h
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)p_block.h 1.16 04/02/20 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)
+ */
+
+#ifndef _p_block_h_
+#define _p_block_h_
+
+#define MIN_WORDS_OVERLAP 64 /* 16 bit words */
+#define MIN_WORDS_SEARCH 64 /* 16 bit words */
+#define MIN_WORDS_RIFT 16 /* 16 bit words */
+#define MAX_SECTOR_OVERLAP 32 /* sectors */
+#define MIN_SECTOR_EPSILON 128 /* words */
+#define MIN_SECTOR_BACKUP 16 /* sectors */
+#define JIGGLE_MODULO 15 /* sectors */
+#define MIN_SILENCE_BOUNDARY 1024 /* 16 bit words */
+
+#define min(x, y) ((x) > (y)?(y):(x))
+#define max(x, y) ((x) < (y)?(y):(x))
+
+#include "isort.h"
+
+typedef struct linked_list {
+ /* linked list */
+ struct linked_element *head;
+ struct linked_element *tail;
+
+ void *(*new_poly)(void);
+ void (*free_poly)(void *poly);
+ long current;
+ long active;
+
+} linked_list;
+
+typedef struct linked_element {
+ void *ptr;
+ struct linked_element *prev;
+ struct linked_element *next;
+
+ struct linked_list *list;
+ int stamp;
+} linked_element;
+
+extern linked_list *new_list(void *(*newp) (void),
+ void (*freep) (void *));
+extern linked_element *new_elem(linked_list *list);
+extern linked_element *add_elem(linked_list *list, void *elem);
+extern void free_list(linked_list *list, int free_ptr); /* unlink or free */
+extern void free_elem(linked_element *e, int free_ptr); /* unlink or free */
+extern void *get_elem(linked_element *e);
+extern linked_list *copy_list(linked_list *list); /* shallow; doesn't copy */
+ /* contained structures */
+
+typedef struct c_block {
+ /* The buffer */
+ Int16_t *vector;
+ long begin;
+ long size;
+
+ /* auxiliary support structures */
+ unsigned char *flags;
+ /*
+ * 1 known boundaries in read data
+ * 2 known blanked data
+ * 4 matched sample
+ * 8 reserved
+ * 16 reserved
+ * 32 reserved
+ * 64 reserved
+ * 128 reserved
+ */
+
+ /* end of session cases */
+ long lastsector;
+ struct cdrom_paranoia *p;
+ struct linked_element *e;
+} c_block;
+
+extern void free_c_block(c_block *c);
+extern void i_cblock_destructor(c_block *c);
+extern c_block *new_c_block(struct cdrom_paranoia *p);
+
+typedef struct v_fragment {
+ c_block *one;
+
+ long begin;
+ long size;
+ Int16_t *vector;
+
+ /* end of session cases */
+ long lastsector;
+
+ /* linked list */
+ struct cdrom_paranoia *p;
+ struct linked_element *e;
+
+} v_fragment;
+
+extern void free_v_fragment(v_fragment *c);
+extern v_fragment *new_v_fragment(struct cdrom_paranoia *p, c_block *one,
+ long begin, long end,
+ int lastsector);
+extern Int16_t *v_buffer(v_fragment *v);
+
+extern c_block *c_first(struct cdrom_paranoia *p);
+extern c_block *c_last(struct cdrom_paranoia *p);
+extern c_block *c_next(c_block *c);
+extern c_block *c_prev(c_block *c);
+
+extern v_fragment *v_first(struct cdrom_paranoia *p);
+extern v_fragment *v_last(struct cdrom_paranoia *p);
+extern v_fragment *v_next(v_fragment *v);
+extern v_fragment *v_prev(v_fragment *v);
+
+typedef struct root_block {
+ long returnedlimit;
+ long lastsector;
+ struct cdrom_paranoia *p;
+
+ c_block *vector; /* doesn't use any sorting */
+ int silenceflag;
+ long silencebegin;
+} root_block;
+
+typedef struct offsets {
+ long offpoints;
+ long newpoints;
+ long offaccum;
+ long offdiff;
+ long offmin;
+ long offmax;
+
+} offsets;
+
+typedef struct cdrom_paranoia {
+ void *d; /* A pointer to the driver interface */
+ int nsectors; /* # of sectors that fit into DMA buf */
+
+ root_block root; /* verified/reconstructed cached data */
+ linked_list *cache; /* our data as read from the cdrom */
+ long cache_limit;
+ linked_list *fragments; /* fragments of blocks that have been */
+ /* 'verified' */
+ sort_info *sortcache;
+
+ int readahead; /* sectors of readahead in each readop */
+ int jitter;
+ long lastread;
+
+ int enable;
+ long cursor;
+ long current_lastsector;
+ long current_firstsector;
+
+ /* statistics for drift/overlap */
+ struct offsets stage1;
+ struct offsets stage2;
+
+ long mindynoverlap;
+ long maxdynoverlap;
+ long dynoverlap;
+ long dyndrift;
+
+ /* statistics for verification */
+
+} cdrom_paranoia;
+
+extern c_block *c_alloc(Int16_t *vector, long begin, long size);
+extern void c_set(c_block *v, long begin);
+extern void c_insert(c_block *v, long pos, Int16_t *b, long size);
+extern void c_remove(c_block *v, long cutpos, long cutsize);
+extern void c_overwrite(c_block *v, long pos, Int16_t *b, long size);
+extern void c_append(c_block *v, Int16_t *vector, long size);
+extern void c_removef(c_block *v, long cut);
+
+#define ce(v) ((v)->begin + (v)->size)
+#define cb(v) ((v)->begin)
+#define cs(v) ((v)->size)
+
+/*
+ * pos here is vector position from zero
+ */
+extern void recover_cache(cdrom_paranoia *p);
+extern void i_paranoia_firstlast(cdrom_paranoia *p);
+
+#define cv(c) ((c)->vector)
+
+#define fe(f) ((f)->begin + (f)->size)
+#define fb(f) ((f)->begin)
+#define fs(f) ((f)->size)
+#define fv(f) (v_buffer(f))
+
+#define CDP_COMPILE
+#endif
diff --git a/libparanoia/paranoia.c b/libparanoia/paranoia.c
new file mode 100644
index 0000000..56a7be4
--- /dev/null
+++ b/libparanoia/paranoia.c
@@ -0,0 +1,1741 @@
+/*
+ * 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;
+}
diff --git a/libparanoia/pmalloc.c b/libparanoia/pmalloc.c
new file mode 100644
index 0000000..65dee97
--- /dev/null
+++ b/libparanoia/pmalloc.c
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)pmalloc.c 1.3 04/05/15 Copyright 2004 J. Schilling */
+/*
+ * Paranoia malloc() functions
+ *
+ * Copyright (c) 2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdxlib.h>
+#include <standard.h>
+#include <schily.h>
+#include "pmalloc.h"
+
+#ifdef PM_ADD_DEBUG
+static int madd = 8192;
+static int cadd = 8192;
+static int radd = 8192;
+#else
+static int madd = 0;
+/*static int cadd = 0;*/
+static int radd = 0;
+#endif
+
+void _pfree(void *ptr)
+{
+ free(ptr);
+}
+
+void *_pmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size + madd);
+ if (p == NULL)
+ raisecond("NO MEM", 0L);
+ return (p);
+}
+
+void *_pcalloc(size_t nelem, size_t elsize)
+{
+ void *p;
+#ifdef PM_ADD_DEBUG
+ size_t n = nelem * elsize;
+
+ n += cadd;
+ p = calloc(1, n);
+#else
+ p = calloc(nelem, elsize);
+#endif
+ if (p == NULL)
+ raisecond("NO MEM", 0L);
+ return (p);
+}
+
+void *_prealloc(void *ptr, size_t size)
+{
+ void *p;
+
+ p = realloc(ptr, size + radd);
+ if (p == NULL)
+ raisecond("NO MEM", 0L);
+ return (p);
+}
diff --git a/libparanoia/pmalloc.h b/libparanoia/pmalloc.h
new file mode 100644
index 0000000..f64ad2f
--- /dev/null
+++ b/libparanoia/pmalloc.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)pmalloc.h 1.1 04/02/20 Copyright 2004 J. Schilling */
+/*
+ * Paranoia malloc() functions
+ *
+ * Copyright (c) 2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PMALLOC_H
+#define _PMALLOC_H
+
+extern void _pfree(void *ptr);
+extern void *_pmalloc(size_t size);
+extern void *_pcalloc(size_t nelem, size_t elsize);
+extern void *_prealloc(void *ptr, size_t size);
+
+#endif /* _PMALLOC_H */