#!/bin/sh

methode=bourrin # On essaie toutes les combinaisons de blocs possibles, jusqu'à ce que PHP nous signale qu'il arrive à charger l'image.
methode=prog # On essaie progressivement de reconstituer l'image: pour chaque bloc, on essaie de l'accoler au début déjà reconstitué, et on demande voir si AppleScript arrive à la lire "un peu plus loin".

[ -z "$1" ] && echo "# Utilisation: reconstituer <dossier>" >&2 && echo "#   <dossier>: dossier contenant les pasbons* qui contiennent les photos endommagées (pour être reconstituable, une photo doit se trouver dans plusieurs de ces pasbons*, avec des blocs endommagés différents dans chacun)." >&2 && exit 1

cd "$1" || exit 1
mkdir /tmp/$$

[ $methode = prog ] && ( cat > /tmp/$$/trouve.c <<TERMINE
#include <stdio.h>
int main(int argc, char ** argv)
{
	char b[0x1000];
	char * p, * p2;
	int n = 0;
	int lus;
	while((p2 = (p = &b[0]) + read(0, b, 0x1000)) > p)
	{
		--p;
		while(++p < p2)
			if(*p != 0x6d) // Apple nous met ce joli gris pour indiquer une absence d'image, autrement dit une erreur de lecture.
				n = 0;
			else if(++n > 10) // Plus de 10 pixels gris d'affilée? On sort.
			{
				printf("%d\n", (int)(lus + (p - 10 - &b[0])));
				return 0;
			}
		lus += p - &b[0];
	}
	printf("%d\n", lus);
	return 0;
}
TERMINE
) && cc -o /tmp/$$/trouve /tmp/$$/trouve.c

traiter()
{
mkdir -p oui

# Liste de l'ensemble des fichiers contenus dans au moins deux des répertoires d'images partiellement corrompues.
for i in pasbons*
do
	(cd "$i" && ls )
done | sort | tee /tmp/$$/1 | sort -u > /tmp/$$/2
diff /tmp/$$/2 /tmp/$$/1 | grep '^>' | cut -c 3- | sort -u > /tmp/$$/liste

cat /tmp/$$/liste | while read i
do
	echo "=== $i ===" >&2
	ref=
	rm -f /tmp/$$/hex.*
	true > /tmp/$$/diffs
	for j in pasbons*
	do
		# Si le fichier existe dans cet arrivage.
		if [ -f "$j/$i" ]
		then
			# On repère les problèmes sur les débuts de blocs de 2 Ko: quand erreur il y a, elle se trouve sur un tel bloc, bien alignée, et elle se manifeste dès les premiers octets du bloc.
			#hexdump -C "$j/$i" | grep ^.....[08]00 > /tmp/$$/hex.$j # Ce grep est incorrect, car parfois en tête de bloc les octets sont identiques: on loupe alors le repérage du bloc.
			hexdump -C "$j/$i" > /tmp/$$/hex.$j
			if [ -z "$ref" ]
			then
				ref=/tmp/$$/hex.$j
				cat "$j/$i" > /tmp/$$/ref
			else
				( echo ibase=16 ; diff "$ref" /tmp/$$/hex.$j | grep '^<' | cut -c 3- | cut -d ' ' -f 1 | tr 'abcdef' 'ABCDEF' | sed -e 's#$#/800#' ) | bc | sort >> /tmp/$$/diffs
			fi
		fi
	done
	# Si trop d'erreurs à la lecture, on fera du manuel: la machine ne tiendra pas l'exploration des n envois puissance p blocs en erreur possibilités. À nous d'intelligemment décider, pour n > 2, si un bloc apparaît dans plus d'un envoi, qu'il est le bon.
	sort -nu < /tmp/$$/diffs > /tmp/$$/diff
	echo "`wc -l < /tmp/$$/diff` blocs variables" >&2
	[ "`wc -l < /tmp/$$/diff`" -lt 2 ] && continue
	[ $methode = bourrin ] && [ "`wc -l < /tmp/$$/diff`" -gt 11 ] && continue
	rm -Rf /tmp/$$/bouts.*
	rm -Rf /tmp/$$/avant.*
	cat /tmp/$$/diff | while read j
	do
		mkdir /tmp/$$/bouts.$j
		for f in pasbons*/$i
		do
			g="`dirname "$f"`"
			dd if="$f" of=/tmp/$$/bouts.$j/$g bs=2048 skip=$j count=1 2> /dev/null
		done
		# Si on a des doublons de bouts, on les regroupe. On ne décide pas que ceux en plus grand nombre sont le bon: il m'est arrivé d'avoir deux envois avec le même mauvais segment avant d'en avoir un bon.
		cksum /tmp/$$/bouts.$j/* | sort | tee /tmp/$$/sum.0 | sort -u -k 1,1 > /tmp/$$/sum.1
		diff /tmp/$$/sum.1 /tmp/$$/sum.0 | grep '^> ' | cut -c 3- | while read rien rien k
		do
			rm "$k"
		done
	done
	$methode
done
}
	
bourrin()
{
	# On va générer autant de commandes de composition de nouveau fichier qu'il y a de combinaisons possibles entre blocs divergents.
	# Pour chaque divergence de données (un répertoire par endroit de divergence), on duplique autant de fois que de versions de la divergence, l'accumulation précédente.
	prec=0
	echo cat > /tmp/$$/commandes
	while read j
	do
		# Le bout intercalaire sans erreur.
		dd if=/tmp/$$/ref bs=2048 skip=$prec count="`expr $j - $prec`" of=/tmp/$$/avant.$j 2> /dev/null
		# Les différentes version du bout erroné.
		true > /tmp/$$/commandes.1
		for f in /tmp/$$/bouts.$j/*
		do
			sed -e "s#\$# /tmp/$$/avant.$j $f#" < /tmp/$$/commandes >> /tmp/$$/commandes.1
		done
		cat /tmp/$$/commandes.1 > /tmp/$$/commandes
		prec=`expr $j + 1`
	done < /tmp/$$/diff
	dd if=/tmp/$$/ref bs=2048 skip=$prec of=/tmp/$$/apres.dernier 2> /dev/null
	sed -e "s#\$# /tmp/$$/apres.dernier > /tmp/$$/test#" < /tmp/$$/commandes > /tmp/$$/comm
	# Premier écrémage: on essaie de ne garder pour chaque bloc divergent qu'une seule version, celle qui placée après un bon début de fichier fait sortir en "fin prématurée de fichier" plutôt qu'en "valeur incorrecte". Si pour un bloc on a 0 version correcte, on ne sait traiter. Si on en a 2, on poursuit le processus combinatoire qui seul nous permettra de déterminer, sur le fichier complet, quel recollage est valide.
if false # En fait on n'a pas vraiment de moyen de distinguer les deux cas d'erreur dans le JPEG.
then
	true > /tmp/$$/reconst
	while read j
	do
		cat /tmp/$$/avant.$j >> /tmp/$$/reconst
		for f in /tmp/$$/bouts.$j/*
		do
			cat /tmp/$$/reconst $f > /tmp/$$/reconst.2
			php -r "imagecreatefromjpeg('/tmp/$$/reconst.2');" 2>&1
			# À FAIRE: si le truc précédent voit des données valides mais équeutées, on continue. S'il voit des données pourries, on supprime $f après l'avoir grepmoinvé de comm.
		done
		# À FAIRE: s'il nous reste 0 /tmp/$$/bouts.$j/*, continue 2; s'il nous en reste 2 ou plus, break (on passe à l'algo bourrin qui suit).
	done < /tmp/$$/diff
fi
	# Exécution des commandes.
	n=0
	cat /tmp/$$/comm | while read j
	do
		printf "%d... " "$n" >&2
		sh -c "$j"
		r=non
		php -r "imagecreatefromjpeg('/tmp/$$/test');" 2>&1 | grep -q JPEG || r=oui
		echo "$r" >&2
		[ $r = oui ] && cp /tmp/$$/test oui/$i.$n && break
		n="`expr $n + 1`"
	done
}

prog()
{
	prec=0
	true > /tmp/$$/reconst.jpg
	rm -f /tmp/$$/essai.*.jpg
	true > /tmp/$$/essai.0.jpg
	while read j
	do
		printf "  - bout $j " >&2
		# Le bout intercalaire sans erreur.
		max=-1
		n=0
		for precedent in /tmp/$$/essai.*.jpg
		do
			dd if=/tmp/$$/ref bs=2048 skip=$prec count="`expr $j - $prec`" >> $precedent 2> /dev/null
			# Les différentes version du bout erroné.
		for f in /tmp/$$/bouts.$j/*
		do
				cat $precedent $f > /tmp/$$/reconst.essai.jpg
				#dd if=/tmp/$$/ref bs=2048 skip=`expr $j + 1` >> /tmp/$$/reconst.essai.jpg 2> /dev/null
				true > /tmp/$$/reconst.essai.bmp
			sips -s format bmp /tmp/$$/reconst.essai.jpg --out /tmp/$$/reconst.essai.bmp 2> /dev/null > /dev/null # C'est ce qu'appelle le composant AppleScipr "Image Events"… Sauf que lui a un comportement fiable (Image Events, parfois convertit, parfois pas, la même image).
			t=`/tmp/$$/trouve < /tmp/$$/reconst.essai.bmp`
				# Si on atteint un nouveau (ou un ancien) maximum, on range le fichier résultant dans un dossier au nom de ce max
				[ $t -ge $max ] && max=$t && mkdir -p /tmp/$$/reconst.$max && mv /tmp/$$/reconst.essai.jpg /tmp/$$/reconst.$max/essai.$n.jpg && n=`expr $n + 1`
			done
		done
		prec=`expr $j + 1`
		rm -f /tmp/$$/essai.*.jpg
		mv /tmp/$$/reconst.$max/* /tmp/$$/
		rm -Rf /tmp/$$/reconst.*
		[ "`ls /tmp/$$/essai.*.jpg | wc -l`" -gt 10 ] && echo 'x' >&2 && break # Bon, là on commence à avoir engrangé trop de variantes qui nous donnent toutes la même taille au final. Ce qui signifie probablement qu'avant dans le fichier on a déjà un bloc inreconstituable (avec quelque variante sienne que ce soit la lecture reste coincée à cet endroit). On sort donc plutôt que de continuer à s'épuiser.
		if [ "`ls /tmp/$$/essai.*.jpg | wc -l`" -gt 1 ]
		then
			echo "# $max octets lisibles sur `ls /tmp/$$/essai.*.jpg | wc -l` variantes" >&2
		else
			echo 'o' >&2
		fi
	done < /tmp/$$/diff
	cat "`ls /tmp/$$/essai.*.jpg | head -1`" > oui/$i
	dd if=/tmp/$$/ref bs=2048 skip=$prec >> oui/$i 2> /dev/null
}

traiter

rm -Rf /tmp/$$
