Page 1 sur 1

Conversion d'une routine de décompression en C

Publié : jeu. 22/mars/2012 17:35
par Crashdisk
Bonjour,
j'ai adapté, pour les besoins de mon projet, une routine de décompression écrite à l'origine en C. Je pense l'avoir pas trop mal converti mais je rencontre un problème que je n'arrive pas à identifier. La décompression est correcte à 99% mais ce n'est pas un résultat acceptable, vous en conviendrez. Voici la routine d'origine hors contexte :

Code : Tout sélectionner

int crunch_quick(unsigned char *source, unsigned char *source_end,
               unsigned char *destination, unsigned char *destination_end)
{
   register unsigned int control = 0;
   register int shift = 0;
   int count, offset;

   quick_local += 5; /* i have no idea why it adds 5 */
   if(!no_clear_flag)
      for(quick_local = count = 0; count < 256; count++)
         quick_buffer[count] = 0;

   while((destination < destination_end) && (source < source_end))
   {
      control <<= 9; /* all codes are at least 9 bits long */
      if((shift += 9) > 0)
      {
         control += *source++ << (8 + shift);
         control += *source++ << shift;
         shift -= 16;
      }
      if(control & 16777216)
      {
         *destination++ = quick_buffer[quick_local++ & 255] = control >> 16;
      }
      else
      {
         control <<= 2; /* 2 extra bits for length */
         if((shift += 2) > 0)
         {
            control += *source++ << (8 + shift);
            control += *source++ << shift;
            shift -= 16;
         }
         count = ((control >> 24) & 3) + 2;
         offset = quick_local - ((control >> 16) & 255) - 1;
         while((destination < destination_end) && (count--))
            *destination++ = quick_buffer[quick_local++ & 255] =
                             quick_buffer[offset++ & 255];
      }
   } /* while */

if(DEBUG)printf(" ...quick %s",((source > source_end) || (destination != destination_end)) ? "bad" : "good");
   return((source > source_end) || (destination != destination_end));
}
et mon adaptation :

Code : Tout sélectionner

  Procedure UnpackQuick(*source, *source_end, *destination, *destination_end)
    Protected control.i, shift.i, count.i, offset.i
    control = 0
    shift = 0
    quick_local + 5
    If ~no_clear_flag
  	  count = 0
  	  quick_local = count
  	  While count < 256
  	   quick_buffer(count) = 0
  	   count + 1
  	  Wend
    EndIf
    While (*destination < *destination_end) And (*source < *source_end)
      control << 9      ; all codes are at least 9 bits long
      shift + 9
      If shift > 0
  	    control + PeekA(*source) << (8 + shift)
  	    *source + 1
  	    control + PeekA(*source) << shift
  	    *source + 1
  	    shift - 16
  	  EndIf
      If control & 16777216
        quick_buffer(quick_local & 255) = control >> 16
  	    PokeA(*destination, quick_buffer(quick_local & 255))
  	    *destination + 1
        quick_local + 1
      Else
  	    control << 2    ; 2 extra bits For length
  	    shift + 2
  	    If shift > 0
  	      control + PeekA(*source) << (8 + shift)
  	      *source + 1
  	      control + PeekA(*source) << shift
  	      *source + 1
  		    shift - 16
  		  EndIf
  	    count = ((control >> 24) & 3) + 2
  	    offset = quick_local - ((control >> 16) & 255) - 1
  	    While ((*destination < *destination_end) And count)
  	      count - 1
  	      quick_buffer(quick_local & 255) = quick_buffer(offset & 255)
  	      offset + 1
  	      PokeA(*destination, quick_buffer(quick_local & 255))
  	      quick_local + 1
  	      *destination + 1
  	    Wend
  	  EndIf
  	Wend
  EndProcedure
Pour illustrer mon problème voici le (presque) même code autonome à tester chez soi ^^

Code : Tout sélectionner

Global Dim Donnees.a(14)
Global no_clear_flag.i
Global Dim quick_buffer.a(256)

Donnees(0)  = $17
Donnees(1)  = $B0
Donnees(2)  = $08
Donnees(3)  = $51
Donnees(4)  = $03
Donnees(5)  = $84
Donnees(6)  = $90
Donnees(7)  = $56
Donnees(8)  = $03
Donnees(9)  = $00
Donnees(10) = $99
Donnees(11) = $30
Donnees(12) = $58
Donnees(13) = $56

  Procedure UnpackQuick()
    Protected control.i, shift.i, count.i, offset.i, compt.i
    control = 0
    shift = 0
    compt = 0
    quick_local + 5
    If ~no_clear_flag
  	  count = 0
  	  quick_local = count
  	  While count < 256
  	   quick_buffer(count) = 0
  	   count + 1
  	  Wend
    EndIf
    While compt < 14
      control << 9      ; all codes are at least 9 bits long
      shift + 9
      If shift > 0
        control + Donnees(compt) << (8 + shift)
  	    compt + 1
  	    control + Donnees(compt) << shift
  	    compt + 1
  	    shift - 16
  	  EndIf
      If control & 16777216
        quick_buffer(quick_local & 255) = control >> 16
        Debug "Rout1 : $" + Hex(quick_buffer(quick_local & 255),#PB_Byte)
        quick_local + 1
      Else
  	    control << 2    ; 2 extra bits For length
  	    shift + 2
  	    If shift > 0
  	      control + Donnees(compt) << (8 + shift)
  	      compt + 1
  	      control + Donnees(compt) << shift
  	      compt + 1
  		    shift - 16
  		  EndIf
  	    count = ((control >> 24) & 3) + 2
  	    offset = quick_local - ((control >> 16) & 255) - 1
  	    While count
  	      count - 1
  	      quick_buffer(quick_local & 255) = quick_buffer(offset & 255)
  	      offset + 1
  	      Debug "Rout2 : $" + Hex(quick_buffer(quick_local & 255),#PB_Byte)
  	      quick_local + 1
  	    Wend
  	  EndIf
  	Wend
  EndProcedure
  no_clear_flag = $01 & 1
UnpackQuick()
Avec ces données brutes compressées :
17 B0 08 51 03 84 90 56 03 00 99 30 58 56

je devrais obtenir celle là :
00 01 00 0A 00 01 00 09 00 0A 00 0A 00 0A 00 0A 00 32 00 0A 00 0A 00

or j'obtiens ça :
00 00 00 0A 00 00 00 09 00 0A 00 0A 00 0A 00 0A 00 32 00 0A 00 0A 00

Le problème semble toujours lié à la dernière partie du code et particulièrement au quick_buffer mais ça, je n'en suis pas sûr car il y a encore des choses qui m'échappent dans le fonctionnement même de ce code :oops:

Re: Conversion d'une routine de décompression en C

Publié : jeu. 22/mars/2012 19:28
par graph100
ça m’intéresse ce que tu fais, mais malheureusement le C n'est pas de mon rayon :s

Re: Conversion d'une routine de décompression en C

Publié : ven. 23/mars/2012 10:01
par Mesa
Aurais-tu plus d'infos sur la formule mathématique utilisée ?

J'ai un doute sur la traduction de

Code : Tout sélectionner

control += *source++ << (8 + shift);
control += *source++ << shift;
C'est le *source++ qui m'intrigue.
Avec la formule de math ça serait plus clair.

Mesa.

Re: Conversion d'une routine de décompression en C

Publié : ven. 23/mars/2012 19:22
par Crashdisk
graph100 a écrit :ça m’intéresse ce que tu fais, mais malheureusement le C n'est pas de mon rayon :s
Dommage en effet :wink:
Mesa a écrit :Aurais-tu plus d'infos sur la formule mathématique utilisée ?

J'ai un doute sur la traduction de

Code : Tout sélectionner

control += *source++ << (8 + shift);
control += *source++ << shift;
C'est le *source++ qui m'intrigue.
Avec la formule de math ça serait plus clair.

Mesa.
Je ne pense pas que le problème vient de "*source++"
sa fonction est de lire l'octet à l'adresse "source" et d'incrémenter l'adresse de 1

Mon projet est de décortiquer, décompresser et de décrypter un vieux format de fichier utilisé sur Amiga mais en plus d'avoir un format mal documenté et il est en plus réputé pour comporter de nombreuses versions avec parfois des bug dans ses algos de compression ... sic
De plus il existe très peu de sources de programme permettant la décompression, du moins, ils sont pratiquement tous basés sur le même code.
En continuant le débogage, j'ai compris que mon problème principal se situe dans le tableau "quick_buffer".
Lorsque l'on exécute le code autonome du premier post, on peut remarqué que dès le début, c'est la deuxième partie du code qui est exécuté or les données écrites sont toutes issue du quick_buffer alors même qu'il est vide!!
Cela signifie que certains blocs de données compressé sont dépendant du dictionnaire (quick_buffer) généré par une décompression précédente.
Au début du code, on vérifie le no_clear_flag pour savoir s'il faut effacer ou non le quick_buffer or dans mon fichier de test la condition est toujours vrai...
Même en définissant moi même les conditions, la plupart des résultats sont toujours mauvais.
Je vais continuer de me creuser la tête quoi .... :?

En tout cas, ça m'a permis de trouver un bug dans l'émulateur WinUAE, c'est déjà ça ^^

Re: Conversion d'une routine de décompression en C

Publié : ven. 23/mars/2012 20:36
par Ar-S
Quel format de compression ?
A part le lha je ne me rappel plus de grand chose.

Re: Conversion d'une routine de décompression en C

Publié : ven. 23/mars/2012 20:50
par Crashdisk
Justement c'est une des question que je me pose ... il s'agit dans pour mon problème du mode de compression QUICK de DMS et à part l'algo LZH utilisé pour le mode HEAVY 1/2 et le RLE pour le mode SIMPLE, pour le reste je ne sais pas...

Je travail sur une routine alternative pour voir ....

Re: Conversion d'une routine de décompression en C

Publié : sam. 24/mars/2012 1:54
par Crashdisk
Je viens de trouver une "erreur" dans ma conversion:

Code : Tout sélectionner

no_clear_flag = $01 & 1
...
If ~no_clear_flag
dans le code en C la variable no_clear_flag est un integer non signé et il semble que lorsque l'on fait un NOT sur cette variable=1 alors le résultat est 0. Elle se comporte donc comme une variable booléen. Mais en PB c'est différent puisque:

Code : Tout sélectionner

no_clear_flag  =    1  = %00000001
~no_clear_flag => 254  = %11111110
Cela fait que la condition est vrai car différente de 0 !
Du coup, pour contourner le problème je dois changer l'expression :
~($01 & 1) => %11111110
par
~($01) & 1 => %00000000

Subtil ... :? et cela explique une grosse partie de mon problème :lol:

Re: Conversion d'une routine de décompression en C

Publié : sam. 24/mars/2012 17:50
par case
tu peux aussi utiliser pour inverser la valeur de 0 à 1

Code : Tout sélectionner

  abs(no_clear_flag -1)
si
no_clear_flag=0
no_clear_flag -1=-1
abs(-1)=1

si
no_clear_flag=1
no_clear_flag -1=0
abs(0)=0

Re: Conversion d'une routine de décompression en C

Publié : dim. 25/mars/2012 0:32
par Crashdisk
Merci pour l'astuce, elle devrait me reservir même si le commentaire de la notice laisse un doute sur sa viabilité:
"Si un entier est passé en argument et que sa valeur est grande une perte de précision sera constatée"
c'est un peu flou tout de même ...

Ce qui est dommage en fait, c'est que PB ne gère pas les variables booléenne car je suis un grand fan de flag :-P

Sinon, j'ai fini la conversion d'une autre routine qui fonctionne très bien mais pour le sport, je mettrai à jour le code de test pour les données qui passe mal avec cette première routine...histoire de comprendre où se trouve les autres problèmes.

Re: Conversion d'une routine de décompression en C

Publié : dim. 25/mars/2012 21:51
par Crashdisk
C'est bon, j'ai compris d’où vient le problème!
Le code qui m'a servi de base est bogué (fallait y penser ...) !!
Après avoir corrigé le problème sur la réutilisation du dictionnaire, les données obtenu n'étaient toujours pas conforme et pour cause. Dans le code C la routine s'arrête dès que je ne rempli plus l'une de ces deux conditions "(*destination < *destination_end) And (*source < *source_end)" or, lorsque "*source = *source_end" la décompression n'est pas obligatoirement fini pour autant...
En effet, les données stockées dans "control" peuvent suffire à faire un cycle complet sans effectuer de lecture via "*source" et bien sur, c'était le cas lorsque j'avais des données à problème. Le correctif et donc:
While (*destination < *destination_end) And (*source <= *source_end)

Et paf ça marche ^^

Si ça intéresse quelqu'un, j'ai un autre code qui fait le même boulot différemment. Merci pour vos encouragements et astuces ;-)