Conversion d'une routine de décompression en C

Vous débutez et vous avez besoin d'aide ? N'hésitez pas à poser vos questions
Crashdisk
Messages : 15
Inscription : sam. 18/févr./2012 17:09

Conversion d'une routine de décompression en C

Message 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:
Avatar de l’utilisateur
graph100
Messages : 1318
Inscription : sam. 21/mai/2005 17:50

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

Message par graph100 »

ça m’intéresse ce que tu fais, mais malheureusement le C n'est pas de mon rayon :s
_________________________________________________
Mon site : CeriseCode (Attention Chantier perpétuel ;))
Mesa
Messages : 1126
Inscription : mer. 14/sept./2011 16:59

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

Message 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.
Crashdisk
Messages : 15
Inscription : sam. 18/févr./2012 17:09

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

Message 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 ^^
Avatar de l’utilisateur
Ar-S
Messages : 9539
Inscription : dim. 09/oct./2005 16:51
Contact :

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

Message par Ar-S »

Quel format de compression ?
A part le lha je ne me rappel plus de grand chose.
~~~~Règles du forum ~~~~
⋅.˳˳.⋅ॱ˙˙ॱ⋅.˳Ar-S ˳.⋅ॱ˙˙ॱ⋅.˳˳.⋅
W11x64 PB 6.x
Section HORS SUJET : ICI
LDV MULTIMEDIA : Dépannage informatique & mes Logiciels PB
UPLOAD D'IMAGES : Uploader des images de vos logiciels
Crashdisk
Messages : 15
Inscription : sam. 18/févr./2012 17:09

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

Message 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 ....
Crashdisk
Messages : 15
Inscription : sam. 18/févr./2012 17:09

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

Message 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:
Avatar de l’utilisateur
case
Messages : 1545
Inscription : lun. 10/sept./2007 11:13

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

Message 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
ImageImage
Crashdisk
Messages : 15
Inscription : sam. 18/févr./2012 17:09

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

Message 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.
Crashdisk
Messages : 15
Inscription : sam. 18/févr./2012 17:09

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

Message 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 ;-)
Répondre