/* dvdisaster: Additional error correction for optical media. * Copyright (C) 2004-2017 Carsten Gnoerlich. * * Email: carsten@dvdisaster.org -or- cgnoerlich@fsfe.org * Project homepage: http://www.dvdisaster.org * * This file is part of dvdisaster. * * dvdisaster is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * dvdisaster 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 dvdisaster. If not, see . */ #include "dvdisaster.h" //#define LOCAL_ONLY 1 //#define PRINT_STEPS 1 #define VERBOSE 1 #ifdef VERBOSE #define verbose(format,...) printf(format, __VA_ARGS__) #else #define verbose(format,...) #endif /*** *** Weights for the evaluation heuristics ***/ /* Weights for self-correcting vectors */ /* Vector corrected itself */ #define BONUS_SELF_CORRECT 50 /* Vector corrected itself and improved another one */ #define BONUS_CROSSED_CORRECT 100 /* Vector corrected itself but destroyed another one */ #define MALUS_CROSSED_DESTROY 100 /* Weights for swapping vector alternatives */ /* Vector was swapped with an accepting one */ #define BONUS_SWAPPED_WITH_BETTER_VECTOR 50 /* Swapped vector improved another vector */ #define BONUS_SWAP_IMPROVED_CROSSING 100 /* Swapped vector destroyed another vector */ #define MALUS_SWAP_DESTROYED_CROSSING 100 /* Swapped for improvement bonus */ #define SWAPPED_WITH_IMPROVEMENT_BONUS 95 /* Swapped for improvement change crossing vector for worse */ #define SWAPPED_WITH_IMPROVEMENT_MALUS 50 /* Indirect improvement bonus for initial P */ #define INDIRECT_IMPROVEMENT_BONUS_FOR_P 90 #define INDIRECT_IMPROVEMENT_MALUS_FOR_P 40 /* Indirect improvement bonus for inner P2 and Q*/ #define INDIRECT_IMPROVEMENT_BONUS_FOR_P2 50 #define INDIRECT_IMPROVEMENT_BONUS_FOR_Q 90 /* Penalty for reaching an already existing solution */ #define CYCLE_PENALTY 20 /*** *** Local data struct ***/ enum { ITERATION_AUTORUN, ITERATION_PICK_BEST_SECTOR, ITERATION_RUN_HEURISTICS }; typedef struct _sh_context { RawBuffer *rb; unsigned char *visited; /* md5sums of already tried solutions */ int *penalty; /* penalty for allready tried solution */ int visitedMax; int visitedCnt; int iteration; /* for iterative running within the editor */ char msg[SMART_LEC_MESSAGE_SIZE]; /* diagnostic output */ unsigned char bestFrame[MAX_RAW_TRANSFER_SIZE]; int bestBonus; int bestMalus; int pState[N_P_VECTORS]; int pPosition[N_P_VECTORS]; /* position of erroneous byte in P vector */ int pValue[N_P_VECTORS]; /* value of corrected byte */ int crossedQ[N_P_VECTORS]; /* q vector affected by corrected byte */ int crossedQIdx[N_P_VECTORS]; /* q vector affected by corrected byte */ int hitByQ[N_P_VECTORS]; /* hit by how many q vectors? */ int qState[N_Q_VECTORS]; int qPosition[N_Q_VECTORS]; /* position of erroneous byte in Q vector */ int qValue[N_Q_VECTORS]; /* value of corrected byte */ int crossedP[N_Q_VECTORS]; /* p vector affected by corrected byte */ int crossedPIdx[N_Q_VECTORS]; /* p vector affected by corrected byte */ int hitByP[N_Q_VECTORS]; /* hit by how many p vectors? */ } sh_context; static sh_context* create_sh_context(RawBuffer *rb) { sh_context *shc = g_malloc0(sizeof(sh_context)); shc->rb = rb; shc->visited = g_malloc(16*4); shc->penalty = g_malloc(sizeof(int)*4); shc->visitedMax = 4; shc->visitedCnt = 0; return shc; } static void free_sh_context(sh_context *shc) { g_free(shc->visited); g_free(shc->penalty); g_free(shc); } /* * Predicate for recognizing a better solution */ int found_better_solution(sh_context *shc, int bonus, int malus) { if(bonus < 0 || malus > shc->bestMalus) return FALSE; if( (malus < shc->bestMalus && bonus > 0) || (malus == shc->bestMalus && bonus > shc->bestBonus)) { shc->bestBonus = bonus; shc->bestMalus = malus; verbose("pick %d/%d\n",bonus,malus); return TRUE; } return FALSE; } /* * Add a frame to the visited list */ static void push_frame(sh_context *shc, unsigned char *frame) { MD5Context ctxt; if(shc->visitedCnt >= shc->visitedMax) { shc->visitedMax *= 2; shc->visited = g_realloc(shc->visited, 16*shc->visitedMax); shc->penalty = g_realloc(shc->penalty, sizeof(int)*shc->visitedMax); } MD5Init(&ctxt); MD5Update(&ctxt, frame, shc->rb->sampleSize); MD5Final(&shc->visited[16*shc->visitedCnt], &ctxt); shc->penalty[shc->visitedCnt] = 0; shc->visitedCnt++; printf("pushed\n"); } /* * Check whether the solution was found before */ static int frame_visited(sh_context *shc, unsigned char *frame) { MD5Context ctxt; unsigned char md5sum[16]; int i,n; MD5Init(&ctxt); MD5Update(&ctxt, frame, shc->rb->sampleSize); MD5Final(md5sum, &ctxt); n = shc->visitedCnt*16; for(i=0; ivisited[i], 16)) return i/16+1; return 0; } /* * Calculate a penalty for already seen solution */ static int cycle_penalty(sh_context *shc, unsigned char *frame) { int idx = frame_visited(shc, frame); if(!idx) return 0; idx--; shc->penalty[idx] += CYCLE_PENALTY; return shc->penalty[idx]; } /* * Check whether the frame has been corrected */ static int frame_corrected(RawBuffer *rb) { if( CheckEDC(rb->recovered, rb->xaMode) && CheckMSF(rb->recovered, rb->lba, STRICT_MSF_CHECK)) return TRUE; return FALSE; } /* * Try lec with given p/q vectors without erasures. * Returns position of error if err==1 */ static int decode_p(RawBuffer *rb, unsigned char* p_vector, int *pos) { int eras[2], err; unsigned char backup[P_VECTOR_SIZE]; memcpy(backup, p_vector, P_VECTOR_SIZE); err = DecodePQ(rb->rt, p_vector, P_PADDING, eras, 0); if(err == 0) return err; if(err == 1) { int i; for(i=0; irt, q_vector, Q_PADDING, eras, 0); if(err == 0) return err; if(err == 1) { int i; for(i=0; isamplesRead) /* We need at least one sample */ return; sample_idx = rb->samplesRead-1; /* Find all P vectors which are accepted by the error correction */ for(p=0; ppn[p]; GetPVector(rb->rawBuf[sample_idx], vector, p); err = DecodePQ(rb->rt, vector, P_PADDING, eras, 0); if(err != 0 && err != 1) continue; for(i=0; ipList[p][i], vector, P_VECTOR_SIZE)) { found = TRUE; break; } if(!found) { memcpy(rb->pList[p][last_p], vector, P_VECTOR_SIZE); rb->pn[p]++; } } /* Find all Q vectors which are accepted by the error correction */ for(q=0; qqn[q]; GetQVector(rb->rawBuf[sample_idx], vector, q); err = DecodePQ(rb->rt, vector, Q_PADDING, eras, 0); if(err != 0 && err != 1) continue; for(i=0; iqList[q][i], vector, Q_VECTOR_SIZE)) { found = TRUE; break; } if(!found) { memcpy(rb->qList[q][last_q], vector, Q_VECTOR_SIZE); rb->qn[q]++; } } } /* * Debugging function */ void PrintPQStats(RawBuffer *rb) { int i; PrintLog("PQ vector good variants from read samples:\n"); for(i=0; ipn[i]); for(i=0; iqn[i]); } /*** *** Find correctable vectors and their interrelationship ***/ static void update_pq_state(sh_context *shc) { RawBuffer *rb = shc->rb; unsigned char vector[Q_VECTOR_SIZE]; int eras[2],err,i; int crossed,crossed_idx; memset(shc->crossedP, 0, sizeof(shc->crossedP)); memset(shc->crossedQ, 0, sizeof(shc->crossedQ)); memset(shc->crossedPIdx, 0, sizeof(shc->crossedPIdx)); memset(shc->crossedQIdx, 0, sizeof(shc->crossedQIdx)); memset(shc->hitByP, 0, sizeof(shc->hitByP)); memset(shc->hitByQ, 0, sizeof(shc->hitByQ)); for(i=0; irecovered, vector, i); err = DecodePQ(rb->rt, vector, P_PADDING, eras, 0); switch(err) { case 0: shc->pState[i] = 0; break; case 1: shc->pState[i] = 1; shc->pPosition[i] = eras[0]; shc->pValue[i] = vector[eras[0]]; ByteIndexToQ(PToByteIndex(i, eras[0]), &crossed, &crossed_idx); shc->crossedQ[i] = crossed; shc->crossedQIdx[i] = crossed_idx; shc->hitByP[crossed]++; break; default: shc->pState[i] = 2; break; } } for(i=0; irecovered, vector, i); err = DecodePQ(rb->rt, vector, Q_PADDING, eras, 0); switch(err) { case 0: shc->qState[i] = 0; break; case 1: shc->qState[i] = 1; shc->qPosition[i] = eras[0]; shc->qValue[i] = vector[eras[0]]; ByteIndexToP(QToByteIndex(i, eras[0]), &crossed, &crossed_idx); shc->crossedP[i] = crossed; shc->crossedPIdx[i] = crossed_idx; shc->hitByQ[crossed]++; break; default: shc->qState[i] = 2; break; } } } static void print_pq_state(sh_context *shc) { int i; verbose("%s", "PQ states: \n"); for(i=0; ipState[i] == 1) verbose("P %02d: correctable, crosses Q %02d\n", i, shc->crossedQ[i]); if(shc->pState[i] == 2) verbose("P %02d: failure\n", i); } for(i=0; iqState[i] == 1) verbose("Q %02d: correctable, crosses P %02d\n", i, shc->crossedP[i]); if(shc->qState[i] == 2) verbose("Q %02d: failure\n", i); } } /*** *** Track changes of one byte recursively ***/ static void recursive_q_correction(RawBuffer*, unsigned char*, int, int, int*, int*); static void recursive_p_correction(RawBuffer *rb, unsigned char *frame, int byte_idx, int new_byte, int *better, int *worse) { unsigned char p_vector[P_VECTOR_SIZE]; int old_err, new_err, pos; int p,p_idx; ByteIndexToP(byte_idx, &p, &p_idx); GetPVector(frame, p_vector, p); old_err = decode_p(rb, p_vector, &pos); GetPVector(frame, p_vector, p); p_vector[p_idx] = new_byte; new_err = decode_p(rb, p_vector, &pos); if(new_err > old_err) { *worse += (new_err - old_err); verbose("rec_p_corr(%d): %d > %d, stopping\n", p, new_err, old_err); return; } /* Return if no improvent occurred. Exception: If P goes from 1->1 it might have made up its mind on which byte to correct. */ if(new_err == old_err && new_err != 1) { verbose("rec_p_corr(%d): %d == %d, no improvement\n", p, new_err, old_err); return; } /* P corrected another byte; update frame and continue with crossed Q */ frame[byte_idx] = new_byte; (*better)++; verbose("rec_p_corr(%d): %d < %d, continue\n", p, new_err, old_err); recursive_q_correction(rb, frame, PToByteIndex(p, pos), p_vector[pos], better, worse); } static void recursive_q_correction(RawBuffer *rb, unsigned char *frame, int byte_idx, int new_byte, int *better, int *worse) { unsigned char q_vector[Q_VECTOR_SIZE]; int old_err, new_err, pos; int q,q_idx; ByteIndexToQ(byte_idx, &q, &q_idx); GetQVector(frame, q_vector, q); old_err = decode_q(rb, q_vector, &pos); GetQVector(frame, q_vector, q); q_vector[q_idx] = new_byte; new_err = decode_q(rb, q_vector, &pos); if(new_err > old_err) { *worse += (new_err - old_err); verbose("rec_q_corr(%d): %d > %d, stopping\n", q, new_err, old_err); return; } /* Return if no improvent occurred. Exception: If Q goes from 1->1 it might have made up its mind on which byte to correct. */ if(new_err == old_err && new_err != 1) { verbose("rec_q_corr(%d): %d == %d, no improvement\n", q, new_err, old_err); return; } /* Q corrected another byte; update frame and continue with crossed P */ frame[byte_idx] = new_byte; (*better)++; verbose("rec_q_corr(%d): %d < %d, continue\n", q, new_err, old_err); recursive_p_correction(rb, frame, QToByteIndex(q, pos), q_vector[pos], better, worse); } /*** *** See if multiple vectors can correct a crossed vector *** * Strategy note: If the crossed vector becomes correctable, * e.g. it evaluates to err=1, we could try to propagate * the one-byte correction further (and possible gain a good * scoring). This might have the disadvantage of masking off * another crossing correction, though * Example: Q is crossed by 4 P vectors; if all 4 P are corrected, * Q will evaluate to zero errors, also. That implies that applying * all combinations of 3 P will leave Q with err=1, and the recursive * propagation might give it a higher score than the solution with * 4 corrected P. Therefore we might miss picking the better case * using all 4 corrected P. So we avoid doing the recursive * propagation here in order to max out the crossing vectors. */ /* * Try to correct a q vector from crossing p vectors */ static void many_p_correct_one_q(sh_context *shc) { RawBuffer *rb = shc->rb; int crossing_p[N_P_VECTORS]; int selection[N_P_VECTORS]; int i,p,q; int n_p=0; /* Determine all P vectors which cross the Q */ for(q=0; qhitByP[q] < 2) continue; verbose("Q%02d is crossed by: ",q); n_p = 0; for(p=0; pcrossedQ[p] == q) { crossing_p[n_p] = p; selection[n_p] = 1; n_p++; n_iterations *= 2; verbose("P%02d ", p); } n_iterations--; /* number of combinations to test */ verbose(" (%d combinations)\n", n_iterations); if(n_iterations <= 0) continue; /* Enumerate all combinations by using as a binary counter */ verbose("Trying all combinations for Q%02d\n", q); while(n_iterations--) { unsigned char scratch[MAX_RAW_TRANSFER_SIZE]; unsigned char vector[Q_VECTOR_SIZE]; int q_before, q_after, pos; int bonus = 0; int malus = 0; int index = 0; int better = 0; int worse = 0; for(p=n_p-1; p>=0; p--) verbose("%d", selection[p]); /* evaluate the vector combination */ GetQVector(rb->recovered, vector, q); q_before = decode_q(rb, vector, &pos); GetQVector(rb->recovered, vector, q); for(i=0; icrossedQIdx[p]] = shc->pValue[p]; } } q_after = decode_q(rb, vector, &pos); if(q_after <= q_before) bonus += (q_before-q_after)*BONUS_CROSSED_CORRECT; else malus += (q_after-q_before)*MALUS_CROSSED_DESTROY; verbose(" Q:%d->%d; bonus: %3d malus: %3d\n", q_before, q_after, bonus, malus); /* If q becomes correctable, continue correction. */ if(q_after == 1) { unsigned char byte_before_corr; int byte_idx; byte_idx = QToByteIndex(q, pos); byte_before_corr = rb->recovered[byte_idx]; /* Copy rb->recovered into scratch, including all bytes changed in Q (there may be many, up to n_p. However the byte which Q currently wants to correct is NOT included in scratch so that recursive_p_correction() can judge for itself. */ memcpy(scratch, rb->recovered, rb->sampleSize); SetQVector(scratch, vector, q); scratch[byte_idx] = byte_before_corr; recursive_p_correction(rb, scratch, byte_idx, vector[pos], &better, &worse); verbose(" recursive gain: better/worse: %d / %d\n", better, worse); bonus += BONUS_CROSSED_CORRECT * better; // malus -= MALUS_CROSSED_DESTROY; /* P went back from 1->0 */ malus += MALUS_CROSSED_DESTROY * worse; } /* Better solution found? */ bonus -= cycle_penalty(shc, scratch); if(q_after < 2 && found_better_solution(shc, bonus, malus)) { if(q_after != 1) { memcpy(shc->bestFrame, rb->recovered, rb->sampleSize); SetQVector(shc->bestFrame, vector, q); } else memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "Q%02d improved from %d->%d by crossing P vectors (bonus %d/ malus %d).", q,q_before, q_after, bonus, malus); } /* binary decrement on selection[] */ while(selection[index] != 1 && index < n_p) { index++; selection[index-1] = 1; } if(index >= n_p) break; selection[index]=0; } } } /* * Try to correct a p vector from crossing q vectors */ static void many_q_correct_one_p(sh_context *shc) { RawBuffer *rb = shc->rb; int crossing_q[N_Q_VECTORS]; int selection[N_Q_VECTORS]; int i,p,q; int n_q=0; /* Determine all Q vectors which cross the P */ for(p=0; phitByQ[p] < 1) continue; verbose("P%02d is crossed by: ",p); n_q = 0; for(q=0; qcrossedP[q] == p) { crossing_q[n_q] = q; selection[n_q] = 1; n_q++; n_iterations *= 2; verbose("Q%02d ", q); } n_iterations--; /* number of combinations to test */ verbose("(%d combinations)\n", n_iterations); if(n_iterations <= 0) continue; /* Enumerate all combinations by using as a binary counter */ verbose("Trying all combinations for P%02d\n", p); while(n_iterations--) { unsigned char scratch[MAX_RAW_TRANSFER_SIZE]; unsigned char vector[Q_VECTOR_SIZE]; int p_before, p_after, pos; int bonus = 0; int malus = 0; int index = 0; int better = 0; int worse = 0; for(q=n_q-1; q>=0; q--) verbose("%d", selection[q]); /* evaluate the vector combination */ GetPVector(rb->recovered, vector, p); p_before = decode_p(rb, vector, &pos); GetPVector(rb->recovered, vector, p); for(i=0; icrossedPIdx[q]] = shc->qValue[q]; } } p_after = decode_p(rb, vector, &pos); if(p_after <= p_before) bonus += (p_before-p_after)*BONUS_CROSSED_CORRECT; else malus += (p_after-p_before)*MALUS_CROSSED_DESTROY; verbose(" P:%d->%d; bonus: %3d malus: %3d\n", p_before, p_after, bonus, malus); /* If p becomes correctable, continue correction. */ if(p_after == 1) { unsigned char byte_before_corr; int byte_idx; byte_idx = PToByteIndex(p, pos); byte_before_corr = rb->recovered[byte_idx]; /* Copy rb->recovered into scratch, including all bytes changed in P (there may be many, up to n_q. However the byte which P currently wants to correct is NOT included in scratch so that recursive_q_correction() can judge for itself. */ memcpy(scratch, rb->recovered, rb->sampleSize); SetPVector(scratch, vector, p); scratch[byte_idx] = byte_before_corr; recursive_q_correction(rb, scratch, byte_idx, vector[pos], &better, &worse); verbose(" recursive gain: better/worse: %d / %d\n", better, worse); bonus += BONUS_CROSSED_CORRECT * better; // malus -= MALUS_CROSSED_DESTROY; /* P went back from 1->0 */ malus += MALUS_CROSSED_DESTROY * worse; } /* Better solution found? */ bonus -= cycle_penalty(shc, scratch); if(p_after < 2 && found_better_solution(shc, bonus, malus)) { if(p_after != 1) { memcpy(shc->bestFrame, rb->recovered, rb->sampleSize); SetPVector(shc->bestFrame, vector, p); } else memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "P%02d improved from %d->%d by crossing Q vectors (bonus %d/ malus %d).", p,p_before, p_after, bonus, malus); } verbose("%d\n", rb->sampleSize); /* binary decrement on selection[] */ while(selection[index] != 1 && index < n_q) { selection[index] = 1; index++; } selection[index]=0; } } } /*** *** See if replacing a vector with another accepting version improves anything ***/ static void evaluate_new_frame(sh_context *shc, unsigned char *new, int *better, int *worse) { RawBuffer *rb = shc->rb; unsigned char vector[Q_VECTOR_SIZE]; int p,q; int err, pos; for(p=0; p shc->pState[p]) *worse += err - shc->pState[p]; else *better += shc->pState[p] - err; } for(q=0; q shc->qState[q]) *worse += err - shc->qState[q]; else *better += shc->qState[q] - err; } } static void try_alternative_vectors(sh_context *shc) { RawBuffer *rb = shc->rb; unsigned char scratch[MAX_RAW_TRANSFER_SIZE]; int better, worse; int i,p,q; for(p=0; ppState[p] == 0) continue; for(i=0; ipn[p]; i++) { int bonus; int malus; memcpy(scratch, rb->recovered, rb->sampleSize); SetPVector(scratch, rb->pList[p][i], p); better = -shc->pState[p]; /* We replaced damaged P with an accepting P */ worse = 0; evaluate_new_frame(shc, scratch, &better, &worse); bonus = BONUS_SWAPPED_WITH_BETTER_VECTOR*2 + BONUS_SWAP_IMPROVED_CROSSING*better; malus = MALUS_SWAP_DESTROYED_CROSSING*worse; bonus -= cycle_penalty(shc, scratch); verbose("Trying P%02d, variant %d: %d better, %d worse (%d/%d)\n", p, i, better, worse, bonus, malus); /* This is maybe too restrictive? */ if(malus > 0) continue; if(found_better_solution(shc, bonus, malus)) { memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "Found alternative vector for P%02d (bonus %d/ malus %d).", p, bonus, malus); } } } for(q=0; qqState[q] == 0) continue; for(i=0; iqn[q]; i++) { int bonus; int malus; memcpy(scratch, rb->recovered, rb->sampleSize); SetQVector(scratch, rb->qList[q][i], q); better = -shc->qState[q]; /* We replaced damaged Q with an accepting Q */ worse = 0; evaluate_new_frame(shc, scratch, &better, &worse); bonus = BONUS_SWAPPED_WITH_BETTER_VECTOR*2 + BONUS_SWAP_IMPROVED_CROSSING*better; malus = MALUS_SWAP_DESTROYED_CROSSING*worse; bonus -= cycle_penalty(shc, scratch); verbose("Trying Q%02d, variant %d: %d better, %d worse (%d/%d)\n", q, i, better, worse, bonus, malus); /* This is maybe too restrictive? */ if(malus > 0) continue; if(found_better_solution(shc, bonus, malus)) { memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "Found alternative vector for Q%02d (bonus %d/ malus %d).", q, bonus, malus); } } } } /*** *** See if swapping some bytes from alternative vectors improves something ***/ static void try_alternative_crossing_bytes(sh_context *shc) { RawBuffer *rb = shc->rb; unsigned char scratch[MAX_RAW_TRANSFER_SIZE]; int better, worse; int i,j,p,q; for(p=0; ppState[p] == 0) continue; GetPVector(rb->recovered, current_p, p); for(i=0; ipn[p]; i++) { unsigned char *alternative_p = rb->pList[p][i]; /* If the alternative vector differs in byte j, see if swapping the current byte with byte j improves the crossed vector. */ for(j=0; jqState[crossed] > 0) { better = worse = 0; memcpy(scratch, rb->recovered, rb->sampleSize); recursive_q_correction(rb, scratch, byte_idx, alternative_p[j], &better, &worse); if(better > 0) { int bonus, malus; verbose("Q%02d improved by swapping %d in P%02d, %d/%d\n", crossed, j, p, better, worse); bonus = better*BONUS_CROSSED_CORRECT - cycle_penalty(shc, scratch); malus = worse*MALUS_CROSSED_DESTROY; if(found_better_solution(shc, bonus, malus)) { memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "Q%02d improved by swapping %d in P%02d, %d/%d (bonus %d/ malus %d).", crossed, j, p, better, worse, bonus, malus); } } } } } } } for(q=0; qqState[q] == 0) continue; GetQVector(rb->recovered, current_q, q); for(i=0; iqn[q]; i++) { unsigned char *alternative_q = rb->qList[q][i]; /* If the alternative vector differs in byte j, see if swapping the current byte with byte j improves the crossed vector. */ for(j=0; jpState[crossed] > 0) { better = worse = 0; memcpy(scratch, rb->recovered, rb->sampleSize); recursive_p_correction(rb, scratch, byte_idx, alternative_q[j], &better, &worse); if(better > 0) { int bonus, malus; verbose("P%02d improved by swapping %d in Q%02d, %d/%d\n", crossed, j, q, better, worse); bonus = better*BONUS_CROSSED_CORRECT - cycle_penalty(shc, scratch); malus = worse*MALUS_CROSSED_DESTROY; if(found_better_solution(shc, bonus, malus)) { memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "P%02d improved by swapping %d in Q%02d, %d/%d (bonus %d/ malus %d).", crossed, j, q, better, worse, bonus, malus); } } } } } } } } /*** *** See if a p can improve two crossing q vectors ***/ static void recursive_double_q_correction(RawBuffer*, unsigned char*, int, int, int, int, int*, int*); static void recursive_double_p_correction(RawBuffer *rb, unsigned char *frame, int byte_idx1, int new_byte1, int byte_idx2, int new_byte2, int *better, int *worse) { unsigned char p_vector1[P_VECTOR_SIZE]; unsigned char p_vector2[P_VECTOR_SIZE]; int old_err1, new_err1, pos1; int old_err2, new_err2, pos2; int p1,p1_idx,p2,p2_idx; ByteIndexToP(byte_idx1, &p1, &p1_idx); GetPVector(frame, p_vector1, p1); old_err1 = decode_p(rb, p_vector1, &pos1); GetPVector(frame, p_vector1, p1); p_vector1[p1_idx] = new_byte1; new_err1 = decode_p(rb, p_vector1, &pos1); ByteIndexToP(byte_idx2, &p2, &p2_idx); GetPVector(frame, p_vector2, p2); old_err2 = decode_p(rb, p_vector2, &pos2); if(p1 != p2) { GetPVector(frame, p_vector2, p2); p_vector2[p2_idx] = new_byte2; new_err2 = decode_p(rb, p_vector2, &pos2); } else new_err2 = old_err2 = new_err1; verbose("rec 2p: old %d %d, new %d %d - %d %d\n", old_err1, old_err2, new_err1, new_err2, p1, p2); /* Best case: both P improved */ if(new_err1 < old_err1 && new_err2 < old_err2) { frame[byte_idx1] = new_byte1; frame[byte_idx2] = new_byte2; (*better) +=(old_err1-new_err1)+(old_err2-new_err2); recursive_double_q_correction(rb, frame, PToByteIndex(p1, pos1), p_vector1[pos1], PToByteIndex(p2, pos2), p_vector2[pos2], better, worse); return; } /* Nothing improved at all */ if(new_err1 >= old_err1 && new_err2 >= old_err2) { (*worse)+=(new_err1-old_err1)+(new_err2-old_err2); return; } /* One P improved */ if(new_err1 < old_err1) /* first P improved */ { (*better) += old_err1 - new_err1; (*worse) += new_err2 - old_err2; frame[byte_idx1] = new_byte1; recursive_q_correction(rb, frame, PToByteIndex(p1, pos1), p_vector1[pos1], better, worse); return; } else /* second P improved */ { (*better) += old_err2 - new_err2; (*worse) += new_err1 - old_err1; frame[byte_idx2] = new_byte2; recursive_q_correction(rb, frame, PToByteIndex(p2, pos2), p_vector2[pos2], better, worse); return; } } static void recursive_double_q_correction(RawBuffer *rb, unsigned char *frame, int byte_idx1, int new_byte1, int byte_idx2, int new_byte2, int *better, int *worse) { unsigned char q_vector1[Q_VECTOR_SIZE]; unsigned char q_vector2[Q_VECTOR_SIZE]; int old_err1, new_err1, pos1; int old_err2, new_err2, pos2; int q1,q1_idx,q2,q2_idx; ByteIndexToQ(byte_idx1, &q1, &q1_idx); GetQVector(frame, q_vector1, q1); old_err1 = decode_q(rb, q_vector1, &pos1); GetQVector(frame, q_vector1, q1); q_vector1[q1_idx] = new_byte1; new_err1 = decode_q(rb, q_vector1, &pos1); ByteIndexToQ(byte_idx2, &q2, &q2_idx); GetQVector(frame, q_vector2, q2); old_err2 = decode_q(rb, q_vector2, &pos2); if(q1 != q2) { GetQVector(frame, q_vector2, q2); q_vector2[q2_idx] = new_byte2; new_err2 = decode_q(rb, q_vector2, &pos2); } else new_err2 = old_err2 = new_err1; verbose("rec 2q: old %d %d, new %d %d - %d %d\n", old_err1, old_err2, new_err1, new_err2, q1, q2); /* Best case: both Q improved */ if(new_err1 < old_err1 && new_err2 < old_err2) { frame[byte_idx1] = new_byte1; frame[byte_idx2] = new_byte2; (*better) +=(old_err1-new_err1)+(old_err2-new_err2); recursive_double_p_correction(rb, frame, QToByteIndex(q1, pos1), q_vector1[pos1], QToByteIndex(q2, pos2), q_vector2[pos2], better, worse); return; } /* Nothing improved at all */ if(new_err1 >= old_err1 && new_err2 >= old_err2) { (*worse)+=(new_err1-old_err1)+(new_err2-old_err2); return; } /* One Q improved */ if(new_err1 < old_err1) /* first Q improved */ { (*better) += old_err1 - new_err1; (*worse) += new_err2 - old_err2; frame[byte_idx1] = new_byte1; recursive_p_correction(rb, frame, QToByteIndex(q1, pos1), q_vector1[pos1], better, worse); return; } else /* second Q improved */ { (*better) += old_err2 - new_err2; (*worse) += new_err1 - old_err1; frame[byte_idx2] = new_byte2; recursive_p_correction(rb, frame, QToByteIndex(q2, pos2), q_vector2[pos2], better, worse); return; } } static void find_p_with_two_erasures(sh_context *shc) { RawBuffer *rb = shc->rb; int erasure_pos[P_VECTOR_SIZE]; int crossed_q[P_VECTOR_SIZE]; int crossed_q_idx[P_VECTOR_SIZE]; int n_q = 0; int p,q,i; int e1, e2; for(p=0; p<=N_P_VECTORS; p++) { if(shc->pState[p] != 2) continue; for(i=0; iqState[q] > 0) { erasure_pos[n_q] = i; crossed_q[n_q] = q; crossed_q_idx[n_q] = q_idx; n_q++; } } if(n_q < 2) continue; if(n_q > 7) return; /* too much damage */ /* Enumerate all erasure combinations */ for(e1=0; e1recovered, p_vector, p); eras[0] = erasure_pos[e1]; eras[1] = erasure_pos[e2]; err = DecodePQ(rb->rt, p_vector, P_PADDING, eras, 2); if(err != 2) continue; better = worse = 0; memcpy(scratch, rb->recovered, rb->sampleSize); recursive_double_q_correction(rb, scratch, QToByteIndex(crossed_q[e1],crossed_q_idx[e1]), p_vector[erasure_pos[e1]], QToByteIndex(crossed_q[e2],crossed_q_idx[e2]), p_vector[erasure_pos[e2]], &better, &worse); if(better > 0) { int bonus, malus; verbose("P%02d with erasure pos %02d/%02d: %d better, %d worse\n", p,erasure_pos[e1],erasure_pos[e2],better,worse); bonus = better*BONUS_CROSSED_CORRECT - cycle_penalty(shc, scratch); malus = worse*MALUS_CROSSED_DESTROY; if(found_better_solution(shc, bonus, malus)) { memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "P%02d with erasures %02d/%02d: %d better, %d worse (bonus %d/ malus %d).", p,erasure_pos[e1],erasure_pos[e2],better,worse,bonus,malus); } } } } } /*** *** Swap P with an alternative vector, *** then selectively correct the crossing vectors * to see if a new combination arises which makes P * correct a new vector. */ static void swap_p_for_new_improvement(sh_context *shc) { RawBuffer *rb = shc->rb; unsigned char old_vector[Q_VECTOR_SIZE]; unsigned char new_vector[Q_VECTOR_SIZE]; unsigned char p_vector[P_VECTOR_SIZE]; unsigned char q_vector[Q_VECTOR_SIZE]; unsigned char scratch[MAX_RAW_TRANSFER_SIZE]; int err_pos; int p,q; for(p=0; ppState[p] == 0) continue; GetPVector(rb->recovered, old_vector, p); for(i=0; ipn[p]; i++) /* try all alternative p vectors */ { int selection[N_Q_VECTORS]; int crossing_q[N_Q_VECTORS]; int crossing_err[N_Q_VECTORS]; int err; int n_q = 0; int n_iterations = 1; verbose("swap p for new improvement: alternative %d for P%02d\n",i, p); memcpy(scratch, rb->recovered, rb->sampleSize); SetPVector(scratch, rb->pList[p][i], p); memcpy(new_vector, rb->pList[p][i], P_VECTOR_SIZE); for(j=0; j%d\n", j, crossed, shc->qState[crossed], err); crossing_q[n_q] = crossed; crossing_err[n_q] = err; selection[n_q] = 1; n_q++; n_iterations *= 2; } } if(n_q < 2) /* We need at least 2 crossing vectors */ continue; n_iterations--; /* number of combinations to test */ /* Enumerate all combinations by using as a binary counter */ verbose("Trying all combinations for P%02d\n", p); while(n_iterations--) { int index = 0; int count = 0; int bonus = 0; int malus = 0; for(q=n_q-1; q>=0; q--) verbose("%d", selection[q]); /* Count number of changed crossed vectors. Less than 2 do not produce an interesting new P vector. */ for(j=0; jrecovered, rb->sampleSize); SetPVector(scratch, new_vector, p); for(j=0; j %d", prev_state, new_state); /* Better solution found? */ if(prev_state > new_state) { bonus = SWAPPED_WITH_IMPROVEMENT_BONUS - cycle_penalty(shc, scratch); if(found_better_solution(shc, bonus, malus)) { memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "P%02d swapped; Q%02d improved (%d/%d)",p,crossed_q,bonus,malus); } } } /* binary decrement on selection[] */ decrement: while(selection[index] != 1 && index < n_q) { index++; selection[index-1] = 1; } if(index >= n_q) break; selection[index]=0; verbose("%s", "\n"); } } } } /*** *** Try to get out of a local maximum. *** * See if swapping a byte in P produces a Q which can be changed * by another crossing P. At worst, we get rid of the crossing P * and the changed Q might correct something else in a later stage. */ static void try_indirect_improvement(sh_context *shc) { RawBuffer *rb = shc->rb; unsigned char scratch[MAX_RAW_TRANSFER_SIZE]; unsigned char vector[Q_VECTOR_SIZE]; int i,j,p,q; /* Find uncorrected P vectors */ for(p=0; ppState[p] == 0) continue; /* already good */ GetPVector(rb->recovered, current_p, p); for(i=0; ipn[p]; i++) { unsigned char *alternative_p = rb->pList[p][i]; /* If the alternative vector differs in byte j, see what happens with the Q crossing byte j. */ for(j=0; jrecovered, crossed_q, q); crossed_q[crossed_idx] = alternative_p[j]; /* Calculated state of modified Q. We want a Q which either improves or stays within the correctable state (maybe it corrects the right byte now) */ memcpy(vector, crossed_q, Q_VECTOR_SIZE); err = decode_q(rb, vector, &ignore); if(err == 1) { int p2,p2_index,k; verbose("indirect for P%02d: interesting Q%02d %d->%d\n", p, q, shc->qState[q], err); /* Now see if we can find another crossing P which makes Q go from 1->1 or 0 */ memcpy(scratch, rb->recovered, rb->sampleSize); scratch[byte_idx] = alternative_p[j]; for(k=0; kpState[p2] != 1) || (shc->pPosition[p2] != p2_index)) continue; /* trivially reject; crossing P won't change Q */ /* Crossed P changed something in Q. Re-evaluate Q. */ memcpy(vector, crossed_q, Q_VECTOR_SIZE); vector[k] = shc->pValue[p2]; err = decode_q(rb, vector, &ignore); if(err>1) /* Q got uncorrectable */ continue; scratch[byte_idx] = shc->pValue[p2]; /* Add bonus for improved Q (err of Q was 1 before) */ if(!err) bonus += INDIRECT_IMPROVEMENT_BONUS_FOR_Q; /* Set score for the outer P */ memcpy(p_vector, current_p, P_VECTOR_SIZE); current_p[j] = alternative_p[j]; err = decode_p(rb, p_vector, &ignore); if(err < shc->pState[p]) bonus += INDIRECT_IMPROVEMENT_BONUS_FOR_P; if(err > shc->pState[p]) malus += INDIRECT_IMPROVEMENT_MALUS_FOR_P; /* Add some for the inner P which changed from 1->0 */ bonus += INDIRECT_IMPROVEMENT_BONUS_FOR_P; verbose("crossed P%02d does something (%d/%d)\n", p2,bonus,malus); if(found_better_solution(shc, bonus, malus)) { memcpy(shc->bestFrame, scratch, rb->sampleSize); snprintf(shc->msg, SMART_LEC_MESSAGE_SIZE, "Q%02d improved indirectly by P%02d, P%02d (%d/%d)",q,p,p2,bonus,malus); } } } } } } } } /*** *** Try all bytes at positions where uncorrectable p/q overlap ***/ #if 0 static void guess_crossing_byte(sh_context *shc) { RawBuffer *rb = shc->rb; int p,q,i,j,g; for(p=0; ppState[p] != 2) continue; for(i=0; iqState[q] != 2) continue; verbose("Guess candidate P%02d.%02d / Q%02d.%02d\n", p,i,q,crossed_idx); for(g=0; g<256; g++) { unsigned char p_vector[P_VECTOR_SIZE]; unsigned char q_vector[Q_VECTOR_SIZE]; int ignore; int idx = PToByteIndex(p, i); for(j=0; jsamplesRead; j++) if(rb->rawBuf[j][idx] == g) break; if(j>=256) continue; GetPVector(rb->recovered, p_vector, p); p_vector[i] = g; if(decode_p(rb, p_vector, &ignore) == 1) { GetQVector(rb->recovered, q_vector, q); q_vector[crossed_idx] = g; if(decode_q(rb, q_vector, &ignore) == 1) verbose("... guess %d solves P and Q\n", g); } } } } } #endif /*** *** The smart lec wrapper. ***/ static void pick_best_frame(sh_context *shc, char *message) { RawBuffer *rb = shc->rb; printf("Best P: %d/%d, %d/%d\n", rb->bestP2, rb->bestP1, rb->bestQ2, rb->bestQ1); memcpy(rb->recovered, rb->rawBuf[rb->bestFrame], rb->sampleSize); if(message) snprintf(message, SMART_LEC_MESSAGE_SIZE, "selected best sector frame %d with %d/%d, %d/%d defective P/Q vectors.", rb->bestFrame, rb->bestP2, rb->bestP1, rb->bestQ2, rb->bestQ1); } static int smart_lec_iteration(sh_context *shc, char *message) { RawBuffer *rb = shc->rb; shc->bestBonus = 0; shc->bestMalus = 100000; memcpy(shc->bestFrame, rb->recovered, rb->sampleSize); sprintf(shc->msg, "smart_lec: no further improvement"); update_pq_state(shc); print_pq_state(shc); many_p_correct_one_q(shc); many_q_correct_one_p(shc); #ifndef LOCAL_ONLY try_alternative_vectors(shc); try_alternative_crossing_bytes(shc); #endif find_p_with_two_erasures(shc); swap_p_for_new_improvement(shc); try_indirect_improvement(shc); if(frame_visited(shc, shc->bestFrame)) printf("pruning!\n"); memcpy(rb->recovered, shc->bestFrame, rb->sampleSize); push_frame(shc, shc->bestFrame); if(message) memcpy(message, shc->msg, SMART_LEC_MESSAGE_SIZE); return TRUE; } int SmartLEC(RawBuffer *rb) { sh_context *shc = create_sh_context(rb); unsigned char prev_state[rb->sampleSize]; pick_best_frame(shc, NULL); #ifdef PRINT_STEPS printf("%s\n", shc->msg); #endif CreateMissingSector(prev_state, rb->lba, NULL, 0, "SmartLEC() dummy sector"); while(TRUE) { smart_lec_iteration(shc, NULL); #ifdef PRINT_STEPS printf("%s\n", shc->msg); #endif if(frame_corrected(shc->rb)) { free_sh_context(shc); return TRUE; } if(!memcmp(prev_state, rb->recovered, rb->sampleSize)) { free_sh_context(shc); return FALSE; } memcpy(prev_state, rb->recovered, rb->sampleSize); } free_sh_context(shc); return FALSE; } /* * Special actions for iteratively running the smart lec * from the raw editor */ void *PrepareIterativeSmartLEC(RawBuffer *rb) { sh_context *shc = create_sh_context(rb); shc->iteration = ITERATION_PICK_BEST_SECTOR; return shc; } void SmartLECIteration(void *shc_handle, char *message) { sh_context *shc = (sh_context*)shc_handle; switch(shc->iteration) { case ITERATION_PICK_BEST_SECTOR: #ifndef LOCAL_ONLY pick_best_frame(shc, message); #endif shc->iteration++; break; default: smart_lec_iteration(shc, message); break; } #ifdef PRINT_STEPS printf("%s\n", shc->msg); #endif } void EndIterativeSmartLEC(void *shc) { free_sh_context((sh_context*)shc); }